seroval 0.2.1 → 0.4.0-alpha.0

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/compat.ts ADDED
@@ -0,0 +1,247 @@
1
+ /**
2
+ * References
3
+ * - https://kangax.github.io/compat-table/es6/
4
+ * - MDN
5
+ * - ESBuild
6
+ */
7
+
8
+ import assert from './assert';
9
+
10
+ export type Version = [major: number, minor: number, patch: number];
11
+
12
+ function compareVersion(left: Version, right: Version) {
13
+ let diff = left[0] - right[0];
14
+ if (diff === 0) {
15
+ diff = left[1] - right[1];
16
+ }
17
+ if (diff === 0) {
18
+ diff = left[2] - right[2];
19
+ }
20
+ return diff;
21
+ }
22
+
23
+ export interface PlatformVersion {
24
+ es: Version;
25
+ // desktop
26
+ chrome?: Version;
27
+ edge?: Version;
28
+ safari?: Version;
29
+ firefox?: Version;
30
+ opera?: Version;
31
+ // mobile
32
+ ios?: Version;
33
+ samsung?: Version;
34
+ // js runtimes
35
+ deno?: Version;
36
+ node?: Version;
37
+ }
38
+
39
+ export type Platform = keyof PlatformVersion;
40
+
41
+ interface VersionTable {
42
+ 'aggregate-error': PlatformVersion;
43
+ 'array-values': PlatformVersion;
44
+ 'arrow-function': PlatformVersion;
45
+ 'bigint': PlatformVersion;
46
+ 'map': PlatformVersion;
47
+ 'method-shorthand': PlatformVersion;
48
+ 'object-assign': PlatformVersion;
49
+ 'promise': PlatformVersion;
50
+ 'set': PlatformVersion;
51
+ 'symbol-iterator': PlatformVersion;
52
+ 'typed-arrays': PlatformVersion;
53
+ }
54
+
55
+ export type Feature = keyof VersionTable;
56
+
57
+ const VERSION_TABLE: VersionTable = {
58
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AggregateError#browser_compatibility
59
+ 'aggregate-error': {
60
+ es: [2021, 0, 0],
61
+ chrome: [85, 0, 0],
62
+ edge: [85, 0, 0],
63
+ firefox: [79, 0, 0],
64
+ opera: [71, 0, 0],
65
+ safari: [14, 0, 0],
66
+ ios: [14, 0, 0],
67
+ samsung: [14, 0, 0],
68
+ deno: [1, 2, 0],
69
+ node: [15, 0, 0],
70
+ },
71
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/values#browser_compatibility
72
+ 'array-values': {
73
+ es: [6, 0, 0],
74
+ chrome: [66, 0, 0],
75
+ edge: [14, 0, 0],
76
+ firefox: [60, 0, 0],
77
+ opera: [53, 0, 0],
78
+ safari: [9, 0, 0],
79
+ ios: [9, 0, 0],
80
+ samsung: [9, 0, 0],
81
+ deno: [1, 0, 0],
82
+ node: [10, 9, 0],
83
+ },
84
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions#browser_compatibility
85
+ 'arrow-function': {
86
+ es: [6, 0, 0],
87
+ chrome: [45, 0, 0],
88
+ edge: [12, 0, 0],
89
+ firefox: [24, 0, 0],
90
+ opera: [32, 0, 0],
91
+ safari: [10, 0, 0],
92
+ ios: [10, 0, 0],
93
+ samsung: [5, 0, 0],
94
+ deno: [1, 0, 0],
95
+ node: [4, 0, 0],
96
+ },
97
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt#browser_compatibility
98
+ bigint: {
99
+ es: [2020, 0, 0],
100
+ chrome: [67, 0, 0],
101
+ edge: [79, 0, 0],
102
+ firefox: [68, 0, 0],
103
+ opera: [54, 0, 0],
104
+ safari: [14, 0, 0],
105
+ ios: [14, 0, 0],
106
+ samsung: [9, 0, 0],
107
+ deno: [1, 0, 0],
108
+ node: [10, 4, 0],
109
+ },
110
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map#browser_compatibility
111
+ map: {
112
+ es: [6, 0, 0],
113
+ chrome: [38, 0, 0],
114
+ edge: [12, 0, 0],
115
+ firefox: [13, 0, 0],
116
+ opera: [25, 0, 0],
117
+ safari: [8, 0, 0],
118
+ ios: [8, 0, 0],
119
+ samsung: [3, 0, 0],
120
+ deno: [1, 0, 0],
121
+ node: [0, 12, 0],
122
+ },
123
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Method_definitions#browser_compatibility
124
+ 'method-shorthand': {
125
+ es: [6, 0, 0],
126
+ chrome: [39, 0, 0],
127
+ edge: [12, 0, 0],
128
+ firefox: [34, 0, 0],
129
+ opera: [26, 0, 0],
130
+ safari: [9, 0, 0],
131
+ ios: [9, 0, 0],
132
+ samsung: [4, 0, 0],
133
+ deno: [1, 0, 0],
134
+ node: [4, 0, 0],
135
+ },
136
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#browser_compatibility
137
+ 'object-assign': {
138
+ es: [6, 0, 0],
139
+ chrome: [45, 0, 0],
140
+ edge: [12, 0, 0],
141
+ firefox: [34, 0, 0],
142
+ opera: [32, 0, 0],
143
+ safari: [9, 0, 0],
144
+ ios: [9, 0, 0],
145
+ samsung: [5, 0, 0],
146
+ deno: [1, 0, 0],
147
+ node: [4, 0, 0],
148
+ },
149
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise#browser_compatibility
150
+ promise: {
151
+ es: [6, 0, 0],
152
+ chrome: [32, 0, 0],
153
+ edge: [12, 0, 0],
154
+ firefox: [29, 0, 0],
155
+ opera: [19, 0, 0],
156
+ safari: [8, 0, 0],
157
+ ios: [8, 0, 0],
158
+ samsung: [2, 0, 0],
159
+ deno: [1, 0, 0],
160
+ node: [0, 12, 0],
161
+ },
162
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set#browser_compatibility
163
+ set: {
164
+ es: [6, 0, 0],
165
+ chrome: [38, 0, 0],
166
+ edge: [12, 0, 0],
167
+ firefox: [13, 0, 0],
168
+ opera: [25, 0, 0],
169
+ safari: [8, 0, 0],
170
+ ios: [8, 0, 0],
171
+ samsung: [3, 0, 0],
172
+ deno: [1, 0, 0],
173
+ node: [0, 12, 0],
174
+ },
175
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/iterator#browser_compatibility
176
+ 'symbol-iterator': {
177
+ es: [6, 0, 0],
178
+ chrome: [43, 0, 0],
179
+ edge: [12, 0, 0],
180
+ firefox: [36, 0, 0],
181
+ opera: [30, 0, 0],
182
+ safari: [10, 0, 0],
183
+ ios: [10, 0, 0],
184
+ samsung: [4, 0, 0],
185
+ deno: [1, 0, 0],
186
+ node: [0, 12, 0],
187
+ },
188
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray#browser_compatibility
189
+ 'typed-arrays': {
190
+ es: [6, 0, 0],
191
+ chrome: [7, 0, 0],
192
+ edge: [12, 0, 0],
193
+ firefox: [4, 0, 0],
194
+ opera: [11, 6, 0],
195
+ safari: [5, 1, 0],
196
+ ios: [4, 2, 0],
197
+ samsung: [1, 0, 0],
198
+ deno: [1, 0, 0],
199
+ node: [0, 10, 0],
200
+ },
201
+ };
202
+
203
+ const TARGET_REGEX = /^(es|chrome|edge|safari|firefox|opera|ios|samsung|deno|node)([0-9]+(\.[0-9]+(\.[0-9]+)?)?)$/i;
204
+
205
+ export type Target = [platform: Platform, version: Version];
206
+
207
+ function parseTarget(target: string): Target {
208
+ const result = TARGET_REGEX.exec(target);
209
+ assert(result, `Invalid target "${target}"`);
210
+ const [, platform, version] = result;
211
+ const [major, minor = '0', patch = '0'] = version.split('.');
212
+ return [platform as Platform, [Number(major), Number(minor), Number(patch)]];
213
+ }
214
+
215
+ function getTargetVersions(targets: string | string[]): Target[] {
216
+ if (Array.isArray(targets)) {
217
+ const versions: Target[] = [];
218
+ for (const target of targets) {
219
+ versions.push(parseTarget(target));
220
+ }
221
+ return versions;
222
+ }
223
+ return [parseTarget(targets)];
224
+ }
225
+
226
+ function isFeatureSupported(key: Feature, targets: Target[]) {
227
+ const base = VERSION_TABLE[key];
228
+ let flag = true;
229
+ for (const [platform, version] of targets) {
230
+ const baseVersion = base[platform];
231
+ flag = flag && !!baseVersion && (compareVersion(baseVersion, version) <= 0);
232
+ }
233
+ return flag;
234
+ }
235
+
236
+ export function parseTargets(targets: string | string[]): Set<Feature> {
237
+ const parsed = getTargetVersions(targets);
238
+ const flags = new Set<Feature>();
239
+
240
+ for (const key in VERSION_TABLE) {
241
+ if (isFeatureSupported(key as Feature, parsed)) {
242
+ flags.add(key as Feature);
243
+ }
244
+ }
245
+
246
+ return flags;
247
+ }
package/src/index.ts CHANGED
@@ -1,43 +1,65 @@
1
1
  /* eslint-disable no-await-in-loop */
