elysia 2.0.0-beta.2 → 2.0.0-beta.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 (175) hide show
  1. package/dist/adapter/bun/index.d.ts +1 -1
  2. package/dist/adapter/bun/index.js +320 -56
  3. package/dist/adapter/bun/index.mjs +321 -57
  4. package/dist/adapter/utils.js +51 -30
  5. package/dist/adapter/utils.mjs +52 -31
  6. package/dist/adapter/web-standard/handler.js +5 -2
  7. package/dist/adapter/web-standard/handler.mjs +5 -2
  8. package/dist/base.d.ts +18 -7
  9. package/dist/base.js +213 -101
  10. package/dist/base.mjs +215 -103
  11. package/dist/compile/aot-capture.js +1 -1
  12. package/dist/compile/aot-capture.mjs +1 -1
  13. package/dist/compile/aot.d.ts +5 -4
  14. package/dist/compile/aot.js +17 -9
  15. package/dist/compile/aot.mjs +18 -9
  16. package/dist/compile/handler/descriptor.js +1 -1
  17. package/dist/compile/handler/descriptor.mjs +1 -1
  18. package/dist/compile/handler/frozen-validator.d.ts +4 -1
  19. package/dist/compile/handler/frozen-validator.js +8 -3
  20. package/dist/compile/handler/frozen-validator.mjs +5 -3
  21. package/dist/compile/handler/index.d.ts +2 -2
  22. package/dist/compile/handler/index.js +8 -6
  23. package/dist/compile/handler/index.mjs +8 -6
  24. package/dist/compile/handler/reconstruct.d.ts +1 -1
  25. package/dist/compile/handler/reconstruct.js +4 -3
  26. package/dist/compile/handler/reconstruct.mjs +4 -3
  27. package/dist/compile/handler/utils.js +2 -2
  28. package/dist/compile/handler/utils.mjs +3 -3
  29. package/dist/compile/jit-probe.d.ts +2 -2
  30. package/dist/context.d.ts +1 -1
  31. package/dist/context.js +8 -5
  32. package/dist/context.mjs +8 -5
  33. package/dist/cookie/config.d.ts +1 -0
  34. package/dist/cookie/config.js +3 -2
  35. package/dist/cookie/config.mjs +3 -2
  36. package/dist/cookie/cookie.d.ts +1 -0
  37. package/dist/cookie/cookie.js +6 -5
  38. package/dist/cookie/cookie.mjs +6 -5
  39. package/dist/cookie/crypto.d.ts +8 -7
  40. package/dist/cookie/crypto.js +38 -31
  41. package/dist/cookie/crypto.mjs +38 -32
  42. package/dist/cookie/error.js +1 -2
  43. package/dist/cookie/error.mjs +1 -2
  44. package/dist/cookie/lib.js +5 -1
  45. package/dist/cookie/lib.mjs +5 -1
  46. package/dist/cookie/serialize.js +1 -1
  47. package/dist/cookie/serialize.mjs +1 -1
  48. package/dist/cookie/types.d.ts +10 -0
  49. package/dist/cookie/utils.js +28 -14
  50. package/dist/cookie/utils.mjs +29 -15
  51. package/dist/handler/error.d.ts +23 -12
  52. package/dist/handler/error.js +24 -21
  53. package/dist/handler/error.mjs +20 -21
  54. package/dist/handler/fetch.js +6 -7
  55. package/dist/handler/fetch.mjs +7 -8
  56. package/dist/handler/utils.js +4 -2
  57. package/dist/handler/utils.mjs +5 -3
  58. package/dist/index.js +1 -8
  59. package/dist/package.js +1 -1
  60. package/dist/package.mjs +1 -1
  61. package/dist/plugin/aot/core.d.ts +33 -6
  62. package/dist/plugin/aot/core.js +120 -40
  63. package/dist/plugin/aot/core.mjs +121 -43
  64. package/dist/plugin/aot/esbuild.js +8 -2
  65. package/dist/plugin/aot/esbuild.mjs +9 -3
  66. package/dist/plugin/aot/hooks.d.ts +2 -1
  67. package/dist/plugin/aot/hooks.js +3 -3
  68. package/dist/plugin/aot/hooks.mjs +4 -4
  69. package/dist/plugin/aot/source.d.ts +4 -1
  70. package/dist/plugin/aot/source.js +21 -14
  71. package/dist/plugin/aot/source.mjs +21 -14
  72. package/dist/plugin/aot/worker.js +2 -2
  73. package/dist/plugin/aot/worker.mjs +2 -2
  74. package/dist/route-table.d.ts +3 -0
  75. package/dist/route-table.js +16 -11
  76. package/dist/route-table.mjs +16 -11
  77. package/dist/schema-snapshot.js +17 -22
  78. package/dist/schema-snapshot.mjs +17 -22
  79. package/dist/type/bridge-live.d.ts +1 -1
  80. package/dist/type/bridge-live.js +2 -2
  81. package/dist/type/bridge-live.mjs +2 -2
  82. package/dist/type/bridge.d.ts +1 -1
  83. package/dist/type/coerce-plan.js +3 -4
  84. package/dist/type/coerce-plan.mjs +1 -1
  85. package/dist/type/coerce.d.ts +1 -1
  86. package/dist/type/compat.d.ts +3 -0
  87. package/dist/type/compat.js +32 -14
  88. package/dist/type/compat.mjs +27 -8
  89. package/dist/type/constants.d.ts +1 -1
  90. package/dist/type/elysia/array-buffer.js +2 -3
  91. package/dist/type/elysia/array-buffer.mjs +1 -1
  92. package/dist/type/elysia/array-string.js +7 -4
  93. package/dist/type/elysia/array-string.mjs +9 -5
  94. package/dist/type/elysia/boolean-string.js +2 -3
  95. package/dist/type/elysia/boolean-string.mjs +1 -1
  96. package/dist/type/elysia/boolean.js +4 -3
  97. package/dist/type/elysia/boolean.mjs +4 -2
  98. package/dist/type/elysia/cookie.js +2 -1
  99. package/dist/type/elysia/cookie.mjs +2 -1
  100. package/dist/type/elysia/date.js +7 -7
  101. package/dist/type/elysia/date.mjs +3 -2
  102. package/dist/type/elysia/file-type.d.ts +9 -2
  103. package/dist/type/elysia/file-type.js +16 -1
  104. package/dist/type/elysia/file-type.mjs +16 -2
  105. package/dist/type/elysia/file.js +8 -4
  106. package/dist/type/elysia/file.mjs +8 -3
  107. package/dist/type/elysia/files.js +6 -4
  108. package/dist/type/elysia/files.mjs +5 -2
  109. package/dist/type/elysia/form.js +2 -3
  110. package/dist/type/elysia/form.mjs +3 -3
  111. package/dist/type/elysia/integer-string.js +4 -5
  112. package/dist/type/elysia/integer-string.mjs +3 -3
  113. package/dist/type/elysia/integer.js +4 -3
  114. package/dist/type/elysia/integer.mjs +4 -2
  115. package/dist/type/elysia/maybe-empty.js +3 -4
  116. package/dist/type/elysia/maybe-empty.mjs +1 -1
  117. package/dist/type/elysia/nullable.js +2 -3
  118. package/dist/type/elysia/nullable.mjs +1 -1
  119. package/dist/type/elysia/number.js +4 -3
  120. package/dist/type/elysia/number.mjs +4 -2
  121. package/dist/type/elysia/numeric-enum.js +2 -3
  122. package/dist/type/elysia/numeric-enum.mjs +1 -1
  123. package/dist/type/elysia/numeric.js +5 -5
  124. package/dist/type/elysia/numeric.mjs +3 -2
  125. package/dist/type/elysia/object-string.js +7 -4
  126. package/dist/type/elysia/object-string.mjs +9 -5
  127. package/dist/type/elysia/string.js +4 -3
  128. package/dist/type/elysia/string.mjs +4 -2
  129. package/dist/type/elysia/uint8-array.js +2 -3
  130. package/dist/type/elysia/uint8-array.mjs +1 -1
  131. package/dist/type/elysia/union-enum.js +2 -3
  132. package/dist/type/elysia/union-enum.mjs +1 -1
  133. package/dist/type/elysia/utils.js +2 -3
  134. package/dist/type/elysia/utils.mjs +1 -1
  135. package/dist/type/exports.js +10 -1
  136. package/dist/type/exports.mjs +7 -0
  137. package/dist/type/index.d.ts +9 -1
  138. package/dist/type/index.js +41 -13
  139. package/dist/type/index.mjs +44 -9
  140. package/dist/type/typebox-type-live.d.ts +9 -0
  141. package/dist/type/typebox-type-live.js +31 -0
  142. package/dist/type/typebox-type-live.mjs +16 -0
  143. package/dist/type/typebox-type.d.ts +24 -0
  144. package/dist/type/typebox-type.js +130 -0
  145. package/dist/type/typebox-type.mjs +66 -0
  146. package/dist/type/typebox-value-live.d.ts +9 -0
  147. package/dist/type/typebox-value-live.js +27 -0
  148. package/dist/type/typebox-value-live.mjs +11 -0
  149. package/dist/type/typebox-value.d.ts +30 -0
  150. package/dist/type/typebox-value.js +147 -0
  151. package/dist/type/typebox-value.mjs +62 -0
  152. package/dist/type/validator/custom-error.js +4 -5
  153. package/dist/type/validator/custom-error.mjs +3 -3
  154. package/dist/type/validator/default-precompute.js +4 -5
  155. package/dist/type/validator/default-precompute.mjs +1 -1
  156. package/dist/type/validator/index.d.ts +1 -1
  157. package/dist/type/validator/index.js +51 -42
  158. package/dist/type/validator/index.mjs +33 -23
  159. package/dist/type/validator/validator-cache.d.ts +1 -1
  160. package/dist/type/validator/validator-cache.js +27 -14
  161. package/dist/type/validator/validator-cache.mjs +27 -13
  162. package/dist/universal/constants.d.ts +2 -1
  163. package/dist/universal/constants.js +3 -1
  164. package/dist/universal/constants.mjs +2 -1
  165. package/dist/utils.d.ts +19 -1
  166. package/dist/utils.js +25 -3
  167. package/dist/utils.mjs +19 -3
  168. package/dist/validator/index.js +2 -1
  169. package/dist/validator/index.mjs +2 -1
  170. package/dist/ws/context.d.ts +8 -1
  171. package/dist/ws/context.js +65 -29
  172. package/dist/ws/context.mjs +64 -29
  173. package/dist/ws/route.js +176 -28
  174. package/dist/ws/route.mjs +179 -31
  175. package/package.json +5 -3
