@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.
Files changed (37) hide show
  1. package/.eslintrc.js +6 -8
  2. package/CHANGELOG.md +117 -0
  3. package/README.md +38 -7
  4. package/bin/fluid-fetch +0 -0
  5. package/dist/fluidAnalyzeMessages.d.ts.map +1 -1
  6. package/dist/fluidAnalyzeMessages.js +106 -116
  7. package/dist/fluidAnalyzeMessages.js.map +1 -1
  8. package/dist/fluidFetch.js +5 -3
  9. package/dist/fluidFetch.js.map +1 -1
  10. package/dist/fluidFetchArgs.d.ts +0 -3
  11. package/dist/fluidFetchArgs.d.ts.map +1 -1
  12. package/dist/fluidFetchArgs.js +10 -14
  13. package/dist/fluidFetchArgs.js.map +1 -1
  14. package/dist/fluidFetchInit.d.ts +0 -1
  15. package/dist/fluidFetchInit.d.ts.map +1 -1
  16. package/dist/fluidFetchInit.js +41 -34
  17. package/dist/fluidFetchInit.js.map +1 -1
  18. package/dist/fluidFetchMessages.d.ts.map +1 -1
  19. package/dist/fluidFetchMessages.js +168 -200
  20. package/dist/fluidFetchMessages.js.map +1 -1
  21. package/dist/fluidFetchSharePoint.d.ts +0 -1
  22. package/dist/fluidFetchSharePoint.d.ts.map +1 -1
  23. package/dist/fluidFetchSharePoint.js +20 -6
  24. package/dist/fluidFetchSharePoint.js.map +1 -1
  25. package/dist/fluidFetchSnapshot.d.ts.map +1 -1
  26. package/dist/fluidFetchSnapshot.js +18 -20
  27. package/dist/fluidFetchSnapshot.js.map +1 -1
  28. package/package.json +47 -42
  29. package/prettier.config.cjs +8 -0
  30. package/src/fluidAnalyzeMessages.ts +701 -630
  31. package/src/fluidFetch.ts +93 -88
  32. package/src/fluidFetchArgs.ts +167 -168
  33. package/src/fluidFetchInit.ts +133 -104
  34. package/src/fluidFetchMessages.ts +253 -232
  35. package/src/fluidFetchSharePoint.ts +130 -112
  36. package/src/fluidFetchSnapshot.ts +313 -295
  37. package/tsconfig.json +8 -15
@@ -4,277 +4,298 @@
4
4
  */
5
5
 
6
6
  import fs from "fs";
7
- import { assert } from "@fluidframework/common-utils";
7
+ import { assert } from "@fluidframework/core-utils";
8
+ import { IDocumentService } from "@fluidframework/driver-definitions";
8
9
  import {
9
- IDocumentService,
10
- } from "@fluidframework/driver-definitions";
11
- import {
12
- IClient,
13
- ISequencedDocumentMessage,
14
- MessageType,
15
- ScopeType,
10
+ IClient,
11
+ ISequencedDocumentMessage,
12
+ MessageType,
13
+ ScopeType,
16
14
  } from "@fluidframework/protocol-definitions";
17
15
  import { printMessageStats } from "./fluidAnalyzeMessages";
18
16
  import {
19
- connectToWebSocket,
20
- dumpMessages,
21
- dumpMessageStats,
22
- overWrite,
23
- paramActualFormatting,
24
- messageTypeFilter,
17
+ connectToWebSocket,
18
+ dumpMessages,
19
+ dumpMessageStats,
20
+ overWrite,
21
+ paramActualFormatting,
22
+ messageTypeFilter,
25
23
  } from "./fluidFetchArgs";
26
24
 
27
25
  function filenameFromIndex(index: number): string {
28
- return index === 0 ? "" : index.toString(); // support old tools...
26
+ return index === 0 ? "" : index.toString(); // support old tools...
29
27
  }
30
28
 
31
29
  let firstAvailableDelta = 1;
