elysia 2.0.0-rc.3 → 2.0.0-rc.4

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 (149) hide show
  1. package/dist/adapter/utils.d.ts +8 -12
  2. package/dist/adapter/utils.js +88 -106
  3. package/dist/adapter/utils.mjs +88 -106
  4. package/dist/base.d.ts +2 -3
  5. package/dist/base.js +97 -194
  6. package/dist/base.mjs +95 -192
  7. package/dist/compile/aot-capture.js +24 -5
  8. package/dist/compile/aot-capture.mjs +24 -5
  9. package/dist/compile/aot-emit.js +6 -0
  10. package/dist/compile/aot-emit.mjs +6 -0
  11. package/dist/compile/aot.d.ts +43 -10
  12. package/dist/compile/aot.js +31 -25
  13. package/dist/compile/aot.mjs +29 -27
  14. package/dist/compile/handler/descriptor.d.ts +2 -0
  15. package/dist/compile/handler/descriptor.js +9 -3
  16. package/dist/compile/handler/descriptor.mjs +9 -3
  17. package/dist/compile/handler/frozen-validator.d.ts +1 -2
  18. package/dist/compile/handler/frozen-validator.js +0 -2
  19. package/dist/compile/handler/frozen-validator.mjs +1 -2
  20. package/dist/compile/handler/index.js +3 -5
  21. package/dist/compile/handler/index.mjs +3 -5
  22. package/dist/compile/handler/jit.d.ts +4 -2
  23. package/dist/compile/handler/jit.js +26 -18
  24. package/dist/compile/handler/jit.mjs +26 -19
  25. package/dist/compile/handler/param-resolvers.d.ts +3 -2
  26. package/dist/compile/handler/param-resolvers.js +2 -0
  27. package/dist/compile/handler/param-resolvers.mjs +3 -2
  28. package/dist/compile/handler/param-table.js +1 -0
  29. package/dist/compile/handler/param-table.mjs +2 -1
  30. package/dist/compile/handler/params.d.ts +1 -5
  31. package/dist/compile/handler/params.js +2 -3
  32. package/dist/compile/handler/params.mjs +2 -2
  33. package/dist/compile/handler/reconstruct.js +4 -4
  34. package/dist/compile/handler/reconstruct.mjs +3 -3
  35. package/dist/compile/handler/route-shape.d.ts +9 -1
  36. package/dist/compile/handler/route-shape.js +14 -2
  37. package/dist/compile/handler/route-shape.mjs +13 -4
  38. package/dist/compile/handler/utils.d.ts +4 -2
  39. package/dist/compile/handler/utils.js +19 -3
  40. package/dist/compile/handler/utils.mjs +19 -4
  41. package/dist/compile/utils.d.ts +3 -1
  42. package/dist/compile/utils.js +2 -0
  43. package/dist/compile/utils.mjs +2 -1
  44. package/dist/context.js +2 -0
  45. package/dist/context.mjs +2 -0
  46. package/dist/cookie/utils.js +37 -10
  47. package/dist/cookie/utils.mjs +37 -10
  48. package/dist/error.d.ts +11 -1
  49. package/dist/error.js +6 -1
  50. package/dist/error.mjs +6 -1
  51. package/dist/handler/error.js +4 -2
  52. package/dist/handler/error.mjs +4 -2
  53. package/dist/handler/fetch.js +91 -144
  54. package/dist/handler/fetch.mjs +93 -146
  55. package/dist/index.d.ts +2 -2
  56. package/dist/package.js +1 -1
  57. package/dist/package.mjs +1 -1
  58. package/dist/plugin/aot/core.d.ts +27 -15
  59. package/dist/plugin/aot/core.js +22 -17
  60. package/dist/plugin/aot/core.mjs +20 -16
  61. package/dist/plugin/aot/source.js +7 -1
  62. package/dist/plugin/aot/source.mjs +7 -1
  63. package/dist/plugin/trace.d.ts +1 -0
  64. package/dist/plugin/trace.js +3 -1
  65. package/dist/plugin/trace.mjs +4 -2
  66. package/dist/plugin/websocket.d.ts +1 -0
  67. package/dist/route-table.d.ts +0 -1
  68. package/dist/route-table.js +1 -2
  69. package/dist/route-table.mjs +1 -2
  70. package/dist/schema-snapshot.d.ts +7 -2
  71. package/dist/schema-snapshot.js +50 -16
  72. package/dist/schema-snapshot.mjs +51 -17
  73. package/dist/sucrose.d.ts +3 -2
  74. package/dist/sucrose.js +22 -6
  75. package/dist/sucrose.mjs +22 -6
  76. package/dist/trace.d.ts +31 -2
  77. package/dist/trace.js +84 -0
  78. package/dist/trace.mjs +83 -1
  79. package/dist/type/bridge-live.d.ts +1 -1
  80. package/dist/type/bridge-live.js +1 -1
  81. package/dist/type/bridge-live.mjs +1 -1
  82. package/dist/type/bridge.d.ts +1 -1
  83. package/dist/type/coerce.d.ts +1 -1
  84. package/dist/type/constants.d.ts +1 -1
  85. package/dist/type/typebox-value-live.d.ts +2 -2
  86. package/dist/type/typebox-value-live.js +2 -2
  87. package/dist/type/typebox-value-live.mjs +2 -2
  88. package/dist/type/typebox-value-ops.d.ts +2 -2
  89. package/dist/type/typebox-value-ops.js +2 -2
  90. package/dist/type/typebox-value-ops.mjs +2 -2
  91. package/dist/type/typebox-value-require.js +9 -8
  92. package/dist/type/typebox-value-require.mjs +9 -8
  93. package/dist/type/typebox-value.d.ts +6 -6
  94. package/dist/type/validator/custom-error.d.ts +18 -3
  95. package/dist/type/validator/custom-error.js +1 -18
  96. package/dist/type/validator/custom-error.mjs +2 -19
  97. package/dist/type/validator/frozen-check.d.ts +3 -2
  98. package/dist/type/validator/frozen-check.js +20 -1
  99. package/dist/type/validator/frozen-check.mjs +19 -1
  100. package/dist/type/validator/string-codec-aot.js +1 -1
  101. package/dist/type/validator/string-codec-aot.mjs +1 -1
  102. package/dist/types.d.ts +6 -8
  103. package/dist/utils.d.ts +3 -9
  104. package/dist/utils.js +20 -54
  105. package/dist/utils.mjs +21 -54
  106. package/dist/validator/index.js +2 -3
  107. package/dist/validator/index.mjs +1 -2
  108. package/dist/ws/context.d.ts +1 -1
  109. package/dist/ws/context.js +2 -10
  110. package/dist/ws/context.mjs +3 -11
  111. package/dist/ws/route.js +41 -28
  112. package/dist/ws/route.mjs +44 -31
  113. package/package.json +7 -1
  114. package/src/adapter/utils.ts +124 -138
  115. package/src/base.ts +165 -383
  116. package/src/compile/aot-capture.ts +29 -2
  117. package/src/compile/aot-emit.ts +6 -1
  118. package/src/compile/aot.ts +92 -32
  119. package/src/compile/handler/descriptor.ts +30 -6
  120. package/src/compile/handler/frozen-validator.ts +0 -4
  121. package/src/compile/handler/index.ts +10 -5
  122. package/src/compile/handler/jit.ts +67 -20
  123. package/src/compile/handler/param-resolvers.ts +2 -0
  124. package/src/compile/handler/param-table.ts +2 -0
  125. package/src/compile/handler/params.ts +1 -5
  126. package/src/compile/handler/reconstruct.ts +3 -6
  127. package/src/compile/handler/route-shape.ts +26 -2
  128. package/src/compile/handler/utils.ts +21 -5
  129. package/src/compile/utils.ts +4 -0
  130. package/src/context.ts +7 -1
  131. package/src/cookie/utils.ts +53 -16
  132. package/src/error.ts +27 -2
  133. package/src/handler/error.ts +11 -3
  134. package/src/handler/fetch.ts +209 -276
  135. package/src/plugin/aot/core.ts +83 -33
  136. package/src/plugin/aot/source.ts +17 -2
  137. package/src/plugin/trace.ts +9 -2
  138. package/src/route-table.ts +1 -2
  139. package/src/schema-snapshot.ts +101 -20
  140. package/src/sucrose.ts +44 -13
  141. package/src/trace.ts +185 -1
  142. package/src/type/typebox-value-require.ts +11 -7
  143. package/src/type/validator/custom-error.ts +2 -33
  144. package/src/type/validator/frozen-check.ts +31 -1
  145. package/src/types.ts +5 -8
  146. package/src/utils.ts +29 -83
  147. package/src/validator/index.ts +1 -4
  148. package/src/ws/context.ts +5 -13
  149. package/src/ws/route.ts +73 -54
