seroval 0.4.0-alpha.6 → 0.4.0-alpha.8

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/context.ts CHANGED
@@ -28,16 +28,17 @@ export type Assignment =
28
28
 
29
29
  export interface ParserContext {
30
30
  refs: Map<unknown, number>;
31
- markedRefs: Set<number>;
31
+ markedRefs: Record<number, number>;
32
32
  features: number;
33
33
  }
34
34
 
35
35
  export interface SerializationContext {
36
36
  stack: number[];
37
37
  // Map tree refs to actual refs
38
- validRefs: Map<number, number>;
38
+ validRefs: number[];
39
+ refSize: number;
39
40
  // Refs that are...referenced
40
- markedRefs: Set<number>;
41
+ markedRefs: Record<number, number>;
41
42
  // Variables
42
43
  vars: string[];
43
44
  // Array of assignments to be done (used for recursion)
@@ -58,14 +59,14 @@ export function createParserContext(options: Partial<Options> = {}): ParserConte
58
59
  // eslint-disable-next-line prefer-object-spread
59
60
  const result = Object.assign({}, DEFAULT_OPTIONS, options || {});
60
61
  return {
61
- markedRefs: new Set(),
62
+ markedRefs: {},
62
63
  refs: new Map(),
63
64
  features: parseTargets(result.target),
64
65
  };
65
66
  }
66
67
 
67
68
  export interface SerializationOptions {
68
- markedRefs: number[] | Set<number>;
69
+ markedRefs: Record<number, number>;
69
70
  features: number;
70
71
  }
71
72
 
@@ -74,9 +75,10 @@ export function createSerializationContext(options: SerializationOptions): Seria
74
75
  stack: [],
75
76
  vars: [],
76
77
  assignments: [],
77
- validRefs: new Map(),
78
+ validRefs: [],
79
+ refSize: 0,
78
80
  features: options.features,
79
- markedRefs: new Set(options.markedRefs),
81
+ markedRefs: options.markedRefs,
80
82
  };
81
83
  }
82
84
 
@@ -84,7 +86,7 @@ export function createSerializationContext(options: SerializationOptions): Seria
84
86
  * Increments the number of references the referenced value has
85
87
  */
86
88
  export function markRef(ctx: ParserContext | SerializationContext, current: number) {
87
- ctx.markedRefs.add(current);
89
+ ctx.markedRefs[current] = 1;
88
90
  }
89
91
 
