@fluid-tools/fetch-tool 1.4.0-115997 → 2.0.0-dev-rc.1.0.0.224419
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/.eslintrc.js +6 -8
- package/CHANGELOG.md +117 -0
- package/README.md +38 -7
- package/bin/fluid-fetch +0 -0
- package/dist/fluidAnalyzeMessages.d.ts.map +1 -1
- package/dist/fluidAnalyzeMessages.js +106 -116
- package/dist/fluidAnalyzeMessages.js.map +1 -1
- package/dist/fluidFetch.js +5 -3
- package/dist/fluidFetch.js.map +1 -1
- package/dist/fluidFetchArgs.d.ts +0 -3
- package/dist/fluidFetchArgs.d.ts.map +1 -1
- package/dist/fluidFetchArgs.js +10 -14
- package/dist/fluidFetchArgs.js.map +1 -1
- package/dist/fluidFetchInit.d.ts +0 -1
- package/dist/fluidFetchInit.d.ts.map +1 -1
- package/dist/fluidFetchInit.js +41 -34
- package/dist/fluidFetchInit.js.map +1 -1
- package/dist/fluidFetchMessages.d.ts.map +1 -1
- package/dist/fluidFetchMessages.js +168 -200
- package/dist/fluidFetchMessages.js.map +1 -1
- package/dist/fluidFetchSharePoint.d.ts +0 -1
- package/dist/fluidFetchSharePoint.d.ts.map +1 -1
- package/dist/fluidFetchSharePoint.js +20 -6
- package/dist/fluidFetchSharePoint.js.map +1 -1
- package/dist/fluidFetchSnapshot.d.ts.map +1 -1
- package/dist/fluidFetchSnapshot.js +18 -20
- package/dist/fluidFetchSnapshot.js.map +1 -1
- package/package.json +47 -42
- package/prettier.config.cjs +8 -0
- package/src/fluidAnalyzeMessages.ts +701 -630
- package/src/fluidFetch.ts +93 -88
- package/src/fluidFetchArgs.ts +167 -168
- package/src/fluidFetchInit.ts +133 -104
- package/src/fluidFetchMessages.ts +253 -232
- package/src/fluidFetchSharePoint.ts +130 -112
- package/src/fluidFetchSnapshot.ts +313 -295
- package/tsconfig.json +8 -15
package/src/fluidFetch.ts
CHANGED
|
@@ -13,115 +13,120 @@ import { getSharepointFiles, getSingleSharePointFile } from "./fluidFetchSharePo
|
|
|
13
13
|
import { fluidFetchSnapshot } from "./fluidFetchSnapshot";
|
|
14
14
|
|
|
15
15
|
async function fluidFetchOneFile(urlStr: string, name?: string) {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
16
|
+
const documentService = await fluidFetchInit(urlStr);
|
|
17
|
+
const saveDir =
|
|
18
|
+
paramSaveDir !== undefined
|
|
19
|
+
? name !== undefined
|
|
20
|
+
? `${paramSaveDir}/${name}`
|
|
21
|
+
: paramSaveDir
|
|
22
|
+
: undefined;
|
|
23
|
+
if (saveDir !== undefined) {
|
|
24
|
+
const mkdir = util.promisify(fs.mkdir);
|
|
25
|
+
const writeFile = util.promisify(fs.writeFile);
|
|
26
|
+
await mkdir(saveDir, { recursive: true });
|
|
27
|
+
const info = {
|
|
28
|
+
creationDate: new Date().toString(),
|
|
29
|
+
connectionInfo,
|
|
30
|
+
url: urlStr,
|
|
31
|
+
};
|
|
32
|
+
await writeFile(`${saveDir}/info.json`, JSON.stringify(info, undefined, 2));
|
|
33
|
+
}
|
|
33
34
|
|
|
34
|
-
|
|
35
|
-
|
|
35
|
+
await fluidFetchSnapshot(documentService, saveDir);
|
|
36
|
+
await fluidFetchMessages(documentService, saveDir);
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
async function tryFluidFetchOneSharePointFile(server: string, driveItem: IOdspDriveItem) {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
const { path, name, driveId, itemId } = driveItem;
|
|
41
|
+
console.log(`File: ${path}/${name}`);
|
|
42
|
+
await fluidFetchOneFile(`https://${server}/_api/v2.1/drives/${driveId}/items/${itemId}`, name);
|
|
42
43
|
}
|
|
43
44
|
|
|
44
|
-
function getSharePointSpecificDriveItem(url: URL): { driveId: string; itemId: string
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
45
|
+
function getSharePointSpecificDriveItem(url: URL): { driveId: string; itemId: string } | undefined {
|
|
46
|
+
if (url.searchParams.has("driveId") && url.searchParams.has("itemId")) {
|
|
47
|
+
return {
|
|
48
|
+
driveId: url.searchParams.get("driveId") as string,
|
|
49
|
+
itemId: url.searchParams.get("itemId") as string,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
51
52
|
}
|
|
52
53
|
|
|
53
54
|
function getSharepointServerRelativePathFromURL(url: URL) {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
if (url.pathname.startsWith("/_api/v2.1/drives/")) {
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
57
58
|
|
|
58
|
-
|
|
59
|
-
|
|
59
|
+
const hostnameParts = url.hostname.split(".");
|
|
60
|
+
const suffix = hostnameParts[0].endsWith("-my")
|
|
61
|
+
? "/_layouts/15/onedrive.aspx"
|
|
62
|
+
: "/forms/allitems.aspx";
|
|
60
63
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
64
|
+
let sitePath = url.pathname;
|
|
65
|
+
if (url.searchParams.has("id")) {
|
|
66
|
+
sitePath = url.searchParams.get("id") as string;
|
|
67
|
+
} else if (url.searchParams.has("RootFolder")) {
|
|
68
|
+
sitePath = url.searchParams.get("RootFolder") as string;
|
|
69
|
+
} else if (url.pathname.toLowerCase().endsWith(suffix)) {
|
|
70
|
+
sitePath = sitePath.substr(0, url.pathname.length - suffix.length);
|
|
71
|
+
}
|
|
69
72
|
|
|
70
|
-
|
|
73
|
+
return decodeURI(sitePath);
|
|
71
74
|
}
|
|
72
75
|
|
|
73
76
|
async function fluidFetchMain() {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
+
if (paramURL === undefined) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
77
80
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
81
|
+
const url = new URL(paramURL);
|
|
82
|
+
const server = url.hostname;
|
|
83
|
+
if (isOdspHostname(server)) {
|
|
84
|
+
// See if the url already has the specific item
|
|
85
|
+
const driveItem = getSharePointSpecificDriveItem(url);
|
|
86
|
+
if (driveItem) {
|
|
87
|
+
const file = await getSingleSharePointFile(server, driveItem.driveId, driveItem.itemId);
|
|
88
|
+
await tryFluidFetchOneSharePointFile(server, file);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
88
91
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
92
|
+
// See if the url given represent a sharepoint directory
|
|
93
|
+
const serverRelativePath = getSharepointServerRelativePathFromURL(url);
|
|
94
|
+
if (serverRelativePath !== undefined) {
|
|
95
|
+
const files = await getSharepointFiles(server, serverRelativePath, false);
|
|
96
|
+
for (const file of files) {
|
|
97
|
+
if (file.name.endsWith(".b") || file.name.endsWith(".fluid")) {
|
|
98
|
+
await tryFluidFetchOneSharePointFile(server, file);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
101
104
|
|
|
102
|
-
|
|
105
|
+
return fluidFetchOneFile(paramURL);
|
|
103
106
|
}
|
|
104
107
|
|
|
105
108
|
parseArguments();
|
|
106
109
|
|
|
107
110
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
108
111
|
fluidFetchMain()
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
112
|
+
.catch((error: Error) => {
|
|
113
|
+
if (error instanceof Error) {
|
|
114
|
+
let extraMsg = "";
|
|
115
|
+
for (const key of Object.keys(error)) {
|
|
116
|
+
// error[key] might have circular structure
|
|
117
|
+
try {
|
|
118
|
+
if (key !== "message" && key !== "stack") {
|
|
119
|
+
extraMsg += `\n${key}: ${JSON.stringify(error[key], undefined, 2)}`;
|
|
120
|
+
}
|
|
121
|
+
} catch (_) {}
|
|
122
|
+
}
|
|
123
|
+
console.error(`ERROR: ${error.stack}${extraMsg}`);
|
|
124
|
+
} else if (typeof error === "object") {
|
|
125
|
+
console.error(
|
|
126
|
+
`ERROR: Unknown exception object\n${JSON.stringify(error, undefined, 2)}`,
|
|
127
|
+
);
|
|
128
|
+
} else {
|
|
129
|
+
console.error(`ERROR: ${error}`);
|
|
130
|
+
}
|
|
131
|
+
})
|
|
132
|
+
.then(() => process.exit(0));
|
package/src/fluidFetchArgs.ts
CHANGED
|
@@ -14,16 +14,15 @@ export let dumpSnapshotVersions = false;
|
|
|
14
14
|
export let overWrite = false;
|
|
15
15
|
export let paramSnapshotVersionIndex: number | undefined;
|
|
16
16
|
export let paramNumSnapshotVersions = 10;
|
|
17
|
-
export let paramUnpackAggregatedBlobs = true;
|
|
18
17
|
export let paramActualFormatting = false;
|
|
19
18
|
|
|
20
19
|
let paramForceTokenReauth = false;
|
|
21
20
|
|
|
22
21
|
// Only return true once, to reauth on first call.
|
|
23
22
|
export function getForceTokenReauth() {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
const result = paramForceTokenReauth;
|
|
24
|
+
paramForceTokenReauth = false;
|
|
25
|
+
return result;
|
|
27
26
|
}
|
|
28
27
|
|
|
29
28
|
export let paramSaveDir: string | undefined;
|
|
@@ -36,184 +35,184 @@ export let connectToWebSocket = false;
|
|
|
36
35
|
|
|
37
36
|
export let localDataOnly = false;
|
|
38
37
|
|
|
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
|
-
}
|
|
38
|
+
const optionsArray = [
|
|
39
|
+
["--dump:rawmessage", "dump all messages"],
|
|
40
|
+
["--dump:snapshotVersion", "dump a list of snapshot version"],
|
|
41
|
+
["--dump:snapshotTree", "dump the snapshot trees"],
|
|
42
|
+
["--forceTokenReauth", "Force reauthorize token (SPO only)"],
|
|
43
|
+
["--stat:message", "show message type, channel type, data type statistics"],
|
|
44
|
+
["--stat:snapshot", "show a table of snapshot path and blob size"],
|
|
45
|
+
["--stat", "Show both messages & snapshot stats"],
|
|
46
|
+
["--filter:messageType <type>", "filter message by <type>"],
|
|
47
|
+
["--jwt <token>", "token to be used for routerlicious URLs"],
|
|
48
|
+
["--numSnapshotVersions <number>", "Number of versions to load (default:10)"],
|
|
49
|
+
[
|
|
50
|
+
"--actualPayload",
|
|
51
|
+
"Do not format json payloads nicely, preserve actual bytes / formatting in storage",
|
|
52
|
+
],
|
|
53
|
+
["--saveDir <outdir>", "Save data of the snapshots and messages"],
|
|
54
|
+
["--snapshotVersionIndex <number>", "Index of the version to dump"],
|
|
55
|
+
["--websocket", "Connect to web socket to download initial messages"],
|
|
56
|
+
["--local", "Do not connect to storage, use earlier downloaded data. Requires --saveDir."],
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
function printUsage() {
|
|
60
|
+
console.log("Usage: fluid-fetch [options] URL");
|
|
61
|
+
console.log("URL: <ODSP URL>|<Routerlicious URL>");
|
|
62
|
+
console.log("Options:");
|
|
63
|
+
for (const i of optionsArray) {
|
|
64
|
+
console.log(` ${i[0].padEnd(32)}: ${i[1]}`);
|
|
65
|
+
}
|
|
68
66
|
}
|
|
69
67
|
|
|
70
68
|
// Can be used in unit test to pass in customized argument values
|
|
71
69
|
// More argument options can be added when needed
|
|
72
70
|
export function setArguments(values: {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
71
|
+
saveDir: string;
|
|
72
|
+
paramURL: string;
|
|
73
|
+
dumpMessages?: boolean;
|
|
74
|
+
dumpMessageStats?: boolean;
|
|
75
|
+
dumpSnapshotStats?: boolean;
|
|
76
|
+
dumpSnapshotTrees?: boolean;
|
|
77
|
+
overWrite?: boolean;
|
|
78
|
+
}) {
|
|
79
|
+
paramSaveDir = values.saveDir;
|
|
80
|
+
paramURL = values.paramURL;
|
|
81
|
+
dumpMessages = values.dumpMessages ?? dumpMessages;
|
|
82
|
+
dumpMessageStats = values.dumpMessageStats ?? dumpMessageStats;
|
|
83
|
+
dumpSnapshotStats = values.dumpSnapshotStats ?? dumpSnapshotStats;
|
|
84
|
+
dumpSnapshotTrees = values.dumpSnapshotTrees ?? dumpSnapshotTrees;
|
|
85
|
+
overWrite = values.overWrite ?? overWrite;
|
|
87
86
|
}
|
|
88
87
|
|
|
89
88
|
export function parseArguments() {
|
|
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
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
checkArgs();
|
|
89
|
+
for (let i = 2; i < process.argv.length; i++) {
|
|
90
|
+
const arg = process.argv[i];
|
|
91
|
+
switch (arg) {
|
|
92
|
+
case "--dump:rawmessage":
|
|
93
|
+
dumpMessages = true;
|
|
94
|
+
break;
|
|
95
|
+
case "--dump:rawmessage:overwrite":
|
|
96
|
+
dumpMessages = true;
|
|
97
|
+
overWrite = true;
|
|
98
|
+
break;
|
|
99
|
+
case "--stat:message":
|
|
100
|
+
dumpMessageStats = true;
|
|
101
|
+
break;
|
|
102
|
+
case "--stat":
|
|
103
|
+
dumpMessageStats = true;
|
|
104
|
+
dumpSnapshotStats = true;
|
|
105
|
+
break;
|
|
106
|
+
case "--filter:messageType":
|
|
107
|
+
messageTypeFilter.add(parseStrArg(i++, "type name for messageType filter"));
|
|
108
|
+
break;
|
|
109
|
+
case "--stat:snapshot":
|
|
110
|
+
dumpSnapshotStats = true;
|
|
111
|
+
break;
|
|
112
|
+
case "--dump:snapshotVersion":
|
|
113
|
+
dumpSnapshotVersions = true;
|
|
114
|
+
break;
|
|
115
|
+
case "--dump:snapshotTree":
|
|
116
|
+
dumpSnapshotTrees = true;
|
|
117
|
+
break;
|
|
118
|
+
case "--help":
|
|
119
|
+
printUsage();
|
|
120
|
+
process.exit(0);
|
|
121
|
+
case "--jwt":
|
|
122
|
+
paramJWT = parseStrArg(i++, "jwt token");
|
|
123
|
+
break;
|
|
124
|
+
case "--forceTokenReauth":
|
|
125
|
+
paramForceTokenReauth = true;
|
|
126
|
+
break;
|
|
127
|
+
case "--snapshotVersionIndex":
|
|
128
|
+
paramSnapshotVersionIndex = parseIntArg(i++, "version index", true);
|
|
129
|
+
break;
|
|
130
|
+
case "--numSnapshotVersions":
|
|
131
|
+
paramNumSnapshotVersions = parseIntArg(i++, "number of versions", false);
|
|
132
|
+
break;
|
|
133
|
+
case "--actualPayload":
|
|
134
|
+
paramActualFormatting = true;
|
|
135
|
+
break;
|
|
136
|
+
case "--saveDir":
|
|
137
|
+
paramSaveDir = parseStrArg(i++, "save data path");
|
|
138
|
+
break;
|
|
139
|
+
case "--websocket":
|
|
140
|
+
connectToWebSocket = true;
|
|
141
|
+
break;
|
|
142
|
+
case "--local":
|
|
143
|
+
localDataOnly = true;
|
|
144
|
+
break;
|
|
145
|
+
default:
|
|
146
|
+
try {
|
|
147
|
+
const url = new URL(arg);
|
|
148
|
+
if (url.protocol === "https:") {
|
|
149
|
+
paramURL = arg;
|
|
150
|
+
break;
|
|
151
|
+
}
|
|
152
|
+
if (url.protocol === "http:" && url.hostname === "localhost") {
|
|
153
|
+
paramURL = arg;
|
|
154
|
+
break;
|
|
155
|
+
}
|
|
156
|
+
} catch (e) {
|
|
157
|
+
console.error(e);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
console.error(`ERROR: Invalid argument ${arg}`);
|
|
161
|
+
printUsage();
|
|
162
|
+
process.exit(-1);
|
|
163
|
+
break;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
checkArgs();
|
|
171
167
|
}
|
|
172
168
|
|
|
173
169
|
function parseStrArg(i: number, name: string) {
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
170
|
+
if (i + 1 >= process.argv.length) {
|
|
171
|
+
console.error(`ERROR: Missing ${name}`);
|
|
172
|
+
printUsage();
|
|
173
|
+
process.exit(-1);
|
|
174
|
+
}
|
|
175
|
+
return process.argv[i + 1];
|
|
180
176
|
}
|
|
181
177
|
function parseIntArg(i: number, name: string, allowZero: boolean) {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
178
|
+
if (i + 1 >= process.argv.length) {
|
|
179
|
+
console.error(`ERROR: Missing ${name}`);
|
|
180
|
+
printUsage();
|
|
181
|
+
process.exit(-1);
|
|
182
|
+
}
|
|
183
|
+
const numStr = process.argv[i + 1];
|
|
184
|
+
const paramNumber = parseInt(numStr, 10);
|
|
185
|
+
if (isNaN(paramNumber) || (allowZero ? paramNumber < 0 : paramNumber <= 0)) {
|
|
186
|
+
console.error(`ERROR: Invalid ${name} ${numStr}`);
|
|
187
|
+
printUsage();
|
|
188
|
+
process.exit(-1);
|
|
189
|
+
}
|
|
190
|
+
return paramNumber;
|
|
195
191
|
}
|
|
196
192
|
|
|
197
193
|
function checkArgs() {
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
194
|
+
if (paramSnapshotVersionIndex !== undefined) {
|
|
195
|
+
paramNumSnapshotVersions = Math.max(
|
|
196
|
+
paramSnapshotVersionIndex + 1,
|
|
197
|
+
paramNumSnapshotVersions,
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (paramURL === undefined) {
|
|
202
|
+
if (paramSaveDir !== undefined) {
|
|
203
|
+
const file = `${paramSaveDir}/info.json`;
|
|
204
|
+
if (fs.existsSync(file)) {
|
|
205
|
+
const info = JSON.parse(fs.readFileSync(file, { encoding: "utf-8" }));
|
|
206
|
+
paramURL = info.url;
|
|
207
|
+
} else {
|
|
208
|
+
console.log(`Can't find file ${file}`);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (paramURL === undefined) {
|
|
213
|
+
console.error("ERROR: Missing URL");
|
|
214
|
+
printUsage();
|
|
215
|
+
process.exit(-1);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
219
218
|
}
|