elysia 1.4.30 → 1.4.31

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/schema.mjs CHANGED
@@ -653,6 +653,356 @@ const isOptional = (schema) => schema ? schema?.[Kind] === "Import" && schema.Re
653
653
  }
654
654
  return Object.keys(result).length > 0 ? result : void 0;
655
655
  }
656
+ }, hasOwn = (value, key) => Object.prototype.hasOwnProperty.call(value, key), cloneDefaultValue = (value) => {
657
+ if (Array.isArray(value)) return value.map(cloneDefaultValue);
658
+ if (value instanceof Date) return new Date(value);
659
+ if (value instanceof Map)
660
+ return new Map(
661
+ [...value].map(([key, entry]) => [
662
+ cloneDefaultValue(key),
663
+ cloneDefaultValue(entry)
664
+ ])
665
+ );
666
+ if (value instanceof Set)
667
+ return new Set([...value].map(cloneDefaultValue));
668
+ if (ArrayBuffer.isView(value) && "slice" in value)
669
+ return value.slice();
670
+ if (value === null || typeof value != "object") return value;
671
+ const cloned = /* @__PURE__ */ Object.create(null);
672
+ for (const key of Reflect.ownKeys(value))
673
+ Object.defineProperty(cloned, key, {
674
+ value: cloneDefaultValue(value[key]),
675
+ enumerable: Object.prototype.propertyIsEnumerable.call(value, key),
676
+ configurable: !0,
677
+ writable: !0
678
+ });
679
+ return cloned;
680
+ }, cloneDefault = (value) => typeof value == "function" ? value() : cloneDefaultValue(value), resolveDefaultSchema = (schema, references) => {
681
+ if (schema[Kind] === "Import") {
682
+ const definitions = Object.values(schema.$defs ?? {}), target = schema.$defs?.[schema.$ref];
683
+ return target ? resolveDefaultSchema(
684
+ target,
685
+ [...references, ...definitions]
686
+ ) : schema;
687
+ }
688
+ if (schema[Kind] === "Ref" || schema[Kind] === "This") {
689
+ const reference = references.find(
690
+ (reference2) => reference2.$id === schema.$ref
691
+ );
692
+ if (reference) return resolveDefaultSchema(reference, references);
693
+ }
694
+ return schema;
695
+ }, cloneSchemaInput = (value) => {
696
+ if (Array.isArray(value)) return value.map(cloneForDefaults);
697
+ if (value === null || typeof value != "object" || value instanceof Date || value instanceof Map || value instanceof Set || value instanceof ArrayBuffer || ArrayBuffer.isView(value) || Object.getPrototypeOf(value) !== Object.prototype && Object.getPrototypeOf(value) !== null)
698
+ return value;
699
+ const cloned = /* @__PURE__ */ Object.create(null);
700
+ for (const key of Object.getOwnPropertyNames(value))
701
+ Object.defineProperty(cloned, key, {
702
+ value: cloneSchemaInput(value[key]),
703
+ enumerable: Object.prototype.propertyIsEnumerable.call(value, key),
704
+ configurable: !0,
705
+ writable: !0
706
+ });
707
+ for (const key of Object.getOwnPropertySymbols(value))
708
+ Object.defineProperty(cloned, key, {
709
+ value: cloneSchemaInput(value[key]),
710
+ enumerable: Object.prototype.propertyIsEnumerable.call(value, key),
711
+ configurable: !0,
712
+ writable: !0
713
+ });
714
+ return cloned;
715
+ }, cloneForDefaults = cloneSchemaInput, skipFunctionDefaults = (inputSchema, references, functionDefaults, seen = /* @__PURE__ */ new Set()) => {
716
+ const schema = resolveDefaultSchema(inputSchema, references);
717
+ if (!seen.has(schema)) {
718
+ seen.add(schema), typeof schema.default == "function" && !functionDefaults.has(schema) && functionDefaults.set(schema, { skip: !0 });
719
+ for (const child of [
720
+ ...Object.values(schema.properties ?? {}),
721
+ ...Object.values(schema.patternProperties ?? {}),
722
+ ...schema.allOf ?? [],
723
+ ...schema.anyOf ?? [],
724
+ ...schema.oneOf ?? [],
725
+ ...Array.isArray(schema.items) ? schema.items : schema.items ? [schema.items] : [],
726
+ ...schema.not ? [schema.not] : [],
727
+ ...schema.additionalProperties && typeof schema.additionalProperties == "object" ? [schema.additionalProperties] : []
728
+ ])
729
+ skipFunctionDefaults(child, references, functionDefaults, seen);
730
+ }
731
+ }, matchesExistingProperties = (inputSchema, input, references) => {
732
+ const schema = resolveDefaultSchema(inputSchema, references), properties = getSchemaProperties(schema);
733
+ if (!properties || input === null || typeof input != "object")
734
+ return input === void 0 || Value.Check(schema, references, input);
735
+ for (const [key, propertySchema] of Object.entries(properties))
736
+ if (hasOwn(input, key) && input[key] !== void 0 && !Value.Check(propertySchema, references, input[key]))
737
+ return !1;
738
+ return !0;
739
+ }, prepareDefaultInput = (inputSchema, input, references, nullish, isRoot, functionDefaults) => {
740
+ const schema = resolveDefaultSchema(inputSchema, references);
741
+ let value = input;
742
+ const missing = value === void 0 || nullish && value === null;
743
+ typeof schema.default == "function" ? missing ? (value = cloneDefault(schema.default), functionDefaults.set(schema, { skip: !1, value })) : functionDefaults.has(schema) || functionDefaults.set(schema, { skip: !0 }) : missing && hasOwn(schema, "default") && (value = cloneDefault(schema.default));
744
+ const properties = getSchemaProperties(schema);
745
+ if (isRoot && value === void 0 && (schema[Kind] === "Object" || schema.type === "object") ? value = /* @__PURE__ */ Object.create(null) : value = cloneForDefaults(value), schema[Kind] === "Object" && properties && value !== null && typeof value == "object")
746
+ for (const [key, propertySchema] of Object.entries(properties)) {
747
+ const rawPropertyValue = hasOwn(value, key) ? value[key] : void 0, propertyValue = prepareDefaultInput(
748
+ propertySchema,
749
+ rawPropertyValue,
750
+ references,
751
+ nullish,
752
+ !1,
753
+ functionDefaults
754
+ );
755
+ propertyValue !== void 0 && Object.defineProperty(value, key, {
756
+ value: propertyValue,
757
+ enumerable: !0,
758
+ configurable: !0,
759
+ writable: !0
760
+ });
761
+ }
762
+ else if (Array.isArray(value) && schema.items)
763
+ if (Array.isArray(schema.items))
764
+ for (let i = 0; i < schema.items.length; i++)
765
+ value[i] = prepareDefaultInput(
766
+ schema.items[i],
767
+ value[i],
768
+ references,
769
+ nullish,
770
+ !1,
771
+ functionDefaults
772
+ );
773
+ else
774
+ for (let i = 0; i < value.length; i++)
775
+ value[i] = prepareDefaultInput(
776
+ schema.items,
777
+ value[i],
778
+ references,
779
+ nullish,
780
+ !1,
781
+ functionDefaults
782
+ );
783
+ if (schema.allOf)
784
+ for (const member of schema.allOf)
785
+ prepareDefaultInput(
786
+ member,
787
+ input,
788
+ references,
789
+ nullish,
790
+ !1,
791
+ functionDefaults
792
+ );
793
+ for (const members of [schema.anyOf, schema.oneOf])
794
+ if (members) {
795
+ let selected = !1;
796
+ for (const member of members) {
797
+ if (selected || !matchesExistingProperties(member, input, references)) {
798
+ skipFunctionDefaults(
799
+ member,
800
+ references,
801
+ functionDefaults
802
+ );
803
+ continue;
804
+ }
805
+ const candidate = prepareDefaultInput(
806
+ member,
807
+ input,
808
+ references,
809
+ nullish,
810
+ !1,
811
+ functionDefaults
812
+ );
813
+ Value.Check(member, references, candidate) && (selected = !0, value = candidate);
814
+ }
815
+ }
816
+ if (schema.patternProperties && value !== null && typeof value == "object")
817
+ for (const [pattern, propertySchema] of Object.entries(
818
+ schema.patternProperties
819
+ ))
820
+ for (const key of Object.keys(value))
821
+ new RegExp(pattern).test(key) && (value[key] = prepareDefaultInput(
822
+ propertySchema,
823
+ value[key],
824
+ references,
825
+ nullish,
826
+ !1,
827
+ functionDefaults
828
+ ));
829
+ return value;
830
+ }, withResolvedFunctionDefaults = (functionDefaults, run) => {
831
+ const originals = [];
832
+ try {
833
+ for (const [schema, resolved] of functionDefaults) {
834
+ const descriptor = Object.getOwnPropertyDescriptor(schema, "default");
835
+ descriptor && (originals.push([schema, descriptor]), resolved.skip ? delete schema.default : Object.defineProperty(schema, "default", {
836
+ ...descriptor,
837
+ value: resolved.value
838
+ }));
839
+ }
840
+ return run();
841
+ } finally {
842
+ for (const [schema, descriptor] of originals)
843
+ Object.defineProperty(schema, "default", descriptor);
844
+ }
845
+ }, restoreArrayBuffers = (inputSchema, input, defaulted, references) => {
846
+ const schema = resolveDefaultSchema(inputSchema, references);
847
+ if (schema[Kind] === "ArrayBuffer" && input instanceof ArrayBuffer)
848
+ return input;
849
+ const properties = getSchemaProperties(schema);
850
+ if (properties && input !== null && typeof input == "object" && defaulted !== null && typeof defaulted == "object")
851
+ for (const [key, propertySchema] of Object.entries(properties))
852
+ !hasOwn(input, key) || !hasOwn(defaulted, key) || Object.defineProperty(defaulted, key, {
853
+ value: restoreArrayBuffers(
854
+ propertySchema,
855
+ input[key],
856
+ defaulted[key],
857
+ references
858
+ ),
859
+ enumerable: !0,
860
+ configurable: !0,
861
+ writable: !0
862
+ });
863
+ else if (Array.isArray(input) && Array.isArray(defaulted) && schema.items)
864
+ if (Array.isArray(schema.items))
865
+ for (let i = 0; i < schema.items.length; i++)
866
+ defaulted[i] = restoreArrayBuffers(
867
+ schema.items[i],
868
+ input[i],
869
+ defaulted[i],
870
+ references
871
+ );
872
+ else
873
+ for (let i = 0; i < input.length; i++)
874
+ defaulted[i] = restoreArrayBuffers(
875
+ schema.items,
876
+ input[i],
877
+ defaulted[i],
878
+ references
879
+ );
880
+ return defaulted;
881
+ }, repairPrototypeDefaults = (inputSchema, input, references, nullish) => {
882
+ const schema = resolveDefaultSchema(inputSchema, references);
883
+ if (schema.anyOf || schema.oneOf) {
884
+ const members = schema.anyOf ?? schema.oneOf;
885
+ for (const member of members) {
886
+ const candidate = repairPrototypeDefaults(
887
+ member,
888
+ cloneForDefaults(input),
889
+ references,
890
+ nullish
891
+ );
892
+ if (Value.Check(member, references, candidate)) return candidate;
893
+ }
894
+ return input;
895
+ }
896
+ if (schema.allOf) {
897
+ let value = input;
898
+ for (const member of schema.allOf)
899
+ value = repairPrototypeDefaults(
900
+ member,
901
+ value,
902
+ references,
903
+ nullish
904
+ );
905
+ return value;
906
+ }
907
+ const properties = getSchemaProperties(schema);
908
+ if (properties && input !== null && typeof input == "object")
909
+ for (const [key, propertySchema] of Object.entries(properties))
910
+ (!hasOwn(input, key) || nullish && input[key] === null) && hasOwn(propertySchema, "default") && Object.defineProperty(input, key, {
911
+ value: cloneDefault(propertySchema.default),
912
+ enumerable: !0,
913
+ configurable: !0,
914
+ writable: !0
915
+ }), hasOwn(input, key) && Object.defineProperty(input, key, {
916
+ value: repairPrototypeDefaults(
917
+ propertySchema,
918
+ input[key],
919
+ references,
920
+ nullish
921
+ ),
922
+ enumerable: !0,
923
+ configurable: !0,
924
+ writable: !0
925
+ });
926
+ else if (Array.isArray(input) && schema.items && !Array.isArray(schema.items))
927
+ for (let i = 0; i < input.length; i++)
928
+ input[i] = repairPrototypeDefaults(
929
+ schema.items,
930
+ input[i],
931
+ references,
932
+ nullish
933
+ );
934
+ return input;
935
+ }, removeFileDefaults = (inputSchema, value, references) => {
936
+ const schema = resolveDefaultSchema(inputSchema, references), properties = getSchemaProperties(schema);
937
+ if (properties && value !== null && typeof value == "object")
938
+ for (const [key, propertySchema] of Object.entries(properties))
939
+ hasOwn(value, key) && (propertySchema[Kind] === "File" && value[key] === "File" || propertySchema[Kind] === "Files" && value[key] === "Files" ? delete value[key] : removeFileDefaults(propertySchema, value[key], references));
940
+ else if (Array.isArray(value) && schema.items && !Array.isArray(schema.items))
941
+ for (const item of value) removeFileDefaults(schema.items, item, references);
942
+ }, mergeDefaultResult = (original, defaulted) => {
943
+ if (Array.isArray(defaulted)) {
944
+ const target2 = Array.isArray(original) ? original : [];
945
+ target2.length = defaulted.length;
946
+ for (let i = 0; i < defaulted.length; i++)
947
+ target2[i] = mergeDefaultResult(
948
+ Array.isArray(original) ? original[i] : void 0,
949
+ defaulted[i]
950
+ );
951
+ return target2;
952
+ }
953
+ if (defaulted === null || typeof defaulted != "object" || defaulted instanceof Date || defaulted instanceof Map || defaulted instanceof Set || defaulted instanceof ArrayBuffer || ArrayBuffer.isView(defaulted))
954
+ return defaulted;
955
+ const target = original !== null && typeof original == "object" && !Array.isArray(original) ? original : {};
956
+ for (const key of Object.getOwnPropertyNames(defaulted))
957
+ Object.defineProperty(target, key, {
958
+ value: mergeDefaultResult(
959
+ hasOwn(target, key) ? target[key] : void 0,
960
+ defaulted[key]
961
+ ),
962
+ enumerable: Object.prototype.propertyIsEnumerable.call(defaulted, key),
963
+ configurable: !0,
964
+ writable: !0
965
+ });
966
+ for (const key of Object.getOwnPropertySymbols(defaulted))
967
+ Object.defineProperty(target, key, {
968
+ value: mergeDefaultResult(
969
+ hasOwn(target, key) ? target[key] : void 0,
970
+ defaulted[key]
971
+ ),
972
+ enumerable: Object.prototype.propertyIsEnumerable.call(defaulted, key),
973
+ configurable: !0,
974
+ writable: !0
975
+ });
976
+ return target;
977
+ }, applySchemaDefaults = (checker, value, nullish = !1) => {
978
+ const schema = checker.schema;
979
+ if (!schema) return value;
980
+ const references = typeof checker.References == "function" ? checker.References() : Object.values(schema.$defs ?? {}), functionDefaults = /* @__PURE__ */ new Map(), prepared = prepareDefaultInput(
981
+ schema,
982
+ value,
983
+ references,
984
+ nullish,
985
+ !0,
986
+ functionDefaults
987
+ );
988
+ return withResolvedFunctionDefaults(functionDefaults, () => {
989
+ const defaulted = repairPrototypeDefaults(
990
+ schema,
991
+ restoreArrayBuffers(
992
+ schema,
993
+ prepared,
994
+ Value.Default(
995
+ schema,
996
+ references,
997
+ cloneForDefaults(prepared)
998
+ ),
999
+ references
1000
+ ),
1001
+ references,
1002
+ nullish
1003
+ );
1004
+ return removeFileDefaults(schema, defaulted, references), mergeDefaultResult(value, defaulted);
1005
+ });
656
1006
  }, mergeObjectSchemas = (schemas) => {
657
1007
  if (schemas.length === 0)
658
1008
  return {
@@ -803,6 +1153,8 @@ const isOptional = (schema) => schema ? schema?.[Kind] === "Import" && schema.Re
803
1153
  }), cookieValidator.config = defaultConfig), cookieValidator;
