seroval 0.6.0 → 0.8.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.
Files changed (45) hide show
  1. package/README.md +15 -3
  2. package/dist/cjs/development/index.cjs +1431 -991
  3. package/dist/cjs/development/index.cjs.map +4 -4
  4. package/dist/cjs/production/index.cjs +3 -3
  5. package/dist/esm/development/index.mjs +1431 -991
  6. package/dist/esm/development/index.mjs.map +4 -4
  7. package/dist/esm/production/index.mjs +3 -3
  8. package/dist/types/assert.d.ts +1 -1
  9. package/dist/types/compat.d.ts +1 -1
  10. package/dist/types/context.d.ts +13 -7
  11. package/dist/types/index.d.ts +3 -4
  12. package/dist/types/string.d.ts +3 -0
  13. package/dist/types/tree/UnsupportedTypeError.d.ts +3 -0
  14. package/dist/types/tree/async.d.ts +3 -3
  15. package/dist/types/tree/constants.d.ts +11 -0
  16. package/dist/types/tree/deserialize.d.ts +2 -2
  17. package/dist/types/tree/primitives.d.ts +11 -14
  18. package/dist/types/tree/serialize.d.ts +2 -2
  19. package/dist/types/tree/shared.d.ts +10 -8
  20. package/dist/types/tree/sync.d.ts +3 -3
  21. package/dist/types/tree/types.d.ts +106 -70
  22. package/dist/types/tree/web-api.d.ts +4 -2
  23. package/dist/types/types.d.ts +4 -4
  24. package/package.json +11 -8
  25. package/src/assert.ts +2 -2
  26. package/src/compat.ts +1 -2
  27. package/src/context.ts +24 -13
  28. package/src/get-identifier.ts +1 -1
  29. package/src/index.ts +35 -26
  30. package/src/string.ts +53 -0
  31. package/src/tree/UnsupportedTypeError.ts +8 -0
  32. package/src/tree/async.ts +313 -222
  33. package/src/tree/constants.ts +72 -0
  34. package/src/tree/deserialize.ts +239 -119
  35. package/src/tree/primitives.ts +154 -143
  36. package/src/tree/reference.ts +4 -3
  37. package/src/tree/serialize.ts +394 -170
  38. package/src/tree/shared.ts +31 -22
  39. package/src/tree/symbols.ts +13 -13
  40. package/src/tree/sync.ts +288 -191
  41. package/src/tree/types.ts +173 -91
  42. package/src/tree/web-api.ts +59 -6
  43. package/src/types.ts +7 -3
  44. package/dist/types/quote.d.ts +0 -2
  45. package/src/quote.ts +0 -56
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "seroval",
3
3
  "type": "module",
4
- "version": "0.6.0",
4
+ "version": "0.8.0",
5
5
  "files": [
6
6
  "dist",
7
7
  "src"
@@ -15,12 +15,14 @@
15
15
  ],
16
16
  "devDependencies": {
17
17
  "@types/node": "^18.15.3",
18
- "eslint": "^8.32.0",
19
- "eslint-config-lxsmnsyc": "^0.5.1",
20
- "pridepack": "2.4.1",
21
- "tslib": "^2.4.1",
18
+ "@vitest/ui": "^0.30.1",
19
+ "eslint": "^8.39.0",
20
+ "eslint-config-lxsmnsyc": "^0.6.4",
21
+ "node-fetch-native": "^1.1.0",
22
+ "pridepack": "2.4.4",
23
+ "tslib": "^2.5.0",
22
24
  "typescript": "^4.9.4",
23
- "vitest": "^0.28.1"
25
+ "vitest": "^0.30.1"
24
26
  },
25
27
  "scripts": {
26
28
  "prepublishOnly": "pridepack clean && pridepack build",
@@ -31,7 +33,8 @@
31
33
  "watch": "pridepack watch",
32
34
  "start": "pridepack start",
33
35
  "dev": "pridepack dev",
34
- "test": "vitest"
36
+ "test": "vitest",
37
+ "test:ui": "vitest --ui"
35
38
  },
36
39
  "private": false,
37
40
  "description": "Stringify JS values",
@@ -64,5 +67,5 @@
64
67
  "typesVersions": {
65
68
  "*": {}
66
69
  },
