@fluidframework/telemetry-utils 1.4.0-121020 → 2.0.0-dev-rc.1.0.0.225277

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (156) hide show
  1. package/.eslintrc.js +12 -13
  2. package/.mocharc.js +12 -0
  3. package/CHANGELOG.md +249 -0
  4. package/README.md +68 -1
  5. package/api-extractor-esm.json +5 -0
  6. package/api-extractor-lint.json +4 -0
  7. package/api-extractor.json +2 -2
  8. package/api-report/telemetry-utils.api.md +444 -0
  9. package/dist/config.d.ts +47 -16
  10. package/dist/config.d.ts.map +1 -1
  11. package/dist/config.js +88 -38
  12. package/dist/config.js.map +1 -1
  13. package/dist/error.d.ts +112 -0
  14. package/dist/error.d.ts.map +1 -0
  15. package/dist/error.js +159 -0
  16. package/dist/error.js.map +1 -0
  17. package/dist/errorLogging.d.ts +86 -20
  18. package/dist/errorLogging.d.ts.map +1 -1
  19. package/dist/errorLogging.js +190 -60
  20. package/dist/errorLogging.js.map +1 -1
  21. package/dist/eventEmitterWithErrorHandling.d.ts +9 -3
  22. package/dist/eventEmitterWithErrorHandling.d.ts.map +1 -1
  23. package/dist/eventEmitterWithErrorHandling.js +16 -3
  24. package/dist/eventEmitterWithErrorHandling.js.map +1 -1
  25. package/dist/events.d.ts +27 -3
  26. package/dist/events.d.ts.map +1 -1
  27. package/dist/events.js +26 -2
  28. package/dist/events.js.map +1 -1
  29. package/dist/fluidErrorBase.d.ts +57 -16
  30. package/dist/fluidErrorBase.d.ts.map +1 -1
  31. package/dist/fluidErrorBase.js +27 -14
  32. package/dist/fluidErrorBase.js.map +1 -1
  33. package/dist/index.d.ts +12 -11
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/index.js +55 -21
  36. package/dist/index.js.map +1 -1
  37. package/dist/logger.d.ts +269 -53
  38. package/dist/logger.d.ts.map +1 -1
  39. package/dist/logger.js +423 -132
  40. package/dist/logger.js.map +1 -1
  41. package/dist/mockLogger.d.ts +39 -12
  42. package/dist/mockLogger.d.ts.map +1 -1
  43. package/dist/mockLogger.js +105 -22
  44. package/dist/mockLogger.js.map +1 -1
  45. package/dist/sampledTelemetryHelper.d.ts +18 -12
  46. package/dist/sampledTelemetryHelper.d.ts.map +1 -1
  47. package/dist/sampledTelemetryHelper.js +28 -19
  48. package/dist/sampledTelemetryHelper.js.map +1 -1
  49. package/dist/telemetry-utils-alpha.d.ts +290 -0
  50. package/dist/telemetry-utils-beta.d.ts +264 -0
  51. package/dist/telemetry-utils-public.d.ts +264 -0
  52. package/dist/telemetry-utils-untrimmed.d.ts +1102 -0
  53. package/dist/telemetryTypes.d.ts +115 -0
  54. package/dist/telemetryTypes.d.ts.map +1 -0
  55. package/dist/telemetryTypes.js +7 -0
  56. package/dist/telemetryTypes.js.map +1 -0
  57. package/dist/thresholdCounter.d.ts +6 -5
  58. package/dist/thresholdCounter.d.ts.map +1 -1
  59. package/dist/thresholdCounter.js +4 -3
  60. package/dist/thresholdCounter.js.map +1 -1
  61. package/dist/tsdoc-metadata.json +11 -0
  62. package/dist/utils.d.ts +54 -3
  63. package/dist/utils.d.ts.map +1 -1
  64. package/dist/utils.js +58 -3
  65. package/dist/utils.js.map +1 -1
  66. package/lib/config.d.ts +47 -16
  67. package/lib/config.d.ts.map +1 -1
  68. package/lib/config.js +85 -36
  69. package/lib/config.js.map +1 -1
  70. package/lib/error.d.ts +112 -0
  71. package/lib/error.d.ts.map +1 -0
  72. package/lib/error.js +150 -0
  73. package/lib/error.js.map +1 -0
  74. package/lib/errorLogging.d.ts +86 -20
  75. package/lib/errorLogging.d.ts.map +1 -1
  76. package/lib/errorLogging.js +189 -60
  77. package/lib/errorLogging.js.map +1 -1
  78. package/lib/eventEmitterWithErrorHandling.d.ts +9 -3
  79. package/lib/eventEmitterWithErrorHandling.d.ts.map +1 -1
  80. package/lib/eventEmitterWithErrorHandling.js +15 -2
  81. package/lib/eventEmitterWithErrorHandling.js.map +1 -1
  82. package/lib/events.d.ts +27 -3
  83. package/lib/events.d.ts.map +1 -1
  84. package/lib/events.js +26 -2
  85. package/lib/events.js.map +1 -1
  86. package/lib/fluidErrorBase.d.ts +57 -16
  87. package/lib/fluidErrorBase.d.ts.map +1 -1
  88. package/lib/fluidErrorBase.js +27 -14
  89. package/lib/fluidErrorBase.js.map +1 -1
  90. package/lib/index.d.ts +12 -11
  91. package/lib/index.d.ts.map +1 -1
  92. package/lib/index.js +11 -11
  93. package/lib/index.js.map +1 -1
  94. package/lib/logger.d.ts +269 -53
  95. package/lib/logger.d.ts.map +1 -1
  96. package/lib/logger.js +415 -131
  97. package/lib/logger.js.map +1 -1
  98. package/lib/mockLogger.d.ts +39 -12
  99. package/lib/mockLogger.d.ts.map +1 -1
  100. package/lib/mockLogger.js +106 -23
  101. package/lib/mockLogger.js.map +1 -1
  102. package/lib/sampledTelemetryHelper.d.ts +18 -12
  103. package/lib/sampledTelemetryHelper.d.ts.map +1 -1
  104. package/lib/sampledTelemetryHelper.js +26 -17
  105. package/lib/sampledTelemetryHelper.js.map +1 -1
  106. package/lib/telemetry-utils-alpha.d.mts +290 -0
  107. package/lib/telemetry-utils-beta.d.mts +264 -0
  108. package/lib/telemetry-utils-public.d.mts +264 -0
  109. package/lib/telemetry-utils-untrimmed.d.mts +1102 -0
  110. package/lib/telemetryTypes.d.ts +115 -0
  111. package/lib/telemetryTypes.d.ts.map +1 -0
  112. package/lib/telemetryTypes.js +6 -0
  113. package/lib/telemetryTypes.js.map +1 -0
  114. package/lib/thresholdCounter.d.ts +6 -5
  115. package/lib/thresholdCounter.d.ts.map +1 -1
  116. package/lib/thresholdCounter.js +4 -3
  117. package/lib/thresholdCounter.js.map +1 -1
  118. package/lib/utils.d.ts +54 -3
  119. package/lib/utils.d.ts.map +1 -1
  120. package/lib/utils.js +56 -2
  121. package/lib/utils.js.map +1 -1
  122. package/package.json +86 -57
  123. package/prettier.config.cjs +8 -0
  124. package/src/config.ts +254 -189
  125. package/src/error.ts +235 -0
  126. package/src/errorLogging.ts +440 -290
  127. package/src/eventEmitterWithErrorHandling.ts +26 -14
  128. package/src/events.ts +54 -25
  129. package/src/fluidErrorBase.ts +94 -46
  130. package/src/index.ts +76 -17
  131. package/src/logger.ts +972 -505
  132. package/src/mockLogger.ts +225 -83
  133. package/src/sampledTelemetryHelper.ts +136 -128
  134. package/src/telemetryTypes.ts +140 -0
  135. package/src/thresholdCounter.ts +38 -37
  136. package/src/utils.ts +108 -17
  137. package/tsconfig.esnext.json +6 -6
  138. package/tsconfig.json +9 -13
  139. package/dist/debugLogger.d.ts +0 -39
  140. package/dist/debugLogger.d.ts.map +0 -1
  141. package/dist/debugLogger.js +0 -101
  142. package/dist/debugLogger.js.map +0 -1
  143. package/dist/packageVersion.d.ts +0 -9
  144. package/dist/packageVersion.d.ts.map +0 -1
  145. package/dist/packageVersion.js +0 -12
  146. package/dist/packageVersion.js.map +0 -1
  147. package/lib/debugLogger.d.ts +0 -39
  148. package/lib/debugLogger.d.ts.map +0 -1
  149. package/lib/debugLogger.js +0 -97
  150. package/lib/debugLogger.js.map +0 -1
  151. package/lib/packageVersion.d.ts +0 -9
  152. package/lib/packageVersion.d.ts.map +0 -1
  153. package/lib/packageVersion.js +0 -9
  154. package/lib/packageVersion.js.map +0 -1
  155. package/src/debugLogger.ts +0 -126
  156. package/src/packageVersion.ts +0 -9
