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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
|
/*** UTILITY ------------------------------------------ ***/
import type { Tab } from "./session.svelte.ts";
const MAX_STRING_BYTES = 1024 * 1024;
const MAX_TABS = 50;
function isObject(value: unknown): value is Record<string, unknown> {
return typeof value === "object" && value !== null && !Array.isArray(value);
}
function stringTooLong(value: string): boolean {
return value.length > MAX_STRING_BYTES;
}
function validateTab(raw: unknown, index: number): TabExport | string {
if (!isObject(raw))
return `tabs[${index}]: not an object`;
if (typeof raw.headers !== "string")
return `tabs[${index}].headers: not a string`;
if (typeof raw.query !== "string")
return `tabs[${index}].query: not a string`;
if (typeof raw.title !== "string")
return `tabs[${index}].title: not a string`;
if (typeof raw.variables !== "string")
return `tabs[${index}].variables: not a string`;
if (raw.operationName !== null && typeof raw.operationName !== "string")
return `tabs[${index}].operationName: not a string or null`;
if (stringTooLong(raw.headers))
return `tabs[${index}].headers: exceeds 1 MB`;
if (stringTooLong(raw.query))
return `tabs[${index}].query: exceeds 1 MB`;
if (stringTooLong(raw.title))
return `tabs[${index}].title: exceeds 1 MB`;
if (stringTooLong(raw.variables))
return `tabs[${index}].variables: exceeds 1 MB`;
if (typeof raw.operationName === "string" && stringTooLong(raw.operationName))
return `tabs[${index}].operationName: exceeds 1 MB`;
return {
headers: raw.headers,
operationName: raw.operationName,
query: raw.query,
title: raw.title,
variables: raw.variables
};
}
/*** EXPORT ------------------------------------------- ***/
export type TabExport = {
headers: string;
operationName: string | null;
query: string;
title: string;
variables: string;
};
export type SessionExport = {
exportedAt: string;
tabs: TabExport[];
version: 1;
};
export type ImportResult = {
added: number;
errors: string[];
skipped: number;
};
export function tabToExport(tab: Tab): TabExport {
return {
headers: tab.headers,
operationName: tab.operationName,
query: tab.query,
title: tab.title,
variables: tab.variables
};
}
export function validateSessionExport(data: unknown): SessionExport | { error: string } {
if (!isObject(data))
return { error: "not an object" };
if (data.version !== 1)
return { error: `unsupported version: ${String(data.version)}` };
if (typeof data.exportedAt !== "string")
return { error: "exportedAt: not a string" };
if (!Array.isArray(data.tabs))
return { error: "tabs: not an array" };
if (data.tabs.length > MAX_TABS)
return { error: `tabs: exceeds ${MAX_TABS}` };
const tabs: TabExport[] = [];
for (let i = 0; i < data.tabs.length; i++) {
const result = validateTab(data.tabs[i], i);
if (typeof result === "string")
return { error: result };
tabs.push(result);
}
return {
exportedAt: data.exportedAt,
tabs,
version: 1
};
}
|