@@ -8,6 +8,19 @@ function pickValidator(validators, defaultValidator, data) {
8
8
  if (data instanceof require_error.ElysiaStatus) return validators[data.status];
9
9
  return defaultValidator;
10
10
  }
11
+ function trackWSSettling(data, result, onSettled) {
12
+ if (!(result instanceof Promise)) return;
13
+ data.settling = (data.settling ?? 0) + 1;
14
+ const release = () => {
15
+ data.settling--;
16
+ onSettled?.();
17
+ };
18
+ result.then(release, release);
19
+ }
20
+ const runWSLifecycle = (data, callback, ...args) => {
21
+ const lifecycle = data["~lifecycleRun"];
22
+ return lifecycle ? lifecycle(callback, ...args) : callback(...args);
23
+ };
11
24
  function memoize(view, key, value) {
12
25
  const self = view.raw.data.elysia ?? view;
13
26
  Object.defineProperty(self, key, {
@@ -24,6 +37,15 @@ var ElysiaWS = class {
24
37
  this.raw = raw;
25
38
  if (context) for (const key in context) {
26
39
  if (key === "ws" || key === "body") continue;
40
+ if (key === "__proto__") {
41
+ Object.defineProperty(this, key, {
42
+ value: context[key],
43
+ writable: true,
44
+ enumerable: true,
45
+ configurable: true
46
+ });
47
+ continue;
48
+ }
27
49
  this[key] = context[key];
28
50
  }
29
51
  }
@@ -62,58 +84,71 @@ var ElysiaWS = class {
62
84
  get data() {
63
85
  return this.raw.data;
64
86
  }
65
- #prepare(data) {
66
- if (data === void 0) return null;
67
- if (data instanceof require_error.ElysiaStatus) return JSON.stringify({
68
- status: data.status,
69
- error: data.response
70
- });
71
- if (typeof data === "object") return JSON.stringify(data);
72
- return data;
73
- }
74
- #validatedOrError(data) {
87
+ /**
88
+ * Validate, redact and serialize an outbound value, or return the
89
+ * `ValidationError` the payload was refused with.
90
+ */
91
+ #frame(data) {
75
92
  const connectionData = this.raw.data;
76
93
  const v = pickValidator(connectionData?.validator, connectionData?.defaultValidator, data);
77
- if (!v) return;
78
- const value = data instanceof require_error.ElysiaStatus ? data.response : data;
79
- if (v.Check(value)) return;
80
- return new require_error.ValidationError("message", value, v.Errors(value)).message;
94
+ const status = data instanceof require_error.ElysiaStatus ? data : void 0;
95
+ let value = status ? status.response : data;
96
+ if (v) {
97
+ const encode = v.EncodeFrom;
98
+ let redacted;
99
+ if (typeof encode === "function" && !v.hasCodec && !v.mayReturnPromise) try {
100
+ redacted = encode.call(v, value, "message");
101
+ } catch {}
102
+ if (redacted !== void 0) value = redacted;
103
+ else if (!v.Check(value)) return new require_error.ValidationError("message", value, v.Errors(value));
104
+ }
105
+ if (status) return JSON.stringify({
106
+ status: status.status,
107
+ error: value
108
+ });
109
+ if (value === void 0) return null;
110
+ if (typeof value === "object") return JSON.stringify(value);
111
+ return value;
81
112
  }
82
113
  #send(data, compress) {
83
114
  if (data === void 0) return 0;
84
115
  if (data instanceof ArrayBuffer || ArrayBuffer.isView(data)) return this.raw.send(data, compress);
85
- const err = this.#validatedOrError(data);
86
- if (err !== void 0) return this.raw.send(err);
87
- return this.raw.send(this.#prepare(data), compress);
116
+ const frame = this.#frame(data);
117
+ if (frame instanceof require_error.ValidationError) return this.raw.send(frame.message);
118
+ return this.raw.send(frame, compress);
88
119
  }
89
120
  #ping(data) {
90
121
  if (data === void 0) return this.raw.ping();
91
122
  if (data instanceof ArrayBuffer || ArrayBuffer.isView(data)) return this.raw.ping(data);
92
- const err = this.#validatedOrError(data);
93
- if (err !== void 0) return this.raw.send(err);
94
- return this.raw.ping(this.#prepare(data));
123
+ const frame = this.#frame(data);
124
+ if (frame instanceof require_error.ValidationError) return this.raw.send(frame.message);
125
+ return this.raw.ping(frame);
95
126
  }
96
127
  #pong(data) {
97
128
  if (data === void 0) return this.raw.pong();
98
129
  if (data instanceof ArrayBuffer || ArrayBuffer.isView(data)) return this.raw.pong(data);
99
- const err = this.#validatedOrError(data);
100
- if (err !== void 0) return this.raw.send(err);
101
- return this.raw.pong(this.#prepare(data));
130
+ const frame = this.#frame(data);
131
+ if (frame instanceof require_error.ValidationError) return this.raw.send(frame.message);
132
+ return this.raw.pong(frame);
102
133
  }
103
134
  #publish(topic, data, compress) {
104
135
  if (data === void 0) return 0;
105
136
  if (data instanceof ArrayBuffer || ArrayBuffer.isView(data)) return this.raw.publish(topic, data, compress);
106
- const err = this.#validatedOrError(data);
107
- if (err !== void 0) return this.raw.send(err);
108
- return this.raw.publish(topic, this.#prepare(data), compress);
137
+ const frame = this.#frame(data);
138
+ if (frame instanceof require_error.ValidationError) return this.raw.send(frame.message);
139
+ return this.raw.publish(topic, frame, compress);
109
140
  }
110
141
  #close(code, reason) {
111
142
  const data = this.raw.data;
112
143
  if (!data.closeHandlerInvoked && data.close) {
113
144
  data.closeHandlerInvoked = true;
114
145
  try {
115
- const result = data.close(this, code ?? 1e3, reason ?? "");
116
- if (result instanceof Promise) result.then(() => this.raw.close(code, reason)).catch(() => this.raw.close(code, reason));
146
+ const result = runWSLifecycle(data, data.close, this, code ?? 1e3, reason ?? "");
147
+ if (result instanceof Promise) {
148
+ const closing = result.catch(() => {});
149
+ trackWSSettling(data, closing);
150
+ closing.then(() => this.raw.close(code, reason)).catch(() => this.raw.close(code, reason));
151
+ }
117
152
  return;
118
153
  } catch {}
119
154
  }
@@ -149,4 +184,5 @@ function isGeneratorObject(value) {
149
184
 
150
185
  //#endregion
151
186
  exports.ElysiaWS = ElysiaWS;
152
- exports.isGeneratorObject = isGeneratorObject;
187
+ exports.isGeneratorObject = isGeneratorObject;
188
+ exports.trackWSSettling = trackWSSettling;
@@ -7,6 +7,19 @@ function pickValidator(validators, defaultValidator, data) {
7
7
  if (data instanceof ElysiaStatus) return validators[data.status];
8
8
  return defaultValidator;
9
9
  }
10
+ function trackWSSettling(data, result, onSettled) {
11
+ if (!(result instanceof Promise)) return;
12
+ data.settling = (data.settling ?? 0) + 1;
13
+ const release = () => {
14
+ data.settling--;
15
+ onSettled?.();
16
+ };
17
+ result.then(release, release);
18
+ }
19
+ const runWSLifecycle = (data, callback, ...args) => {
20
+ const lifecycle = data["~lifecycleRun"];
21
+ return lifecycle ? lifecycle(callback, ...args) : callback(...args);
22
+ };
10
23
  function memoize(view, key, value) {
11
24
  const self = view.raw.data.elysia ?? view;
12
25
  Object.defineProperty(self, key, {
@@ -23,6 +36,15 @@ var ElysiaWS = class {
23
36
  this.raw = raw;
24
37
  if (context) for (const key in context) {
25
38
  if (key === "ws" || key === "body") continue;
39
+ if (key === "__proto__") {
40
+ Object.defineProperty(this, key, {
41
+ value: context[key],
42
+ writable: true,
43
+ enumerable: true,
44
+ configurable: true
45
+ });
46
+ continue;
47
+ }
26
48
  this[key] = context[key];
27
49
  }
28
50
  }
@@ -61,58 +83,71 @@ var ElysiaWS = class {
61
83
  get data() {
62
84
  return this.raw.data;
63
85
  }
64
- #prepare(data) {
65
- if (data === void 0) return null;
66
- if (data instanceof ElysiaStatus) return JSON.stringify({
67
- status: data.status,
68
- error: data.response
69
- });
70
- if (typeof data === "object") return JSON.stringify(data);
71
- return data;
72
- }
73
- #validatedOrError(data) {
86
+ /**
87
+ * Validate, redact and serialize an outbound value, or return the
88
+ * `ValidationError` the payload was refused with.
89
+ */
90
+ #frame(data) {
74
91
  const connectionData = this.raw.data;
75
92
  const v = pickValidator(connectionData?.validator, connectionData?.defaultValidator, data);
76
- if (!v) return;
77
- const value = data instanceof ElysiaStatus ? data.response : data;
78
- if (v.Check(value)) return;
79
- return new ValidationError("message", value, v.Errors(value)).message;
93
+ const status = data instanceof ElysiaStatus ? data : void 0;
94
+ let value = status ? status.response : data;
95
+ if (v) {
96
+ const encode = v.EncodeFrom;
97
+ let redacted;
98
+ if (typeof encode === "function" && !v.hasCodec && !v.mayReturnPromise) try {
99
+ redacted = encode.call(v, value, "message");
100
+ } catch {}
101
+ if (redacted !== void 0) value = redacted;
102
+ else if (!v.Check(value)) return new ValidationError("message", value, v.Errors(value));
103
+ }
104
+ if (status) return JSON.stringify({
105
+ status: status.status,
106
+ error: value
107
+ });
108
+ if (value === void 0) return null;
109
+ if (typeof value === "object") return JSON.stringify(value);
110
+ return value;
80
111
  }
81
112
  #send(data, compress) {
82
113
  if (data === void 0) return 0;
83
114
  if (data instanceof ArrayBuffer || ArrayBuffer.isView(data)) return this.raw.send(data, compress);
84
- const err = this.#validatedOrError(data);
85
- if (err !== void 0) return this.raw.send(err);
86
- return this.raw.send(this.#prepare(data), compress);
115
+ const frame = this.#frame(data);
116
+ if (frame instanceof ValidationError) return this.raw.send(frame.message);
117
+ return this.raw.send(frame, compress);
87
118
  }
88
119
  #ping(data) {
89
120
  if (data === void 0) return this.raw.ping();
90
121
  if (data instanceof ArrayBuffer || ArrayBuffer.isView(data)) return this.raw.ping(data);
91
- const err = this.#validatedOrError(data);
92
- if (err !== void 0) return this.raw.send(err);
93
- return this.raw.ping(this.#prepare(data));
122
+ const frame = this.#frame(data);
123
+ if (frame instanceof ValidationError) return this.raw.send(frame.message);
124
+ return this.raw.ping(frame);
94
125
  }
95
126
  #pong(data) {
96
127
  if (data === void 0) return this.raw.pong();
97
128
  if (data instanceof ArrayBuffer || ArrayBuffer.isView(data)) return this.raw.pong(data);
98
- const err = this.#validatedOrError(data);
99
- if (err !== void 0) return this.raw.send(err);
100
- return this.raw.pong(this.#prepare(data));
129
+ const frame = this.#frame(data);
130
+ if (frame instanceof ValidationError) return this.raw.send(frame.message);
131
+ return this.raw.pong(frame);
101
132
  }
102
133
  #publish(topic, data, compress) {
103
134
  if (data === void 0) return 0;
104
135
  if (data instanceof ArrayBuffer || ArrayBuffer.isView(data)) return this.raw.publish(topic, data, compress);
105
- const err = this.#validatedOrError(data);
106
- if (err !== void 0) return this.raw.send(err);
107
- return this.raw.publish(topic, this.#prepare(data), compress);
136
+ const frame = this.#frame(data);
137
+ if (frame instanceof ValidationError) return this.raw.send(frame.message);
138
+ return this.raw.publish(topic, frame, compress);
108
139
  }
109
140
  #close(code, reason) {
110
141
  const data = this.raw.data;
111
142
  if (!data.closeHandlerInvoked && data.close) {
112
143
  data.closeHandlerInvoked = true;
113
144
  try {
114
- const result = data.close(this, code ?? 1e3, reason ?? "");
115
- if (result instanceof Promise) result.then(() => this.raw.close(code, reason)).catch(() => this.raw.close(code, reason));
145
+ const result = runWSLifecycle(data, data.close, this, code ?? 1e3, reason ?? "");
146
+ if (result instanceof Promise) {
147
+ const closing = result.catch(() => {});
148
+ trackWSSettling(data, closing);
149
+ closing.then(() => this.raw.close(code, reason)).catch(() => this.raw.close(code, reason));
150
+ }
116
151
  return;
117
152
  } catch {}
118
153
  }
@@ -147,4 +182,4 @@ function isGeneratorObject(value) {
147
182
  }
148
183
 
149
184
  //#endregion
150
- export { ElysiaWS, isGeneratorObject };
185
+ export { ElysiaWS, isGeneratorObject, trackWSSettling };
package/dist/ws/route.js CHANGED
@@ -2,11 +2,12 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
2
  const require_universal_constants = require('../universal/constants.js');
3
3
  const require_utils = require('../utils.js');
4
4
  const require_type_bridge = require('../type/bridge.js');
5
- const require_universal_is_production = require('../universal/is-production.js');
6
5
  const require_error = require('../error.js');
7
6
  const require_adapter_web_standard_handler = require('../adapter/web-standard/handler.js');
8
7
  const require_parse_query = require('../parse-query.js');
9
8
  const require_generation = require('../generation.js');
9
+ const require_cookie_config = require('../cookie/config.js');
10
+ const require_cookie_utils = require('../cookie/utils.js');
10
11
  const require_handler_error = require('../handler/error.js');
11
12
  const require_compile_handler_utils = require('../compile/handler/utils.js');
12
13
  const require_validator_index = require('../validator/index.js');
@@ -181,9 +182,45 @@ function wsErrorFrame(error) {
181
182
  }
182
183
  return wsErrorFrameFallback(error);
183
184
  }
185
+ /**
186
+ * Frame served by an error that claims an RFC 9457 problem document, mirroring
187
+ * the HTTP tiers in `handler/error.ts`: `value` replaces the whole frame,
188
+ * `detail` fills the `detail` member, the message is the last resort.
189
+ *
190
+ * A frame carries no headers, so a `headers` annotation is ignored outright
191
+ * rather than half-applied
192
+ */
193
+ function wsProblemFrame(error) {
194
+ const status = require_handler_error.resolveStatus(error.status);
195
+ const served = typeof status === "number" && status >= 100 ? status : 500;
196
+ const stringify = (value) => typeof value === "object" && value !== null ? JSON.stringify(value) : String(value);
197
+ const problemFrame = (detail) => JSON.stringify(require_error.problemBody({
198
+ type: error.type ?? "about:blank",
199
+ detail,
200
+ status: served
201
+ }));
202
+ const serveMessage = () => problemFrame(require_handler_error.statusFallbackBody(error, served));
203
+ const serveDetail = () => {
204
+ const detail = require_handler_error.readAnnotation(error, "detail", true);
205
+ if (detail === void 0) return serveMessage();
206
+ if (detail instanceof Promise) return detail.then((resolved) => resolved === void 0 ? serveMessage() : problemFrame(resolved)).catch(() => wsLegacyFrame(error));
207
+ return problemFrame(detail);
208
+ };
209
+ const value = require_handler_error.readAnnotation(error, "value", true);
210
+ if (value === void 0) return serveDetail();
211
+ if (value instanceof Promise) return value.then((resolved) => resolved === void 0 ? serveDetail() : stringify(resolved)).catch(() => wsLegacyFrame(error));
212
+ return stringify(value);
213
+ }
184
214
  function wsErrorFrameFallback(error) {
215
+ if (require_handler_error.claimsProblemType(error)) try {
216
+ return wsProblemFrame(error);
217
+ } catch {}
218
+ return wsLegacyFrame(error);
219
+ }
220
+ /** Frame an error that never self-described has always served */
221
+ function wsLegacyFrame(error) {
185
222
  if (error?.status) {
186
- const body = error.response !== void 0 ? error.response : require_universal_is_production.isProduction() ? error.status >= 500 ? "Internal Server Error" : "" : error.message ?? "";
223
+ const body = require_handler_error.statusFallbackBody(error, error.status);
187
224
  return typeof body === "object" ? JSON.stringify(body) : String(body);
188
225
  }
189
226
  return require_error.internalServerErrorBodyString(error);
@@ -218,13 +255,20 @@ const wsOptions = [
218
255
  ];
219
256
  function buildWSRoute(route, app) {
220
257
  const hook = require_compile_handler_index.resolveWSLocalHook(require_compile_handler_index.localMacroRoot(route[7] ?? route[3] ?? app, app), route[4], app) ?? require_utils.nullObject();
258
+ const instance = route[3] ?? app;
259
+ const appHookChain = route[5];
260
+ const inheritedChain = route[6];
261
+ const composed = require_compile_handler_index.composeRouteHook(instance, route[4], appHookChain, inheritedChain, app, route[7]) ?? hook;
221
262
  let validators;
222
- const frozenEager = require_type_bridge.isBridgeLive() ? void 0 : require_compile_handler_frozen_validator.buildFrozenRouteValidator(hook, app, "WS", route[1]);
263
+ const frozenEager = require_type_bridge.isBridgeLive() ? void 0 : require_compile_handler_frozen_validator.buildFrozenRouteValidator(composed, app, "WS", route[1]);
223
264
  if (frozenEager) validators = frozenEager;
224
265
  else try {
225
- validators = new require_validator_route.RouteValidator(hook, {
266
+ validators = new require_validator_route.RouteValidator(composed, {
226
267
  models: require_generation.frozenRootOf(app)["~ext"]?.models,
227
268
  app,
269
+ normalize: require_generation.frozenRootOf(app)["~config"]?.normalize,
270
+ sanitize: require_generation.frozenRootOf(app)["~config"]?.sanitize,
271
+ schemas: composed.schemas,
228
272
  aot: {
229
273
  method: "WS",
230
274
  path: route[1]
@@ -233,18 +277,17 @@ function buildWSRoute(route, app) {
233
277
  });
234
278
  } catch (error) {
235
279
  if (!require_compile_handler_frozen_validator.isBridgeNotInitialized(error)) throw error;
236
- const frozen = require_compile_handler_frozen_validator.buildFrozenRouteValidator(hook, app, "WS", route[1]);
280
+ const frozen = require_compile_handler_frozen_validator.buildFrozenRouteValidator(composed, app, "WS", route[1]);
237
281
  if (!frozen) throw error;
238
282
  validators = frozen;
239
283
  }
240
284
  const responseValidator = validators.response;
241
285
  const defaultResponseValidator = responseValidator ? responseValidator[200] ?? responseValidator[Object.keys(responseValidator)[0]] : void 0;
286
+ const cookieConfig = validators.cookie ? require_cookie_config.compileCookieConfig(composed.cookie, require_generation.frozenRootOf(app)["~config"]?.cookie) : void 0;
287
+ const cookieIsOptional = !!composed.cookie?.["~optional"];
242
288
  const queryChannels = require_compile_handler_utils.getQueryParseChannels(validators.query?.schema);
243
289
  const queryArray = queryChannels?.array;
244
290
  const queryObject = queryChannels?.object;
245
- const instance = route[3] ?? app;
246
- const appHookChain = route[5];
247
- const inheritedChain = route[6];
248
291
  const flatAppHook = require_compile_handler_index.composeRouteHook(instance, void 0, appHookChain, inheritedChain, app, route[7]) ?? {};
249
292
  const parseHooks = hook.parse == null ? [] : Array.isArray(hook.parse) ? hook.parse : [hook.parse];
250
293
  const transforms = concatHooks(flatAppHook.transform, hook.transform);
@@ -272,6 +315,7 @@ function buildWSRoute(route, app) {
272
315
  break;
273
316
  }
274
317
  if (r !== void 0) {
318
+ r = require_handler_error.adoptErrorType(r, error);
275
319
  try {
276
320
  await handleWSResponse(ws, r, mapResponses);
277
321
  } catch {}
@@ -419,6 +463,15 @@ function buildWSRoute(route, app) {
419
463
  if (r instanceof Promise) r = await r;
420
464
  context.headers = r;
421
465
  }
466
+ if (cookieConfig) {
467
+ const raw = await require_cookie_utils.parseCookieRaw(request.headers.get("cookie"), cookieConfig);
468
+ let r = raw;
469
+ if (!cookieIsOptional || Object.keys(raw).length) {
470
+ r = validateUpgradeChannel(validators.cookie, raw, "cookie");
471
+ if (r instanceof Promise) r = await r;
472
+ }
473
+ context.cookie = require_cookie_utils.buildCookieJar(context.set, r, cookieConfig);
474
+ }
422
475
  for (let i = 0; i < transforms.length; i++) {
423
476
  const r = transforms[i](context);
424
477
  if (r instanceof Promise) await r;
@@ -430,7 +483,7 @@ function buildWSRoute(route, app) {
430
483
  const deriveMode = upgradeDeriveModes?.[i];
431
484
  if (deriveMode !== void 0 && !(r instanceof require_error.ElysiaStatus)) {
432
485
  if (r && typeof r === "object") if (deriveMode) context = require_compile_handler_utils.replaceDeriveContext(context, r);
433
- else Object.assign(context, r);
486
+ else require_utils.assignOwn(context, r);
434
487
  } else if (r !== void 0) {
435
488
  if (r instanceof Response) return r;
436
489
  return require_adapter_web_standard_handler.mapResponse(r, context.set, context.request);
@@ -485,23 +538,40 @@ function resolveWSOptions(entries) {
485
538
  const incoming = entry.value[key];
486
539
  if (key in result && result[key] !== incoming && !warned?.has(key)) {
487
540
  (warned ??= /* @__PURE__ */ new Set()).add(key);
488
- console.warn(`[Elysia] Conflicting WebSocket option '${key}' across .use(websocket()) registrations; the shallowest (nearest-root) registration wins.`);
541
+ console.warn(`[Elysia] Conflicting WebSocket option '${key}' across .use(websocket()). Using nearest-root registration.`);
489
542
  }
490
543
  result[key] = incoming;
491
544
  }
492
545
  return result;
493
546
  }
547
+ const wsLimits = [
548
+ "maxPayloadLength",
549
+ "backpressureLimit",
550
+ "idleTimeout"
551
+ ];
552
+ const routeOwnedKeys = /* @__PURE__ */ new WeakMap();
494
553
  function accumulateWSOptions(target, routeOptions, _path) {
495
554
  if (!require_utils.isNotEmpty(routeOptions)) return;
496
- if (require_universal_constants.isBun) {
497
- for (const key in routeOptions) if (key in target && target[key] !== routeOptions[key]) {
498
- console.warn(`[Elysia] Conflicting per-route WebSocket option '${key}'\nBun uses one global WebSocket config per server, per-route values are not enforced (the last-registered route wins).`);
499
- console.warn((/* @__PURE__ */ new Error()).stack);
555
+ let owned = routeOwnedKeys.get(target);
556
+ if (!owned) routeOwnedKeys.set(target, owned = /* @__PURE__ */ new Set());
557
+ for (const key in routeOptions) {
558
+ const incoming = routeOptions[key];
559
+ const current = target[key];
560
+ if (key in target && current !== incoming) {
561
+ if (require_universal_constants.isBun) {
562
+ console.warn(`[Elysia] Conflicting per-route WebSocket option '${key}'\nBun uses one global WebSocket config per server, per-route values are not enforced (for limits the strictest route wins, otherwise the last-registered route).`);
563
+ console.warn((/* @__PURE__ */ new Error()).stack);
564
+ }
565
+ if (owned.has(key) && typeof current === "number" && typeof incoming === "number" && current < incoming && wsLimits.includes(key)) continue;
500
566
  }
567
+ owned.add(key);
568
+ target[key] = incoming;
501
569
  }
502
- Object.assign(target, routeOptions);
503
570
  }
571
+ const MAX_INFLIGHT_MESSAGES = 256;
504
572
  function buildGlobalWSHandler() {
573
+ let lifecycle;
574
+ const runLifecycle = (callback, ...args) => lifecycle ? lifecycle.run(callback, ...args) : callback(...args);
505
575
  function getElysia(ws) {
506
576
  let elysia = ws.data.elysia;
507
577
  if (!elysia) {
@@ -516,39 +586,117 @@ function buildGlobalWSHandler() {
516
586
  ws.send(typeof error?.message === "string" ? error.message : "Internal Server Error");
517
587
  } catch {}
518
588
  };
519
- return {
589
+ const releaseLifecycle = (ws) => {
590
+ if (!lifecycle?.closing && require_utils.isSocketQuiet(ws)) {
591
+ delete ws.data["~lifecycleRun"];
592
+ lifecycle?.sockets.delete(ws);
593
+ }
594
+ };
595
+ const release = (ws) => {
596
+ const data = ws.data;
597
+ const inflight = data.inflight;
598
+ if (inflight) {
599
+ data.inflight = inflight - 1;
600
+ if (inflight === 1 && data.closeHandlerInvoked) releaseLifecycle(ws);
601
+ }
602
+ };
603
+ function dispatch(ws, message) {
604
+ let result;
605
+ try {
606
+ result = ws.data.message(getElysia(ws), message);
607
+ } catch (error) {
608
+ release(ws);
609
+ sendErrMsg(ws, error);
610
+ return;
611
+ }
612
+ if (result instanceof Promise) result.then(() => release(ws), (error) => {
613
+ release(ws);
614
+ sendErrMsg(ws, error);
615
+ });
616
+ else release(ws);
617
+ }
618
+ const handler = {
520
619
  message(ws, message) {
521
- let result;
522
- try {
523
- result = ws.data.message?.(getElysia(ws), message);
524
- } catch (error) {
525
- sendErrMsg(ws, error);
620
+ if (lifecycle?.closing) return;
621
+ const data = ws.data;
622
+ if (!data.message) return;
623
+ const inflight = (data.inflight ?? 0) + 1;
624
+ if (inflight > MAX_INFLIGHT_MESSAGES) {
625
+ try {
626
+ ws.close(1013, "Too many in-flight messages");
627
+ } catch {}
526
628
  return;
527
629
  }
528
- if (result instanceof Promise) result.catch((error) => sendErrMsg(ws, error));
630
+ data.inflight = inflight;
631
+ const opening = data.opening;
632
+ if (opening) return void opening.then(() => {
633
+ if (lifecycle?.closing || ws.readyState > 1) return release(ws);
634
+ dispatch(ws, message);
635
+ });
636
+ dispatch(ws, message);
529
637
  },
530
638
  open(ws) {
531
- ws.data.open?.(getElysia(ws));
639
+ if (lifecycle) Object.defineProperty(ws.data, "~lifecycleRun", {
640
+ configurable: true,
641
+ value: lifecycle.run
642
+ });
643
+ lifecycle?.sockets.add(ws);
644
+ if (lifecycle?.closing) {
645
+ ws.data.closeHandlerInvoked = true;
646
+ try {
647
+ ws.terminate();
648
+ } catch {}
649
+ return;
650
+ }
651
+ const open = ws.data.open;
652
+ const result = open && runLifecycle(open, getElysia(ws));
653
+ if (result instanceof Promise) {
654
+ const clear = () => {
655
+ ws.data.opening = void 0;
656
+ releaseLifecycle(ws);
657
+ };
658
+ ws.data.opening = result.then(clear, clear);
659
+ }
532
660
  },
533
661
  drain(ws) {
662
+ if (lifecycle?.closing) return;
534
663
  const elyWs = getElysia(ws);
535
664
  drainWaiters(elyWs);
536
- ws.data.drain?.(elyWs);
665
+ const drain = ws.data.drain;
666
+ require_ws_context.trackWSSettling(ws.data, drain && runLifecycle(drain, elyWs), () => releaseLifecycle(ws));
537
667
  },
538
668
  close(ws, code, reason) {
539
669
  const elyWs = getElysia(ws);
540
670
  drainWaiters(elyWs);
541
- if (ws.data.closeHandlerInvoked) return;
671
+ if (ws.data.closeHandlerInvoked) return releaseLifecycle(ws);
542
672
  ws.data.closeHandlerInvoked = true;
543
- ws.data.close?.(elyWs, code, reason);
673
+ let result;
674
+ try {
675
+ const close = ws.data.close;
676
+ result = close && runLifecycle(close, elyWs, code, reason);
677
+ } finally {
678
+ if (!(result instanceof Promise)) releaseLifecycle(ws);
679
+ }
680
+ if (result instanceof Promise) {
681
+ const closing = result.catch(() => {});
682
+ require_ws_context.trackWSSettling(ws.data, closing, () => releaseLifecycle(ws));
683
+ }
544
684
  },
545
685
  ping(ws, data) {
546
- ws.data.ping?.(getElysia(ws), data);
686
+ if (lifecycle?.closing) return;
687
+ const ping = ws.data.ping;
688
+ require_ws_context.trackWSSettling(ws.data, ping && runLifecycle(ping, getElysia(ws), data), () => releaseLifecycle(ws));
547
689
  },
548
690
  pong(ws, data) {
549
- ws.data.pong?.(getElysia(ws), data);
691
+ if (lifecycle?.closing) return;
692
+ const pong = ws.data.pong;
693
+ require_ws_context.trackWSSettling(ws.data, pong && runLifecycle(pong, getElysia(ws), data), () => releaseLifecycle(ws));
550
694
  }
551
695
  };
696
+ Object.defineProperty(handler, "~lifecycle", { set(value) {
697
+ lifecycle = value;
698
+ } });
699
+ return handler;
552
700
  }
553
701
 
554
702
  //#endregion