32
30
  async function* loadAllSequencedMessages(
33
- documentService?: IDocumentService,
34
- dir?: string,
35
- files?: string[]) {
36
- let lastSeq = 0;
37
- // flag for mismatch between last sequence number read and new one to be read
38
- let seqNumMismatch = false;
31
+ documentService?: IDocumentService,
32
+ dir?: string,
33
+ files?: string[],
34
+ ) {
35
+ let lastSeq = 0;
36
+ // flag for mismatch between last sequence number read and new one to be read
37
+ let seqNumMismatch = false;
39
38
 
40
- // If we have local save, read ops from there first
41
- if (files !== undefined) {
42
- for (let i = 0; i < files.length; i++) {
43
- const file = filenameFromIndex(i);
44
- try {
45
- console.log(`reading messages${file}.json`);
46
- const fileContent = fs.readFileSync(`${dir}/messages${file}.json`, { encoding: "utf-8" });
47
- const messages: ISequencedDocumentMessage[] = JSON.parse(fileContent);
48
- // check if there is mismatch
49
- seqNumMismatch = messages[0].sequenceNumber !== lastSeq + 1;
50
- assert(!seqNumMismatch, 0x1b9 /* "Unexpected value for sequence number of first message in file" */);
51
- lastSeq = messages[messages.length - 1].sequenceNumber;
52
- yield messages;
53
- } catch (e) {
54
- if (seqNumMismatch) {
55
- if (overWrite) {
56
- // with overWrite option on, we will delete all exisintg message.json files
57
- for (let index = 0; index < files.length; index++) {
58
- const name = filenameFromIndex(index);
59
- fs.unlinkSync(`${dir}/messages${name}.json`);
60
- }
61
- break;
62
- }
63
- // prompt user to back up and delete existing files
64
- console.error("There are deleted ops in the document being requested," +
65
- " please back up the existing messages.json file and delete it from its directory." +
66
- " Then try fetch tool again.");
67
- console.error(e);
68
- return;
69
- } else {
70
- console.error(`Error reading / parsing messages from ${files}`);
71
- console.error(e);
72
- return;
73
- }
74
- }
75
- }
76
- if (lastSeq !== 0) {
77
- console.log(`Read ${lastSeq} ops from local cache`);
78
- }
79
- }
39
+ // If we have local save, read ops from there first
40
+ if (files !== undefined) {
41
+ for (let i = 0; i < files.length; i++) {
42
+ const file = filenameFromIndex(i);
43
+ try {
44
+ console.log(`reading messages${file}.json`);
45
+ const fileContent = fs.readFileSync(`${dir}/messages${file}.json`, {
46
+ encoding: "utf-8",
47
+ });
48
+ const messages: ISequencedDocumentMessage[] = JSON.parse(fileContent);
49
+ // check if there is mismatch
50
+ seqNumMismatch = messages[0].sequenceNumber !== lastSeq + 1;
51
+ assert(
52
+ !seqNumMismatch,
53
+ 0x1b9 /* "Unexpected value for sequence number of first message in file" */,
54
+ );
55
+ lastSeq = messages[messages.length - 1].sequenceNumber;
56
+ yield messages;
57
+ } catch (e) {
58
+ if (seqNumMismatch) {
59
+ if (overWrite) {
60
+ // with overWrite option on, we will delete all exisintg message.json files
61
+ for (let index = 0; index < files.length; index++) {
62
+ const name = filenameFromIndex(index);
63
+ fs.unlinkSync(`${dir}/messages${name}.json`);
64
+ }
65
+ break;
66
+ }
67
+ // prompt user to back up and delete existing files
68
+ console.error(
69
+ "There are deleted ops in the document being requested," +
70
+ " please back up the existing messages.json file and delete it from its directory." +
71
+ " Then try fetch tool again.",
72
+ );
73
+ console.error(e);
74
+ return;
75
+ } else {
76
+ console.error(`Error reading / parsing messages from ${files}`);
77
+ console.error(e);
78
+ return;
79
+ }
80
+ }
81
+ }
82
+ if (lastSeq !== 0) {
83
+ console.log(`Read ${lastSeq} ops from local cache`);
84
+ }
85
+ }
80
86
 
81
- if (!documentService) {
82
- return;
83
- }
87
+ if (!documentService) {
88
+ return;
89
+ }
84
90
 
85
- const deltaStorage = await documentService.connectToDeltaStorage();
91
+ const deltaStorage = await documentService.connectToDeltaStorage();
86
92
 
87
- let timeStart = Date.now();
88
- let requests = 0;
89
- let opsStorage = 0;
93
+ let timeStart = Date.now();
94
+ let requests = 0;
95
+ let opsStorage = 0;
90
96
 
91
- // reading only 1 op to test if there is mismatch
92
- const teststream = deltaStorage.fetchMessages(
93
- lastSeq + 1,
94
- lastSeq + 2);
97
+ // reading only 1 op to test if there is mismatch
98
+ const teststream = deltaStorage.fetchMessages(lastSeq + 1, lastSeq + 2);
95
99
 
96
- let statusCode;
97
- let innerMostErrorCode;
98
- let response;
100
+ let statusCode;
101
+ let innerMostErrorCode;
102
+ let response;
99
103
 
100
- try {
101
- await teststream.read();
102
- } catch (error: any) {
103
- statusCode = error.getTelemetryProperties().statusCode;
104
- innerMostErrorCode = error.getTelemetryProperties().innerMostErrorCode;
105
- // if there is gap between ops, catch the error and check it is the error we need
106
- if (statusCode !== 410 || innerMostErrorCode !== "fluidDeltaDataNotAvailable") {
107
- throw error;
108
- }
109
- // get firstAvailableDelta from the error response, and set current sequence number to that
110
- response = JSON.parse(error.getTelemetryProperties().response);
111
- firstAvailableDelta = response.error.firstAvailableDelta;
112
- lastSeq = firstAvailableDelta - 1;
113
- }
104
+ try {
105
+ await teststream.read();
106
+ } catch (error: any) {
107
+ statusCode = error.getTelemetryProperties().statusCode;
108
+ innerMostErrorCode = error.getTelemetryProperties().innerMostErrorCode;
109
+ // if there is gap between ops, catch the error and check it is the error we need
110
+ if (statusCode !== 410 || innerMostErrorCode !== "fluidDeltaDataNotAvailable") {
111
+ throw error;
112
+ }
113
+ // get firstAvailableDelta from the error response, and set current sequence number to that
114
+ response = JSON.parse(error.getTelemetryProperties().response);
115
+ firstAvailableDelta = response.error.firstAvailableDelta;
116
+ lastSeq = firstAvailableDelta - 1;
117
+ }
114
118
 
115
- // continue reading rest of the ops
116
- const stream = deltaStorage.fetchMessages(
117
- lastSeq + 1, // inclusive left
118
- undefined, // to
119
- );
119
+ // continue reading rest of the ops
120
+ const stream = deltaStorage.fetchMessages(
121
+ lastSeq + 1, // inclusive left
122
+ undefined, // to
123
+ );
120
124
 
121
- while (true) {
122
- const result = await stream.read();
123
- if (result.done) {
124
- break;
125
- }
126
- requests++;
127
- const messages = result.value;
125
+ while (true) {
126
+ const result = await stream.read();
127
+ if (result.done) {
128
+ break;
129
+ }
130
+ requests++;
131
+ const messages = result.value;
128
132
 
129
- // Empty buckets should never be returned
130
- assert(messages.length !== 0, 0x1ba /* "should not return empty buckets" */);
131
- // console.log(`Loaded ops at ${messages[0].sequenceNumber}`);
133
+ // Empty buckets should never be returned
134
+ assert(messages.length !== 0, 0x1ba /* "should not return empty buckets" */);
135
+ // console.log(`Loaded ops at ${messages[0].sequenceNumber}`);
132
136
 
133
- // This parsing of message contents happens in delta manager. But when we analyze messages
134
- // for message stats, we skip that path. So parsing of json contents needs to happen here.
135
- for (const message of messages) {
136
- if (typeof message.contents === "string"
137
- && message.contents !== ""
138
- && message.type !== MessageType.ClientLeave
139
- ) {
140
- message.contents = JSON.parse(message.contents);
141
- }
142
- }
137
+ // This parsing of message contents happens in delta manager. But when we analyze messages
138
+ // for message stats, we skip that path. So parsing of json contents needs to happen here.
139
+ for (const message of messages) {
140
+ if (
141
+ typeof message.contents === "string" &&
142
+ message.contents !== "" &&
143
+ message.type !== MessageType.ClientLeave
144
+ ) {
145
+ message.contents = JSON.parse(message.contents);
146
+ }
147
+ }
143
148
 
144
- opsStorage += messages.length;
145
- lastSeq = messages[messages.length - 1].sequenceNumber;
146
- yield messages;
147
- }
149
+ opsStorage += messages.length;
150
+ lastSeq = messages[messages.length - 1].sequenceNumber;
151
+ yield messages;
152
+ }
148
153
 
149
- // eslint-disable-next-line max-len
150
- console.log(`\n${Math.floor((Date.now() - timeStart) / 1000)} seconds to retrieve ${opsStorage} ops in ${requests} requests`);
154
+ console.log(
155
+ `\n${Math.floor(
156
+ (Date.now() - timeStart) / 1000,
157
+ )} seconds to retrieve ${opsStorage} ops in ${requests} requests`,
158
+ );
151
159
 
152
- if (connectToWebSocket) {
153
- let logMsg = "";
154
- const client: IClient = {
155
- mode: "write",
156
- permission: [],
157
- scopes: [ScopeType.DocRead, ScopeType.DocWrite, ScopeType.SummaryWrite],
158
- details: {
159
- capabilities: { interactive: true },
160
- },
161
- user: { id: "blah" },
162
- };
163
- console.log("Retrieving messages from web socket");
164
- timeStart = Date.now();
165
- const deltaStream = await documentService.connectToDeltaStream(client);
166
- const initialMessages = deltaStream.initialMessages;
167
- deltaStream.dispose();
168
- console.log(`${Math.floor((Date.now() - timeStart) / 1000)} seconds to connect to web socket`);
160
+ if (connectToWebSocket) {
161
+ let logMsg = "";
162
+ const client: IClient = {
163
+ mode: "write",
164
+ permission: [],
165
+ scopes: [ScopeType.DocRead, ScopeType.DocWrite, ScopeType.SummaryWrite],
166
+ details: {
167
+ capabilities: { interactive: true },
168
+ },
169
+ user: { id: "blah" },
170
+ };
171
+ console.log("Retrieving messages from web socket");
172
+ timeStart = Date.now();
173
+ const deltaStream = await documentService.connectToDeltaStream(client);
174
+ const initialMessages = deltaStream.initialMessages;
175
+ deltaStream.dispose();
176
+ console.log(
177
+ `${Math.floor((Date.now() - timeStart) / 1000)} seconds to connect to web socket`,
178
+ );
169
179
 
170
- if (initialMessages !== undefined) {
171
- const lastSequenceNumber = lastSeq;
172
- const filtered = initialMessages.filter((a) => a.sequenceNumber > lastSequenceNumber);
173
- const sorted = filtered.sort((a, b) => a.sequenceNumber - b.sequenceNumber);
174
- lastSeq = sorted[sorted.length - 1].sequenceNumber;
175
- // eslint-disable-next-line max-len
176
- logMsg = ` (${opsStorage} delta storage, ${initialMessages.length} initial ws messages, ${initialMessages.length - sorted.length} dup)`;
177
- yield sorted;
178
- }
179
- console.log(`${lastSeq} total messages${logMsg}`);
180
- }
180
+ if (initialMessages !== undefined) {
181
+ const lastSequenceNumber = lastSeq;
182
+ const filtered = initialMessages.filter((a) => a.sequenceNumber > lastSequenceNumber);
183
+ const sorted = filtered.sort((a, b) => a.sequenceNumber - b.sequenceNumber);
184
+ lastSeq = sorted[sorted.length - 1].sequenceNumber;
185
+ logMsg = ` (${opsStorage} delta storage, ${
186
+ initialMessages.length
187
+ } initial ws messages, ${initialMessages.length - sorted.length} dup)`;
188
+ yield sorted;
189
+ }
190
+ console.log(`${lastSeq} total messages${logMsg}`);
191
+ }
181
192
  }
