seroval 0.4.0-alpha.1 → 0.4.0-alpha.2

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/src/tree.ts CHANGED
@@ -1,3 +1,4 @@
1
+ /* eslint-disable @typescript-eslint/no-use-before-define */
1
2
  /* eslint-disable guard-for-in */
2
3
  /* eslint-disable @typescript-eslint/no-for-in-array */
3
4
  /* eslint-disable no-await-in-loop */
@@ -535,35 +536,37 @@ function generateSemiPrimitiveValue(
535
536
  return undefined;
536
537
  }
537
538
 
539
+ function serializePropertiesSync(
540
+ ctx: SerializationContext,
541
+ properties: Record<string, unknown>,
542
+ ): SerovalDictionaryNode {
543
+ const keyNodes: string[] = [];
544
+ const valueNodes: SerovalNode[] = [];
545
+ const deferredKeys: string[] = [];
546
+ const deferredValues: ServerValue[] = [];
547
+ const keys = Object.keys(properties);
548
+ let deferredSize = 0;
549
+ for (const key of keys) {
550
+ if (isIterable(properties[key])) {
551
+ deferredKeys.push(key);
552
+ deferredValues.push(properties[key] as ServerValue);
553
+ deferredSize++;
554
+ } else {
555
+ keyNodes.push(key);
556
+ valueNodes.push(generateTreeSync(ctx, properties[key] as ServerValue));
557
+ }
558
+ }
559
+ for (let i = 0; i < deferredSize; i++) {
560
+ keyNodes.push(deferredKeys[i]);
561
+ valueNodes.push(generateTreeSync(ctx, deferredValues[i]));
562
+ }
563
+ return [keyNodes, valueNodes, keys.length];
564
+ }
565
+
538
566
  export function generateTreeSync(
539
567
  ctx: SerializationContext,
540
568
  current: ServerValue,
541
569
  ): SerovalNode {
542
- function serializeProperties(
543
- properties: Record<string, unknown>,
544
- ): SerovalDictionaryNode {
545
- const keyNodes: string[] = [];
546
- const valueNodes: SerovalNode[] = [];
547
- const deferredKeys: string[] = [];
548
- const deferredValues: ServerValue[] = [];
549
- const keys = Object.keys(properties);
550
- let deferredSize = 0;
551
- for (const key of keys) {
552
- if (isIterable(properties[key])) {
553
- deferredKeys.push(key);
554
- deferredValues.push(properties[key] as ServerValue);
555
- deferredSize++;
556
- } else {
557
- keyNodes.push(key);
558
- valueNodes.push(generateTreeSync(ctx, properties[key] as ServerValue));
559
- }
560
- }
561
- for (let i = 0; i < deferredSize; i++) {
562
- keyNodes.push(deferredKeys[i]);
563
- valueNodes.push(generateTreeSync(ctx, deferredValues[i]));
564
- }
565
- return [keyNodes, valueNodes, keys.length];
566
- }
567
570
  if (isPrimitive(current)) {
568
571
  return [SerovalNodeType.Primitive, current];
569
572
  }
@@ -642,7 +645,7 @@ export function generateTreeSync(
642
645
  if (current instanceof AggregateError && ctx.features.has(Feature.AggregateError)) {
643
646
  const options = getErrorOptions(current);
644
647
  const optionsNode = options
645
- ? serializeProperties(options)
648
+ ? serializePropertiesSync(ctx, options)
646
649
  : undefined;
647
650
  const errorsNode = generateTreeSync(ctx, current.errors as ServerValue);
648
651
  return [SerovalNodeType.AggregateError, [current.message, optionsNode, errorsNode], id];
@@ -650,7 +653,7 @@ export function generateTreeSync(
650
653
  if (current instanceof Error) {
651
654
  const options = getErrorOptions(current);
652
655
  const optionsNode = options
653
- ? serializeProperties(options)
656
+ ? serializePropertiesSync(ctx, options)
654
657
  : undefined;
655
658
  return [
656
659
  SerovalNodeType.Error,
@@ -663,7 +666,7 @@ export function generateTreeSync(
663
666
  const options = getIterableOptions(current);
664
667
  return [SerovalNodeType.Iterable, [
665
668
  // Parse options first before the items
666
- options ? serializeProperties(options) : undefined,
669
+ options ? serializePropertiesSync(ctx, options) : undefined,
667
670
  generateTreeSync(ctx, Array.from(current)),
668
671
  ], id];
669
672
  }
@@ -671,42 +674,44 @@ export function generateTreeSync(
671
674
  if (current.constructor === Object || empty) {
672
675
  return [
673
676
  empty ? SerovalNodeType.NullConstructor : SerovalNodeType.Object,
674
- serializeProperties(current as Record<string, unknown>),
677
+ serializePropertiesSync(ctx, current as Record<string, unknown>),
675
678
  id,
676
679
  ];
677
680
  }
678
681
  throw new Error('Unsupported value');
679
682
  }
680
683
 
684
+ async function serializePropertiesAsync(
685
+ ctx: SerializationContext,
686
+ properties: Record<string, unknown>,
687
+ ): Promise<SerovalDictionaryNode> {
688
+ const keyNodes: string[] = [];
689
+ const valueNodes: SerovalNode[] = [];
690
+ const deferredKeys: string[] = [];
691
+ const deferredValues: ServerValue[] = [];
692
+ const keys = Object.keys(properties);
693
+ let deferredSize = 0;
694
+ for (const key of keys) {
695
+ if (isIterable(properties[key])) {
696
+ deferredKeys.push(key);
697
+ deferredValues.push(properties[key] as ServerValue);
698
+ deferredSize++;
699
+ } else {
700
+ keyNodes.push(key);
701
+ valueNodes.push(await generateTreeAsync(ctx, properties[key] as ServerValue));
702
+ }
703
+ }
704
+ for (let i = 0; i < deferredSize; i++) {
705
+ keyNodes.push(deferredKeys[i]);
706
+ valueNodes.push(await generateTreeAsync(ctx, deferredValues[i]));
707
+ }
708
+ return [keyNodes, valueNodes, keys.length];
709
+ }
710
+
681
711
  export async function generateTreeAsync(
682
712
  ctx: SerializationContext,
683
713
  current: AsyncServerValue,
684
714
  ): Promise<SerovalNode> {
685
- async function serializeProperties(
686
- properties: Record<string, unknown>,
687
- ): Promise<SerovalDictionaryNode> {
688
- const keyNodes: string[] = [];
689
- const valueNodes: SerovalNode[] = [];
690
- const deferredKeys: string[] = [];
691
- const deferredValues: ServerValue[] = [];
692
- const keys = Object.keys(properties);
693
- let deferredSize = 0;
694
- for (const key of keys) {
695
- if (isIterable(properties[key])) {
696
- deferredKeys.push(key);
697
- deferredValues.push(properties[key] as ServerValue);
698
- deferredSize++;
699
- } else {
700
- keyNodes.push(key);
701
- valueNodes.push(await generateTreeAsync(ctx, properties[key] as ServerValue));
702
- }
703
- }
704
- for (let i = 0; i < deferredSize; i++) {
705
- keyNodes.push(deferredKeys[i]);
706
- valueNodes.push(await generateTreeAsync(ctx, deferredValues[i]));
707
- }
708
- return [keyNodes, valueNodes, keys.length];
709
- }
710
715
  if (isPrimitive(current)) {
711
716
  return [SerovalNodeType.Primitive, current];
712
717
  }
@@ -789,7 +794,7 @@ export async function generateTreeAsync(
789
794
  if (current instanceof AggregateError) {
790
795
  const options = getErrorOptions(current);
791
796
  const optionsNode = options
792
- ? await serializeProperties(options)
797
+ ? await serializePropertiesAsync(ctx, options)
793
798
  : undefined;
794
799
  const errorsNode = await generateTreeAsync(ctx, current.errors as ServerValue);
795
800
  return [SerovalNodeType.AggregateError, [current.message, optionsNode, errorsNode], id];
@@ -797,7 +802,7 @@ export async function generateTreeAsync(
797
802
  if (current instanceof Error) {
798
803
  const options = getErrorOptions(current);
799
804
  const optionsNode = options
800
- ? await serializeProperties(options)
805
+ ? await serializePropertiesAsync(ctx, options)
801
806
  : undefined;
802
807
  return [
803
808
  SerovalNodeType.Error,
@@ -810,7 +815,7 @@ export async function generateTreeAsync(
810
815
  const options = getIterableOptions(current);
811
816
  return [SerovalNodeType.Iterable, [
812
817
  options
813
- ? await serializeProperties(options)
818
+ ? await serializePropertiesAsync(ctx, options)
814
819
  : undefined,
815
820
  await generateTreeAsync(ctx, Array.from(current)),
816
821
  ], id];
@@ -819,7 +824,7 @@ export async function generateTreeAsync(
819
824
  if (current.constructor === Object || empty) {
820
825
  return [
821
826
  empty ? SerovalNodeType.NullConstructor : SerovalNodeType.Object,
822
- await serializeProperties(current as Record<string, unknown>),
827
+ await serializePropertiesAsync(ctx, current as Record<string, unknown>),
823
828
  id,
824
829
  ];
825
830
  }
@@ -863,80 +868,86 @@ export function serializePrimitive(
863
868
  return String(value);
864
869
  }
865
870
 
866
- export function serializeTree(
871
+ const IDENTIFIER_CHECK = /^([$A-Z_][0-9A-Z_$]*)$/i;
872
+
873
+ function serializeAssignments(
867
874
  ctx: SerializationContext,
868
- [type, value, id]: SerovalNode,
869
- ): string {
870
- function serializeAssignments(
871
- targetRef: number,
872
- [keys, values, size]: SerovalDictionaryNode,
873
- ) {
874
- ctx.stack.push(targetRef);
875
- const mainAssignments: Assignment[] = [];
876
- for (let i = 0; i < size; i++) {
877
- const parentStack = ctx.stack;
878
- ctx.stack = [];
879
- const refParam = serializeTree(ctx, values[i]);
880
- ctx.stack = parentStack;
881
- const key = keys[i];
882
- const check = Number(key);
883
- // Test if key is a valid number or JS identifier
884
- // so that we don't have to serialize the key and wrap with brackets
885
- const parentAssignment = ctx.assignments;
886
- ctx.assignments = mainAssignments;
887
- if (
888
- check >= 0
889
- || (Number.isNaN(check) && /^([$A-Z_][0-9A-Z_$]*)$/i.test(key))
890
- ) {
891
- if (!Number.isNaN(check)) {
892
- createObjectComputedAssign(ctx, targetRef, key, refParam);
893
- } else {
894
- createObjectIdentifierAssign(ctx, targetRef, key, refParam);
895
- }
875
+ targetRef: number,
876
+ [keys, values, size]: SerovalDictionaryNode,
877
+ ) {
878
+ ctx.stack.push(targetRef);
879
+ const mainAssignments: Assignment[] = [];
880
+ for (let i = 0; i < size; i++) {
881
+ const parentStack = ctx.stack;
882
+ ctx.stack = [];
883
+ const refParam = serializeTree(ctx, values[i]);
884
+ ctx.stack = parentStack;
885
+ const key = keys[i];
886
+ const check = Number(key);
887
+ // Test if key is a valid number or JS identifier
888
+ // so that we don't have to serialize the key and wrap with brackets
889
+ const parentAssignment = ctx.assignments;
890
+ ctx.assignments = mainAssignments;
891
+ if (
892
+ check >= 0
893
+ || (Number.isNaN(check) && IDENTIFIER_CHECK.test(key))
894
+ ) {
895
+ if (!Number.isNaN(check)) {
896
+ createObjectComputedAssign(ctx, targetRef, key, refParam);
896
897
  } else {
897
- createObjectComputedAssign(ctx, targetRef, quote(key), refParam);
898
+ createObjectIdentifierAssign(ctx, targetRef, key, refParam);
898
899
  }
899
- ctx.assignments = parentAssignment;
900
+ } else {
901
+ createObjectComputedAssign(ctx, targetRef, quote(key), refParam);
900
902
  }
901
- ctx.stack.pop();
902
- return resolveAssignments(mainAssignments);
903
+ ctx.assignments = parentAssignment;
903
904
  }
904
- function serializeObject(
905
- sourceID: number,
906
- [keys, values, size]: SerovalDictionaryNode,
907
- ) {
908
- let result = '';
909
- ctx.stack.push(sourceID);
910
- for (let i = 0; i < size; i++) {
911
- const key = keys[i];
912
- const val = values[i];
913
- const check = Number(key);
914
- // Test if key is a valid number or JS identifier
915
- // so that we don't have to serialize the key and wrap with brackets
916
- if (
917
- check >= 0
918
- || (Number.isNaN(check) && /^([$A-Z_][0-9A-Z_$]*)$/i.test(key))
919
- ) {
920
- if (isReferenceInStack(ctx, val)) {
921
- const refParam = getRefParam(ctx, val[1]);
922
- if (!Number.isNaN(check)) {
923
- createObjectComputedAssign(ctx, sourceID, key, refParam);
924
- } else {
925
- createObjectIdentifierAssign(ctx, sourceID, key, refParam);
926
- }
905
+ ctx.stack.pop();
906
+ return resolveAssignments(mainAssignments);
907
+ }
908
+
909
+ function serializeObject(
910
+ ctx: SerializationContext,
911
+ sourceID: number,
912
+ [keys, values, size]: SerovalDictionaryNode,
913
+ ) {
914
+ let result = '';
915
+ ctx.stack.push(sourceID);
916
+ for (let i = 0; i < size; i++) {
917
+ const key = keys[i];
918
+ const val = values[i];
919
+ const check = Number(key);
920
+ // Test if key is a valid number or JS identifier
921
+ // so that we don't have to serialize the key and wrap with brackets
922
+ if (
923
+ check >= 0
924
+ || (Number.isNaN(check) && IDENTIFIER_CHECK.test(key))
925
+ ) {
926
+ if (isReferenceInStack(ctx, val)) {
927
+ const refParam = getRefParam(ctx, val[1]);
928
+ if (!Number.isNaN(check)) {
929
+ createObjectComputedAssign(ctx, sourceID, key, refParam);
927
930
  } else {
928
- result += `${key}:${serializeTree(ctx, val)},`;
931
+ createObjectIdentifierAssign(ctx, sourceID, key, refParam);
929
932
  }
930
- } else if (isReferenceInStack(ctx, val)) {
931
- const refParam = getRefParam(ctx, val[1]);
932
- createObjectComputedAssign(ctx, sourceID, quote(key), refParam);
933
933
  } else {
934
- result += `${quote(key)}:${serializeTree(ctx, val)},`;
934
+ result += `${key}:${serializeTree(ctx, val)},`;
935
935
  }
936
+ } else if (isReferenceInStack(ctx, val)) {
937
+ const refParam = getRefParam(ctx, val[1]);
938
+ createObjectComputedAssign(ctx, sourceID, quote(key), refParam);
939
+ } else {
940
+ result += `${quote(key)}:${serializeTree(ctx, val)},`;
936
941
  }
937
- ctx.stack.pop();
938
- return `{${result.substring(0, result.length - 1)}}`;
939
942
  }
943
+ ctx.stack.pop();
944
+ return `{${result.substring(0, result.length - 1)}}`;
945
+ }
946
+
947
+ export function serializeTree(
948
+ ctx: SerializationContext,
949
+ [type, value, id]: SerovalNode,
950
+ ): string {
940
951
  switch (type) {
941
952
  case SerovalNodeType.Primitive:
942
953
  return serializePrimitive(ctx, value);
@@ -1090,11 +1101,11 @@ export function serializeTree(
1090
1101
  // Make sure to assign extra properties
1091
1102
  if (value[1]) {
1092
1103
  if (ctx.features.has(Feature.ObjectAssign)) {
1093
- const options = serializeObject(id, value[1]);
1104
+ const options = serializeObject(ctx, id, value[1]);
1094
1105
  serialized = `Object.assign(${serialized},${options})`;
1095
1106
  } else {
1096
1107
  markRef(ctx, id);
1097
- const assignments = serializeAssignments(id, value[1]);
1108
+ const assignments = serializeAssignments(ctx, id, value[1]);
1098
1109
  if (assignments) {
1099
1110
  const ref = getRefParam(ctx, id);
1100
1111
  return `(${assignRef(ctx, id, serialized)},${assignments}${ref})`;
@@ -1107,11 +1118,11 @@ export function serializeTree(
1107
1118
  let serialized = `new ${value[0]}(${quote(value[1])})`;
1108
1119
  if (value[2]) {
1109
1120
  if (ctx.features.has(Feature.ObjectAssign)) {
1110
- const options = serializeObject(id, value[2]);
1121
+ const options = serializeObject(ctx, id, value[2]);
1111
1122
  serialized = `Object.assign(${serialized},${options})`;
1112
1123
  } else {
1113
1124
  markRef(ctx, id);
1114
- const assignments = serializeAssignments(id, value[2]);
1125
+ const assignments = serializeAssignments(ctx, id, value[2]);
1115
1126
  if (assignments) {
1116
1127
  const ref = getRefParam(ctx, id);
1117
1128
  return `(${assignRef(ctx, id, serialized)},${assignments}${ref})`;
@@ -1140,11 +1151,11 @@ export function serializeTree(
1140
1151
  }
1141
1152
  if (value[0]) {
1142
1153
  if (ctx.features.has(Feature.ObjectAssign)) {
1143
- const options = serializeObject(id, value[0]);
1154
+ const options = serializeObject(ctx, id, value[0]);
1144
1155
  serialized = `Object.assign(${serialized},${options})`;
1145
1156
  } else {
1146
1157
  markRef(ctx, id);
1147
- const assignments = serializeAssignments(id, value[0]);
1158
+ const assignments = serializeAssignments(ctx, id, value[0]);
1148
1159
  if (assignments) {
1149
1160
  const ref = getRefParam(ctx, id);
1150
1161
  return `(${assignRef(ctx, id, serialized)},${assignments}${ref})`;
@@ -1156,13 +1167,13 @@ export function serializeTree(
1156
1167
  case SerovalNodeType.NullConstructor: {
1157
1168
  let serialized = 'Object.create(null)';
1158
1169
  if (ctx.features.has(Feature.ObjectAssign)) {
1159
- const fields = serializeObject(id, value);
1170
+ const fields = serializeObject(ctx, id, value);
1160
1171
  if (fields !== '{}') {
1161
1172
  serialized = `Object.assign(${serialized},${fields})`;
1162
1173
  }
1163
1174
  } else {
1164
1175
  markRef(ctx, id);
1165
- const assignments = serializeAssignments(id, value);
1176
+ const assignments = serializeAssignments(ctx, id, value);
1166
1177
  if (assignments) {
1167
1178
  const ref = getRefParam(ctx, id);
1168
1179
  return `(${assignRef(ctx, id, serialized)},${assignments}${ref})`;
@@ -1171,7 +1182,7 @@ export function serializeTree(
1171
1182
  return assignRef(ctx, id, serialized);
1172
1183
  }
1173
1184
  case SerovalNodeType.Object:
1174
- return assignRef(ctx, id, serializeObject(id, value));
1185
+ return assignRef(ctx, id, serializeObject(ctx, id, value));
1175
1186
  default:
1176
1187
  throw new Error('Unsupported type');
1177
1188
  }