804
1154
  }, unwrapImportSchema = (schema) => schema && schema[Kind] === "Import" && schema.$defs[schema.$ref][Kind] === "Object" ? schema.$defs[schema.$ref] : schema;
805
1155
  export {
1156
+ applySchemaDefaults,
1157
+ cloneSchemaInput,
806
1158
  getCookieValidator,
807
1159
  getResponseSchemaValidator,
808
1160
  getSchemaProperties,
package/dist/types.d.ts CHANGED
@@ -354,6 +354,11 @@ export type HookContainer<T extends Function = Function> = {
354
354
  hasReturn?: boolean;
355
355
  };
356
356
  export interface LifeCycleStore {
357
+ config?: {
358
+ allowMeta?: boolean;
359
+ mount?: Function;
360
+ cookie?: ElysiaConfig<any>['cookie'];
361
+ };
357
362
  type?: ContentType;
358
363
  start: HookContainer<GracefulHandler<any>>[];
359
364
  request: HookContainer<PreHandler<any, any>>[];
@@ -703,7 +708,13 @@ type _ResolveReturnTypeArray<T, Carry = {}> = T extends [
703
708
  infer Fn,
704
709
  ...infer Rest
705
710
  ] ? Exclude<Awaited<ReturnType<Fn>>, AnyElysiaCustomStatusResponse> extends infer Value extends Record<any, unknown> ? _ResolveReturnTypeArray<Rest, Value & Carry> : _ResolveReturnTypeArray<Rest, Carry & {}> : Prettify<Carry>;
706
- export type AnyLocalHook = LocalHook<any, any, any, any, any>;
711
+ export type AnyLocalHook = LocalHook<any, any, any, any, any> & {
712
+ config?: {
713
+ allowMeta?: boolean;
714
+ mount?: Function;
715
+ cookie?: ElysiaConfig<any>['cookie'];
716
+ };
717
+ };
707
718
  export interface BaseHookLifeCycle<in out Schema extends RouteSchema, in out Singleton extends SingletonBase, in out Errors extends {
708
719
  [key in string]: Error;
709
720
  }, in out Parser extends keyof any = ''> {
package/dist/utils.d.ts CHANGED
@@ -3,6 +3,8 @@ import type { LifeCycleStore, MaybeArray, InputSchema, LifeCycleType, HookContai
3
3
  import { ElysiaFile } from './universal/file';
4
4
  export declare const replaceUrlPath: (url: string, pathname: string) => string;
5
5
  export declare const isClass: (v: Object) => boolean;
6
+ export declare const isSafeObjectKey: (key: string) => boolean;
7
+ export declare const safeAssign: <Target extends object>(target: Target, source: unknown) => Target;
6
8
  export declare const mergeDeep: <A extends Record<string, any>, B extends Record<string, any>>(target: A, source: B, options?: {
7
9
  skipKeys?: string[];
8
10
  override?: boolean;
package/dist/utils.js CHANGED
@@ -39,6 +39,7 @@ __export(utils_exports, {
39
39
  isClass: () => isClass,
40
40
  isNotEmpty: () => isNotEmpty,
41
41
  isNumericString: () => isNumericString,
42
+ isSafeObjectKey: () => isSafeObjectKey,
42
43
  lifeCycleToArray: () => lifeCycleToArray,
43
44
  lifeCycleToFn: () => lifeCycleToFn,
44
45
  localHookToLifeCycleStore: () => localHookToLifeCycleStore,
@@ -54,6 +55,7 @@ __export(utils_exports, {
54
55
  randomId: () => randomId,
55
56
  redirect: () => redirect,
56
57
  replaceUrlPath: () => replaceUrlPath,
58
+ safeAssign: () => safeAssign,
57
59
  signCookie: () => signCookie,
58
60
  sse: () => sse,
59
61
  supportPerMethodInlineHandler: () => supportPerMethodInlineHandler,
@@ -69,7 +71,15 @@ v.toString && // Handle import * as Sentry from '@sentry/bun'
69
71
  // This also handle [object Date], [object Array]
70
72
  // and FFI value like [object Prisma]
71
73
  v.toString().startsWith("[object ") && v.toString() !== "[object Object]" || // If object prototype is not pure, then probably a class-like object
72
- isNotEmpty(Object.getPrototypeOf(v)), isObject = (item) => item && typeof item == "object" && !Array.isArray(item), mergeDeep = (target, source, options) => {
74
+ isNotEmpty(Object.getPrototypeOf(v)), isObject = (item) => item && typeof item == "object" && !Array.isArray(item), dangerousKeys = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]), isSafeObjectKey = (key) => !dangerousKeys.has(key), safeAssign = (target, source) => {
75
+ if (source == null) return target;
76
+ const input = Object(source), output = target;
77
+ for (const key of Object.keys(input))
78
+ isSafeObjectKey(key) && (output[key] = input[key]);
79
+ for (const key of Object.getOwnPropertySymbols(input))
80
+ Object.prototype.propertyIsEnumerable.call(input, key) && (output[key] = input[key]);
81
+ return target;
82
+ }, mergeDeep = (target, source, options) => {
73
83
  const skipKeys = options?.skipKeys, override = options?.override ?? !0, mergeArray = options?.mergeArray ?? !1, seen = options?.seen ?? /* @__PURE__ */ new WeakSet();
74
84
  if (!isObject(target) || !isObject(source) || seen.has(source)) return target;
75
85
  seen.add(source);
@@ -444,17 +454,31 @@ const encoder = new TextEncoder(), signCookie = async (val, secret) => {
444
454
  encoder.encode(val)
445
455
  );
446
456
  return val + "." + removeTrailingEquals(Buffer.from(hmacBuffer).toString("base64"));
447
- }, constantTimeEqual = typeof crypto?.timingSafeEqual == "function" ? (a, b) => {
448
- const ab = Buffer.from(a, "utf8"), bb = Buffer.from(b, "utf8");
449
- return ab.length !== bb.length ? !1 : crypto.timingSafeEqual(ab, bb);
450
- } : (a, b) => a === b, unsignCookie = async (input, secret) => {
457
+ }, unsignCookie = async (input, secret) => {
451
458
  if (typeof input != "string")
452
459
  throw new TypeError("Signed cookie string must be provided.");
453
460
  const dot = input.lastIndexOf(".");
454
461
  if (dot === -1)
455
462
  return secret === null ? input : !1;
456
- const tentativeValue = input.slice(0, dot), expectedInput = await signCookie(tentativeValue, secret);
457
- return constantTimeEqual(expectedInput, input) ? tentativeValue : !1;
463
+ const tentativeValue = input.slice(0, dot), encodedSignature = input.slice(dot + 1);
464
+ if (!/^[A-Za-z0-9+/]{43}$/.test(encodedSignature)) return !1;
465
+ const signature = Buffer.from(encodedSignature, "base64");
466
+ if (removeTrailingEquals(signature.toString("base64")) !== encodedSignature)
467
+ return !1;
468
+ if (secret === null) throw new TypeError("Secret key must be provided");
469
+ const secretKey = await crypto.subtle.importKey(
470
+ "raw",
471
+ encoder.encode(secret),
472
+ { name: "HMAC", hash: "SHA-256" },
473
+ !1,
474
+ ["verify"]
475
+ );
476
+ return await crypto.subtle.verify(
477
+ "HMAC",
478
+ secretKey,
479
+ signature,
480
+ encoder.encode(tentativeValue)
481
+ ) ? tentativeValue : !1;
458
482
  }, insertStandaloneValidator = (hook, name, value) => {
459
483
  if (!hook.standaloneValidator?.length || !Array.isArray(hook.standaloneValidator)) {
460
484
  hook.standaloneValidator = [
@@ -598,13 +622,35 @@ const fnToContainer = (fn, subType) => {
598
622
  const payload = typeof _payload == "string" ? { data: _payload } : _payload;
599
623
  return payload.sse = !0, payload.toSSE = () => {
600
624
  let payloadString = "";
601
- return payload.id !== void 0 && payload.id !== null && (payloadString += `id: ${payload.id}
602
- `), payload.event && (payloadString += `event: ${payload.event}
603
- `), payload.retry !== void 0 && (payloadString += `retry: ${payload.retry}
604
- `), payload.data === null ? payloadString += `data: null
605
- ` : typeof payload.data == "string" ? payloadString += `data: ${payload.data}
606
- ` : typeof payload.data == "object" && (payloadString += `data: ${JSON.stringify(payload.data)}
607
- `), payloadString && (payloadString += `
625
+ if (payload.id !== void 0 && payload.id !== null) {
626
+ const id = String(payload.id);
627
+ if (/[\r\n]/.test(id))
628
+ throw new TypeError("SSE id must not contain newlines");
629
+ payloadString += `id: ${id}
630
+ `;
631
+ }
632
+ if (payload.event) {
633
+ if (/[\r\n]/.test(payload.event))
634
+ throw new TypeError("SSE event must not contain newlines");
635
+ payloadString += `event: ${payload.event}
636
+ `;
637
+ }
638
+ if (payload.retry !== void 0) {
639
+ if (!Number.isSafeInteger(payload.retry) || payload.retry < 0)
640
+ throw new TypeError(
641
+ "SSE retry must be a non-negative safe integer"
642
+ );
643
+ payloadString += `retry: ${payload.retry}
644
+ `;
645
+ }
646
+ const data = typeof payload.data == "string" ? payload.data : JSON.stringify(payload.data);
647
+ if (data !== void 0)
648
+ for (const line of data.replace(/\r\n?/g, `
649
+ `).split(`
650
+ `))
651
+ payloadString += `data: ${line}
652
+ `;
653
+ return payloadString && (payloadString += `
608
654
  `), payloadString;
609
655
  }, payload;
610
656
  };
@@ -675,6 +721,7 @@ function deepClone(source, weak = /* @__PURE__ */ new WeakMap()) {
675
721
  isClass,
676
722
  isNotEmpty,
677
723
  isNumericString,
724
+ isSafeObjectKey,
678
725
  lifeCycleToArray,
679
726
  lifeCycleToFn,
680
727
  localHookToLifeCycleStore,
@@ -690,6 +737,7 @@ function deepClone(source, weak = /* @__PURE__ */ new WeakMap()) {
690
737
  randomId,
691
738
  redirect,
692
739
  replaceUrlPath,
740
+ safeAssign,
693
741
  signCookie,
694
742
  sse,
695
743
  supportPerMethodInlineHandler,
package/dist/utils.mjs CHANGED
@@ -8,7 +8,15 @@ v.toString && // Handle import * as Sentry from '@sentry/bun'
8
8
  // This also handle [object Date], [object Array]
9
9
  // and FFI value like [object Prisma]
10
10
  v.toString().startsWith("[object ") && v.toString() !== "[object Object]" || // If object prototype is not pure, then probably a class-like object
11
- isNotEmpty(Object.getPrototypeOf(v)), isObject = (item) => item && typeof item == "object" && !Array.isArray(item), mergeDeep = (target, source, options) => {
11
+ isNotEmpty(Object.getPrototypeOf(v)), isObject = (item) => item && typeof item == "object" && !Array.isArray(item), dangerousKeys = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]), isSafeObjectKey = (key) => !dangerousKeys.has(key), safeAssign = (target, source) => {
12
+ if (source == null) return target;
13
+ const input = Object(source), output = target;
14
+ for (const key of Object.keys(input))
15
+ isSafeObjectKey(key) && (output[key] = input[key]);
16
+ for (const key of Object.getOwnPropertySymbols(input))
17
+ Object.prototype.propertyIsEnumerable.call(input, key) && (output[key] = input[key]);
18
+ return target;
19
+ }, mergeDeep = (target, source, options) => {
12
20
  const skipKeys = options?.skipKeys, override = options?.override ?? !0, mergeArray = options?.mergeArray ?? !1, seen = options?.seen ?? /* @__PURE__ */ new WeakSet();
13
21
  if (!isObject(target) || !isObject(source) || seen.has(source)) return target;
14
22
  seen.add(source);
@@ -383,17 +391,31 @@ const encoder = new TextEncoder(), signCookie = async (val, secret) => {
383
391
  encoder.encode(val)
384
392
  );
385
393
  return val + "." + removeTrailingEquals(Buffer.from(hmacBuffer).toString("base64"));
386
- }, constantTimeEqual = typeof crypto?.timingSafeEqual == "function" ? (a, b) => {
387
- const ab = Buffer.from(a, "utf8"), bb = Buffer.from(b, "utf8");
388
- return ab.length !== bb.length ? !1 : crypto.timingSafeEqual(ab, bb);
389
- } : (a, b) => a === b, unsignCookie = async (input, secret) => {
394
+ }, unsignCookie = async (input, secret) => {
390
395
  if (typeof input != "string")
391
396
  throw new TypeError("Signed cookie string must be provided.");
392
397
  const dot = input.lastIndexOf(".");
393
398
  if (dot === -1)
394
399
  return secret === null ? input : !1;
395
- const tentativeValue = input.slice(0, dot), expectedInput = await signCookie(tentativeValue, secret);
396
- return constantTimeEqual(expectedInput, input) ? tentativeValue : !1;
400
+ const tentativeValue = input.slice(0, dot), encodedSignature = input.slice(dot + 1);
401
+ if (!/^[A-Za-z0-9+/]{43}$/.test(encodedSignature)) return !1;
402
+ const signature = Buffer.from(encodedSignature, "base64");
403
+ if (removeTrailingEquals(signature.toString("base64")) !== encodedSignature)
404
+ return !1;
405
+ if (secret === null) throw new TypeError("Secret key must be provided");
406
+ const secretKey = await crypto.subtle.importKey(
407
+ "raw",
408
+ encoder.encode(secret),
409
+ { name: "HMAC", hash: "SHA-256" },
410
+ !1,
411
+ ["verify"]
412
+ );
413
+ return await crypto.subtle.verify(
414
+ "HMAC",
415
+ secretKey,
416
+ signature,
417
+ encoder.encode(tentativeValue)
418
+ ) ? tentativeValue : !1;
397
419
  }, insertStandaloneValidator = (hook, name, value) => {
398
420
  if (!hook.standaloneValidator?.length || !Array.isArray(hook.standaloneValidator)) {
399
421
  hook.standaloneValidator = [
@@ -537,13 +559,35 @@ const fnToContainer = (fn, subType) => {
537
559
  const payload = typeof _payload == "string" ? { data: _payload } : _payload;
538
560
  return payload.sse = !0, payload.toSSE = () => {
539
561
  let payloadString = "";
540
- return payload.id !== void 0 && payload.id !== null && (payloadString += `id: ${payload.id}
541
- `), payload.event && (payloadString += `event: ${payload.event}
542
- `), payload.retry !== void 0 && (payloadString += `retry: ${payload.retry}
543
- `), payload.data === null ? payloadString += `data: null
544
- ` : typeof payload.data == "string" ? payloadString += `data: ${payload.data}
545
- ` : typeof payload.data == "object" && (payloadString += `data: ${JSON.stringify(payload.data)}
546
- `), payloadString && (payloadString += `
562
+ if (payload.id !== void 0 && payload.id !== null) {
563
+ const id = String(payload.id);
564
+ if (/[\r\n]/.test(id))
565
+ throw new TypeError("SSE id must not contain newlines");
566
+ payloadString += `id: ${id}
567
+ `;
568
+ }
569
+ if (payload.event) {
570
+ if (/[\r\n]/.test(payload.event))
571
+ throw new TypeError("SSE event must not contain newlines");
572
+ payloadString += `event: ${payload.event}
573
+ `;
574
+ }
575
+ if (payload.retry !== void 0) {
576
+ if (!Number.isSafeInteger(payload.retry) || payload.retry < 0)
577
+ throw new TypeError(
578
+ "SSE retry must be a non-negative safe integer"
579
+ );
580
+ payloadString += `retry: ${payload.retry}
581
+ `;
582
+ }
583
+ const data = typeof payload.data == "string" ? payload.data : JSON.stringify(payload.data);
584
+ if (data !== void 0)
585
+ for (const line of data.replace(/\r\n?/g, `
586
+ `).split(`
587
+ `))
588
+ payloadString += `data: ${line}
589
+ `;
590
+ return payloadString && (payloadString += `
547
591
  `), payloadString;
548
592
  }, payload;
549
593
  };
@@ -613,6 +657,7 @@ export {
613
657
  isClass,
614
658
  isNotEmpty,
615
659
  isNumericString,
660
+ isSafeObjectKey,
616
661
  lifeCycleToArray,
617
662
  lifeCycleToFn,
618
663
  localHookToLifeCycleStore,
@@ -628,6 +673,7 @@ export {
628
673
  randomId,
629
674
  redirect,
630
675
  replaceUrlPath,
676
+ safeAssign,
631
677
  signCookie,
632
678
  sse,
633
679
  supportPerMethodInlineHandler,
@@ -73,5 +73,5 @@ export declare class ElysiaWS<Context = unknown, Route extends RouteSchema = {}>
73
73
  get id(): string;
74
74
  }
75
75
  export declare const createWSMessageParser: (parse: MaybeArray<WSParseHandler<any>>) => (ws: ServerWebSocket<any>, message: any) => Promise<any>;
76
- export declare const createHandleWSResponse: (responseValidator: TypeCheck<any> | ElysiaTypeCheck<any> | undefined) => (ws: ServerWebSocket<any>, data: unknown) => unknown;
76
+ export declare const createHandleWSResponse: (responseValidator: TypeCheck<any> | ElysiaTypeCheck<any> | undefined) => (ws: ServerWebSocket<any>, data: unknown) => Promise<unknown>;
77
77
  export type { WSLocalHook } from './types';