package/lib/config.js CHANGED
@@ -1,8 +1,11 @@
1
- import { Lazy } from "@fluidframework/common-utils";
1
+ import { Lazy } from "@fluidframework/core-utils";
2
+ import { createChildLogger, tagCodeArtifacts } from "./logger";
2
3
  /**
3
4
  * Creates a base configuration provider based on `sessionStorage`
4
5
  *
5
6
  * @returns A lazy initialized base configuration provider with `sessionStorage` as the underlying config store
7
+ *
8
+ * @internal
6
9
  */
7
10
  export const sessionStorageConfigProvider = new Lazy(() => inMemoryConfigProvider(safeSessionStorage()));
8
11
  const NullConfigProvider = {
@@ -17,14 +20,14 @@ const NullConfigProvider = {
17
20
  */
18
21
  export const inMemoryConfigProvider = (storage) => {
19
22
  if (storage !== undefined && storage !== null) {
20
- return new CachedConfigProvider({
23
+ return new CachedConfigProvider(undefined, {
21
24
  getRawConfig: (name) => {
22
- var _a, _b;
23
25
  try {
24
- return (_b = stronglyTypedParse((_a = storage.getItem(name)) !== null && _a !== void 0 ? _a : undefined)) === null || _b === void 0 ? void 0 : _b.raw;
26
+ return stronglyTypedParse(storage.getItem(name) ?? undefined)?.raw;
27
+ }
28
+ catch {
29
+ return undefined;
25
30
  }
26
- catch (_c) { }
27
- return undefined;
28
31
  },
29
32
  });
30
33
  }
@@ -34,10 +37,12 @@ function isPrimitiveType(type) {
34
37
  switch (type) {
35
38
  case "boolean":
36
39
  case "number":
37
- case "string":
40
+ case "string": {
38
41
  return true;
39
- default:
42
+ }
43
+ default: {
40
44
  return false;
45
+ }
41
46
  }
42
47
  }
43
48
  /**
@@ -68,14 +73,16 @@ function stronglyTypedParse(input) {
68
73
  // casting.
69
74
  defaultReturn = { raw: input, string: input };
70
75
  }
71
- catch (_a) { }
76
+ catch {
77
+ // No-op
78
+ }
72
79
  }
73
80
  if (output === undefined) {
74
81
  return defaultReturn;
75
82
  }
76
83
  const outputType = typeof output;
77
84
  if (isPrimitiveType(outputType)) {
78
- return Object.assign(Object.assign({}, defaultReturn), { raw: input, [outputType]: output });
85
+ return { ...defaultReturn, raw: input, [outputType]: output };
79
86
  }
80
87
  if (Array.isArray(output)) {
81
88
  const firstType = typeof output[0];
@@ -90,16 +97,25 @@ function stronglyTypedParse(input) {
90
97
  return defaultReturn;
91
98
  }
92
99
  }
93
- return Object.assign(Object.assign({}, defaultReturn), { raw: input, [`${firstType}[]`]: output });
100
+ return { ...defaultReturn, raw: input, [`${firstType}[]`]: output };
94
101
  }
95
102
  return defaultReturn;
96
103
  }
97
- /** Referencing the `sessionStorage` variable can throw in some environments such as Node */
104
+ /**
105
+ * `sessionStorage` is undefined in some environments such as Node and web pages with session storage disabled.
106
+ */
98
107
  const safeSessionStorage = () => {
108
+ // For some configurations accessing "globalThis.sessionStorage" throws
109
+ // "'sessionStorage' property from 'Window': Access is denied for this document" rather than returning undefined.
110
+ // Therefor check for it before accessing.
99
111
  try {
100
- return sessionStorage !== null ? sessionStorage : undefined;
112
+ // Using globalThis and checking for undefined is preferred over just accessing global sessionStorage
113
+ // since it avoids an exception when running in node.
114
+ // In some cases this has returned null when disabled in the browser, so ensure its undefined in that case:
115
+ return globalThis.sessionStorage ?? undefined;
101
116
  }
102
- catch (_a) {
117
+ catch {
118
+ // For browsers which error on the above when session storage is disabled:
103
119
  return undefined;
104
120
  }
105
121
  };
@@ -107,16 +123,17 @@ const safeSessionStorage = () => {
107
123
  * Implementation of {@link IConfigProvider} which contains nested {@link IConfigProviderBase} instances
108
124
  */
109
125
  export class CachedConfigProvider {
110
- constructor(...orderedBaseProviders) {
126
+ constructor(logger, ...orderedBaseProviders) {
127
+ this.logger = logger;
111
128
  this.configCache = new Map();
112
129
  this.orderedBaseProviders = [];
113
130
  const knownProviders = new Set();
114
131
  const candidateProviders = [...orderedBaseProviders];
115
132
  while (candidateProviders.length > 0) {
116
133
  const baseProvider = candidateProviders.shift();
117
- if (baseProvider !== undefined
118
- && isConfigProviderBase(baseProvider)
119
- && !knownProviders.has(baseProvider)) {
134
+ if (baseProvider !== undefined &&
135
+ isConfigProviderBase(baseProvider) &&
136
+ !knownProviders.has(baseProvider)) {
120
137
  knownProviders.add(baseProvider);
121
138
  if (baseProvider instanceof CachedConfigProvider) {
122
139
  candidateProviders.push(...baseProvider.orderedBaseProviders);
@@ -128,39 +145,40 @@ export class CachedConfigProvider {
128
145
  }
129
146
  }
130
147
  getBoolean(name) {
131
- var _a;
132
- return (_a = this.getCacheEntry(name)) === null || _a === void 0 ? void 0 : _a.boolean;
148
+ return this.getCacheEntry(name)?.boolean;
133
149
  }
134
150
  getNumber(name) {
135
- var _a;
136
- return (_a = this.getCacheEntry(name)) === null || _a === void 0 ? void 0 : _a.number;
151
+ return this.getCacheEntry(name)?.number;
137
152
  }
138
153
  getString(name) {
139
- var _a;
140
- return (_a = this.getCacheEntry(name)) === null || _a === void 0 ? void 0 : _a.string;
154
+ return this.getCacheEntry(name)?.string;
141
155
  }
142
156
  getBooleanArray(name) {
143
- var _a;
144
- return (_a = this.getCacheEntry(name)) === null || _a === void 0 ? void 0 : _a["boolean[]"];
157
+ return this.getCacheEntry(name)?.["boolean[]"];
145
158
  }
146
159
  getNumberArray(name) {
147
- var _a;
148
- return (_a = this.getCacheEntry(name)) === null || _a === void 0 ? void 0 : _a["number[]"];
160
+ return this.getCacheEntry(name)?.["number[]"];
149
161
  }
150
162
  getStringArray(name) {
151
- var _a;
152
- return (_a = this.getCacheEntry(name)) === null || _a === void 0 ? void 0 : _a["string[]"];
163
+ return this.getCacheEntry(name)?.["string[]"];
153
164
  }
154
165
  getRawConfig(name) {
155
- var _a;
156
- return (_a = this.getCacheEntry(name)) === null || _a === void 0 ? void 0 : _a.raw;
166
+ return this.getCacheEntry(name)?.raw;
157
167
  }
158
168
  getCacheEntry(name) {
159
169
  if (!this.configCache.has(name)) {
160
170
  for (const provider of this.orderedBaseProviders) {
161
- const parsed = stronglyTypedParse(provider === null || provider === void 0 ? void 0 : provider.getRawConfig(name));
171
+ const parsed = stronglyTypedParse(provider?.getRawConfig(name));
162
172
  if (parsed !== undefined) {
163
173
  this.configCache.set(name, parsed);
174
+ this.logger?.send({
175
+ category: "generic",
176
+ eventName: "ConfigRead",
177
+ ...tagCodeArtifacts({
178
+ configName: name,
179
+ configValue: JSON.stringify(parsed),
180
+ }),
181
+ });
164
182
  return parsed;
165
183
  }
166
184
  }
@@ -170,16 +188,38 @@ export class CachedConfigProvider {
170
188
  return this.configCache.get(name);
171
189
  }
172
190
  }
191
+ /**
192
+ * Determines whether or not the provided object is a {@link MonitoringContext}.
193
+ * @remarks Can be used for type-narrowing.
194
+ *
195
+ * @internal
196
+ */
173
197
  export function loggerIsMonitoringContext(obj) {
174
198
  const maybeConfig = obj;
175
- return isConfigProviderBase(maybeConfig === null || maybeConfig === void 0 ? void 0 : maybeConfig.config) && (maybeConfig === null || maybeConfig === void 0 ? void 0 : maybeConfig.logger) !== undefined;
199
+ return isConfigProviderBase(maybeConfig?.config) && maybeConfig?.logger !== undefined;
176
200
  }
201
+ /**
202
+ * Creates a {@link MonitoringContext} from the provided logger, if it isn't already one.
203
+ *
204
+ * @internal
205
+ */
177
206
  export function loggerToMonitoringContext(logger) {
178
207
  if (loggerIsMonitoringContext(logger)) {
179
208
  return logger;
180
209
  }
181
210
  return mixinMonitoringContext(logger, sessionStorageConfigProvider.value);
182
211
  }
212
+ /**
213
+ * Creates a {@link MonitoringContext} from the provided logger.
214
+ *
215
+ * @remarks
216
+ * Assumes that the provided logger is not itself already a {@link MonitoringContext}, and will throw an error if it is.
217
+ * If you are unsure, use {@link loggerToMonitoringContext} instead.
218
+ *
219
+ * @throws If the provided logger is already a {@link MonitoringContext}.
220
+ *
221
+ * @internal
222
+ */
183
223
  export function mixinMonitoringContext(logger, ...configs) {
184
224
  if (loggerIsMonitoringContext(logger)) {
185
225
  throw new Error("Logger is already a monitoring context");
@@ -193,12 +233,21 @@ export function mixinMonitoringContext(logger, ...configs) {
193
233
  * of the MonitoringContext and get the config provider.
194
234
  */
195
235
  const mc = logger;
196
- mc.config = new CachedConfigProvider(...configs);
236
+ mc.config = new CachedConfigProvider(logger, ...configs);
197
237
  mc.logger = logger;
198
238
  return mc;
199
239
  }
200
240
  function isConfigProviderBase(obj) {
201
241
  const maybeConfig = obj;
202
- return typeof (maybeConfig === null || maybeConfig === void 0 ? void 0 : maybeConfig.getRawConfig) === "function";
242
+ return typeof maybeConfig?.getRawConfig === "function";
243
+ }
244
+ /**
245
+ * Creates a child logger with a {@link MonitoringContext}.
246
+ *
247
+ * @see {@link loggerToMonitoringContext}
248
+ * @internal
249
+ */
250
+ export function createChildMonitoringContext(props) {
251
+ return loggerToMonitoringContext(createChildLogger(props));
203
252
  }
204
253
  //# sourceMappingURL=config.js.map
package/lib/config.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.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 */\nimport { ITelemetryBaseLogger, ITelemetryLogger } from \"@fluidframework/common-definitions\";\nimport { Lazy } from \"@fluidframework/common-utils\";\n\nexport type ConfigTypes = string | number | boolean | number[] | string[] | boolean[] | undefined;\n\n/**\n * Base interface for providing configurations to enable/disable/control features\n */\nexport interface IConfigProviderBase {\n getRawConfig(name: string): ConfigTypes;\n}\n\n/**\n * Explicitly typed interface for reading configurations\n */\n export interface IConfigProvider extends IConfigProviderBase {\n getBoolean(name: string): boolean | undefined;\n getNumber(name: string): number | undefined;\n getString(name: string): string | undefined;\n getBooleanArray(name: string): boolean[] | undefined;\n getNumberArray(name: string): number[] | undefined;\n getStringArray(name: string): string[] | undefined;\n }\n/**\n * Creates a base configuration provider based on `sessionStorage`\n *\n * @returns A lazy initialized base configuration provider with `sessionStorage` as the underlying config store\n */\nexport const sessionStorageConfigProvider =\n new Lazy<IConfigProviderBase>(() => inMemoryConfigProvider(safeSessionStorage()));\n\nconst NullConfigProvider: IConfigProviderBase = {\n getRawConfig: () => undefined,\n};\n\n/**\n * Creates a base configuration provider based on the supplied `Storage` instance\n *\n * @param storage - instance of `Storage` to be used as storage media for the config\n * @returns A base configuration provider with\n * the supplied `Storage` instance as the underlying config store\n */\nexport const inMemoryConfigProvider =\n (storage: Storage | undefined): IConfigProviderBase => {\n if (storage !== undefined && storage !== null) {\n return new CachedConfigProvider({\n getRawConfig: (name: string) => {\n try {\n return stronglyTypedParse(storage.getItem(name) ?? undefined)?.raw;\n } catch { }\n return undefined;\n },\n });\n }\n return NullConfigProvider;\n};\n\ninterface ConfigTypeStringToType {\n number: number;\n string: string;\n boolean: boolean;\n [\"number[]\"]: number[];\n [\"string[]\"]: string[];\n [\"boolean[]\"]: boolean[];\n}\n\ntype PrimitiveTypeStrings = \"number\" | \"string\" | \"boolean\";\n\nfunction isPrimitiveType(type: string): type is PrimitiveTypeStrings {\n switch (type) {\n case \"boolean\":\n case \"number\":\n case \"string\":\n return true;\n default:\n return false;\n }\n}\n\ninterface StronglyTypedValue extends Partial<ConfigTypeStringToType> {\n raw: ConfigTypes;\n}\n/**\n * Takes any supported config type, and returns the value with a strong type. If the type of\n * the config is not a supported type undefined will be returned.\n * The user of this function should cache the result to avoid duplicated work.\n *\n * Strings will be attempted to be parsed and coerced into a strong config type.\n * if it is not possible to parsed and coerce a string to a strong config type the original string\n * will be return with a string type for the consumer to handle further if necessary.\n */\nfunction stronglyTypedParse(input: ConfigTypes): StronglyTypedValue | undefined {\n let output: ConfigTypes = input;\n let defaultReturn: Pick<StronglyTypedValue, \"raw\" | \"string\"> | undefined;\n // we do special handling for strings to try and coerce\n // them into a config type if we can. This makes it easy\n // for config sources like sessionStorage which only\n // holds strings\n if (typeof input === \"string\") {\n try {\n output = JSON.parse(input);\n // we succeeded in parsing, but we don't support parsing\n // for any object as we can't do it type safely\n // so in this case, the default return will be string\n // rather than undefined, and the consumer\n // can parse, as we don't want to provide\n // a false sense of security by just\n // casting.\n defaultReturn = { raw: input, string: input };\n } catch { }\n }\n\n if (output === undefined) {\n return defaultReturn;\n }\n\n const outputType = typeof output;\n if (isPrimitiveType(outputType)) {\n return { ...defaultReturn, raw: input, [outputType]: output };\n }\n\n if (Array.isArray(output)) {\n const firstType = typeof output[0];\n // ensure the first elements is a primitive type\n if (!isPrimitiveType(firstType)) {\n return defaultReturn;\n }\n // ensue all the elements types are homogeneous\n // aka they all have the same type as the first\n for (const v of output) {\n if (typeof v !== firstType) {\n return defaultReturn;\n }\n }\n return { ...defaultReturn, raw: input, [`${firstType}[]`]: output };\n }\n\n return defaultReturn;\n}\n\n/** Referencing the `sessionStorage` variable can throw in some environments such as Node */\nconst safeSessionStorage = (): Storage | undefined => {\n try {\n return sessionStorage !== null ? sessionStorage : undefined;\n } catch { return undefined; }\n};\n\n/**\n * Implementation of {@link IConfigProvider} which contains nested {@link IConfigProviderBase} instances\n */\nexport class CachedConfigProvider implements IConfigProvider {\n private readonly configCache = new Map<string, StronglyTypedValue>();\n private readonly orderedBaseProviders: (IConfigProviderBase | undefined)[];\n\n constructor(\n ... orderedBaseProviders: (IConfigProviderBase | undefined)[]\n ) {\n this.orderedBaseProviders = [];\n const knownProviders = new Set<IConfigProviderBase>();\n const candidateProviders = [...orderedBaseProviders];\n while (candidateProviders.length > 0) {\n const baseProvider = candidateProviders.shift()!;\n if (baseProvider !== undefined\n && isConfigProviderBase(baseProvider)\n && !knownProviders.has(baseProvider)\n ) {\n knownProviders.add(baseProvider);\n if (baseProvider instanceof CachedConfigProvider) {\n candidateProviders.push(...baseProvider.orderedBaseProviders);\n } else {\n this.orderedBaseProviders.push(baseProvider);\n }\n }\n }\n }\n getBoolean(name: string): boolean | undefined {\n return this.getCacheEntry(name)?.boolean;\n }\n getNumber(name: string): number | undefined {\n return this.getCacheEntry(name)?.number;\n }\n getString(name: string): string | undefined {\n return this.getCacheEntry(name)?.string;\n }\n getBooleanArray(name: string): boolean[] | undefined {\n return this.getCacheEntry(name)?.[\"boolean[]\"];\n }\n getNumberArray(name: string): number[] | undefined {\n return this.getCacheEntry(name)?.[\"number[]\"];\n }\n getStringArray(name: string): string[] | undefined {\n return this.getCacheEntry(name)?.[\"string[]\"];\n }\n\n getRawConfig(name: string): ConfigTypes {\n return this.getCacheEntry(name)?.raw;\n }\n\n private getCacheEntry(name: string): StronglyTypedValue | undefined {\n if (!this.configCache.has(name)) {\n for (const provider of this.orderedBaseProviders) {\n const parsed = stronglyTypedParse(provider?.getRawConfig(name));\n if (parsed !== undefined) {\n this.configCache.set(name, parsed);\n return parsed;\n }\n }\n // configs are immutable, if the first lookup returned no results, all lookups should\n this.configCache.set(name, { raw: undefined });\n }\n return this.configCache.get(name);\n }\n}\n\n/**\n * A type containing both a telemetry logger and a configuration provider\n */\nexport interface MonitoringContext<\n L extends ITelemetryBaseLogger = ITelemetryLogger,\n> {\n config: IConfigProvider;\n logger: L;\n}\n\nexport function loggerIsMonitoringContext<L extends ITelemetryBaseLogger = ITelemetryLogger>(\n obj: L): obj is L & MonitoringContext<L> {\n const maybeConfig = obj as Partial<MonitoringContext<L>> | undefined;\n return isConfigProviderBase(maybeConfig?.config) && maybeConfig?.logger !== undefined;\n}\n\nexport function loggerToMonitoringContext<L extends ITelemetryBaseLogger = ITelemetryLogger>(\n logger: L): MonitoringContext<L> {\n if (loggerIsMonitoringContext<L>(logger)) {\n return logger;\n }\n return mixinMonitoringContext<L>(logger, sessionStorageConfigProvider.value);\n}\n\nexport function mixinMonitoringContext<L extends ITelemetryBaseLogger = ITelemetryLogger>(\n logger: L, ... configs: (IConfigProviderBase | undefined)[]) {\n if (loggerIsMonitoringContext<L>(logger)) {\n throw new Error(\"Logger is already a monitoring context\");\n }\n /**\n * this is the tricky bit we use for now to smuggle monitoring context around.\n * To the logger we mixin both config and itself, so mc.logger === logger as it is self-referential.\n * We then expose it as a Monitoring context, so via types we hide the outer logger methods.\n * To layers that expect just a logger we can pass mc.logger, but this is still a MonitoringContext\n * so if a deeper layer then converts that logger to a monitoring context it can find the smuggled properties\n * of the MonitoringContext and get the config provider.\n */\n const mc: L & Partial<MonitoringContext<L>> = logger;\n mc.config = new CachedConfigProvider(...configs);\n mc.logger = logger;\n return mc as MonitoringContext<L>;\n}\n\nfunction isConfigProviderBase(obj: unknown): obj is IConfigProviderBase {\n const maybeConfig = obj as Partial<IConfigProviderBase> | undefined;\n return typeof (maybeConfig?.getRawConfig) === \"function\";\n}\n"]}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.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 */\nimport {\n\tITelemetryBaseLogger,\n\tIConfigProviderBase,\n\tConfigTypes,\n} from \"@fluidframework/core-interfaces\";\nimport { Lazy } from \"@fluidframework/core-utils\";\nimport { createChildLogger, tagCodeArtifacts } from \"./logger\";\nimport { ITelemetryLoggerExt } from \"./telemetryTypes\";\n\n/**\n * Explicitly typed interface for reading configurations.\n *\n * @internal\n */\nexport interface IConfigProvider extends IConfigProviderBase {\n\tgetBoolean(name: string): boolean | undefined;\n\tgetNumber(name: string): number | undefined;\n\tgetString(name: string): string | undefined;\n\tgetBooleanArray(name: string): boolean[] | undefined;\n\tgetNumberArray(name: string): number[] | undefined;\n\tgetStringArray(name: string): string[] | undefined;\n}\n/**\n * Creates a base configuration provider based on `sessionStorage`\n *\n * @returns A lazy initialized base configuration provider with `sessionStorage` as the underlying config store\n *\n * @internal\n */\nexport const sessionStorageConfigProvider = new Lazy<IConfigProviderBase>(() =>\n\tinMemoryConfigProvider(safeSessionStorage()),\n);\n\nconst NullConfigProvider: IConfigProviderBase = {\n\tgetRawConfig: () => undefined,\n};\n\n/**\n * Creates a base configuration provider based on the supplied `Storage` instance\n *\n * @param storage - instance of `Storage` to be used as storage media for the config\n * @returns A base configuration provider with\n * the supplied `Storage` instance as the underlying config store\n */\nexport const inMemoryConfigProvider = (storage: Storage | undefined): IConfigProviderBase => {\n\tif (storage !== undefined && storage !== null) {\n\t\treturn new CachedConfigProvider(undefined, {\n\t\t\tgetRawConfig: (name: string): ConfigTypes | undefined => {\n\t\t\t\ttry {\n\t\t\t\t\treturn stronglyTypedParse(storage.getItem(name) ?? undefined)?.raw;\n\t\t\t\t} catch {\n\t\t\t\t\treturn undefined;\n\t\t\t\t}\n\t\t\t},\n\t\t});\n\t}\n\treturn NullConfigProvider;\n};\n\ninterface ConfigTypeStringToType {\n\tnumber: number;\n\tstring: string;\n\tboolean: boolean;\n\t[\"number[]\"]: number[];\n\t[\"string[]\"]: string[];\n\t[\"boolean[]\"]: boolean[];\n}\n\ntype PrimitiveTypeStrings = \"number\" | \"string\" | \"boolean\";\n\nfunction isPrimitiveType(type: string): type is PrimitiveTypeStrings {\n\tswitch (type) {\n\t\tcase \"boolean\":\n\t\tcase \"number\":\n\t\tcase \"string\": {\n\t\t\treturn true;\n\t\t}\n\t\tdefault: {\n\t\t\treturn false;\n\t\t}\n\t}\n}\n\ninterface StronglyTypedValue extends Partial<ConfigTypeStringToType> {\n\traw: ConfigTypes;\n}\n/**\n * Takes any supported config type, and returns the value with a strong type. If the type of\n * the config is not a supported type undefined will be returned.\n * The user of this function should cache the result to avoid duplicated work.\n *\n * Strings will be attempted to be parsed and coerced into a strong config type.\n * if it is not possible to parsed and coerce a string to a strong config type the original string\n * will be return with a string type for the consumer to handle further if necessary.\n */\nfunction stronglyTypedParse(input: ConfigTypes): StronglyTypedValue | undefined {\n\tlet output: ConfigTypes = input;\n\tlet defaultReturn: Pick<StronglyTypedValue, \"raw\" | \"string\"> | undefined;\n\t// we do special handling for strings to try and coerce\n\t// them into a config type if we can. This makes it easy\n\t// for config sources like sessionStorage which only\n\t// holds strings\n\tif (typeof input === \"string\") {\n\t\ttry {\n\t\t\toutput = JSON.parse(input) as ConfigTypes;\n\t\t\t// we succeeded in parsing, but we don't support parsing\n\t\t\t// for any object as we can't do it type safely\n\t\t\t// so in this case, the default return will be string\n\t\t\t// rather than undefined, and the consumer\n\t\t\t// can parse, as we don't want to provide\n\t\t\t// a false sense of security by just\n\t\t\t// casting.\n\t\t\tdefaultReturn = { raw: input, string: input };\n\t\t} catch {\n\t\t\t// No-op\n\t\t}\n\t}\n\n\tif (output === undefined) {\n\t\treturn defaultReturn;\n\t}\n\n\tconst outputType = typeof output;\n\tif (isPrimitiveType(outputType)) {\n\t\treturn { ...defaultReturn, raw: input, [outputType]: output };\n\t}\n\n\tif (Array.isArray(output)) {\n\t\tconst firstType = typeof output[0];\n\t\t// ensure the first elements is a primitive type\n\t\tif (!isPrimitiveType(firstType)) {\n\t\t\treturn defaultReturn;\n\t\t}\n\t\t// ensue all the elements types are homogeneous\n\t\t// aka they all have the same type as the first\n\t\tfor (const v of output) {\n\t\t\tif (typeof v !== firstType) {\n\t\t\t\treturn defaultReturn;\n\t\t\t}\n\t\t}\n\t\treturn { ...defaultReturn, raw: input, [`${firstType}[]`]: output };\n\t}\n\n\treturn defaultReturn;\n}\n\n/**\n * `sessionStorage` is undefined in some environments such as Node and web pages with session storage disabled.\n */\nconst safeSessionStorage = (): Storage | undefined => {\n\t// For some configurations accessing \"globalThis.sessionStorage\" throws\n\t// \"'sessionStorage' property from 'Window': Access is denied for this document\" rather than returning undefined.\n\t// Therefor check for it before accessing.\n\ttry {\n\t\t// Using globalThis and checking for undefined is preferred over just accessing global sessionStorage\n\t\t// since it avoids an exception when running in node.\n\t\t// In some cases this has returned null when disabled in the browser, so ensure its undefined in that case:\n\t\treturn globalThis.sessionStorage ?? undefined;\n\t} catch {\n\t\t// For browsers which error on the above when session storage is disabled:\n\t\treturn undefined;\n\t}\n};\n\n/**\n * Implementation of {@link IConfigProvider} which contains nested {@link IConfigProviderBase} instances\n */\nexport class CachedConfigProvider implements IConfigProvider {\n\tprivate readonly configCache = new Map<string, StronglyTypedValue>();\n\tprivate readonly orderedBaseProviders: (IConfigProviderBase | undefined)[];\n\n\tconstructor(\n\t\tprivate readonly logger?: ITelemetryBaseLogger,\n\t\t...orderedBaseProviders: (IConfigProviderBase | undefined)[]\n\t) {\n\t\tthis.orderedBaseProviders = [];\n\t\tconst knownProviders = new Set<IConfigProviderBase>();\n\t\tconst candidateProviders = [...orderedBaseProviders];\n\t\twhile (candidateProviders.length > 0) {\n\t\t\tconst baseProvider = candidateProviders.shift()!;\n\t\t\tif (\n\t\t\t\tbaseProvider !== undefined &&\n\t\t\t\tisConfigProviderBase(baseProvider) &&\n\t\t\t\t!knownProviders.has(baseProvider)\n\t\t\t) {\n\t\t\t\tknownProviders.add(baseProvider);\n\t\t\t\tif (baseProvider instanceof CachedConfigProvider) {\n\t\t\t\t\tcandidateProviders.push(...baseProvider.orderedBaseProviders);\n\t\t\t\t} else {\n\t\t\t\t\tthis.orderedBaseProviders.push(baseProvider);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tgetBoolean(name: string): boolean | undefined {\n\t\treturn this.getCacheEntry(name)?.boolean;\n\t}\n\tgetNumber(name: string): number | undefined {\n\t\treturn this.getCacheEntry(name)?.number;\n\t}\n\tgetString(name: string): string | undefined {\n\t\treturn this.getCacheEntry(name)?.string;\n\t}\n\tgetBooleanArray(name: string): boolean[] | undefined {\n\t\treturn this.getCacheEntry(name)?.[\"boolean[]\"];\n\t}\n\tgetNumberArray(name: string): number[] | undefined {\n\t\treturn this.getCacheEntry(name)?.[\"number[]\"];\n\t}\n\tgetStringArray(name: string): string[] | undefined {\n\t\treturn this.getCacheEntry(name)?.[\"string[]\"];\n\t}\n\n\tgetRawConfig(name: string): ConfigTypes {\n\t\treturn this.getCacheEntry(name)?.raw;\n\t}\n\n\tprivate getCacheEntry(name: string): StronglyTypedValue | undefined {\n\t\tif (!this.configCache.has(name)) {\n\t\t\tfor (const provider of this.orderedBaseProviders) {\n\t\t\t\tconst parsed = stronglyTypedParse(provider?.getRawConfig(name));\n\t\t\t\tif (parsed !== undefined) {\n\t\t\t\t\tthis.configCache.set(name, parsed);\n\t\t\t\t\tthis.logger?.send({\n\t\t\t\t\t\tcategory: \"generic\",\n\t\t\t\t\t\teventName: \"ConfigRead\",\n\t\t\t\t\t\t...tagCodeArtifacts({\n\t\t\t\t\t\t\tconfigName: name,\n\t\t\t\t\t\t\tconfigValue: JSON.stringify(parsed),\n\t\t\t\t\t\t}),\n\t\t\t\t\t});\n\t\t\t\t\treturn parsed;\n\t\t\t\t}\n\t\t\t}\n\t\t\t// configs are immutable, if the first lookup returned no results, all lookups should\n\t\t\tthis.configCache.set(name, { raw: undefined });\n\t\t}\n\t\treturn this.configCache.get(name);\n\t}\n}\n\n/**\n * A type containing both a telemetry logger and a configuration provider.\n *\n * @internal\n */\nexport interface MonitoringContext<L extends ITelemetryBaseLogger = ITelemetryLoggerExt> {\n\tconfig: IConfigProvider;\n\tlogger: L;\n}\n\n/**\n * Determines whether or not the provided object is a {@link MonitoringContext}.\n * @remarks Can be used for type-narrowing.\n *\n * @internal\n */\nexport function loggerIsMonitoringContext<L extends ITelemetryBaseLogger = ITelemetryLoggerExt>(\n\tobj: L,\n): obj is L & MonitoringContext<L> {\n\tconst maybeConfig = obj as Partial<MonitoringContext<L>> | undefined;\n\treturn isConfigProviderBase(maybeConfig?.config) && maybeConfig?.logger !== undefined;\n}\n\n/**\n * Creates a {@link MonitoringContext} from the provided logger, if it isn't already one.\n *\n * @internal\n */\nexport function loggerToMonitoringContext<L extends ITelemetryBaseLogger = ITelemetryLoggerExt>(\n\tlogger: L,\n): MonitoringContext<L> {\n\tif (loggerIsMonitoringContext<L>(logger)) {\n\t\treturn logger;\n\t}\n\treturn mixinMonitoringContext<L>(logger, sessionStorageConfigProvider.value);\n}\n\n/**\n * Creates a {@link MonitoringContext} from the provided logger.\n *\n * @remarks\n * Assumes that the provided logger is not itself already a {@link MonitoringContext}, and will throw an error if it is.\n * If you are unsure, use {@link loggerToMonitoringContext} instead.\n *\n * @throws If the provided logger is already a {@link MonitoringContext}.\n *\n * @internal\n */\nexport function mixinMonitoringContext<L extends ITelemetryBaseLogger = ITelemetryLoggerExt>(\n\tlogger: L,\n\t...configs: (IConfigProviderBase | undefined)[]\n): MonitoringContext<L> {\n\tif (loggerIsMonitoringContext<L>(logger)) {\n\t\tthrow new Error(\"Logger is already a monitoring context\");\n\t}\n\t/**\n\t * this is the tricky bit we use for now to smuggle monitoring context around.\n\t * To the logger we mixin both config and itself, so mc.logger === logger as it is self-referential.\n\t * We then expose it as a Monitoring context, so via types we hide the outer logger methods.\n\t * To layers that expect just a logger we can pass mc.logger, but this is still a MonitoringContext\n\t * so if a deeper layer then converts that logger to a monitoring context it can find the smuggled properties\n\t * of the MonitoringContext and get the config provider.\n\t */\n\tconst mc: L & Partial<MonitoringContext<L>> = logger;\n\tmc.config = new CachedConfigProvider(logger, ...configs);\n\tmc.logger = logger;\n\treturn mc as MonitoringContext<L>;\n}\n\nfunction isConfigProviderBase(obj: unknown): obj is IConfigProviderBase {\n\tconst maybeConfig = obj as Partial<IConfigProviderBase> | undefined;\n\treturn typeof maybeConfig?.getRawConfig === \"function\";\n}\n\n/**\n * Creates a child logger with a {@link MonitoringContext}.\n *\n * @see {@link loggerToMonitoringContext}\n * @internal\n */\nexport function createChildMonitoringContext(\n\tprops: Parameters<typeof createChildLogger>[0],\n): MonitoringContext {\n\treturn loggerToMonitoringContext(createChildLogger(props));\n}\n"]}
package/lib/error.d.ts ADDED
@@ -0,0 +1,112 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+ import { IGenericError, IErrorBase, ITelemetryBaseProperties, IUsageError } from "@fluidframework/core-interfaces";
6
+ import { ISequencedDocumentMessage } from "@fluidframework/protocol-definitions";
7
+ import { LoggingError } from "./errorLogging";
8
+ import { IFluidErrorBase } from "./fluidErrorBase";
9
+ /**
10
+ * Throws a UsageError with the given message if the condition is not met.
11
+ * Use this API when `false` indicates a precondition is not met on a public API (for any FF layer).
12
+ *
13
+ * @param condition - The condition that should be true, if the condition is false a UsageError will be thrown.
14
+ * @param message - The message to include in the error when the condition does not hold.
15
+ * @param props - Telemetry props to include on the error when the condition does not hold.
16
+ * @internal
17
+ */
18
+ export declare function validatePrecondition(condition: boolean, message: string, props?: ITelemetryBaseProperties): asserts condition;
19
+ /**
20
+ * Generic wrapper for an unrecognized/uncategorized error object
21
+ *
22
+ * @internal
23
+ */
24
+ export declare class GenericError extends LoggingError implements IGenericError, IFluidErrorBase {
25
+ readonly error?: any;
26
+ readonly errorType: "genericError";
27
+ /**
28
+ * Create a new GenericError
29
+ * @param message - Error message
30
+ * @param error - inner error object
31
+ * @param props - Telemetry props to include when the error is logged
32
+ */
33
+ constructor(message: string, error?: any, props?: ITelemetryBaseProperties);
34
+ }
35
+ /**
36
+ * Error indicating an API is being used improperly resulting in an invalid operation.
37
+ *
38
+ * @internal
39
+ */
40
+ export declare class UsageError extends LoggingError implements IUsageError, IFluidErrorBase {
41
+ readonly errorType: "usageError";
42
+ constructor(message: string, props?: ITelemetryBaseProperties);
43
+ }
44
+ /**
45
+ * DataCorruptionError indicates that we encountered definitive evidence that the data at rest
46
+ * backing this container is corrupted, and this container would never be expected to load properly again
47
+ *
48
+ * @internal
49
+ */
50
+ export declare class DataCorruptionError extends LoggingError implements IErrorBase, IFluidErrorBase {
51
+ readonly errorType: "dataCorruptionError";
52
+ readonly canRetry = false;
53
+ constructor(message: string, props: ITelemetryBaseProperties);
54
+ }
55
+ /**
56
+ * Indicates we hit a fatal error while processing incoming data from the Fluid Service.
57
+ *
58
+ * @remarks
59
+ *
60
+ * The error will often originate in the dataStore or DDS implementation that is responding to incoming changes.
61
+ * This differs from {@link DataCorruptionError} in that this may be a transient error that will not repro in another
62
+ * client or session.
63
+ *
64
+ * @internal
65
+ */
66
+ export declare class DataProcessingError extends LoggingError implements IErrorBase, IFluidErrorBase {
67
+ /**
68
+ * {@inheritDoc IFluidErrorBase.errorType}
69
+ */
70
+ readonly errorType: "dataProcessingError";
71
+ readonly canRetry = false;
72
+ private constructor();
73
+ /**
74
+ * Create a new `DataProcessingError` detected and raised within the Fluid Framework.
75
+ */
76
+ static create(errorMessage: string, dataProcessingCodepath: string, sequencedMessage?: ISequencedDocumentMessage, props?: ITelemetryBaseProperties): IFluidErrorBase;
77
+ /**
78
+ * Wrap the given error in a `DataProcessingError`, unless the error is already of a known type
79
+ * with the exception of a normalized {@link LoggingError}, which will still be wrapped.
80
+ *
81
+ * In either case, the error will have some relevant properties added for telemetry.
82
+ *
83
+ * @remarks
84
+ *
85
+ * We wrap conditionally since known error types represent well-understood failure modes, and ideally
86
+ * one day we will move away from throwing these errors but rather we'll return them.
87
+ * But an unrecognized error needs to be classified as `DataProcessingError`.
88
+ *
89
+ * @param originalError - The error to be converted.
90
+ * @param dataProcessingCodepath - Which code-path failed while processing data.
91
+ * @param messageLike - Message to include info about via telemetry props.
92
+ *
93
+ * @returns Either a new `DataProcessingError`, or (if wrapping is deemed unnecessary) the given error.
94
+ */
95
+ static wrapIfUnrecognized(originalError: unknown, dataProcessingCodepath: string, messageLike?: Partial<Pick<ISequencedDocumentMessage, "clientId" | "sequenceNumber" | "clientSequenceNumber" | "referenceSequenceNumber" | "minimumSequenceNumber" | "timestamp">>): IFluidErrorBase;
96
+ }
97
+ /**
98
+ * Extracts specific properties from the provided message that we know are safe to log.
99
+ *
100
+ * @param messageLike - Message to include info about via telemetry props.
101
+ *
102
+ * @internal
103
+ */
104
+ export declare const extractSafePropertiesFromMessage: (messageLike: Partial<Pick<ISequencedDocumentMessage, "clientId" | "sequenceNumber" | "clientSequenceNumber" | "referenceSequenceNumber" | "minimumSequenceNumber" | "timestamp">>) => {
105
+ messageClientId: string | undefined;
106
+ messageSequenceNumber: number | undefined;
107
+ messageClientSequenceNumber: number | undefined;
108
+ messageReferenceSequenceNumber: number | undefined;
109
+ messageMinimumSequenceNumber: number | undefined;
110
+ messageTimestamp: number | undefined;
111
+ };
112
+ //# sourceMappingURL=error.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error.d.ts","sourceRoot":"","sources":["../src/error.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAEN,aAAa,EACb,UAAU,EACV,wBAAwB,EACxB,WAAW,EACX,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,yBAAyB,EAAE,MAAM,sCAAsC,CAAC;AAEjF,OAAO,EACN,YAAY,EAKZ,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAEnD;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CACnC,SAAS,EAAE,OAAO,EAClB,OAAO,EAAE,MAAM,EACf,KAAK,CAAC,EAAE,wBAAwB,GAC9B,OAAO,CAAC,SAAS,CAInB;AAED;;;;GAIG;AACH,qBAAa,YAAa,SAAQ,YAAa,YAAW,aAAa,EAAE,eAAe;aAatE,KAAK,CAAC;IAZvB,QAAQ,CAAC,SAAS,iBAAgC;IAElD;;;;;OAKG;gBAEF,OAAO,EAAE,MAAM,EAGC,KAAK,CAAC,KAAK,EAC3B,KAAK,CAAC,EAAE,wBAAwB;CAKjC;AAED;;;;GAIG;AACH,qBAAa,UAAW,SAAQ,YAAa,YAAW,WAAW,EAAE,eAAe;IACnF,QAAQ,CAAC,SAAS,eAA8B;gBAEpC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,wBAAwB;CAG7D;AAED;;;;;GAKG;AACH,qBAAa,mBAAoB,SAAQ,YAAa,YAAW,UAAU,EAAE,eAAe;IAC3F,QAAQ,CAAC,SAAS,wBAAuC;IACzD,QAAQ,CAAC,QAAQ,SAAS;gBAEd,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,wBAAwB;CAG5D;AAED;;;;;;;;;;GAUG;AACH,qBAAa,mBAAoB,SAAQ,YAAa,YAAW,UAAU,EAAE,eAAe;IAC3F;;OAEG;IACH,SAAgB,SAAS,wBAAuC;IAEhE,SAAgB,QAAQ,SAAS;IAEjC,OAAO;IAIP;;OAEG;WACW,MAAM,CACnB,YAAY,EAAE,MAAM,EACpB,sBAAsB,EAAE,MAAM,EAC9B,gBAAgB,CAAC,EAAE,yBAAyB,EAC5C,KAAK,GAAE,wBAA6B,GAClC,eAAe;IAWlB;;;;;;;;;;;;;;;;;OAiBG;WACW,kBAAkB,CAC/B,aAAa,EAAE,OAAO,EACtB,sBAAsB,EAAE,MAAM,EAC9B,WAAW,CAAC,EAAE,OAAO,CACpB,IAAI,CACH,yBAAyB,EACvB,UAAU,GACV,gBAAgB,GAChB,sBAAsB,GACtB,yBAAyB,GACzB,uBAAuB,GACvB,WAAW,CACb,CACD,GACC,eAAe;CA6BlB;AAED;;;;;;GAMG;AACH,eAAO,MAAM,gCAAgC,gBAC/B,QACZ,KACC,yBAAyB,EACvB,UAAU,GACV,gBAAgB,GAChB,sBAAsB,GACtB,yBAAyB,GACzB,uBAAuB,GACvB,WAAW,CACb,CACD,KACC;IACF,eAAe,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,qBAAqB,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1C,2BAA2B,EAAE,MAAM,GAAG,SAAS,CAAC;IAChD,8BAA8B,EAAE,MAAM,GAAG,SAAS,CAAC;IACnD,4BAA4B,EAAE,MAAM,GAAG,SAAS,CAAC;IACjD,gBAAgB,EAAE,MAAM,GAAG,SAAS,CAAC;CAQpC,CAAC"}
package/lib/error.js ADDED
@@ -0,0 +1,150 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+ import { FluidErrorTypes, } from "@fluidframework/core-interfaces";
6
+ import { LoggingError, NORMALIZED_ERROR_TYPE, isExternalError, normalizeError, wrapError, } from "./errorLogging";
7
+ /**
8
+ * Throws a UsageError with the given message if the condition is not met.
9
+ * Use this API when `false` indicates a precondition is not met on a public API (for any FF layer).
10
+ *
11
+ * @param condition - The condition that should be true, if the condition is false a UsageError will be thrown.
12
+ * @param message - The message to include in the error when the condition does not hold.
13
+ * @param props - Telemetry props to include on the error when the condition does not hold.
14
+ * @internal
15
+ */
16
+ export function validatePrecondition(condition, message, props) {
17
+ if (!condition) {
18
+ throw new UsageError(message, props);
19
+ }
20
+ }
21
+ /**
22
+ * Generic wrapper for an unrecognized/uncategorized error object
23
+ *
24
+ * @internal
25
+ */
26
+ export class GenericError extends LoggingError {
27
+ /**
28
+ * Create a new GenericError
29
+ * @param message - Error message
30
+ * @param error - inner error object
31
+ * @param props - Telemetry props to include when the error is logged
32
+ */
33
+ constructor(message,
34
+ // TODO: Use `unknown` instead (API breaking change because error is not just an input parameter, but a public member of the class)
35
+ // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any
36
+ error, props) {
37
+ // Don't try to log the inner error
38
+ super(message, props, new Set(["error"]));
39
+ this.error = error;
40
+ this.errorType = FluidErrorTypes.genericError;
41
+ }
42
+ }
43
+ /**
44
+ * Error indicating an API is being used improperly resulting in an invalid operation.
45
+ *
46
+ * @internal
47
+ */
48
+ export class UsageError extends LoggingError {
49
+ constructor(message, props) {
50
+ super(message, { ...props, usageError: true });
51
+ this.errorType = FluidErrorTypes.usageError;
52
+ }
53
+ }
54
+ /**
55
+ * DataCorruptionError indicates that we encountered definitive evidence that the data at rest
56
+ * backing this container is corrupted, and this container would never be expected to load properly again
57
+ *
58
+ * @internal
59
+ */
60
+ export class DataCorruptionError extends LoggingError {
61
+ constructor(message, props) {
62
+ super(message, { ...props, dataProcessingError: 1 });
63
+ this.errorType = FluidErrorTypes.dataCorruptionError;
64
+ this.canRetry = false;
65
+ }
66
+ }
67
+ /**
68
+ * Indicates we hit a fatal error while processing incoming data from the Fluid Service.
69
+ *
70
+ * @remarks
71
+ *
72
+ * The error will often originate in the dataStore or DDS implementation that is responding to incoming changes.
73
+ * This differs from {@link DataCorruptionError} in that this may be a transient error that will not repro in another
74
+ * client or session.
75
+ *
76
+ * @internal
77
+ */
78
+ export class DataProcessingError extends LoggingError {
79
+ constructor(errorMessage, props) {
80
+ super(errorMessage, props);
81
+ /**
82
+ * {@inheritDoc IFluidErrorBase.errorType}
83
+ */
84
+ this.errorType = FluidErrorTypes.dataProcessingError;
85
+ this.canRetry = false;
86
+ }
87
+ /**
88
+ * Create a new `DataProcessingError` detected and raised within the Fluid Framework.
89
+ */
90
+ static create(errorMessage, dataProcessingCodepath, sequencedMessage, props = {}) {
91
+ const dataProcessingError = DataProcessingError.wrapIfUnrecognized(errorMessage, dataProcessingCodepath, sequencedMessage);
92
+ dataProcessingError.addTelemetryProperties(props);
93
+ return dataProcessingError;
94
+ }
95
+ /**
96
+ * Wrap the given error in a `DataProcessingError`, unless the error is already of a known type
97
+ * with the exception of a normalized {@link LoggingError}, which will still be wrapped.
98
+ *
99
+ * In either case, the error will have some relevant properties added for telemetry.
100
+ *
101
+ * @remarks
102
+ *
103
+ * We wrap conditionally since known error types represent well-understood failure modes, and ideally
104
+ * one day we will move away from throwing these errors but rather we'll return them.
105
+ * But an unrecognized error needs to be classified as `DataProcessingError`.
106
+ *
107
+ * @param originalError - The error to be converted.
108
+ * @param dataProcessingCodepath - Which code-path failed while processing data.
109
+ * @param messageLike - Message to include info about via telemetry props.
110
+ *
111
+ * @returns Either a new `DataProcessingError`, or (if wrapping is deemed unnecessary) the given error.
112
+ */
113
+ static wrapIfUnrecognized(originalError, dataProcessingCodepath, messageLike) {
114
+ const props = {
115
+ dataProcessingError: 1,
116
+ dataProcessingCodepath,
117
+ ...(messageLike === undefined
118
+ ? undefined
119
+ : extractSafePropertiesFromMessage(messageLike)),
120
+ };
121
+ const normalizedError = normalizeError(originalError, { props });
122
+ // Note that other errors may have the NORMALIZED_ERROR_TYPE errorType,
123
+ // but if so they are still suitable to be wrapped as DataProcessingError.
124
+ if (isExternalError(normalizedError) ||
125
+ normalizedError.errorType === NORMALIZED_ERROR_TYPE) {
126
+ // Create a new DataProcessingError to wrap this external error
127
+ const dataProcessingError = wrapError(normalizedError, (message) => new DataProcessingError(message));
128
+ // Copy over the props above and any others added to this error since first being normalized
129
+ dataProcessingError.addTelemetryProperties(normalizedError.getTelemetryProperties());
130
+ return dataProcessingError;
131
+ }
132
+ return normalizedError;
133
+ }
134
+ }
135
+ /**
136
+ * Extracts specific properties from the provided message that we know are safe to log.
137
+ *
138
+ * @param messageLike - Message to include info about via telemetry props.
139
+ *
140
+ * @internal
141
+ */
142
+ export const extractSafePropertiesFromMessage = (messageLike) => ({
143
+ messageClientId: messageLike.clientId === null ? "null" : messageLike.clientId,
144
+ messageSequenceNumber: messageLike.sequenceNumber,
145
+ messageClientSequenceNumber: messageLike.clientSequenceNumber,
146
+ messageReferenceSequenceNumber: messageLike.referenceSequenceNumber,
147
+ messageMinimumSequenceNumber: messageLike.minimumSequenceNumber,
148
+ messageTimestamp: messageLike.timestamp,
149
+ });
150
+ //# sourceMappingURL=error.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error.js","sourceRoot":"","sources":["../src/error.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 {\n\tFluidErrorTypes,\n\tIGenericError,\n\tIErrorBase,\n\tITelemetryBaseProperties,\n\tIUsageError,\n} from \"@fluidframework/core-interfaces\";\nimport { ISequencedDocumentMessage } from \"@fluidframework/protocol-definitions\";\n\nimport {\n\tLoggingError,\n\tNORMALIZED_ERROR_TYPE,\n\tisExternalError,\n\tnormalizeError,\n\twrapError,\n} from \"./errorLogging\";\nimport { IFluidErrorBase } from \"./fluidErrorBase\";\n\n/**\n * Throws a UsageError with the given message if the condition is not met.\n * Use this API when `false` indicates a precondition is not met on a public API (for any FF layer).\n *\n * @param condition - The condition that should be true, if the condition is false a UsageError will be thrown.\n * @param message - The message to include in the error when the condition does not hold.\n * @param props - Telemetry props to include on the error when the condition does not hold.\n * @internal\n */\nexport function validatePrecondition(\n\tcondition: boolean,\n\tmessage: string,\n\tprops?: ITelemetryBaseProperties,\n): asserts condition {\n\tif (!condition) {\n\t\tthrow new UsageError(message, props);\n\t}\n}\n\n/**\n * Generic wrapper for an unrecognized/uncategorized error object\n *\n * @internal\n */\nexport class GenericError extends LoggingError implements IGenericError, IFluidErrorBase {\n\treadonly errorType = FluidErrorTypes.genericError;\n\n\t/**\n\t * Create a new GenericError\n\t * @param message - Error message\n\t * @param error - inner error object\n\t * @param props - Telemetry props to include when the error is logged\n\t */\n\tconstructor(\n\t\tmessage: string,\n\t\t// TODO: Use `unknown` instead (API breaking change because error is not just an input parameter, but a public member of the class)\n\t\t// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any\n\t\tpublic readonly error?: any,\n\t\tprops?: ITelemetryBaseProperties,\n\t) {\n\t\t// Don't try to log the inner error\n\t\tsuper(message, props, new Set([\"error\"]));\n\t}\n}\n\n/**\n * Error indicating an API is being used improperly resulting in an invalid operation.\n *\n * @internal\n */\nexport class UsageError extends LoggingError implements IUsageError, IFluidErrorBase {\n\treadonly errorType = FluidErrorTypes.usageError;\n\n\tconstructor(message: string, props?: ITelemetryBaseProperties) {\n\t\tsuper(message, { ...props, usageError: true });\n\t}\n}\n\n/**\n * DataCorruptionError indicates that we encountered definitive evidence that the data at rest\n * backing this container is corrupted, and this container would never be expected to load properly again\n *\n * @internal\n */\nexport class DataCorruptionError extends LoggingError implements IErrorBase, IFluidErrorBase {\n\treadonly errorType = FluidErrorTypes.dataCorruptionError;\n\treadonly canRetry = false;\n\n\tconstructor(message: string, props: ITelemetryBaseProperties) {\n\t\tsuper(message, { ...props, dataProcessingError: 1 });\n\t}\n}\n\n/**\n * Indicates we hit a fatal error while processing incoming data from the Fluid Service.\n *\n * @remarks\n *\n * The error will often originate in the dataStore or DDS implementation that is responding to incoming changes.\n * This differs from {@link DataCorruptionError} in that this may be a transient error that will not repro in another\n * client or session.\n *\n * @internal\n */\nexport class DataProcessingError extends LoggingError implements IErrorBase, IFluidErrorBase {\n\t/**\n\t * {@inheritDoc IFluidErrorBase.errorType}\n\t */\n\tpublic readonly errorType = FluidErrorTypes.dataProcessingError;\n\n\tpublic readonly canRetry = false;\n\n\tprivate constructor(errorMessage: string, props?: ITelemetryBaseProperties) {\n\t\tsuper(errorMessage, props);\n\t}\n\n\t/**\n\t * Create a new `DataProcessingError` detected and raised within the Fluid Framework.\n\t */\n\tpublic static create(\n\t\terrorMessage: string,\n\t\tdataProcessingCodepath: string,\n\t\tsequencedMessage?: ISequencedDocumentMessage,\n\t\tprops: ITelemetryBaseProperties = {},\n\t): IFluidErrorBase {\n\t\tconst dataProcessingError = DataProcessingError.wrapIfUnrecognized(\n\t\t\terrorMessage,\n\t\t\tdataProcessingCodepath,\n\t\t\tsequencedMessage,\n\t\t);\n\t\tdataProcessingError.addTelemetryProperties(props);\n\n\t\treturn dataProcessingError;\n\t}\n\n\t/**\n\t * Wrap the given error in a `DataProcessingError`, unless the error is already of a known type\n\t * with the exception of a normalized {@link LoggingError}, which will still be wrapped.\n\t *\n\t * In either case, the error will have some relevant properties added for telemetry.\n\t *\n\t * @remarks\n\t *\n\t * We wrap conditionally since known error types represent well-understood failure modes, and ideally\n\t * one day we will move away from throwing these errors but rather we'll return them.\n\t * But an unrecognized error needs to be classified as `DataProcessingError`.\n\t *\n\t * @param originalError - The error to be converted.\n\t * @param dataProcessingCodepath - Which code-path failed while processing data.\n\t * @param messageLike - Message to include info about via telemetry props.\n\t *\n\t * @returns Either a new `DataProcessingError`, or (if wrapping is deemed unnecessary) the given error.\n\t */\n\tpublic static wrapIfUnrecognized(\n\t\toriginalError: unknown,\n\t\tdataProcessingCodepath: string,\n\t\tmessageLike?: Partial<\n\t\t\tPick<\n\t\t\t\tISequencedDocumentMessage,\n\t\t\t\t| \"clientId\"\n\t\t\t\t| \"sequenceNumber\"\n\t\t\t\t| \"clientSequenceNumber\"\n\t\t\t\t| \"referenceSequenceNumber\"\n\t\t\t\t| \"minimumSequenceNumber\"\n\t\t\t\t| \"timestamp\"\n\t\t\t>\n\t\t>,\n\t): IFluidErrorBase {\n\t\tconst props = {\n\t\t\tdataProcessingError: 1,\n\t\t\tdataProcessingCodepath,\n\t\t\t...(messageLike === undefined\n\t\t\t\t? undefined\n\t\t\t\t: extractSafePropertiesFromMessage(messageLike)),\n\t\t};\n\n\t\tconst normalizedError = normalizeError(originalError, { props });\n\t\t// Note that other errors may have the NORMALIZED_ERROR_TYPE errorType,\n\t\t// but if so they are still suitable to be wrapped as DataProcessingError.\n\t\tif (\n\t\t\tisExternalError(normalizedError) ||\n\t\t\tnormalizedError.errorType === NORMALIZED_ERROR_TYPE\n\t\t) {\n\t\t\t// Create a new DataProcessingError to wrap this external error\n\t\t\tconst dataProcessingError = wrapError(\n\t\t\t\tnormalizedError,\n\t\t\t\t(message: string) => new DataProcessingError(message),\n\t\t\t);\n\n\t\t\t// Copy over the props above and any others added to this error since first being normalized\n\t\t\tdataProcessingError.addTelemetryProperties(normalizedError.getTelemetryProperties());\n\n\t\t\treturn dataProcessingError;\n\t\t}\n\t\treturn normalizedError;\n\t}\n}\n\n/**\n * Extracts specific properties from the provided message that we know are safe to log.\n *\n * @param messageLike - Message to include info about via telemetry props.\n *\n * @internal\n */\nexport const extractSafePropertiesFromMessage = (\n\tmessageLike: Partial<\n\t\tPick<\n\t\t\tISequencedDocumentMessage,\n\t\t\t| \"clientId\"\n\t\t\t| \"sequenceNumber\"\n\t\t\t| \"clientSequenceNumber\"\n\t\t\t| \"referenceSequenceNumber\"\n\t\t\t| \"minimumSequenceNumber\"\n\t\t\t| \"timestamp\"\n\t\t>\n\t>,\n): {\n\tmessageClientId: string | undefined;\n\tmessageSequenceNumber: number | undefined;\n\tmessageClientSequenceNumber: number | undefined;\n\tmessageReferenceSequenceNumber: number | undefined;\n\tmessageMinimumSequenceNumber: number | undefined;\n\tmessageTimestamp: number | undefined;\n} => ({\n\tmessageClientId: messageLike.clientId === null ? \"null\" : messageLike.clientId,\n\tmessageSequenceNumber: messageLike.sequenceNumber,\n\tmessageClientSequenceNumber: messageLike.clientSequenceNumber,\n\tmessageReferenceSequenceNumber: messageLike.referenceSequenceNumber,\n\tmessageMinimumSequenceNumber: messageLike.minimumSequenceNumber,\n\tmessageTimestamp: messageLike.timestamp,\n});\n"]}