@fluidframework/telemetry-utils 0.57.0-51086 → 0.58.0-55561

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.
@@ -49,13 +49,16 @@ function copyProps(target, source) {
49
49
  }
50
50
  }
51
51
  }
52
- /** For backwards compatibility with pre-fluidErrorCode valid errors */
53
- function patchWithErrorCode(legacyError) {
52
+ /** For backwards compatibility with pre-errorInstanceId valid errors */
53
+ function patchLegacyError(legacyError) {
54
54
  const patchMe = legacyError;
55
- if (patchMe.fluidErrorCode === undefined) {
56
- patchMe.fluidErrorCode = "<error predates fluidErrorCode>";
55
+ if (patchMe.errorInstanceId === undefined) {
56
+ patchMe.errorInstanceId = uuid();
57
57
  }
58
58
  }
59
+ // errorType "genericError" is used as a default value throughout the code.
60
+ // Note that this matches ContainerErrorType/DriverErrorType's genericError
61
+ const defaultErrorTypeForNormalize = "genericError";
59
62
  /**
60
63
  * Normalize the given error yielding a valid Fluid Error
61
64
  * @returns A valid Fluid Error with any provided annotations applied
@@ -66,7 +69,7 @@ export function normalizeError(error, annotations = {}) {
66
69
  var _a;
67
70
  // Back-compat, while IFluidErrorBase is rolled out
68
71
  if (isValidLegacyError(error)) {
69
- patchWithErrorCode(error);
72
+ patchLegacyError(error);
70
73
  }
71
74
  if (isFluidError(error)) {
72
75
  // We can simply add the telemetry props to the error and return it
@@ -76,8 +79,7 @@ export function normalizeError(error, annotations = {}) {
76
79
  // We have to construct a new Fluid Error, copying safe properties over
77
80
  const { message, stack } = extractLogSafeErrorProperties(error, false /* sanitizeStack */);
78
81
  const fluidError = new SimpleFluidError({
79
- errorType: "genericError",
80
- fluidErrorCode: "",
82
+ errorType: defaultErrorTypeForNormalize,
81
83
  message,
82
84
  stack,
83
85
  });
@@ -130,7 +132,14 @@ export function wrapError(innerError, newErrorFn) {
130
132
  if (stack !== undefined) {
131
133
  overwriteStack(newError, stack);
132
134
  }
135
+ // Mark external errors with untrustedOrigin flag
136
+ if (originatedAsExternalError(innerError)) {
137
+ newError.addTelemetryProperties({ untrustedOrigin: 1 });
138
+ }
139
+ // Reuse errorInstanceId
133
140
  if (hasErrorInstanceId(innerError)) {
141
+ newError.overwriteErrorInstanceId(innerError.errorInstanceId);
142
+ // For "back-compat" in the logs
134
143
  newError.addTelemetryProperties({ innerErrorInstanceId: innerError.errorInstanceId });
135
144
  }
136
145
  return newError;
@@ -138,11 +147,13 @@ export function wrapError(innerError, newErrorFn) {
138
147
  /** The same as wrapError, but also logs the innerError, including the wrapping error's instance id */
139
148
  export function wrapErrorAndLog(innerError, newErrorFn, logger) {
140
149
  const newError = wrapError(innerError, newErrorFn);
141
- const wrappedByErrorInstanceId = hasErrorInstanceId(newError)
142
- ? newError.errorInstanceId
143
- : undefined;
150
+ // This will match innerError.errorInstanceId if present (see wrapError)
151
+ const errorInstanceId = newError.errorInstanceId;
152
+ // For "back-compat" in the logs
153
+ const wrappedByErrorInstanceId = errorInstanceId;
144
154
  logger.sendTelemetryEvent({
145
155
  eventName: "WrapError",
156
+ errorInstanceId,
146
157
  wrappedByErrorInstanceId,
147
158
  }, innerError);
148
159
  return newError;
@@ -156,6 +167,22 @@ function overwriteStack(error, stack) {
156
167
  error.addTelemetryProperties({ stack2: stack });
157
168
  }
158
169
  }
170
+ /**
171
+ * True for any error object that is either external itself or is a wrapped/normalized external error
172
+ * False for any error we created and raised within the FF codebase.
173
+ */
174
+ export function originatedAsExternalError(e) {
175
+ return !isValidLegacyError(e) || (e.getTelemetryProperties().untrustedOrigin === 1);
176
+ }
177
+ /**
178
+ * True for any error object that is an (optionally normalized) external error
179
+ * False for any error we created and raised within the FF codebase, or wrapped in a well-known error type
180
+ */
181
+ export function isExternalError(e) {
182
+ return !isValidLegacyError(e) ||
183
+ (e.getTelemetryProperties().untrustedOrigin === 1 &&
184
+ e.errorType === defaultErrorTypeForNormalize);
185
+ }
159
186
  /**
160
187
  * Type guard to identify if a particular value (loosely) appears to be a tagged telemetry property
161
188
  */
@@ -229,13 +256,19 @@ export class LoggingError extends Error {
229
256
  constructor(message, props, omitPropsFromLogging = new Set()) {
230
257
  super(message);
231
258
  this.omitPropsFromLogging = omitPropsFromLogging;
232
- this.errorInstanceId = uuid();
233
- // Don't log this list itself either
259
+ this._errorInstanceId = uuid();
260
+ /** Back-compat to appease isFluidError typeguard in old code that may handle this error */
261
+ // @ts-expect-error - This field shouldn't be referenced in the current version, but needs to exist at runtime.
262
+ this.fluidErrorCode = "-";
263
+ // Don't log this list itself, or the private _errorInstanceId
234
264
  omitPropsFromLogging.add("omitPropsFromLogging");
265
+ omitPropsFromLogging.add("_errorInstanceId");
235
266
  if (props) {
236
267
  this.addTelemetryProperties(props);
237
268
  }
238
269
  }
270
+ get errorInstanceId() { return this._errorInstanceId; }
271
+ overwriteErrorInstanceId(id) { this._errorInstanceId = id; }
239
272
  /**
240
273
  * Add additional properties to be logged
241
274
  */
@@ -247,8 +280,8 @@ export class LoggingError extends Error {
247
280
  */
248
281
  getTelemetryProperties() {
249
282
  const taggableProps = getValidTelemetryProps(this, this.omitPropsFromLogging);
250
- // Include non-enumerable props inherited from Error that are not returned by getValidTelemetryProps
251
- return Object.assign(Object.assign({}, taggableProps), { stack: this.stack, message: this.message });
283
+ // Include non-enumerable props that are not returned by getValidTelemetryProps
284
+ return Object.assign(Object.assign({}, taggableProps), { stack: this.stack, message: this.message, errorInstanceId: this._errorInstanceId });
252
285
  }
253
286
  }
254
287
  /** Simple implementation of IFluidErrorBase, extending LoggingError */
@@ -256,7 +289,6 @@ class SimpleFluidError extends LoggingError {
256
289
  constructor(errorProps) {
257
290
  super(errorProps.message);
258
291
  this.errorType = errorProps.errorType;
259
- this.fluidErrorCode = errorProps.fluidErrorCode;
260
292
  if (errorProps.stack !== undefined) {
261
293
  overwriteStack(this, errorProps.stack);
262
294
  }
@@ -1 +1 @@
1
- {"version":3,"file":"errorLogging.js","sourceRoot":"","sources":["../src/errorLogging.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 ILoggingError,\n ITaggedTelemetryPropertyType,\n ITelemetryLogger,\n ITelemetryProperties,\n} from \"@fluidframework/common-definitions\";\nimport { v4 as uuid } from \"uuid\";\nimport {\n hasErrorInstanceId,\n IFluidErrorBase,\n isFluidError,\n isValidLegacyError,\n} from \"./fluidErrorBase\";\n\n/** @returns true if value is an object but neither null nor an array */\nconst isRegularObject = (value: any): boolean => {\n return value !== null && !Array.isArray(value) && typeof value === \"object\";\n};\n\n/** Inspect the given error for common \"safe\" props and return them */\nexport function extractLogSafeErrorProperties(error: any, sanitizeStack: boolean) {\n const removeMessageFromStack = (stack: string, errorName?: string) => {\n if (!sanitizeStack) {\n return stack;\n }\n const stackFrames = stack.split(\"\\n\");\n stackFrames.shift(); // Remove \"[ErrorName]: [ErrorMessage]\"\n if (errorName !== undefined) {\n stackFrames.unshift(errorName); // Add \"[ErrorName]\"\n }\n return stackFrames.join(\"\\n\");\n };\n\n const message = (typeof error?.message === \"string\")\n ? error.message as string\n : String(error);\n\n const safeProps: { message: string; errorType?: string; stack?: string } = {\n message,\n };\n\n if (isRegularObject(error)) {\n const { errorType, stack, name } = error;\n\n if (typeof errorType === \"string\") {\n safeProps.errorType = errorType;\n }\n\n if (typeof stack === \"string\") {\n const errorName = (typeof name === \"string\") ? name : undefined;\n safeProps.stack = removeMessageFromStack(stack, errorName);\n }\n }\n\n return safeProps;\n}\n\n/** type guard for ILoggingError interface */\nexport const isILoggingError = (x: any): x is ILoggingError => typeof x?.getTelemetryProperties === \"function\";\n\n/** Copy props from source onto target, but do not overwrite an existing prop that matches */\nfunction copyProps(target: ITelemetryProperties | LoggingError, source: ITelemetryProperties) {\n for (const key of Object.keys(source)) {\n if (target[key] === undefined) {\n target[key] = source[key];\n }\n }\n}\n\n/** Metadata to annotate an error object when annotating or normalizing it */\nexport interface IFluidErrorAnnotations {\n /** Telemetry props to log with the error */\n props?: ITelemetryProperties;\n}\n\n/** For backwards compatibility with pre-fluidErrorCode valid errors */\nfunction patchWithErrorCode(\n legacyError: Omit<IFluidErrorBase, \"fluidErrorCode\">,\n): asserts legacyError is IFluidErrorBase {\n const patchMe: { -readonly [P in \"fluidErrorCode\"]?: IFluidErrorBase[P] } = legacyError as any;\n if (patchMe.fluidErrorCode === undefined) {\n patchMe.fluidErrorCode = \"<error predates fluidErrorCode>\";\n }\n}\n\n/**\n * Normalize the given error yielding a valid Fluid Error\n * @returns A valid Fluid Error with any provided annotations applied\n * @param error - The error to normalize\n * @param annotations - Annotations to apply to the normalized error\n */\nexport function normalizeError(\n error: unknown,\n annotations: IFluidErrorAnnotations = {},\n): IFluidErrorBase {\n // Back-compat, while IFluidErrorBase is rolled out\n if (isValidLegacyError(error)) {\n patchWithErrorCode(error);\n }\n\n if (isFluidError(error)) {\n // We can simply add the telemetry props to the error and return it\n error.addTelemetryProperties(annotations.props ?? {});\n return error;\n }\n\n // We have to construct a new Fluid Error, copying safe properties over\n const { message, stack } = extractLogSafeErrorProperties(error, false /* sanitizeStack */);\n const fluidError: IFluidErrorBase = new SimpleFluidError({\n errorType: \"genericError\", // Match Container/Driver generic error type\n fluidErrorCode: \"\",\n message,\n stack,\n });\n\n fluidError.addTelemetryProperties({\n ...annotations.props,\n untrustedOrigin: 1, // This will let us filter to errors not originated by our own code\n });\n\n if (typeof(error) !== \"object\") {\n // This is only interesting for non-objects\n fluidError.addTelemetryProperties({ typeofError: typeof(error) });\n }\n return fluidError;\n}\n\nlet stackPopulatedOnCreation: boolean | undefined;\n\n/**\n * The purpose of this function is to provide ability to capture stack context quickly.\n * Accessing new Error().stack is slow, and the slowest part is accessing stack property itself.\n * There are scenarios where we generate error with stack, but error is handled in most cases and\n * stack property is not accessed.\n * For such cases it's better to not read stack property right away, but rather delay it until / if it's needed\n * Some browsers will populate stack right away, others require throwing Error, so we do auto-detection on the fly.\n * @returns Error object that has stack populated.\n */\n export function generateErrorWithStack(): Error {\n const err = new Error(\"<<generated stack>>\");\n\n if (stackPopulatedOnCreation === undefined) {\n stackPopulatedOnCreation = (err.stack !== undefined);\n }\n\n if (stackPopulatedOnCreation) {\n return err;\n }\n\n try {\n throw err;\n } catch (e) {\n return e as Error;\n }\n}\n\nexport function generateStack(): string | undefined {\n return generateErrorWithStack().stack;\n}\n\n/**\n * Create a new error, wrapping and caused by the given unknown error.\n * Copies the inner error's message and stack over but otherwise uses newErrorFn to define the error.\n * The inner error's instance id will also be logged for telemetry analysis.\n * @param innerError - An error from untrusted/unknown origins\n * @param newErrorFn - callback that will create a new error given the original error's message\n * @returns A new error object \"wrapping\" the given error\n */\n export function wrapError<T extends IFluidErrorBase>(\n innerError: unknown,\n newErrorFn: (message: string) => T,\n): T {\n const {\n message,\n stack,\n } = extractLogSafeErrorProperties(innerError, false /* sanitizeStack */);\n\n const newError = newErrorFn(message);\n\n if (stack !== undefined) {\n overwriteStack(newError, stack);\n }\n\n if (hasErrorInstanceId(innerError)) {\n newError.addTelemetryProperties({ innerErrorInstanceId: innerError.errorInstanceId });\n }\n\n return newError;\n}\n\n/** The same as wrapError, but also logs the innerError, including the wrapping error's instance id */\nexport function wrapErrorAndLog<T extends IFluidErrorBase>(\n innerError: unknown,\n newErrorFn: (message: string) => T,\n logger: ITelemetryLogger,\n) {\n const newError = wrapError(innerError, newErrorFn);\n const wrappedByErrorInstanceId = hasErrorInstanceId(newError)\n ? newError.errorInstanceId\n : undefined;\n\n logger.sendTelemetryEvent({\n eventName: \"WrapError\",\n wrappedByErrorInstanceId,\n }, innerError);\n\n return newError;\n}\n\nfunction overwriteStack(error: IFluidErrorBase, stack: string) {\n // supposedly setting stack on an Error can throw.\n try {\n Object.assign(error, { stack });\n } catch (errorSettingStack) {\n error.addTelemetryProperties({ stack2: stack });\n }\n}\n\n/**\n * Type guard to identify if a particular value (loosely) appears to be a tagged telemetry property\n */\nexport function isTaggedTelemetryPropertyValue(x: any): x is ITaggedTelemetryPropertyType {\n return (typeof(x?.value) !== \"object\" && typeof(x?.tag) === \"string\");\n}\n\n/**\n * Walk an object's enumerable properties to find those fit for telemetry.\n */\nfunction getValidTelemetryProps(obj: any, keysToOmit: Set<string>): ITelemetryProperties {\n const props: ITelemetryProperties = {};\n for (const key of Object.keys(obj)) {\n if (keysToOmit.has(key)) {\n continue;\n }\n const val = obj[key];\n switch (typeof val) {\n case \"string\":\n case \"number\":\n case \"boolean\":\n case \"undefined\":\n props[key] = val;\n break;\n default: {\n if (isTaggedTelemetryPropertyValue(val)) {\n props[key] = val;\n } else {\n // We don't support logging arbitrary objects\n props[key] = \"REDACTED (arbitrary object)\";\n }\n break;\n }\n }\n }\n return props;\n}\n\n/**\n * Borrowed from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Cyclic_object_value#examples\n * Avoids runtime errors with circular references.\n * Not ideal, as will cut values that are not necessarily circular references.\n * Could be improved by implementing Node's util.inspect() for browser (minus all the coloring code)\n*/\nexport const getCircularReplacer = () => {\n const seen = new WeakSet();\n return (key: string, value: any): any => {\n if (typeof value === \"object\" && value !== null) {\n if (seen.has(value)) {\n return \"<removed/circular>\";\n }\n seen.add(value);\n }\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return value;\n };\n};\n\n/**\n * Base class for \"trusted\" errors we create, whose properties can generally be logged to telemetry safely.\n * All properties set on the object, or passed in (via the constructor or getTelemetryProperties),\n * will be logged in accordance with their tag, if present.\n *\n * PLEASE take care to avoid setting sensitive data on this object without proper tagging!\n */\nexport class LoggingError extends Error implements ILoggingError, Pick<IFluidErrorBase, \"errorInstanceId\"> {\n readonly errorInstanceId = uuid();\n\n /**\n * Create a new LoggingError\n * @param message - Error message to use for Error base class\n * @param props - telemetry props to include on the error for when it's logged\n * @param omitPropsFromLogging - properties by name to omit from telemetry props\n */\n constructor(\n message: string,\n props?: ITelemetryProperties,\n private readonly omitPropsFromLogging: Set<string> = new Set(),\n ) {\n super(message);\n\n // Don't log this list itself either\n omitPropsFromLogging.add(\"omitPropsFromLogging\");\n\n if (props) {\n this.addTelemetryProperties(props);\n }\n }\n\n /**\n * Add additional properties to be logged\n */\n public addTelemetryProperties(props: ITelemetryProperties) {\n copyProps(this, props);\n }\n\n /**\n * Get all properties fit to be logged to telemetry for this error\n */\n public getTelemetryProperties(): ITelemetryProperties {\n const taggableProps = getValidTelemetryProps(this, this.omitPropsFromLogging);\n // Include non-enumerable props inherited from Error that are not returned by getValidTelemetryProps\n return {\n ...taggableProps,\n stack: this.stack,\n message: this.message,\n };\n }\n}\n\n/** Simple implementation of IFluidErrorBase, extending LoggingError */\nclass SimpleFluidError extends LoggingError implements IFluidErrorBase {\n readonly errorType: string;\n readonly fluidErrorCode: string;\n\n constructor(\n errorProps: Omit<IFluidErrorBase,\n | \"getTelemetryProperties\"\n | \"addTelemetryProperties\"\n | \"errorInstanceId\"\n | \"name\">,\n ) {\n super(errorProps.message);\n this.errorType = errorProps.errorType;\n this.fluidErrorCode = errorProps.fluidErrorCode;\n if (errorProps.stack !== undefined) {\n overwriteStack(this, errorProps.stack);\n }\n }\n}\n"]}
1
+ {"version":3,"file":"errorLogging.js","sourceRoot":"","sources":["../src/errorLogging.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAQH,OAAO,EAAE,EAAE,IAAI,IAAI,EAAE,MAAM,MAAM,CAAC;AAClC,OAAO,EACH,kBAAkB,EAElB,YAAY,EACZ,kBAAkB,GACrB,MAAM,kBAAkB,CAAC;AAE1B,wEAAwE;AACxE,MAAM,eAAe,GAAG,CAAC,KAAU,EAAW,EAAE;IAC5C,OAAO,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,OAAO,KAAK,KAAK,QAAQ,CAAC;AAChF,CAAC,CAAC;AAEF,sEAAsE;AACtE,MAAM,UAAU,6BAA6B,CAAC,KAAU,EAAE,aAAsB;IAC5E,MAAM,sBAAsB,GAAG,CAAC,KAAa,EAAE,SAAkB,EAAE,EAAE;QACjE,IAAI,CAAC,aAAa,EAAE;YAChB,OAAO,KAAK,CAAC;SAChB;QACD,MAAM,WAAW,GAAG,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACtC,WAAW,CAAC,KAAK,EAAE,CAAC,CAAC,uCAAuC;QAC5D,IAAI,SAAS,KAAK,SAAS,EAAE;YACzB,WAAW,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,oBAAoB;SACvD;QACD,OAAO,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAClC,CAAC,CAAC;IAEF,MAAM,OAAO,GAAG,CAAC,QAAO,KAAK,aAAL,KAAK,uBAAL,KAAK,CAAE,OAAO,CAAA,KAAK,QAAQ,CAAC;QAChD,CAAC,CAAC,KAAK,CAAC,OAAiB;QACzB,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAEpB,MAAM,SAAS,GAA4D;QACvE,OAAO;KACV,CAAC;IAEF,IAAI,eAAe,CAAC,KAAK,CAAC,EAAE;QACxB,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,KAAK,CAAC;QAEzC,IAAI,OAAO,SAAS,KAAK,QAAQ,EAAE;YAC/B,SAAS,CAAC,SAAS,GAAG,SAAS,CAAC;SACnC;QAED,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE;YAC3B,MAAM,SAAS,GAAG,CAAC,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;YAChE,SAAS,CAAC,KAAK,GAAG,sBAAsB,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;SAC9D;KACJ;IAED,OAAO,SAAS,CAAC;AACrB,CAAC;AAED,6CAA6C;AAC7C,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAM,EAAsB,EAAE,CAAC,QAAO,CAAC,aAAD,CAAC,uBAAD,CAAC,CAAE,sBAAsB,CAAA,KAAK,UAAU,CAAC;AAE/G,6FAA6F;AAC7F,SAAS,SAAS,CAAC,MAA2C,EAAE,MAA4B;IACxF,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE;QACnC,IAAI,MAAM,CAAC,GAAG,CAAC,KAAK,SAAS,EAAE;YAC3B,MAAM,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;SAC7B;KACJ;AACL,CAAC;AAQD,wEAAwE;AACxE,SAAS,gBAAgB,CACrB,WAAqD;IAErD,MAAM,OAAO,GAAgE,WAAkB,CAAC;IAChG,IAAI,OAAO,CAAC,eAAe,KAAK,SAAS,EAAE;QACvC,OAAO,CAAC,eAAe,GAAG,IAAI,EAAE,CAAC;KACpC;AACL,CAAC;AAED,2EAA2E;AAC3E,2EAA2E;AAC3E,MAAM,4BAA4B,GAAG,cAAc,CAAC;AAEpD;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAC1B,KAAc,EACd,cAAsC,EAAE;;IAExC,mDAAmD;IACnD,IAAI,kBAAkB,CAAC,KAAK,CAAC,EAAE;QAC3B,gBAAgB,CAAC,KAAK,CAAC,CAAC;KAC3B;IAED,IAAI,YAAY,CAAC,KAAK,CAAC,EAAE;QACrB,mEAAmE;QACnE,KAAK,CAAC,sBAAsB,OAAC,WAAW,CAAC,KAAK,mCAAI,EAAE,CAAC,CAAC;QACtD,OAAO,KAAK,CAAC;KAChB;IAED,uEAAuE;IACvE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,6BAA6B,CAAC,KAAK,EAAE,KAAK,CAAC,mBAAmB,CAAC,CAAC;IAC3F,MAAM,UAAU,GAAoB,IAAI,gBAAgB,CAAC;QACrD,SAAS,EAAE,4BAA4B;QACvC,OAAO;QACP,KAAK;KACR,CAAC,CAAC;IAEH,UAAU,CAAC,sBAAsB,iCAC1B,WAAW,CAAC,KAAK,KACpB,eAAe,EAAE,CAAC,IACpB,CAAC;IAEH,IAAI,OAAM,CAAC,KAAK,CAAC,KAAK,QAAQ,EAAE;QAC5B,2CAA2C;QAC3C,UAAU,CAAC,sBAAsB,CAAC,EAAE,WAAW,EAAE,OAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;KACrE;IACD,OAAO,UAAU,CAAC;AACtB,CAAC;AAED,IAAI,wBAA6C,CAAC;AAElD;;;;;;;;GAQG;AACF,MAAM,UAAU,sBAAsB;IACnC,MAAM,GAAG,GAAG,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC;IAE7C,IAAI,wBAAwB,KAAK,SAAS,EAAE;QACxC,wBAAwB,GAAG,CAAC,GAAG,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC;KACxD;IAED,IAAI,wBAAwB,EAAE;QAC1B,OAAO,GAAG,CAAC;KACd;IAED,IAAI;QACA,MAAM,GAAG,CAAC;KACb;IAAC,OAAO,CAAC,EAAE;QACR,OAAO,CAAU,CAAC;KACrB;AACL,CAAC;AAED,MAAM,UAAU,aAAa;IACzB,OAAO,sBAAsB,EAAE,CAAC,KAAK,CAAC;AAC1C,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,SAAS,CACrB,UAAmB,EACnB,UAAkC;IAElC,MAAM,EACF,OAAO,EACP,KAAK,GACR,GAAG,6BAA6B,CAAC,UAAU,EAAE,KAAK,CAAC,mBAAmB,CAAC,CAAC;IAEzE,MAAM,QAAQ,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC;IAErC,IAAI,KAAK,KAAK,SAAS,EAAE;QACrB,cAAc,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;KACnC;IAED,iDAAiD;IACjD,IAAI,yBAAyB,CAAC,UAAU,CAAC,EAAE;QACvC,QAAQ,CAAC,sBAAsB,CAAC,EAAE,eAAe,EAAE,CAAC,EAAE,CAAC,CAAC;KAC3D;IAED,wBAAwB;IACxB,IAAI,kBAAkB,CAAC,UAAU,CAAC,EAAE;QAChC,QAAQ,CAAC,wBAAwB,CAAC,UAAU,CAAC,eAAe,CAAC,CAAC;QAE9D,gCAAgC;QAChC,QAAQ,CAAC,sBAAsB,CAAC,EAAE,oBAAoB,EAAE,UAAU,CAAC,eAAe,EAAE,CAAC,CAAC;KACzF;IAED,OAAO,QAAQ,CAAC;AACpB,CAAC;AAED,sGAAsG;AACtG,MAAM,UAAU,eAAe,CAC3B,UAAmB,EACnB,UAAkC,EAClC,MAAwB;IAExB,MAAM,QAAQ,GAAG,SAAS,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;IAEnD,wEAAwE;IACxE,MAAM,eAAe,GAAG,QAAQ,CAAC,eAAe,CAAC;IAEjD,gCAAgC;IAChC,MAAM,wBAAwB,GAAG,eAAe,CAAC;IAEjD,MAAM,CAAC,kBAAkB,CAAC;QACtB,SAAS,EAAE,WAAW;QACtB,eAAe;QACf,wBAAwB;KAC3B,EAAE,UAAU,CAAC,CAAC;IAEf,OAAO,QAAQ,CAAC;AACpB,CAAC;AAED,SAAS,cAAc,CAAC,KAAqC,EAAE,KAAa;IACxE,kDAAkD;IAClD,IAAI;QACA,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;KACnC;IAAC,OAAO,iBAAiB,EAAE;QACxB,KAAK,CAAC,sBAAsB,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;KACnD;AACL,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,yBAAyB,CAAC,CAAM;IAC5C,OAAO,CAAC,kBAAkB,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,sBAAsB,EAAE,CAAC,eAAe,KAAK,CAAC,CAAC,CAAC;AACxF,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,CAAM;IAClC,OAAO,CAAC,kBAAkB,CAAC,CAAC,CAAC;QACzB,CAAC,CAAC,CAAC,sBAAsB,EAAE,CAAC,eAAe,KAAK,CAAC;YAChD,CAAC,CAAC,SAAS,KAAK,4BAA4B,CAAC,CAAC;AACvD,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,8BAA8B,CAAC,CAAM;IACjD,OAAO,CAAC,OAAM,CAAC,CAAC,aAAD,CAAC,uBAAD,CAAC,CAAE,KAAK,CAAC,KAAK,QAAQ,IAAI,OAAM,CAAC,CAAC,aAAD,CAAC,uBAAD,CAAC,CAAE,GAAG,CAAC,KAAK,QAAQ,CAAC,CAAC;AAC1E,CAAC;AAED;;GAEG;AACH,SAAS,sBAAsB,CAAC,GAAQ,EAAE,UAAuB;IAC7D,MAAM,KAAK,GAAyB,EAAE,CAAC;IACvC,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE;QAChC,IAAI,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE;YACrB,SAAS;SACZ;QACD,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC;QACrB,QAAQ,OAAO,GAAG,EAAE;YAChB,KAAK,QAAQ,CAAC;YACd,KAAK,QAAQ,CAAC;YACd,KAAK,SAAS,CAAC;YACf,KAAK,WAAW;gBACZ,KAAK,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC;gBACjB,MAAM;YACV,OAAO,CAAC,CAAC;gBACL,IAAI,8BAA8B,CAAC,GAAG,CAAC,EAAE;oBACrC,KAAK,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC;iBACpB;qBAAM;oBACH,6CAA6C;oBAC7C,KAAK,CAAC,GAAG,CAAC,GAAG,6BAA6B,CAAC;iBAC9C;gBACD,MAAM;aACT;SACJ;KACJ;IACD,OAAO,KAAK,CAAC;AACjB,CAAC;AAED;;;;;EAKE;AACF,MAAM,CAAC,MAAM,mBAAmB,GAAG,GAAG,EAAE;IACpC,MAAM,IAAI,GAAG,IAAI,OAAO,EAAE,CAAC;IAC3B,OAAO,CAAC,GAAW,EAAE,KAAU,EAAO,EAAE;QACpC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,EAAE;YAC7C,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE;gBACjB,OAAO,oBAAoB,CAAC;aAC/B;YACD,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;SACnB;QACD,+DAA+D;QAC/D,OAAO,KAAK,CAAC;IACjB,CAAC,CAAC;AACN,CAAC,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,OAAO,YAAa,SAAQ,KAAK;IASnC;;;;;OAKG;IACH,YACI,OAAe,EACf,KAA4B,EACX,uBAAoC,IAAI,GAAG,EAAE;QAE9D,KAAK,CAAC,OAAO,CAAC,CAAC;QAFE,yBAAoB,GAApB,oBAAoB,CAAyB;QAjB1D,qBAAgB,GAAG,IAAI,EAAE,CAAC;QAIlC,2FAA2F;QAC3F,+GAA+G;QACvG,mBAAc,GAAQ,GAAG,CAAC;QAe9B,8DAA8D;QAC9D,oBAAoB,CAAC,GAAG,CAAC,sBAAsB,CAAC,CAAC;QACjD,oBAAoB,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;QAE7C,IAAI,KAAK,EAAE;YACP,IAAI,CAAC,sBAAsB,CAAC,KAAK,CAAC,CAAC;SACtC;IACL,CAAC;IA3BD,IAAI,eAAe,KAAK,OAAO,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC;IACvD,wBAAwB,CAAC,EAAU,IAAI,IAAI,CAAC,gBAAgB,GAAG,EAAE,CAAC,CAAC,CAAC;IA4BpE;;OAEG;IACI,sBAAsB,CAAC,KAA2B;QACrD,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAC3B,CAAC;IAED;;OAEG;IACI,sBAAsB;QACzB,MAAM,aAAa,GAAG,sBAAsB,CAAC,IAAI,EAAE,IAAI,CAAC,oBAAoB,CAAC,CAAC;QAC9E,+EAA+E;QAC/E,uCACO,aAAa,KAChB,KAAK,EAAE,IAAI,CAAC,KAAK,EACjB,OAAO,EAAE,IAAI,CAAC,OAAO,EACrB,eAAe,EAAE,IAAI,CAAC,gBAAgB,IACxC;IACN,CAAC;CACJ;AAED,uEAAuE;AACvE,MAAM,gBAAiB,SAAQ,YAAY;IAGvC,YACI,UAIC;QAED,KAAK,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;QAC1B,IAAI,CAAC,SAAS,GAAG,UAAU,CAAC,SAAS,CAAC;QACtC,IAAI,UAAU,CAAC,KAAK,KAAK,SAAS,EAAE;YAChC,cAAc,CAAC,IAAI,EAAE,UAAU,CAAC,KAAK,CAAC,CAAC;SAC1C;IACL,CAAC;CACJ","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport {\n ILoggingError,\n ITaggedTelemetryPropertyType,\n ITelemetryLogger,\n ITelemetryProperties,\n} from \"@fluidframework/common-definitions\";\nimport { v4 as uuid } from \"uuid\";\nimport {\n hasErrorInstanceId,\n IFluidErrorBase,\n isFluidError,\n isValidLegacyError,\n} from \"./fluidErrorBase\";\n\n/** @returns true if value is an object but neither null nor an array */\nconst isRegularObject = (value: any): boolean => {\n return value !== null && !Array.isArray(value) && typeof value === \"object\";\n};\n\n/** Inspect the given error for common \"safe\" props and return them */\nexport function extractLogSafeErrorProperties(error: any, sanitizeStack: boolean) {\n const removeMessageFromStack = (stack: string, errorName?: string) => {\n if (!sanitizeStack) {\n return stack;\n }\n const stackFrames = stack.split(\"\\n\");\n stackFrames.shift(); // Remove \"[ErrorName]: [ErrorMessage]\"\n if (errorName !== undefined) {\n stackFrames.unshift(errorName); // Add \"[ErrorName]\"\n }\n return stackFrames.join(\"\\n\");\n };\n\n const message = (typeof error?.message === \"string\")\n ? error.message as string\n : String(error);\n\n const safeProps: { message: string; errorType?: string; stack?: string } = {\n message,\n };\n\n if (isRegularObject(error)) {\n const { errorType, stack, name } = error;\n\n if (typeof errorType === \"string\") {\n safeProps.errorType = errorType;\n }\n\n if (typeof stack === \"string\") {\n const errorName = (typeof name === \"string\") ? name : undefined;\n safeProps.stack = removeMessageFromStack(stack, errorName);\n }\n }\n\n return safeProps;\n}\n\n/** type guard for ILoggingError interface */\nexport const isILoggingError = (x: any): x is ILoggingError => typeof x?.getTelemetryProperties === \"function\";\n\n/** Copy props from source onto target, but do not overwrite an existing prop that matches */\nfunction copyProps(target: ITelemetryProperties | LoggingError, source: ITelemetryProperties) {\n for (const key of Object.keys(source)) {\n if (target[key] === undefined) {\n target[key] = source[key];\n }\n }\n}\n\n/** Metadata to annotate an error object when annotating or normalizing it */\nexport interface IFluidErrorAnnotations {\n /** Telemetry props to log with the error */\n props?: ITelemetryProperties;\n}\n\n/** For backwards compatibility with pre-errorInstanceId valid errors */\nfunction patchLegacyError(\n legacyError: Omit<IFluidErrorBase, \"errorInstanceId\">,\n): asserts legacyError is IFluidErrorBase {\n const patchMe: { -readonly [P in \"errorInstanceId\"]?: IFluidErrorBase[P] } = legacyError as any;\n if (patchMe.errorInstanceId === undefined) {\n patchMe.errorInstanceId = uuid();\n }\n}\n\n// errorType \"genericError\" is used as a default value throughout the code.\n// Note that this matches ContainerErrorType/DriverErrorType's genericError\nconst defaultErrorTypeForNormalize = \"genericError\";\n\n/**\n * Normalize the given error yielding a valid Fluid Error\n * @returns A valid Fluid Error with any provided annotations applied\n * @param error - The error to normalize\n * @param annotations - Annotations to apply to the normalized error\n */\nexport function normalizeError(\n error: unknown,\n annotations: IFluidErrorAnnotations = {},\n): IFluidErrorBase {\n // Back-compat, while IFluidErrorBase is rolled out\n if (isValidLegacyError(error)) {\n patchLegacyError(error);\n }\n\n if (isFluidError(error)) {\n // We can simply add the telemetry props to the error and return it\n error.addTelemetryProperties(annotations.props ?? {});\n return error;\n }\n\n // We have to construct a new Fluid Error, copying safe properties over\n const { message, stack } = extractLogSafeErrorProperties(error, false /* sanitizeStack */);\n const fluidError: IFluidErrorBase = new SimpleFluidError({\n errorType: defaultErrorTypeForNormalize,\n message,\n stack,\n });\n\n fluidError.addTelemetryProperties({\n ...annotations.props,\n untrustedOrigin: 1, // This will let us filter to errors not originated by our own code\n });\n\n if (typeof(error) !== \"object\") {\n // This is only interesting for non-objects\n fluidError.addTelemetryProperties({ typeofError: typeof(error) });\n }\n return fluidError;\n}\n\nlet stackPopulatedOnCreation: boolean | undefined;\n\n/**\n * The purpose of this function is to provide ability to capture stack context quickly.\n * Accessing new Error().stack is slow, and the slowest part is accessing stack property itself.\n * There are scenarios where we generate error with stack, but error is handled in most cases and\n * stack property is not accessed.\n * For such cases it's better to not read stack property right away, but rather delay it until / if it's needed\n * Some browsers will populate stack right away, others require throwing Error, so we do auto-detection on the fly.\n * @returns Error object that has stack populated.\n */\n export function generateErrorWithStack(): Error {\n const err = new Error(\"<<generated stack>>\");\n\n if (stackPopulatedOnCreation === undefined) {\n stackPopulatedOnCreation = (err.stack !== undefined);\n }\n\n if (stackPopulatedOnCreation) {\n return err;\n }\n\n try {\n throw err;\n } catch (e) {\n return e as Error;\n }\n}\n\nexport function generateStack(): string | undefined {\n return generateErrorWithStack().stack;\n}\n\n/**\n * Create a new error, wrapping and caused by the given unknown error.\n * Copies the inner error's message and stack over but otherwise uses newErrorFn to define the error.\n * The inner error's instance id will also be logged for telemetry analysis.\n * @param innerError - An error from untrusted/unknown origins\n * @param newErrorFn - callback that will create a new error given the original error's message\n * @returns A new error object \"wrapping\" the given error\n */\nexport function wrapError<T extends LoggingError>(\n innerError: unknown,\n newErrorFn: (message: string) => T,\n): T {\n const {\n message,\n stack,\n } = extractLogSafeErrorProperties(innerError, false /* sanitizeStack */);\n\n const newError = newErrorFn(message);\n\n if (stack !== undefined) {\n overwriteStack(newError, stack);\n }\n\n // Mark external errors with untrustedOrigin flag\n if (originatedAsExternalError(innerError)) {\n newError.addTelemetryProperties({ untrustedOrigin: 1 });\n }\n\n // Reuse errorInstanceId\n if (hasErrorInstanceId(innerError)) {\n newError.overwriteErrorInstanceId(innerError.errorInstanceId);\n\n // For \"back-compat\" in the logs\n newError.addTelemetryProperties({ innerErrorInstanceId: innerError.errorInstanceId });\n }\n\n return newError;\n}\n\n/** The same as wrapError, but also logs the innerError, including the wrapping error's instance id */\nexport function wrapErrorAndLog<T extends LoggingError>(\n innerError: unknown,\n newErrorFn: (message: string) => T,\n logger: ITelemetryLogger,\n) {\n const newError = wrapError(innerError, newErrorFn);\n\n // This will match innerError.errorInstanceId if present (see wrapError)\n const errorInstanceId = newError.errorInstanceId;\n\n // For \"back-compat\" in the logs\n const wrappedByErrorInstanceId = errorInstanceId;\n\n logger.sendTelemetryEvent({\n eventName: \"WrapError\",\n errorInstanceId,\n wrappedByErrorInstanceId,\n }, innerError);\n\n return newError;\n}\n\nfunction overwriteStack(error: IFluidErrorBase | LoggingError, stack: string) {\n // supposedly setting stack on an Error can throw.\n try {\n Object.assign(error, { stack });\n } catch (errorSettingStack) {\n error.addTelemetryProperties({ stack2: stack });\n }\n}\n\n/**\n * True for any error object that is either external itself or is a wrapped/normalized external error\n * False for any error we created and raised within the FF codebase.\n */\nexport function originatedAsExternalError(e: any): boolean {\n return !isValidLegacyError(e) || (e.getTelemetryProperties().untrustedOrigin === 1);\n}\n\n/**\n * True for any error object that is an (optionally normalized) external error\n * False for any error we created and raised within the FF codebase, or wrapped in a well-known error type\n */\nexport function isExternalError(e: any): boolean {\n return !isValidLegacyError(e) ||\n (e.getTelemetryProperties().untrustedOrigin === 1 &&\n e.errorType === defaultErrorTypeForNormalize);\n}\n\n/**\n * Type guard to identify if a particular value (loosely) appears to be a tagged telemetry property\n */\nexport function isTaggedTelemetryPropertyValue(x: any): x is ITaggedTelemetryPropertyType {\n return (typeof(x?.value) !== \"object\" && typeof(x?.tag) === \"string\");\n}\n\n/**\n * Walk an object's enumerable properties to find those fit for telemetry.\n */\nfunction getValidTelemetryProps(obj: any, keysToOmit: Set<string>): ITelemetryProperties {\n const props: ITelemetryProperties = {};\n for (const key of Object.keys(obj)) {\n if (keysToOmit.has(key)) {\n continue;\n }\n const val = obj[key];\n switch (typeof val) {\n case \"string\":\n case \"number\":\n case \"boolean\":\n case \"undefined\":\n props[key] = val;\n break;\n default: {\n if (isTaggedTelemetryPropertyValue(val)) {\n props[key] = val;\n } else {\n // We don't support logging arbitrary objects\n props[key] = \"REDACTED (arbitrary object)\";\n }\n break;\n }\n }\n }\n return props;\n}\n\n/**\n * Borrowed from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Cyclic_object_value#examples\n * Avoids runtime errors with circular references.\n * Not ideal, as will cut values that are not necessarily circular references.\n * Could be improved by implementing Node's util.inspect() for browser (minus all the coloring code)\n*/\nexport const getCircularReplacer = () => {\n const seen = new WeakSet();\n return (key: string, value: any): any => {\n if (typeof value === \"object\" && value !== null) {\n if (seen.has(value)) {\n return \"<removed/circular>\";\n }\n seen.add(value);\n }\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return value;\n };\n};\n\n/**\n * Base class for \"trusted\" errors we create, whose properties can generally be logged to telemetry safely.\n * All properties set on the object, or passed in (via the constructor or getTelemetryProperties),\n * will be logged in accordance with their tag, if present.\n *\n * PLEASE take care to avoid setting sensitive data on this object without proper tagging!\n */\nexport class LoggingError extends Error implements ILoggingError, Pick<IFluidErrorBase, \"errorInstanceId\"> {\n private _errorInstanceId = uuid();\n get errorInstanceId() { return this._errorInstanceId; }\n overwriteErrorInstanceId(id: string) { this._errorInstanceId = id; }\n\n /** Back-compat to appease isFluidError typeguard in old code that may handle this error */\n // @ts-expect-error - This field shouldn't be referenced in the current version, but needs to exist at runtime.\n private fluidErrorCode: \"-\" = \"-\";\n\n /**\n * Create a new LoggingError\n * @param message - Error message to use for Error base class\n * @param props - telemetry props to include on the error for when it's logged\n * @param omitPropsFromLogging - properties by name to omit from telemetry props\n */\n constructor(\n message: string,\n props?: ITelemetryProperties,\n private readonly omitPropsFromLogging: Set<string> = new Set(),\n ) {\n super(message);\n\n // Don't log this list itself, or the private _errorInstanceId\n omitPropsFromLogging.add(\"omitPropsFromLogging\");\n omitPropsFromLogging.add(\"_errorInstanceId\");\n\n if (props) {\n this.addTelemetryProperties(props);\n }\n }\n\n /**\n * Add additional properties to be logged\n */\n public addTelemetryProperties(props: ITelemetryProperties) {\n copyProps(this, props);\n }\n\n /**\n * Get all properties fit to be logged to telemetry for this error\n */\n public getTelemetryProperties(): ITelemetryProperties {\n const taggableProps = getValidTelemetryProps(this, this.omitPropsFromLogging);\n // Include non-enumerable props that are not returned by getValidTelemetryProps\n return {\n ...taggableProps,\n stack: this.stack,\n message: this.message,\n errorInstanceId: this._errorInstanceId,\n };\n }\n}\n\n/** Simple implementation of IFluidErrorBase, extending LoggingError */\nclass SimpleFluidError extends LoggingError implements IFluidErrorBase {\n readonly errorType: string;\n\n constructor(\n errorProps: Pick<IFluidErrorBase,\n | \"message\"\n | \"stack\"\n | \"errorType\"\n >,\n ) {\n super(errorProps.message);\n this.errorType = errorProps.errorType;\n if (errorProps.stack !== undefined) {\n overwriteStack(this, errorProps.stack);\n }\n }\n}\n"]}
@@ -5,18 +5,17 @@
5
5
  import { ITelemetryProperties } from "@fluidframework/common-definitions";
6
6
  /**
7
7
  * All normalized errors flowing through the Fluid Framework adhere to this readonly interface.
8
- * It features errorType, fluidErrorCode, and message strings, plus Error's members as optional
8
+ * It features errorType and errorInstanceId on top of Error's members as readonly,
9
9
  * and a getter/setter for telemetry props to be included when the error is logged.
10
10
  */
11
11
  export interface IFluidErrorBase extends Error {
12
12
  /** Classification of what type of error this is, used programmatically by consumers to interpret the error */
13
13
  readonly errorType: string;
14
14
  /**
15
- * Indicates a point in code where this error originated.
16
- * Avoid crafting these via string format or otherwise including variable data, so they're easy to find the code.
15
+ * Error's message property, made readonly.
16
+ * Be specific, but also take care when including variable data to consider suitability for aggregation in telemetry
17
+ * Also avoid including any data that jeopardizes the user's privacy. Add a tagged telemetry property instead.
17
18
  */
18
- readonly fluidErrorCode: string;
19
- /** The free-form error message */
20
19
  readonly message: string;
21
20
  /** Error's stack property, made readonly */
22
21
  readonly stack?: string;
@@ -39,5 +38,5 @@ export declare const hasErrorInstanceId: (x: any) => x is {
39
38
  /** type guard for IFluidErrorBase interface */
40
39
  export declare function isFluidError(e: any): e is IFluidErrorBase;
41
40
  /** type guard for old standard of valid/known errors */
42
- export declare function isValidLegacyError(e: any): e is Omit<IFluidErrorBase, "fluidErrorCode">;
41
+ export declare function isValidLegacyError(e: any): e is Omit<IFluidErrorBase, "errorInstanceId">;
43
42
  //# sourceMappingURL=fluidErrorBase.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"fluidErrorBase.d.ts","sourceRoot":"","sources":["../src/fluidErrorBase.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,oCAAoC,CAAC;AAE1E;;;;GAIG;AACH,MAAM,WAAW,eAAgB,SAAQ,KAAK;IAC1C,8GAA8G;IAC9G,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAE3B;;;OAGG;IACH,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAEhC,kCAAkC;IAClC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAEzB,4CAA4C;IAC5C,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAExB,2CAA2C;IAC3C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAEtB;;;;OAIG;IACH,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IAEjC,qEAAqE;IACrE,sBAAsB,IAAI,oBAAoB,CAAC;IAC/C,iFAAiF;IACjF,sBAAsB,EAAE,CAAC,KAAK,EAAE,oBAAoB,KAAK,IAAI,CAAC;CACjE;AAMD,eAAO,MAAM,kBAAkB,MAAO,GAAG;qBAA2B,MAAM;CAChC,CAAC;AAE3C,+CAA+C;AAC/C,wBAAgB,YAAY,CAAC,CAAC,EAAE,GAAG,GAAG,CAAC,IAAI,eAAe,CAMzD;AAED,wDAAwD;AACxD,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,GAAG,GAAG,CAAC,IAAI,IAAI,CAAC,eAAe,EAAE,gBAAgB,CAAC,CAIvF"}
1
+ {"version":3,"file":"fluidErrorBase.d.ts","sourceRoot":"","sources":["../src/fluidErrorBase.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,oCAAoC,CAAC;AAE1E;;;;GAIG;AACH,MAAM,WAAW,eAAgB,SAAQ,KAAK;IAC1C,8GAA8G;IAC9G,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAE3B;;;;OAIG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAEzB,4CAA4C;IAC5C,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAExB,2CAA2C;IAC3C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAEtB;;;;OAIG;IACH,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IAEjC,qEAAqE;IACrE,sBAAsB,IAAI,oBAAoB,CAAC;IAC/C,iFAAiF;IACjF,sBAAsB,EAAE,CAAC,KAAK,EAAE,oBAAoB,KAAK,IAAI,CAAC;CACjE;AAMD,eAAO,MAAM,kBAAkB,MAAO,GAAG;qBAA2B,MAAM;CAChC,CAAC;AAE3C,+CAA+C;AAC/C,wBAAgB,YAAY,CAAC,CAAC,EAAE,GAAG,GAAG,CAAC,IAAI,eAAe,CAKzD;AAED,wDAAwD;AACxD,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,GAAG,GAAG,CAAC,IAAI,IAAI,CAAC,eAAe,EAAE,iBAAiB,CAAC,CAIxF"}
@@ -8,9 +8,8 @@ export const hasErrorInstanceId = (x) => typeof (x === null || x === void 0 ? vo
8
8
  /** type guard for IFluidErrorBase interface */
9
9
  export function isFluidError(e) {
10
10
  return typeof (e === null || e === void 0 ? void 0 : e.errorType) === "string" &&
11
- typeof (e === null || e === void 0 ? void 0 : e.fluidErrorCode) === "string" &&
12
11
  typeof (e === null || e === void 0 ? void 0 : e.message) === "string" &&
13
- typeof (e === null || e === void 0 ? void 0 : e.errorInstanceId) === "string" &&
12
+ hasErrorInstanceId(e) &&
14
13
  hasTelemetryPropFunctions(e);
15
14
  }
16
15
  /** type guard for old standard of valid/known errors */
@@ -1 +1 @@
1
- {"version":3,"file":"fluidErrorBase.js","sourceRoot":"","sources":["../src/fluidErrorBase.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAyCH,MAAM,yBAAyB,GAAG,CAAC,CAAM,EAAW,EAAE,CAClD,QAAO,CAAC,aAAD,CAAC,uBAAD,CAAC,CAAE,sBAAsB,CAAA,KAAK,UAAU;IAC/C,QAAO,CAAC,aAAD,CAAC,uBAAD,CAAC,CAAE,sBAAsB,CAAA,KAAK,UAAU,CAAC;AAEpD,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAM,EAAoC,EAAE,CAC3E,QAAO,CAAC,aAAD,CAAC,uBAAD,CAAC,CAAE,eAAe,CAAA,KAAK,QAAQ,CAAC;AAE3C,+CAA+C;AAC/C,MAAM,UAAU,YAAY,CAAC,CAAM;IAC/B,OAAO,QAAO,CAAC,aAAD,CAAC,uBAAD,CAAC,CAAE,SAAS,CAAA,KAAK,QAAQ;QACnC,QAAO,CAAC,aAAD,CAAC,uBAAD,CAAC,CAAE,cAAc,CAAA,KAAK,QAAQ;QACrC,QAAO,CAAC,aAAD,CAAC,uBAAD,CAAC,CAAE,OAAO,CAAA,KAAK,QAAQ;QAC9B,QAAO,CAAC,aAAD,CAAC,uBAAD,CAAC,CAAE,eAAe,CAAA,KAAK,QAAQ;QACtC,yBAAyB,CAAC,CAAC,CAAC,CAAC;AACrC,CAAC;AAED,wDAAwD;AACxD,MAAM,UAAU,kBAAkB,CAAC,CAAM;IACrC,OAAO,QAAO,CAAC,aAAD,CAAC,uBAAD,CAAC,CAAE,SAAS,CAAA,KAAK,QAAQ;QACnC,QAAO,CAAC,aAAD,CAAC,uBAAD,CAAC,CAAE,OAAO,CAAA,KAAK,QAAQ;QAC9B,yBAAyB,CAAC,CAAC,CAAC,CAAC;AACrC,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { ITelemetryProperties } from \"@fluidframework/common-definitions\";\n\n/**\n * All normalized errors flowing through the Fluid Framework adhere to this readonly interface.\n * It features errorType, fluidErrorCode, and message strings, plus Error's members as optional\n * and a getter/setter for telemetry props to be included when the error is logged.\n */\nexport interface IFluidErrorBase extends Error {\n /** Classification of what type of error this is, used programmatically by consumers to interpret the error */\n readonly errorType: string;\n\n /**\n * Indicates a point in code where this error originated.\n * Avoid crafting these via string format or otherwise including variable data, so they're easy to find the code.\n */\n readonly fluidErrorCode: string;\n\n /** The free-form error message */\n readonly message: string;\n\n /** Error's stack property, made readonly */\n readonly stack?: string;\n\n /** Error's name property, made readonly */\n readonly name: string;\n\n /**\n * A Guid identifying this error instance.\n * Useful in telemetry for deduping multiple logging events arising from the same error,\n * or correlating an error with an inner error that caused it, in case of error wrapping.\n */\n readonly errorInstanceId: string;\n\n /** Get the telemetry properties stashed on this error for logging */\n getTelemetryProperties(): ITelemetryProperties;\n /** Add telemetry properties to this error which will be logged with the error */\n addTelemetryProperties: (props: ITelemetryProperties) => void;\n}\n\nconst hasTelemetryPropFunctions = (x: any): boolean =>\n typeof x?.getTelemetryProperties === \"function\" &&\n typeof x?.addTelemetryProperties === \"function\";\n\nexport const hasErrorInstanceId = (x: any): x is { errorInstanceId: string } =>\n typeof x?.errorInstanceId === \"string\";\n\n/** type guard for IFluidErrorBase interface */\nexport function isFluidError(e: any): e is IFluidErrorBase {\n return typeof e?.errorType === \"string\" &&\n typeof e?.fluidErrorCode === \"string\" &&\n typeof e?.message === \"string\" &&\n typeof e?.errorInstanceId === \"string\" &&\n hasTelemetryPropFunctions(e);\n}\n\n/** type guard for old standard of valid/known errors */\nexport function isValidLegacyError(e: any): e is Omit<IFluidErrorBase, \"fluidErrorCode\"> {\n return typeof e?.errorType === \"string\" &&\n typeof e?.message === \"string\" &&\n hasTelemetryPropFunctions(e);\n}\n"]}
1
+ {"version":3,"file":"fluidErrorBase.js","sourceRoot":"","sources":["../src/fluidErrorBase.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAuCH,MAAM,yBAAyB,GAAG,CAAC,CAAM,EAAW,EAAE,CAClD,QAAO,CAAC,aAAD,CAAC,uBAAD,CAAC,CAAE,sBAAsB,CAAA,KAAK,UAAU;IAC/C,QAAO,CAAC,aAAD,CAAC,uBAAD,CAAC,CAAE,sBAAsB,CAAA,KAAK,UAAU,CAAC;AAEpD,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAM,EAAoC,EAAE,CAC3E,QAAO,CAAC,aAAD,CAAC,uBAAD,CAAC,CAAE,eAAe,CAAA,KAAK,QAAQ,CAAC;AAE3C,+CAA+C;AAC/C,MAAM,UAAU,YAAY,CAAC,CAAM;IAC/B,OAAO,QAAO,CAAC,aAAD,CAAC,uBAAD,CAAC,CAAE,SAAS,CAAA,KAAK,QAAQ;QACnC,QAAO,CAAC,aAAD,CAAC,uBAAD,CAAC,CAAE,OAAO,CAAA,KAAK,QAAQ;QAC9B,kBAAkB,CAAC,CAAC,CAAC;QACrB,yBAAyB,CAAC,CAAC,CAAC,CAAC;AACrC,CAAC;AAED,wDAAwD;AACxD,MAAM,UAAU,kBAAkB,CAAC,CAAM;IACrC,OAAO,QAAO,CAAC,aAAD,CAAC,uBAAD,CAAC,CAAE,SAAS,CAAA,KAAK,QAAQ;QACnC,QAAO,CAAC,aAAD,CAAC,uBAAD,CAAC,CAAE,OAAO,CAAA,KAAK,QAAQ;QAC9B,yBAAyB,CAAC,CAAC,CAAC,CAAC;AACrC,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { ITelemetryProperties } from \"@fluidframework/common-definitions\";\n\n/**\n * All normalized errors flowing through the Fluid Framework adhere to this readonly interface.\n * It features errorType and errorInstanceId on top of Error's members as readonly,\n * and a getter/setter for telemetry props to be included when the error is logged.\n */\nexport interface IFluidErrorBase extends Error {\n /** Classification of what type of error this is, used programmatically by consumers to interpret the error */\n readonly errorType: string;\n\n /**\n * Error's message property, made readonly.\n * Be specific, but also take care when including variable data to consider suitability for aggregation in telemetry\n * Also avoid including any data that jeopardizes the user's privacy. Add a tagged telemetry property instead.\n */\n readonly message: string;\n\n /** Error's stack property, made readonly */\n readonly stack?: string;\n\n /** Error's name property, made readonly */\n readonly name: string;\n\n /**\n * A Guid identifying this error instance.\n * Useful in telemetry for deduping multiple logging events arising from the same error,\n * or correlating an error with an inner error that caused it, in case of error wrapping.\n */\n readonly errorInstanceId: string;\n\n /** Get the telemetry properties stashed on this error for logging */\n getTelemetryProperties(): ITelemetryProperties;\n /** Add telemetry properties to this error which will be logged with the error */\n addTelemetryProperties: (props: ITelemetryProperties) => void;\n}\n\nconst hasTelemetryPropFunctions = (x: any): boolean =>\n typeof x?.getTelemetryProperties === \"function\" &&\n typeof x?.addTelemetryProperties === \"function\";\n\nexport const hasErrorInstanceId = (x: any): x is { errorInstanceId: string } =>\n typeof x?.errorInstanceId === \"string\";\n\n/** type guard for IFluidErrorBase interface */\nexport function isFluidError(e: any): e is IFluidErrorBase {\n return typeof e?.errorType === \"string\" &&\n typeof e?.message === \"string\" &&\n hasErrorInstanceId(e) &&\n hasTelemetryPropFunctions(e);\n}\n\n/** type guard for old standard of valid/known errors */\nexport function isValidLegacyError(e: any): e is Omit<IFluidErrorBase, \"errorInstanceId\"> {\n return typeof e?.errorType === \"string\" &&\n typeof e?.message === \"string\" &&\n hasTelemetryPropFunctions(e);\n}\n"]}
@@ -21,7 +21,7 @@ export declare class MockLogger extends TelemetryLogger implements ITelemetryLog
21
21
  */
22
22
  matchEvents(expectedEvents: Omit<ITelemetryBaseEvent, "category">[]): boolean;
23
23
  /** Asserts that matchEvents is true, and prints the actual/expected output if not */
24
- assertMatch(expectedEvents: Omit<ITelemetryBaseEvent, "category">[]): void;
24
+ assertMatch(expectedEvents: Omit<ITelemetryBaseEvent, "category">[], message?: string): void;
25
25
  /**
26
26
  * Search events logged since the last time matchEvents was called, looking for any of the given
27
27
  * expected events.
@@ -32,7 +32,7 @@ export declare class MockLogger extends TelemetryLogger implements ITelemetryLog
32
32
  */
33
33
  matchAnyEvent(expectedEvents: Omit<ITelemetryBaseEvent, "category">[]): boolean;
34
34
  /** Asserts that matchAnyEvent is true, and prints the actual/expected output if not */
35
- assertMatchAny(expectedEvents: Omit<ITelemetryBaseEvent, "category">[]): void;
35
+ assertMatchAny(expectedEvents: Omit<ITelemetryBaseEvent, "category">[], message?: string): void;
36
36
  private getMatchedEventsCount;
37
37
  /**
38
38
  * Ensure the expected event is a strict subset of the actual event
@@ -1 +1 @@
1
- {"version":3,"file":"mockLogger.d.ts","sourceRoot":"","sources":["../src/mockLogger.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,oCAAoC,CAAC;AAE3F,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAE3C;;;GAGG;AACH,qBAAa,UAAW,SAAQ,eAAgB,YAAW,gBAAgB;IACvE,MAAM,EAAE,mBAAmB,EAAE,CAAM;;IAInC,IAAI,CAAC,KAAK,EAAE,mBAAmB,GAAG,IAAI;IAItC;;;;;;OAMG;IACH,WAAW,CAAC,cAAc,EAAE,IAAI,CAAC,mBAAmB,EAAE,UAAU,CAAC,EAAE,GAAG,OAAO;IAO7E,qFAAqF;IACrF,WAAW,CAAC,cAAc,EAAE,IAAI,CAAC,mBAAmB,EAAE,UAAU,CAAC,EAAE;IAUnE;;;;;;;OAOG;IACH,aAAa,CAAC,cAAc,EAAE,IAAI,CAAC,mBAAmB,EAAE,UAAU,CAAC,EAAE,GAAG,OAAO;IAK/E,uFAAuF;IACvF,cAAc,CAAC,cAAc,EAAE,IAAI,CAAC,mBAAmB,EAAE,UAAU,CAAC,EAAE;IAWtE,OAAO,CAAC,qBAAqB;IAkB7B;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,WAAW;CAI7B"}
1
+ {"version":3,"file":"mockLogger.d.ts","sourceRoot":"","sources":["../src/mockLogger.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,oCAAoC,CAAC;AAC3F,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAE3C;;;GAGG;AACH,qBAAa,UAAW,SAAQ,eAAgB,YAAW,gBAAgB;IACvE,MAAM,EAAE,mBAAmB,EAAE,CAAM;;IAInC,IAAI,CAAC,KAAK,EAAE,mBAAmB,GAAG,IAAI;IAItC;;;;;;OAMG;IACH,WAAW,CAAC,cAAc,EAAE,IAAI,CAAC,mBAAmB,EAAE,UAAU,CAAC,EAAE,GAAG,OAAO;IAO7E,qFAAqF;IACrF,WAAW,CAAC,cAAc,EAAE,IAAI,CAAC,mBAAmB,EAAE,UAAU,CAAC,EAAE,EAAE,OAAO,CAAC,EAAE,MAAM;IAYrF;;;;;;;OAOG;IACH,aAAa,CAAC,cAAc,EAAE,IAAI,CAAC,mBAAmB,EAAE,UAAU,CAAC,EAAE,GAAG,OAAO;IAK/E,uFAAuF;IACvF,cAAc,CAAC,cAAc,EAAE,IAAI,CAAC,mBAAmB,EAAE,UAAU,CAAC,EAAE,EAAE,OAAO,CAAC,EAAE,MAAM;IAYxF,OAAO,CAAC,qBAAqB;IAkB7B;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,WAAW;CAI7B"}
package/lib/mockLogger.js CHANGED
@@ -2,7 +2,6 @@
2
2
  * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
3
  * Licensed under the MIT License.
4
4
  */
5
- import { assert } from "@fluidframework/common-utils";
6
5
  import { TelemetryLogger } from "./logger";
7
6
  /**
8
7
  * The MockLogger records events sent to it, and then can walk back over those events
@@ -30,14 +29,16 @@ export class MockLogger extends TelemetryLogger {
30
29
  return unmatchedExpectedEventCount === 0;
31
30
  }
32
31
  /** Asserts that matchEvents is true, and prints the actual/expected output if not */
33
- assertMatch(expectedEvents) {
32
+ assertMatch(expectedEvents, message) {
34
33
  const actualEvents = this.events;
35
- assert(this.matchEvents(expectedEvents), `
34
+ if (!this.matchEvents(expectedEvents)) {
35
+ throw new Error(`${message}
36
36
  expected:
37
37
  ${JSON.stringify(expectedEvents)}
38
38
 
39
39
  actual:
40
40
  ${JSON.stringify(actualEvents)}`);
41
+ }
41
42
  }
42
43
  /**
43
44
  * Search events logged since the last time matchEvents was called, looking for any of the given
@@ -52,14 +53,16 @@ ${JSON.stringify(actualEvents)}`);
52
53
  return matchedExpectedEventCount > 0;
53
54
  }
54
55
  /** Asserts that matchAnyEvent is true, and prints the actual/expected output if not */
55
- assertMatchAny(expectedEvents) {
56
+ assertMatchAny(expectedEvents, message) {
56
57
  const actualEvents = this.events;
57
- assert(this.matchAnyEvent(expectedEvents), `
58
+ if (!this.matchAnyEvent(expectedEvents)) {
59
+ throw new Error(`${message}
58
60
  expected:
59
61
  ${JSON.stringify(expectedEvents)}
60
62
 
61
63
  actual:
62
64
  ${JSON.stringify(actualEvents)}`);
65
+ }
63
66
  }
64
67
  getMatchedEventsCount(expectedEvents) {
65
68
  let iExpectedEvent = 0;
@@ -1 +1 @@
1
- {"version":3,"file":"mockLogger.js","sourceRoot":"","sources":["../src/mockLogger.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,MAAM,EAAE,MAAM,8BAA8B,CAAC;AACtD,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAE3C;;;GAGG;AACH,MAAM,OAAO,UAAW,SAAQ,eAAe;IAG3C;QAAgB,KAAK,EAAE,CAAC;QAFxB,WAAM,GAA0B,EAAE,CAAC;IAEV,CAAC;IAE1B,IAAI,CAAC,KAA0B;QAC3B,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC5B,CAAC;IAED;;;;;;OAMG;IACH,WAAW,CAAC,cAAuD;QAC/D,MAAM,yBAAyB,GAAG,IAAI,CAAC,qBAAqB,CAAC,cAAc,CAAC,CAAC;QAC7E,2DAA2D;QAC3D,MAAM,2BAA2B,GAAG,cAAc,CAAC,MAAM,GAAG,yBAAyB,CAAC;QACtF,OAAO,2BAA2B,KAAK,CAAC,CAAC;IAC7C,CAAC;IAED,qFAAqF;IACrF,WAAW,CAAC,cAAuD;QAC/D,MAAM,YAAY,GAAG,IAAI,CAAC,MAAM,CAAC;QACjC,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,cAAc,CAAC,EAAE;;EAE/C,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC;;;EAG9B,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;IAC9B,CAAC;IAED;;;;;;;OAOG;IACH,aAAa,CAAC,cAAuD;QACjE,MAAM,yBAAyB,GAAG,IAAI,CAAC,qBAAqB,CAAC,cAAc,CAAC,CAAC;QAC7E,OAAO,yBAAyB,GAAG,CAAC,CAAC;IACzC,CAAC;IAED,uFAAuF;IACvF,cAAc,CAAC,cAAuD;QAClE,MAAM,YAAY,GAAG,IAAI,CAAC,MAAM,CAAC;QAEjC,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,cAAc,CAAC,EAAE;;EAEjD,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC;;;EAG9B,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;IAC9B,CAAC;IAEO,qBAAqB,CAAC,cAAuD;QACjF,IAAI,cAAc,GAAG,CAAC,CAAC;QACvB,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE;YAC1B,IAAI,cAAc,GAAG,cAAc,CAAC,MAAM;gBACtC,UAAU,CAAC,WAAW,CAAC,KAAK,EAAE,cAAc,CAAC,cAAc,CAAC,CAAC,EAC/D;gBACE,8CAA8C;gBAC9C,EAAE,cAAc,CAAC;aACpB;QACL,CAAC,CAAC,CAAC;QAEH,oFAAoF;QACpF,IAAI,CAAC,MAAM,GAAG,EAAE,CAAC;QAEjB,sCAAsC;QACtC,OAAO,cAAc,CAAC;IAC1B,CAAC;IAED;;OAEG;IACK,MAAM,CAAC,WAAW,CAAC,MAA2B,EAAE,QAA+C;QACnG,MAAM,MAAM,mCAAQ,MAAM,GAAK,QAAQ,CAAE,CAAC;QAC1C,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IAC7D,CAAC;CACJ","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { ITelemetryLogger, ITelemetryBaseEvent } from \"@fluidframework/common-definitions\";\nimport { assert } from \"@fluidframework/common-utils\";\nimport { TelemetryLogger } from \"./logger\";\n\n/**\n * The MockLogger records events sent to it, and then can walk back over those events\n * searching for a set of expected events to match against the logged events.\n */\nexport class MockLogger extends TelemetryLogger implements ITelemetryLogger {\n events: ITelemetryBaseEvent[] = [];\n\n constructor() { super(); }\n\n send(event: ITelemetryBaseEvent): void {\n this.events.push(event);\n }\n\n /**\n * Search events logged since the last time matchEvents was called, looking for the given expected\n * events in order.\n * @param expectedEvents - events in order that are expected to appear in the recorded log.\n * These event objects may be subsets of the logged events.\n * Note: category is ommitted from the type because it's usually uninteresting and tedious to type.\n */\n matchEvents(expectedEvents: Omit<ITelemetryBaseEvent, \"category\">[]): boolean {\n const matchedExpectedEventCount = this.getMatchedEventsCount(expectedEvents);\n // How many expected events were left over? Hopefully none.\n const unmatchedExpectedEventCount = expectedEvents.length - matchedExpectedEventCount;\n return unmatchedExpectedEventCount === 0;\n }\n\n /** Asserts that matchEvents is true, and prints the actual/expected output if not */\n assertMatch(expectedEvents: Omit<ITelemetryBaseEvent, \"category\">[]) {\n const actualEvents = this.events;\n assert(this.matchEvents(expectedEvents), `\nexpected:\n${JSON.stringify(expectedEvents)}\n\nactual:\n${JSON.stringify(actualEvents)}`);\n }\n\n /**\n * Search events logged since the last time matchEvents was called, looking for any of the given\n * expected events.\n * @param expectedEvents - events that are expected to appear in the recorded log.\n * These event objects may be subsets of the logged events.\n * Note: category is ommitted from the type because it's usually uninteresting and tedious to type.\n * @returns if any of the expected events is found.\n */\n matchAnyEvent(expectedEvents: Omit<ITelemetryBaseEvent, \"category\">[]): boolean {\n const matchedExpectedEventCount = this.getMatchedEventsCount(expectedEvents);\n return matchedExpectedEventCount > 0;\n }\n\n /** Asserts that matchAnyEvent is true, and prints the actual/expected output if not */\n assertMatchAny(expectedEvents: Omit<ITelemetryBaseEvent, \"category\">[]) {\n const actualEvents = this.events;\n\n assert(this.matchAnyEvent(expectedEvents), `\nexpected:\n${JSON.stringify(expectedEvents)}\n\nactual:\n${JSON.stringify(actualEvents)}`);\n }\n\n private getMatchedEventsCount(expectedEvents: Omit<ITelemetryBaseEvent, \"category\">[]): number {\n let iExpectedEvent = 0;\n this.events.forEach((event) => {\n if (iExpectedEvent < expectedEvents.length &&\n MockLogger.eventsMatch(event, expectedEvents[iExpectedEvent])\n ) {\n // We found the next expected event; increment\n ++iExpectedEvent;\n }\n });\n\n // Remove the events so far; next call will just compare subsequent events from here\n this.events = [];\n\n // Return the count of matched events.\n return iExpectedEvent;\n }\n\n /**\n * Ensure the expected event is a strict subset of the actual event\n */\n private static eventsMatch(actual: ITelemetryBaseEvent, expected: Omit<ITelemetryBaseEvent, \"category\">): boolean {\n const masked = { ...actual, ...expected };\n return JSON.stringify(masked) === JSON.stringify(actual);\n }\n}\n"]}
1
+ {"version":3,"file":"mockLogger.js","sourceRoot":"","sources":["../src/mockLogger.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAE3C;;;GAGG;AACH,MAAM,OAAO,UAAW,SAAQ,eAAe;IAG3C;QAAgB,KAAK,EAAE,CAAC;QAFxB,WAAM,GAA0B,EAAE,CAAC;IAEV,CAAC;IAE1B,IAAI,CAAC,KAA0B;QAC3B,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC5B,CAAC;IAED;;;;;;OAMG;IACH,WAAW,CAAC,cAAuD;QAC/D,MAAM,yBAAyB,GAAG,IAAI,CAAC,qBAAqB,CAAC,cAAc,CAAC,CAAC;QAC7E,2DAA2D;QAC3D,MAAM,2BAA2B,GAAG,cAAc,CAAC,MAAM,GAAG,yBAAyB,CAAC;QACtF,OAAO,2BAA2B,KAAK,CAAC,CAAC;IAC7C,CAAC;IAED,qFAAqF;IACrF,WAAW,CAAC,cAAuD,EAAE,OAAgB;QACjF,MAAM,YAAY,GAAG,IAAI,CAAC,MAAM,CAAC;QACjC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,cAAc,CAAC,EAAE;YACnC,MAAM,IAAI,KAAK,CAAC,GAAG,OAAO;;EAEpC,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC;;;EAG9B,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;SACzB;IACL,CAAC;IAED;;;;;;;OAOG;IACH,aAAa,CAAC,cAAuD;QACjE,MAAM,yBAAyB,GAAG,IAAI,CAAC,qBAAqB,CAAC,cAAc,CAAC,CAAC;QAC7E,OAAO,yBAAyB,GAAG,CAAC,CAAC;IACzC,CAAC;IAED,uFAAuF;IACvF,cAAc,CAAC,cAAuD,EAAE,OAAgB;QACpF,MAAM,YAAY,GAAG,IAAI,CAAC,MAAM,CAAC;QACjC,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,cAAc,CAAC,EAAE;YACrC,MAAM,IAAI,KAAK,CAAC,GAAG,OAAO;;EAEpC,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC;;;EAG9B,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;SACrB;IACT,CAAC;IAEO,qBAAqB,CAAC,cAAuD;QACjF,IAAI,cAAc,GAAG,CAAC,CAAC;QACvB,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE;YAC1B,IAAI,cAAc,GAAG,cAAc,CAAC,MAAM;gBACtC,UAAU,CAAC,WAAW,CAAC,KAAK,EAAE,cAAc,CAAC,cAAc,CAAC,CAAC,EAC/D;gBACE,8CAA8C;gBAC9C,EAAE,cAAc,CAAC;aACpB;QACL,CAAC,CAAC,CAAC;QAEH,oFAAoF;QACpF,IAAI,CAAC,MAAM,GAAG,EAAE,CAAC;QAEjB,sCAAsC;QACtC,OAAO,cAAc,CAAC;IAC1B,CAAC;IAED;;OAEG;IACK,MAAM,CAAC,WAAW,CAAC,MAA2B,EAAE,QAA+C;QACnG,MAAM,MAAM,mCAAQ,MAAM,GAAK,QAAQ,CAAE,CAAC;QAC1C,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IAC7D,CAAC;CACJ","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { ITelemetryLogger, ITelemetryBaseEvent } from \"@fluidframework/common-definitions\";\nimport { TelemetryLogger } from \"./logger\";\n\n/**\n * The MockLogger records events sent to it, and then can walk back over those events\n * searching for a set of expected events to match against the logged events.\n */\nexport class MockLogger extends TelemetryLogger implements ITelemetryLogger {\n events: ITelemetryBaseEvent[] = [];\n\n constructor() { super(); }\n\n send(event: ITelemetryBaseEvent): void {\n this.events.push(event);\n }\n\n /**\n * Search events logged since the last time matchEvents was called, looking for the given expected\n * events in order.\n * @param expectedEvents - events in order that are expected to appear in the recorded log.\n * These event objects may be subsets of the logged events.\n * Note: category is ommitted from the type because it's usually uninteresting and tedious to type.\n */\n matchEvents(expectedEvents: Omit<ITelemetryBaseEvent, \"category\">[]): boolean {\n const matchedExpectedEventCount = this.getMatchedEventsCount(expectedEvents);\n // How many expected events were left over? Hopefully none.\n const unmatchedExpectedEventCount = expectedEvents.length - matchedExpectedEventCount;\n return unmatchedExpectedEventCount === 0;\n }\n\n /** Asserts that matchEvents is true, and prints the actual/expected output if not */\n assertMatch(expectedEvents: Omit<ITelemetryBaseEvent, \"category\">[], message?: string) {\n const actualEvents = this.events;\n if (!this.matchEvents(expectedEvents)) {\n throw new Error(`${message}\nexpected:\n${JSON.stringify(expectedEvents)}\n\nactual:\n${JSON.stringify(actualEvents)}`);\n }\n }\n\n /**\n * Search events logged since the last time matchEvents was called, looking for any of the given\n * expected events.\n * @param expectedEvents - events that are expected to appear in the recorded log.\n * These event objects may be subsets of the logged events.\n * Note: category is ommitted from the type because it's usually uninteresting and tedious to type.\n * @returns if any of the expected events is found.\n */\n matchAnyEvent(expectedEvents: Omit<ITelemetryBaseEvent, \"category\">[]): boolean {\n const matchedExpectedEventCount = this.getMatchedEventsCount(expectedEvents);\n return matchedExpectedEventCount > 0;\n }\n\n /** Asserts that matchAnyEvent is true, and prints the actual/expected output if not */\n assertMatchAny(expectedEvents: Omit<ITelemetryBaseEvent, \"category\">[], message?: string) {\n const actualEvents = this.events;\n if (!this.matchAnyEvent(expectedEvents)) {\n throw new Error(`${message}\nexpected:\n${JSON.stringify(expectedEvents)}\n\nactual:\n${JSON.stringify(actualEvents)}`);\n }\n }\n\n private getMatchedEventsCount(expectedEvents: Omit<ITelemetryBaseEvent, \"category\">[]): number {\n let iExpectedEvent = 0;\n this.events.forEach((event) => {\n if (iExpectedEvent < expectedEvents.length &&\n MockLogger.eventsMatch(event, expectedEvents[iExpectedEvent])\n ) {\n // We found the next expected event; increment\n ++iExpectedEvent;\n }\n });\n\n // Remove the events so far; next call will just compare subsequent events from here\n this.events = [];\n\n // Return the count of matched events.\n return iExpectedEvent;\n }\n\n /**\n * Ensure the expected event is a strict subset of the actual event\n */\n private static eventsMatch(actual: ITelemetryBaseEvent, expected: Omit<ITelemetryBaseEvent, \"category\">): boolean {\n const masked = { ...actual, ...expected };\n return JSON.stringify(masked) === JSON.stringify(actual);\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/telemetry-utils";
8
- export declare const pkgVersion = "0.57.0-51086";
8
+ export declare const pkgVersion = "0.58.0-55561";
9
9
  //# sourceMappingURL=packageVersion.d.ts.map
@@ -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/telemetry-utils";
8
- export const pkgVersion = "0.57.0-51086";
8
+ export const pkgVersion = "0.58.0-55561";
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,iCAAiC,CAAC;AACzD,MAAM,CAAC,MAAM,UAAU,GAAG,cAAc,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/telemetry-utils\";\nexport const pkgVersion = \"0.57.0-51086\";\n"]}
1
+ {"version":3,"file":"packageVersion.js","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,CAAC,MAAM,OAAO,GAAG,iCAAiC,CAAC;AACzD,MAAM,CAAC,MAAM,UAAU,GAAG,cAAc,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/telemetry-utils\";\nexport const pkgVersion = \"0.58.0-55561\";\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fluidframework/telemetry-utils",
3
- "version": "0.57.0-51086",
3
+ "version": "0.58.0-55561",
4
4
  "description": "Collection of telemetry relates utilities for Fluid",
5
5
  "homepage": "https://fluidframework.com",
6
6
  "repository": "https://github.com/microsoft/FluidFramework",
@@ -68,7 +68,7 @@
68
68
  "devDependencies": {
69
69
  "@fluidframework/build-common": "^0.23.0",
70
70
  "@fluidframework/eslint-config-fluid": "^0.26.0-0",
71
- "@fluidframework/mocha-test-setup": "0.57.0-51086",
71
+ "@fluidframework/mocha-test-setup": "0.58.0-55561",
72
72
  "@microsoft/api-extractor": "^7.16.1",
73
73
  "@rushstack/eslint-config": "^2.5.1",
74
74
  "@types/debug": "^4.1.5",