@fluidframework/tool-utils 0.59.2001 → 0.59.3000-66610

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 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,CAAC,kBAAkB,CAAC,CAAC;AACvD,aAAK,CAAC,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
+ {"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,CAAA;aACrB,CAAA;SACJ,CAAA;KACJ,CAAA;CACJ;AAID,wBAAsB,MAAM,IAAI,OAAO,CAAC,UAAU,CAAC,CAclD;AAED,wBAAsB,MAAM,CAAC,EAAE,EAAE,UAAU,iBAI1C;AAED,wBAAsB,MAAM,iBAS3B"}
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"}
@@ -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
  },
@@ -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,CAAC,aAAa,EAAE,EAAE;QACzB,OAAO,EAAE;YACL,OAAO,EAAE,IAAI;SAChB;QACD,KAAK,EAAC,KAAK;QACX,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"]}
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"]}
@@ -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"]}
@@ -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.2001";
8
+ export declare const pkgVersion = "0.59.3000-66610";
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,cAAc,CAAC"}
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"}
@@ -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.2001";
11
+ exports.pkgVersion = "0.59.3000-66610";
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,WAAW,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.2001\";\n"]}
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-66610\";\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;AAE9C,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;AAmFD;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,KAAK,EAAE,MAAM,CAAC,EAAE,yBAAyB,GAAG,KAAK,CAgBhG"}
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"]}
@@ -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,CAAA;aACrB,CAAA;SACJ,CAAA;KACJ,CAAA;CACJ;AAID,wBAAsB,MAAM,IAAI,OAAO,CAAC,UAAU,CAAC,CAclD;AAED,wBAAsB,MAAM,CAAC,EAAE,EAAE,UAAU,iBAI1C;AAED,wBAAsB,MAAM,iBAS3B"}
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"}
@@ -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,EAAC,KAAK;QACX,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
+ {"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"]}
@@ -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.2001";
8
+ export declare const pkgVersion = "0.59.3000-66610";
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,cAAc,CAAC"}
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"}
@@ -5,5 +5,5 @@
5
5
  * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY
6
6
  */
7
7
  export const pkgName = "@fluidframework/tool-utils";
8
- export const pkgVersion = "0.59.2001";
8
+ export const pkgVersion = "0.59.3000-66610";
9
9
  //# sourceMappingURL=packageVersion.js.map
@@ -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,WAAW,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.2001\";\n"]}
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-66610\";\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;AAE9C,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;AAmFD;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,KAAK,EAAE,MAAM,CAAC,EAAE,yBAAyB,GAAG,KAAK,CAgBhG"}
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.2001",
3
+ "version": "0.59.3000-66610",
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": "^0.59.2001",
66
- "@fluidframework/protocol-base": "^0.1036.2000",
65
+ "@fluidframework/odsp-doclib-utils": "0.59.3000-66610",
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/eslint-config-fluid": "^0.28.1000",
76
- "@fluidframework/mocha-test-setup": "^0.59.2001",
77
- "@fluidframework/tool-utils-previous": "npm:@fluidframework/tool-utils@0.59.1000",
75
+ "@fluidframework/build-tools": "^0.2.66048",
76
+ "@fluidframework/eslint-config-fluid": "^0.28.2000-0",
77
+ "@fluidframework/mocha-test-setup": "0.59.3000-66610",
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.1.3",
103
+ "typescript": "~4.5.5",
103
104
  "typescript-formatter": "7.1.0"
104
105
  },
105
106
  "typeValidation": {
106
- "version": "0.59.2000",
107
+ "version": "0.59.3000",
107
108
  "broken": {}
108
109
  }
109
110
  }
@@ -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
  }
@@ -6,4 +6,4 @@
6
6
  */
7
7
 
8
8
  export const pkgName = "@fluidframework/tool-utils";
9
- export const pkgVersion = "0.59.2001";
9
+ export const pkgVersion = "0.59.3000-66610";
@@ -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
  }