90
92
  /**
@@ -97,13 +99,13 @@ function createValidRef(
97
99
  ctx: SerializationContext,
98
100
  index: number,
99
101
  ) {
100
- const current = ctx.validRefs.get(index);
101
- if (current != null) {
102
- return current;
102
+ const current = ctx.validRefs[index];
103
+ if (current == null) {
104
+ const value = ctx.refSize++;
105
+ ctx.validRefs[index] = value;
106
+ return value;
103
107
  }
104
- const id = ctx.validRefs.size;
105
- ctx.validRefs.set(index, id);
106
- return id;
108
+ return current;
107
109
  }
108
110
 
109
111
  /**
package/src/index.ts CHANGED
@@ -106,7 +106,7 @@ type SerovalJSON = [
106
106
  root: number,
107
107
  isObject: boolean,
108
108
  feature: number,
109
- markedRefs: number[],
109
+ markedRefs: Record<number, number>,
110
110
  ];
111
111
 
112
112
  export function toJSON<T extends ServerValue>(
@@ -120,7 +120,7 @@ export function toJSON<T extends ServerValue>(
120
120
  root,
121
121
  isObject,
122
122
  ctx.features,
123
- [...ctx.markedRefs],
123
+ ctx.markedRefs,
124
124
  ]);
125
125
  }
126
126
 
@@ -135,7 +135,7 @@ export async function toJSONAsync<T extends AsyncServerValue>(
135
135
  root,
136
136
  isObject,
137
137
  ctx.features,
138
- [...ctx.markedRefs],
138
+ ctx.markedRefs,
139
139
  ]);
140
140
  }
141
141
 
package/src/tree/async.ts CHANGED
@@ -30,24 +30,34 @@ import {
30
30
  SerovalSetNode,
31
31
  } from './types';
32
32
 
33
- async function generateDictionary(
33
+ async function serializePropertiesAsync(
34
34
  ctx: ParserContext,
35
- properties: Record<string, AsyncServerValue>,
35
+ properties: Record<string, unknown>,
36
36
  ): Promise<SerovalDictionaryNode> {
37
37
  const keys = Object.keys(properties);
38
- const nodes: SerovalDictionaryNode = {};
39
- const deferred: Record<string, AsyncServerValue> = {};
38
+ const size = keys.length;
39
+ const keyNodes = new Array<string>(size);
40
+ const valueNodes = new Array<SerovalNode>(size);
41
+ const deferredKeys = new Array<string>(size);
42
+ const deferredValues = new Array<AsyncServerValue>(size);
43
+ let deferredSize = 0;
44
+ let nodesSize = 0;
40
45
  for (const key of keys) {
41
46
  if (isIterable(properties[key])) {
42
- deferred[key] = properties[key];
47
+ deferredKeys[deferredSize] = key;
48
+ deferredValues[deferredSize] = properties[key] as AsyncServerValue;
49
+ deferredSize++;
43
50
  } else {
44
- nodes[key] = await generateTreeAsync(ctx, properties[key]);
51
+ keyNodes[nodesSize] = key;
52
+ valueNodes[nodesSize] = await generateTreeAsync(ctx, properties[key] as AsyncServerValue);
53
+ nodesSize++;
45
54
  }
46
55
  }
47
- for (const key of Object.keys(deferred)) {
48
- nodes[key] = await generateTreeAsync(ctx, deferred[key]);
56
+ for (let i = 0; i < deferredSize; i++) {
57
+ keyNodes[nodesSize + i] = deferredKeys[i];
58
+ valueNodes[nodesSize + i] = await generateTreeAsync(ctx, deferredValues[i]);
49
59
  }
50
- return nodes;
60
+ return [keyNodes, valueNodes, size];
51
61
  }
52
62
 
53
63
  async function generateSetNode(
@@ -147,7 +157,7 @@ async function generateAggregateErrorNode(
147
157
  ): Promise<SerovalAggregateErrorNode> {
148
158
  const options = getErrorOptions(current);
149
159
  const optionsNode = options
150
- ? await generateDictionary(ctx, options)
160
+ ? await serializePropertiesAsync(ctx, options)
151
161
  : undefined;
152
162
  const errorsNode = await generateTreeAsync(ctx, current.errors as AsyncServerValue);
153
163
  return [
@@ -164,7 +174,7 @@ async function generateErrorNode(
164
174
  ): Promise<SerovalErrorNode> {
165
175
  const options = getErrorOptions(current);
166
176
  const optionsNode = options
167
- ? await generateDictionary(ctx, options)
177
+ ? await serializePropertiesAsync(ctx, options)
168
178
  : undefined;
169
179
  return [
170
180
  SerovalNodeType.Error,
@@ -182,9 +192,7 @@ async function generateIterableNode(
182
192
  const options = getIterableOptions(current);
183
193
  return [SerovalNodeType.Iterable, [
184
194
  // Parse options first before the items
185
- options
186
- ? await generateDictionary(ctx, options as Record<string, AsyncServerValue>)
187
- : undefined,
195
+ options ? await serializePropertiesAsync(ctx, options) : undefined,
188
196
  await generateNodeList(ctx, Array.from(current)),
189
197
  ], id];
190
198
  }
@@ -236,7 +244,7 @@ async function generateTreeAsync(
236
244
  if (current.constructor === Object || empty) {
237
245
  return [
238
246
  empty ? SerovalNodeType.NullConstructor : SerovalNodeType.Object,
239
- await generateDictionary(ctx, current as Record<string, AsyncServerValue>),
247
+ await serializePropertiesAsync(ctx, current as Record<string, unknown>),
240
248
  id,
241
249
  ];
242
250
  }
@@ -96,7 +96,7 @@ function assignRef(
96
96
  index: number,
97
97
  value: string,
98
98
  ) {
99
- if (ctx.markedRefs.has(index)) {
99
+ if (ctx.markedRefs[index]) {
100
100
  return `${getRefParam(ctx, index)}=${value}`;
101
101
  }
102
102
  return value;
@@ -145,7 +145,7 @@ function createMapSet(
145
145
  function createArrayAssign(
146
146
  ctx: SerializationContext,
147
147
  ref: number,
148
- index: number,
148
+ index: number | string,
149
149
  value: string,
150
150
  ) {
151
151
  markRef(ctx, ref);
@@ -157,11 +157,9 @@ function createObjectAssign(
157
157
  ref: number,
158
158
  key: string,
159
159
  value: string,
160
- computed: boolean,
161
160
  ) {
162
161
  markRef(ctx, ref);
163
- const member = computed ? `[${key}]` : `.${key}`;
164
- createAssignment(ctx, `${getRefParam(ctx, ref)}${member}`, value);
162
+ createAssignment(ctx, `${getRefParam(ctx, ref)}.${key}`, value);
165
163
  }
166
164
 
167
165
  const IDENTIFIER_CHECK = /^([$A-Z_][0-9A-Z_$]*)$/i;
@@ -169,59 +167,59 @@ const IDENTIFIER_CHECK = /^([$A-Z_][0-9A-Z_$]*)$/i;
169
167
  function serializeAssignments(
170
168
  ctx: SerializationContext,
171
169
  targetRef: number,
172
- record: SerovalDictionaryNode,
170
+ [keys, values, size]: SerovalDictionaryNode,
173
171
  ) {
174
172
  ctx.stack.push(targetRef);
175
173
  const mainAssignments: Assignment[] = [];
176
- for (const key of Object.keys(record)) {
174
+ for (let i = 0; i < size; i++) {
177
175
  const parentStack = ctx.stack;
178
176
  ctx.stack = [];
179
- const refParam = serializeTree(ctx, record[key]);
177
+ const refParam = serializeTree(ctx, values[i]);
180
178
  ctx.stack = parentStack;
179
+ const key = keys[i];
181
180
  const check = Number(key);
182
181
  const parentAssignment = ctx.assignments;
183
182
  ctx.assignments = mainAssignments;
184
183
  // Test if key is a valid number or JS identifier
185
184
  // so that we don't have to serialize the key and wrap with brackets
186
185
  const isIdentifier = check >= 0 || IDENTIFIER_CHECK.test(key);
187
- createObjectAssign(
188
- ctx,
189
- targetRef,
190
- isIdentifier ? key : quote(key),
191
- refParam,
192
- !(isIdentifier && Number.isNaN(check)),
193
- );
186
+ if (isIdentifier && Number.isNaN(check)) {
187
+ createObjectAssign(ctx, targetRef, key, refParam);
188
+ } else {
189
+ createArrayAssign(ctx, targetRef, isIdentifier ? key : quote(key), refParam);
190
+ }
194
191
  ctx.assignments = parentAssignment;
195
192
  }
196
193
  ctx.stack.pop();
197
194
  return resolveAssignments(mainAssignments);
198
195
  }
199
196
 
200
- function serializeDictionary(
197
+ function serializeObject(
201
198
  ctx: SerializationContext,
202
199
  sourceID: number,
203
- record: SerovalDictionaryNode,
200
+ [keys, values, size]: SerovalDictionaryNode,
204
201
  ) {
202
+ if (size === 0) {
203
+ return '{}';
204
+ }
205
205
  let result = '';
206
206
  ctx.stack.push(sourceID);
207
- for (const key of Object.keys(record)) {
208
- const val = record[key];
207
+ for (let i = 0; i < size; i++) {
208
+ const key = keys[i];
209
+ const val = values[i];
209
210
  const check = Number(key);
210
211
  // Test if key is a valid number or JS identifier
211
212
  // so that we don't have to serialize the key and wrap with brackets
212
213
  const isIdentifier = check >= 0 || IDENTIFIER_CHECK.test(key);
213
- const validKey = isIdentifier ? key : quote(key);
214
214
  if (isReferenceInStack(ctx, val)) {
215
215
  const refParam = getRefParam(ctx, val[1]);
216
- createObjectAssign(
217
- ctx,
218
- sourceID,
219
- validKey,
220
- refParam,
221
- !(isIdentifier && Number.isNaN(check)),
222
- );
216
+ if (isIdentifier && Number.isNaN(check)) {
217
+ createObjectAssign(ctx, sourceID, key, refParam);
218
+ } else {
219
+ createArrayAssign(ctx, sourceID, isIdentifier ? key : quote(key), refParam);
220
+ }
223
221
  } else {
224
- result += `${validKey}:${serializeTree(ctx, val)},`;
222
+ result += `${isIdentifier ? key : quote(key)}:${serializeTree(ctx, val)},`;
225
223
  }
226
224
  }
227
225
  ctx.stack.pop();
@@ -400,7 +398,7 @@ function serializeWithObjectAssign(
400
398
  id: number,
401
399
  serialized: string,
402
400
  ) {
403
- const fields = serializeDictionary(ctx, id, value);
401
+ const fields = serializeObject(ctx, id, value);
404
402
  if (fields !== '{}') {
405
403
  return `Object.assign(${serialized},${fields})`;
406
404
  }
@@ -551,7 +549,7 @@ export default function serializeTree(
551
549
  case SerovalNodeType.NullConstructor:
552
550
  return serializeNullConstructor(ctx, value, id);
553
551
  case SerovalNodeType.Object:
554
- return assignRef(ctx, id, serializeDictionary(ctx, id, value));
552
+ return assignRef(ctx, id, serializeObject(ctx, id, value));
555
553
  default:
556
554
  throw new Error('Unsupported type');
557
555
  }
package/src/tree/sync.ts CHANGED
@@ -24,24 +24,34 @@ import {
24
24
  SerovalSetNode,
25
25
  } from './types';
26
26
 
27
- function generateDictionary(
27
+ function serializePropertiesSync(
28
28
  ctx: ParserContext,
29
- properties: Record<string, ServerValue>,
29
+ properties: Record<string, unknown>,
30
30
  ): SerovalDictionaryNode {
31
31
  const keys = Object.keys(properties);
32
- const nodes: SerovalDictionaryNode = {};
33
- const deferred: Record<string, ServerValue> = {};
32
+ const size = keys.length;
33
+ const keyNodes = new Array<string>(size);
34
+ const valueNodes = new Array<SerovalNode>(size);
35
+ const deferredKeys = new Array<string>(size);
36
+ const deferredValues = new Array<ServerValue>(size);
37
+ let deferredSize = 0;
38
+ let nodesSize = 0;
34
39
  for (const key of keys) {
35
40
  if (isIterable(properties[key])) {
36
- deferred[key] = properties[key];
41
+ deferredKeys[deferredSize] = key;
42
+ deferredValues[deferredSize] = properties[key] as ServerValue;
43
+ deferredSize++;
37
44
  } else {
38
- nodes[key] = generateTreeSync(ctx, properties[key]);
45
+ keyNodes[nodesSize] = key;
46
+ valueNodes[nodesSize] = generateTreeSync(ctx, properties[key] as ServerValue);
47
+ nodesSize++;
39
48
  }
40
49
  }
41
- for (const key of Object.keys(deferred)) {
42
- nodes[key] = generateTreeSync(ctx, deferred[key]);
50
+ for (let i = 0; i < deferredSize; i++) {
51
+ keyNodes[nodesSize + i] = deferredKeys[i];
52
+ valueNodes[nodesSize + i] = generateTreeSync(ctx, deferredValues[i]);
43
53
  }
44
- return nodes;
54
+ return [keyNodes, valueNodes, size];
45
55
  }
46
56
 
47
57
  function generateSetNode(
@@ -141,7 +151,7 @@ function generateAggregateErrorNode(
141
151
  ): SerovalAggregateErrorNode {
142
152
  const options = getErrorOptions(current);
143
153
  const optionsNode = options
144
- ? generateDictionary(ctx, options)
154
+ ? serializePropertiesSync(ctx, options)
145
155
  : undefined;
146
156
  const errorsNode = generateTreeSync(ctx, current.errors as ServerValue);
147
157
  return [SerovalNodeType.AggregateError, [current.message, optionsNode, errorsNode], id];
@@ -154,7 +164,7 @@ function generateErrorNode(
154
164
  ): SerovalErrorNode {
155
165
  const options = getErrorOptions(current);
156
166
  const optionsNode = options
157
- ? generateDictionary(ctx, options)
167
+ ? serializePropertiesSync(ctx, options)
158
168
  : undefined;
159
169
  return [
160
170
  SerovalNodeType.Error,
@@ -172,9 +182,7 @@ function generateIterableNode(
172
182
  const options = getIterableOptions(current);
173
183
  return [SerovalNodeType.Iterable, [
174
184
  // Parse options first before the items
175
- options
176
- ? generateDictionary(ctx, options as Record<string, ServerValue>)
177
- : undefined,
185
+ options ? serializePropertiesSync(ctx, options) : undefined,
178
186
  generateNodeList(ctx, Array.from(current)),
179
187
  ], id];
180
188
  }
@@ -218,7 +226,7 @@ function generateTreeSync(
218
226
  if (current.constructor === Object || empty) {
219
227
  return [
220
228
  empty ? SerovalNodeType.NullConstructor : SerovalNodeType.Object,
221
- generateDictionary(ctx, current as Record<string, ServerValue>),
229
+ serializePropertiesSync(ctx, current as Record<string, unknown>),
222
230
  id,
223
231
  ];
224
232
  }
package/src/tree/types.ts CHANGED
@@ -39,7 +39,7 @@ export type SerovalSemiPrimitiveNode =
39
39
  id: number
40
40
  ];
41
41
 
42
- export type SerovalDictionaryNode = Record<string, SerovalNode>;
42
+ export type SerovalDictionaryNode = [key: string[], value: SerovalNode[], size: number];
43
43
  export type SerovalSetNode = [type: SerovalNodeType.Set, value: SerovalNode[], id: number];
44
44
  export type SerovalMapNode = [
45
45
  type: SerovalNodeType.Map,