elysia 2.0.0-beta.3 → 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 (69) 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 +48 -27
  5. package/dist/adapter/utils.mjs +48 -27
  6. package/dist/base.d.ts +16 -5
  7. package/dist/base.js +171 -74
  8. package/dist/base.mjs +172 -75
  9. package/dist/compile/aot-capture.js +1 -1
  10. package/dist/compile/aot-capture.mjs +1 -1
  11. package/dist/compile/aot.d.ts +1 -0
  12. package/dist/compile/aot.js +3 -0
  13. package/dist/compile/aot.mjs +3 -0
  14. package/dist/compile/handler/frozen-validator.d.ts +4 -1
  15. package/dist/compile/handler/frozen-validator.js +8 -3
  16. package/dist/compile/handler/frozen-validator.mjs +5 -3
  17. package/dist/compile/handler/index.d.ts +1 -1
  18. package/dist/compile/handler/index.js +3 -3
  19. package/dist/compile/handler/index.mjs +3 -3
  20. package/dist/compile/handler/reconstruct.d.ts +1 -1
  21. package/dist/compile/handler/reconstruct.js +4 -3
  22. package/dist/compile/handler/reconstruct.mjs +4 -3
  23. package/dist/compile/jit-probe.d.ts +2 -2
  24. package/dist/context.d.ts +1 -1
  25. package/dist/context.js +5 -1
  26. package/dist/context.mjs +5 -1
  27. package/dist/package.js +1 -1
  28. package/dist/package.mjs +1 -1
  29. package/dist/plugin/aot/core.d.ts +11 -5
  30. package/dist/plugin/aot/core.js +86 -38
  31. package/dist/plugin/aot/core.mjs +87 -41
  32. package/dist/plugin/aot/esbuild.js +8 -2
  33. package/dist/plugin/aot/esbuild.mjs +9 -3
  34. package/dist/plugin/aot/hooks.d.ts +2 -1
  35. package/dist/plugin/aot/hooks.js +3 -3
  36. package/dist/plugin/aot/hooks.mjs +4 -4
  37. package/dist/plugin/aot/source.d.ts +4 -1
  38. package/dist/plugin/aot/source.js +21 -14
  39. package/dist/plugin/aot/source.mjs +21 -14
  40. package/dist/plugin/aot/worker.js +2 -2
  41. package/dist/plugin/aot/worker.mjs +2 -2
  42. package/dist/route-table.d.ts +3 -0
  43. package/dist/route-table.js +4 -1
  44. package/dist/route-table.mjs +4 -1
  45. package/dist/schema-snapshot.js +17 -22
  46. package/dist/schema-snapshot.mjs +17 -22
  47. package/dist/type/bridge-live.js +1 -1
  48. package/dist/type/bridge-live.mjs +1 -1
  49. package/dist/type/coerce.d.ts +1 -1
  50. package/dist/type/constants.d.ts +1 -1
  51. package/dist/type/typebox-value-live.d.ts +2 -2
  52. package/dist/type/typebox-value-live.js +2 -2
  53. package/dist/type/typebox-value-live.mjs +2 -2
  54. package/dist/type/typebox-value.d.ts +6 -6
  55. package/dist/type/validator/index.d.ts +1 -1
  56. package/dist/type/validator/index.js +10 -7
  57. package/dist/type/validator/index.mjs +10 -7
  58. package/dist/type/validator/validator-cache.d.ts +1 -1
  59. package/dist/type/validator/validator-cache.js +17 -9
  60. package/dist/type/validator/validator-cache.mjs +17 -9
  61. package/dist/utils.d.ts +10 -1
  62. package/dist/utils.js +8 -1
  63. package/dist/utils.mjs +6 -1
  64. package/dist/ws/context.d.ts +4 -1
  65. package/dist/ws/context.js +21 -3
  66. package/dist/ws/context.mjs +20 -3
  67. package/dist/ws/route.js +57 -12
  68. package/dist/ws/route.mjs +59 -14
  69. package/package.json +2 -2
@@ -1,8 +1,8 @@
1
1
  import { MaybePromise } from "../../types.js";
2
2
  import { Validator as Validator$1, ValidatorOptions } from "../../validator/index.js";