2
- import { join } from './array';
3
- import { lookupRefsAsync, traverseAsync } from './async';
2
+ import { isPrimitive } from './checks';
4
3
  import {
5
- SerializationContext,
6
- getRefParam,
7
4
  createRef,
8
- resolvePatches,
9
5
  createSerializationContext,
10
- } from './context';
11
- import isPrimitive from './is-primitive';
12
- import serializePrimitive from './serialize-primitive';
13
- import { lookupRefs, traverseSync } from './sync';
6
+ generateTreeAsync,
7
+ generateTreeSync,
8
+ getRefParam,
9
+ Options,
10
+ resolvePatches,
11
+ SerializationContext,
12
+ serializePrimitive,
13
+ serializeTree,
14
+ } from './tree';
14
15
  import {
15
16
  AsyncServerValue,
16
17
  NonPrimitiveServerValue,
17
18
  PrimitiveValue,
18
19
  ServerValue,
20
+ CommonServerValue,
21
+ SemiPrimitiveValue,
22
+ ErrorValue,
19
23
  } from './types';
20
24
 
21
- export { AsyncServerValue, ServerValue, PrimitiveValue };
25
+ export {
26
+ AsyncServerValue,
27
+ ServerValue,
28
+ PrimitiveValue,
29
+ CommonServerValue,
30
+ SemiPrimitiveValue,
31
+ ErrorValue,
32
+ };
22
33
 
