@fluidframework/tool-utils 1.2.7 → 2.0.0-dev.1.3.0.96595
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/.mocharc.js +12 -0
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.d.ts.map +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/snapshotNormalizer.js +31 -0
- package/dist/snapshotNormalizer.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.d.ts.map +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/snapshotNormalizer.js +31 -0
- package/lib/snapshotNormalizer.js.map +1 -1
- package/package.json +12 -12
- package/src/packageVersion.ts +1 -1
- package/src/snapshotNormalizer.ts +46 -0
package/.mocharc.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
|
+
* Licensed under the MIT License.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
const getFluidTestMochaConfig = require('@fluidframework/mocha-test-setup/mocharc-common');
|
|
9
|
+
|
|
10
|
+
const packageDir = __dirname;
|
|
11
|
+
const config = getFluidTestMochaConfig(packageDir);
|
|
12
|
+
module.exports = config;
|
package/dist/packageVersion.d.ts
CHANGED
|
@@ -5,5 +5,5 @@
|
|
|
5
5
|
* THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY
|
|
6
6
|
*/
|
|
7
7
|
export declare const pkgName = "@fluidframework/tool-utils";
|
|
8
|
-
export declare const pkgVersion = "1.
|
|
8
|
+
export declare const pkgVersion = "2.0.0-dev.1.3.0.96595";
|
|
9
9
|
//# sourceMappingURL=packageVersion.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"packageVersion.d.ts","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,eAAO,MAAM,OAAO,+BAA+B,CAAC;AACpD,eAAO,MAAM,UAAU,
|
|
1
|
+
{"version":3,"file":"packageVersion.d.ts","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,eAAO,MAAM,OAAO,+BAA+B,CAAC;AACpD,eAAO,MAAM,UAAU,0BAA0B,CAAC"}
|
package/dist/packageVersion.js
CHANGED
|
@@ -8,5 +8,5 @@
|
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
9
|
exports.pkgVersion = exports.pkgName = void 0;
|
|
10
10
|
exports.pkgName = "@fluidframework/tool-utils";
|
|
11
|
-
exports.pkgVersion = "1.
|
|
11
|
+
exports.pkgVersion = "2.0.0-dev.1.3.0.96595";
|
|
12
12
|
//# sourceMappingURL=packageVersion.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"packageVersion.js","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;AAEU,QAAA,OAAO,GAAG,4BAA4B,CAAC;AACvC,QAAA,UAAU,GAAG,
|
|
1
|
+
{"version":3,"file":"packageVersion.js","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;AAEU,QAAA,OAAO,GAAG,4BAA4B,CAAC;AACvC,QAAA,UAAU,GAAG,uBAAuB,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n *\n * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY\n */\n\nexport const pkgName = \"@fluidframework/tool-utils\";\nexport const pkgVersion = \"2.0.0-dev.1.3.0.96595\";\n"]}
|
|
@@ -134,6 +134,34 @@ function getNormalizedSnapshot(snapshot, config) {
|
|
|
134
134
|
};
|
|
135
135
|
}
|
|
136
136
|
exports.getNormalizedSnapshot = getNormalizedSnapshot;
|
|
137
|
+
function normalizeMatrix(value) {
|
|
138
|
+
const rows = value.entries.find((e) => e.path === "rows");
|
|
139
|
+
if (!rows || !("entries" in rows.value)) {
|
|
140
|
+
return value;
|
|
141
|
+
}
|
|
142
|
+
const segments = rows.value.entries.find((e) => e.path === "segments");
|
|
143
|
+
if (!segments || !("entries" in segments.value)) {
|
|
144
|
+
return value;
|
|
145
|
+
}
|
|
146
|
+
const header = segments.value.entries.find((e) => e.path === "header");
|
|
147
|
+
if (!header || !("contents" in header.value)) {
|
|
148
|
+
return value;
|
|
149
|
+
}
|
|
150
|
+
if (!(header === null || header === void 0 ? void 0 : header.value.contents.includes("removedClientId"))) {
|
|
151
|
+
return value;
|
|
152
|
+
}
|
|
153
|
+
const contents = JSON.parse(header === null || header === void 0 ? void 0 : header.value.contents);
|
|
154
|
+
for (const segment of contents.segments) {
|
|
155
|
+
if ("removedClientId" in segment) {
|
|
156
|
+
segment.removedClientId = undefined;
|
|
157
|
+
}
|
|
158
|
+
if ("removedClientIds" in segment) {
|
|
159
|
+
segment.removedClientIds = undefined;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
header.value.contents = JSON.stringify(contents);
|
|
163
|
+
return value;
|
|
164
|
+
}
|
|
137
165
|
function normalizeEntry(entry, config) {
|
|
138
166
|
var _a;
|
|
139
167
|
switch (entry.type) {
|
|
@@ -150,6 +178,9 @@ function normalizeEntry(entry, config) {
|
|
|
150
178
|
for (const maybeAttributes of entry.value.entries) {
|
|
151
179
|
if (maybeAttributes.type === protocol_definitions_1.TreeEntry.Blob && maybeAttributes.path === ".attributes") {
|
|
152
180
|
const parsed = JSON.parse(maybeAttributes.value.contents);
|
|
181
|
+
if (parsed.type === "https://graph.microsoft.com/types/sharedmatrix") {
|
|
182
|
+
return new protocol_base_1.TreeTreeEntry(entry.path, normalizeMatrix(getNormalizedSnapshot(entry.value, config)));
|
|
183
|
+
}
|
|
153
184
|
if (parsed.type !== undefined && config.excludedChannelContentTypes.includes(parsed.type)) {
|
|
154
185
|
// remove everything to match the unknown channel
|
|
155
186
|
return new protocol_base_1.TreeTreeEntry(entry.path, { entries: [maybeAttributes] });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"snapshotNormalizer.js","sourceRoot":"","sources":["../src/snapshotNormalizer.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { AttachmentTreeEntry, BlobTreeEntry, TreeTreeEntry } from \"@fluidframework/protocol-base\";\nimport {\n ITree,\n TreeEntry,\n ITreeEntry,\n} from \"@fluidframework/protocol-definitions\";\n\n/** The name of the metadata blob added to the root of the container runtime. */\nconst metadataBlobName = \".metadata\";\n/** The prefix that all GC blob names start with. */\nexport const gcBlobPrefix = \"__gc\";\n\nexport interface ISnapshotNormalizerConfig {\n // The paths of blobs whose contents should be normalized.\n blobsToNormalize?: string[];\n /**\n * channel types who's content (non-attribute) blobs will be excluded.\n * this is used to exclude the content of channels who's content cannot be compared\n * as the content is non-deterministic between snapshot at the same sequence number.\n */\n excludedChannelContentTypes?: string[];\n}\n\n/**\n * Function that deep sorts an array. It handles cases where array elements are objects or arrays.\n * @returns the sorted array.\n */\nfunction getDeepSortedArray(array: any[]): any[] {\n const sortedArray: any[] = [];\n // Sort arrays and objects, if any, in the array.\n for (const element of array) {\n if (Array.isArray(element)) {\n sortedArray.push(getDeepSortedArray(element));\n } else if (element instanceof Object) {\n sortedArray.push(getDeepSortedObject(element));\n } else {\n sortedArray.push(element);\n }\n }\n\n // Now that all the arrays and objects in this array's elements have been sorted, sort it by comparing each\n // element's stringified version.\n const sortFn = (elem1: any, elem2: any) => {\n const serializedElem1 = JSON.stringify(elem1);\n const serializedElem2 = JSON.stringify(elem2);\n return serializedElem1.localeCompare(serializedElem2);\n };\n\n return sortedArray.sort(sortFn);\n}\n\n/**\n * Function that deep sorts an object. It handles cases where object properties are arrays or objects.\n * @returns the sorted object.\n */\nfunction getDeepSortedObject(obj: any): any {\n const sortedObj: any = {};\n // Sort the object keys first. Then sort arrays and objects, if any, in the object.\n const keys = Object.keys(obj).sort();\n for (const key of keys) {\n const value = obj[key];\n if (Array.isArray(value)) {\n sortedObj[key] = getDeepSortedArray(value);\n } else if (value instanceof Object) {\n sortedObj[key] = getDeepSortedObject(value);\n } else {\n sortedObj[key] = value;\n }\n }\n\n return sortedObj;\n}\n\n/**\n * Function that normalizes a blob's content. If the content is an object or an array, deep sorts them.\n * Special handling for certain runtime blobs, such as the \"gc\" blob.\n * @returns the normalized blob content.\n */\nfunction getNormalizedBlobContent(blobContent: string, blobName: string): string {\n let content = blobContent;\n if (blobName.startsWith(gcBlobPrefix)) {\n // GC blobs may contain `unreferencedTimestampMs` for node that became unreferenced. This is the timestamp\n // of the last op processed or current timestamp and can differ between clients depending on when GC was run.\n // So, remove it for the purposes of comparing snapshots.\n const gcState = JSON.parse(content);\n for (const [, data] of Object.entries(gcState.gcNodes)) {\n delete (data as any).unreferencedTimestampMs;\n }\n content = JSON.stringify(gcState);\n }\n\n /**\n * The metadata blob has \"summaryNumber\" or \"summaryCount\" that tells which summary this is for a container. It can\n * be different in summaries of two clients even if they are generated at the same sequence#. For instance, at seq#\n * 1000, if one client has summarized 10 times and other has summarizer 15 times, summaryNumber will be different\n * for them. So, update \"summaryNumber\" to 0 for purposes of comparing snapshots.\n */\n if (blobName === metadataBlobName) {\n const metadata = JSON.parse(content);\n if (metadata.summaryNumber !== undefined) {\n metadata.summaryNumber = 0;\n }\n if (metadata.summaryCount !== undefined) {\n metadata.summaryCount = 0;\n }\n content = JSON.stringify(metadata);\n }\n\n // Deep sort the content if it's parseable.\n try {\n let contentObj = JSON.parse(content);\n if (Array.isArray(contentObj)) {\n contentObj = getDeepSortedArray(contentObj);\n } else if (contentObj instanceof Object) {\n contentObj = getDeepSortedObject(contentObj);\n }\n content = JSON.stringify(contentObj);\n } catch {}\n return content;\n}\n\n/**\n * Helper function that normalizes the given snapshot tree. It sorts objects and arrays in the snapshot. It also\n * normalizes certain blob contents for which the order of content does not matter. For example, garbage collection\n * blobs contains objects / arrays whose element order do not matter.\n * @param snapshot - The snapshot tree to normalize.\n * @param config - Configs to use when normalizing snapshot. For example, it can contain paths of blobs whose contents\n * should be normalized as well.\n * @returns a copy of the normalized snapshot tree.\n */\nexport function getNormalizedSnapshot(snapshot: ITree, config?: ISnapshotNormalizerConfig): ITree {\n // Merge blobs to normalize in the config with runtime blobs to normalize. The contents of these blobs will be\n // parsed and deep sorted.\n const normalizedEntries: ITreeEntry[] = [];\n\n // The metadata blob in the root of the summary tree needs to be normalized.\n const blobsToNormalize = [metadataBlobName, ...config?.blobsToNormalize ?? []];\n for (const entry of snapshot.entries) {\n normalizedEntries.push(normalizeEntry(entry, { ...config, blobsToNormalize }));\n }\n\n // Sort the tree entries based on their path.\n normalizedEntries.sort((a, b) => a.path.localeCompare(b.path));\n\n return {\n entries: normalizedEntries,\n id: snapshot.id,\n };\n}\n\nfunction normalizeEntry(\n entry: ITreeEntry,\n config: ISnapshotNormalizerConfig | undefined,\n): ITreeEntry {\n switch (entry.type) {\n case TreeEntry.Blob: {\n let contents = entry.value.contents;\n // If this blob has to be normalized or it's a GC blob, parse and sort the blob contents first.\n if (config?.blobsToNormalize?.includes(entry.path) || entry.path.startsWith(gcBlobPrefix)) {\n contents = getNormalizedBlobContent(contents, entry.path);\n }\n return new BlobTreeEntry(entry.path, contents);\n }\n case TreeEntry.Tree: {\n if (config?.excludedChannelContentTypes !== undefined) {\n for (const maybeAttributes of entry.value.entries) {\n if (maybeAttributes.type === TreeEntry.Blob && maybeAttributes.path === \".attributes\") {\n const parsed: { type?: string; } = JSON.parse(maybeAttributes.value.contents);\n if (parsed.type !== undefined && config.excludedChannelContentTypes.includes(parsed.type)) {\n // remove everything to match the unknown channel\n return new TreeTreeEntry(entry.path, { entries: [maybeAttributes] });\n }\n }\n }\n }\n\n return new TreeTreeEntry(entry.path, getNormalizedSnapshot(entry.value, config));\n }\n case TreeEntry.Attachment: {\n return new AttachmentTreeEntry(entry.path, (entry.value).id);\n }\n\n default:\n throw new Error(\"Unknown entry type\");\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"snapshotNormalizer.js","sourceRoot":"","sources":["../src/snapshotNormalizer.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { AttachmentTreeEntry, BlobTreeEntry, TreeTreeEntry } from \"@fluidframework/protocol-base\";\nimport {\n ITree,\n TreeEntry,\n ITreeEntry,\n} from \"@fluidframework/protocol-definitions\";\n\n/** The name of the metadata blob added to the root of the container runtime. */\nconst metadataBlobName = \".metadata\";\n/** The prefix that all GC blob names start with. */\nexport const gcBlobPrefix = \"__gc\";\n\nexport interface ISnapshotNormalizerConfig {\n // The paths of blobs whose contents should be normalized.\n blobsToNormalize?: string[];\n /**\n * channel types who's content (non-attribute) blobs will be excluded.\n * this is used to exclude the content of channels who's content cannot be compared\n * as the content is non-deterministic between snapshot at the same sequence number.\n */\n excludedChannelContentTypes?: string[];\n}\n\n/**\n * Function that deep sorts an array. It handles cases where array elements are objects or arrays.\n * @returns the sorted array.\n */\nfunction getDeepSortedArray(array: any[]): any[] {\n const sortedArray: any[] = [];\n // Sort arrays and objects, if any, in the array.\n for (const element of array) {\n if (Array.isArray(element)) {\n sortedArray.push(getDeepSortedArray(element));\n } else if (element instanceof Object) {\n sortedArray.push(getDeepSortedObject(element));\n } else {\n sortedArray.push(element);\n }\n }\n\n // Now that all the arrays and objects in this array's elements have been sorted, sort it by comparing each\n // element's stringified version.\n const sortFn = (elem1: any, elem2: any) => {\n const serializedElem1 = JSON.stringify(elem1);\n const serializedElem2 = JSON.stringify(elem2);\n return serializedElem1.localeCompare(serializedElem2);\n };\n\n return sortedArray.sort(sortFn);\n}\n\n/**\n * Function that deep sorts an object. It handles cases where object properties are arrays or objects.\n * @returns the sorted object.\n */\nfunction getDeepSortedObject(obj: any): any {\n const sortedObj: any = {};\n // Sort the object keys first. Then sort arrays and objects, if any, in the object.\n const keys = Object.keys(obj).sort();\n for (const key of keys) {\n const value = obj[key];\n if (Array.isArray(value)) {\n sortedObj[key] = getDeepSortedArray(value);\n } else if (value instanceof Object) {\n sortedObj[key] = getDeepSortedObject(value);\n } else {\n sortedObj[key] = value;\n }\n }\n\n return sortedObj;\n}\n\n/**\n * Function that normalizes a blob's content. If the content is an object or an array, deep sorts them.\n * Special handling for certain runtime blobs, such as the \"gc\" blob.\n * @returns the normalized blob content.\n */\nfunction getNormalizedBlobContent(blobContent: string, blobName: string): string {\n let content = blobContent;\n if (blobName.startsWith(gcBlobPrefix)) {\n // GC blobs may contain `unreferencedTimestampMs` for node that became unreferenced. This is the timestamp\n // of the last op processed or current timestamp and can differ between clients depending on when GC was run.\n // So, remove it for the purposes of comparing snapshots.\n const gcState = JSON.parse(content);\n for (const [, data] of Object.entries(gcState.gcNodes)) {\n delete (data as any).unreferencedTimestampMs;\n }\n content = JSON.stringify(gcState);\n }\n\n /**\n * The metadata blob has \"summaryNumber\" or \"summaryCount\" that tells which summary this is for a container. It can\n * be different in summaries of two clients even if they are generated at the same sequence#. For instance, at seq#\n * 1000, if one client has summarized 10 times and other has summarizer 15 times, summaryNumber will be different\n * for them. So, update \"summaryNumber\" to 0 for purposes of comparing snapshots.\n */\n if (blobName === metadataBlobName) {\n const metadata = JSON.parse(content);\n if (metadata.summaryNumber !== undefined) {\n metadata.summaryNumber = 0;\n }\n if (metadata.summaryCount !== undefined) {\n metadata.summaryCount = 0;\n }\n content = JSON.stringify(metadata);\n }\n\n // Deep sort the content if it's parseable.\n try {\n let contentObj = JSON.parse(content);\n if (Array.isArray(contentObj)) {\n contentObj = getDeepSortedArray(contentObj);\n } else if (contentObj instanceof Object) {\n contentObj = getDeepSortedObject(contentObj);\n }\n content = JSON.stringify(contentObj);\n } catch {}\n return content;\n}\n\n/**\n * Helper function that normalizes the given snapshot tree. It sorts objects and arrays in the snapshot. It also\n * normalizes certain blob contents for which the order of content does not matter. For example, garbage collection\n * blobs contains objects / arrays whose element order do not matter.\n * @param snapshot - The snapshot tree to normalize.\n * @param config - Configs to use when normalizing snapshot. For example, it can contain paths of blobs whose contents\n * should be normalized as well.\n * @returns a copy of the normalized snapshot tree.\n */\nexport function getNormalizedSnapshot(snapshot: ITree, config?: ISnapshotNormalizerConfig): ITree {\n // Merge blobs to normalize in the config with runtime blobs to normalize. The contents of these blobs will be\n // parsed and deep sorted.\n const normalizedEntries: ITreeEntry[] = [];\n\n // The metadata blob in the root of the summary tree needs to be normalized.\n const blobsToNormalize = [metadataBlobName, ...config?.blobsToNormalize ?? []];\n for (const entry of snapshot.entries) {\n normalizedEntries.push(normalizeEntry(entry, { ...config, blobsToNormalize }));\n }\n\n // Sort the tree entries based on their path.\n normalizedEntries.sort((a, b) => a.path.localeCompare(b.path));\n\n return {\n entries: normalizedEntries,\n id: snapshot.id,\n };\n}\n\nfunction normalizeMatrix(value: ITree): ITree {\n const rows = value.entries.find((e) => e.path === \"rows\");\n\n if (!rows || !(\"entries\" in rows.value)) {\n return value;\n }\n\n const segments = rows.value.entries.find((e) => e.path === \"segments\");\n\n if (!segments || !(\"entries\" in segments.value)) {\n return value;\n }\n\n const header = segments.value.entries.find((e) => e.path === \"header\");\n\n if (!header || !(\"contents\" in header.value)) {\n return value;\n }\n\n if (!header?.value.contents.includes(\"removedClientId\")) {\n return value;\n }\n\n const contents = JSON.parse(header?.value.contents);\n\n for (const segment of contents.segments) {\n if (\"removedClientId\" in segment) {\n segment.removedClientId = undefined;\n }\n\n if (\"removedClientIds\" in segment) {\n segment.removedClientIds = undefined;\n }\n }\n\n header.value.contents = JSON.stringify(contents);\n\n return value;\n}\n\nfunction normalizeEntry(\n entry: ITreeEntry,\n config: ISnapshotNormalizerConfig | undefined,\n): ITreeEntry {\n switch (entry.type) {\n case TreeEntry.Blob: {\n let contents = entry.value.contents;\n // If this blob has to be normalized or it's a GC blob, parse and sort the blob contents first.\n if (config?.blobsToNormalize?.includes(entry.path) || entry.path.startsWith(gcBlobPrefix)) {\n contents = getNormalizedBlobContent(contents, entry.path);\n }\n return new BlobTreeEntry(entry.path, contents);\n }\n case TreeEntry.Tree: {\n if (config?.excludedChannelContentTypes !== undefined) {\n for (const maybeAttributes of entry.value.entries) {\n if (maybeAttributes.type === TreeEntry.Blob && maybeAttributes.path === \".attributes\") {\n const parsed: { type?: string; } = JSON.parse(maybeAttributes.value.contents);\n if (parsed.type === \"https://graph.microsoft.com/types/sharedmatrix\") {\n return new TreeTreeEntry(\n entry.path,\n normalizeMatrix(getNormalizedSnapshot(entry.value, config)),\n );\n }\n if (parsed.type !== undefined && config.excludedChannelContentTypes.includes(parsed.type)) {\n // remove everything to match the unknown channel\n return new TreeTreeEntry(entry.path, { entries: [maybeAttributes] });\n }\n }\n }\n }\n\n return new TreeTreeEntry(entry.path, getNormalizedSnapshot(entry.value, config));\n }\n case TreeEntry.Attachment: {\n return new AttachmentTreeEntry(entry.path, (entry.value).id);\n }\n\n default:\n throw new Error(\"Unknown entry type\");\n }\n}\n"]}
|
package/lib/packageVersion.d.ts
CHANGED
|
@@ -5,5 +5,5 @@
|
|
|
5
5
|
* THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY
|
|
6
6
|
*/
|
|
7
7
|
export declare const pkgName = "@fluidframework/tool-utils";
|
|
8
|
-
export declare const pkgVersion = "1.
|
|
8
|
+
export declare const pkgVersion = "2.0.0-dev.1.3.0.96595";
|
|
9
9
|
//# sourceMappingURL=packageVersion.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"packageVersion.d.ts","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,eAAO,MAAM,OAAO,+BAA+B,CAAC;AACpD,eAAO,MAAM,UAAU,
|
|
1
|
+
{"version":3,"file":"packageVersion.d.ts","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,eAAO,MAAM,OAAO,+BAA+B,CAAC;AACpD,eAAO,MAAM,UAAU,0BAA0B,CAAC"}
|
package/lib/packageVersion.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"packageVersion.js","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,CAAC,MAAM,OAAO,GAAG,4BAA4B,CAAC;AACpD,MAAM,CAAC,MAAM,UAAU,GAAG,
|
|
1
|
+
{"version":3,"file":"packageVersion.js","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,CAAC,MAAM,OAAO,GAAG,4BAA4B,CAAC;AACpD,MAAM,CAAC,MAAM,UAAU,GAAG,uBAAuB,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n *\n * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY\n */\n\nexport const pkgName = \"@fluidframework/tool-utils\";\nexport const pkgVersion = \"2.0.0-dev.1.3.0.96595\";\n"]}
|
|
@@ -130,6 +130,34 @@ export function getNormalizedSnapshot(snapshot, config) {
|
|
|
130
130
|
id: snapshot.id,
|
|
131
131
|
};
|
|
132
132
|
}
|
|
133
|
+
function normalizeMatrix(value) {
|
|
134
|
+
const rows = value.entries.find((e) => e.path === "rows");
|
|
135
|
+
if (!rows || !("entries" in rows.value)) {
|
|
136
|
+
return value;
|
|
137
|
+
}
|
|
138
|
+
const segments = rows.value.entries.find((e) => e.path === "segments");
|
|
139
|
+
if (!segments || !("entries" in segments.value)) {
|
|
140
|
+
return value;
|
|
141
|
+
}
|
|
142
|
+
const header = segments.value.entries.find((e) => e.path === "header");
|
|
143
|
+
if (!header || !("contents" in header.value)) {
|
|
144
|
+
return value;
|
|
145
|
+
}
|
|
146
|
+
if (!(header === null || header === void 0 ? void 0 : header.value.contents.includes("removedClientId"))) {
|
|
147
|
+
return value;
|
|
148
|
+
}
|
|
149
|
+
const contents = JSON.parse(header === null || header === void 0 ? void 0 : header.value.contents);
|
|
150
|
+
for (const segment of contents.segments) {
|
|
151
|
+
if ("removedClientId" in segment) {
|
|
152
|
+
segment.removedClientId = undefined;
|
|
153
|
+
}
|
|
154
|
+
if ("removedClientIds" in segment) {
|
|
155
|
+
segment.removedClientIds = undefined;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
header.value.contents = JSON.stringify(contents);
|
|
159
|
+
return value;
|
|
160
|
+
}
|
|
133
161
|
function normalizeEntry(entry, config) {
|
|
134
162
|
var _a;
|
|
135
163
|
switch (entry.type) {
|
|
@@ -146,6 +174,9 @@ function normalizeEntry(entry, config) {
|
|
|
146
174
|
for (const maybeAttributes of entry.value.entries) {
|
|
147
175
|
if (maybeAttributes.type === TreeEntry.Blob && maybeAttributes.path === ".attributes") {
|
|
148
176
|
const parsed = JSON.parse(maybeAttributes.value.contents);
|
|
177
|
+
if (parsed.type === "https://graph.microsoft.com/types/sharedmatrix") {
|
|
178
|
+
return new TreeTreeEntry(entry.path, normalizeMatrix(getNormalizedSnapshot(entry.value, config)));
|
|
179
|
+
}
|
|
149
180
|
if (parsed.type !== undefined && config.excludedChannelContentTypes.includes(parsed.type)) {
|
|
150
181
|
// remove everything to match the unknown channel
|
|
151
182
|
return new TreeTreeEntry(entry.path, { entries: [maybeAttributes] });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"snapshotNormalizer.js","sourceRoot":"","sources":["../src/snapshotNormalizer.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { AttachmentTreeEntry, BlobTreeEntry, TreeTreeEntry } from \"@fluidframework/protocol-base\";\nimport {\n ITree,\n TreeEntry,\n ITreeEntry,\n} from \"@fluidframework/protocol-definitions\";\n\n/** The name of the metadata blob added to the root of the container runtime. */\nconst metadataBlobName = \".metadata\";\n/** The prefix that all GC blob names start with. */\nexport const gcBlobPrefix = \"__gc\";\n\nexport interface ISnapshotNormalizerConfig {\n // The paths of blobs whose contents should be normalized.\n blobsToNormalize?: string[];\n /**\n * channel types who's content (non-attribute) blobs will be excluded.\n * this is used to exclude the content of channels who's content cannot be compared\n * as the content is non-deterministic between snapshot at the same sequence number.\n */\n excludedChannelContentTypes?: string[];\n}\n\n/**\n * Function that deep sorts an array. It handles cases where array elements are objects or arrays.\n * @returns the sorted array.\n */\nfunction getDeepSortedArray(array: any[]): any[] {\n const sortedArray: any[] = [];\n // Sort arrays and objects, if any, in the array.\n for (const element of array) {\n if (Array.isArray(element)) {\n sortedArray.push(getDeepSortedArray(element));\n } else if (element instanceof Object) {\n sortedArray.push(getDeepSortedObject(element));\n } else {\n sortedArray.push(element);\n }\n }\n\n // Now that all the arrays and objects in this array's elements have been sorted, sort it by comparing each\n // element's stringified version.\n const sortFn = (elem1: any, elem2: any) => {\n const serializedElem1 = JSON.stringify(elem1);\n const serializedElem2 = JSON.stringify(elem2);\n return serializedElem1.localeCompare(serializedElem2);\n };\n\n return sortedArray.sort(sortFn);\n}\n\n/**\n * Function that deep sorts an object. It handles cases where object properties are arrays or objects.\n * @returns the sorted object.\n */\nfunction getDeepSortedObject(obj: any): any {\n const sortedObj: any = {};\n // Sort the object keys first. Then sort arrays and objects, if any, in the object.\n const keys = Object.keys(obj).sort();\n for (const key of keys) {\n const value = obj[key];\n if (Array.isArray(value)) {\n sortedObj[key] = getDeepSortedArray(value);\n } else if (value instanceof Object) {\n sortedObj[key] = getDeepSortedObject(value);\n } else {\n sortedObj[key] = value;\n }\n }\n\n return sortedObj;\n}\n\n/**\n * Function that normalizes a blob's content. If the content is an object or an array, deep sorts them.\n * Special handling for certain runtime blobs, such as the \"gc\" blob.\n * @returns the normalized blob content.\n */\nfunction getNormalizedBlobContent(blobContent: string, blobName: string): string {\n let content = blobContent;\n if (blobName.startsWith(gcBlobPrefix)) {\n // GC blobs may contain `unreferencedTimestampMs` for node that became unreferenced. This is the timestamp\n // of the last op processed or current timestamp and can differ between clients depending on when GC was run.\n // So, remove it for the purposes of comparing snapshots.\n const gcState = JSON.parse(content);\n for (const [, data] of Object.entries(gcState.gcNodes)) {\n delete (data as any).unreferencedTimestampMs;\n }\n content = JSON.stringify(gcState);\n }\n\n /**\n * The metadata blob has \"summaryNumber\" or \"summaryCount\" that tells which summary this is for a container. It can\n * be different in summaries of two clients even if they are generated at the same sequence#. For instance, at seq#\n * 1000, if one client has summarized 10 times and other has summarizer 15 times, summaryNumber will be different\n * for them. So, update \"summaryNumber\" to 0 for purposes of comparing snapshots.\n */\n if (blobName === metadataBlobName) {\n const metadata = JSON.parse(content);\n if (metadata.summaryNumber !== undefined) {\n metadata.summaryNumber = 0;\n }\n if (metadata.summaryCount !== undefined) {\n metadata.summaryCount = 0;\n }\n content = JSON.stringify(metadata);\n }\n\n // Deep sort the content if it's parseable.\n try {\n let contentObj = JSON.parse(content);\n if (Array.isArray(contentObj)) {\n contentObj = getDeepSortedArray(contentObj);\n } else if (contentObj instanceof Object) {\n contentObj = getDeepSortedObject(contentObj);\n }\n content = JSON.stringify(contentObj);\n } catch {}\n return content;\n}\n\n/**\n * Helper function that normalizes the given snapshot tree. It sorts objects and arrays in the snapshot. It also\n * normalizes certain blob contents for which the order of content does not matter. For example, garbage collection\n * blobs contains objects / arrays whose element order do not matter.\n * @param snapshot - The snapshot tree to normalize.\n * @param config - Configs to use when normalizing snapshot. For example, it can contain paths of blobs whose contents\n * should be normalized as well.\n * @returns a copy of the normalized snapshot tree.\n */\nexport function getNormalizedSnapshot(snapshot: ITree, config?: ISnapshotNormalizerConfig): ITree {\n // Merge blobs to normalize in the config with runtime blobs to normalize. The contents of these blobs will be\n // parsed and deep sorted.\n const normalizedEntries: ITreeEntry[] = [];\n\n // The metadata blob in the root of the summary tree needs to be normalized.\n const blobsToNormalize = [metadataBlobName, ...config?.blobsToNormalize ?? []];\n for (const entry of snapshot.entries) {\n normalizedEntries.push(normalizeEntry(entry, { ...config, blobsToNormalize }));\n }\n\n // Sort the tree entries based on their path.\n normalizedEntries.sort((a, b) => a.path.localeCompare(b.path));\n\n return {\n entries: normalizedEntries,\n id: snapshot.id,\n };\n}\n\nfunction normalizeEntry(\n entry: ITreeEntry,\n config: ISnapshotNormalizerConfig | undefined,\n): ITreeEntry {\n switch (entry.type) {\n case TreeEntry.Blob: {\n let contents = entry.value.contents;\n // If this blob has to be normalized or it's a GC blob, parse and sort the blob contents first.\n if (config?.blobsToNormalize?.includes(entry.path) || entry.path.startsWith(gcBlobPrefix)) {\n contents = getNormalizedBlobContent(contents, entry.path);\n }\n return new BlobTreeEntry(entry.path, contents);\n }\n case TreeEntry.Tree: {\n if (config?.excludedChannelContentTypes !== undefined) {\n for (const maybeAttributes of entry.value.entries) {\n if (maybeAttributes.type === TreeEntry.Blob && maybeAttributes.path === \".attributes\") {\n const parsed: { type?: string; } = JSON.parse(maybeAttributes.value.contents);\n if (parsed.type !== undefined && config.excludedChannelContentTypes.includes(parsed.type)) {\n // remove everything to match the unknown channel\n return new TreeTreeEntry(entry.path, { entries: [maybeAttributes] });\n }\n }\n }\n }\n\n return new TreeTreeEntry(entry.path, getNormalizedSnapshot(entry.value, config));\n }\n case TreeEntry.Attachment: {\n return new AttachmentTreeEntry(entry.path, (entry.value).id);\n }\n\n default:\n throw new Error(\"Unknown entry type\");\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"snapshotNormalizer.js","sourceRoot":"","sources":["../src/snapshotNormalizer.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { AttachmentTreeEntry, BlobTreeEntry, TreeTreeEntry } from \"@fluidframework/protocol-base\";\nimport {\n ITree,\n TreeEntry,\n ITreeEntry,\n} from \"@fluidframework/protocol-definitions\";\n\n/** The name of the metadata blob added to the root of the container runtime. */\nconst metadataBlobName = \".metadata\";\n/** The prefix that all GC blob names start with. */\nexport const gcBlobPrefix = \"__gc\";\n\nexport interface ISnapshotNormalizerConfig {\n // The paths of blobs whose contents should be normalized.\n blobsToNormalize?: string[];\n /**\n * channel types who's content (non-attribute) blobs will be excluded.\n * this is used to exclude the content of channels who's content cannot be compared\n * as the content is non-deterministic between snapshot at the same sequence number.\n */\n excludedChannelContentTypes?: string[];\n}\n\n/**\n * Function that deep sorts an array. It handles cases where array elements are objects or arrays.\n * @returns the sorted array.\n */\nfunction getDeepSortedArray(array: any[]): any[] {\n const sortedArray: any[] = [];\n // Sort arrays and objects, if any, in the array.\n for (const element of array) {\n if (Array.isArray(element)) {\n sortedArray.push(getDeepSortedArray(element));\n } else if (element instanceof Object) {\n sortedArray.push(getDeepSortedObject(element));\n } else {\n sortedArray.push(element);\n }\n }\n\n // Now that all the arrays and objects in this array's elements have been sorted, sort it by comparing each\n // element's stringified version.\n const sortFn = (elem1: any, elem2: any) => {\n const serializedElem1 = JSON.stringify(elem1);\n const serializedElem2 = JSON.stringify(elem2);\n return serializedElem1.localeCompare(serializedElem2);\n };\n\n return sortedArray.sort(sortFn);\n}\n\n/**\n * Function that deep sorts an object. It handles cases where object properties are arrays or objects.\n * @returns the sorted object.\n */\nfunction getDeepSortedObject(obj: any): any {\n const sortedObj: any = {};\n // Sort the object keys first. Then sort arrays and objects, if any, in the object.\n const keys = Object.keys(obj).sort();\n for (const key of keys) {\n const value = obj[key];\n if (Array.isArray(value)) {\n sortedObj[key] = getDeepSortedArray(value);\n } else if (value instanceof Object) {\n sortedObj[key] = getDeepSortedObject(value);\n } else {\n sortedObj[key] = value;\n }\n }\n\n return sortedObj;\n}\n\n/**\n * Function that normalizes a blob's content. If the content is an object or an array, deep sorts them.\n * Special handling for certain runtime blobs, such as the \"gc\" blob.\n * @returns the normalized blob content.\n */\nfunction getNormalizedBlobContent(blobContent: string, blobName: string): string {\n let content = blobContent;\n if (blobName.startsWith(gcBlobPrefix)) {\n // GC blobs may contain `unreferencedTimestampMs` for node that became unreferenced. This is the timestamp\n // of the last op processed or current timestamp and can differ between clients depending on when GC was run.\n // So, remove it for the purposes of comparing snapshots.\n const gcState = JSON.parse(content);\n for (const [, data] of Object.entries(gcState.gcNodes)) {\n delete (data as any).unreferencedTimestampMs;\n }\n content = JSON.stringify(gcState);\n }\n\n /**\n * The metadata blob has \"summaryNumber\" or \"summaryCount\" that tells which summary this is for a container. It can\n * be different in summaries of two clients even if they are generated at the same sequence#. For instance, at seq#\n * 1000, if one client has summarized 10 times and other has summarizer 15 times, summaryNumber will be different\n * for them. So, update \"summaryNumber\" to 0 for purposes of comparing snapshots.\n */\n if (blobName === metadataBlobName) {\n const metadata = JSON.parse(content);\n if (metadata.summaryNumber !== undefined) {\n metadata.summaryNumber = 0;\n }\n if (metadata.summaryCount !== undefined) {\n metadata.summaryCount = 0;\n }\n content = JSON.stringify(metadata);\n }\n\n // Deep sort the content if it's parseable.\n try {\n let contentObj = JSON.parse(content);\n if (Array.isArray(contentObj)) {\n contentObj = getDeepSortedArray(contentObj);\n } else if (contentObj instanceof Object) {\n contentObj = getDeepSortedObject(contentObj);\n }\n content = JSON.stringify(contentObj);\n } catch {}\n return content;\n}\n\n/**\n * Helper function that normalizes the given snapshot tree. It sorts objects and arrays in the snapshot. It also\n * normalizes certain blob contents for which the order of content does not matter. For example, garbage collection\n * blobs contains objects / arrays whose element order do not matter.\n * @param snapshot - The snapshot tree to normalize.\n * @param config - Configs to use when normalizing snapshot. For example, it can contain paths of blobs whose contents\n * should be normalized as well.\n * @returns a copy of the normalized snapshot tree.\n */\nexport function getNormalizedSnapshot(snapshot: ITree, config?: ISnapshotNormalizerConfig): ITree {\n // Merge blobs to normalize in the config with runtime blobs to normalize. The contents of these blobs will be\n // parsed and deep sorted.\n const normalizedEntries: ITreeEntry[] = [];\n\n // The metadata blob in the root of the summary tree needs to be normalized.\n const blobsToNormalize = [metadataBlobName, ...config?.blobsToNormalize ?? []];\n for (const entry of snapshot.entries) {\n normalizedEntries.push(normalizeEntry(entry, { ...config, blobsToNormalize }));\n }\n\n // Sort the tree entries based on their path.\n normalizedEntries.sort((a, b) => a.path.localeCompare(b.path));\n\n return {\n entries: normalizedEntries,\n id: snapshot.id,\n };\n}\n\nfunction normalizeMatrix(value: ITree): ITree {\n const rows = value.entries.find((e) => e.path === \"rows\");\n\n if (!rows || !(\"entries\" in rows.value)) {\n return value;\n }\n\n const segments = rows.value.entries.find((e) => e.path === \"segments\");\n\n if (!segments || !(\"entries\" in segments.value)) {\n return value;\n }\n\n const header = segments.value.entries.find((e) => e.path === \"header\");\n\n if (!header || !(\"contents\" in header.value)) {\n return value;\n }\n\n if (!header?.value.contents.includes(\"removedClientId\")) {\n return value;\n }\n\n const contents = JSON.parse(header?.value.contents);\n\n for (const segment of contents.segments) {\n if (\"removedClientId\" in segment) {\n segment.removedClientId = undefined;\n }\n\n if (\"removedClientIds\" in segment) {\n segment.removedClientIds = undefined;\n }\n }\n\n header.value.contents = JSON.stringify(contents);\n\n return value;\n}\n\nfunction normalizeEntry(\n entry: ITreeEntry,\n config: ISnapshotNormalizerConfig | undefined,\n): ITreeEntry {\n switch (entry.type) {\n case TreeEntry.Blob: {\n let contents = entry.value.contents;\n // If this blob has to be normalized or it's a GC blob, parse and sort the blob contents first.\n if (config?.blobsToNormalize?.includes(entry.path) || entry.path.startsWith(gcBlobPrefix)) {\n contents = getNormalizedBlobContent(contents, entry.path);\n }\n return new BlobTreeEntry(entry.path, contents);\n }\n case TreeEntry.Tree: {\n if (config?.excludedChannelContentTypes !== undefined) {\n for (const maybeAttributes of entry.value.entries) {\n if (maybeAttributes.type === TreeEntry.Blob && maybeAttributes.path === \".attributes\") {\n const parsed: { type?: string; } = JSON.parse(maybeAttributes.value.contents);\n if (parsed.type === \"https://graph.microsoft.com/types/sharedmatrix\") {\n return new TreeTreeEntry(\n entry.path,\n normalizeMatrix(getNormalizedSnapshot(entry.value, config)),\n );\n }\n if (parsed.type !== undefined && config.excludedChannelContentTypes.includes(parsed.type)) {\n // remove everything to match the unknown channel\n return new TreeTreeEntry(entry.path, { entries: [maybeAttributes] });\n }\n }\n }\n }\n\n return new TreeTreeEntry(entry.path, getNormalizedSnapshot(entry.value, config));\n }\n case TreeEntry.Attachment: {\n return new AttachmentTreeEntry(entry.path, (entry.value).id);\n }\n\n default:\n throw new Error(\"Unknown entry type\");\n }\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fluidframework/tool-utils",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "2.0.0-dev.1.3.0.96595",
|
|
4
4
|
"description": "Common utilities for Fluid tools",
|
|
5
5
|
"homepage": "https://fluidframework.com",
|
|
6
6
|
"repository": {
|
|
@@ -61,21 +61,21 @@
|
|
|
61
61
|
"temp-directory": "nyc/.nyc_output"
|
|
62
62
|
},
|
|
63
63
|
"dependencies": {
|
|
64
|
-
"@fluidframework/common-utils": "^0.
|
|
65
|
-
"@fluidframework/odsp-doclib-utils": "
|
|
66
|
-
"@fluidframework/protocol-base": "^0.
|
|
67
|
-
"@fluidframework/protocol-definitions": "^0.
|
|
64
|
+
"@fluidframework/common-utils": "^1.0.0",
|
|
65
|
+
"@fluidframework/odsp-doclib-utils": "2.0.0-dev.1.3.0.96595",
|
|
66
|
+
"@fluidframework/protocol-base": "^0.1037.2001",
|
|
67
|
+
"@fluidframework/protocol-definitions": "^1.0.0",
|
|
68
68
|
"async-mutex": "^0.3.1",
|
|
69
69
|
"debug": "^4.1.1",
|
|
70
70
|
"jwt-decode": "^2.2.0",
|
|
71
71
|
"proper-lockfile": "^4.1.2"
|
|
72
72
|
},
|
|
73
73
|
"devDependencies": {
|
|
74
|
-
"@fluidframework/build-common": "^0.
|
|
75
|
-
"@fluidframework/build-tools": "^0.
|
|
76
|
-
"@fluidframework/eslint-config-fluid": "^0.
|
|
77
|
-
"@fluidframework/mocha-test-setup": "
|
|
78
|
-
"@fluidframework/tool-utils-previous": "npm:@fluidframework/tool-utils
|
|
74
|
+
"@fluidframework/build-common": "^1.0.0",
|
|
75
|
+
"@fluidframework/build-tools": "^0.4.6000",
|
|
76
|
+
"@fluidframework/eslint-config-fluid": "^1.0.0",
|
|
77
|
+
"@fluidframework/mocha-test-setup": "2.0.0-dev.1.3.0.96595",
|
|
78
|
+
"@fluidframework/tool-utils-previous": "npm:@fluidframework/tool-utils@^1.0.0",
|
|
79
79
|
"@microsoft/api-extractor": "^7.22.2",
|
|
80
80
|
"@rushstack/eslint-config": "^2.5.1",
|
|
81
81
|
"@types/debug": "^4.1.5",
|
|
@@ -83,7 +83,7 @@
|
|
|
83
83
|
"@types/mocha": "^9.1.1",
|
|
84
84
|
"@types/node": "^14.18.0",
|
|
85
85
|
"concurrently": "^6.2.0",
|
|
86
|
-
"copyfiles": "^2.1
|
|
86
|
+
"copyfiles": "^2.4.1",
|
|
87
87
|
"cross-env": "^7.0.2",
|
|
88
88
|
"eslint": "~8.6.0",
|
|
89
89
|
"mocha": "^10.0.0",
|
|
@@ -93,7 +93,7 @@
|
|
|
93
93
|
"typescript-formatter": "7.1.0"
|
|
94
94
|
},
|
|
95
95
|
"typeValidation": {
|
|
96
|
-
"version": "
|
|
96
|
+
"version": "2.0.0",
|
|
97
97
|
"broken": {}
|
|
98
98
|
}
|
|
99
99
|
}
|
package/src/packageVersion.ts
CHANGED
|
@@ -153,6 +153,46 @@ export function getNormalizedSnapshot(snapshot: ITree, config?: ISnapshotNormali
|
|
|
153
153
|
};
|
|
154
154
|
}
|
|
155
155
|
|
|
156
|
+
function normalizeMatrix(value: ITree): ITree {
|
|
157
|
+
const rows = value.entries.find((e) => e.path === "rows");
|
|
158
|
+
|
|
159
|
+
if (!rows || !("entries" in rows.value)) {
|
|
160
|
+
return value;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const segments = rows.value.entries.find((e) => e.path === "segments");
|
|
164
|
+
|
|
165
|
+
if (!segments || !("entries" in segments.value)) {
|
|
166
|
+
return value;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const header = segments.value.entries.find((e) => e.path === "header");
|
|
170
|
+
|
|
171
|
+
if (!header || !("contents" in header.value)) {
|
|
172
|
+
return value;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (!header?.value.contents.includes("removedClientId")) {
|
|
176
|
+
return value;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const contents = JSON.parse(header?.value.contents);
|
|
180
|
+
|
|
181
|
+
for (const segment of contents.segments) {
|
|
182
|
+
if ("removedClientId" in segment) {
|
|
183
|
+
segment.removedClientId = undefined;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if ("removedClientIds" in segment) {
|
|
187
|
+
segment.removedClientIds = undefined;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
header.value.contents = JSON.stringify(contents);
|
|
192
|
+
|
|
193
|
+
return value;
|
|
194
|
+
}
|
|
195
|
+
|
|
156
196
|
function normalizeEntry(
|
|
157
197
|
entry: ITreeEntry,
|
|
158
198
|
config: ISnapshotNormalizerConfig | undefined,
|
|
@@ -171,6 +211,12 @@ function normalizeEntry(
|
|
|
171
211
|
for (const maybeAttributes of entry.value.entries) {
|
|
172
212
|
if (maybeAttributes.type === TreeEntry.Blob && maybeAttributes.path === ".attributes") {
|
|
173
213
|
const parsed: { type?: string; } = JSON.parse(maybeAttributes.value.contents);
|
|
214
|
+
if (parsed.type === "https://graph.microsoft.com/types/sharedmatrix") {
|
|
215
|
+
return new TreeTreeEntry(
|
|
216
|
+
entry.path,
|
|
217
|
+
normalizeMatrix(getNormalizedSnapshot(entry.value, config)),
|
|
218
|
+
);
|
|
219
|
+
}
|
|
174
220
|
if (parsed.type !== undefined && config.excludedChannelContentTypes.includes(parsed.type)) {
|
|
175
221
|
// remove everything to match the unknown channel
|
|
176
222
|
return new TreeTreeEntry(entry.path, { entries: [maybeAttributes] });
|