3
3
  import { TypeBoxValidatorCache, mayHaveFileType } from "./validator-cache.js";
4
- import { Static, StaticDecode, StaticEncode, TAny, TSchema } from "typebox/type";
5
4
  import { Validator } from "typebox/schema";
5
+ import { Static, StaticDecode, StaticEncode, TAny, TSchema } from "typebox/type";
6
6
  import { TLocalizedValidationError } from "typebox/error";
7
7
 
8
8
  //#region src/type/validator/index.d.ts
@@ -348,8 +348,8 @@ var TypeBoxValidator = class extends require_validator_index.Validator {
348
348
  #error(value, type, errors) {
349
349
  return new require_error.ValidationError(type, value, errors ?? (() => this.Errors(value)), this.schema, this.#findCustomError);
350
350
  }
351
- #validate(value) {
352
- if (!this.#refinements) return this.Check(value) ? void 0 : [];
351
+ #validate(value, countHit = true) {
352
+ if (!this.#refinements) return (countHit ? this.Check(value) : this.#check(value, false)) ? void 0 : [];
353
353
  const pool = this.#refineScratch ??= freshRefineValidation(this.#refinements);
354
354
  const usingPool = !pool.active;
355
355
  const validation = usingPool ? pool : freshRefineValidation(this.#refinements);
@@ -359,7 +359,7 @@ var TypeBoxValidator = class extends require_validator_index.Validator {
359
359
  validation.replay = false;
360
360
  activeRefineValidation = validation;
361
361
  try {
362
- if (this.Check(value)) return;
362
+ if (countHit ? this.Check(value) : this.#check(value, false)) return;
363
363
  validation.replay = true;
364
364
  validation.replayEpoch++;
365
365
  return this.Errors(value);
@@ -430,7 +430,10 @@ var TypeBoxValidator = class extends require_validator_index.Validator {
430
430
  } catch {}
431
431
  }
432
432
  Check(value) {
433
- this.#tick();
433
+ return this.#check(value, true);
434
+ }
435
+ #check(value, countHit) {
436
+ if (countHit) this.#tick();
434
437
  const pool = this.#refineScratch;
435
438
  if (pool?.active && this.#refinements) {
436
439
  if (pool.checking) {
@@ -494,12 +497,12 @@ var TypeBoxValidator = class extends require_validator_index.Validator {
494
497
  EncodeFrom(value, type) {
495
498
  this.#tick();
496
499
  if (this.#isForm) {
497
- const errors = this.#noValidate ? void 0 : this.#validate(value);
500
+ const errors = this.#noValidate ? void 0 : this.#validate(value, false);
498
501
  if (errors) throw this.#error(value, type, errors.length ? errors : void 0);
499
502
  return value;
500
503
  }
501
504
  if (!this.hasCodec) {
502
- const errors = this.#noValidate ? void 0 : this.#validate(value);
505
+ const errors = this.#noValidate ? void 0 : this.#validate(value, false);
503
506
  if (errors) throw this.#error(value, type, errors.length ? errors : void 0);
504
507
  if (this.Clean) value = this.Clean(value);
505
508
  return value;
@@ -507,7 +510,7 @@ var TypeBoxValidator = class extends require_validator_index.Validator {
507
510
  try {
508
511
  if (this.#encodeMirror) {
509
512
  const out = this.#encodeMirror(value);
510
- const errors = this.#noValidate ? void 0 : this.#validate(out);
513
+ const errors = this.#noValidate ? void 0 : this.#validate(out, false);
511
514
  if (errors) throw new require_error.ValidationError(type, out, errors.length ? errors : () => this.Errors(out), this.schema);
512
515
  return out;
513
516
  }
@@ -347,8 +347,8 @@ var TypeBoxValidator = class extends Validator {
347
347
  #error(value, type, errors) {
348
348
  return new ValidationError(type, value, errors ?? (() => this.Errors(value)), this.schema, this.#findCustomError);
349
349
  }
350
- #validate(value) {
351
- if (!this.#refinements) return this.Check(value) ? void 0 : [];
350
+ #validate(value, countHit = true) {
351
+ if (!this.#refinements) return (countHit ? this.Check(value) : this.#check(value, false)) ? void 0 : [];
352
352
  const pool = this.#refineScratch ??= freshRefineValidation(this.#refinements);
353
353
  const usingPool = !pool.active;
354
354
  const validation = usingPool ? pool : freshRefineValidation(this.#refinements);
@@ -358,7 +358,7 @@ var TypeBoxValidator = class extends Validator {
358
358
  validation.replay = false;
359
359
  activeRefineValidation = validation;
360
360
  try {
361
- if (this.Check(value)) return;
361
+ if (countHit ? this.Check(value) : this.#check(value, false)) return;
362
362
  validation.replay = true;
363
363
  validation.replayEpoch++;
364
364
  return this.Errors(value);
@@ -429,7 +429,10 @@ var TypeBoxValidator = class extends Validator {
429
429
  } catch {}
430
430
  }
431
431
  Check(value) {
432
- this.#tick();
432
+ return this.#check(value, true);
433
+ }
434
+ #check(value, countHit) {
435
+ if (countHit) this.#tick();
433
436
  const pool = this.#refineScratch;
434
437
  if (pool?.active && this.#refinements) {
435
438
  if (pool.checking) {
@@ -493,12 +496,12 @@ var TypeBoxValidator = class extends Validator {
493
496
  EncodeFrom(value, type) {
494
497
  this.#tick();
495
498
  if (this.#isForm) {
496
- const errors = this.#noValidate ? void 0 : this.#validate(value);
499
+ const errors = this.#noValidate ? void 0 : this.#validate(value, false);
497
500
  if (errors) throw this.#error(value, type, errors.length ? errors : void 0);
498
501
  return value;
499
502
  }
500
503
  if (!this.hasCodec) {
501
- const errors = this.#noValidate ? void 0 : this.#validate(value);
504
+ const errors = this.#noValidate ? void 0 : this.#validate(value, false);
502
505
  if (errors) throw this.#error(value, type, errors.length ? errors : void 0);
503
506
  if (this.Clean) value = this.Clean(value);
504
507
  return value;
@@ -506,7 +509,7 @@ var TypeBoxValidator = class extends Validator {
506
509
  try {
507
510
  if (this.#encodeMirror) {
508
511
  const out = this.#encodeMirror(value);
509
- const errors = this.#noValidate ? void 0 : this.#validate(out);
512
+ const errors = this.#noValidate ? void 0 : this.#validate(out, false);
510
513
  if (errors) throw new ValidationError(type, out, errors.length ? errors : () => this.Errors(out), this.schema);
511
514
  return out;
512
515
  }
@@ -1,7 +1,7 @@
1
1
  import { CoerceOption } from "../coerce.js";
2
- import { TSchema } from "typebox/type";
3
2
  import * as _$typebox_schema0 from "typebox/schema";
4
3
  import { Validator } from "typebox/schema";
4
+ import { TSchema } from "typebox/type";
5
5
 
6
6
  //#region src/type/validator/validator-cache.d.ts
7
7
  interface SchemaMeta {
@@ -189,8 +189,15 @@ var TypeBoxValidatorCache = class TypeBoxValidatorCache {
189
189
  get(schema, coercions = TypeBoxValidatorCache.EMPTY, normalize = "", models) {
190
190
  const meta = TypeBoxValidatorCache.meta(schema);
191
191
  normalize += TypeBoxValidatorCache.#modelsToken(models, meta.hasRef);
192
- const refBucket = this.#referenceCache.get(schema)?.get(normalize);
193
- if (refBucket?.has(coercions)) return refBucket.get(coercions);
192
+ const byNormalize = this.#referenceCache.get(schema);
193
+ const refBucket = byNormalize?.get(normalize);
194
+ if (refBucket?.has(coercions)) {
195
+ if (byNormalize.size >= DEFAULT_CACHE_LIMIT) {
196
+ byNormalize.delete(normalize);
197
+ byNormalize.set(normalize, refBucket);
198
+ }
199
+ return refBucket.get(coercions);
200
+ }
194
201
  if (meta.special) return;
195
202
  const key = meta.key + "\0" + normalize;
196
203
  const coercionsCache = this.#cache.get(key);
@@ -202,13 +209,14 @@ var TypeBoxValidatorCache = class TypeBoxValidatorCache {
202
209
  if (coercionsCache.has(coercions)) return coercionsCache.get(coercions);
203
210
  }
204
211
  }
205
- #refBucket(schema) {
212
+ #setRefBucket(schema, normalize, cache) {
206
213
  let byNormalize = this.#referenceCache.get(schema);
207
214
  if (!byNormalize) {
208
215
  byNormalize = /* @__PURE__ */ new Map();
209
216
  this.#referenceCache.set(schema, byNormalize);
210
217
  }
211
- return byNormalize;
218
+ if (!byNormalize.has(normalize) && byNormalize.size >= DEFAULT_CACHE_LIMIT) require_utils.evictOldestHalf(byNormalize);
219
+ byNormalize.set(normalize, cache);
212
220
  }
213
221
  set(schema, coercions = TypeBoxValidatorCache.EMPTY, validator, normalize = "", models) {
214
222
  this.#scheduleClear();
@@ -216,21 +224,21 @@ var TypeBoxValidatorCache = class TypeBoxValidatorCache {
216
224
  normalize += TypeBoxValidatorCache.#modelsToken(models, meta.hasRef);
217
225
  if (meta.special) {
218
226
  const cache = (/* @__PURE__ */ new WeakMap()).set(coercions, validator);
219
- this.#refBucket(schema).set(normalize, cache);
227
+ this.#setRefBucket(schema, normalize, cache);
220
228
  return;
221
229
  }
222
230
  const key = meta.key + "\0" + normalize;
223
231
  if (this.#cache.has(key)) {
224
232
  const cache = this.#cache.get(key).set(coercions, validator);
225
- const byNormalize = this.#refBucket(schema);
226
- if (byNormalize.has(normalize)) byNormalize.get(normalize).set(coercions, validator);
227
- else byNormalize.set(normalize, cache);
233
+ const byNormalize = this.#referenceCache.get(schema);
234
+ if (byNormalize?.has(normalize)) byNormalize.get(normalize).set(coercions, validator);
235
+ else this.#setRefBucket(schema, normalize, cache);
228
236
  return;
229
237
  }
230
238
  if (this.#cache.size >= DEFAULT_CACHE_LIMIT) require_utils.evictOldestHalf(this.#cache);
231
239
  const cache = (/* @__PURE__ */ new WeakMap()).set(coercions, validator);
232
240
  this.#cache.set(key, cache);
233
- this.#refBucket(schema).set(normalize, cache);
241
+ this.#setRefBucket(schema, normalize, cache);
234
242
  }
235
243
  clear() {
236
244
  if (this.#gc) {
@@ -188,8 +188,15 @@ var TypeBoxValidatorCache = class TypeBoxValidatorCache {
188
188
  get(schema, coercions = TypeBoxValidatorCache.EMPTY, normalize = "", models) {
189
189
  const meta = TypeBoxValidatorCache.meta(schema);
190
190
  normalize += TypeBoxValidatorCache.#modelsToken(models, meta.hasRef);
191
- const refBucket = this.#referenceCache.get(schema)?.get(normalize);
192
- if (refBucket?.has(coercions)) return refBucket.get(coercions);
191
+ const byNormalize = this.#referenceCache.get(schema);
192
+ const refBucket = byNormalize?.get(normalize);
193
+ if (refBucket?.has(coercions)) {
194
+ if (byNormalize.size >= DEFAULT_CACHE_LIMIT) {
195
+ byNormalize.delete(normalize);
196
+ byNormalize.set(normalize, refBucket);
197
+ }
198
+ return refBucket.get(coercions);
199
+ }
193
200
  if (meta.special) return;
194
201
  const key = meta.key + "\0" + normalize;
195
202
  const coercionsCache = this.#cache.get(key);
@@ -201,13 +208,14 @@ var TypeBoxValidatorCache = class TypeBoxValidatorCache {
201
208
  if (coercionsCache.has(coercions)) return coercionsCache.get(coercions);
202
209
  }
203
210
  }
204
- #refBucket(schema) {
211
+ #setRefBucket(schema, normalize, cache) {
205
212
  let byNormalize = this.#referenceCache.get(schema);
206
213
  if (!byNormalize) {
207
214
  byNormalize = /* @__PURE__ */ new Map();
208
215
  this.#referenceCache.set(schema, byNormalize);
209
216
  }
210
- return byNormalize;
217
+ if (!byNormalize.has(normalize) && byNormalize.size >= DEFAULT_CACHE_LIMIT) evictOldestHalf(byNormalize);
218
+ byNormalize.set(normalize, cache);
211
219
  }
212
220
  set(schema, coercions = TypeBoxValidatorCache.EMPTY, validator, normalize = "", models) {
213
221
  this.#scheduleClear();
@@ -215,21 +223,21 @@ var TypeBoxValidatorCache = class TypeBoxValidatorCache {
215
223
  normalize += TypeBoxValidatorCache.#modelsToken(models, meta.hasRef);
216
224
  if (meta.special) {
217
225
  const cache = (/* @__PURE__ */ new WeakMap()).set(coercions, validator);
218
- this.#refBucket(schema).set(normalize, cache);
226
+ this.#setRefBucket(schema, normalize, cache);
219
227
  return;
220
228
  }
221
229
  const key = meta.key + "\0" + normalize;
222
230
  if (this.#cache.has(key)) {
223
231
  const cache = this.#cache.get(key).set(coercions, validator);
224
- const byNormalize = this.#refBucket(schema);
225
- if (byNormalize.has(normalize)) byNormalize.get(normalize).set(coercions, validator);
226
- else byNormalize.set(normalize, cache);
232
+ const byNormalize = this.#referenceCache.get(schema);
233
+ if (byNormalize?.has(normalize)) byNormalize.get(normalize).set(coercions, validator);
234
+ else this.#setRefBucket(schema, normalize, cache);
227
235
  return;
228
236
  }
229
237
  if (this.#cache.size >= DEFAULT_CACHE_LIMIT) evictOldestHalf(this.#cache);
230
238
  const cache = (/* @__PURE__ */ new WeakMap()).set(coercions, validator);
231
239
  this.#cache.set(key, cache);
232
- this.#refBucket(schema).set(normalize, cache);
240
+ this.#setRefBucket(schema, normalize, cache);
233
241
  }
234
242
  clear() {
235
243
  if (this.#gc) {
package/dist/utils.d.ts CHANGED
@@ -183,5 +183,14 @@ declare namespace prefix {
183
183
  var capitalize: <T extends string, Models extends Record<string, AnySchema>>(prefix: T, models: Models) => { [k in keyof Models as `${T}.${Capitalize<k & string>}`]: Models[k] };
184
184
  }
185
185
  declare function serializeMacroSeed(_key: string, value: unknown): unknown;
186
+ declare const throwLifecycleErrors: (errors: unknown[]) => void;
187
+ declare const isSocketQuiet: (socket: {
188
+ readyState: number;
189
+ data: {
190
+ inflight?: number;
191
+ opening?: unknown;
192
+ settling?: number;
193
+ };
194
+ }) => boolean;
186
195
  //#endregion
187
- export { ChainNode, CompactBeforeHandleChunk, CompactBeforeHandlePrefix, DeriveEntry, ELYSIA_FORM_PROTOTYPE, assignOwn, authorityEnd, bytes, clearFlattenChainMemo, cloneHook, clonePlainDecorators, clonePlainDeep, coalesceSchemas, compactBeforeHandleConflicts, compactBeforeHandlePrefix, constantTimeEqual, createErrorEventHandler, deriveEntryFn, eventProperties, evictOldestHalf, fallbackRequestId, flattenChain, flattenChainMemo, flattenChainMemoReadonly, fnOrigin, fnv1a, form, formToFormData, getLoosePath, guardNonPlainLeaves, hookToGuard, invalidateMacroEpoch, isBlob, isByteStream, isCompactBeforeHandleOnly, isElysiaForm, isEmpty, isLocalScope, isMapDeriveEntry, isNotEmpty, isRecordNumber, joinPath, macroEpoch, macroOrigin, mapDeriveEntry, mergeArray, mergeDeep, mergeHook, mergeResponse, nullObject, prefix, pushField, redirect, replaceUrlPath, requestId, schemaProperties, serializeMacroSeed, sse, sseData };
196
+ export { ChainNode, CompactBeforeHandleChunk, CompactBeforeHandlePrefix, DeriveEntry, ELYSIA_FORM_PROTOTYPE, assignOwn, authorityEnd, bytes, clearFlattenChainMemo, cloneHook, clonePlainDecorators, clonePlainDeep, coalesceSchemas, compactBeforeHandleConflicts, compactBeforeHandlePrefix, constantTimeEqual, createErrorEventHandler, deriveEntryFn, eventProperties, evictOldestHalf, fallbackRequestId, flattenChain, flattenChainMemo, flattenChainMemoReadonly, fnOrigin, fnv1a, form, formToFormData, getLoosePath, guardNonPlainLeaves, hookToGuard, invalidateMacroEpoch, isBlob, isByteStream, isCompactBeforeHandleOnly, isElysiaForm, isEmpty, isLocalScope, isMapDeriveEntry, isNotEmpty, isRecordNumber, isSocketQuiet, joinPath, macroEpoch, macroOrigin, mapDeriveEntry, mergeArray, mergeDeep, mergeHook, mergeResponse, nullObject, prefix, pushField, redirect, replaceUrlPath, requestId, schemaProperties, serializeMacroSeed, sse, sseData, throwLifecycleErrors };
package/dist/utils.js CHANGED
@@ -690,6 +690,11 @@ function serializeMacroSeed(_key, value) {
690
690
  default: return value;
691
691
  }
692
692
  }
693
+ const throwLifecycleErrors = (errors) => {
694
+ if (errors.length === 1) throw errors[0];
695
+ if (errors.length) throw new AggregateError(errors, "Multiple lifecycle failures");
696
+ };
697
+ const isSocketQuiet = (socket) => socket.readyState === 3 && !socket.data.inflight && !socket.data.opening && !socket.data.settling;
693
698
 
694
699
  //#endregion
695
700
  exports.ELYSIA_FORM_PROTOTYPE = ELYSIA_FORM_PROTOTYPE;
@@ -729,6 +734,7 @@ exports.isLocalScope = isLocalScope;
729
734
  exports.isMapDeriveEntry = isMapDeriveEntry;
730
735
  exports.isNotEmpty = isNotEmpty;
731
736
  exports.isRecordNumber = isRecordNumber;
737
+ exports.isSocketQuiet = isSocketQuiet;
732
738
  exports.joinPath = joinPath;
733
739
  exports.macroEpoch = macroEpoch;
734
740
  exports.macroOrigin = macroOrigin;
@@ -746,4 +752,5 @@ exports.requestId = requestId;
746
752
  exports.schemaProperties = schemaProperties;
747
753
  exports.serializeMacroSeed = serializeMacroSeed;
748
754
  exports.sse = sse;
749
- exports.sseData = sseData;
755
+ exports.sseData = sseData;
756
+ exports.throwLifecycleErrors = throwLifecycleErrors;
package/dist/utils.mjs CHANGED
@@ -689,6 +689,11 @@ function serializeMacroSeed(_key, value) {
689
689
  default: return value;
690
690
  }
691
691
  }
692
+ const throwLifecycleErrors = (errors) => {
693
+ if (errors.length === 1) throw errors[0];
694
+ if (errors.length) throw new AggregateError(errors, "Multiple lifecycle failures");
695
+ };
696
+ const isSocketQuiet = (socket) => socket.readyState === 3 && !socket.data.inflight && !socket.data.opening && !socket.data.settling;
692
697
 
693
698
  //#endregion
694
- export { ELYSIA_FORM_PROTOTYPE, assignOwn, authorityEnd, bytes, clearFlattenChainMemo, cloneHook, clonePlainDecorators, clonePlainDeep, coalesceSchemas, compactBeforeHandleConflicts, compactBeforeHandlePrefix, constantTimeEqual, createErrorEventHandler, deriveEntryFn, eventProperties, evictOldestHalf, fallbackRequestId, flattenChain, flattenChainMemo, flattenChainMemoReadonly, fnOrigin, fnv1a, form, formToFormData, getLoosePath, guardNonPlainLeaves, hookToGuard, invalidateMacroEpoch, isBlob, isByteStream, isCompactBeforeHandleOnly, isElysiaForm, isEmpty, isLocalScope, isMapDeriveEntry, isNotEmpty, isRecordNumber, joinPath, macroEpoch, macroOrigin, mapDeriveEntry, mergeArray, mergeDeep, mergeHook, mergeResponse, nullObject, prefix, pushField, redirect, replaceUrlPath, requestId, schemaProperties, serializeMacroSeed, sse, sseData };
699
+ export { ELYSIA_FORM_PROTOTYPE, assignOwn, authorityEnd, bytes, clearFlattenChainMemo, cloneHook, clonePlainDecorators, clonePlainDeep, coalesceSchemas, compactBeforeHandleConflicts, compactBeforeHandlePrefix, constantTimeEqual, createErrorEventHandler, deriveEntryFn, eventProperties, evictOldestHalf, fallbackRequestId, flattenChain, flattenChainMemo, flattenChainMemoReadonly, fnOrigin, fnv1a, form, formToFormData, getLoosePath, guardNonPlainLeaves, hookToGuard, invalidateMacroEpoch, isBlob, isByteStream, isCompactBeforeHandleOnly, isElysiaForm, isEmpty, isLocalScope, isMapDeriveEntry, isNotEmpty, isRecordNumber, isSocketQuiet, joinPath, macroEpoch, macroOrigin, mapDeriveEntry, mergeArray, mergeDeep, mergeHook, mergeResponse, nullObject, prefix, pushField, redirect, replaceUrlPath, requestId, schemaProperties, serializeMacroSeed, sse, sseData, throwLifecycleErrors };
@@ -15,12 +15,15 @@ interface WSConnectionData {
15
15
  inflight?: number;
16
16
  /** settles when an async `open` hook finished; dispatch chains onto it */
17
17
  opening?: Promise<void>;
18
+ /** async socket lifecycle callbacks other than message/open */
19
+ settling?: number;
18
20
  elysia?: ElysiaWS<any>;
19
21
  context?: Record<string, unknown>;
20
22
  resumeWaiters?: Set<() => void>;
21
23
  validator?: WSResponseValidator;
22
24
  defaultValidator?: WSValidatorLike;
23
25
  }
26
+ declare function trackWSSettling(data: WSConnectionData, result: void | Promise<void>, onSettled?: () => void): void;
24
27
  declare class ElysiaWS<Route extends RouteSchema = {}> {
25
28
  #private;
26
29
  raw: ServerWebSocket<WSConnectionData>;
@@ -50,4 +53,4 @@ declare class ElysiaWS<Route extends RouteSchema = {}> {
50
53
  }
51
54
  declare function isGeneratorObject(value: unknown): boolean;
52
55
  //#endregion
53
- export { ElysiaWS, WSConnectionData, isGeneratorObject };
56
+ export { ElysiaWS, WSConnectionData, isGeneratorObject, trackWSSettling };
@@ -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, {
@@ -130,8 +143,12 @@ var ElysiaWS = class {
130
143
  if (!data.closeHandlerInvoked && data.close) {
131
144
  data.closeHandlerInvoked = true;
132
145
  try {
133
- const result = data.close(this, code ?? 1e3, reason ?? "");
134
- 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
+ }
135
152
  return;
136
153
  } catch {}
137
154
  }
@@ -167,4 +184,5 @@ function isGeneratorObject(value) {
167
184
 
168
185
  //#endregion
169
186
  exports.ElysiaWS = ElysiaWS;
170
- 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, {
@@ -129,8 +142,12 @@ var ElysiaWS = class {
129
142
  if (!data.closeHandlerInvoked && data.close) {
130
143
  data.closeHandlerInvoked = true;
131
144
  try {
132
- const result = data.close(this, code ?? 1e3, reason ?? "");
133
- 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
+ }
134
151
  return;
135
152
  } catch {}
136
153
  }
@@ -165,4 +182,4 @@ function isGeneratorObject(value) {
165
182
  }
166
183
 
167
184
  //#endregion
168
- export { ElysiaWS, isGeneratorObject };
185
+ export { ElysiaWS, isGeneratorObject, trackWSSettling };
package/dist/ws/route.js CHANGED
@@ -570,6 +570,8 @@ function accumulateWSOptions(target, routeOptions, _path) {
570
570
  }
571
571
  const MAX_INFLIGHT_MESSAGES = 256;
572
572
  function buildGlobalWSHandler() {
573
+ let lifecycle;
574
+ const runLifecycle = (callback, ...args) => lifecycle ? lifecycle.run(callback, ...args) : callback(...args);
573
575
  function getElysia(ws) {
574
576
  let elysia = ws.data.elysia;
575
577
  if (!elysia) {
@@ -584,9 +586,19 @@ function buildGlobalWSHandler() {
584
586
  ws.send(typeof error?.message === "string" ? error.message : "Internal Server Error");
585
587
  } catch {}
586
588
  };
589
+ const releaseLifecycle = (ws) => {
590
+ if (!lifecycle?.closing && require_utils.isSocketQuiet(ws)) {
591
+ delete ws.data["~lifecycleRun"];
592
+ lifecycle?.sockets.delete(ws);
593
+ }
594
+ };
587
595
  const release = (ws) => {
588
- const inflight = ws.data.inflight;
589
- if (inflight) ws.data.inflight = inflight - 1;
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
+ }
590
602
  };
591
603
  function dispatch(ws, message) {
592
604
  let result;
@@ -603,8 +615,9 @@ function buildGlobalWSHandler() {
603
615
  });
604
616
  else release(ws);
605
617
  }
606
- return {
618
+ const handler = {
607
619
  message(ws, message) {
620
+ if (lifecycle?.closing) return;
608
621
  const data = ws.data;
609
622
  if (!data.message) return;
610
623
  const inflight = (data.inflight ?? 0) + 1;
@@ -617,41 +630,73 @@ function buildGlobalWSHandler() {
617
630
  data.inflight = inflight;
618
631
  const opening = data.opening;
619
632
  if (opening) return void opening.then(() => {
620
- if (ws.readyState > 1) return release(ws);
633
+ if (lifecycle?.closing || ws.readyState > 1) return release(ws);
621
634
  dispatch(ws, message);
622
635
  });
623
636
  dispatch(ws, message);
624
637
  },
625
638
  open(ws) {
626
- const result = 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));
627
653
  if (result instanceof Promise) {
628
654
  const clear = () => {
629
655
  ws.data.opening = void 0;
656
+ releaseLifecycle(ws);
630
657
  };
631
658
  ws.data.opening = result.then(clear, clear);
632
659
  }
633
660
  },
634
661
  drain(ws) {
662
+ if (lifecycle?.closing) return;
635
663
  const elyWs = getElysia(ws);
636
664
  drainWaiters(elyWs);
637
- ws.data.drain?.(elyWs);
665
+ const drain = ws.data.drain;
666
+ require_ws_context.trackWSSettling(ws.data, drain && runLifecycle(drain, elyWs), () => releaseLifecycle(ws));
638
667
  },
639
668
  close(ws, code, reason) {
640
669
  const elyWs = getElysia(ws);
641
670
  drainWaiters(elyWs);
642
- ws.data.opening = void 0;
643
- if (ws.data.closeHandlerInvoked) return;
671
+ if (ws.data.closeHandlerInvoked) return releaseLifecycle(ws);
644
672
  ws.data.closeHandlerInvoked = true;
645
- const result = ws.data.close?.(elyWs, code, reason);
646
- if (result instanceof Promise) result.catch(() => {});
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
+ }
647
684
  },
648
685
  ping(ws, data) {
649
- 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));
650
689
  },
651
690
  pong(ws, data) {
652
- 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));
653
694
  }
654
695
  };
696
+ Object.defineProperty(handler, "~lifecycle", { set(value) {
697
+ lifecycle = value;
698
+ } });
699
+ return handler;
655
700
  }
656
701
 
657
702
  //#endregion