@colyseus/schema 3.0.53 → 3.0.55

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.
@@ -683,15 +683,7 @@ class TypeContext {
683
683
  if (typeof (fieldType) === "string") {
684
684
  continue;
685
685
  }
686
- if (Array.isArray(fieldType)) {
687
- const type = fieldType[0];
688
- // skip primitive types
689
- if (type === "string") {
690
- continue;
691
- }
692
- this.discoverTypes(type, klass, index, parentHasViewTag || fieldHasViewTag);
693
- }
694
- else if (typeof (fieldType) === "function") {
686
+ if (typeof (fieldType) === "function") {
695
687
  this.discoverTypes(fieldType, klass, index, parentHasViewTag || fieldHasViewTag);
696
688
  }
697
689
  else {
@@ -741,11 +733,43 @@ class TypeContext {
741
733
  }
742
734
 
743
735
  function getNormalizedType(type) {
744
- return (Array.isArray(type))
745
- ? { array: type[0] }
746
- : (typeof (type['type']) !== "undefined")
747
- ? type['type']
748
- : type;
736
+ if (Array.isArray(type)) {
737
+ return { array: getNormalizedType(type[0]) };
738
+ }
739
+ else if (typeof (type['type']) !== "undefined") {
740
+ return type['type'];
741
+ }
742
+ else if (isTSEnum(type)) {
743
+ // Detect TS Enum type (either string or number)
744
+ return Object.keys(type).every(key => typeof type[key] === "string")
745
+ ? "string"
746
+ : "number";
747
+ }
748
+ else if (typeof type === "object" && type !== null) {
749
+ // Handle collection types
750
+ const collectionType = Object.keys(type).find(k => registeredTypes[k] !== undefined);
751
+ if (collectionType) {
752
+ type[collectionType] = getNormalizedType(type[collectionType]);
753
+ return type;
754
+ }
755
+ }
756
+ return type;
757
+ }
758
+ function isTSEnum(_enum) {
759
+ if (typeof _enum === 'function' && _enum[Symbol.metadata]) {
760
+ return false;
761
+ }
762
+ const keys = Object.keys(_enum);
763
+ const numericFields = keys.filter(k => /\d+/.test(k));
764
+ // Check for number enum (has numeric keys and reverse mapping)
765
+ if (numericFields.length > 0 && numericFields.length === (keys.length / 2) && _enum[_enum[numericFields[0]]] == numericFields[0]) {
766
+ return true;
767
+ }
768
+ // Check for string enum (all values are strings and keys match values)
769
+ if (keys.length > 0 && keys.every(key => typeof _enum[key] === 'string' && _enum[key] === key)) {
770
+ return true;
771
+ }
772
+ return false;
749
773
  }
750
774
  const Metadata = {
751
775
  addField(metadata, index, name, type, descriptor) {
@@ -860,14 +884,12 @@ const Metadata = {
860
884
  ?? -1; // no fields defined
861
885
  fieldIndex++;
862
886
  for (const field in fields) {
863
- const type = fields[field];
887
+ const type = getNormalizedType(fields[field]);
864
888
  // FIXME: this code is duplicated from @type() annotation
865
- const complexTypeKlass = (Array.isArray(type))
866
- ? getType("array")
867
- : (typeof (Object.keys(type)[0]) === "string") && getType(Object.keys(type)[0]);
889
+ const complexTypeKlass = typeof (Object.keys(type)[0]) === "string" && getType(Object.keys(type)[0]);
868
890
  const childType = (complexTypeKlass)
869
891
  ? Object.values(type)[0]
870
- : getNormalizedType(type);
892
+ : type;
871
893
  Metadata.addField(metadata, fieldIndex, field, type, getPropertyDescriptor(`_${field}`, fieldIndex, childType, complexTypeKlass));
872
894
  fieldIndex++;
873
895
  }
@@ -3329,6 +3351,8 @@ function type(type, options) {
3329
3351
  if (!type) {
3330
3352
  throw new Error(`${constructor.name}: @type() reference provided for "${field}" is undefined. Make sure you don't have any circular dependencies.`);
3331
3353
  }
3354
+ // Normalize type (enum/collection/etc)
3355
+ type = getNormalizedType(type);
3332
3356
  // for inheritance support
3333
3357
  TypeContext.register(constructor);
3334
3358
  const parentClass = Object.getPrototypeOf(constructor);
@@ -3373,9 +3397,7 @@ function type(type, options) {
3373
3397
  });
3374
3398
  }
3375
3399
  else {
3376
- const complexTypeKlass = (Array.isArray(type))
3377
- ? getType("array")
3378
- : (typeof (Object.keys(type)[0]) === "string") && getType(Object.keys(type)[0]);
3400
+ const complexTypeKlass = typeof (Object.keys(type)[0]) === "string" && getType(Object.keys(type)[0]);
3379
3401
  const childType = (complexTypeKlass)
3380
3402
  ? Object.values(type)[0]
3381
3403
  : type;
@@ -3502,9 +3524,10 @@ function schema(fieldsAndMethods, name, inherits = Schema) {
3502
3524
  ? DEFAULT_VIEW_TAG
3503
3525
  : value['view'];
3504
3526
  }
3505
- fields[fieldName] = value;
3527
+ fields[fieldName] = getNormalizedType(value);
3506
3528
  // If no explicit default provided, handle automatic instantiation for collection types
3507
3529
  if (!Object.prototype.hasOwnProperty.call(value, 'default')) {
3530
+ // TODO: remove Array.isArray() check. Use ['array'] !== undefined only.
3508
3531
  if (Array.isArray(value) || value['array'] !== undefined) {
3509
3532
  // Collection: Array → new ArraySchema()
3510
3533
  defaultValues[fieldName] = new ArraySchema();
@@ -3534,14 +3557,14 @@ function schema(fieldsAndMethods, name, inherits = Schema) {
3534
3557
  if (Schema.is(value)) {
3535
3558
  // Direct Schema type: Type → new Type()
3536
3559
  defaultValues[fieldName] = new value();
3537
- fields[fieldName] = value;
3560
+ fields[fieldName] = getNormalizedType(value);
3538
3561
  }
3539
3562
  else {
3540
3563
  methods[fieldName] = value;
3541
3564
  }
3542
3565
  }
3543
3566
  else {
3544
- fields[fieldName] = value;
3567
+ fields[fieldName] = getNormalizedType(value);
3545
3568
  }
3546
3569
  }
3547
3570
  const getDefaultValues = () => {
@@ -3983,7 +4006,7 @@ class Root {
3983
4006
  changeTree.forEachChild((child, _) => {
3984
4007
  if (child.removeParent(changeTree.ref)) {
3985
4008
  if ((child.parentChain === undefined || // no parent, remove it
3986
- (child.parentChain && this.refCount[child.refId] > 1) // parent is still in use, but has more than one reference, remove it
4009
+ (child.parentChain && this.refCount[child.refId] > 0) // parent is still in use, but has more than one reference, remove it
3987
4010
  )) {
3988
4011
  this.remove(child);
3989
4012
  }