@fluidframework/telemetry-utils 2.0.0-internal.6.1.1 → 2.0.0-internal.6.3.0

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 (109) hide show
  1. package/.eslintrc.js +2 -1
  2. package/CHANGELOG.md +59 -0
  3. package/README.md +4 -3
  4. package/dist/config.d.ts.map +1 -1
  5. package/dist/config.js +9 -4
  6. package/dist/config.js.map +1 -1
  7. package/dist/error.d.ts +92 -0
  8. package/dist/error.d.ts.map +1 -0
  9. package/dist/error.js +133 -0
  10. package/dist/error.js.map +1 -0
  11. package/dist/errorLogging.d.ts +34 -18
  12. package/dist/errorLogging.d.ts.map +1 -1
  13. package/dist/errorLogging.js +42 -17
  14. package/dist/errorLogging.js.map +1 -1
  15. package/dist/eventEmitterWithErrorHandling.d.ts +3 -3
  16. package/dist/eventEmitterWithErrorHandling.d.ts.map +1 -1
  17. package/dist/eventEmitterWithErrorHandling.js +10 -3
  18. package/dist/eventEmitterWithErrorHandling.js.map +1 -1
  19. package/dist/events.d.ts +1 -1
  20. package/dist/events.d.ts.map +1 -1
  21. package/dist/events.js.map +1 -1
  22. package/dist/fluidErrorBase.d.ts +48 -15
  23. package/dist/fluidErrorBase.d.ts.map +1 -1
  24. package/dist/fluidErrorBase.js +18 -11
  25. package/dist/fluidErrorBase.js.map +1 -1
  26. package/dist/index.d.ts +2 -1
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +7 -1
  29. package/dist/index.js.map +1 -1
  30. package/dist/logger.d.ts +38 -22
  31. package/dist/logger.d.ts.map +1 -1
  32. package/dist/logger.js +68 -21
  33. package/dist/logger.js.map +1 -1
  34. package/dist/mockLogger.d.ts +17 -6
  35. package/dist/mockLogger.d.ts.map +1 -1
  36. package/dist/mockLogger.js +22 -9
  37. package/dist/mockLogger.js.map +1 -1
  38. package/dist/sampledTelemetryHelper.d.ts +8 -7
  39. package/dist/sampledTelemetryHelper.d.ts.map +1 -1
  40. package/dist/sampledTelemetryHelper.js +13 -11
  41. package/dist/sampledTelemetryHelper.js.map +1 -1
  42. package/dist/telemetryTypes.d.ts +20 -6
  43. package/dist/telemetryTypes.d.ts.map +1 -1
  44. package/dist/telemetryTypes.js.map +1 -1
  45. package/dist/thresholdCounter.d.ts.map +1 -1
  46. package/dist/thresholdCounter.js.map +1 -1
  47. package/dist/utils.d.ts +1 -1
  48. package/dist/utils.d.ts.map +1 -1
  49. package/dist/utils.js.map +1 -1
  50. package/lib/config.d.ts.map +1 -1
  51. package/lib/config.js +9 -4
  52. package/lib/config.js.map +1 -1
  53. package/lib/error.d.ts +92 -0
  54. package/lib/error.d.ts.map +1 -0
  55. package/lib/error.js +125 -0
  56. package/lib/error.js.map +1 -0
  57. package/lib/errorLogging.d.ts +34 -18
  58. package/lib/errorLogging.d.ts.map +1 -1
  59. package/lib/errorLogging.js +42 -17
  60. package/lib/errorLogging.js.map +1 -1
  61. package/lib/eventEmitterWithErrorHandling.d.ts +3 -3
  62. package/lib/eventEmitterWithErrorHandling.d.ts.map +1 -1
  63. package/lib/eventEmitterWithErrorHandling.js +9 -2
  64. package/lib/eventEmitterWithErrorHandling.js.map +1 -1
  65. package/lib/events.d.ts +1 -1
  66. package/lib/events.d.ts.map +1 -1
  67. package/lib/events.js.map +1 -1
  68. package/lib/fluidErrorBase.d.ts +48 -15
  69. package/lib/fluidErrorBase.d.ts.map +1 -1
  70. package/lib/fluidErrorBase.js +18 -11
  71. package/lib/fluidErrorBase.js.map +1 -1
  72. package/lib/index.d.ts +2 -1
  73. package/lib/index.d.ts.map +1 -1
  74. package/lib/index.js +1 -0
  75. package/lib/index.js.map +1 -1
  76. package/lib/logger.d.ts +38 -22
  77. package/lib/logger.d.ts.map +1 -1
  78. package/lib/logger.js +64 -17
  79. package/lib/logger.js.map +1 -1
  80. package/lib/mockLogger.d.ts +17 -6
  81. package/lib/mockLogger.d.ts.map +1 -1
  82. package/lib/mockLogger.js +22 -9
  83. package/lib/mockLogger.js.map +1 -1
  84. package/lib/sampledTelemetryHelper.d.ts +8 -7
  85. package/lib/sampledTelemetryHelper.d.ts.map +1 -1
  86. package/lib/sampledTelemetryHelper.js +11 -9
  87. package/lib/sampledTelemetryHelper.js.map +1 -1
  88. package/lib/telemetryTypes.d.ts +20 -6
  89. package/lib/telemetryTypes.d.ts.map +1 -1
  90. package/lib/telemetryTypes.js.map +1 -1
  91. package/lib/thresholdCounter.d.ts.map +1 -1
  92. package/lib/thresholdCounter.js.map +1 -1
  93. package/lib/utils.d.ts +1 -1
  94. package/lib/utils.d.ts.map +1 -1
  95. package/lib/utils.js.map +1 -1
  96. package/package.json +15 -18
  97. package/src/config.ts +12 -7
  98. package/src/error.ts +202 -0
  99. package/src/errorLogging.ts +90 -52
  100. package/src/eventEmitterWithErrorHandling.ts +5 -3
  101. package/src/events.ts +3 -3
  102. package/src/fluidErrorBase.ts +62 -26
  103. package/src/index.ts +8 -0
  104. package/src/logger.ts +143 -45
  105. package/src/mockLogger.ts +33 -16
  106. package/src/sampledTelemetryHelper.ts +18 -14
  107. package/src/telemetryTypes.ts +29 -6
  108. package/src/thresholdCounter.ts +2 -2
  109. package/src/utils.ts +1 -1
