-
Notifications
You must be signed in to change notification settings - Fork 463
/
Copy pathvariables.ts
102 lines (93 loc) · 2.93 KB
/
variables.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
export type FileMap = Map<string, File | Blob>;
const seen: Set<any> = new Set();
const cache: WeakMap<any, any> = new WeakMap();
const stringify = (x: any, includeFiles: boolean): string => {
if (x === null || seen.has(x)) {
return 'null';
} else if (typeof x !== 'object') {
return JSON.stringify(x) || '';
} else if (x.toJSON) {
return stringify(x.toJSON(), includeFiles);
} else if (Array.isArray(x)) {
let out = '[';
for (let i = 0, l = x.length; i < l; i++) {
if (out.length > 1) out += ',';
out += stringify(x[i], includeFiles) || 'null';
}
out += ']';
return out;
} else if (
!includeFiles &&
((FileConstructor !== NoopConstructor && x instanceof FileConstructor) ||
(BlobConstructor !== NoopConstructor && x instanceof BlobConstructor))
) {
return 'null';
}
const keys = Object.keys(x).sort();
if (
!keys.length &&
x.constructor &&
Object.getPrototypeOf(x).constructor !== Object.prototype.constructor
) {
const key = cache.get(x) || Math.random().toString(36).slice(2);
cache.set(x, key);
return stringify({ __key: key }, includeFiles);
}
seen.add(x);
let out = '{';
for (let i = 0, l = keys.length; i < l; i++) {
const value = stringify(x[keys[i]], includeFiles);
if (value) {
if (out.length > 1) out += ',';
out += stringify(keys[i], includeFiles) + ':' + value;
}
}
seen.delete(x);
out += '}';
return out;
};
const extract = (map: FileMap, path: string, x: any): void => {
if (x == null || typeof x !== 'object' || x.toJSON || seen.has(x)) {
/*noop*/
} else if (Array.isArray(x)) {
for (let i = 0, l = x.length; i < l; i++)
extract(map, `${path}.${i}`, x[i]);
} else if (x instanceof FileConstructor || x instanceof BlobConstructor) {
map.set(path, x as File | Blob);
} else {
seen.add(x);
for (const key in x) extract(map, `${path}.${key}`, x[key]);
}
};
/** A stable stringifier for GraphQL variables objects.
*
* @param x - any JSON-like data.
* @return A JSON string.
*
* @remarks
* This utility creates a stable JSON string from any passed data,
* and protects itself from throwing.
*
* The JSON string is stable insofar as objects’ keys are sorted,
* and instances of non-plain objects are replaced with random keys
* replacing their values, which remain stable for the objects’
* instance.
*/
export const stringifyVariables = (x: any, includeFiles?: boolean): string => {
seen.clear();
return stringify(x, includeFiles || false);
};
class NoopConstructor {}
const FileConstructor = typeof File !== 'undefined' ? File : NoopConstructor;
const BlobConstructor = typeof Blob !== 'undefined' ? Blob : NoopConstructor;
export const extractFiles = (x: any): FileMap => {
const map: FileMap = new Map();
if (
FileConstructor !== NoopConstructor ||
BlobConstructor !== NoopConstructor
) {
seen.clear();
extract(map, 'variables', x);
}
return map;
};