182
193
 
183
194
  async function* saveOps(
184
- gen, // AsyncGenerator<ISequencedDocumentMessage[]>,
185
- dir: string,
186
- files: string[]) {
187
- // Split into 100K ops
188
- const chunk = 100 * 1000;
195
+ gen, // AsyncGenerator<ISequencedDocumentMessage[]>,
196
+ dir: string,
197
+ files: string[],
198
+ ) {
199
+ // Split into 100K ops
200
+ const chunk = 100 * 1000;
189
201
 
190
- let sequencedMessages: ISequencedDocumentMessage[] = [];
202
+ let sequencedMessages: ISequencedDocumentMessage[] = [];
191
203
 
192
- // Figure out first file we want to write to
193
- let index = 0;
194
- let curr: number = 1;
195
- if (files.length !== 0) {
196
- index = files.length - 1;
197
- const name = filenameFromIndex(index);
198
- const fileContent = fs.readFileSync(`${dir}/messages${name}.json`, { encoding: "utf-8" });
199
- const messages: ISequencedDocumentMessage[] = JSON.parse(fileContent);
200
- curr = messages[0].sequenceNumber;
201
- }
204
+ // Figure out first file we want to write to
205
+ let index = 0;
206
+ let curr: number = 1;
207
+ if (files.length !== 0) {
208
+ index = files.length - 1;
209
+ const name = filenameFromIndex(index);
210
+ const fileContent = fs.readFileSync(`${dir}/messages${name}.json`, { encoding: "utf-8" });
211
+ const messages: ISequencedDocumentMessage[] = JSON.parse(fileContent);
212
+ curr = messages[0].sequenceNumber;
213
+ }
202
214
 
203
- while (true) {
204
- const result: IteratorResult<ISequencedDocumentMessage[]> = await gen.next();
205
- if (files.length === 0) {
206
- curr = firstAvailableDelta;
207
- }
208
- if (result.done !== true) {
209
- let messages = result.value;
210
- yield messages;
211
- if (messages[messages.length - 1].sequenceNumber < curr) {
212
- // Nothing interesting.
213
- continue;
214
- }
215
- if (messages[0].sequenceNumber < curr) {
216
- messages = messages.filter((msg) => msg.sequenceNumber >= curr);
217
- }
218
- sequencedMessages = sequencedMessages.concat(messages);
219
- assert(sequencedMessages[0].sequenceNumber === curr,
220
- 0x1bb /* "Unexpected sequence number on first of messages to save" */);
221
- assert(sequencedMessages[sequencedMessages.length - 1].sequenceNumber
222
- === curr + sequencedMessages.length - 1,
223
- 0x1bc /* "Unexpected sequence number on last of messages to save" */);
224
- }
215
+ while (true) {
216
+ const result: IteratorResult<ISequencedDocumentMessage[]> = await gen.next();
217
+ if (files.length === 0) {
218
+ curr = firstAvailableDelta;
219
+ }
220
+ if (result.done !== true) {
221
+ let messages = result.value;
222
+ yield messages;
223
+ if (messages[messages.length - 1].sequenceNumber < curr) {
224
+ // Nothing interesting.
225
+ continue;
226
+ }
227
+ if (messages[0].sequenceNumber < curr) {
228
+ messages = messages.filter((msg) => msg.sequenceNumber >= curr);
229
+ }
230
+ sequencedMessages = sequencedMessages.concat(messages);
231
+ assert(
232
+ sequencedMessages[0].sequenceNumber === curr,
233
+ 0x1bb /* "Unexpected sequence number on first of messages to save" */,
234
+ );
235
+ assert(
236
+ sequencedMessages[sequencedMessages.length - 1].sequenceNumber ===
237
+ curr + sequencedMessages.length - 1,
238
+ 0x1bc /* "Unexpected sequence number on last of messages to save" */,
239
+ );
240
+ }
225
241
 
226
- // Time to write it out?
227
- while (sequencedMessages.length >= chunk || (result.done === true && sequencedMessages.length !== 0)) {
228
- const name = filenameFromIndex(index);
229
- const write = sequencedMessages.splice(0, chunk);
230
- console.log(`writing messages${name}.json`);
231
- fs.writeFileSync(
232
- `${dir}/messages${name}.json`,
233
- JSON.stringify(write, undefined, paramActualFormatting ? 0 : 2));
234
- // increment curr by chunk
235
- curr += chunk;
236
- assert(sequencedMessages.length === 0 || sequencedMessages[0].sequenceNumber === curr,
237
- 0x1bd /* "Stopped writing at unexpected sequence number" */);
238
- index++;
239
- }
242
+ // Time to write it out?
243
+ while (
244
+ sequencedMessages.length >= chunk ||
245
+ (result.done === true && sequencedMessages.length !== 0)
246
+ ) {
247
+ const name = filenameFromIndex(index);
248
+ const write = sequencedMessages.splice(0, chunk);
249
+ console.log(`writing messages${name}.json`);
250
+ fs.writeFileSync(
251
+ `${dir}/messages${name}.json`,
252
+ JSON.stringify(write, undefined, paramActualFormatting ? 0 : 2),
253
+ );
254
+ // increment curr by chunk
255
+ curr += chunk;
256
+ assert(
257
+ sequencedMessages.length === 0 || sequencedMessages[0].sequenceNumber === curr,
258
+ 0x1bd /* "Stopped writing at unexpected sequence number" */,
259
+ );
260
+ index++;
261
+ }
240
262
 
241
- if (result.done === true) {
242
- break;
243
- }
244
- }
263
+ if (result.done === true) {
264
+ break;
265
+ }
266
+ }
245
267
  }