67
- "gitHead": "1d6e69079251612c6fc486e8517e225690363442"
70
+ "gitHead": "f7fe3305e0295499989d9f675ff54f9305db23b0"
68
71
  }
package/src/assert.ts CHANGED
@@ -1,5 +1,5 @@
1
- export default function assert(cond: unknown, error: string): asserts cond {
1
+ export default function assert(cond: unknown, error: Error): asserts cond {
2
2
  if (!cond) {
3
- throw new Error(error);
3
+ throw error;
4
4
  }
5
5
  }
package/src/compat.ts CHANGED
@@ -1,4 +1,3 @@
1
- /* eslint-disable guard-for-in */
2
1
  /**
3
2
  * References
4
3
  * - https://kangax.github.io/compat-table/es6/
@@ -22,4 +21,4 @@ export const enum Feature {
22
21
  WebAPI = 0x2000,
23
22
  }
24
23
 
25
- export const ALL_ENABLED = 0x2FFF;
24
+ export const ALL_ENABLED = 0x3FFF;
package/src/context.ts CHANGED
@@ -8,25 +8,33 @@ interface IndexAssignment {
8
8
  v: string;
9
9
  }
10
10
 
11
- interface MapAssignment {
12
- t: 'map';
11
+ interface SetAssignment {
12
+ t: 'set';
13
13
  s: string;
14
14
  k: string;
15
15
  v: string;
16
16
  }
17
17
 
18
- interface SetAssignment {
19
- t: 'set';
18
+ interface AddAssignment {
19
+ t: 'add';
20
20
  s: string;
21
21
  k: undefined;
22
22
  v: string;
23
23
  }
24
24
 
25
+ interface AppendAssignment {
26
+ t: 'append';
27
+ s: string;
28
+ k: string;
29
+ v: string;
30
+ }
31
+
25
32
  // Array of assignments to be done (used for recursion)
26
33
  export type Assignment =
27
34
  | IndexAssignment
28
- | MapAssignment
29
- | SetAssignment;
35
+ | AddAssignment
36
+ | SetAssignment
37
+ | AppendAssignment;
30
38
 
31
39
  export interface ParserContext {
32
40
  refs: Map<unknown, number>;
@@ -37,12 +45,12 @@ export interface ParserContext {
37
45
  export interface SerializationContext {
38
46
  stack: number[];
39
47
  // Map tree refs to actual refs
40
- validRefs: number[];
48
+ validRefs: (number | undefined)[];
41
49
  refSize: number;
42
50
  // Refs that are...referenced
43
51
  markedRefs: Set<number>;
44
52
  // Variables
45
- vars: string[];
53
+ vars: (string | undefined)[];
46
54
  // Array of assignments to be done (used for recursion)
47
55
  assignments: Assignment[];
48
56
  // Supported features
@@ -61,7 +69,7 @@ const DEFAULT_OPTIONS: Options = {
61
69
 
62
70
  export function createParserContext(options: Partial<Options> = {}): ParserContext {
63
71
  // eslint-disable-next-line prefer-object-spread
64
- const result = Object.assign({}, DEFAULT_OPTIONS, options || {});
72
+ const result = Object.assign({}, DEFAULT_OPTIONS, options);
65
73
  return {
66
74
  markedRefs: new Set(),
67
75
  refs: new Map(),
@@ -90,14 +98,17 @@ export function createSerializationContext(options: SerializationOptions): Seria
90
98
  /**
91
99
  * Increments the number of references the referenced value has
92
100
  */
93
- export function markRef(ctx: ParserContext | SerializationContext, current: number) {
101
+ export function markRef(
102
+ ctx: ParserContext | SerializationContext,
103
+ current: number,
104
+ ): void {
94
105
  ctx.markedRefs.add(current);
95
106
  }
96
107
  /**
97
108
  * Creates the reference param (identifier) from the given reference ID
98
109
  * Calling this function means the value has been referenced somewhere
99
110
  */
100
- export function getRefParam(ctx: SerializationContext, index: number) {
111
+ export function getRefParam(ctx: SerializationContext, index: number): string {
101
112
  /**
102
113
  * Creates a new reference ID from a given reference ID
103
114
  * This new reference ID means that the reference itself
@@ -120,7 +131,7 @@ export function getRefParam(ctx: SerializationContext, index: number) {
120
131
  export function getRootID<T>(
121
132
  ctx: ParserContext,
122
133
  current: T,
123
- ) {
134
+ ): number {
124
135
  const ref = ctx.refs.get(current);
125
136
  if (ref == null) {
126
137
  return ctx.refs.size;
@@ -131,7 +142,7 @@ export function getRootID<T>(
131
142
  export function createIndexedValue<T>(
132
143
  ctx: ParserContext,
133
144
  current: T,
134
- ) {
145
+ ): number {
135
146
  const ref = ctx.refs.get(current);
136
147
  if (ref == null) {
137
148
  const id = ctx.refs.size;
@@ -4,7 +4,7 @@ const REF_START_CHARS_LEN = REF_START_CHARS.length;
4
4
  const REF_CHARS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789$_';
5
5
  const REF_CHARS_LEN = REF_CHARS.length;
6
6
 
7
- export default function getIdentifier(index: number) {
7
+ export default function getIdentifier(index: number): string {
8
8
  let mod = index % REF_START_CHARS_LEN;
9
9
  let ref = REF_START_CHARS[mod];
10
10
  index = (index - mod) / REF_START_CHARS_LEN;
package/src/index.ts CHANGED
@@ -1,19 +1,23 @@
1
1
  /* eslint-disable no-await-in-loop */
2
2
  import { Feature } from './compat';
3
- import {
3
+ import type {
4
4
  SerializationContext,
5
- getRefParam,
6
5
  Options,
6
+ } from './context';
7
+ import {
8
+ getRefParam,
7
9
  createParserContext,
8
10
  createSerializationContext,
11
+ getRootID,
9
12
  } from './context';
13
+ import parseSync from './tree/sync';
10
14
  import parseAsync from './tree/async';
11
15
  import deserializeTree from './tree/deserialize';
12
16
  import serializeTree, { resolvePatches } from './tree/serialize';
13
- import parseSync from './tree/sync';
14
- import { SerovalNode } from './tree/types';
17
+ import type { SerovalNode } from './tree/types';
18
+ import { SerovalNodeType } from './tree/types';
15
19
 
16
- export {
20
+ export type {
17
21
  AsyncServerValue,
18
22
  ServerValue,
19
23
  PrimitiveValue,
@@ -28,7 +32,7 @@ function finalize(
28
32
  rootID: number,
29
33
  isObject: boolean,
30
34
  result: string,
31
- ) {
35
+ ): string {
32
36
  // Shared references detected
33
37
  if (ctx.vars.length) {
34
38
  const patches = resolvePatches(ctx);
@@ -62,23 +66,33 @@ function finalize(
62
66
  export function serialize<T>(
63
67
  source: T,
64
68
  options?: Partial<Options>,
65
- ) {
69
+ ): string {
66
70
  const ctx = createParserContext(options);
67
- const [tree, rootID, isObject] = parseSync(ctx, source);
71
+ const tree = parseSync(ctx, source);
68
72
  const serial = createSerializationContext(ctx);
69
73
  const result = serializeTree(serial, tree);
70
- return finalize(serial, rootID, isObject, result);
74
+ return finalize(
75
+ serial,
76
+ getRootID(ctx, source),
77
+ tree.t === SerovalNodeType.Object,
78
+ result,
79
+ );
71
80
  }
72
81
 
73
82
  export async function serializeAsync<T>(
74
83
  source: T,
75
84
  options?: Partial<Options>,
76
- ) {
85
+ ): Promise<string> {
77
86
  const ctx = createParserContext(options);
78
- const [tree, rootID, isObject] = await parseAsync(ctx, source);
87
+ const tree = await parseAsync(ctx, source);
79
88
  const serial = createSerializationContext(ctx);
80
89
  const result = serializeTree(serial, tree);
81
- return finalize(serial, rootID, isObject, result);
90
+ return finalize(
91
+ serial,
92
+ getRootID(ctx, source),
93
+ tree.t === SerovalNodeType.Object,
94
+ result,
95
+ );
82
96
  }
83
97
 
84
98
  export function deserialize<T>(source: string): T {
@@ -87,11 +101,10 @@ export function deserialize<T>(source: string): T {
87
101
  }
88
102
 
89
103
  export interface SerovalJSON {
90
- t: SerovalNode,
91
- r: number,
92
- i: boolean,
93
- f: number,
94
- m: number[],
104
+ t: SerovalNode;
105
+ r: number;
106
+ f: number;
107
+ m: number[];
95
108
  }
96
109
 
97
110
  export function toJSON<T>(
@@ -99,11 +112,9 @@ export function toJSON<T>(
99
112
  options?: Partial<Options>,
100
113
  ): SerovalJSON {
101
114
  const ctx = createParserContext(options);
102
- const [tree, root, isObject] = parseSync(ctx, source);
103
115
  return {
104
- t: tree,
105
- r: root,
106
- i: isObject,
116
+ t: parseSync(ctx, source),
117
+ r: getRootID(ctx, source),
107
118
  f: ctx.features,
108
119
  m: Array.from(ctx.markedRefs),
109
120
  };
@@ -114,11 +125,9 @@ export async function toJSONAsync<T>(
114
125
  options?: Partial<Options>,
115
126
  ): Promise<SerovalJSON> {
116
127
  const ctx = createParserContext(options);
117
- const [tree, root, isObject] = await parseAsync(ctx, source);
118
128
  return {
119
- t: tree,
120
- r: root,
121
- i: isObject,
129
+ t: await parseAsync(ctx, source),
130
+ r: getRootID(ctx, source),
122
131
  f: ctx.features,
123
132
  m: Array.from(ctx.markedRefs),
124
133
  };
@@ -130,7 +139,7 @@ export function compileJSON(source: SerovalJSON): string {
130
139
  markedRefs: source.m,
131
140
  });
132
141
  const result = serializeTree(serial, source.t);
133
- return finalize(serial, source.r, source.i, result);
142
+ return finalize(serial, source.r, source.t.i === SerovalNodeType.Object, result);
134
143
  }
135
144
 
136
145
  export function fromJSON<T>(source: SerovalJSON): T {
package/src/string.ts ADDED
@@ -0,0 +1,53 @@
1
+ export function serializeChar(str: string): string | undefined {
2
+ switch (str) {
3
+ case '"': return '\\"';
4
+ case '\\': return '\\\\';
5
+ case '\n': return '\\n';
6
+ case '\r': return '\\r';
7
+ case '\b': return '\\b';
8
+ case '\t': return '\\t';
9
+ case '\f': return '\\f';
10
+ case '<': return '\\x3C';
11
+ case '\u2028': return '\\u2028';
12
+ case '\u2029': return '\\u2029';
13
+ default: return undefined;
14
+ }
15
+ }
16
+
17
+ // Written by https://github.com/DylanPiercey and is distributed under the MIT license.
18
+ // Creates a JavaScript double quoted string and escapes all characters
19
+ // not listed as DoubleStringCharacters on
20
+ // Also includes "<" to escape "</script>" and "\" to avoid invalid escapes in the output.
21
+ // http://www.ecma-international.org/ecma-262/5.1/#sec-7.8.4
22
+ export function serializeString(str: string): string {
23
+ let result = '';
24
+ let lastPos = 0;
25
+ let replacement: string | undefined;
26
+ for (let i = 0, len = str.length; i < len; i++) {
27
+ replacement = serializeChar(str[i]);
28
+ if (replacement) {
29
+ result += str.slice(lastPos, i) + replacement;
30
+ lastPos = i + 1;
31
+ }
32
+ }
33
+ if (lastPos === 0) {
34
+ result = str;
35
+ } else {
36
+ result += str.slice(lastPos);
37
+ }
38
+ return result;
39
+ }
40
+
41
+ export function deserializeString(str: string): string {
42
+ return str
43
+ .replace(/\\"/g, '"')
44
+ .replace(/\\\\/g, '\\')
45
+ .replace(/\\n/g, '\n')
46
+ .replace(/\\r/g, '\r')
47
+ .replace(/\\b/g, '\b')
48
+ .replace(/\\t/g, '\t')
49
+ .replace(/\\f/g, '\f')
50
+ .replace(/\\x3C/g, '<')
51
+ .replace(/\\u2028/g, '\u2028')
52
+ .replace(/\\u2029/g, '\u2029');
53
+ }
@@ -0,0 +1,8 @@
1
+ // eslint-disable-next-line @typescript-eslint/unbound-method
2
+ const { toString } = Object.prototype;
3
+
4
+ export default class UnsupportedTypeError extends Error {
5
+ constructor(value: unknown) {
6
+ super('Unsupported type "' + toString.call(value) + '"');
7
+ }
8
+ }