23
- function finalize<T extends ServerValue | AsyncServerValue>(
34
+ function finalize<T extends NonPrimitiveServerValue<ServerValue | AsyncServerValue>>(
24
35
  ctx: SerializationContext,
25
- source: NonPrimitiveServerValue<T>,
36
+ source: T,
26
37
  result: string,
27
38
  ) {
28
39
  // Shared references detected
29
40
  if (ctx.vars.length) {
30
- // Get (or create) a ref from the source
31
- const index = getRefParam(ctx, createRef(ctx, source));
32
41
  const patches = resolvePatches(ctx);
33
- const params = ctx.vars.length > 1
34
- ? `(${join(ctx.vars, ',')})`
42
+ let body = result;
43
+ if (patches) {
44
+ // Get (or create) a ref from the source
45
+ const index = getRefParam(ctx, createRef(ctx, source));
46
+ if (result.startsWith(`${index}=`)) {
47
+ body = `${result},${patches}${index}`;
48
+ } else {
49
+ body = `${index}=${result},${patches}${index}`;
50
+ }
51
+ }
52
+ let params = ctx.vars.length > 1
53
+ ? ctx.vars.join(',')
35
54
  : ctx.vars[0];
36
55
  // Source is probably already assigned
37
- if (result.startsWith(`${index}=`)) {
38
- return `(${params}=>(${result},${patches}${index}))()`;
56
+ if (ctx.features.has('arrow-function')) {
57
+ params = ctx.vars.length > 1 || ctx.vars.length === 0
58
+ ? `(${params})`
59
+ : params;
60
+ return `(${params}=>(${body}))()`;
39
61
  }
40
- return `(${params}=>(${index}=${result},${patches}${index}))()`;
62
+ return `(function(${params}){return ${body}})()`;
41
63
  }
42
64
  if (source.constructor === Object) {
43
65
  return `(${result})`;
@@ -45,45 +67,29 @@ function finalize<T extends ServerValue | AsyncServerValue>(
45
67
  return result;
46
68
  }
47
69
 
48
- export function serialize(source: ServerValue) {
70
+ export function serialize<T extends ServerValue>(
71
+ source: T,
72
+ options?: Partial<Options>,
73
+ ) {
74
+ const ctx = createSerializationContext(options);
49
75
  if (isPrimitive(source)) {
50
- return serializePrimitive(source);
51
- }
52
- const ctx = createSerializationContext();
53
- // Lookup possible shared references
54
- lookupRefs(ctx, source);
55
- // Add shared references to refs
56
- for (const [key, value] of ctx.refCount.entries()) {
57
- if (value > 1) {
58
- createRef(ctx, key);
59
- }
60
- }
61
- // Get top-level serialization
62
- const result = traverseSync(ctx, source);
63
- if (typeof result !== 'string') {
64
- throw new Error('Unreachable error');
76
+ return serializePrimitive(ctx, source);
65
77
  }
78
+ const tree = generateTreeSync(ctx, source);
79
+ const result = serializeTree(ctx, tree);
66
80
  return finalize(ctx, source, result);
67
81
  }
68
82
 
69
- export async function serializeAsync(source: AsyncServerValue) {
83
+ export async function serializeAsync<T extends AsyncServerValue>(
84
+ source: T,
85
+ options?: Partial<Options>,
86
+ ) {
87
+ const ctx = createSerializationContext(options);
70
88
  if (isPrimitive(source)) {
71
- return serializePrimitive(source);
72
- }
73
- const ctx = createSerializationContext();
74
- // Lookup possible shared references
75
- await lookupRefsAsync(ctx, source);
76
- // Add shared references to refs
77
- for (const [key, value] of ctx.refCount.entries()) {
78
- if (value > 1) {
79
- createRef(ctx, key);
80
- }
81
- }
82
- // Get top-level serialization
83
- const result = await traverseAsync(ctx, source);
84
- if (typeof result !== 'string') {
85
- throw new Error('Unreachable error');
89
+ return serializePrimitive(ctx, source);
86
90
  }
91
+ const tree = await generateTreeAsync(ctx, source);
92
+ const result = serializeTree(ctx, tree);
87
93
  return finalize(ctx, source, result);
88
94
  }
89
95