246
268
 
247
269
  export async function fluidFetchMessages(documentService?: IDocumentService, saveDir?: string) {
248
- const messageStats = dumpMessageStats || dumpMessages;
249
- if (!messageStats && (saveDir === undefined || documentService === undefined)) {
250
- return;
251
- }
270
+ const messageStats = dumpMessageStats || dumpMessages;
271
+ if (!messageStats && (saveDir === undefined || documentService === undefined)) {
272
+ return;
273
+ }
252
274
 
253
- const files = saveDir === undefined
254
- ? undefined
255
- : fs.readdirSync(saveDir)
256
- .filter((file) => {
257
- if (!file.startsWith("messages")) {
258
- return false;
259
- }
260
- return true;
261
- })
262
- .sort((a, b) => a.localeCompare(b));
275
+ const files =
276
+ saveDir === undefined
277
+ ? undefined
278
+ : fs
279
+ .readdirSync(saveDir)
280
+ .filter((file) => {
281
+ if (!file.startsWith("messages")) {
282
+ return false;
283
+ }
284
+ return true;
285
+ })
286
+ .sort((a, b) => a.localeCompare(b));
263
287
 
264
- let generator = loadAllSequencedMessages(documentService, saveDir, files);
288
+ let generator = loadAllSequencedMessages(documentService, saveDir, files);
265
289
 
266
- if (saveDir !== undefined && files !== undefined && documentService) {
267
- generator = saveOps(generator, saveDir, files);
268
- }
290
+ if (saveDir !== undefined && files !== undefined && documentService) {
291
+ generator = saveOps(generator, saveDir, files);
292
+ }
269
293
 
270
- if (messageStats) {
271
- return printMessageStats(
272
- generator,
273
- dumpMessageStats,
274
- dumpMessages,
275
- messageTypeFilter);
276
- } else {
277
- let item;
278
- for await (item of generator) { }
279
- }
294
+ if (messageStats) {
295
+ return printMessageStats(generator, dumpMessageStats, dumpMessages, messageTypeFilter);
296
+ } else {
297
+ let item;
298
+ for await (item of generator) {
299
+ }
300
+ }
280
301
  }