@@ -3,6 +3,7 @@ import { BaseCookie } from "../cookie/types.js";
3
3
  import { Context } from "../context.js";
4
4
  import { ElysiaFile } from "../universal/file.js";
5
5
  import { HTTPHeaders } from "../types.js";
6
+
6
7
  //#region src/adapter/utils.d.ts
7
8
  declare const hget: (h: Headers | Record<string, unknown>, k: string) => unknown;
8
9
  declare function hput(h: Headers | Record<string, unknown>, k: string, v: unknown, soft?: 1): void;
@@ -17,7 +18,7 @@ declare function responseToSetHeaders(response: Response, set?: Context['set']):
17
18
  redirect?: never;
18
19
  } | {
19
20
  headers: any;
20
- 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";
21
+ status: number | "Not Found" | "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" | "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";
21
22
  };
22
23
  interface CreateHandlerParameter {
23
24
  mapResponse(response: unknown, set: Context['set'], request?: Request, owned?: boolean): Response;
@@ -38,23 +39,18 @@ declare function createResponseHandler(): (response: Response, set?: Context["se
38
39
  */
39
40
  declare function observeStream(source: ReadableStream): [ReadableStream, AsyncIterable<unknown>, AsyncIterableIterator<unknown>] | undefined;
40
41
  /**
41
- * Split async source into `branches` independent iterators
42
+ * Split async source into a value iterator and a completion iterator
42
43
  *
43
- * A producer drains the source ahead of consumers
44
+ * A producer drains the source ahead of the value consumer (response/client)
44
45
  *
45
46
  * To prevent long/infinite stream, the unconsumed window is capped:
46
- * Consumed-by-every-branch entries are trimmed off the front
47
47
  * Producer backpressures whenever the window hits `cap` ENTRIES or
48
48
  * `capBytes` bytes, whichever comes first
49
49
  *
50
- * Streams below both caps buffer eagerly
51
- * Only streams exceeding one gate on the slowest consumer
52
- *
53
- * Branch 0 is the value consumer (response/client)
54
- * When `return()` (client abort / early exit), source is stopped
55
- * so the observer branches can still reach completion instead of spinning
56
- * an infinite source
50
+ * The second iterator yields nothing: it settles once the source ends
51
+ * (rejects with its error) or the value iterator returns, which stops
52
+ * the source so an infinite one can't keep afterResponse waiting
57
53
  */
58
- declare function tee<T>(source: AsyncIterable<T>, branches?: number, cap?: number, capBytes?: number): AsyncIterableIterator<T>[];
54
+ declare function tee<T>(source: AsyncIterable<T>, cap?: number, capBytes?: number): AsyncIterableIterator<T>[];
59
55
  //#endregion
60
56
  export { createResponseHandler, createStreamHandler, handleFile, handleSet, hget, hput, materializeSetHeaders, normalizeContentType, observeStream, parseSetCookies, responseToSetHeaders, streamResponse, tee };
@@ -3,6 +3,7 @@ const require_universal_constants = require('../universal/constants.js');
3
3
  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
+ const require_error = require('../error.js');
6
7
  const require_cookie_serialize = require('../cookie/serialize.js');
7
8
  const require_adapter_response_ownership = require('./response-ownership.js');
8
9
  const require_adapter_default_headers = require('./default-headers.js');
@@ -147,8 +148,8 @@ function createStreamHandler({ mapResponse, mapCompactResponse }) {
147
148
  }
148
149
  return new Response(generator, { headers: { "content-type": "application/octet-stream" } });
149
150
  }
150
- const responses = set?.["~chunk"];
151
- if (responses) set["~chunk"] = void 0;
151
+ const pickChunk = set?.["~chunk"];
152
+ if (pickChunk) set["~chunk"] = void 0;
152
153
  const typedSSE = !skipFormat && "sse" in generator && generator.sse === true;
153
154
  const sourceIterator = typeof generator.next === "function" ? generator : void 0;
154
155
  let init = typedSSE ? void 0 : generator.next?.();
@@ -166,8 +167,23 @@ function createStreamHandler({ mapResponse, mapCompactResponse }) {
166
167
  const yieldedStream = init?.value instanceof ReadableStream;
167
168
  if (yieldedStream) generator = init.value;
168
169
  else if (init && (typeof init?.done === "undefined" || init?.done)) {
169
- if (set) return mapResponse(init.value, set, request, owned);
170
- return mapCompactResponse(init.value, request, owned);
170
+ let value = init.value;
171
+ if (pickChunk) {
172
+ if (value instanceof Promise || typeof value?.then === "function") value = await value;
173
+ const wrapped = value instanceof require_error.ElysiaStatus;
174
+ const validator = pickChunk(wrapped ? value.status : set?.status ?? 200);
175
+ if (validator) {
176
+ let body = wrapped ? value.response : value;
177
+ if (body instanceof Promise || typeof body?.then === "function") body = await body;
178
+ if (typeof body?.next === "function") {
179
+ if (set) set["~chunk"] = pickChunk;
180
+ } else if (!(body instanceof Error) && !(body instanceof Response) && !(body instanceof ReadableStream)) body = await (validator.mayReturnPromise ? validator.From(body, "response", true) : validator.EncodeFrom(body, "response"));
181
+ if (wrapped) value.response = body;
182
+ else value = body;
183
+ }
184
+ }
185
+ if (set) return mapResponse(value, set, request, owned);
186
+ return mapCompactResponse(value, request, owned);
171
187
  }
172
188
  const isSSE = typedSSE || !skipFormat && (init?.value?.sse ?? generator?.sse ?? (set && hget(set.headers, "content-type")?.startsWith("text/event-stream")));
173
189
  const format = isSSE ? sseFormat : identityFormat;
@@ -246,14 +262,10 @@ function createStreamHandler({ mapResponse, mapCompactResponse }) {
246
262
  controller.enqueue(new Uint8Array(value.buffer, value.byteOffset, value.byteLength));
247
263
  return;
248
264
  }
249
- if (typeof value === "object") try {
250
- controller.enqueue(textEncoder.encode(format(JSON.stringify(value))));
251
- } catch {
252
- controller.enqueue(textEncoder.encode(format(value.toString())));
253
- }
265
+ if (typeof value === "object") controller.enqueue(textEncoder.encode(format(JSON.stringify(value))));
254
266
  else controller.enqueue(textEncoder.encode(format(value.toString())));
255
267
  };
256
- const validator = sourceStream ? void 0 : responses?.get(set.status ?? 200);
268
+ const validator = sourceStream ? void 0 : pickChunk?.(set.status ?? 200);
257
269
  const enqueueChunk = validator?.Clean || validator?.mayReturnPromise ? (controller, value) => {
258
270
  const cleaned = cleanChunk(validator, value, typedSSE);
259
271
  return cleaned instanceof Promise && cleaned !== value ? cleaned.then((value) => enqueueValue(controller, value)) : enqueueValue(controller, cleaned);
@@ -443,20 +455,23 @@ function observeStream(source) {
443
455
  value
444
456
  ];
445
457
  }
446
- function tee(source, branches = 2, cap = 64, capBytes = 1 << 22) {
458
+ function tee(source, cap = 64, capBytes = 1 << 22) {
447
459
  const iterator = source[Symbol.asyncIterator]?.() ?? source[Symbol.iterator]();
448
460
  const buffer = [];
449
461
  const sizes = [];
450
- let base = 0;
451
462
  let windowBytes = 0;
452
463
  let done = false;
453
464
  let stopped = false;
454
465
  let failed = false;
455
466
  let sourceError;
456
467
  let drainResume = null;
457
- const cursors = new Array(branches).fill(0);
458
- let active = branches;
459
- const pending = new Array(branches).fill(null);
468
+ let pending = null;
469
+ let finish;
470
+ const completed = new Promise((resolve, reject) => finish = {
471
+ resolve,
472
+ reject
473
+ });
474
+ completed.catch(() => {});
460
475
  const resumeProducer = () => {
461
476
  if (drainResume) {
462
477
  const resume = drainResume;
@@ -464,135 +479,102 @@ function tee(source, branches = 2, cap = 64, capBytes = 1 << 22) {
464
479
  resume();
465
480
  }
466
481
  };
467
- const trim = () => {
468
- if (active > 0) {
469
- let min = Infinity;
470
- for (const c of cursors) if (c < min) min = c;
471
- const consumed = min - base;
472
- if (min !== Infinity && consumed > 0) {
473
- for (let i = 0; i < consumed; i++) windowBytes -= sizes[i];
474
- buffer.copyWithin(0, consumed);
475
- buffer.length -= consumed;
476
- sizes.copyWithin(0, consumed);
477
- sizes.length -= consumed;
478
- base = min;
479
- }
480
- }
481
- if (buffer.length < cap && windowBytes < capBytes) resumeProducer();
482
- };
483
- const closeBranch = (me) => {
484
- if (cursors[me] === Infinity) return;
485
- cursors[me] = Infinity;
486
- active--;
487
- if (me === 0 && !stopped) {
488
- stopped = true;
489
- done = true;
490
- resumeProducer();
491
- try {
492
- const r = iterator.return?.();
493
- if (r && typeof r.then === "function") r.catch(() => {});
494
- } catch {}
495
- wakeAll();
496
- }
497
- if (active === 0) {
498
- buffer.length = 0;
499
- sizes.length = 0;
500
- windowBytes = 0;
501
- } else trim();
502
- };
503
- const serve = (me, p) => {
504
- const i = cursors[me];
505
- if (i === Infinity) return p.resolve(doneResult);
506
- if (i < base + buffer.length) {
507
- const value = buffer[i - base];
508
- cursors[me] = i + 1;
509
- trim();
510
- return p.resolve({
511
- done: false,
512
- value
513
- });
514
- }
515
- if (failed) return p.reject(sourceError);
516
- if (done) {
517
- closeBranch(me);
518
- return p.resolve(doneResult);
519
- }
520
- pending[me] = p;
521
- };
522
- const wakeAll = () => {
523
- for (let b = 0; b < branches; b++) {
524
- const p = pending[b];
525
- if (!p) continue;
526
- pending[b] = null;
527
- serve(b, p);
528
- }
482
+ const stop = (p) => {
483
+ pending = null;
484
+ p?.resolve(doneResult);
485
+ if (stopped) return;
486
+ stopped = done = true;
487
+ buffer.length = sizes.length = windowBytes = 0;
488
+ resumeProducer();
489
+ try {
490
+ const r = iterator.return?.();
491
+ if (r && typeof r.then === "function") r.catch(() => {});
492
+ } catch {}
493
+ finish.resolve(doneResult);
529
494
  };
530
495
  (async () => {
531
496
  try {
532
497
  while (!stopped) {
533
498
  const result = await iterator.next();
534
499
  if (result.done || stopped) break;
500
+ const p = pending;
501
+ if (p) {
502
+ pending = null;
503
+ p.resolve({
504
+ done: false,
505
+ value: result.value
506
+ });
507
+ continue;
508
+ }
535
509
  buffer.push(result.value);
536
510
  sizes.push(teeChunkCost(result.value));
537
511
  windowBytes += sizes[sizes.length - 1];
538
- wakeAll();
539
- if ((buffer.length >= cap || windowBytes >= capBytes) && active > 0 && !stopped) await new Promise((resolve) => {
512
+ if ((buffer.length >= cap || windowBytes >= capBytes) && !stopped) await new Promise((resolve) => {
540
513
  drainResume = resolve;
541
514
  });
542
515
  }
543
516
  } catch (error) {
544
- failed = true;
517
+ failed = !stopped;
545
518
  sourceError = error;
546
519
  } finally {
547
520
  done = true;
548
- wakeAll();
521
+ const p = pending;
522
+ pending = null;
523
+ if (failed) {
524
+ p?.reject(sourceError);
525
+ finish.reject(sourceError);
526
+ } else {
527
+ p?.resolve(doneResult);
528
+ finish.resolve(doneResult);
529
+ }
549
530
  }
550
531
  })();
551
- const makeBranch = (me) => Object.assign(Object.create(null), {
532
+ const value = Object.assign(Object.create(null), {
552
533
  [Symbol.asyncIterator]() {
553
534
  return this;
554
535
  },
555
536
  next: () => {
556
- const i = cursors[me];
557
- if (i < base + buffer.length) {
558
- const value = buffer[i - base];
559
- cursors[me] = i + 1;
560
- trim();
537
+ if (buffer.length) {
538
+ const value = buffer.shift();
539
+ windowBytes -= sizes.shift();
540
+ if (buffer.length < cap && windowBytes < capBytes) resumeProducer();
561
541
  return Promise.resolve({
562
542
  done: false,
563
543
  value
564
544
  });
565
545
  }
566
- return new Promise((resolve, reject) => serve(me, {
546
+ if (failed) return Promise.reject(sourceError);
547
+ if (done) return Promise.resolve(doneResult);
548
+ return new Promise((resolve, reject) => pending = {
567
549
  resolve,
568
550
  reject
569
- }));
551
+ });
570
552
  },
571
- return: (value) => {
572
- const p = pending[me];
573
- if (p) {
574
- pending[me] = null;
575
- p.resolve(doneResult);
576
- }
577
- closeBranch(me);
553
+ return: (result) => {
554
+ stop(pending);
578
555
  return Promise.resolve({
579
556
  done: true,
580
- value
557
+ value: result
581
558
  });
582
559
  },
583
560
  throw: (error) => {
584
- const p = pending[me];
585
- if (p) {
586
- pending[me] = null;
587
- p.resolve(doneResult);
588
- }
589
- closeBranch(me);
561
+ stop(pending);
590
562
  return Promise.reject(error);
591
563
  }
592
564
  });
593
- const out = Array.from({ length: branches }, (_, b) => makeBranch(b));
594
- if (source.sse === true) out[0].sse = true;
595
- return out;
565
+ const observer = Object.assign(Object.create(null), {
566
+ [Symbol.asyncIterator]() {
567
+ return this;
568
+ },
569
+ next: () => completed,
570
+ return: (result) => Promise.resolve({
571
+ done: true,
572
+ value: result
573
+ }),
574
+ throw: (error) => Promise.reject(error)
575
+ });
576
+ if (source.sse === true) value.sse = true;
577
+ return [value, observer];
596
578
  }
597
579
 
598
580
  //#endregion
@@ -3,6 +3,7 @@ import { hasHeaderShorthand } from "../universal/constants.mjs";
3
3
  import { StatusMap } from "../constants.mjs";
4
4
  import { EmptyObject, isByteStream, isNotEmpty, sseData } from "../utils.mjs";
5
5
  import { skipClone } from "./skip-clone.mjs";
6
+ import { ElysiaStatus } from "../error.mjs";
6
7
  import { serializeCookie } from "../cookie/serialize.mjs";
7
8
  import { isBorrowedResponse } from "./response-ownership.mjs";
8
9
  import { defaultHeaders } from "./default-headers.mjs";
@@ -147,8 +148,8 @@ function createStreamHandler({ mapResponse, mapCompactResponse }) {
147
148
  }
148
149
  return new Response(generator, { headers: { "content-type": "application/octet-stream" } });
149
150
  }
150
- const responses = set?.["~chunk"];
151
- if (responses) set["~chunk"] = void 0;
151
+ const pickChunk = set?.["~chunk"];
152
+ if (pickChunk) set["~chunk"] = void 0;
152
153
  const typedSSE = !skipFormat && "sse" in generator && generator.sse === true;
153
154
  const sourceIterator = typeof generator.next === "function" ? generator : void 0;
154
155
  let init = typedSSE ? void 0 : generator.next?.();
@@ -166,8 +167,23 @@ function createStreamHandler({ mapResponse, mapCompactResponse }) {
166
167
  const yieldedStream = init?.value instanceof ReadableStream;
167
168
  if (yieldedStream) generator = init.value;
168
169
  else if (init && (typeof init?.done === "undefined" || init?.done)) {
169
- if (set) return mapResponse(init.value, set, request, owned);
170
- return mapCompactResponse(init.value, request, owned);
170
+ let value = init.value;
171
+ if (pickChunk) {
172
+ if (value instanceof Promise || typeof value?.then === "function") value = await value;
173
+ const wrapped = value instanceof ElysiaStatus;
174
+ const validator = pickChunk(wrapped ? value.status : set?.status ?? 200);
175
+ if (validator) {
176
+ let body = wrapped ? value.response : value;
177
+ if (body instanceof Promise || typeof body?.then === "function") body = await body;
178
+ if (typeof body?.next === "function") {
179
+ if (set) set["~chunk"] = pickChunk;
180
+ } else if (!(body instanceof Error) && !(body instanceof Response) && !(body instanceof ReadableStream)) body = await (validator.mayReturnPromise ? validator.From(body, "response", true) : validator.EncodeFrom(body, "response"));
181
+ if (wrapped) value.response = body;
182
+ else value = body;
183
+ }
184
+ }
185
+ if (set) return mapResponse(value, set, request, owned);
186
+ return mapCompactResponse(value, request, owned);
171
187
  }
172
188
  const isSSE = typedSSE || !skipFormat && (init?.value?.sse ?? generator?.sse ?? (set && hget(set.headers, "content-type")?.startsWith("text/event-stream")));
173
189
  const format = isSSE ? sseFormat : identityFormat;
@@ -246,14 +262,10 @@ function createStreamHandler({ mapResponse, mapCompactResponse }) {
246
262
  controller.enqueue(new Uint8Array(value.buffer, value.byteOffset, value.byteLength));
247
263
  return;
248
264
  }
249
- if (typeof value === "object") try {
250
- controller.enqueue(textEncoder.encode(format(JSON.stringify(value))));
251
- } catch {
252
- controller.enqueue(textEncoder.encode(format(value.toString())));
253
- }
265
+ if (typeof value === "object") controller.enqueue(textEncoder.encode(format(JSON.stringify(value))));
254
266
  else controller.enqueue(textEncoder.encode(format(value.toString())));
255
267
  };
256
- const validator = sourceStream ? void 0 : responses?.get(set.status ?? 200);
268
+ const validator = sourceStream ? void 0 : pickChunk?.(set.status ?? 200);
257
269
  const enqueueChunk = validator?.Clean || validator?.mayReturnPromise ? (controller, value) => {
258
270
  const cleaned = cleanChunk(validator, value, typedSSE);
259
271
  return cleaned instanceof Promise && cleaned !== value ? cleaned.then((value) => enqueueValue(controller, value)) : enqueueValue(controller, cleaned);
@@ -443,20 +455,23 @@ function observeStream(source) {
443
455
  value
444
456
  ];
445
457
  }
446
- function tee(source, branches = 2, cap = 64, capBytes = 1 << 22) {
458
+ function tee(source, cap = 64, capBytes = 1 << 22) {
447
459
  const iterator = source[Symbol.asyncIterator]?.() ?? source[Symbol.iterator]();
448
460
  const buffer = [];
449
461
  const sizes = [];
450
- let base = 0;
451
462
  let windowBytes = 0;
452
463
  let done = false;
453
464
  let stopped = false;
454
465
  let failed = false;
455
466
  let sourceError;
456
467
  let drainResume = null;
457
- const cursors = new Array(branches).fill(0);
458
- let active = branches;
459
- const pending = new Array(branches).fill(null);
468
+ let pending = null;
469
+ let finish;
470
+ const completed = new Promise((resolve, reject) => finish = {
471
+ resolve,
472
+ reject
473
+ });
474
+ completed.catch(() => {});
460
475
  const resumeProducer = () => {
461
476
  if (drainResume) {
462
477
  const resume = drainResume;
@@ -464,135 +479,102 @@ function tee(source, branches = 2, cap = 64, capBytes = 1 << 22) {
464
479
  resume();
465
480
  }
466
481
  };
467
- const trim = () => {
468
- if (active > 0) {
469
- let min = Infinity;
470
- for (const c of cursors) if (c < min) min = c;
471
- const consumed = min - base;
472
- if (min !== Infinity && consumed > 0) {
473
- for (let i = 0; i < consumed; i++) windowBytes -= sizes[i];
474
- buffer.copyWithin(0, consumed);
475
- buffer.length -= consumed;
476
- sizes.copyWithin(0, consumed);
477
- sizes.length -= consumed;
478
- base = min;
479
- }
480
- }
481
- if (buffer.length < cap && windowBytes < capBytes) resumeProducer();
482
- };
483
- const closeBranch = (me) => {
484
- if (cursors[me] === Infinity) return;
485
- cursors[me] = Infinity;
486
- active--;
487
- if (me === 0 && !stopped) {
488
- stopped = true;
489
- done = true;
490
- resumeProducer();
491
- try {
492
- const r = iterator.return?.();
493
- if (r && typeof r.then === "function") r.catch(() => {});
494
- } catch {}
495
- wakeAll();
496
- }
497
- if (active === 0) {
498
- buffer.length = 0;
499
- sizes.length = 0;
500
- windowBytes = 0;
501
- } else trim();
502
- };
503
- const serve = (me, p) => {
504
- const i = cursors[me];
505
- if (i === Infinity) return p.resolve(doneResult);
506
- if (i < base + buffer.length) {
507
- const value = buffer[i - base];
508
- cursors[me] = i + 1;
509
- trim();
510
- return p.resolve({
511
- done: false,
512
- value
513
- });
514
- }
515
- if (failed) return p.reject(sourceError);
516
- if (done) {
517
- closeBranch(me);
518
- return p.resolve(doneResult);
519
- }
520
- pending[me] = p;
521
- };
522
- const wakeAll = () => {
523
- for (let b = 0; b < branches; b++) {
524
- const p = pending[b];
525
- if (!p) continue;
526
- pending[b] = null;
527
- serve(b, p);
528
- }
482
+ const stop = (p) => {
483
+ pending = null;
484
+ p?.resolve(doneResult);
485
+ if (stopped) return;
486
+ stopped = done = true;
487
+ buffer.length = sizes.length = windowBytes = 0;
488
+ resumeProducer();
489
+ try {
490
+ const r = iterator.return?.();
491
+ if (r && typeof r.then === "function") r.catch(() => {});
492
+ } catch {}
493
+ finish.resolve(doneResult);
529
494
  };
530
495
  (async () => {
531
496
  try {
532
497
  while (!stopped) {
533
498
  const result = await iterator.next();
534
499
  if (result.done || stopped) break;
500
+ const p = pending;
501
+ if (p) {
502
+ pending = null;
503
+ p.resolve({
504
+ done: false,
505
+ value: result.value
506
+ });
507
+ continue;
508
+ }
535
509
  buffer.push(result.value);
536
510
  sizes.push(teeChunkCost(result.value));
537
511
  windowBytes += sizes[sizes.length - 1];
538
- wakeAll();
539
- if ((buffer.length >= cap || windowBytes >= capBytes) && active > 0 && !stopped) await new Promise((resolve) => {
512
+ if ((buffer.length >= cap || windowBytes >= capBytes) && !stopped) await new Promise((resolve) => {
540
513
  drainResume = resolve;
541
514
  });
542
515
  }
543
516
  } catch (error) {
544
- failed = true;
517
+ failed = !stopped;
545
518
  sourceError = error;
546
519
  } finally {
547
520
  done = true;
548
- wakeAll();
521
+ const p = pending;
522
+ pending = null;
523
+ if (failed) {
524
+ p?.reject(sourceError);
525
+ finish.reject(sourceError);
526
+ } else {
527
+ p?.resolve(doneResult);
528
+ finish.resolve(doneResult);
529
+ }
549
530
  }
550
531
  })();
551
- const makeBranch = (me) => Object.assign(Object.create(null), {
532
+ const value = Object.assign(Object.create(null), {
552
533
  [Symbol.asyncIterator]() {
553
534
  return this;
554
535
  },
555
536
  next: () => {
556
- const i = cursors[me];
557
- if (i < base + buffer.length) {
558
- const value = buffer[i - base];
559
- cursors[me] = i + 1;
560
- trim();
537
+ if (buffer.length) {
538
+ const value = buffer.shift();
539
+ windowBytes -= sizes.shift();
540
+ if (buffer.length < cap && windowBytes < capBytes) resumeProducer();
561
541
  return Promise.resolve({
562
542
  done: false,
563
543
  value
564
544
  });
565
545
  }
566
- return new Promise((resolve, reject) => serve(me, {
546
+ if (failed) return Promise.reject(sourceError);
547
+ if (done) return Promise.resolve(doneResult);
548
+ return new Promise((resolve, reject) => pending = {
567
549
  resolve,
568
550
  reject
569
- }));
551
+ });
570
552
  },
571
- return: (value) => {
572
- const p = pending[me];
573
- if (p) {
574
- pending[me] = null;
575
- p.resolve(doneResult);
576
- }
577
- closeBranch(me);
553
+ return: (result) => {
554
+ stop(pending);
578
555
  return Promise.resolve({
579
556
  done: true,
580
- value
557
+ value: result
581
558
  });
582
559
  },
583
560
  throw: (error) => {
584
- const p = pending[me];
585
- if (p) {
586
- pending[me] = null;
587
- p.resolve(doneResult);
588
- }
589
- closeBranch(me);
561
+ stop(pending);
590
562
  return Promise.reject(error);
591
563
  }
592
564
  });
593
- const out = Array.from({ length: branches }, (_, b) => makeBranch(b));
594
- if (source.sse === true) out[0].sse = true;
595
- return out;
565
+ const observer = Object.assign(Object.create(null), {
566
+ [Symbol.asyncIterator]() {
567
+ return this;
568
+ },
569
+ next: () => completed,
570
+ return: (result) => Promise.resolve({
571
+ done: true,
572
+ value: result
573
+ }),
574
+ throw: (error) => Promise.reject(error)
575
+ });
576
+ if (source.sse === true) value.sse = true;
577
+ return [value, observer];
596
578
  }
597
579
 
598
580
  //#endregion
package/dist/base.d.ts CHANGED
@@ -5,7 +5,7 @@ import { Context, ErrorContext, LifecycleContext } from "./context.js";
5
5
  import { TraceCapability, TraceHandler } from "./trace.js";
6
6
  import { AnySchema } from "./type/types.js";
7
7
  import { AotFingerprint, CompilerSession, ProgramId } from "./compile/aot.js";
8
- import { AddRoute, AddWSRoute, AfterHandler, AfterResponseHandler, AnyErrorConstructor, AnyLocalHook, BodyHandler, CatchAllEntries, CatchAllHookReturn, CompiledHandler, CreateEden, DefaultEphemeral, DefaultMetadata, DefaultSingleton, DefinitionBase, DeriveResponse, ElysiaConfig, ElysiaHandlerToResponseSchemaAmbiguous, EphemeralType, ErrorDefinitionEntry, ErrorHandler, ErrorHookStatus, EventScope, ExcludeElysiaResponse, GlobalHookReturn, GracefulHandler, GuardHookSingleton, GuardLocalHook, HTTPMethod, HeadersKeyCheck, HistoryEntry, HookContextSchema, HookContextSingleton, InlineHandler, InlineHandlerNonMacro, InputSchema, InputSchemaKey, InstanceHookReturn, InternalRoute, IntersectIfObjectSchema, JoinPath, LegacyScope, LocalHook, LocalHookReturn, Macro, MacroCatchAll, MacroOptionContext, MacroToContext, MacroToProperty, MapResponse, MaybeArray, MaybePromise, MergeElysiaInstances, MergeSchema, MergeScopedSchemas, MetadataBase, ObjectMacroDefs, OptionalHandler, ParentInput, ParentResponse, PluginHookReturn, PreHandler, PreValidationTransform, Prettify, PublicRoute, ResolveUsedRouteErrors, RouteBase, ScopedHookReturn, ScopedMapDeriveReturn, SingletonBase, StaticMapAliases, UnionResponseStatus, UnwrapRoute, UnwrapSchema, UsedRoutesUnder, WithErrorEntries, WithoutMacroCatch, WrapFn } from "./types.js";
8
+ import { AddRoute, AddWSRoute, AfterHandler, AfterResponseHandler, AnyErrorConstructor, AnyLocalHook, BodyHandler, CatchAllEntries, CatchAllHookReturn, CompiledHandler, CreateEden, DefaultEphemeral, DefaultMetadata, DefaultSingleton, DefinitionBase, DeriveResponse, ElysiaConfig, ElysiaHandlerToResponseSchemaAmbiguous, EphemeralType, ErrorDefinitionEntry, ErrorHandler, ErrorHookStatus, EventScope, ExcludeElysiaResponse, GlobalHookReturn, GracefulHandler, GuardHookSingleton, GuardLocalHook, HTTPMethod, HeadersKeyCheck, HistoryEntry, HookContextSchema, HookContextSingleton, InlineHandler, InlineHandlerNonMacro, InputSchema, InputSchemaKey, InstanceHookReturn, InternalRoute, IntersectIfObjectSchema, JoinPath, LegacyScope, LocalHook, LocalHookReturn, Macro, MacroCatchAll, MacroOptionContext, MacroToContext, MacroToProperty, MapResponse, MaybeArray, MaybePromise, MergeElysiaInstances, MergeSchema, MergeScopedSchemas, MetadataBase, ObjectMacroDefs, OptionalHandler, ParentInput, ParentResponse, PluginHookReturn, PreHandler, PreValidationTransform, Prettify, PublicRoute, ResolveUsedRouteErrors, RouteBase, ScopedHookReturn, ScopedMapDeriveReturn, SingletonBase, UnionResponseStatus, UnwrapRoute, UnwrapSchema, UsedRoutesUnder, WithErrorEntries, WithoutMacroCatch, WrapFn } from "./types.js";
9
9
  import { RouteErrorHooks } from "./handler/utils.js";
10
10
  import { WSCapability, WSHandlerResponse, WSLocalHook, WSMessageHandler, WSOptions, WSOptionsEntry } from "./ws/types.js";
11
11
  import { RouteTable } from "./route-table.js";
@@ -103,7 +103,6 @@ declare class Elysia<const in out BasePath extends string = '', const in out Sco
103
103
  private jitColdRemaining?;
104
104
  private jitTable?;
105
105
  private jitRoute?;
106
- private jitAliases?;
107
106
  '~router'?: Memoirist<CompiledHandler | number>;
108
107
  '~map'?: {
109
108
  [method: string]: {
@@ -1044,7 +1043,7 @@ declare class Elysia<const in out BasePath extends string = '', const in out Sco
1044
1043
  /** @internal Run a route build under the cookie config of route `index` */
1045
1044
  ['~withRouteCookie']<Args extends unknown[], Result>(index: number, build: (...args: Args) => Result, ...args: Args): Result;
1046
1045
  /** @internal */
1047
- handler(index: number, immediate?: boolean, route?: InternalRoute, aliases?: StaticMapAliases, table?: RouteTable): CompiledHandler;
1046
+ handler(index: number, immediate?: boolean, route?: InternalRoute, table?: RouteTable): CompiledHandler;
1048
1047
  '~dispatch'(index: number, context: any): MaybePromise<Response>;
1049
1048
  private fetchFn?;
1050
1049
  get fetch(): (request: Request) => MaybePromise<Response>;