@fluidframework/tool-utils 0.59.2000 → 0.59.3000-67119
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/dist/debug.js +2 -2
- package/dist/debug.js.map +1 -1
- package/dist/fluidToolRC.d.ts.map +1 -1
- package/dist/fluidToolRC.js +1 -1
- package/dist/fluidToolRC.js.map +1 -1
- package/dist/odspTokenManager.js +20 -20
- package/dist/odspTokenManager.js.map +1 -1
- 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.d.ts +1 -0
- package/dist/snapshotNormalizer.d.ts.map +1 -1
- package/dist/snapshotNormalizer.js +23 -1
- package/dist/snapshotNormalizer.js.map +1 -1
- package/lib/fluidToolRC.d.ts.map +1 -1
- package/lib/fluidToolRC.js.map +1 -1
- package/lib/odspTokenManager.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.d.ts +1 -0
- package/lib/snapshotNormalizer.d.ts.map +1 -1
- package/lib/snapshotNormalizer.js +23 -1
- package/lib/snapshotNormalizer.js.map +1 -1
- package/package.json +9 -8
- package/src/fluidToolRC.ts +6 -6
- package/src/packageVersion.ts +1 -1
- package/src/snapshotNormalizer.ts +29 -7
package/dist/debug.js
CHANGED
|
@@ -10,6 +10,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
10
10
|
exports.debug = void 0;
|
|
11
11
|
const debug_1 = __importDefault(require("debug"));
|
|
12
12
|
const packageVersion_1 = require("./packageVersion");
|
|
13
|
-
exports.debug = debug_1.default("fluid:tool-utils");
|
|
14
|
-
exports.debug(`Package: ${packageVersion_1.pkgName} - Version: ${packageVersion_1.pkgVersion}`);
|
|
13
|
+
exports.debug = (0, debug_1.default)("fluid:tool-utils");
|
|
14
|
+
(0, exports.debug)(`Package: ${packageVersion_1.pkgName} - Version: ${packageVersion_1.pkgVersion}`);
|
|
15
15
|
//# sourceMappingURL=debug.js.map
|
package/dist/debug.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"debug.js","sourceRoot":"","sources":["../src/debug.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;;;;AAEH,kDAAkC;AAClC,qDAAuD;AAE1C,QAAA,KAAK,GAAG,eAAa,
|
|
1
|
+
{"version":3,"file":"debug.js","sourceRoot":"","sources":["../src/debug.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;;;;AAEH,kDAAkC;AAClC,qDAAuD;AAE1C,QAAA,KAAK,GAAG,IAAA,eAAa,EAAC,kBAAkB,CAAC,CAAC;AACvD,IAAA,aAAK,EAAC,YAAY,wBAAO,eAAe,2BAAU,EAAE,CAAC,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport registerDebug from \"debug\";\nimport { pkgName, pkgVersion } from \"./packageVersion\";\n\nexport const debug = registerDebug(\"fluid:tool-utils\");\ndebug(`Package: ${pkgName} - Version: ${pkgVersion}`);\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fluidToolRC.d.ts","sourceRoot":"","sources":["../src/fluidToolRC.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAOH,OAAO,EAAE,WAAW,EAAE,MAAM,mCAAmC,CAAC;AAEhE,MAAM,WAAW,WAAW,CAAC,IAAI,EAAE,MAAM;IACrC,GAAG,CAAC,GAAG,EAAE,IAAI,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IAC5C,IAAI,CAAC,GAAG,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9C,IAAI,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;CACnD;AAED,MAAM,WAAW,UAAU;IACvB,MAAM,CAAC,EAAE;QACL,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,IAAI,EAAE;YACF,CAAC,GAAG,EAAE,MAAM,GAAG;gBACX,OAAO,CAAC,EAAE,WAAW,CAAC;gBACtB,IAAI,CAAC,EAAE,WAAW,
|
|
1
|
+
{"version":3,"file":"fluidToolRC.d.ts","sourceRoot":"","sources":["../src/fluidToolRC.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAOH,OAAO,EAAE,WAAW,EAAE,MAAM,mCAAmC,CAAC;AAEhE,MAAM,WAAW,WAAW,CAAC,IAAI,EAAE,MAAM;IACrC,GAAG,CAAC,GAAG,EAAE,IAAI,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IAC5C,IAAI,CAAC,GAAG,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9C,IAAI,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;CACnD;AAED,MAAM,WAAW,UAAU;IACvB,MAAM,CAAC,EAAE;QACL,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,IAAI,EAAE;YACF,CAAC,GAAG,EAAE,MAAM,GAAG;gBACX,OAAO,CAAC,EAAE,WAAW,CAAC;gBACtB,IAAI,CAAC,EAAE,WAAW,CAAC;aACtB,CAAC;SACL,CAAC;KACL,CAAC;CACL;AAID,wBAAsB,MAAM,IAAI,OAAO,CAAC,UAAU,CAAC,CAclD;AAED,wBAAsB,MAAM,CAAC,EAAE,EAAE,UAAU,iBAI1C;AAED,wBAAsB,MAAM,iBAS3B"}
|
package/dist/fluidToolRC.js
CHANGED
|
@@ -39,7 +39,7 @@ async function saveRC(rc) {
|
|
|
39
39
|
exports.saveRC = saveRC;
|
|
40
40
|
async function lockRC() {
|
|
41
41
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
42
|
-
return proper_lockfile_1.lock(getRCFileName(), {
|
|
42
|
+
return (0, proper_lockfile_1.lock)(getRCFileName(), {
|
|
43
43
|
retries: {
|
|
44
44
|
forever: true,
|
|
45
45
|
},
|
package/dist/fluidToolRC.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fluidToolRC.js","sourceRoot":"","sources":["../src/fluidToolRC.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;;;;AAEH,4CAAoB;AACpB,4CAAoB;AACpB,gDAAwB;AACxB,gDAAwB;AACxB,qDAAuC;AAqBvC,MAAM,aAAa,GAAG,GAAG,EAAE,CAAC,cAAI,CAAC,IAAI,CAAC,YAAE,CAAC,OAAO,EAAE,EAAE,cAAc,CAAC,CAAC;AAE7D,KAAK,UAAU,MAAM;IACxB,MAAM,QAAQ,GAAG,cAAI,CAAC,SAAS,CAAC,YAAE,CAAC,QAAQ,CAAC,CAAC;IAC7C,MAAM,MAAM,GAAG,cAAI,CAAC,SAAS,CAAC,YAAE,CAAC,MAAM,CAAC,CAAC;IACzC,MAAM,QAAQ,GAAG,aAAa,EAAE,CAAC;IACjC,IAAI,MAAM,MAAM,CAAC,QAAQ,CAAC,EAAE;QACxB,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACrC,IAAI;YACA,+DAA+D;YAC/D,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;SAC3C;QAAC,OAAO,CAAC,EAAE;YACR,UAAU;SACb;KACJ;IACD,OAAO,EAAE,CAAC;AACd,CAAC;AAdD,wBAcC;AAEM,KAAK,UAAU,MAAM,CAAC,EAAc;IACvC,MAAM,SAAS,GAAG,cAAI,CAAC,SAAS,CAAC,YAAE,CAAC,SAAS,CAAC,CAAC;IAC/C,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC;IACjD,OAAO,SAAS,CAAC,aAAa,EAAE,EAAE,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC;AACpE,CAAC;AAJD,wBAIC;AAEM,KAAK,UAAU,MAAM;IACxB,+DAA+D;IAC/D,OAAO,sBAAI,
|
|
1
|
+
{"version":3,"file":"fluidToolRC.js","sourceRoot":"","sources":["../src/fluidToolRC.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;;;;AAEH,4CAAoB;AACpB,4CAAoB;AACpB,gDAAwB;AACxB,gDAAwB;AACxB,qDAAuC;AAqBvC,MAAM,aAAa,GAAG,GAAG,EAAE,CAAC,cAAI,CAAC,IAAI,CAAC,YAAE,CAAC,OAAO,EAAE,EAAE,cAAc,CAAC,CAAC;AAE7D,KAAK,UAAU,MAAM;IACxB,MAAM,QAAQ,GAAG,cAAI,CAAC,SAAS,CAAC,YAAE,CAAC,QAAQ,CAAC,CAAC;IAC7C,MAAM,MAAM,GAAG,cAAI,CAAC,SAAS,CAAC,YAAE,CAAC,MAAM,CAAC,CAAC;IACzC,MAAM,QAAQ,GAAG,aAAa,EAAE,CAAC;IACjC,IAAI,MAAM,MAAM,CAAC,QAAQ,CAAC,EAAE;QACxB,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACrC,IAAI;YACA,+DAA+D;YAC/D,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;SAC3C;QAAC,OAAO,CAAC,EAAE;YACR,UAAU;SACb;KACJ;IACD,OAAO,EAAE,CAAC;AACd,CAAC;AAdD,wBAcC;AAEM,KAAK,UAAU,MAAM,CAAC,EAAc;IACvC,MAAM,SAAS,GAAG,cAAI,CAAC,SAAS,CAAC,YAAE,CAAC,SAAS,CAAC,CAAC;IAC/C,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC;IACjD,OAAO,SAAS,CAAC,aAAa,EAAE,EAAE,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC;AACpE,CAAC;AAJD,wBAIC;AAEM,KAAK,UAAU,MAAM;IACxB,+DAA+D;IAC/D,OAAO,IAAA,sBAAI,EAAC,aAAa,EAAE,EAAE;QACzB,OAAO,EAAE;YACL,OAAO,EAAE,IAAI;SAChB;QACD,KAAK,EAAE,KAAK;QACZ,QAAQ,EAAE,KAAK;KAClB,CAAC,CAAC;AACP,CAAC;AATD,wBASC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport fs from \"fs\";\nimport os from \"os\";\nimport path from \"path\";\nimport util from \"util\";\nimport { lock } from \"proper-lockfile\";\nimport { IOdspTokens } from \"@fluidframework/odsp-doclib-utils\";\n\nexport interface IAsyncCache<TKey, TValue> {\n get(key: TKey): Promise<TValue | undefined>;\n save(key: TKey, value: TValue): Promise<void>;\n lock<T>(callback: () => Promise<T>): Promise<T>;\n}\n\nexport interface IResources {\n tokens?: {\n version?: number;\n data: {\n [key: string]: {\n storage?: IOdspTokens;\n push?: IOdspTokens;\n };\n };\n };\n}\n\nconst getRCFileName = () => path.join(os.homedir(), \".fluidtoolrc\");\n\nexport async function loadRC(): Promise<IResources> {\n const readFile = util.promisify(fs.readFile);\n const exists = util.promisify(fs.exists);\n const fileName = getRCFileName();\n if (await exists(fileName)) {\n const buf = await readFile(fileName);\n try {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return JSON.parse(buf.toString(\"utf8\"));\n } catch (e) {\n // Nothing\n }\n }\n return {};\n}\n\nexport async function saveRC(rc: IResources) {\n const writeFile = util.promisify(fs.writeFile);\n const content = JSON.stringify(rc, undefined, 2);\n return writeFile(getRCFileName(), Buffer.from(content, \"utf8\"));\n}\n\nexport async function lockRC() {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return lock(getRCFileName(), {\n retries: {\n forever: true,\n },\n stale: 60000,\n realpath: false,\n });\n}\n"]}
|
package/dist/odspTokenManager.js
CHANGED
|
@@ -38,7 +38,7 @@ const isValidToken = (token) => {
|
|
|
38
38
|
if (!token || token.length === 0) {
|
|
39
39
|
return false;
|
|
40
40
|
}
|
|
41
|
-
const decodedToken = jwt_decode_1.default(token);
|
|
41
|
+
const decodedToken = (0, jwt_decode_1.default)(token);
|
|
42
42
|
// Give it a 60s buffer
|
|
43
43
|
return (decodedToken.exp - 60 >= (new Date().getTime() / 1000));
|
|
44
44
|
};
|
|
@@ -59,7 +59,7 @@ class OdspTokenManager {
|
|
|
59
59
|
}
|
|
60
60
|
async updateTokensCacheWithoutLock(key, value) {
|
|
61
61
|
var _a;
|
|
62
|
-
debug_1.debug(`${cacheKeyToString(key)}: Saving tokens`);
|
|
62
|
+
(0, debug_1.debug)(`${cacheKeyToString(key)}: Saving tokens`);
|
|
63
63
|
const memoryCache = key.isPush ? this.pushCache : this.storageCache;
|
|
64
64
|
memoryCache.set(key.userOrServer, value);
|
|
65
65
|
await ((_a = this.tokenCache) === null || _a === void 0 ? void 0 : _a.save(key, value));
|
|
@@ -75,12 +75,12 @@ class OdspTokenManager {
|
|
|
75
75
|
const memoryCache = cacheKey.isPush ? this.pushCache : this.storageCache;
|
|
76
76
|
const memoryToken = memoryCache.get(cacheKey.userOrServer);
|
|
77
77
|
if (memoryToken) {
|
|
78
|
-
debug_1.debug(`${cacheKeyToString(cacheKey)}: Token found in memory `);
|
|
78
|
+
(0, debug_1.debug)(`${cacheKeyToString(cacheKey)}: Token found in memory `);
|
|
79
79
|
return memoryToken;
|
|
80
80
|
}
|
|
81
81
|
const fileToken = await ((_a = this.tokenCache) === null || _a === void 0 ? void 0 : _a.get(cacheKey));
|
|
82
82
|
if (fileToken) {
|
|
83
|
-
debug_1.debug(`${cacheKeyToString(cacheKey)}: Token found in file`);
|
|
83
|
+
(0, debug_1.debug)(`${cacheKeyToString(cacheKey)}: Token found in file`);
|
|
84
84
|
memoryCache.set(cacheKey.userOrServer, fileToken);
|
|
85
85
|
return fileToken;
|
|
86
86
|
}
|
|
@@ -103,11 +103,11 @@ class OdspTokenManager {
|
|
|
103
103
|
const tokensFromCache = await this.getTokenFromCache(cacheKey);
|
|
104
104
|
if (tokensFromCache) {
|
|
105
105
|
if (isValidToken(tokensFromCache.accessToken)) {
|
|
106
|
-
debug_1.debug(`${cacheKeyToString(cacheKey)}: Token reused from cache `);
|
|
106
|
+
(0, debug_1.debug)(`${cacheKeyToString(cacheKey)}: Token reused from cache `);
|
|
107
107
|
await this.onTokenRetrievalFromCache(tokenConfig, tokensFromCache);
|
|
108
108
|
return tokensFromCache;
|
|
109
109
|
}
|
|
110
|
-
debug_1.debug(`${cacheKeyToString(cacheKey)}: Token expired from cache `);
|
|
110
|
+
(0, debug_1.debug)(`${cacheKeyToString(cacheKey)}: Token expired from cache `);
|
|
111
111
|
}
|
|
112
112
|
}
|
|
113
113
|
if (this.tokenCache) {
|
|
@@ -117,7 +117,7 @@ class OdspTokenManager {
|
|
|
117
117
|
return invokeGetTokensCore();
|
|
118
118
|
}
|
|
119
119
|
async getTokensCore(isPush, server, clientConfig, tokenConfig, forceRefresh, forceReauth) {
|
|
120
|
-
const scope = isPush ? odsp_doclib_utils_1.pushScope : odsp_doclib_utils_1.getOdspScope(server);
|
|
120
|
+
const scope = isPush ? odsp_doclib_utils_1.pushScope : (0, odsp_doclib_utils_1.getOdspScope)(server);
|
|
121
121
|
const cacheKey = OdspTokenManager.getCacheKey(isPush, tokenConfig, server);
|
|
122
122
|
let tokens;
|
|
123
123
|
if (!forceReauth) {
|
|
@@ -127,16 +127,16 @@ class OdspTokenManager {
|
|
|
127
127
|
if (forceRefresh || !isValidToken(tokensFromCache.accessToken)) {
|
|
128
128
|
try {
|
|
129
129
|
// This updates the tokens in tokensFromCache
|
|
130
|
-
tokens = await odsp_doclib_utils_1.refreshTokens(server, scope, clientConfig, tokensFromCache);
|
|
130
|
+
tokens = await (0, odsp_doclib_utils_1.refreshTokens)(server, scope, clientConfig, tokensFromCache);
|
|
131
131
|
await this.updateTokensCacheWithoutLock(cacheKey, tokens);
|
|
132
132
|
}
|
|
133
133
|
catch (error) {
|
|
134
|
-
debug_1.debug(`${cacheKeyToString(cacheKey)}: Error in refreshing token. ${error}`);
|
|
134
|
+
(0, debug_1.debug)(`${cacheKeyToString(cacheKey)}: Error in refreshing token. ${error}`);
|
|
135
135
|
}
|
|
136
136
|
}
|
|
137
137
|
else {
|
|
138
138
|
tokens = tokensFromCache;
|
|
139
|
-
debug_1.debug(`${cacheKeyToString(cacheKey)}: Token reused from locked cache `);
|
|
139
|
+
(0, debug_1.debug)(`${cacheKeyToString(cacheKey)}: Token reused from locked cache `);
|
|
140
140
|
}
|
|
141
141
|
}
|
|
142
142
|
}
|
|
@@ -149,10 +149,10 @@ class OdspTokenManager {
|
|
|
149
149
|
tokens = await this.acquireTokensWithPassword(server, scope, clientConfig, tokenConfig.username, tokenConfig.password);
|
|
150
150
|
break;
|
|
151
151
|
case "browserLogin":
|
|
152
|
-
tokens = await this.acquireTokensViaBrowserLogin(odsp_doclib_utils_1.getLoginPageUrl(server, clientConfig, scope, odspAuthRedirectUri), server, clientConfig, scope, tokenConfig.navigator, tokenConfig.redirectUriCallback);
|
|
152
|
+
tokens = await this.acquireTokensViaBrowserLogin((0, odsp_doclib_utils_1.getLoginPageUrl)(server, clientConfig, scope, odspAuthRedirectUri), server, clientConfig, scope, tokenConfig.navigator, tokenConfig.redirectUriCallback);
|
|
153
153
|
break;
|
|
154
154
|
default:
|
|
155
|
-
common_utils_1.unreachableCase(tokenConfig);
|
|
155
|
+
(0, common_utils_1.unreachableCase)(tokenConfig);
|
|
156
156
|
}
|
|
157
157
|
await this.updateTokensCacheWithoutLock(cacheKey, tokens);
|
|
158
158
|
return tokens;
|
|
@@ -163,26 +163,26 @@ class OdspTokenManager {
|
|
|
163
163
|
username,
|
|
164
164
|
password,
|
|
165
165
|
};
|
|
166
|
-
return odsp_doclib_utils_1.fetchTokens(server, scope, clientConfig, credentials);
|
|
166
|
+
return (0, odsp_doclib_utils_1.fetchTokens)(server, scope, clientConfig, credentials);
|
|
167
167
|
}
|
|
168
168
|
async acquireTokensViaBrowserLogin(loginPageUrl, server, clientConfig, scope, navigator, redirectUriCallback) {
|
|
169
169
|
// Start up a local auth redirect handler service to receive the tokens after login
|
|
170
|
-
const tokenGetter = await httpHelpers_1.serverListenAndHandle(odspAuthRedirectPort, async (req, res) => {
|
|
170
|
+
const tokenGetter = await (0, httpHelpers_1.serverListenAndHandle)(odspAuthRedirectPort, async (req, res) => {
|
|
171
171
|
// extract code from request URL and fetch the tokens
|
|
172
172
|
const credentials = {
|
|
173
173
|
grant_type: "authorization_code",
|
|
174
174
|
code: this.extractAuthorizationCode(req.url),
|
|
175
175
|
redirect_uri: odspAuthRedirectUri,
|
|
176
176
|
};
|
|
177
|
-
const tokens = await odsp_doclib_utils_1.fetchTokens(server, scope, clientConfig, credentials);
|
|
177
|
+
const tokens = await (0, odsp_doclib_utils_1.fetchTokens)(server, scope, clientConfig, credentials);
|
|
178
178
|
// redirect now that the browser is done with auth
|
|
179
179
|
if (redirectUriCallback) {
|
|
180
180
|
res.writeHead(301, { Location: await redirectUriCallback(tokens) });
|
|
181
|
-
await httpHelpers_1.endResponse(res);
|
|
181
|
+
await (0, httpHelpers_1.endResponse)(res);
|
|
182
182
|
}
|
|
183
183
|
else {
|
|
184
184
|
res.write("Please close the window");
|
|
185
|
-
await httpHelpers_1.endResponse(res);
|
|
185
|
+
await (0, httpHelpers_1.endResponse)(res);
|
|
186
186
|
}
|
|
187
187
|
return tokens;
|
|
188
188
|
});
|
|
@@ -211,7 +211,7 @@ class OdspTokenManager {
|
|
|
211
211
|
}
|
|
212
212
|
exports.OdspTokenManager = OdspTokenManager;
|
|
213
213
|
async function loadAndPatchRC() {
|
|
214
|
-
const rc = await fluidToolRC_1.loadRC();
|
|
214
|
+
const rc = await (0, fluidToolRC_1.loadRC)();
|
|
215
215
|
if (rc.tokens && rc.tokens.version === undefined) {
|
|
216
216
|
// Clean up older versions
|
|
217
217
|
delete rc.tokens;
|
|
@@ -239,10 +239,10 @@ exports.odspTokensCache = {
|
|
|
239
239
|
rc.tokens.data[key.userOrServer] = prevTokens;
|
|
240
240
|
}
|
|
241
241
|
prevTokens[key.isPush ? "push" : "storage"] = tokens;
|
|
242
|
-
return fluidToolRC_1.saveRC(rc);
|
|
242
|
+
return (0, fluidToolRC_1.saveRC)(rc);
|
|
243
243
|
},
|
|
244
244
|
async lock(callback) {
|
|
245
|
-
const release = await fluidToolRC_1.lockRC();
|
|
245
|
+
const release = await (0, fluidToolRC_1.lockRC)();
|
|
246
246
|
try {
|
|
247
247
|
return await callback();
|
|
248
248
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"odspTokenManager.js","sourceRoot":"","sources":["../src/odspTokenManager.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 { unreachableCase } from \"@fluidframework/common-utils\";\nimport {\n IOdspTokens,\n IClientConfig,\n fetchTokens,\n refreshTokens,\n getOdspScope,\n pushScope,\n getLoginPageUrl,\n TokenRequestCredentials,\n} from \"@fluidframework/odsp-doclib-utils\";\nimport jwtDecode from \"jwt-decode\";\nimport { Mutex } from \"async-mutex\";\nimport { debug } from \"./debug\";\nimport { IAsyncCache, loadRC, saveRC, lockRC } from \"./fluidToolRC\";\nimport { serverListenAndHandle, endResponse } from \"./httpHelpers\";\n\nconst odspAuthRedirectPort = 7000;\nconst odspAuthRedirectOrigin = `http://localhost:${odspAuthRedirectPort}`;\nconst odspAuthRedirectUri = new URL(\"/auth/callback\", odspAuthRedirectOrigin).href;\n\nexport const getMicrosoftConfiguration = (): IClientConfig => ({\n get clientId() {\n if (!process.env.login__microsoft__clientId) {\n throw new Error(\"Client ID environment variable not set: login__microsoft__clientId.\");\n }\n return process.env.login__microsoft__clientId;\n },\n get clientSecret() {\n if (!process.env.login__microsoft__secret) {\n throw new Error(\"Client Secret environment variable not set: login__microsoft__secret.\");\n }\n return process.env.login__microsoft__secret;\n },\n});\n\nexport type OdspTokenConfig = {\n type: \"password\";\n username: string;\n password: string;\n} | {\n type: \"browserLogin\";\n navigator: (url: string) => void;\n redirectUriCallback?: (tokens: IOdspTokens) => Promise<string>;\n};\n\nexport interface IOdspTokenManagerCacheKey {\n readonly isPush: boolean;\n readonly userOrServer: string;\n}\n\nconst isValidToken = (token: string) => {\n // Return false for undefined or empty tokens.\n if (!token || token.length === 0) {\n return false;\n }\n\n const decodedToken = jwtDecode<any>(token);\n // Give it a 60s buffer\n return (decodedToken.exp - 60 >= (new Date().getTime() / 1000));\n};\n\nconst cacheKeyToString = (key: IOdspTokenManagerCacheKey) => {\n return `${key.userOrServer}${key.isPush ? \"[Push]\" : \"\"}`;\n};\n\nexport class OdspTokenManager {\n private readonly storageCache = new Map<string, IOdspTokens>();\n private readonly pushCache = new Map<string, IOdspTokens>();\n private readonly cacheMutex = new Mutex();\n constructor(\n private readonly tokenCache?: IAsyncCache<IOdspTokenManagerCacheKey, IOdspTokens>,\n ) { }\n\n public async updateTokensCache(key: IOdspTokenManagerCacheKey, value: IOdspTokens) {\n await this.cacheMutex.runExclusive(async () => {\n await this.updateTokensCacheWithoutLock(key, value);\n });\n }\n\n private async updateTokensCacheWithoutLock(key: IOdspTokenManagerCacheKey, value: IOdspTokens) {\n debug(`${cacheKeyToString(key)}: Saving tokens`);\n const memoryCache = key.isPush ? this.pushCache : this.storageCache;\n memoryCache.set(key.userOrServer, value);\n await this.tokenCache?.save(key, value);\n }\n\n public async getOdspTokens(\n server: string,\n clientConfig: IClientConfig,\n tokenConfig: OdspTokenConfig,\n forceRefresh = false,\n forceReauth = false,\n ): Promise<IOdspTokens> {\n return this.getTokens(\n false,\n server,\n clientConfig,\n tokenConfig,\n forceRefresh,\n forceReauth,\n );\n }\n\n public async getPushTokens(\n server: string,\n clientConfig: IClientConfig,\n tokenConfig: OdspTokenConfig,\n forceRefresh = false,\n forceReauth = false,\n ): Promise<IOdspTokens> {\n return this.getTokens(\n true,\n server,\n clientConfig,\n tokenConfig,\n forceRefresh,\n forceReauth,\n );\n }\n\n private async getTokenFromCache(\n cacheKey: IOdspTokenManagerCacheKey,\n ) {\n const memoryCache = cacheKey.isPush ? this.pushCache : this.storageCache;\n const memoryToken = memoryCache.get(cacheKey.userOrServer);\n if (memoryToken) {\n debug(`${cacheKeyToString(cacheKey)}: Token found in memory `);\n return memoryToken;\n }\n const fileToken = await this.tokenCache?.get(cacheKey);\n if (fileToken) {\n debug(`${cacheKeyToString(cacheKey)}: Token found in file`);\n memoryCache.set(cacheKey.userOrServer, fileToken);\n return fileToken;\n }\n }\n\n private static getCacheKey(\n isPush: boolean,\n tokenConfig: OdspTokenConfig,\n server: string,\n ): IOdspTokenManagerCacheKey {\n // If we are using password, we should cache the token per user instead of per server\n return { isPush, userOrServer: tokenConfig.type === \"password\" ? tokenConfig.username : server };\n }\n\n private async getTokens(\n isPush: boolean,\n server: string,\n clientConfig: IClientConfig,\n tokenConfig: OdspTokenConfig,\n forceRefresh: boolean,\n forceReauth: boolean,\n ): Promise<IOdspTokens> {\n const invokeGetTokensCore = async () => {\n // Don't solely rely on tokenCache lock, ensure serialized execution of\n // cache update to avoid multiple fetch.\n return this.cacheMutex.runExclusive(async () => {\n return this.getTokensCore(\n isPush,\n server,\n clientConfig,\n tokenConfig,\n forceRefresh,\n forceReauth);\n });\n };\n if (!forceReauth && !forceRefresh) {\n // check and return if it exists without lock\n const cacheKey = OdspTokenManager.getCacheKey(isPush, tokenConfig, server);\n const tokensFromCache = await this.getTokenFromCache(cacheKey);\n if (tokensFromCache) {\n if (isValidToken(tokensFromCache.accessToken)) {\n debug(`${cacheKeyToString(cacheKey)}: Token reused from cache `);\n await this.onTokenRetrievalFromCache(tokenConfig, tokensFromCache);\n return tokensFromCache;\n }\n debug(`${cacheKeyToString(cacheKey)}: Token expired from cache `);\n }\n }\n if (this.tokenCache) {\n // check with lock, used to prevent concurrent auth attempts\n return this.tokenCache.lock(invokeGetTokensCore);\n }\n return invokeGetTokensCore();\n }\n\n private async getTokensCore(\n isPush: boolean,\n server: string,\n clientConfig: IClientConfig,\n tokenConfig: OdspTokenConfig,\n forceRefresh,\n forceReauth,\n ): Promise<IOdspTokens> {\n const scope = isPush ? pushScope : getOdspScope(server);\n const cacheKey = OdspTokenManager.getCacheKey(isPush, tokenConfig, server);\n let tokens: IOdspTokens | undefined;\n if (!forceReauth) {\n // check the cache again under the lock (if it is there)\n const tokensFromCache = await this.getTokenFromCache(cacheKey);\n if (tokensFromCache) {\n if (forceRefresh || !isValidToken(tokensFromCache.accessToken)) {\n try {\n // This updates the tokens in tokensFromCache\n tokens = await refreshTokens(server, scope, clientConfig, tokensFromCache);\n await this.updateTokensCacheWithoutLock(cacheKey, tokens);\n } catch (error) {\n debug(`${cacheKeyToString(cacheKey)}: Error in refreshing token. ${error}`);\n }\n } else {\n tokens = tokensFromCache;\n debug(`${cacheKeyToString(cacheKey)}: Token reused from locked cache `);\n }\n }\n }\n\n if (tokens) {\n await this.onTokenRetrievalFromCache(tokenConfig, tokens);\n return tokens;\n }\n\n switch (tokenConfig.type) {\n case \"password\":\n tokens = await this.acquireTokensWithPassword(\n server,\n scope,\n clientConfig,\n tokenConfig.username,\n tokenConfig.password,\n );\n break;\n case \"browserLogin\":\n tokens = await this.acquireTokensViaBrowserLogin(\n getLoginPageUrl(server, clientConfig, scope, odspAuthRedirectUri),\n server,\n clientConfig,\n scope,\n tokenConfig.navigator,\n tokenConfig.redirectUriCallback,\n );\n break;\n default:\n unreachableCase(tokenConfig);\n }\n\n await this.updateTokensCacheWithoutLock(cacheKey, tokens);\n return tokens;\n }\n\n private async acquireTokensWithPassword(\n server: string,\n scope: string,\n clientConfig: IClientConfig,\n username: string,\n password: string,\n ): Promise<IOdspTokens> {\n const credentials: TokenRequestCredentials = {\n grant_type: \"password\",\n username,\n password,\n };\n return fetchTokens(server, scope, clientConfig, credentials);\n }\n\n private async acquireTokensViaBrowserLogin(\n loginPageUrl: string,\n server: string,\n clientConfig: IClientConfig,\n scope: string,\n navigator: (url: string) => void,\n redirectUriCallback?: (tokens: IOdspTokens) => Promise<string>,\n ): Promise<IOdspTokens> {\n // Start up a local auth redirect handler service to receive the tokens after login\n const tokenGetter = await serverListenAndHandle(odspAuthRedirectPort, async (req, res) => {\n // extract code from request URL and fetch the tokens\n const credentials: TokenRequestCredentials = {\n grant_type: \"authorization_code\",\n code: this.extractAuthorizationCode(req.url),\n redirect_uri: odspAuthRedirectUri,\n };\n const tokens = await fetchTokens(server, scope, clientConfig, credentials);\n\n // redirect now that the browser is done with auth\n if (redirectUriCallback) {\n res.writeHead(301, { Location: await redirectUriCallback(tokens) });\n await endResponse(res);\n } else {\n res.write(\"Please close the window\");\n await endResponse(res);\n }\n\n return tokens;\n });\n\n // Now that our local redirect handler is up, navigate the browser to the login page\n navigator(loginPageUrl);\n\n // Receive and extract the tokens\n const odspTokens = await tokenGetter();\n\n return odspTokens;\n }\n\n private async onTokenRetrievalFromCache(config: OdspTokenConfig, tokens: IOdspTokens) {\n if (config.type === \"browserLogin\" && config.redirectUriCallback) {\n config.navigator(await config.redirectUriCallback(tokens));\n }\n }\n\n private extractAuthorizationCode(relativeUrl: string | undefined): string {\n if (relativeUrl === undefined) {\n throw Error(\"Failed to get authorization\");\n }\n const parsedUrl = new URL(relativeUrl, odspAuthRedirectOrigin);\n const code = parsedUrl.searchParams.get(\"code\");\n if (!code) {\n throw Error(\"Failed to get authorization\");\n }\n return code;\n }\n}\n\nasync function loadAndPatchRC() {\n const rc = await loadRC();\n if (rc.tokens && rc.tokens.version === undefined) {\n // Clean up older versions\n delete (rc as any).tokens;\n delete (rc as any).pushTokens;\n }\n return rc;\n}\n\nexport const odspTokensCache: IAsyncCache<IOdspTokenManagerCacheKey, IOdspTokens> = {\n async get(key: IOdspTokenManagerCacheKey): Promise<IOdspTokens | undefined> {\n const rc = await loadAndPatchRC();\n return rc.tokens?.data[key.userOrServer]?.[key.isPush ? \"push\" : \"storage\"];\n },\n async save(key: IOdspTokenManagerCacheKey, tokens: IOdspTokens): Promise<void> {\n const rc = await loadAndPatchRC();\n if (!rc.tokens) {\n rc.tokens = {\n version: 1,\n data: {},\n };\n }\n let prevTokens = rc.tokens.data[key.userOrServer];\n if (!prevTokens) {\n prevTokens = {};\n rc.tokens.data[key.userOrServer] = prevTokens;\n }\n prevTokens[key.isPush ? \"push\" : \"storage\"] = tokens;\n return saveRC(rc);\n },\n async lock<T>(callback: () => Promise<T>): Promise<T> {\n const release = await lockRC();\n try {\n return await callback();\n } finally {\n await release();\n }\n },\n};\n"]}
|
|
1
|
+
{"version":3,"file":"odspTokenManager.js","sourceRoot":"","sources":["../src/odspTokenManager.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 { unreachableCase } from \"@fluidframework/common-utils\";\nimport {\n IOdspTokens,\n IClientConfig,\n fetchTokens,\n refreshTokens,\n getOdspScope,\n pushScope,\n getLoginPageUrl,\n TokenRequestCredentials,\n} from \"@fluidframework/odsp-doclib-utils\";\nimport jwtDecode from \"jwt-decode\";\nimport { Mutex } from \"async-mutex\";\nimport { debug } from \"./debug\";\nimport { IAsyncCache, loadRC, saveRC, lockRC } from \"./fluidToolRC\";\nimport { serverListenAndHandle, endResponse } from \"./httpHelpers\";\n\nconst odspAuthRedirectPort = 7000;\nconst odspAuthRedirectOrigin = `http://localhost:${odspAuthRedirectPort}`;\nconst odspAuthRedirectUri = new URL(\"/auth/callback\", odspAuthRedirectOrigin).href;\n\nexport const getMicrosoftConfiguration = (): IClientConfig => ({\n get clientId() {\n if (!process.env.login__microsoft__clientId) {\n throw new Error(\"Client ID environment variable not set: login__microsoft__clientId.\");\n }\n return process.env.login__microsoft__clientId;\n },\n get clientSecret() {\n if (!process.env.login__microsoft__secret) {\n throw new Error(\"Client Secret environment variable not set: login__microsoft__secret.\");\n }\n return process.env.login__microsoft__secret;\n },\n});\n\nexport type OdspTokenConfig = {\n type: \"password\";\n username: string;\n password: string;\n} | {\n type: \"browserLogin\";\n navigator: (url: string) => void;\n redirectUriCallback?: (tokens: IOdspTokens) => Promise<string>;\n};\n\nexport interface IOdspTokenManagerCacheKey {\n readonly isPush: boolean;\n readonly userOrServer: string;\n}\n\nconst isValidToken = (token: string) => {\n // Return false for undefined or empty tokens.\n if (!token || token.length === 0) {\n return false;\n }\n\n const decodedToken = jwtDecode<any>(token);\n // Give it a 60s buffer\n return (decodedToken.exp - 60 >= (new Date().getTime() / 1000));\n};\n\nconst cacheKeyToString = (key: IOdspTokenManagerCacheKey) => {\n return `${key.userOrServer}${key.isPush ? \"[Push]\" : \"\"}`;\n};\n\nexport class OdspTokenManager {\n private readonly storageCache = new Map<string, IOdspTokens>();\n private readonly pushCache = new Map<string, IOdspTokens>();\n private readonly cacheMutex = new Mutex();\n constructor(\n private readonly tokenCache?: IAsyncCache<IOdspTokenManagerCacheKey, IOdspTokens>,\n ) { }\n\n public async updateTokensCache(key: IOdspTokenManagerCacheKey, value: IOdspTokens) {\n await this.cacheMutex.runExclusive(async () => {\n await this.updateTokensCacheWithoutLock(key, value);\n });\n }\n\n private async updateTokensCacheWithoutLock(key: IOdspTokenManagerCacheKey, value: IOdspTokens) {\n debug(`${cacheKeyToString(key)}: Saving tokens`);\n const memoryCache = key.isPush ? this.pushCache : this.storageCache;\n memoryCache.set(key.userOrServer, value);\n await this.tokenCache?.save(key, value);\n }\n\n public async getOdspTokens(\n server: string,\n clientConfig: IClientConfig,\n tokenConfig: OdspTokenConfig,\n forceRefresh = false,\n forceReauth = false,\n ): Promise<IOdspTokens> {\n return this.getTokens(\n false,\n server,\n clientConfig,\n tokenConfig,\n forceRefresh,\n forceReauth,\n );\n }\n\n public async getPushTokens(\n server: string,\n clientConfig: IClientConfig,\n tokenConfig: OdspTokenConfig,\n forceRefresh = false,\n forceReauth = false,\n ): Promise<IOdspTokens> {\n return this.getTokens(\n true,\n server,\n clientConfig,\n tokenConfig,\n forceRefresh,\n forceReauth,\n );\n }\n\n private async getTokenFromCache(\n cacheKey: IOdspTokenManagerCacheKey,\n ) {\n const memoryCache = cacheKey.isPush ? this.pushCache : this.storageCache;\n const memoryToken = memoryCache.get(cacheKey.userOrServer);\n if (memoryToken) {\n debug(`${cacheKeyToString(cacheKey)}: Token found in memory `);\n return memoryToken;\n }\n const fileToken = await this.tokenCache?.get(cacheKey);\n if (fileToken) {\n debug(`${cacheKeyToString(cacheKey)}: Token found in file`);\n memoryCache.set(cacheKey.userOrServer, fileToken);\n return fileToken;\n }\n }\n\n private static getCacheKey(\n isPush: boolean,\n tokenConfig: OdspTokenConfig,\n server: string,\n ): IOdspTokenManagerCacheKey {\n // If we are using password, we should cache the token per user instead of per server\n return { isPush, userOrServer: tokenConfig.type === \"password\" ? tokenConfig.username : server };\n }\n\n private async getTokens(\n isPush: boolean,\n server: string,\n clientConfig: IClientConfig,\n tokenConfig: OdspTokenConfig,\n forceRefresh: boolean,\n forceReauth: boolean,\n ): Promise<IOdspTokens> {\n const invokeGetTokensCore = async () => {\n // Don't solely rely on tokenCache lock, ensure serialized execution of\n // cache update to avoid multiple fetch.\n return this.cacheMutex.runExclusive(async () => {\n return this.getTokensCore(\n isPush,\n server,\n clientConfig,\n tokenConfig,\n forceRefresh,\n forceReauth);\n });\n };\n if (!forceReauth && !forceRefresh) {\n // check and return if it exists without lock\n const cacheKey = OdspTokenManager.getCacheKey(isPush, tokenConfig, server);\n const tokensFromCache = await this.getTokenFromCache(cacheKey);\n if (tokensFromCache) {\n if (isValidToken(tokensFromCache.accessToken)) {\n debug(`${cacheKeyToString(cacheKey)}: Token reused from cache `);\n await this.onTokenRetrievalFromCache(tokenConfig, tokensFromCache);\n return tokensFromCache;\n }\n debug(`${cacheKeyToString(cacheKey)}: Token expired from cache `);\n }\n }\n if (this.tokenCache) {\n // check with lock, used to prevent concurrent auth attempts\n return this.tokenCache.lock(invokeGetTokensCore);\n }\n return invokeGetTokensCore();\n }\n\n private async getTokensCore(\n isPush: boolean,\n server: string,\n clientConfig: IClientConfig,\n tokenConfig: OdspTokenConfig,\n forceRefresh,\n forceReauth,\n ): Promise<IOdspTokens> {\n const scope = isPush ? pushScope : getOdspScope(server);\n const cacheKey = OdspTokenManager.getCacheKey(isPush, tokenConfig, server);\n let tokens: IOdspTokens | undefined;\n if (!forceReauth) {\n // check the cache again under the lock (if it is there)\n const tokensFromCache = await this.getTokenFromCache(cacheKey);\n if (tokensFromCache) {\n if (forceRefresh || !isValidToken(tokensFromCache.accessToken)) {\n try {\n // This updates the tokens in tokensFromCache\n tokens = await refreshTokens(server, scope, clientConfig, tokensFromCache);\n await this.updateTokensCacheWithoutLock(cacheKey, tokens);\n } catch (error) {\n debug(`${cacheKeyToString(cacheKey)}: Error in refreshing token. ${error}`);\n }\n } else {\n tokens = tokensFromCache;\n debug(`${cacheKeyToString(cacheKey)}: Token reused from locked cache `);\n }\n }\n }\n\n if (tokens) {\n await this.onTokenRetrievalFromCache(tokenConfig, tokens);\n return tokens;\n }\n\n switch (tokenConfig.type) {\n case \"password\":\n tokens = await this.acquireTokensWithPassword(\n server,\n scope,\n clientConfig,\n tokenConfig.username,\n tokenConfig.password,\n );\n break;\n case \"browserLogin\":\n tokens = await this.acquireTokensViaBrowserLogin(\n getLoginPageUrl(server, clientConfig, scope, odspAuthRedirectUri),\n server,\n clientConfig,\n scope,\n tokenConfig.navigator,\n tokenConfig.redirectUriCallback,\n );\n break;\n default:\n unreachableCase(tokenConfig);\n }\n\n await this.updateTokensCacheWithoutLock(cacheKey, tokens);\n return tokens;\n }\n\n private async acquireTokensWithPassword(\n server: string,\n scope: string,\n clientConfig: IClientConfig,\n username: string,\n password: string,\n ): Promise<IOdspTokens> {\n const credentials: TokenRequestCredentials = {\n grant_type: \"password\",\n username,\n password,\n };\n return fetchTokens(server, scope, clientConfig, credentials);\n }\n\n private async acquireTokensViaBrowserLogin(\n loginPageUrl: string,\n server: string,\n clientConfig: IClientConfig,\n scope: string,\n navigator: (url: string) => void,\n redirectUriCallback?: (tokens: IOdspTokens) => Promise<string>,\n ): Promise<IOdspTokens> {\n // Start up a local auth redirect handler service to receive the tokens after login\n const tokenGetter = await serverListenAndHandle(odspAuthRedirectPort, async (req, res) => {\n // extract code from request URL and fetch the tokens\n const credentials: TokenRequestCredentials = {\n grant_type: \"authorization_code\",\n code: this.extractAuthorizationCode(req.url),\n redirect_uri: odspAuthRedirectUri,\n };\n const tokens = await fetchTokens(server, scope, clientConfig, credentials);\n\n // redirect now that the browser is done with auth\n if (redirectUriCallback) {\n res.writeHead(301, { Location: await redirectUriCallback(tokens) });\n await endResponse(res);\n } else {\n res.write(\"Please close the window\");\n await endResponse(res);\n }\n\n return tokens;\n });\n\n // Now that our local redirect handler is up, navigate the browser to the login page\n navigator(loginPageUrl);\n\n // Receive and extract the tokens\n const odspTokens = await tokenGetter();\n\n return odspTokens;\n }\n\n private async onTokenRetrievalFromCache(config: OdspTokenConfig, tokens: IOdspTokens) {\n if (config.type === \"browserLogin\" && config.redirectUriCallback) {\n config.navigator(await config.redirectUriCallback(tokens));\n }\n }\n\n private extractAuthorizationCode(relativeUrl: string | undefined): string {\n if (relativeUrl === undefined) {\n throw Error(\"Failed to get authorization\");\n }\n const parsedUrl = new URL(relativeUrl, odspAuthRedirectOrigin);\n const code = parsedUrl.searchParams.get(\"code\");\n if (!code) {\n throw Error(\"Failed to get authorization\");\n }\n return code;\n }\n}\n\nasync function loadAndPatchRC() {\n const rc = await loadRC();\n if (rc.tokens && rc.tokens.version === undefined) {\n // Clean up older versions\n delete (rc as any).tokens;\n delete (rc as any).pushTokens;\n }\n return rc;\n}\n\nexport const odspTokensCache: IAsyncCache<IOdspTokenManagerCacheKey, IOdspTokens> = {\n async get(key: IOdspTokenManagerCacheKey): Promise<IOdspTokens | undefined> {\n const rc = await loadAndPatchRC();\n return rc.tokens?.data[key.userOrServer]?.[key.isPush ? \"push\" : \"storage\"];\n },\n async save(key: IOdspTokenManagerCacheKey, tokens: IOdspTokens): Promise<void> {\n const rc = await loadAndPatchRC();\n if (!rc.tokens) {\n rc.tokens = {\n version: 1,\n data: {},\n };\n }\n let prevTokens = rc.tokens.data[key.userOrServer];\n if (!prevTokens) {\n prevTokens = {};\n rc.tokens.data[key.userOrServer] = prevTokens;\n }\n prevTokens[key.isPush ? \"push\" : \"storage\"] = tokens;\n return saveRC(rc);\n },\n async lock<T>(callback: () => Promise<T>): Promise<T> {\n const release = await lockRC();\n try {\n return await callback();\n } finally {\n await release();\n }\n },\n};\n"]}
|
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 = "0.59.
|
|
8
|
+
export declare const pkgVersion = "0.59.3000-67119";
|
|
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,oBAAoB,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 = "0.59.
|
|
11
|
+
exports.pkgVersion = "0.59.3000-67119";
|
|
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,iBAAiB,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 = \"0.59.3000-67119\";\n"]}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
import { ITree } from "@fluidframework/protocol-definitions";
|
|
6
|
+
/** The prefix that all GC blob names start with. */
|
|
6
7
|
export declare const gcBlobPrefix = "__gc";
|
|
7
8
|
export interface ISnapshotNormalizerConfig {
|
|
8
9
|
blobsToNormalize?: string[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"snapshotNormalizer.d.ts","sourceRoot":"","sources":["../src/snapshotNormalizer.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EACH,KAAK,EAGR,MAAM,sCAAsC,CAAC;
|
|
1
|
+
{"version":3,"file":"snapshotNormalizer.d.ts","sourceRoot":"","sources":["../src/snapshotNormalizer.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EACH,KAAK,EAGR,MAAM,sCAAsC,CAAC;AAI9C,oDAAoD;AACpD,eAAO,MAAM,YAAY,SAAS,CAAC;AAEnC,MAAM,WAAW,yBAAyB;IAEtC,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B;;;;OAIG;IACH,2BAA2B,CAAC,EAAE,MAAM,EAAE,CAAC;CAC1C;AAoGD;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,KAAK,EAAE,MAAM,CAAC,EAAE,yBAAyB,GAAG,KAAK,CAkBhG"}
|
|
@@ -7,6 +7,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
7
7
|
exports.getNormalizedSnapshot = exports.gcBlobPrefix = void 0;
|
|
8
8
|
const protocol_base_1 = require("@fluidframework/protocol-base");
|
|
9
9
|
const protocol_definitions_1 = require("@fluidframework/protocol-definitions");
|
|
10
|
+
/** The name of the metadata blob added to the root of the container runtime. */
|
|
11
|
+
const metadataBlobName = ".metadata";
|
|
12
|
+
/** The prefix that all GC blob names start with. */
|
|
10
13
|
exports.gcBlobPrefix = "__gc";
|
|
11
14
|
/**
|
|
12
15
|
* Function that deep sorts an array. It handles cases where array elements are objects or arrays.
|
|
@@ -74,6 +77,22 @@ function getNormalizedBlobContent(blobContent, blobName) {
|
|
|
74
77
|
}
|
|
75
78
|
content = JSON.stringify(gcState);
|
|
76
79
|
}
|
|
80
|
+
/**
|
|
81
|
+
* The metadata blob has "summaryNumber" or "summaryCount" that tells which summary this is for a container. It can
|
|
82
|
+
* be different in summaries of two clients even if they are generated at the same sequence#. For instance, at seq#
|
|
83
|
+
* 1000, if one client has summarized 10 times and other has summarizer 15 times, summaryNumber will be different
|
|
84
|
+
* for them. So, update "summaryNumber" to 0 for purposes of comparing snapshots.
|
|
85
|
+
*/
|
|
86
|
+
if (blobName === metadataBlobName) {
|
|
87
|
+
const metadata = JSON.parse(content);
|
|
88
|
+
if (metadata.summaryNumber !== undefined) {
|
|
89
|
+
metadata.summaryNumber = 0;
|
|
90
|
+
}
|
|
91
|
+
if (metadata.summaryCount !== undefined) {
|
|
92
|
+
metadata.summaryCount = 0;
|
|
93
|
+
}
|
|
94
|
+
content = JSON.stringify(metadata);
|
|
95
|
+
}
|
|
77
96
|
// Deep sort the content if it's parseable.
|
|
78
97
|
try {
|
|
79
98
|
let contentObj = JSON.parse(content);
|
|
@@ -98,11 +117,14 @@ function getNormalizedBlobContent(blobContent, blobName) {
|
|
|
98
117
|
* @returns a copy of the normalized snapshot tree.
|
|
99
118
|
*/
|
|
100
119
|
function getNormalizedSnapshot(snapshot, config) {
|
|
120
|
+
var _a;
|
|
101
121
|
// Merge blobs to normalize in the config with runtime blobs to normalize. The contents of these blobs will be
|
|
102
122
|
// parsed and deep sorted.
|
|
103
123
|
const normalizedEntries = [];
|
|
124
|
+
// The metadata blob in the root of the summary tree needs to be normalized.
|
|
125
|
+
const blobsToNormalize = [metadataBlobName, ...(_a = config === null || config === void 0 ? void 0 : config.blobsToNormalize) !== null && _a !== void 0 ? _a : []];
|
|
104
126
|
for (const entry of snapshot.entries) {
|
|
105
|
-
normalizedEntries.push(normalizeEntry(entry, config));
|
|
127
|
+
normalizedEntries.push(normalizeEntry(entry, Object.assign(Object.assign({}, config), { blobsToNormalize })));
|
|
106
128
|
}
|
|
107
129
|
// Sort the tree entries based on their path.
|
|
108
130
|
normalizedEntries.sort((a, b) => a.path.localeCompare(b.path));
|
|
@@ -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\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 // 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 for (const entry of snapshot.entries) {\n normalizedEntries.push(normalizeEntry(entry, config));\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 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"]}
|
package/lib/fluidToolRC.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fluidToolRC.d.ts","sourceRoot":"","sources":["../src/fluidToolRC.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAOH,OAAO,EAAE,WAAW,EAAE,MAAM,mCAAmC,CAAC;AAEhE,MAAM,WAAW,WAAW,CAAC,IAAI,EAAE,MAAM;IACrC,GAAG,CAAC,GAAG,EAAE,IAAI,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IAC5C,IAAI,CAAC,GAAG,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9C,IAAI,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;CACnD;AAED,MAAM,WAAW,UAAU;IACvB,MAAM,CAAC,EAAE;QACL,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,IAAI,EAAE;YACF,CAAC,GAAG,EAAE,MAAM,GAAG;gBACX,OAAO,CAAC,EAAE,WAAW,CAAC;gBACtB,IAAI,CAAC,EAAE,WAAW,
|
|
1
|
+
{"version":3,"file":"fluidToolRC.d.ts","sourceRoot":"","sources":["../src/fluidToolRC.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAOH,OAAO,EAAE,WAAW,EAAE,MAAM,mCAAmC,CAAC;AAEhE,MAAM,WAAW,WAAW,CAAC,IAAI,EAAE,MAAM;IACrC,GAAG,CAAC,GAAG,EAAE,IAAI,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IAC5C,IAAI,CAAC,GAAG,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9C,IAAI,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;CACnD;AAED,MAAM,WAAW,UAAU;IACvB,MAAM,CAAC,EAAE;QACL,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,IAAI,EAAE;YACF,CAAC,GAAG,EAAE,MAAM,GAAG;gBACX,OAAO,CAAC,EAAE,WAAW,CAAC;gBACtB,IAAI,CAAC,EAAE,WAAW,CAAC;aACtB,CAAC;SACL,CAAC;KACL,CAAC;CACL;AAID,wBAAsB,MAAM,IAAI,OAAO,CAAC,UAAU,CAAC,CAclD;AAED,wBAAsB,MAAM,CAAC,EAAE,EAAE,UAAU,iBAI1C;AAED,wBAAsB,MAAM,iBAS3B"}
|
package/lib/fluidToolRC.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fluidToolRC.js","sourceRoot":"","sources":["../src/fluidToolRC.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAqBvC,MAAM,aAAa,GAAG,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,cAAc,CAAC,CAAC;AAEpE,MAAM,CAAC,KAAK,UAAU,MAAM;IACxB,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC;IAC7C,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC;IACzC,MAAM,QAAQ,GAAG,aAAa,EAAE,CAAC;IACjC,IAAI,MAAM,MAAM,CAAC,QAAQ,CAAC,EAAE;QACxB,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACrC,IAAI;YACA,+DAA+D;YAC/D,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;SAC3C;QAAC,OAAO,CAAC,EAAE;YACR,UAAU;SACb;KACJ;IACD,OAAO,EAAE,CAAC;AACd,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,EAAc;IACvC,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC;IAC/C,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC;IACjD,OAAO,SAAS,CAAC,aAAa,EAAE,EAAE,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC;AACpE,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,MAAM;IACxB,+DAA+D;IAC/D,OAAO,IAAI,CAAC,aAAa,EAAE,EAAE;QACzB,OAAO,EAAE;YACL,OAAO,EAAE,IAAI;SAChB;QACD,KAAK,
|
|
1
|
+
{"version":3,"file":"fluidToolRC.js","sourceRoot":"","sources":["../src/fluidToolRC.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAqBvC,MAAM,aAAa,GAAG,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,cAAc,CAAC,CAAC;AAEpE,MAAM,CAAC,KAAK,UAAU,MAAM;IACxB,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC;IAC7C,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC;IACzC,MAAM,QAAQ,GAAG,aAAa,EAAE,CAAC;IACjC,IAAI,MAAM,MAAM,CAAC,QAAQ,CAAC,EAAE;QACxB,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACrC,IAAI;YACA,+DAA+D;YAC/D,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;SAC3C;QAAC,OAAO,CAAC,EAAE;YACR,UAAU;SACb;KACJ;IACD,OAAO,EAAE,CAAC;AACd,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,EAAc;IACvC,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC;IAC/C,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC;IACjD,OAAO,SAAS,CAAC,aAAa,EAAE,EAAE,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC;AACpE,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,MAAM;IACxB,+DAA+D;IAC/D,OAAO,IAAI,CAAC,aAAa,EAAE,EAAE;QACzB,OAAO,EAAE;YACL,OAAO,EAAE,IAAI;SAChB;QACD,KAAK,EAAE,KAAK;QACZ,QAAQ,EAAE,KAAK;KAClB,CAAC,CAAC;AACP,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport fs from \"fs\";\nimport os from \"os\";\nimport path from \"path\";\nimport util from \"util\";\nimport { lock } from \"proper-lockfile\";\nimport { IOdspTokens } from \"@fluidframework/odsp-doclib-utils\";\n\nexport interface IAsyncCache<TKey, TValue> {\n get(key: TKey): Promise<TValue | undefined>;\n save(key: TKey, value: TValue): Promise<void>;\n lock<T>(callback: () => Promise<T>): Promise<T>;\n}\n\nexport interface IResources {\n tokens?: {\n version?: number;\n data: {\n [key: string]: {\n storage?: IOdspTokens;\n push?: IOdspTokens;\n };\n };\n };\n}\n\nconst getRCFileName = () => path.join(os.homedir(), \".fluidtoolrc\");\n\nexport async function loadRC(): Promise<IResources> {\n const readFile = util.promisify(fs.readFile);\n const exists = util.promisify(fs.exists);\n const fileName = getRCFileName();\n if (await exists(fileName)) {\n const buf = await readFile(fileName);\n try {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return JSON.parse(buf.toString(\"utf8\"));\n } catch (e) {\n // Nothing\n }\n }\n return {};\n}\n\nexport async function saveRC(rc: IResources) {\n const writeFile = util.promisify(fs.writeFile);\n const content = JSON.stringify(rc, undefined, 2);\n return writeFile(getRCFileName(), Buffer.from(content, \"utf8\"));\n}\n\nexport async function lockRC() {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return lock(getRCFileName(), {\n retries: {\n forever: true,\n },\n stale: 60000,\n realpath: false,\n });\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"odspTokenManager.js","sourceRoot":"","sources":["../src/odspTokenManager.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 { unreachableCase } from \"@fluidframework/common-utils\";\nimport {\n IOdspTokens,\n IClientConfig,\n fetchTokens,\n refreshTokens,\n getOdspScope,\n pushScope,\n getLoginPageUrl,\n TokenRequestCredentials,\n} from \"@fluidframework/odsp-doclib-utils\";\nimport jwtDecode from \"jwt-decode\";\nimport { Mutex } from \"async-mutex\";\nimport { debug } from \"./debug\";\nimport { IAsyncCache, loadRC, saveRC, lockRC } from \"./fluidToolRC\";\nimport { serverListenAndHandle, endResponse } from \"./httpHelpers\";\n\nconst odspAuthRedirectPort = 7000;\nconst odspAuthRedirectOrigin = `http://localhost:${odspAuthRedirectPort}`;\nconst odspAuthRedirectUri = new URL(\"/auth/callback\", odspAuthRedirectOrigin).href;\n\nexport const getMicrosoftConfiguration = (): IClientConfig => ({\n get clientId() {\n if (!process.env.login__microsoft__clientId) {\n throw new Error(\"Client ID environment variable not set: login__microsoft__clientId.\");\n }\n return process.env.login__microsoft__clientId;\n },\n get clientSecret() {\n if (!process.env.login__microsoft__secret) {\n throw new Error(\"Client Secret environment variable not set: login__microsoft__secret.\");\n }\n return process.env.login__microsoft__secret;\n },\n});\n\nexport type OdspTokenConfig = {\n type: \"password\";\n username: string;\n password: string;\n} | {\n type: \"browserLogin\";\n navigator: (url: string) => void;\n redirectUriCallback?: (tokens: IOdspTokens) => Promise<string>;\n};\n\nexport interface IOdspTokenManagerCacheKey {\n readonly isPush: boolean;\n readonly userOrServer: string;\n}\n\nconst isValidToken = (token: string) => {\n // Return false for undefined or empty tokens.\n if (!token || token.length === 0) {\n return false;\n }\n\n const decodedToken = jwtDecode<any>(token);\n // Give it a 60s buffer\n return (decodedToken.exp - 60 >= (new Date().getTime() / 1000));\n};\n\nconst cacheKeyToString = (key: IOdspTokenManagerCacheKey) => {\n return `${key.userOrServer}${key.isPush ? \"[Push]\" : \"\"}`;\n};\n\nexport class OdspTokenManager {\n private readonly storageCache = new Map<string, IOdspTokens>();\n private readonly pushCache = new Map<string, IOdspTokens>();\n private readonly cacheMutex = new Mutex();\n constructor(\n private readonly tokenCache?: IAsyncCache<IOdspTokenManagerCacheKey, IOdspTokens>,\n ) { }\n\n public async updateTokensCache(key: IOdspTokenManagerCacheKey, value: IOdspTokens) {\n await this.cacheMutex.runExclusive(async () => {\n await this.updateTokensCacheWithoutLock(key, value);\n });\n }\n\n private async updateTokensCacheWithoutLock(key: IOdspTokenManagerCacheKey, value: IOdspTokens) {\n debug(`${cacheKeyToString(key)}: Saving tokens`);\n const memoryCache = key.isPush ? this.pushCache : this.storageCache;\n memoryCache.set(key.userOrServer, value);\n await this.tokenCache?.save(key, value);\n }\n\n public async getOdspTokens(\n server: string,\n clientConfig: IClientConfig,\n tokenConfig: OdspTokenConfig,\n forceRefresh = false,\n forceReauth = false,\n ): Promise<IOdspTokens> {\n return this.getTokens(\n false,\n server,\n clientConfig,\n tokenConfig,\n forceRefresh,\n forceReauth,\n );\n }\n\n public async getPushTokens(\n server: string,\n clientConfig: IClientConfig,\n tokenConfig: OdspTokenConfig,\n forceRefresh = false,\n forceReauth = false,\n ): Promise<IOdspTokens> {\n return this.getTokens(\n true,\n server,\n clientConfig,\n tokenConfig,\n forceRefresh,\n forceReauth,\n );\n }\n\n private async getTokenFromCache(\n cacheKey: IOdspTokenManagerCacheKey,\n ) {\n const memoryCache = cacheKey.isPush ? this.pushCache : this.storageCache;\n const memoryToken = memoryCache.get(cacheKey.userOrServer);\n if (memoryToken) {\n debug(`${cacheKeyToString(cacheKey)}: Token found in memory `);\n return memoryToken;\n }\n const fileToken = await this.tokenCache?.get(cacheKey);\n if (fileToken) {\n debug(`${cacheKeyToString(cacheKey)}: Token found in file`);\n memoryCache.set(cacheKey.userOrServer, fileToken);\n return fileToken;\n }\n }\n\n private static getCacheKey(\n isPush: boolean,\n tokenConfig: OdspTokenConfig,\n server: string,\n ): IOdspTokenManagerCacheKey {\n // If we are using password, we should cache the token per user instead of per server\n return { isPush, userOrServer: tokenConfig.type === \"password\" ? tokenConfig.username : server };\n }\n\n private async getTokens(\n isPush: boolean,\n server: string,\n clientConfig: IClientConfig,\n tokenConfig: OdspTokenConfig,\n forceRefresh: boolean,\n forceReauth: boolean,\n ): Promise<IOdspTokens> {\n const invokeGetTokensCore = async () => {\n // Don't solely rely on tokenCache lock, ensure serialized execution of\n // cache update to avoid multiple fetch.\n return this.cacheMutex.runExclusive(async () => {\n return this.getTokensCore(\n isPush,\n server,\n clientConfig,\n tokenConfig,\n forceRefresh,\n forceReauth);\n });\n };\n if (!forceReauth && !forceRefresh) {\n // check and return if it exists without lock\n const cacheKey = OdspTokenManager.getCacheKey(isPush, tokenConfig, server);\n const tokensFromCache = await this.getTokenFromCache(cacheKey);\n if (tokensFromCache) {\n if (isValidToken(tokensFromCache.accessToken)) {\n debug(`${cacheKeyToString(cacheKey)}: Token reused from cache `);\n await this.onTokenRetrievalFromCache(tokenConfig, tokensFromCache);\n return tokensFromCache;\n }\n debug(`${cacheKeyToString(cacheKey)}: Token expired from cache `);\n }\n }\n if (this.tokenCache) {\n // check with lock, used to prevent concurrent auth attempts\n return this.tokenCache.lock(invokeGetTokensCore);\n }\n return invokeGetTokensCore();\n }\n\n private async getTokensCore(\n isPush: boolean,\n server: string,\n clientConfig: IClientConfig,\n tokenConfig: OdspTokenConfig,\n forceRefresh,\n forceReauth,\n ): Promise<IOdspTokens> {\n const scope = isPush ? pushScope : getOdspScope(server);\n const cacheKey = OdspTokenManager.getCacheKey(isPush, tokenConfig, server);\n let tokens: IOdspTokens | undefined;\n if (!forceReauth) {\n // check the cache again under the lock (if it is there)\n const tokensFromCache = await this.getTokenFromCache(cacheKey);\n if (tokensFromCache) {\n if (forceRefresh || !isValidToken(tokensFromCache.accessToken)) {\n try {\n // This updates the tokens in tokensFromCache\n tokens = await refreshTokens(server, scope, clientConfig, tokensFromCache);\n await this.updateTokensCacheWithoutLock(cacheKey, tokens);\n } catch (error) {\n debug(`${cacheKeyToString(cacheKey)}: Error in refreshing token. ${error}`);\n }\n } else {\n tokens = tokensFromCache;\n debug(`${cacheKeyToString(cacheKey)}: Token reused from locked cache `);\n }\n }\n }\n\n if (tokens) {\n await this.onTokenRetrievalFromCache(tokenConfig, tokens);\n return tokens;\n }\n\n switch (tokenConfig.type) {\n case \"password\":\n tokens = await this.acquireTokensWithPassword(\n server,\n scope,\n clientConfig,\n tokenConfig.username,\n tokenConfig.password,\n );\n break;\n case \"browserLogin\":\n tokens = await this.acquireTokensViaBrowserLogin(\n getLoginPageUrl(server, clientConfig, scope, odspAuthRedirectUri),\n server,\n clientConfig,\n scope,\n tokenConfig.navigator,\n tokenConfig.redirectUriCallback,\n );\n break;\n default:\n unreachableCase(tokenConfig);\n }\n\n await this.updateTokensCacheWithoutLock(cacheKey, tokens);\n return tokens;\n }\n\n private async acquireTokensWithPassword(\n server: string,\n scope: string,\n clientConfig: IClientConfig,\n username: string,\n password: string,\n ): Promise<IOdspTokens> {\n const credentials: TokenRequestCredentials = {\n grant_type: \"password\",\n username,\n password,\n };\n return fetchTokens(server, scope, clientConfig, credentials);\n }\n\n private async acquireTokensViaBrowserLogin(\n loginPageUrl: string,\n server: string,\n clientConfig: IClientConfig,\n scope: string,\n navigator: (url: string) => void,\n redirectUriCallback?: (tokens: IOdspTokens) => Promise<string>,\n ): Promise<IOdspTokens> {\n // Start up a local auth redirect handler service to receive the tokens after login\n const tokenGetter = await serverListenAndHandle(odspAuthRedirectPort, async (req, res) => {\n // extract code from request URL and fetch the tokens\n const credentials: TokenRequestCredentials = {\n grant_type: \"authorization_code\",\n code: this.extractAuthorizationCode(req.url),\n redirect_uri: odspAuthRedirectUri,\n };\n const tokens = await fetchTokens(server, scope, clientConfig, credentials);\n\n // redirect now that the browser is done with auth\n if (redirectUriCallback) {\n res.writeHead(301, { Location: await redirectUriCallback(tokens) });\n await endResponse(res);\n } else {\n res.write(\"Please close the window\");\n await endResponse(res);\n }\n\n return tokens;\n });\n\n // Now that our local redirect handler is up, navigate the browser to the login page\n navigator(loginPageUrl);\n\n // Receive and extract the tokens\n const odspTokens = await tokenGetter();\n\n return odspTokens;\n }\n\n private async onTokenRetrievalFromCache(config: OdspTokenConfig, tokens: IOdspTokens) {\n if (config.type === \"browserLogin\" && config.redirectUriCallback) {\n config.navigator(await config.redirectUriCallback(tokens));\n }\n }\n\n private extractAuthorizationCode(relativeUrl: string | undefined): string {\n if (relativeUrl === undefined) {\n throw Error(\"Failed to get authorization\");\n }\n const parsedUrl = new URL(relativeUrl, odspAuthRedirectOrigin);\n const code = parsedUrl.searchParams.get(\"code\");\n if (!code) {\n throw Error(\"Failed to get authorization\");\n }\n return code;\n }\n}\n\nasync function loadAndPatchRC() {\n const rc = await loadRC();\n if (rc.tokens && rc.tokens.version === undefined) {\n // Clean up older versions\n delete (rc as any).tokens;\n delete (rc as any).pushTokens;\n }\n return rc;\n}\n\nexport const odspTokensCache: IAsyncCache<IOdspTokenManagerCacheKey, IOdspTokens> = {\n async get(key: IOdspTokenManagerCacheKey): Promise<IOdspTokens | undefined> {\n const rc = await loadAndPatchRC();\n return rc.tokens?.data[key.userOrServer]?.[key.isPush ? \"push\" : \"storage\"];\n },\n async save(key: IOdspTokenManagerCacheKey, tokens: IOdspTokens): Promise<void> {\n const rc = await loadAndPatchRC();\n if (!rc.tokens) {\n rc.tokens = {\n version: 1,\n data: {},\n };\n }\n let prevTokens = rc.tokens.data[key.userOrServer];\n if (!prevTokens) {\n prevTokens = {};\n rc.tokens.data[key.userOrServer] = prevTokens;\n }\n prevTokens[key.isPush ? \"push\" : \"storage\"] = tokens;\n return saveRC(rc);\n },\n async lock<T>(callback: () => Promise<T>): Promise<T> {\n const release = await lockRC();\n try {\n return await callback();\n } finally {\n await release();\n }\n },\n};\n"]}
|
|
1
|
+
{"version":3,"file":"odspTokenManager.js","sourceRoot":"","sources":["../src/odspTokenManager.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 { unreachableCase } from \"@fluidframework/common-utils\";\nimport {\n IOdspTokens,\n IClientConfig,\n fetchTokens,\n refreshTokens,\n getOdspScope,\n pushScope,\n getLoginPageUrl,\n TokenRequestCredentials,\n} from \"@fluidframework/odsp-doclib-utils\";\nimport jwtDecode from \"jwt-decode\";\nimport { Mutex } from \"async-mutex\";\nimport { debug } from \"./debug\";\nimport { IAsyncCache, loadRC, saveRC, lockRC } from \"./fluidToolRC\";\nimport { serverListenAndHandle, endResponse } from \"./httpHelpers\";\n\nconst odspAuthRedirectPort = 7000;\nconst odspAuthRedirectOrigin = `http://localhost:${odspAuthRedirectPort}`;\nconst odspAuthRedirectUri = new URL(\"/auth/callback\", odspAuthRedirectOrigin).href;\n\nexport const getMicrosoftConfiguration = (): IClientConfig => ({\n get clientId() {\n if (!process.env.login__microsoft__clientId) {\n throw new Error(\"Client ID environment variable not set: login__microsoft__clientId.\");\n }\n return process.env.login__microsoft__clientId;\n },\n get clientSecret() {\n if (!process.env.login__microsoft__secret) {\n throw new Error(\"Client Secret environment variable not set: login__microsoft__secret.\");\n }\n return process.env.login__microsoft__secret;\n },\n});\n\nexport type OdspTokenConfig = {\n type: \"password\";\n username: string;\n password: string;\n} | {\n type: \"browserLogin\";\n navigator: (url: string) => void;\n redirectUriCallback?: (tokens: IOdspTokens) => Promise<string>;\n};\n\nexport interface IOdspTokenManagerCacheKey {\n readonly isPush: boolean;\n readonly userOrServer: string;\n}\n\nconst isValidToken = (token: string) => {\n // Return false for undefined or empty tokens.\n if (!token || token.length === 0) {\n return false;\n }\n\n const decodedToken = jwtDecode<any>(token);\n // Give it a 60s buffer\n return (decodedToken.exp - 60 >= (new Date().getTime() / 1000));\n};\n\nconst cacheKeyToString = (key: IOdspTokenManagerCacheKey) => {\n return `${key.userOrServer}${key.isPush ? \"[Push]\" : \"\"}`;\n};\n\nexport class OdspTokenManager {\n private readonly storageCache = new Map<string, IOdspTokens>();\n private readonly pushCache = new Map<string, IOdspTokens>();\n private readonly cacheMutex = new Mutex();\n constructor(\n private readonly tokenCache?: IAsyncCache<IOdspTokenManagerCacheKey, IOdspTokens>,\n ) { }\n\n public async updateTokensCache(key: IOdspTokenManagerCacheKey, value: IOdspTokens) {\n await this.cacheMutex.runExclusive(async () => {\n await this.updateTokensCacheWithoutLock(key, value);\n });\n }\n\n private async updateTokensCacheWithoutLock(key: IOdspTokenManagerCacheKey, value: IOdspTokens) {\n debug(`${cacheKeyToString(key)}: Saving tokens`);\n const memoryCache = key.isPush ? this.pushCache : this.storageCache;\n memoryCache.set(key.userOrServer, value);\n await this.tokenCache?.save(key, value);\n }\n\n public async getOdspTokens(\n server: string,\n clientConfig: IClientConfig,\n tokenConfig: OdspTokenConfig,\n forceRefresh = false,\n forceReauth = false,\n ): Promise<IOdspTokens> {\n return this.getTokens(\n false,\n server,\n clientConfig,\n tokenConfig,\n forceRefresh,\n forceReauth,\n );\n }\n\n public async getPushTokens(\n server: string,\n clientConfig: IClientConfig,\n tokenConfig: OdspTokenConfig,\n forceRefresh = false,\n forceReauth = false,\n ): Promise<IOdspTokens> {\n return this.getTokens(\n true,\n server,\n clientConfig,\n tokenConfig,\n forceRefresh,\n forceReauth,\n );\n }\n\n private async getTokenFromCache(\n cacheKey: IOdspTokenManagerCacheKey,\n ) {\n const memoryCache = cacheKey.isPush ? this.pushCache : this.storageCache;\n const memoryToken = memoryCache.get(cacheKey.userOrServer);\n if (memoryToken) {\n debug(`${cacheKeyToString(cacheKey)}: Token found in memory `);\n return memoryToken;\n }\n const fileToken = await this.tokenCache?.get(cacheKey);\n if (fileToken) {\n debug(`${cacheKeyToString(cacheKey)}: Token found in file`);\n memoryCache.set(cacheKey.userOrServer, fileToken);\n return fileToken;\n }\n }\n\n private static getCacheKey(\n isPush: boolean,\n tokenConfig: OdspTokenConfig,\n server: string,\n ): IOdspTokenManagerCacheKey {\n // If we are using password, we should cache the token per user instead of per server\n return { isPush, userOrServer: tokenConfig.type === \"password\" ? tokenConfig.username : server };\n }\n\n private async getTokens(\n isPush: boolean,\n server: string,\n clientConfig: IClientConfig,\n tokenConfig: OdspTokenConfig,\n forceRefresh: boolean,\n forceReauth: boolean,\n ): Promise<IOdspTokens> {\n const invokeGetTokensCore = async () => {\n // Don't solely rely on tokenCache lock, ensure serialized execution of\n // cache update to avoid multiple fetch.\n return this.cacheMutex.runExclusive(async () => {\n return this.getTokensCore(\n isPush,\n server,\n clientConfig,\n tokenConfig,\n forceRefresh,\n forceReauth);\n });\n };\n if (!forceReauth && !forceRefresh) {\n // check and return if it exists without lock\n const cacheKey = OdspTokenManager.getCacheKey(isPush, tokenConfig, server);\n const tokensFromCache = await this.getTokenFromCache(cacheKey);\n if (tokensFromCache) {\n if (isValidToken(tokensFromCache.accessToken)) {\n debug(`${cacheKeyToString(cacheKey)}: Token reused from cache `);\n await this.onTokenRetrievalFromCache(tokenConfig, tokensFromCache);\n return tokensFromCache;\n }\n debug(`${cacheKeyToString(cacheKey)}: Token expired from cache `);\n }\n }\n if (this.tokenCache) {\n // check with lock, used to prevent concurrent auth attempts\n return this.tokenCache.lock(invokeGetTokensCore);\n }\n return invokeGetTokensCore();\n }\n\n private async getTokensCore(\n isPush: boolean,\n server: string,\n clientConfig: IClientConfig,\n tokenConfig: OdspTokenConfig,\n forceRefresh,\n forceReauth,\n ): Promise<IOdspTokens> {\n const scope = isPush ? pushScope : getOdspScope(server);\n const cacheKey = OdspTokenManager.getCacheKey(isPush, tokenConfig, server);\n let tokens: IOdspTokens | undefined;\n if (!forceReauth) {\n // check the cache again under the lock (if it is there)\n const tokensFromCache = await this.getTokenFromCache(cacheKey);\n if (tokensFromCache) {\n if (forceRefresh || !isValidToken(tokensFromCache.accessToken)) {\n try {\n // This updates the tokens in tokensFromCache\n tokens = await refreshTokens(server, scope, clientConfig, tokensFromCache);\n await this.updateTokensCacheWithoutLock(cacheKey, tokens);\n } catch (error) {\n debug(`${cacheKeyToString(cacheKey)}: Error in refreshing token. ${error}`);\n }\n } else {\n tokens = tokensFromCache;\n debug(`${cacheKeyToString(cacheKey)}: Token reused from locked cache `);\n }\n }\n }\n\n if (tokens) {\n await this.onTokenRetrievalFromCache(tokenConfig, tokens);\n return tokens;\n }\n\n switch (tokenConfig.type) {\n case \"password\":\n tokens = await this.acquireTokensWithPassword(\n server,\n scope,\n clientConfig,\n tokenConfig.username,\n tokenConfig.password,\n );\n break;\n case \"browserLogin\":\n tokens = await this.acquireTokensViaBrowserLogin(\n getLoginPageUrl(server, clientConfig, scope, odspAuthRedirectUri),\n server,\n clientConfig,\n scope,\n tokenConfig.navigator,\n tokenConfig.redirectUriCallback,\n );\n break;\n default:\n unreachableCase(tokenConfig);\n }\n\n await this.updateTokensCacheWithoutLock(cacheKey, tokens);\n return tokens;\n }\n\n private async acquireTokensWithPassword(\n server: string,\n scope: string,\n clientConfig: IClientConfig,\n username: string,\n password: string,\n ): Promise<IOdspTokens> {\n const credentials: TokenRequestCredentials = {\n grant_type: \"password\",\n username,\n password,\n };\n return fetchTokens(server, scope, clientConfig, credentials);\n }\n\n private async acquireTokensViaBrowserLogin(\n loginPageUrl: string,\n server: string,\n clientConfig: IClientConfig,\n scope: string,\n navigator: (url: string) => void,\n redirectUriCallback?: (tokens: IOdspTokens) => Promise<string>,\n ): Promise<IOdspTokens> {\n // Start up a local auth redirect handler service to receive the tokens after login\n const tokenGetter = await serverListenAndHandle(odspAuthRedirectPort, async (req, res) => {\n // extract code from request URL and fetch the tokens\n const credentials: TokenRequestCredentials = {\n grant_type: \"authorization_code\",\n code: this.extractAuthorizationCode(req.url),\n redirect_uri: odspAuthRedirectUri,\n };\n const tokens = await fetchTokens(server, scope, clientConfig, credentials);\n\n // redirect now that the browser is done with auth\n if (redirectUriCallback) {\n res.writeHead(301, { Location: await redirectUriCallback(tokens) });\n await endResponse(res);\n } else {\n res.write(\"Please close the window\");\n await endResponse(res);\n }\n\n return tokens;\n });\n\n // Now that our local redirect handler is up, navigate the browser to the login page\n navigator(loginPageUrl);\n\n // Receive and extract the tokens\n const odspTokens = await tokenGetter();\n\n return odspTokens;\n }\n\n private async onTokenRetrievalFromCache(config: OdspTokenConfig, tokens: IOdspTokens) {\n if (config.type === \"browserLogin\" && config.redirectUriCallback) {\n config.navigator(await config.redirectUriCallback(tokens));\n }\n }\n\n private extractAuthorizationCode(relativeUrl: string | undefined): string {\n if (relativeUrl === undefined) {\n throw Error(\"Failed to get authorization\");\n }\n const parsedUrl = new URL(relativeUrl, odspAuthRedirectOrigin);\n const code = parsedUrl.searchParams.get(\"code\");\n if (!code) {\n throw Error(\"Failed to get authorization\");\n }\n return code;\n }\n}\n\nasync function loadAndPatchRC() {\n const rc = await loadRC();\n if (rc.tokens && rc.tokens.version === undefined) {\n // Clean up older versions\n delete (rc as any).tokens;\n delete (rc as any).pushTokens;\n }\n return rc;\n}\n\nexport const odspTokensCache: IAsyncCache<IOdspTokenManagerCacheKey, IOdspTokens> = {\n async get(key: IOdspTokenManagerCacheKey): Promise<IOdspTokens | undefined> {\n const rc = await loadAndPatchRC();\n return rc.tokens?.data[key.userOrServer]?.[key.isPush ? \"push\" : \"storage\"];\n },\n async save(key: IOdspTokenManagerCacheKey, tokens: IOdspTokens): Promise<void> {\n const rc = await loadAndPatchRC();\n if (!rc.tokens) {\n rc.tokens = {\n version: 1,\n data: {},\n };\n }\n let prevTokens = rc.tokens.data[key.userOrServer];\n if (!prevTokens) {\n prevTokens = {};\n rc.tokens.data[key.userOrServer] = prevTokens;\n }\n prevTokens[key.isPush ? \"push\" : \"storage\"] = tokens;\n return saveRC(rc);\n },\n async lock<T>(callback: () => Promise<T>): Promise<T> {\n const release = await lockRC();\n try {\n return await callback();\n } finally {\n await release();\n }\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 = "0.59.
|
|
8
|
+
export declare const pkgVersion = "0.59.3000-67119";
|
|
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,oBAAoB,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,iBAAiB,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 = \"0.59.3000-67119\";\n"]}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
import { ITree } from "@fluidframework/protocol-definitions";
|
|
6
|
+
/** The prefix that all GC blob names start with. */
|
|
6
7
|
export declare const gcBlobPrefix = "__gc";
|
|
7
8
|
export interface ISnapshotNormalizerConfig {
|
|
8
9
|
blobsToNormalize?: string[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"snapshotNormalizer.d.ts","sourceRoot":"","sources":["../src/snapshotNormalizer.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EACH,KAAK,EAGR,MAAM,sCAAsC,CAAC;
|
|
1
|
+
{"version":3,"file":"snapshotNormalizer.d.ts","sourceRoot":"","sources":["../src/snapshotNormalizer.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EACH,KAAK,EAGR,MAAM,sCAAsC,CAAC;AAI9C,oDAAoD;AACpD,eAAO,MAAM,YAAY,SAAS,CAAC;AAEnC,MAAM,WAAW,yBAAyB;IAEtC,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B;;;;OAIG;IACH,2BAA2B,CAAC,EAAE,MAAM,EAAE,CAAC;CAC1C;AAoGD;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,KAAK,EAAE,MAAM,CAAC,EAAE,yBAAyB,GAAG,KAAK,CAkBhG"}
|
|
@@ -4,6 +4,9 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { AttachmentTreeEntry, BlobTreeEntry, TreeTreeEntry } from "@fluidframework/protocol-base";
|
|
6
6
|
import { TreeEntry, } from "@fluidframework/protocol-definitions";
|
|
7
|
+
/** The name of the metadata blob added to the root of the container runtime. */
|
|
8
|
+
const metadataBlobName = ".metadata";
|
|
9
|
+
/** The prefix that all GC blob names start with. */
|
|
7
10
|
export const gcBlobPrefix = "__gc";
|
|
8
11
|
/**
|
|
9
12
|
* Function that deep sorts an array. It handles cases where array elements are objects or arrays.
|
|
@@ -71,6 +74,22 @@ function getNormalizedBlobContent(blobContent, blobName) {
|
|
|
71
74
|
}
|
|
72
75
|
content = JSON.stringify(gcState);
|
|
73
76
|
}
|
|
77
|
+
/**
|
|
78
|
+
* The metadata blob has "summaryNumber" or "summaryCount" that tells which summary this is for a container. It can
|
|
79
|
+
* be different in summaries of two clients even if they are generated at the same sequence#. For instance, at seq#
|
|
80
|
+
* 1000, if one client has summarized 10 times and other has summarizer 15 times, summaryNumber will be different
|
|
81
|
+
* for them. So, update "summaryNumber" to 0 for purposes of comparing snapshots.
|
|
82
|
+
*/
|
|
83
|
+
if (blobName === metadataBlobName) {
|
|
84
|
+
const metadata = JSON.parse(content);
|
|
85
|
+
if (metadata.summaryNumber !== undefined) {
|
|
86
|
+
metadata.summaryNumber = 0;
|
|
87
|
+
}
|
|
88
|
+
if (metadata.summaryCount !== undefined) {
|
|
89
|
+
metadata.summaryCount = 0;
|
|
90
|
+
}
|
|
91
|
+
content = JSON.stringify(metadata);
|
|
92
|
+
}
|
|
74
93
|
// Deep sort the content if it's parseable.
|
|
75
94
|
try {
|
|
76
95
|
let contentObj = JSON.parse(content);
|
|
@@ -95,11 +114,14 @@ function getNormalizedBlobContent(blobContent, blobName) {
|
|
|
95
114
|
* @returns a copy of the normalized snapshot tree.
|
|
96
115
|
*/
|
|
97
116
|
export function getNormalizedSnapshot(snapshot, config) {
|
|
117
|
+
var _a;
|
|
98
118
|
// Merge blobs to normalize in the config with runtime blobs to normalize. The contents of these blobs will be
|
|
99
119
|
// parsed and deep sorted.
|
|
100
120
|
const normalizedEntries = [];
|
|
121
|
+
// The metadata blob in the root of the summary tree needs to be normalized.
|
|
122
|
+
const blobsToNormalize = [metadataBlobName, ...(_a = config === null || config === void 0 ? void 0 : config.blobsToNormalize) !== null && _a !== void 0 ? _a : []];
|
|
101
123
|
for (const entry of snapshot.entries) {
|
|
102
|
-
normalizedEntries.push(normalizeEntry(entry, config));
|
|
124
|
+
normalizedEntries.push(normalizeEntry(entry, Object.assign(Object.assign({}, config), { blobsToNormalize })));
|
|
103
125
|
}
|
|
104
126
|
// Sort the tree entries based on their path.
|
|
105
127
|
normalizedEntries.sort((a, b) => a.path.localeCompare(b.path));
|
|
@@ -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\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 // 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 for (const entry of snapshot.entries) {\n normalizedEntries.push(normalizeEntry(entry, config));\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 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"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fluidframework/tool-utils",
|
|
3
|
-
"version": "0.59.
|
|
3
|
+
"version": "0.59.3000-67119",
|
|
4
4
|
"description": "Common utilities for Fluid tools",
|
|
5
5
|
"homepage": "https://fluidframework.com",
|
|
6
6
|
"repository": {
|
|
@@ -62,8 +62,8 @@
|
|
|
62
62
|
},
|
|
63
63
|
"dependencies": {
|
|
64
64
|
"@fluidframework/common-utils": "^0.32.1",
|
|
65
|
-
"@fluidframework/odsp-doclib-utils": "
|
|
66
|
-
"@fluidframework/protocol-base": "^0.1036.
|
|
65
|
+
"@fluidframework/odsp-doclib-utils": "0.59.3000-67119",
|
|
66
|
+
"@fluidframework/protocol-base": "^0.1036.3000-66438",
|
|
67
67
|
"@fluidframework/protocol-definitions": "^0.1028.1000",
|
|
68
68
|
"async-mutex": "^0.3.1",
|
|
69
69
|
"debug": "^4.1.1",
|
|
@@ -72,9 +72,10 @@
|
|
|
72
72
|
},
|
|
73
73
|
"devDependencies": {
|
|
74
74
|
"@fluidframework/build-common": "^0.23.0",
|
|
75
|
-
"@fluidframework/
|
|
76
|
-
"@fluidframework/
|
|
77
|
-
"@fluidframework/
|
|
75
|
+
"@fluidframework/build-tools": "^0.2.66793",
|
|
76
|
+
"@fluidframework/eslint-config-fluid": "^0.28.2000-0",
|
|
77
|
+
"@fluidframework/mocha-test-setup": "0.59.3000-67119",
|
|
78
|
+
"@fluidframework/tool-utils-previous": "npm:@fluidframework/tool-utils@0.59.2000",
|
|
78
79
|
"@microsoft/api-extractor": "^7.22.2",
|
|
79
80
|
"@rushstack/eslint-config": "^2.5.1",
|
|
80
81
|
"@types/debug": "^4.1.5",
|
|
@@ -99,11 +100,11 @@
|
|
|
99
100
|
"mocha": "^8.4.0",
|
|
100
101
|
"nyc": "^15.0.0",
|
|
101
102
|
"rimraf": "^2.6.2",
|
|
102
|
-
"typescript": "~4.
|
|
103
|
+
"typescript": "~4.5.5",
|
|
103
104
|
"typescript-formatter": "7.1.0"
|
|
104
105
|
},
|
|
105
106
|
"typeValidation": {
|
|
106
|
-
"version": "0.59.
|
|
107
|
+
"version": "0.59.3000",
|
|
107
108
|
"broken": {}
|
|
108
109
|
}
|
|
109
110
|
}
|
package/src/fluidToolRC.ts
CHANGED
|
@@ -21,11 +21,11 @@ export interface IResources {
|
|
|
21
21
|
version?: number;
|
|
22
22
|
data: {
|
|
23
23
|
[key: string]: {
|
|
24
|
-
storage?: IOdspTokens
|
|
25
|
-
push?: IOdspTokens
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
}
|
|
24
|
+
storage?: IOdspTokens;
|
|
25
|
+
push?: IOdspTokens;
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
const getRCFileName = () => path.join(os.homedir(), ".fluidtoolrc");
|
|
@@ -58,7 +58,7 @@ export async function lockRC() {
|
|
|
58
58
|
retries: {
|
|
59
59
|
forever: true,
|
|
60
60
|
},
|
|
61
|
-
stale:60000,
|
|
61
|
+
stale: 60000,
|
|
62
62
|
realpath: false,
|
|
63
63
|
});
|
|
64
64
|
}
|
package/src/packageVersion.ts
CHANGED
|
@@ -10,6 +10,9 @@ import {
|
|
|
10
10
|
ITreeEntry,
|
|
11
11
|
} from "@fluidframework/protocol-definitions";
|
|
12
12
|
|
|
13
|
+
/** The name of the metadata blob added to the root of the container runtime. */
|
|
14
|
+
const metadataBlobName = ".metadata";
|
|
15
|
+
/** The prefix that all GC blob names start with. */
|
|
13
16
|
export const gcBlobPrefix = "__gc";
|
|
14
17
|
|
|
15
18
|
export interface ISnapshotNormalizerConfig {
|
|
@@ -91,6 +94,23 @@ function getNormalizedBlobContent(blobContent: string, blobName: string): string
|
|
|
91
94
|
content = JSON.stringify(gcState);
|
|
92
95
|
}
|
|
93
96
|
|
|
97
|
+
/**
|
|
98
|
+
* The metadata blob has "summaryNumber" or "summaryCount" that tells which summary this is for a container. It can
|
|
99
|
+
* be different in summaries of two clients even if they are generated at the same sequence#. For instance, at seq#
|
|
100
|
+
* 1000, if one client has summarized 10 times and other has summarizer 15 times, summaryNumber will be different
|
|
101
|
+
* for them. So, update "summaryNumber" to 0 for purposes of comparing snapshots.
|
|
102
|
+
*/
|
|
103
|
+
if (blobName === metadataBlobName) {
|
|
104
|
+
const metadata = JSON.parse(content);
|
|
105
|
+
if (metadata.summaryNumber !== undefined) {
|
|
106
|
+
metadata.summaryNumber = 0;
|
|
107
|
+
}
|
|
108
|
+
if (metadata.summaryCount !== undefined) {
|
|
109
|
+
metadata.summaryCount = 0;
|
|
110
|
+
}
|
|
111
|
+
content = JSON.stringify(metadata);
|
|
112
|
+
}
|
|
113
|
+
|
|
94
114
|
// Deep sort the content if it's parseable.
|
|
95
115
|
try {
|
|
96
116
|
let contentObj = JSON.parse(content);
|
|
@@ -118,8 +138,10 @@ export function getNormalizedSnapshot(snapshot: ITree, config?: ISnapshotNormali
|
|
|
118
138
|
// parsed and deep sorted.
|
|
119
139
|
const normalizedEntries: ITreeEntry[] = [];
|
|
120
140
|
|
|
141
|
+
// The metadata blob in the root of the summary tree needs to be normalized.
|
|
142
|
+
const blobsToNormalize = [metadataBlobName, ...config?.blobsToNormalize ?? []];
|
|
121
143
|
for (const entry of snapshot.entries) {
|
|
122
|
-
normalizedEntries.push(normalizeEntry(entry, config));
|
|
144
|
+
normalizedEntries.push(normalizeEntry(entry, { ...config, blobsToNormalize }));
|
|
123
145
|
}
|
|
124
146
|
|
|
125
147
|
// Sort the tree entries based on their path.
|
|
@@ -145,13 +167,13 @@ function normalizeEntry(
|
|
|
145
167
|
return new BlobTreeEntry(entry.path, contents);
|
|
146
168
|
}
|
|
147
169
|
case TreeEntry.Tree: {
|
|
148
|
-
if(config?.excludedChannelContentTypes !== undefined) {
|
|
149
|
-
for(const maybeAttributes of entry.value.entries) {
|
|
150
|
-
if(maybeAttributes.type === TreeEntry.Blob && maybeAttributes.path === ".attributes") {
|
|
151
|
-
const parsed: {type?: string} = JSON.parse(maybeAttributes.value.contents);
|
|
152
|
-
if(parsed.type !== undefined && config.excludedChannelContentTypes.includes(parsed.type)) {
|
|
170
|
+
if (config?.excludedChannelContentTypes !== undefined) {
|
|
171
|
+
for (const maybeAttributes of entry.value.entries) {
|
|
172
|
+
if (maybeAttributes.type === TreeEntry.Blob && maybeAttributes.path === ".attributes") {
|
|
173
|
+
const parsed: { type?: string; } = JSON.parse(maybeAttributes.value.contents);
|
|
174
|
+
if (parsed.type !== undefined && config.excludedChannelContentTypes.includes(parsed.type)) {
|
|
153
175
|
// remove everything to match the unknown channel
|
|
154
|
-
return new TreeTreeEntry(entry.path, {entries:[maybeAttributes]});
|
|
176
|
+
return new TreeTreeEntry(entry.path, { entries: [maybeAttributes] });
|
|
155
177
|
}
|
|
156
178
|
}
|
|
157
179
|
}
|