package/src/config.ts CHANGED
@@ -50,11 +50,12 @@ const NullConfigProvider: IConfigProviderBase = {
50
50
  export const inMemoryConfigProvider = (storage: Storage | undefined): IConfigProviderBase => {
51
51
  if (storage !== undefined && storage !== null) {
52
52
  return new CachedConfigProvider(undefined, {
53
- getRawConfig: (name: string) => {
53
+ getRawConfig: (name: string): ConfigTypes | undefined => {
54
54
  try {
55
55
  return stronglyTypedParse(storage.getItem(name) ?? undefined)?.raw;
56
- } catch {}
57
- return undefined;
56
+ } catch {
57
+ return undefined;
58
+ }
58
59
  },
59
60
  });
60
61
  }
@@ -104,7 +105,7 @@ function stronglyTypedParse(input: ConfigTypes): StronglyTypedValue | undefined
104
105
  // holds strings
105
106
  if (typeof input === "string") {
106
107
  try {
107
- output = JSON.parse(input);
108
+ output = JSON.parse(input) as ConfigTypes;
108
109
  // we succeeded in parsing, but we don't support parsing
109
110
  // for any object as we can't do it type safely
110
111
  // so in this case, the default return will be string
@@ -113,7 +114,9 @@ function stronglyTypedParse(input: ConfigTypes): StronglyTypedValue | undefined
113
114
  // a false sense of security by just
114
115
  // casting.
115
116
  defaultReturn = { raw: input, string: input };
116
- } catch {}
117
+ } catch {
118
+ // No-op
119
+ }
117
120
  }
118
121
 
119
122
  if (output === undefined) {
@@ -144,7 +147,9 @@ function stronglyTypedParse(input: ConfigTypes): StronglyTypedValue | undefined
144
147
  return defaultReturn;
145
148
  }
146
149
 
147
- /** `sessionStorage` is undefined in some environments such as Node and web pages with session storage disabled */
150
+ /**
151
+ * `sessionStorage` is undefined in some environments such as Node and web pages with session storage disabled.
152
+ */
148
153
  const safeSessionStorage = (): Storage | undefined => {
149
154
  // For some configurations accessing "globalThis.sessionStorage" throws
150
155
  // "'sessionStorage' property from 'Window': Access is denied for this document" rather than returning undefined.
@@ -264,7 +269,7 @@ export function loggerToMonitoringContext<L extends ITelemetryBaseLogger = ITele
264
269
  export function mixinMonitoringContext<L extends ITelemetryBaseLogger = ITelemetryLoggerExt>(
265
270
  logger: L,
266
271
  ...configs: (IConfigProviderBase | undefined)[]
267
- ) {
272
+ ): MonitoringContext<L> {
268
273
  if (loggerIsMonitoringContext<L>(logger)) {
269
274
  throw new Error("Logger is already a monitoring context");
270
275
  }
package/src/error.ts ADDED
@@ -0,0 +1,202 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+ import {
7
+ FluidErrorTypes,
8
+ IGenericError,
9
+ IErrorBase,
10
+ ITelemetryProperties,
11
+ IUsageError,
12
+ } from "@fluidframework/core-interfaces";
13
+ import { ISequencedDocumentMessage } from "@fluidframework/protocol-definitions";
14
+
15
+ import {
16
+ LoggingError,
17
+ NORMALIZED_ERROR_TYPE,
18
+ isExternalError,
19
+ normalizeError,
20
+ wrapError,
21
+ } from "./errorLogging";
22
+ import { IFluidErrorBase } from "./fluidErrorBase";
23
+
24
+ /**
25
+ * Generic wrapper for an unrecognized/uncategorized error object
26
+ */
27
+ export class GenericError extends LoggingError implements IGenericError, IFluidErrorBase {
28
+ readonly errorType = FluidErrorTypes.genericError;
29
+
30
+ /**
31
+ * Create a new GenericError
32
+ * @param message - Error message
33
+ * @param error - inner error object
34
+ * @param props - Telemetry props to include when the error is logged
35
+ */
36
+ // TODO: Use `unknown` instead (API breaking change because error is not just an input parameter, but a public member of the class)
37
+ // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any
38
+ constructor(message: string, public readonly error?: any, props?: ITelemetryProperties) {
39
+ // Don't try to log the inner error
40
+ super(message, props, new Set(["error"]));
41
+ }
42
+ }
43
+
44
+ /**
45
+ * Error indicating an API is being used improperly resulting in an invalid operation.
46
+ */
47
+ export class UsageError extends LoggingError implements IUsageError, IFluidErrorBase {
48
+ readonly errorType = FluidErrorTypes.usageError;
49
+
50
+ constructor(message: string, props?: ITelemetryProperties) {
51
+ super(message, { ...props, usageError: true });
52
+ }
53
+ }
54
+
55
+ /**
56
+ * DataCorruptionError indicates that we encountered definitive evidence that the data at rest
57
+ * backing this container is corrupted, and this container would never be expected to load properly again
58
+ */
59
+ export class DataCorruptionError extends LoggingError implements IErrorBase, IFluidErrorBase {
60
+ readonly errorType = FluidErrorTypes.dataCorruptionError;
61
+ readonly canRetry = false;
62
+
63
+ constructor(message: string, props: ITelemetryProperties) {
64
+ super(message, { ...props, dataProcessingError: 1 });
65
+ }
66
+ }
67
+
68
+ /**
69
+ * Indicates we hit a fatal error while processing incoming data from the Fluid Service.
70
+ *
71
+ * @remarks
72
+ *
73
+ * The error will often originate in the dataStore or DDS implementation that is responding to incoming changes.
74
+ * This differs from {@link DataCorruptionError} in that this may be a transient error that will not repro in another
75
+ * client or session.
76
+ */
77
+ export class DataProcessingError extends LoggingError implements IErrorBase, IFluidErrorBase {
78
+ /**
79
+ * {@inheritDoc IFluidErrorBase.errorType}
80
+ */
81
+ public readonly errorType = FluidErrorTypes.dataProcessingError;
82
+
83
+ public readonly canRetry = false;
84
+
85
+ private constructor(errorMessage: string) {
86
+ super(errorMessage);
87
+ }
88
+
89
+ /**
90
+ * Create a new `DataProcessingError` detected and raised within the Fluid Framework.
91
+ */
92
+ public static create(
93
+ errorMessage: string,
94
+ dataProcessingCodepath: string,
95
+ sequencedMessage?: ISequencedDocumentMessage,
96
+ props: ITelemetryProperties = {},
97
+ ): IFluidErrorBase {
98
+ const dataProcessingError = DataProcessingError.wrapIfUnrecognized(
99
+ errorMessage,
100
+ dataProcessingCodepath,
101
+ sequencedMessage,
102
+ );
103
+ dataProcessingError.addTelemetryProperties(props);
104
+
105
+ return dataProcessingError;
106
+ }
107
+
108
+ /**
109
+ * Wrap the given error in a `DataProcessingError`, unless the error is already of a known type
110
+ * with the exception of a normalized {@link LoggingError}, which will still be wrapped.
111
+ *
112
+ * In either case, the error will have some relevant properties added for telemetry.
113
+ *
114
+ * @remarks
115
+ *
116
+ * We wrap conditionally since known error types represent well-understood failure modes, and ideally
117
+ * one day we will move away from throwing these errors but rather we'll return them.
118
+ * But an unrecognized error needs to be classified as `DataProcessingError`.
119
+ *
120
+ * @param originalError - The error to be converted.
121
+ * @param dataProcessingCodepath - Which code-path failed while processing data.
122
+ * @param messageLike - Message to include info about via telemetry props.
123
+ *
124
+ * @returns Either a new `DataProcessingError`, or (if wrapping is deemed unnecessary) the given error.
125
+ */
126
+ public static wrapIfUnrecognized(
127
+ originalError: unknown,
128
+ dataProcessingCodepath: string,
129
+ messageLike?: Partial<
130
+ Pick<
131
+ ISequencedDocumentMessage,
132
+ | "clientId"
133
+ | "sequenceNumber"
134
+ | "clientSequenceNumber"
135
+ | "referenceSequenceNumber"
136
+ | "minimumSequenceNumber"
137
+ | "timestamp"
138
+ >
139
+ >,
140
+ ): IFluidErrorBase {
141
+ const props = {
142
+ dataProcessingError: 1,
143
+ dataProcessingCodepath,
144
+ ...(messageLike === undefined
145
+ ? undefined
146
+ : extractSafePropertiesFromMessage(messageLike)),
147
+ };
148
+
149
+ const normalizedError = normalizeError(originalError, { props });
150
+ // Note that other errors may have the NORMALIZED_ERROR_TYPE errorType,
151
+ // but if so they are still suitable to be wrapped as DataProcessingError.
152
+ if (
153
+ isExternalError(normalizedError) ||
154
+ normalizedError.errorType === NORMALIZED_ERROR_TYPE
155
+ ) {
156
+ // Create a new DataProcessingError to wrap this external error
157
+ const dataProcessingError = wrapError(
158
+ normalizedError,
159
+ (message: string) => new DataProcessingError(message),
160
+ );
161
+
162
+ // Copy over the props above and any others added to this error since first being normalized
163
+ dataProcessingError.addTelemetryProperties(normalizedError.getTelemetryProperties());
164
+
165
+ return dataProcessingError;
166
+ }
167
+ return normalizedError;
168
+ }
169
+ }
170
+
171
+ /**
172
+ * Extracts specific properties from the provided message that we know are safe to log.
173
+ *
174
+ * @param messageLike - Message to include info about via telemetry props.
175
+ */
176
+ export const extractSafePropertiesFromMessage = (
177
+ messageLike: Partial<
178
+ Pick<
179
+ ISequencedDocumentMessage,
180
+ | "clientId"
181
+ | "sequenceNumber"
182
+ | "clientSequenceNumber"
183
+ | "referenceSequenceNumber"
184
+ | "minimumSequenceNumber"
185
+ | "timestamp"
186
+ >
187
+ >,
188
+ ): {
189
+ messageClientId: string | undefined;
190
+ messageSequenceNumber: number | undefined;
191
+ messageClientSequenceNumber: number | undefined;
192
+ messageReferenceSequenceNumber: number | undefined;
193
+ messageMinimumSequenceNumber: number | undefined;
194
+ messageTimestamp: number | undefined;
195
+ } => ({
196
+ messageClientId: messageLike.clientId === null ? "null" : messageLike.clientId,
197
+ messageSequenceNumber: messageLike.sequenceNumber,
198
+ messageClientSequenceNumber: messageLike.clientSequenceNumber,
199
+ messageReferenceSequenceNumber: messageLike.referenceSequenceNumber,
200
+ messageMinimumSequenceNumber: messageLike.minimumSequenceNumber,
201
+ messageTimestamp: messageLike.timestamp,
202
+ });
@@ -5,9 +5,9 @@
5
5
 
6
6
  import {
7
7
  ILoggingError,
8
- ITaggedTelemetryPropertyType,
9
- ITelemetryProperties,
10
- TelemetryEventPropertyType,
8
+ ITelemetryBaseProperties,
9
+ TelemetryBaseEventPropertyType,
10
+ Tagged,
11
11
  } from "@fluidframework/core-interfaces";
12
12
  import { v4 as uuid } from "uuid";
13
13
  import {
@@ -16,20 +16,27 @@ import {
16
16
  isFluidError,
17
17
  isValidLegacyError,
18
18
  } from "./fluidErrorBase";
19
- import {
20
- ITaggedTelemetryPropertyTypeExt,
21
- ITelemetryLoggerExt,
22
- TelemetryEventPropertyTypeExt,
23
- } from "./telemetryTypes";
19
+ import { ITelemetryLoggerExt, TelemetryEventPropertyTypeExt } from "./telemetryTypes";
24
20
 
25
- /** @returns true if value is an object but neither null nor an array */
26
- const isRegularObject = (value: any): boolean => {
21
+ /**
22
+ * Determines if the provided value is an object but neither null nor an array.
23
+ */
24
+ const isRegularObject = (value: unknown): boolean => {
27
25
  return value !== null && !Array.isArray(value) && typeof value === "object";
28
26
  };
29
27
 
30
- /** Inspect the given error for common "safe" props and return them */
31
- export function extractLogSafeErrorProperties(error: any, sanitizeStack: boolean) {
32
- const removeMessageFromStack = (stack: string, errorName?: string) => {
28
+ /**
29
+ * Inspect the given error for common "safe" props and return them.
30
+ */
31
+ export function extractLogSafeErrorProperties(
32
+ error: unknown,
33
+ sanitizeStack: boolean,
34
+ ): {
35
+ message: string;
36
+ errorType?: string | undefined;
37
+ stack?: string | undefined;
38
+ } {
39
+ const removeMessageFromStack = (stack: string, errorName?: string): string => {
33
40
  if (!sanitizeStack) {
34
41
  return stack;
35
42
  }
@@ -41,14 +48,17 @@ export function extractLogSafeErrorProperties(error: any, sanitizeStack: boolean
41
48
  return stackFrames.join("\n");
42
49
  };
43
50
 
44
- const message = typeof error?.message === "string" ? (error.message as string) : String(error);
51
+ const message =
52
+ typeof (error as Partial<Error>)?.message === "string"
53
+ ? (error as Error).message
54
+ : String(error);
45
55
 
46
56
  const safeProps: { message: string; errorType?: string; stack?: string } = {
47
57
  message,
48
58
  };
49
59
 
50
60
  if (isRegularObject(error)) {
51
- const { errorType, stack, name } = error;
61
+ const { errorType, stack, name } = error as Partial<IFluidErrorBase>;
52
62
 
53
63
  if (typeof errorType === "string") {
54
64
  safeProps.errorType = errorType;
@@ -63,12 +73,19 @@ export function extractLogSafeErrorProperties(error: any, sanitizeStack: boolean
63
73
  return safeProps;
64
74
  }
65
75
 
66
- /** type guard for ILoggingError interface */
67
- export const isILoggingError = (x: any): x is ILoggingError =>
68
- typeof x?.getTelemetryProperties === "function";
76
+ /**
77
+ * type guard for ILoggingError interface
78
+ */
79
+ export const isILoggingError = (x: unknown): x is ILoggingError =>
80
+ typeof (x as Partial<ILoggingError>)?.getTelemetryProperties === "function";
69
81
 
70
- /** Copy props from source onto target, but do not overwrite an existing prop that matches */
71
- function copyProps(target: ITelemetryProperties | LoggingError, source: ITelemetryProperties) {
82
+ /**
83
+ * Copy props from source onto target, but do not overwrite an existing prop that matches
84
+ */
85
+ function copyProps(
86
+ target: ITelemetryBaseProperties | LoggingError,
87
+ source: ITelemetryBaseProperties,
88
+ ): void {
72
89
  for (const key of Object.keys(source)) {
73
90
  if (target[key] === undefined) {
74
91
  target[key] = source[key];
@@ -76,16 +93,23 @@ function copyProps(target: ITelemetryProperties | LoggingError, source: ITelemet
76
93
  }
77
94
  }
78
95
 
79
- /** Metadata to annotate an error object when annotating or normalizing it */
96
+ /**
97
+ * Metadata to annotate an error object when annotating or normalizing it
98
+ */
80
99
  export interface IFluidErrorAnnotations {
81
- /** Telemetry props to log with the error */
82
- props?: ITelemetryProperties;
100
+ /**
101
+ * Telemetry props to log with the error
102
+ */
103
+ props?: ITelemetryBaseProperties;
83
104
  }
84
105
 
85
- /** For backwards compatibility with pre-errorInstanceId valid errors */
106
+ /**
107
+ * For backwards compatibility with pre-errorInstanceId valid errors
108
+ */
86
109
  function patchLegacyError(
87
110
  legacyError: Omit<IFluidErrorBase, "errorInstanceId">,
88
111
  ): asserts legacyError is IFluidErrorBase {
112
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-explicit-any
89
113
  const patchMe: { -readonly [P in "errorInstanceId"]?: IFluidErrorBase[P] } = legacyError as any;
90
114
  if (patchMe.errorInstanceId === undefined) {
91
115
  patchMe.errorInstanceId = uuid();
@@ -180,8 +204,8 @@ export function generateErrorWithStack(): Error {
180
204
 
181
205
  try {
182
206
  throw err;
183
- } catch (e) {
184
- return e as Error;
207
+ } catch (error) {
208
+ return error as Error;
185
209
  }
186
210
  }
187
211
 
@@ -230,12 +254,16 @@ export function wrapError<T extends LoggingError>(
230
254
  return newError;
231
255
  }
232
256
 
233
- /** The same as wrapError, but also logs the innerError, including the wrapping error's instance id */
257
+ /**
258
+ * The same as wrapError, but also logs the innerError, including the wrapping error's instance ID.
259
+ *
260
+ * @typeParam T - The kind of wrapper error to create.
261
+ */
234
262
  export function wrapErrorAndLog<T extends LoggingError>(
235
263
  innerError: unknown,
236
264
  newErrorFn: (message: string) => T,
237
265
  logger: ITelemetryLoggerExt,
238
- ) {
266
+ ): T {
239
267
  const newError = wrapError(innerError, newErrorFn);
240
268
 
241
269
  // This will match innerError.errorInstanceId if present (see wrapError)
@@ -256,11 +284,11 @@ export function wrapErrorAndLog<T extends LoggingError>(
256
284
  return newError;
257
285
  }
258
286
 
259
- function overwriteStack(error: IFluidErrorBase | LoggingError, stack: string) {
287
+ function overwriteStack(error: IFluidErrorBase | LoggingError, stack: string): void {
260
288
  // supposedly setting stack on an Error can throw.
261
289
  try {
262
290
  Object.assign(error, { stack });
263
- } catch (errorSettingStack) {
291
+ } catch {
264
292
  error.addTelemetryProperties({ stack2: stack });
265
293
  }
266
294
  }
@@ -270,26 +298,26 @@ function overwriteStack(error: IFluidErrorBase | LoggingError, stack: string) {
270
298
  * False for any error we created and raised within the FF codebase via LoggingError base class,
271
299
  * or wrapped in a well-known error type
272
300
  */
273
- export function isExternalError(e: any): boolean {
301
+ export function isExternalError(error: unknown): boolean {
274
302
  // LoggingErrors are an internal FF error type. However, an external error can be converted
275
303
  // into a LoggingError if it is normalized. In this case we must use the untrustedOrigin flag to
276
304
  // determine whether the original error was infact external.
277
- if (LoggingError.typeCheck(e)) {
278
- if ((e as NormalizedLoggingError).errorType === NORMALIZED_ERROR_TYPE) {
279
- return e.getTelemetryProperties().untrustedOrigin === 1;
305
+ if (LoggingError.typeCheck(error)) {
306
+ if ((error as NormalizedLoggingError).errorType === NORMALIZED_ERROR_TYPE) {
307
+ return error.getTelemetryProperties().untrustedOrigin === 1;
280
308
  }
281
309
  return false;
282
310
  }
283
- return !isValidLegacyError(e);
311
+ return !isValidLegacyError(error);
284
312
  }
285
313
 
286
314
  /**
287
315
  * Type guard to identify if a particular telemetry property appears to be a tagged telemetry property
288
316
  */
289
317
  export function isTaggedTelemetryPropertyValue(
290
- x: ITaggedTelemetryPropertyTypeExt | TelemetryEventPropertyTypeExt,
291
- ): x is ITaggedTelemetryPropertyType | ITaggedTelemetryPropertyTypeExt {
292
- return typeof (x as any)?.tag === "string";
318
+ x: Tagged<TelemetryEventPropertyTypeExt> | TelemetryEventPropertyTypeExt,
319
+ ): x is Tagged<TelemetryEventPropertyTypeExt> {
320
+ return typeof (x as Partial<Tagged<unknown>>)?.tag === "string";
293
321
  }
294
322
 
295
323
  /**
@@ -298,7 +326,7 @@ export function isTaggedTelemetryPropertyValue(
298
326
  * @returns - as-is if x is primitive. returns stringified if x is an array of primitive.
299
327
  * otherwise returns null since this is what we support at the moment.
300
328
  */
301
- function filterValidTelemetryProps(x: any, key: string): TelemetryEventPropertyType {
329
+ function filterValidTelemetryProps(x: unknown, key: string): TelemetryBaseEventPropertyType {
302
330
  if (Array.isArray(x) && x.every((val) => isTelemetryEventPropertyValue(val))) {
303
331
  return JSON.stringify(x);
304
332
  }
@@ -311,7 +339,7 @@ function filterValidTelemetryProps(x: any, key: string): TelemetryEventPropertyT
311
339
  }
312
340
 
313
341
  // checking type of x, returns false if x is null
314
- function isTelemetryEventPropertyValue(x: any): x is TelemetryEventPropertyType {
342
+ function isTelemetryEventPropertyValue(x: unknown): x is TelemetryBaseEventPropertyType {
315
343
  switch (typeof x) {
316
344
  case "string":
317
345
  case "number":
@@ -325,13 +353,15 @@ function isTelemetryEventPropertyValue(x: any): x is TelemetryEventPropertyType
325
353
  /**
326
354
  * Walk an object's enumerable properties to find those fit for telemetry.
327
355
  */
328
- function getValidTelemetryProps(obj: any, keysToOmit: Set<string>): ITelemetryProperties {
329
- const props: ITelemetryProperties = {};
356
+ function getValidTelemetryProps(obj: object, keysToOmit: Set<string>): ITelemetryBaseProperties {
357
+ const props: ITelemetryBaseProperties = {};
330
358
  for (const key of Object.keys(obj)) {
331
359
  if (keysToOmit.has(key)) {
332
360
  continue;
333
361
  }
334
- const val = obj[key];
362
+ const val = obj[key] as
363
+ | TelemetryEventPropertyTypeExt
364
+ | Tagged<TelemetryEventPropertyTypeExt>;
335
365
 
336
366
  // ensure only valid props get logged, since props of logging error could be in any shape
337
367
  if (isTaggedTelemetryPropertyValue(val)) {
@@ -353,9 +383,11 @@ function getValidTelemetryProps(obj: any, keysToOmit: Set<string>): ITelemetryPr
353
383
  * Not ideal, as will cut values that are not necessarily circular references.
354
384
  * Could be improved by implementing Node's util.inspect() for browser (minus all the coloring code)
355
385
  */
356
- export const getCircularReplacer = () => {
386
+ // TODO: Use `unknown` instead (API breaking change)
387
+ /* eslint-disable @typescript-eslint/no-explicit-any */
388
+ export const getCircularReplacer = (): ((key: string, value: unknown) => any) => {
357
389
  const seen = new WeakSet();
358
- return (key: string, value: any): any => {
390
+ return (key: string, value: unknown): any => {
359
391
  if (typeof value === "object" && value !== null) {
360
392
  if (seen.has(value)) {
361
393
  return "<removed/circular>";
@@ -365,6 +397,7 @@ export const getCircularReplacer = () => {
365
397
  return value;
366
398
  };
367
399
  };
400
+ /* eslint-enable @typescript-eslint/no-explicit-any */
368
401
 
369
402
  /**
370
403
  * Base class for "trusted" errors we create, whose properties can generally be logged to telemetry safely.
@@ -378,15 +411,18 @@ export class LoggingError
378
411
  implements ILoggingError, Omit<IFluidErrorBase, "errorType">
379
412
  {
380
413
  private _errorInstanceId = uuid();
381
- get errorInstanceId() {
414
+ get errorInstanceId(): string {
382
415
  return this._errorInstanceId;
383
416
  }
384
- overwriteErrorInstanceId(id: string) {
417
+ overwriteErrorInstanceId(id: string): void {
385
418
  this._errorInstanceId = id;
386
419
  }
387
420
 
388
- /** Back-compat to appease isFluidError typeguard in old code that may handle this error */
421
+ /**
422
+ * Backwards compatibility to appease {@link isFluidError} in old code that may handle this error.
423
+ */
389
424
  // @ts-expect-error - This field shouldn't be referenced in the current version, but needs to exist at runtime.
425
+ // eslint-disable-next-line @typescript-eslint/prefer-as-const
390
426
  private readonly fluidErrorCode: "-" = "-";
391
427
 
392
428
  /**
@@ -397,7 +433,7 @@ export class LoggingError
397
433
  */
398
434
  constructor(
399
435
  message: string,
400
- props?: ITelemetryProperties,
436
+ props?: ITelemetryBaseProperties,
401
437
  private readonly omitPropsFromLogging: Set<string> = new Set(),
402
438
  ) {
403
439
  super(message);
@@ -430,14 +466,14 @@ export class LoggingError
430
466
  /**
431
467
  * Add additional properties to be logged
432
468
  */
433
- public addTelemetryProperties(props: ITelemetryProperties) {
469
+ public addTelemetryProperties(props: ITelemetryBaseProperties): void {
434
470
  copyProps(this, props);
435
471
  }
436
472
 
437
473
  /**
438
474
  * Get all properties fit to be logged to telemetry for this error
439
475
  */
440
- public getTelemetryProperties(): ITelemetryProperties {
476
+ public getTelemetryProperties(): ITelemetryBaseProperties {
441
477
  const taggableProps = getValidTelemetryProps(this, this.omitPropsFromLogging);
442
478
  // Include non-enumerable props that are not returned by getValidTelemetryProps
443
479
  return {
@@ -449,7 +485,9 @@ export class LoggingError
449
485
  }
450
486
  }
451
487
 
452
- /** The Error class used when normalizing an external error */
488
+ /**
489
+ * The Error class used when normalizing an external error
490
+ */
453
491
  export const NORMALIZED_ERROR_TYPE = "genericError";
454
492
  class NormalizedLoggingError extends LoggingError {
455
493
  // errorType "genericError" is used as a default value throughout the code.
@@ -2,8 +2,8 @@
2
2
  * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
3
  * Licensed under the MIT License.
4
4
  */
5
- import { IEvent } from "@fluidframework/common-definitions";
6
- import { TypedEventEmitter, EventEmitterEventType } from "@fluidframework/common-utils";
5
+ import { TypedEventEmitter, EventEmitterEventType } from "@fluid-internal/client-utils";
6
+ import { IEvent } from "@fluidframework/core-interfaces";
7
7
 
8
8
  /**
9
9
  * Event Emitter helper class
@@ -14,12 +14,14 @@ export class EventEmitterWithErrorHandling<
14
14
  TEvent extends IEvent = IEvent,
15
15
  > extends TypedEventEmitter<TEvent> {
16
16
  constructor(
17
+ // TODO: use `unknown` instead (breaking API change)
18
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
17
19
  private readonly errorHandler: (eventName: EventEmitterEventType, error: any) => void,
18
20
  ) {
19
21
  super();
20
22
  }
21
23
 
22
- public emit(event: EventEmitterEventType, ...args: any[]): boolean {
24
+ public emit(event: EventEmitterEventType, ...args: unknown[]): boolean {
23
25
  try {
24
26
  return super.emit(event, ...args);
25
27
  } catch (error) {
package/src/events.ts CHANGED
@@ -13,8 +13,8 @@ export function safeRaiseEvent(
13
13
  emitter: EventEmitter,
14
14
  logger: ITelemetryLoggerExt,
15
15
  event: string,
16
- ...args
17
- ) {
16
+ ...args: unknown[]
17
+ ): void {
18
18
  try {
19
19
  emitter.emit(event, ...args);
20
20
  } catch (error) {
@@ -36,7 +36,7 @@ export function raiseConnectedEvent(
36
36
  connected: boolean,
37
37
  clientId?: string,
38
38
  disconnectedReason?: string,
39
- ) {
39
+ ): void {
40
40
  try {
41
41
  if (connected) {
42
42
  emitter.emit(connectedEventName, clientId);