@fluidframework/core-utils 2.30.0 → 2.31.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +49 -19
- package/dist/assert.d.ts +31 -0
- package/dist/assert.d.ts.map +1 -1
- package/dist/assert.js +55 -3
- package/dist/assert.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/lib/assert.d.ts +31 -0
- package/lib/assert.d.ts.map +1 -1
- package/lib/assert.js +53 -2
- package/lib/assert.js.map +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -1
- package/lib/index.js.map +1 -1
- package/lib/tsdoc-metadata.json +1 -1
- package/package.json +4 -7
- package/src/assert.ts +56 -2
- package/src/index.ts +1 -0
- package/prettier.config.cjs +0 -8
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,35 @@
|
|
|
1
1
|
# @fluidframework/core-utils
|
|
2
2
|
|
|
3
|
+
## 2.31.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- New alpha `onAssertionFailure` API ([#24089](https://github.com/microsoft/FluidFramework/pull/24089)) [5e933c7a7c](https://github.com/microsoft/FluidFramework/commit/5e933c7a7c31ef4a9f0a331604cf329156afb1aa)
|
|
8
|
+
|
|
9
|
+
A new `@alpha` API is added called `onAssertionFailure` which can be used to get a callback when an assertion fails indicating a bug in the Fluid Framework.
|
|
10
|
+
This callback is invoked before the exception is thrown, reducing the chances of the exception being lost or replaced with a different exception before making it to a catch block which reports it.
|
|
11
|
+
It can also be used to break into the debugger when the assertion occurs to aid in debugging the cause.
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { onAssertionFailure } from "fluid-framework/alpha";
|
|
15
|
+
|
|
16
|
+
let firstAssertion: Error | undefined;
|
|
17
|
+
|
|
18
|
+
onAssertionFailure((error: Error) => {
|
|
19
|
+
const priorErrorNote =
|
|
20
|
+
firstAssertion === undefined
|
|
21
|
+
? "Please report this bug."
|
|
22
|
+
: `Might be caused due to prior error ${JSON.stringify(
|
|
23
|
+
firstAssertion.message,
|
|
24
|
+
)} which should be investigated first.`;
|
|
25
|
+
const message = `Encountered Bug in Fluid Framework: ${error.message}\n${priorErrorNote}\n${error.stack}`;
|
|
26
|
+
console.error(message);
|
|
27
|
+
|
|
28
|
+
debugger;
|
|
29
|
+
firstAssertion ??= error;
|
|
30
|
+
});
|
|
31
|
+
```
|
|
32
|
+
|
|
3
33
|
## 2.30.0
|
|
4
34
|
|
|
5
35
|
Dependency updates only.
|
|
@@ -60,9 +90,9 @@ Dependency updates only.
|
|
|
60
90
|
|
|
61
91
|
### Minor Changes
|
|
62
92
|
|
|
63
|
-
-
|
|
93
|
+
- Update to TypeScript 5.4 ([#21214](https://github.com/microsoft/FluidFramework/pull/21214)) [0e6256c722](https://github.com/microsoft/FluidFramework/commit/0e6256c722d8bf024f4325bf02547daeeb18bfa6)
|
|
64
94
|
|
|
65
|
-
|
|
95
|
+
Update package implementations to use TypeScript 5.4.5.
|
|
66
96
|
|
|
67
97
|
## 2.0.0-rc.4.0.0
|
|
68
98
|
|
|
@@ -72,24 +102,24 @@ Dependency updates only.
|
|
|
72
102
|
|
|
73
103
|
### Major Changes
|
|
74
104
|
|
|
75
|
-
-
|
|
105
|
+
- Packages now use package.json "exports" and require modern module resolution [97d68aa06b](https://github.com/microsoft/FluidFramework/commit/97d68aa06bd5c022ecb026655814aea222a062ae)
|
|
76
106
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
107
|
+
Fluid Framework packages have been updated to use the [package.json "exports"
|
|
108
|
+
field](https://nodejs.org/docs/latest-v18.x/api/packages.html#exports) to define explicit entry points for both
|
|
109
|
+
TypeScript types and implementation code.
|
|
80
110
|
|
|
81
|
-
|
|
111
|
+
This means that using Fluid Framework packages require the following TypeScript settings in tsconfig.json:
|
|
82
112
|
|
|
83
|
-
|
|
84
|
-
|
|
113
|
+
- `"moduleResolution": "Node16"` with `"module": "Node16"`
|
|
114
|
+
- `"moduleResolution": "Bundler"` with `"module": "ESNext"`
|
|
85
115
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
116
|
+
We recommend using Node16/Node16 unless absolutely necessary. That will produce transpiled JavaScript that is suitable
|
|
117
|
+
for use with modern versions of Node.js _and_ Bundlers.
|
|
118
|
+
[See the TypeScript documentation](https://www.typescriptlang.org/tsconfig#moduleResolution) for more information
|
|
119
|
+
regarding the module and moduleResolution options.
|
|
90
120
|
|
|
91
|
-
|
|
92
|
-
|
|
121
|
+
**Node10 moduleResolution is not supported; it does not support Fluid Framework's API structuring pattern that is used
|
|
122
|
+
to distinguish stable APIs from those that are in development.**
|
|
93
123
|
|
|
94
124
|
## 2.0.0-rc.2.0.0
|
|
95
125
|
|
|
@@ -123,9 +153,9 @@ Dependency updates only.
|
|
|
123
153
|
|
|
124
154
|
### Major Changes
|
|
125
155
|
|
|
126
|
-
-
|
|
156
|
+
- Minimum TypeScript version now 5.1.6 [871b3493dd](https://github.com/microsoft/FluidFramework/commits/871b3493dd0d7ea3a89be64998ceb6cb9021a04e)
|
|
127
157
|
|
|
128
|
-
|
|
158
|
+
The minimum supported TypeScript version for Fluid 2.0 clients is now 5.1.6.
|
|
129
159
|
|
|
130
160
|
## 2.0.0-internal.6.4.0
|
|
131
161
|
|
|
@@ -147,9 +177,9 @@ Dependency updates only.
|
|
|
147
177
|
|
|
148
178
|
### Major Changes
|
|
149
179
|
|
|
150
|
-
-
|
|
180
|
+
- Upgraded typescript transpilation target to ES2020 [8abce8cdb4](https://github.com/microsoft/FluidFramework/commits/8abce8cdb4e2832fb6405fb44e393bef03d5648a)
|
|
151
181
|
|
|
152
|
-
|
|
182
|
+
Upgraded typescript transpilation target to ES2020. This is done in order to decrease the bundle sizes of Fluid Framework packages. This has provided size improvements across the board for ex. Loader, Driver, Runtime etc. Reduced bundle sizes helps to load lesser code in apps and hence also helps to improve the perf.If any app wants to target any older versions of browsers with which this target version is not compatible, then they can use packages like babel to transpile to a older target.
|
|
153
183
|
|
|
154
184
|
## 2.0.0-internal.5.4.0
|
|
155
185
|
|
package/dist/assert.d.ts
CHANGED
|
@@ -45,6 +45,37 @@ export declare function assert(condition: boolean, message: string | number): as
|
|
|
45
45
|
* @internal
|
|
46
46
|
*/
|
|
47
47
|
export declare function fail(message: string | number): never;
|
|
48
|
+
/**
|
|
49
|
+
* Add a callback which can be used to report an assertion before it is thrown.
|
|
50
|
+
* @param handler - Called when an assertion occurs before the exception is thrown.
|
|
51
|
+
* @returns a function to remove the handler.
|
|
52
|
+
* @remarks
|
|
53
|
+
* The callback runs just before the exception is thrown, which makes it a better place to report telemetry for Fluid Framework bugs than a catch block or an event like `window.onerror`.
|
|
54
|
+
* Using this API to report telemetry is preferred over those approaches since it eliminates the risk of the exception being swallowed or obfuscated by an intermediate stack frame's catch block
|
|
55
|
+
* or missed due to not having the right catch block or event handler.
|
|
56
|
+
*
|
|
57
|
+
* This does not replace the need for error handling elsewhere since errors (even bugs in Fluid) can cause other kinds of exceptions which this cannot run the callback for.
|
|
58
|
+
* @example
|
|
59
|
+
* ```ts
|
|
60
|
+
* import { onAssertionFailure } from "fluid-framework/alpha";
|
|
61
|
+
*
|
|
62
|
+
* let firstAssertion: Error | undefined;
|
|
63
|
+
*
|
|
64
|
+
* onAssertionFailure((error: Error) => {
|
|
65
|
+
* const priorErrorNote =
|
|
66
|
+
* firstAssertion === undefined
|
|
67
|
+
* ? "Please report this bug."
|
|
68
|
+
* : `Might be caused due to prior error ${JSON.stringify(firstAssertion.message)} which should be investigated first.`;
|
|
69
|
+
* const message = `Encountered Bug in Fluid Framework: ${error.message}\n${priorErrorNote}\n${error.stack}`;
|
|
70
|
+
* console.error(message);
|
|
71
|
+
*
|
|
72
|
+
* debugger;
|
|
73
|
+
* firstAssertion ??= error;
|
|
74
|
+
* });
|
|
75
|
+
* ```
|
|
76
|
+
* @alpha
|
|
77
|
+
*/
|
|
78
|
+
export declare function onAssertionFailure(handler: (error: Error) => void): () => void;
|
|
48
79
|
/**
|
|
49
80
|
* Asserts that can be conditionally enabled in debug/development builds but will be optimized out of production builds.
|
|
50
81
|
*
|
package/dist/assert.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assert.d.ts","sourceRoot":"","sources":["../src/assert.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,MAAM,CAAC,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,SAAS,CAItF;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,KAAK,
|
|
1
|
+
{"version":3,"file":"assert.d.ts","sourceRoot":"","sources":["../src/assert.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,MAAM,CAAC,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,SAAS,CAItF;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,KAAK,CAMpD;AAUD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,GAAG,MAAM,IAAI,CAU9E;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,IAAI,GAAG;IAAE,QAAQ,IAAI,MAAM,CAAA;CAAE,GAAG,IAAI,CAehF;AAID;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAQ/D;AAED;;;;;;;GAOG;AACH,wBAAgB,iCAAiC,IAAI,OAAO,CAM3D"}
|
package/dist/assert.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Licensed under the MIT License.
|
|
5
5
|
*/
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.nonProductionConditionalsIncluded = exports.configureDebugAsserts = exports.debugAssert = exports.fail = exports.assert = void 0;
|
|
7
|
+
exports.nonProductionConditionalsIncluded = exports.configureDebugAsserts = exports.debugAssert = exports.onAssertionFailure = exports.fail = exports.assert = void 0;
|
|
8
8
|
/**
|
|
9
9
|
* Asserts the specified condition.
|
|
10
10
|
*
|
|
@@ -53,9 +53,59 @@ exports.assert = assert;
|
|
|
53
53
|
* @internal
|
|
54
54
|
*/
|
|
55
55
|
function fail(message) {
|
|
56
|
-
|
|
56
|
+
const error = new Error(typeof message === "number" ? `0x${message.toString(16).padStart(3, "0")}` : message);
|
|
57
|
+
onAssertionError(error);
|
|
58
|
+
throw error;
|
|
57
59
|
}
|
|
58
60
|
exports.fail = fail;
|
|
61
|
+
function onAssertionError(error) {
|
|
62
|
+
for (const handler of firstChanceAssertionHandler) {
|
|
63
|
+
handler(error);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
const firstChanceAssertionHandler = new Set();
|
|
67
|
+
/**
|
|
68
|
+
* Add a callback which can be used to report an assertion before it is thrown.
|
|
69
|
+
* @param handler - Called when an assertion occurs before the exception is thrown.
|
|
70
|
+
* @returns a function to remove the handler.
|
|
71
|
+
* @remarks
|
|
72
|
+
* The callback runs just before the exception is thrown, which makes it a better place to report telemetry for Fluid Framework bugs than a catch block or an event like `window.onerror`.
|
|
73
|
+
* Using this API to report telemetry is preferred over those approaches since it eliminates the risk of the exception being swallowed or obfuscated by an intermediate stack frame's catch block
|
|
74
|
+
* or missed due to not having the right catch block or event handler.
|
|
75
|
+
*
|
|
76
|
+
* This does not replace the need for error handling elsewhere since errors (even bugs in Fluid) can cause other kinds of exceptions which this cannot run the callback for.
|
|
77
|
+
* @example
|
|
78
|
+
* ```ts
|
|
79
|
+
* import { onAssertionFailure } from "fluid-framework/alpha";
|
|
80
|
+
*
|
|
81
|
+
* let firstAssertion: Error | undefined;
|
|
82
|
+
*
|
|
83
|
+
* onAssertionFailure((error: Error) => {
|
|
84
|
+
* const priorErrorNote =
|
|
85
|
+
* firstAssertion === undefined
|
|
86
|
+
* ? "Please report this bug."
|
|
87
|
+
* : `Might be caused due to prior error ${JSON.stringify(firstAssertion.message)} which should be investigated first.`;
|
|
88
|
+
* const message = `Encountered Bug in Fluid Framework: ${error.message}\n${priorErrorNote}\n${error.stack}`;
|
|
89
|
+
* console.error(message);
|
|
90
|
+
*
|
|
91
|
+
* debugger;
|
|
92
|
+
* firstAssertion ??= error;
|
|
93
|
+
* });
|
|
94
|
+
* ```
|
|
95
|
+
* @alpha
|
|
96
|
+
*/
|
|
97
|
+
function onAssertionFailure(handler) {
|
|
98
|
+
// To avoid issues if the same callback is registered twice (mainly it not triggering twice and the first unregister removing it),
|
|
99
|
+
// generate a wrapper around the handler.
|
|
100
|
+
const wrapper = (error) => {
|
|
101
|
+
handler(error);
|
|
102
|
+
};
|
|
103
|
+
firstChanceAssertionHandler.add(wrapper);
|
|
104
|
+
return () => {
|
|
105
|
+
firstChanceAssertionHandler.delete(wrapper);
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
exports.onAssertionFailure = onAssertionFailure;
|
|
59
109
|
/**
|
|
60
110
|
* Asserts that can be conditionally enabled in debug/development builds but will be optimized out of production builds.
|
|
61
111
|
*
|
|
@@ -97,7 +147,9 @@ function debugAssert(predicate) {
|
|
|
97
147
|
const result = predicate();
|
|
98
148
|
if (result !== true) {
|
|
99
149
|
debugger;
|
|
100
|
-
|
|
150
|
+
const error = new Error(`Debug assert failed: ${result.toString()}`);
|
|
151
|
+
onAssertionError(error);
|
|
152
|
+
throw error;
|
|
101
153
|
}
|
|
102
154
|
}
|
|
103
155
|
});
|
package/dist/assert.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assert.js","sourceRoot":"","sources":["../src/assert.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,SAAgB,MAAM,CAAC,SAAkB,EAAE,OAAwB;IAClE,IAAI,CAAC,SAAS,EAAE,CAAC;QAChB,IAAI,CAAC,OAAO,CAAC,CAAC;IACf,CAAC;AACF,CAAC;AAJD,wBAIC;AAED;;;;;;;;;;;;;;GAcG;AACH,SAAgB,IAAI,CAAC,OAAwB;IAC5C,MAAM,IAAI,KAAK,CACd,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CACpF,CAAC;AACH,CAAC;AAJD,oBAIC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,SAAgB,WAAW,CAAC,SAA8C;IACzE,uJAAuJ;IACvJ,yJAAyJ;IACzJ,yIAAyI;IACzI,gBAAgB,CAAC,GAAG,EAAE;QACrB,IAAI,mBAAmB,EAAE,CAAC;YACzB,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;YAC3B,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;gBACrB,QAAQ,CAAC;gBACT,MAAM,IAAI,KAAK,CAAC,wBAAwB,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;YAC9D,CAAC;QACF,CAAC;IACF,CAAC,CAAC,CAAC;AACJ,CAAC;AAbD,kCAaC;AAED,IAAI,mBAAmB,GAAG,KAAK,CAAC;AAEhC;;;;;;GAMG;AACH,SAAgB,qBAAqB,CAAC,OAAgB;IACrD,MAAM,CACL,iCAAiC,EAAE,EACnC,KAAK,CAAC,4EAA4E,CAClF,CAAC;IACF,MAAM,GAAG,GAAG,mBAAmB,CAAC;IAChC,mBAAmB,GAAG,OAAO,CAAC;IAC9B,OAAO,GAAG,CAAC;AACZ,CAAC;AARD,sDAQC;AAED;;;;;;;GAOG;AACH,SAAgB,iCAAiC;IAChD,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,gBAAgB,CAAC,GAAG,EAAE;QACrB,QAAQ,GAAG,IAAI,CAAC;IACjB,CAAC,CAAC,CAAC;IACH,OAAO,QAAQ,CAAC;AACjB,CAAC;AAND,8EAMC;AAED;;;;;;;;;;;;;;GAcG;AACH,iMAAiM;AACjM,0CAA0C;AAC1C,wBAAwB;AACxB,SAAS,gBAAgB,CAAC,WAAuB;IAChD,0FAA0F;IAC1F,6IAA6I;IAC7I,iIAAiI;IAEjI,sKAAsK;IACtK,0CAA0C;IAC1C,aAAa,CAAC,WAAW,EAAE,CAAC;AAC7B,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\n/**\n * Asserts the specified condition.\n *\n * @param condition - The condition that should be true, if the condition is false an error will be thrown.\n * Only use this API when `false` indicates a logic error in the problem and thus a bug that should be fixed.\n * @param message - The message to include in the error when the condition does not hold.\n * A number should not be specified manually: use a string.\n * Before a release, policy-check should be run, which will convert any asserts still using strings to\n * use numbered error codes instead.\n * @remarks\n * Use this instead of the node 'assert' package, which requires polyfills and has a big impact on bundle sizes.\n *\n * Assertions using this API will be included in all configurations: there is no option to disable or optimize them out.\n * Thus this API is suitable for detecting conditions that should terminate the application and produce a useful diagnostic message.\n * It can be used to ensure bad states are detected early and to avoid data corruption or harder to debug errors.\n *\n * In cases where the assert is very unlikely to have an impact on production code but is still useful as documentation and for debugging, consider using `debugAssert` instead\n * to optimize bundle size.\n *\n * This API is not intended for use outside of the Fluid Framework client codebase: it will most likely be made internal in the future.\n * @privateRemarks\n * This should be deprecated (as a non internal API) then moved to purely internal.\n * When done the `debugAssert` reference above should be turned into a link.\n * @legacy\n * @alpha\n */\nexport function assert(condition: boolean, message: string | number): asserts condition {\n\tif (!condition) {\n\t\tfail(message);\n\t}\n}\n\n/**\n * Throw an error with a constant message.\n * @remarks\n * Works like {@link assert}, but errors unconditionally instead of taking in a condition.\n *\n * Unlike `assert`, this `fail` is not \"tagged\" by the assert tagging too by default.\n * Use a `assertTagging.config.mjs` file to enable this and any other assert tagging customizations as needed.\n *\n * Returns `never` so it can be used inline as part of an expression, or as a return value.\n * @example\n * ```ts\n * const x: number = numbersMap.get(\"foo\") ?? fail(\"foo missing from map\");\n * ```\n * @internal\n */\nexport function fail(message: string | number): never {\n\tthrow new Error(\n\t\ttypeof message === \"number\" ? `0x${message.toString(16).padStart(3, \"0\")}` : message,\n\t);\n}\n\n/**\n * Asserts that can be conditionally enabled in debug/development builds but will be optimized out of production builds.\n *\n * Disabled by default.\n *\n * If the assert must be enforced/checked in production or enabled by default, use {@link assert} instead.\n *\n * @param predicate - A pure function that should return true if the condition holds, or a string or object describing the condition that failed.\n * This function will only be run in some configurations so it should be pure, and only used to detect bugs (when debugAssert are enabled), and must not be relied on to enforce the condition is true: for that use {@link assert}.\n * @remarks\n * Optimizing the asserts out of the bundle requires a bundler like webpack which leverages `__PURE__` annotations like https://webpack.js.org/guides/tree-shaking/#mark-a-function-call-as-side-effect-free.\n *\n * Exceptions thrown by this function must never be caught in production code, as that will result in different behavior when testing and when running optimized builds.\n * The `predicate` function must be pure (have no side-effects) to ensure that the behavior of code is the same regardless of if the asserts are disabled, enabled or optimized out.\n *\n * These asserts are disabled by default, even in debug builds to ensure that by default code will be tested as production runs, with them disabled.\n * Additionally, this ensures that apps that use a bundler which does not remove `__PURE__` will not incur the runtime cost of calling the predicate.\n * These asserts can be can be enabled by calling `configureDebugAsserts(true)`: see {@link configureDebugAsserts}.\n *\n * @privateRemarks\n * This design was chosen to accomplish two main goals:\n *\n * 1. Make it easy to compile debug asserts fully out of production builds.\n * For webpack this happens by default, avoiding the need for customers to do special configuration.\n * This is important for both performance and bundle size.\n *\n * 2. Make it easy to test (both manually and automated) with and without the predicates running.\n * This ensures it is possible to benefit from the asserts when enabled, but also test with them disabled to ensure this disablement doesn't cause bugs.\n *\n * The default behavior of having debugAsserts disabled helps ensure that tests which don't know about debug asserts will still run in a way that is most similar to production.\n * @internal\n */\nexport function debugAssert(predicate: () => true | { toString(): string }): void {\n\t// This is valid since the contract for this function is that \"predicate\" should be side effect free and never return non true in production scenarios:\n\t// it returning non-true indicates a bug is present, and that the validation it does to detect the bug is only desired in specific test/debug situations.\n\t// Production scenarios, where pure code is removed, should never hit a failing predicate, and thus this code should be side effect free.\n\tskipInProduction(() => {\n\t\tif (debugAssertsEnabled) {\n\t\t\tconst result = predicate();\n\t\t\tif (result !== true) {\n\t\t\t\tdebugger;\n\t\t\t\tthrow new Error(`Debug assert failed: ${result.toString()}`);\n\t\t\t}\n\t\t}\n\t});\n}\n\nlet debugAssertsEnabled = false;\n\n/**\n * Enables {@link debugAssert} validation.\n * @remarks\n * Throws if debugAsserts have been optimized out.\n * @returns The previous state of debugAsserts.\n * @internal\n */\nexport function configureDebugAsserts(enabled: boolean): boolean {\n\tassert(\n\t\tnonProductionConditionalsIncluded(),\n\t\t0xab1 /* Debug asserts cannot be configured since they have been optimized out. */,\n\t);\n\tconst old = debugAssertsEnabled;\n\tdebugAssertsEnabled = enabled;\n\treturn old;\n}\n\n/**\n * Checks if non-production conditional code like {@link debugAssert} is included in this build.\n * @remarks\n * Such code can be optimized out by bundlers: this checks if that has occurred.\n * @privateRemarks\n * See {@link skipInProduction}.\n * @internal\n */\nexport function nonProductionConditionalsIncluded(): boolean {\n\tlet included = false;\n\tskipInProduction(() => {\n\t\tincluded = true;\n\t});\n\treturn included;\n}\n\n/**\n * Run `conditional` only in debug/development (non optimized/minified) builds, but optimize it out of production builds.\n *\n * @param conditional - This function will only be run in some configurations so it should be pure (at least in production scenarios).\n * It can be used to interact with debug only functionality that is also removed in production builds, or to do validation/testing/debugging that can be assumed to be sideeffect free in production where it might be removed.\n * @remarks\n * Great care must be taken when using this to ensure that bugs are not introduced which only occur when `conditional` is not run.\n * One way to do this is to provide an alternative way to disable the effects of `conditional` in development builds so both configurations can be tested:\n * {@link debugAssert} uses this pattern.\n *\n * @privateRemarks\n * Since this function has no built in option for toggling it in development for testing, it is not exported and is only used as a building block for other testable options.\n * There are some additional details about syntax and bundler support in https://github.com/javascript-compiler-hints/compiler-notations-spec/tree/main .\n * This code uses both NO_SIDE_EFFECTS and PURE to maximize compatibility: for any bundler supporting both they are redundant.\n */\n// Using the exact syntax from https://github.com/javascript-compiler-hints/compiler-notations-spec/blob/main/no-side-effects-notation-spec.md to maximize compatibility with tree-shaking tools.\n// eslint-disable-next-line spaced-comment\n/*#__NO_SIDE_EFFECTS__*/\nfunction skipInProduction(conditional: () => void): void {\n\t// Here __PURE__ annotation is used to indicate that is is safe to optimize out this call.\n\t// This is valid since the contract for this function is that \"conditional\" should be side effect free if it were run in production scenarios\n\t// See https://webpack.js.org/guides/tree-shaking/#mark-a-function-call-as-side-effect-free for documentation on this annotation.\n\n\t// Using the exact syntax from https://webpack.js.org/guides/tree-shaking/#mark-a-function-call-as-side-effect-free to maximize compatibility with tree-shaking tools.\n\t// eslint-disable-next-line spaced-comment\n\t/*#__PURE__*/ conditional();\n}\n"]}
|
|
1
|
+
{"version":3,"file":"assert.js","sourceRoot":"","sources":["../src/assert.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,SAAgB,MAAM,CAAC,SAAkB,EAAE,OAAwB;IAClE,IAAI,CAAC,SAAS,EAAE,CAAC;QAChB,IAAI,CAAC,OAAO,CAAC,CAAC;IACf,CAAC;AACF,CAAC;AAJD,wBAIC;AAED;;;;;;;;;;;;;;GAcG;AACH,SAAgB,IAAI,CAAC,OAAwB;IAC5C,MAAM,KAAK,GAAG,IAAI,KAAK,CACtB,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CACpF,CAAC;IACF,gBAAgB,CAAC,KAAK,CAAC,CAAC;IACxB,MAAM,KAAK,CAAC;AACb,CAAC;AAND,oBAMC;AAED,SAAS,gBAAgB,CAAC,KAAY;IACrC,KAAK,MAAM,OAAO,IAAI,2BAA2B,EAAE,CAAC;QACnD,OAAO,CAAC,KAAK,CAAC,CAAC;IAChB,CAAC;AACF,CAAC;AAED,MAAM,2BAA2B,GAAG,IAAI,GAAG,EAA0B,CAAC;AAEtE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,SAAgB,kBAAkB,CAAC,OAA+B;IACjE,kIAAkI;IAClI,yCAAyC;IACzC,MAAM,OAAO,GAAG,CAAC,KAAY,EAAQ,EAAE;QACtC,OAAO,CAAC,KAAK,CAAC,CAAC;IAChB,CAAC,CAAC;IACF,2BAA2B,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IACzC,OAAO,GAAG,EAAE;QACX,2BAA2B,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAC7C,CAAC,CAAC;AACH,CAAC;AAVD,gDAUC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,SAAgB,WAAW,CAAC,SAA8C;IACzE,uJAAuJ;IACvJ,yJAAyJ;IACzJ,yIAAyI;IACzI,gBAAgB,CAAC,GAAG,EAAE;QACrB,IAAI,mBAAmB,EAAE,CAAC;YACzB,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;YAC3B,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;gBACrB,QAAQ,CAAC;gBACT,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,wBAAwB,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;gBACrE,gBAAgB,CAAC,KAAK,CAAC,CAAC;gBACxB,MAAM,KAAK,CAAC;YACb,CAAC;QACF,CAAC;IACF,CAAC,CAAC,CAAC;AACJ,CAAC;AAfD,kCAeC;AAED,IAAI,mBAAmB,GAAG,KAAK,CAAC;AAEhC;;;;;;GAMG;AACH,SAAgB,qBAAqB,CAAC,OAAgB;IACrD,MAAM,CACL,iCAAiC,EAAE,EACnC,KAAK,CAAC,4EAA4E,CAClF,CAAC;IACF,MAAM,GAAG,GAAG,mBAAmB,CAAC;IAChC,mBAAmB,GAAG,OAAO,CAAC;IAC9B,OAAO,GAAG,CAAC;AACZ,CAAC;AARD,sDAQC;AAED;;;;;;;GAOG;AACH,SAAgB,iCAAiC;IAChD,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,gBAAgB,CAAC,GAAG,EAAE;QACrB,QAAQ,GAAG,IAAI,CAAC;IACjB,CAAC,CAAC,CAAC;IACH,OAAO,QAAQ,CAAC;AACjB,CAAC;AAND,8EAMC;AAED;;;;;;;;;;;;;;GAcG;AACH,iMAAiM;AACjM,0CAA0C;AAC1C,wBAAwB;AACxB,SAAS,gBAAgB,CAAC,WAAuB;IAChD,0FAA0F;IAC1F,6IAA6I;IAC7I,iIAAiI;IAEjI,sKAAsK;IACtK,0CAA0C;IAC1C,aAAa,CAAC,WAAW,EAAE,CAAC;AAC7B,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\n/**\n * Asserts the specified condition.\n *\n * @param condition - The condition that should be true, if the condition is false an error will be thrown.\n * Only use this API when `false` indicates a logic error in the problem and thus a bug that should be fixed.\n * @param message - The message to include in the error when the condition does not hold.\n * A number should not be specified manually: use a string.\n * Before a release, policy-check should be run, which will convert any asserts still using strings to\n * use numbered error codes instead.\n * @remarks\n * Use this instead of the node 'assert' package, which requires polyfills and has a big impact on bundle sizes.\n *\n * Assertions using this API will be included in all configurations: there is no option to disable or optimize them out.\n * Thus this API is suitable for detecting conditions that should terminate the application and produce a useful diagnostic message.\n * It can be used to ensure bad states are detected early and to avoid data corruption or harder to debug errors.\n *\n * In cases where the assert is very unlikely to have an impact on production code but is still useful as documentation and for debugging, consider using `debugAssert` instead\n * to optimize bundle size.\n *\n * This API is not intended for use outside of the Fluid Framework client codebase: it will most likely be made internal in the future.\n * @privateRemarks\n * This should be deprecated (as a non internal API) then moved to purely internal.\n * When done the `debugAssert` reference above should be turned into a link.\n * @legacy\n * @alpha\n */\nexport function assert(condition: boolean, message: string | number): asserts condition {\n\tif (!condition) {\n\t\tfail(message);\n\t}\n}\n\n/**\n * Throw an error with a constant message.\n * @remarks\n * Works like {@link assert}, but errors unconditionally instead of taking in a condition.\n *\n * Unlike `assert`, this `fail` is not \"tagged\" by the assert tagging too by default.\n * Use a `assertTagging.config.mjs` file to enable this and any other assert tagging customizations as needed.\n *\n * Returns `never` so it can be used inline as part of an expression, or as a return value.\n * @example\n * ```ts\n * const x: number = numbersMap.get(\"foo\") ?? fail(\"foo missing from map\");\n * ```\n * @internal\n */\nexport function fail(message: string | number): never {\n\tconst error = new Error(\n\t\ttypeof message === \"number\" ? `0x${message.toString(16).padStart(3, \"0\")}` : message,\n\t);\n\tonAssertionError(error);\n\tthrow error;\n}\n\nfunction onAssertionError(error: Error): void {\n\tfor (const handler of firstChanceAssertionHandler) {\n\t\thandler(error);\n\t}\n}\n\nconst firstChanceAssertionHandler = new Set<(error: Error) => void>();\n\n/**\n * Add a callback which can be used to report an assertion before it is thrown.\n * @param handler - Called when an assertion occurs before the exception is thrown.\n * @returns a function to remove the handler.\n * @remarks\n * The callback runs just before the exception is thrown, which makes it a better place to report telemetry for Fluid Framework bugs than a catch block or an event like `window.onerror`.\n * Using this API to report telemetry is preferred over those approaches since it eliminates the risk of the exception being swallowed or obfuscated by an intermediate stack frame's catch block\n * or missed due to not having the right catch block or event handler.\n *\n * This does not replace the need for error handling elsewhere since errors (even bugs in Fluid) can cause other kinds of exceptions which this cannot run the callback for.\n * @example\n * ```ts\n * import { onAssertionFailure } from \"fluid-framework/alpha\";\n *\n * let firstAssertion: Error | undefined;\n *\n * onAssertionFailure((error: Error) => {\n * \tconst priorErrorNote =\n * \t\tfirstAssertion === undefined\n * \t\t\t? \"Please report this bug.\"\n * \t\t\t: `Might be caused due to prior error ${JSON.stringify(firstAssertion.message)} which should be investigated first.`;\n * \tconst message = `Encountered Bug in Fluid Framework: ${error.message}\\n${priorErrorNote}\\n${error.stack}`;\n * \tconsole.error(message);\n *\n * \tdebugger;\n * \tfirstAssertion ??= error;\n * });\n * ```\n * @alpha\n */\nexport function onAssertionFailure(handler: (error: Error) => void): () => void {\n\t// To avoid issues if the same callback is registered twice (mainly it not triggering twice and the first unregister removing it),\n\t// generate a wrapper around the handler.\n\tconst wrapper = (error: Error): void => {\n\t\thandler(error);\n\t};\n\tfirstChanceAssertionHandler.add(wrapper);\n\treturn () => {\n\t\tfirstChanceAssertionHandler.delete(wrapper);\n\t};\n}\n\n/**\n * Asserts that can be conditionally enabled in debug/development builds but will be optimized out of production builds.\n *\n * Disabled by default.\n *\n * If the assert must be enforced/checked in production or enabled by default, use {@link assert} instead.\n *\n * @param predicate - A pure function that should return true if the condition holds, or a string or object describing the condition that failed.\n * This function will only be run in some configurations so it should be pure, and only used to detect bugs (when debugAssert are enabled), and must not be relied on to enforce the condition is true: for that use {@link assert}.\n * @remarks\n * Optimizing the asserts out of the bundle requires a bundler like webpack which leverages `__PURE__` annotations like https://webpack.js.org/guides/tree-shaking/#mark-a-function-call-as-side-effect-free.\n *\n * Exceptions thrown by this function must never be caught in production code, as that will result in different behavior when testing and when running optimized builds.\n * The `predicate` function must be pure (have no side-effects) to ensure that the behavior of code is the same regardless of if the asserts are disabled, enabled or optimized out.\n *\n * These asserts are disabled by default, even in debug builds to ensure that by default code will be tested as production runs, with them disabled.\n * Additionally, this ensures that apps that use a bundler which does not remove `__PURE__` will not incur the runtime cost of calling the predicate.\n * These asserts can be can be enabled by calling `configureDebugAsserts(true)`: see {@link configureDebugAsserts}.\n *\n * @privateRemarks\n * This design was chosen to accomplish two main goals:\n *\n * 1. Make it easy to compile debug asserts fully out of production builds.\n * For webpack this happens by default, avoiding the need for customers to do special configuration.\n * This is important for both performance and bundle size.\n *\n * 2. Make it easy to test (both manually and automated) with and without the predicates running.\n * This ensures it is possible to benefit from the asserts when enabled, but also test with them disabled to ensure this disablement doesn't cause bugs.\n *\n * The default behavior of having debugAsserts disabled helps ensure that tests which don't know about debug asserts will still run in a way that is most similar to production.\n * @internal\n */\nexport function debugAssert(predicate: () => true | { toString(): string }): void {\n\t// This is valid since the contract for this function is that \"predicate\" should be side effect free and never return non true in production scenarios:\n\t// it returning non-true indicates a bug is present, and that the validation it does to detect the bug is only desired in specific test/debug situations.\n\t// Production scenarios, where pure code is removed, should never hit a failing predicate, and thus this code should be side effect free.\n\tskipInProduction(() => {\n\t\tif (debugAssertsEnabled) {\n\t\t\tconst result = predicate();\n\t\t\tif (result !== true) {\n\t\t\t\tdebugger;\n\t\t\t\tconst error = new Error(`Debug assert failed: ${result.toString()}`);\n\t\t\t\tonAssertionError(error);\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}\n\t});\n}\n\nlet debugAssertsEnabled = false;\n\n/**\n * Enables {@link debugAssert} validation.\n * @remarks\n * Throws if debugAsserts have been optimized out.\n * @returns The previous state of debugAsserts.\n * @internal\n */\nexport function configureDebugAsserts(enabled: boolean): boolean {\n\tassert(\n\t\tnonProductionConditionalsIncluded(),\n\t\t0xab1 /* Debug asserts cannot be configured since they have been optimized out. */,\n\t);\n\tconst old = debugAssertsEnabled;\n\tdebugAssertsEnabled = enabled;\n\treturn old;\n}\n\n/**\n * Checks if non-production conditional code like {@link debugAssert} is included in this build.\n * @remarks\n * Such code can be optimized out by bundlers: this checks if that has occurred.\n * @privateRemarks\n * See {@link skipInProduction}.\n * @internal\n */\nexport function nonProductionConditionalsIncluded(): boolean {\n\tlet included = false;\n\tskipInProduction(() => {\n\t\tincluded = true;\n\t});\n\treturn included;\n}\n\n/**\n * Run `conditional` only in debug/development (non optimized/minified) builds, but optimize it out of production builds.\n *\n * @param conditional - This function will only be run in some configurations so it should be pure (at least in production scenarios).\n * It can be used to interact with debug only functionality that is also removed in production builds, or to do validation/testing/debugging that can be assumed to be sideeffect free in production where it might be removed.\n * @remarks\n * Great care must be taken when using this to ensure that bugs are not introduced which only occur when `conditional` is not run.\n * One way to do this is to provide an alternative way to disable the effects of `conditional` in development builds so both configurations can be tested:\n * {@link debugAssert} uses this pattern.\n *\n * @privateRemarks\n * Since this function has no built in option for toggling it in development for testing, it is not exported and is only used as a building block for other testable options.\n * There are some additional details about syntax and bundler support in https://github.com/javascript-compiler-hints/compiler-notations-spec/tree/main .\n * This code uses both NO_SIDE_EFFECTS and PURE to maximize compatibility: for any bundler supporting both they are redundant.\n */\n// Using the exact syntax from https://github.com/javascript-compiler-hints/compiler-notations-spec/blob/main/no-side-effects-notation-spec.md to maximize compatibility with tree-shaking tools.\n// eslint-disable-next-line spaced-comment\n/*#__NO_SIDE_EFFECTS__*/\nfunction skipInProduction(conditional: () => void): void {\n\t// Here __PURE__ annotation is used to indicate that is is safe to optimize out this call.\n\t// This is valid since the contract for this function is that \"conditional\" should be side effect free if it were run in production scenarios\n\t// See https://webpack.js.org/guides/tree-shaking/#mark-a-function-call-as-side-effect-free for documentation on this annotation.\n\n\t// Using the exact syntax from https://webpack.js.org/guides/tree-shaking/#mark-a-function-call-as-side-effect-free to maximize compatibility with tree-shaking tools.\n\t// eslint-disable-next-line spaced-comment\n\t/*#__PURE__*/ conditional();\n}\n"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
|
-
export { assert, fail, debugAssert, configureDebugAsserts, nonProductionConditionalsIncluded, } from "./assert.js";
|
|
5
|
+
export { assert, fail, debugAssert, configureDebugAsserts, nonProductionConditionalsIncluded, onAssertionFailure, } from "./assert.js";
|
|
6
6
|
export { compareArrays } from "./compare.js";
|
|
7
7
|
export { delay } from "./delay.js";
|
|
8
8
|
export type { IComparer, IHeapNode } from "./heap.js";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACN,MAAM,EACN,IAAI,EACJ,WAAW,EACX,qBAAqB,EACrB,iCAAiC,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACN,MAAM,EACN,IAAI,EACJ,WAAW,EACX,qBAAqB,EACrB,iCAAiC,EACjC,kBAAkB,GAClB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,YAAY,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtD,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AACjD,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAC9C,YAAY,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACjF,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,YAAY,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAC7E,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACjE,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAC3D,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -4,13 +4,14 @@
|
|
|
4
4
|
* Licensed under the MIT License.
|
|
5
5
|
*/
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.oob = exports.isPromiseLike = exports.isObject = exports.unreachableCase = exports.Timer = exports.setLongTimeout = exports.PromiseTimer = exports.shallowCloneObject = exports.Deferred = exports.PromiseCache = exports.LazyPromise = exports.Lazy = exports.NumberComparer = exports.Heap = exports.delay = exports.compareArrays = exports.nonProductionConditionalsIncluded = exports.configureDebugAsserts = exports.debugAssert = exports.fail = exports.assert = void 0;
|
|
7
|
+
exports.oob = exports.isPromiseLike = exports.isObject = exports.unreachableCase = exports.Timer = exports.setLongTimeout = exports.PromiseTimer = exports.shallowCloneObject = exports.Deferred = exports.PromiseCache = exports.LazyPromise = exports.Lazy = exports.NumberComparer = exports.Heap = exports.delay = exports.compareArrays = exports.onAssertionFailure = exports.nonProductionConditionalsIncluded = exports.configureDebugAsserts = exports.debugAssert = exports.fail = exports.assert = void 0;
|
|
8
8
|
var assert_js_1 = require("./assert.js");
|
|
9
9
|
Object.defineProperty(exports, "assert", { enumerable: true, get: function () { return assert_js_1.assert; } });
|
|
10
10
|
Object.defineProperty(exports, "fail", { enumerable: true, get: function () { return assert_js_1.fail; } });
|
|
11
11
|
Object.defineProperty(exports, "debugAssert", { enumerable: true, get: function () { return assert_js_1.debugAssert; } });
|
|
12
12
|
Object.defineProperty(exports, "configureDebugAsserts", { enumerable: true, get: function () { return assert_js_1.configureDebugAsserts; } });
|
|
13
13
|
Object.defineProperty(exports, "nonProductionConditionalsIncluded", { enumerable: true, get: function () { return assert_js_1.nonProductionConditionalsIncluded; } });
|
|
14
|
+
Object.defineProperty(exports, "onAssertionFailure", { enumerable: true, get: function () { return assert_js_1.onAssertionFailure; } });
|
|
14
15
|
var compare_js_1 = require("./compare.js");
|
|
15
16
|
Object.defineProperty(exports, "compareArrays", { enumerable: true, get: function () { return compare_js_1.compareArrays; } });
|
|
16
17
|
var delay_js_1 = require("./delay.js");
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAEH,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAEH,yCAOqB;AANpB,mGAAA,MAAM,OAAA;AACN,iGAAA,IAAI,OAAA;AACJ,wGAAA,WAAW,OAAA;AACX,kHAAA,qBAAqB,OAAA;AACrB,8HAAA,iCAAiC,OAAA;AACjC,+GAAA,kBAAkB,OAAA;AAEnB,2CAA6C;AAApC,2GAAA,aAAa,OAAA;AACtB,uCAAmC;AAA1B,iGAAA,KAAK,OAAA;AAEd,qCAAiD;AAAxC,+FAAA,IAAI,OAAA;AAAE,yGAAA,cAAc,OAAA;AAC7B,qCAA8C;AAArC,+FAAA,IAAI,OAAA;AAAE,sGAAA,WAAW,OAAA;AAE1B,qDAAiD;AAAxC,+GAAA,YAAY,OAAA;AACrB,6CAAyC;AAAhC,uGAAA,QAAQ,OAAA;AACjB,qDAAuD;AAA9C,qHAAA,kBAAkB,OAAA;AAE3B,uCAAiE;AAAxD,wGAAA,YAAY,OAAA;AAAE,0GAAA,cAAc,OAAA;AAAE,iGAAA,KAAK,OAAA;AAC5C,mDAAmD;AAA1C,iHAAA,eAAe,OAAA;AACxB,mDAA2D;AAAlD,0GAAA,QAAQ,OAAA;AAAE,+GAAA,aAAa,OAAA;AAChC,mCAA+B;AAAtB,6FAAA,GAAG,OAAA","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nexport {\n\tassert,\n\tfail,\n\tdebugAssert,\n\tconfigureDebugAsserts,\n\tnonProductionConditionalsIncluded,\n\tonAssertionFailure,\n} from \"./assert.js\";\nexport { compareArrays } from \"./compare.js\";\nexport { delay } from \"./delay.js\";\nexport type { IComparer, IHeapNode } from \"./heap.js\";\nexport { Heap, NumberComparer } from \"./heap.js\";\nexport { Lazy, LazyPromise } from \"./lazy.js\";\nexport type { PromiseCacheExpiry, PromiseCacheOptions } from \"./promiseCache.js\";\nexport { PromiseCache } from \"./promiseCache.js\";\nexport { Deferred } from \"./promises.js\";\nexport { shallowCloneObject } from \"./shallowClone.js\";\nexport type { IPromiseTimer, IPromiseTimerResult, ITimer } from \"./timer.js\";\nexport { PromiseTimer, setLongTimeout, Timer } from \"./timer.js\";\nexport { unreachableCase } from \"./unreachable.js\";\nexport { isObject, isPromiseLike } from \"./typesGuards.js\";\nexport { oob } from \"./oob.js\";\n"]}
|
package/lib/assert.d.ts
CHANGED
|
@@ -45,6 +45,37 @@ export declare function assert(condition: boolean, message: string | number): as
|
|
|
45
45
|
* @internal
|
|
46
46
|
*/
|
|
47
47
|
export declare function fail(message: string | number): never;
|
|
48
|
+
/**
|
|
49
|
+
* Add a callback which can be used to report an assertion before it is thrown.
|
|
50
|
+
* @param handler - Called when an assertion occurs before the exception is thrown.
|
|
51
|
+
* @returns a function to remove the handler.
|
|
52
|
+
* @remarks
|
|
53
|
+
* The callback runs just before the exception is thrown, which makes it a better place to report telemetry for Fluid Framework bugs than a catch block or an event like `window.onerror`.
|
|
54
|
+
* Using this API to report telemetry is preferred over those approaches since it eliminates the risk of the exception being swallowed or obfuscated by an intermediate stack frame's catch block
|
|
55
|
+
* or missed due to not having the right catch block or event handler.
|
|
56
|
+
*
|
|
57
|
+
* This does not replace the need for error handling elsewhere since errors (even bugs in Fluid) can cause other kinds of exceptions which this cannot run the callback for.
|
|
58
|
+
* @example
|
|
59
|
+
* ```ts
|
|
60
|
+
* import { onAssertionFailure } from "fluid-framework/alpha";
|
|
61
|
+
*
|
|
62
|
+
* let firstAssertion: Error | undefined;
|
|
63
|
+
*
|
|
64
|
+
* onAssertionFailure((error: Error) => {
|
|
65
|
+
* const priorErrorNote =
|
|
66
|
+
* firstAssertion === undefined
|
|
67
|
+
* ? "Please report this bug."
|
|
68
|
+
* : `Might be caused due to prior error ${JSON.stringify(firstAssertion.message)} which should be investigated first.`;
|
|
69
|
+
* const message = `Encountered Bug in Fluid Framework: ${error.message}\n${priorErrorNote}\n${error.stack}`;
|
|
70
|
+
* console.error(message);
|
|
71
|
+
*
|
|
72
|
+
* debugger;
|
|
73
|
+
* firstAssertion ??= error;
|
|
74
|
+
* });
|
|
75
|
+
* ```
|
|
76
|
+
* @alpha
|
|
77
|
+
*/
|
|
78
|
+
export declare function onAssertionFailure(handler: (error: Error) => void): () => void;
|
|
48
79
|
/**
|
|
49
80
|
* Asserts that can be conditionally enabled in debug/development builds but will be optimized out of production builds.
|
|
50
81
|
*
|
package/lib/assert.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assert.d.ts","sourceRoot":"","sources":["../src/assert.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,MAAM,CAAC,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,SAAS,CAItF;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,KAAK,
|
|
1
|
+
{"version":3,"file":"assert.d.ts","sourceRoot":"","sources":["../src/assert.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,MAAM,CAAC,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,SAAS,CAItF;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,KAAK,CAMpD;AAUD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,GAAG,MAAM,IAAI,CAU9E;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,IAAI,GAAG;IAAE,QAAQ,IAAI,MAAM,CAAA;CAAE,GAAG,IAAI,CAehF;AAID;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAQ/D;AAED;;;;;;;GAOG;AACH,wBAAgB,iCAAiC,IAAI,OAAO,CAM3D"}
|
package/lib/assert.js
CHANGED
|
@@ -49,7 +49,56 @@ export function assert(condition, message) {
|
|
|
49
49
|
* @internal
|
|
50
50
|
*/
|
|
51
51
|
export function fail(message) {
|
|
52
|
-
|
|
52
|
+
const error = new Error(typeof message === "number" ? `0x${message.toString(16).padStart(3, "0")}` : message);
|
|
53
|
+
onAssertionError(error);
|
|
54
|
+
throw error;
|
|
55
|
+
}
|
|
56
|
+
function onAssertionError(error) {
|
|
57
|
+
for (const handler of firstChanceAssertionHandler) {
|
|
58
|
+
handler(error);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
const firstChanceAssertionHandler = new Set();
|
|
62
|
+
/**
|
|
63
|
+
* Add a callback which can be used to report an assertion before it is thrown.
|
|
64
|
+
* @param handler - Called when an assertion occurs before the exception is thrown.
|
|
65
|
+
* @returns a function to remove the handler.
|
|
66
|
+
* @remarks
|
|
67
|
+
* The callback runs just before the exception is thrown, which makes it a better place to report telemetry for Fluid Framework bugs than a catch block or an event like `window.onerror`.
|
|
68
|
+
* Using this API to report telemetry is preferred over those approaches since it eliminates the risk of the exception being swallowed or obfuscated by an intermediate stack frame's catch block
|
|
69
|
+
* or missed due to not having the right catch block or event handler.
|
|
70
|
+
*
|
|
71
|
+
* This does not replace the need for error handling elsewhere since errors (even bugs in Fluid) can cause other kinds of exceptions which this cannot run the callback for.
|
|
72
|
+
* @example
|
|
73
|
+
* ```ts
|
|
74
|
+
* import { onAssertionFailure } from "fluid-framework/alpha";
|
|
75
|
+
*
|
|
76
|
+
* let firstAssertion: Error | undefined;
|
|
77
|
+
*
|
|
78
|
+
* onAssertionFailure((error: Error) => {
|
|
79
|
+
* const priorErrorNote =
|
|
80
|
+
* firstAssertion === undefined
|
|
81
|
+
* ? "Please report this bug."
|
|
82
|
+
* : `Might be caused due to prior error ${JSON.stringify(firstAssertion.message)} which should be investigated first.`;
|
|
83
|
+
* const message = `Encountered Bug in Fluid Framework: ${error.message}\n${priorErrorNote}\n${error.stack}`;
|
|
84
|
+
* console.error(message);
|
|
85
|
+
*
|
|
86
|
+
* debugger;
|
|
87
|
+
* firstAssertion ??= error;
|
|
88
|
+
* });
|
|
89
|
+
* ```
|
|
90
|
+
* @alpha
|
|
91
|
+
*/
|
|
92
|
+
export function onAssertionFailure(handler) {
|
|
93
|
+
// To avoid issues if the same callback is registered twice (mainly it not triggering twice and the first unregister removing it),
|
|
94
|
+
// generate a wrapper around the handler.
|
|
95
|
+
const wrapper = (error) => {
|
|
96
|
+
handler(error);
|
|
97
|
+
};
|
|
98
|
+
firstChanceAssertionHandler.add(wrapper);
|
|
99
|
+
return () => {
|
|
100
|
+
firstChanceAssertionHandler.delete(wrapper);
|
|
101
|
+
};
|
|
53
102
|
}
|
|
54
103
|
/**
|
|
55
104
|
* Asserts that can be conditionally enabled in debug/development builds but will be optimized out of production builds.
|
|
@@ -92,7 +141,9 @@ export function debugAssert(predicate) {
|
|
|
92
141
|
const result = predicate();
|
|
93
142
|
if (result !== true) {
|
|
94
143
|
debugger;
|
|
95
|
-
|
|
144
|
+
const error = new Error(`Debug assert failed: ${result.toString()}`);
|
|
145
|
+
onAssertionError(error);
|
|
146
|
+
throw error;
|
|
96
147
|
}
|
|
97
148
|
}
|
|
98
149
|
});
|
package/lib/assert.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assert.js","sourceRoot":"","sources":["../src/assert.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,UAAU,MAAM,CAAC,SAAkB,EAAE,OAAwB;IAClE,IAAI,CAAC,SAAS,EAAE,CAAC;QAChB,IAAI,CAAC,OAAO,CAAC,CAAC;IACf,CAAC;AACF,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,IAAI,CAAC,OAAwB;IAC5C,MAAM,IAAI,KAAK,CACd,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CACpF,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,MAAM,UAAU,WAAW,CAAC,SAA8C;IACzE,uJAAuJ;IACvJ,yJAAyJ;IACzJ,yIAAyI;IACzI,gBAAgB,CAAC,GAAG,EAAE;QACrB,IAAI,mBAAmB,EAAE,CAAC;YACzB,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;YAC3B,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;gBACrB,QAAQ,CAAC;gBACT,MAAM,IAAI,KAAK,CAAC,wBAAwB,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;YAC9D,CAAC;QACF,CAAC;IACF,CAAC,CAAC,CAAC;AACJ,CAAC;AAED,IAAI,mBAAmB,GAAG,KAAK,CAAC;AAEhC;;;;;;GAMG;AACH,MAAM,UAAU,qBAAqB,CAAC,OAAgB;IACrD,MAAM,CACL,iCAAiC,EAAE,EACnC,KAAK,CAAC,4EAA4E,CAClF,CAAC;IACF,MAAM,GAAG,GAAG,mBAAmB,CAAC;IAChC,mBAAmB,GAAG,OAAO,CAAC;IAC9B,OAAO,GAAG,CAAC;AACZ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,iCAAiC;IAChD,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,gBAAgB,CAAC,GAAG,EAAE;QACrB,QAAQ,GAAG,IAAI,CAAC;IACjB,CAAC,CAAC,CAAC;IACH,OAAO,QAAQ,CAAC;AACjB,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,iMAAiM;AACjM,0CAA0C;AAC1C,wBAAwB;AACxB,SAAS,gBAAgB,CAAC,WAAuB;IAChD,0FAA0F;IAC1F,6IAA6I;IAC7I,iIAAiI;IAEjI,sKAAsK;IACtK,0CAA0C;IAC1C,aAAa,CAAC,WAAW,EAAE,CAAC;AAC7B,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\n/**\n * Asserts the specified condition.\n *\n * @param condition - The condition that should be true, if the condition is false an error will be thrown.\n * Only use this API when `false` indicates a logic error in the problem and thus a bug that should be fixed.\n * @param message - The message to include in the error when the condition does not hold.\n * A number should not be specified manually: use a string.\n * Before a release, policy-check should be run, which will convert any asserts still using strings to\n * use numbered error codes instead.\n * @remarks\n * Use this instead of the node 'assert' package, which requires polyfills and has a big impact on bundle sizes.\n *\n * Assertions using this API will be included in all configurations: there is no option to disable or optimize them out.\n * Thus this API is suitable for detecting conditions that should terminate the application and produce a useful diagnostic message.\n * It can be used to ensure bad states are detected early and to avoid data corruption or harder to debug errors.\n *\n * In cases where the assert is very unlikely to have an impact on production code but is still useful as documentation and for debugging, consider using `debugAssert` instead\n * to optimize bundle size.\n *\n * This API is not intended for use outside of the Fluid Framework client codebase: it will most likely be made internal in the future.\n * @privateRemarks\n * This should be deprecated (as a non internal API) then moved to purely internal.\n * When done the `debugAssert` reference above should be turned into a link.\n * @legacy\n * @alpha\n */\nexport function assert(condition: boolean, message: string | number): asserts condition {\n\tif (!condition) {\n\t\tfail(message);\n\t}\n}\n\n/**\n * Throw an error with a constant message.\n * @remarks\n * Works like {@link assert}, but errors unconditionally instead of taking in a condition.\n *\n * Unlike `assert`, this `fail` is not \"tagged\" by the assert tagging too by default.\n * Use a `assertTagging.config.mjs` file to enable this and any other assert tagging customizations as needed.\n *\n * Returns `never` so it can be used inline as part of an expression, or as a return value.\n * @example\n * ```ts\n * const x: number = numbersMap.get(\"foo\") ?? fail(\"foo missing from map\");\n * ```\n * @internal\n */\nexport function fail(message: string | number): never {\n\tthrow new Error(\n\t\ttypeof message === \"number\" ? `0x${message.toString(16).padStart(3, \"0\")}` : message,\n\t);\n}\n\n/**\n * Asserts that can be conditionally enabled in debug/development builds but will be optimized out of production builds.\n *\n * Disabled by default.\n *\n * If the assert must be enforced/checked in production or enabled by default, use {@link assert} instead.\n *\n * @param predicate - A pure function that should return true if the condition holds, or a string or object describing the condition that failed.\n * This function will only be run in some configurations so it should be pure, and only used to detect bugs (when debugAssert are enabled), and must not be relied on to enforce the condition is true: for that use {@link assert}.\n * @remarks\n * Optimizing the asserts out of the bundle requires a bundler like webpack which leverages `__PURE__` annotations like https://webpack.js.org/guides/tree-shaking/#mark-a-function-call-as-side-effect-free.\n *\n * Exceptions thrown by this function must never be caught in production code, as that will result in different behavior when testing and when running optimized builds.\n * The `predicate` function must be pure (have no side-effects) to ensure that the behavior of code is the same regardless of if the asserts are disabled, enabled or optimized out.\n *\n * These asserts are disabled by default, even in debug builds to ensure that by default code will be tested as production runs, with them disabled.\n * Additionally, this ensures that apps that use a bundler which does not remove `__PURE__` will not incur the runtime cost of calling the predicate.\n * These asserts can be can be enabled by calling `configureDebugAsserts(true)`: see {@link configureDebugAsserts}.\n *\n * @privateRemarks\n * This design was chosen to accomplish two main goals:\n *\n * 1. Make it easy to compile debug asserts fully out of production builds.\n * For webpack this happens by default, avoiding the need for customers to do special configuration.\n * This is important for both performance and bundle size.\n *\n * 2. Make it easy to test (both manually and automated) with and without the predicates running.\n * This ensures it is possible to benefit from the asserts when enabled, but also test with them disabled to ensure this disablement doesn't cause bugs.\n *\n * The default behavior of having debugAsserts disabled helps ensure that tests which don't know about debug asserts will still run in a way that is most similar to production.\n * @internal\n */\nexport function debugAssert(predicate: () => true | { toString(): string }): void {\n\t// This is valid since the contract for this function is that \"predicate\" should be side effect free and never return non true in production scenarios:\n\t// it returning non-true indicates a bug is present, and that the validation it does to detect the bug is only desired in specific test/debug situations.\n\t// Production scenarios, where pure code is removed, should never hit a failing predicate, and thus this code should be side effect free.\n\tskipInProduction(() => {\n\t\tif (debugAssertsEnabled) {\n\t\t\tconst result = predicate();\n\t\t\tif (result !== true) {\n\t\t\t\tdebugger;\n\t\t\t\tthrow new Error(`Debug assert failed: ${result.toString()}`);\n\t\t\t}\n\t\t}\n\t});\n}\n\nlet debugAssertsEnabled = false;\n\n/**\n * Enables {@link debugAssert} validation.\n * @remarks\n * Throws if debugAsserts have been optimized out.\n * @returns The previous state of debugAsserts.\n * @internal\n */\nexport function configureDebugAsserts(enabled: boolean): boolean {\n\tassert(\n\t\tnonProductionConditionalsIncluded(),\n\t\t0xab1 /* Debug asserts cannot be configured since they have been optimized out. */,\n\t);\n\tconst old = debugAssertsEnabled;\n\tdebugAssertsEnabled = enabled;\n\treturn old;\n}\n\n/**\n * Checks if non-production conditional code like {@link debugAssert} is included in this build.\n * @remarks\n * Such code can be optimized out by bundlers: this checks if that has occurred.\n * @privateRemarks\n * See {@link skipInProduction}.\n * @internal\n */\nexport function nonProductionConditionalsIncluded(): boolean {\n\tlet included = false;\n\tskipInProduction(() => {\n\t\tincluded = true;\n\t});\n\treturn included;\n}\n\n/**\n * Run `conditional` only in debug/development (non optimized/minified) builds, but optimize it out of production builds.\n *\n * @param conditional - This function will only be run in some configurations so it should be pure (at least in production scenarios).\n * It can be used to interact with debug only functionality that is also removed in production builds, or to do validation/testing/debugging that can be assumed to be sideeffect free in production where it might be removed.\n * @remarks\n * Great care must be taken when using this to ensure that bugs are not introduced which only occur when `conditional` is not run.\n * One way to do this is to provide an alternative way to disable the effects of `conditional` in development builds so both configurations can be tested:\n * {@link debugAssert} uses this pattern.\n *\n * @privateRemarks\n * Since this function has no built in option for toggling it in development for testing, it is not exported and is only used as a building block for other testable options.\n * There are some additional details about syntax and bundler support in https://github.com/javascript-compiler-hints/compiler-notations-spec/tree/main .\n * This code uses both NO_SIDE_EFFECTS and PURE to maximize compatibility: for any bundler supporting both they are redundant.\n */\n// Using the exact syntax from https://github.com/javascript-compiler-hints/compiler-notations-spec/blob/main/no-side-effects-notation-spec.md to maximize compatibility with tree-shaking tools.\n// eslint-disable-next-line spaced-comment\n/*#__NO_SIDE_EFFECTS__*/\nfunction skipInProduction(conditional: () => void): void {\n\t// Here __PURE__ annotation is used to indicate that is is safe to optimize out this call.\n\t// This is valid since the contract for this function is that \"conditional\" should be side effect free if it were run in production scenarios\n\t// See https://webpack.js.org/guides/tree-shaking/#mark-a-function-call-as-side-effect-free for documentation on this annotation.\n\n\t// Using the exact syntax from https://webpack.js.org/guides/tree-shaking/#mark-a-function-call-as-side-effect-free to maximize compatibility with tree-shaking tools.\n\t// eslint-disable-next-line spaced-comment\n\t/*#__PURE__*/ conditional();\n}\n"]}
|
|
1
|
+
{"version":3,"file":"assert.js","sourceRoot":"","sources":["../src/assert.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,UAAU,MAAM,CAAC,SAAkB,EAAE,OAAwB;IAClE,IAAI,CAAC,SAAS,EAAE,CAAC;QAChB,IAAI,CAAC,OAAO,CAAC,CAAC;IACf,CAAC;AACF,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,IAAI,CAAC,OAAwB;IAC5C,MAAM,KAAK,GAAG,IAAI,KAAK,CACtB,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CACpF,CAAC;IACF,gBAAgB,CAAC,KAAK,CAAC,CAAC;IACxB,MAAM,KAAK,CAAC;AACb,CAAC;AAED,SAAS,gBAAgB,CAAC,KAAY;IACrC,KAAK,MAAM,OAAO,IAAI,2BAA2B,EAAE,CAAC;QACnD,OAAO,CAAC,KAAK,CAAC,CAAC;IAChB,CAAC;AACF,CAAC;AAED,MAAM,2BAA2B,GAAG,IAAI,GAAG,EAA0B,CAAC;AAEtE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,MAAM,UAAU,kBAAkB,CAAC,OAA+B;IACjE,kIAAkI;IAClI,yCAAyC;IACzC,MAAM,OAAO,GAAG,CAAC,KAAY,EAAQ,EAAE;QACtC,OAAO,CAAC,KAAK,CAAC,CAAC;IAChB,CAAC,CAAC;IACF,2BAA2B,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IACzC,OAAO,GAAG,EAAE;QACX,2BAA2B,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAC7C,CAAC,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,MAAM,UAAU,WAAW,CAAC,SAA8C;IACzE,uJAAuJ;IACvJ,yJAAyJ;IACzJ,yIAAyI;IACzI,gBAAgB,CAAC,GAAG,EAAE;QACrB,IAAI,mBAAmB,EAAE,CAAC;YACzB,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;YAC3B,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;gBACrB,QAAQ,CAAC;gBACT,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,wBAAwB,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;gBACrE,gBAAgB,CAAC,KAAK,CAAC,CAAC;gBACxB,MAAM,KAAK,CAAC;YACb,CAAC;QACF,CAAC;IACF,CAAC,CAAC,CAAC;AACJ,CAAC;AAED,IAAI,mBAAmB,GAAG,KAAK,CAAC;AAEhC;;;;;;GAMG;AACH,MAAM,UAAU,qBAAqB,CAAC,OAAgB;IACrD,MAAM,CACL,iCAAiC,EAAE,EACnC,KAAK,CAAC,4EAA4E,CAClF,CAAC;IACF,MAAM,GAAG,GAAG,mBAAmB,CAAC;IAChC,mBAAmB,GAAG,OAAO,CAAC;IAC9B,OAAO,GAAG,CAAC;AACZ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,iCAAiC;IAChD,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,gBAAgB,CAAC,GAAG,EAAE;QACrB,QAAQ,GAAG,IAAI,CAAC;IACjB,CAAC,CAAC,CAAC;IACH,OAAO,QAAQ,CAAC;AACjB,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,iMAAiM;AACjM,0CAA0C;AAC1C,wBAAwB;AACxB,SAAS,gBAAgB,CAAC,WAAuB;IAChD,0FAA0F;IAC1F,6IAA6I;IAC7I,iIAAiI;IAEjI,sKAAsK;IACtK,0CAA0C;IAC1C,aAAa,CAAC,WAAW,EAAE,CAAC;AAC7B,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\n/**\n * Asserts the specified condition.\n *\n * @param condition - The condition that should be true, if the condition is false an error will be thrown.\n * Only use this API when `false` indicates a logic error in the problem and thus a bug that should be fixed.\n * @param message - The message to include in the error when the condition does not hold.\n * A number should not be specified manually: use a string.\n * Before a release, policy-check should be run, which will convert any asserts still using strings to\n * use numbered error codes instead.\n * @remarks\n * Use this instead of the node 'assert' package, which requires polyfills and has a big impact on bundle sizes.\n *\n * Assertions using this API will be included in all configurations: there is no option to disable or optimize them out.\n * Thus this API is suitable for detecting conditions that should terminate the application and produce a useful diagnostic message.\n * It can be used to ensure bad states are detected early and to avoid data corruption or harder to debug errors.\n *\n * In cases where the assert is very unlikely to have an impact on production code but is still useful as documentation and for debugging, consider using `debugAssert` instead\n * to optimize bundle size.\n *\n * This API is not intended for use outside of the Fluid Framework client codebase: it will most likely be made internal in the future.\n * @privateRemarks\n * This should be deprecated (as a non internal API) then moved to purely internal.\n * When done the `debugAssert` reference above should be turned into a link.\n * @legacy\n * @alpha\n */\nexport function assert(condition: boolean, message: string | number): asserts condition {\n\tif (!condition) {\n\t\tfail(message);\n\t}\n}\n\n/**\n * Throw an error with a constant message.\n * @remarks\n * Works like {@link assert}, but errors unconditionally instead of taking in a condition.\n *\n * Unlike `assert`, this `fail` is not \"tagged\" by the assert tagging too by default.\n * Use a `assertTagging.config.mjs` file to enable this and any other assert tagging customizations as needed.\n *\n * Returns `never` so it can be used inline as part of an expression, or as a return value.\n * @example\n * ```ts\n * const x: number = numbersMap.get(\"foo\") ?? fail(\"foo missing from map\");\n * ```\n * @internal\n */\nexport function fail(message: string | number): never {\n\tconst error = new Error(\n\t\ttypeof message === \"number\" ? `0x${message.toString(16).padStart(3, \"0\")}` : message,\n\t);\n\tonAssertionError(error);\n\tthrow error;\n}\n\nfunction onAssertionError(error: Error): void {\n\tfor (const handler of firstChanceAssertionHandler) {\n\t\thandler(error);\n\t}\n}\n\nconst firstChanceAssertionHandler = new Set<(error: Error) => void>();\n\n/**\n * Add a callback which can be used to report an assertion before it is thrown.\n * @param handler - Called when an assertion occurs before the exception is thrown.\n * @returns a function to remove the handler.\n * @remarks\n * The callback runs just before the exception is thrown, which makes it a better place to report telemetry for Fluid Framework bugs than a catch block or an event like `window.onerror`.\n * Using this API to report telemetry is preferred over those approaches since it eliminates the risk of the exception being swallowed or obfuscated by an intermediate stack frame's catch block\n * or missed due to not having the right catch block or event handler.\n *\n * This does not replace the need for error handling elsewhere since errors (even bugs in Fluid) can cause other kinds of exceptions which this cannot run the callback for.\n * @example\n * ```ts\n * import { onAssertionFailure } from \"fluid-framework/alpha\";\n *\n * let firstAssertion: Error | undefined;\n *\n * onAssertionFailure((error: Error) => {\n * \tconst priorErrorNote =\n * \t\tfirstAssertion === undefined\n * \t\t\t? \"Please report this bug.\"\n * \t\t\t: `Might be caused due to prior error ${JSON.stringify(firstAssertion.message)} which should be investigated first.`;\n * \tconst message = `Encountered Bug in Fluid Framework: ${error.message}\\n${priorErrorNote}\\n${error.stack}`;\n * \tconsole.error(message);\n *\n * \tdebugger;\n * \tfirstAssertion ??= error;\n * });\n * ```\n * @alpha\n */\nexport function onAssertionFailure(handler: (error: Error) => void): () => void {\n\t// To avoid issues if the same callback is registered twice (mainly it not triggering twice and the first unregister removing it),\n\t// generate a wrapper around the handler.\n\tconst wrapper = (error: Error): void => {\n\t\thandler(error);\n\t};\n\tfirstChanceAssertionHandler.add(wrapper);\n\treturn () => {\n\t\tfirstChanceAssertionHandler.delete(wrapper);\n\t};\n}\n\n/**\n * Asserts that can be conditionally enabled in debug/development builds but will be optimized out of production builds.\n *\n * Disabled by default.\n *\n * If the assert must be enforced/checked in production or enabled by default, use {@link assert} instead.\n *\n * @param predicate - A pure function that should return true if the condition holds, or a string or object describing the condition that failed.\n * This function will only be run in some configurations so it should be pure, and only used to detect bugs (when debugAssert are enabled), and must not be relied on to enforce the condition is true: for that use {@link assert}.\n * @remarks\n * Optimizing the asserts out of the bundle requires a bundler like webpack which leverages `__PURE__` annotations like https://webpack.js.org/guides/tree-shaking/#mark-a-function-call-as-side-effect-free.\n *\n * Exceptions thrown by this function must never be caught in production code, as that will result in different behavior when testing and when running optimized builds.\n * The `predicate` function must be pure (have no side-effects) to ensure that the behavior of code is the same regardless of if the asserts are disabled, enabled or optimized out.\n *\n * These asserts are disabled by default, even in debug builds to ensure that by default code will be tested as production runs, with them disabled.\n * Additionally, this ensures that apps that use a bundler which does not remove `__PURE__` will not incur the runtime cost of calling the predicate.\n * These asserts can be can be enabled by calling `configureDebugAsserts(true)`: see {@link configureDebugAsserts}.\n *\n * @privateRemarks\n * This design was chosen to accomplish two main goals:\n *\n * 1. Make it easy to compile debug asserts fully out of production builds.\n * For webpack this happens by default, avoiding the need for customers to do special configuration.\n * This is important for both performance and bundle size.\n *\n * 2. Make it easy to test (both manually and automated) with and without the predicates running.\n * This ensures it is possible to benefit from the asserts when enabled, but also test with them disabled to ensure this disablement doesn't cause bugs.\n *\n * The default behavior of having debugAsserts disabled helps ensure that tests which don't know about debug asserts will still run in a way that is most similar to production.\n * @internal\n */\nexport function debugAssert(predicate: () => true | { toString(): string }): void {\n\t// This is valid since the contract for this function is that \"predicate\" should be side effect free and never return non true in production scenarios:\n\t// it returning non-true indicates a bug is present, and that the validation it does to detect the bug is only desired in specific test/debug situations.\n\t// Production scenarios, where pure code is removed, should never hit a failing predicate, and thus this code should be side effect free.\n\tskipInProduction(() => {\n\t\tif (debugAssertsEnabled) {\n\t\t\tconst result = predicate();\n\t\t\tif (result !== true) {\n\t\t\t\tdebugger;\n\t\t\t\tconst error = new Error(`Debug assert failed: ${result.toString()}`);\n\t\t\t\tonAssertionError(error);\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}\n\t});\n}\n\nlet debugAssertsEnabled = false;\n\n/**\n * Enables {@link debugAssert} validation.\n * @remarks\n * Throws if debugAsserts have been optimized out.\n * @returns The previous state of debugAsserts.\n * @internal\n */\nexport function configureDebugAsserts(enabled: boolean): boolean {\n\tassert(\n\t\tnonProductionConditionalsIncluded(),\n\t\t0xab1 /* Debug asserts cannot be configured since they have been optimized out. */,\n\t);\n\tconst old = debugAssertsEnabled;\n\tdebugAssertsEnabled = enabled;\n\treturn old;\n}\n\n/**\n * Checks if non-production conditional code like {@link debugAssert} is included in this build.\n * @remarks\n * Such code can be optimized out by bundlers: this checks if that has occurred.\n * @privateRemarks\n * See {@link skipInProduction}.\n * @internal\n */\nexport function nonProductionConditionalsIncluded(): boolean {\n\tlet included = false;\n\tskipInProduction(() => {\n\t\tincluded = true;\n\t});\n\treturn included;\n}\n\n/**\n * Run `conditional` only in debug/development (non optimized/minified) builds, but optimize it out of production builds.\n *\n * @param conditional - This function will only be run in some configurations so it should be pure (at least in production scenarios).\n * It can be used to interact with debug only functionality that is also removed in production builds, or to do validation/testing/debugging that can be assumed to be sideeffect free in production where it might be removed.\n * @remarks\n * Great care must be taken when using this to ensure that bugs are not introduced which only occur when `conditional` is not run.\n * One way to do this is to provide an alternative way to disable the effects of `conditional` in development builds so both configurations can be tested:\n * {@link debugAssert} uses this pattern.\n *\n * @privateRemarks\n * Since this function has no built in option for toggling it in development for testing, it is not exported and is only used as a building block for other testable options.\n * There are some additional details about syntax and bundler support in https://github.com/javascript-compiler-hints/compiler-notations-spec/tree/main .\n * This code uses both NO_SIDE_EFFECTS and PURE to maximize compatibility: for any bundler supporting both they are redundant.\n */\n// Using the exact syntax from https://github.com/javascript-compiler-hints/compiler-notations-spec/blob/main/no-side-effects-notation-spec.md to maximize compatibility with tree-shaking tools.\n// eslint-disable-next-line spaced-comment\n/*#__NO_SIDE_EFFECTS__*/\nfunction skipInProduction(conditional: () => void): void {\n\t// Here __PURE__ annotation is used to indicate that is is safe to optimize out this call.\n\t// This is valid since the contract for this function is that \"conditional\" should be side effect free if it were run in production scenarios\n\t// See https://webpack.js.org/guides/tree-shaking/#mark-a-function-call-as-side-effect-free for documentation on this annotation.\n\n\t// Using the exact syntax from https://webpack.js.org/guides/tree-shaking/#mark-a-function-call-as-side-effect-free to maximize compatibility with tree-shaking tools.\n\t// eslint-disable-next-line spaced-comment\n\t/*#__PURE__*/ conditional();\n}\n"]}
|
package/lib/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
|
-
export { assert, fail, debugAssert, configureDebugAsserts, nonProductionConditionalsIncluded, } from "./assert.js";
|
|
5
|
+
export { assert, fail, debugAssert, configureDebugAsserts, nonProductionConditionalsIncluded, onAssertionFailure, } from "./assert.js";
|
|
6
6
|
export { compareArrays } from "./compare.js";
|
|
7
7
|
export { delay } from "./delay.js";
|
|
8
8
|
export type { IComparer, IHeapNode } from "./heap.js";
|
package/lib/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACN,MAAM,EACN,IAAI,EACJ,WAAW,EACX,qBAAqB,EACrB,iCAAiC,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACN,MAAM,EACN,IAAI,EACJ,WAAW,EACX,qBAAqB,EACrB,iCAAiC,EACjC,kBAAkB,GAClB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,YAAY,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtD,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AACjD,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAC9C,YAAY,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACjF,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,YAAY,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAC7E,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACjE,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAC3D,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC"}
|
package/lib/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
|
-
export { assert, fail, debugAssert, configureDebugAsserts, nonProductionConditionalsIncluded, } from "./assert.js";
|
|
5
|
+
export { assert, fail, debugAssert, configureDebugAsserts, nonProductionConditionalsIncluded, onAssertionFailure, } from "./assert.js";
|
|
6
6
|
export { compareArrays } from "./compare.js";
|
|
7
7
|
export { delay } from "./delay.js";
|
|
8
8
|
export { Heap, NumberComparer } from "./heap.js";
|
package/lib/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACN,MAAM,EACN,IAAI,EACJ,WAAW,EACX,qBAAqB,EACrB,iCAAiC,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACN,MAAM,EACN,IAAI,EACJ,WAAW,EACX,qBAAqB,EACrB,iCAAiC,EACjC,kBAAkB,GAClB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAEnC,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AACjD,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAE9C,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAEvD,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACjE,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAC3D,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nexport {\n\tassert,\n\tfail,\n\tdebugAssert,\n\tconfigureDebugAsserts,\n\tnonProductionConditionalsIncluded,\n\tonAssertionFailure,\n} from \"./assert.js\";\nexport { compareArrays } from \"./compare.js\";\nexport { delay } from \"./delay.js\";\nexport type { IComparer, IHeapNode } from \"./heap.js\";\nexport { Heap, NumberComparer } from \"./heap.js\";\nexport { Lazy, LazyPromise } from \"./lazy.js\";\nexport type { PromiseCacheExpiry, PromiseCacheOptions } from \"./promiseCache.js\";\nexport { PromiseCache } from \"./promiseCache.js\";\nexport { Deferred } from \"./promises.js\";\nexport { shallowCloneObject } from \"./shallowClone.js\";\nexport type { IPromiseTimer, IPromiseTimerResult, ITimer } from \"./timer.js\";\nexport { PromiseTimer, setLongTimeout, Timer } from \"./timer.js\";\nexport { unreachableCase } from \"./unreachable.js\";\nexport { isObject, isPromiseLike } from \"./typesGuards.js\";\nexport { oob } from \"./oob.js\";\n"]}
|
package/lib/tsdoc-metadata.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fluidframework/core-utils",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.31.1",
|
|
4
4
|
"description": "Not intended for use outside the Fluid client repo.",
|
|
5
5
|
"homepage": "https://fluidframework.com",
|
|
6
6
|
"repository": {
|
|
@@ -69,14 +69,14 @@
|
|
|
69
69
|
"devDependencies": {
|
|
70
70
|
"@arethetypeswrong/cli": "^0.17.1",
|
|
71
71
|
"@biomejs/biome": "~1.9.3",
|
|
72
|
-
"@fluid-internal/mocha-test-setup": "~2.
|
|
72
|
+
"@fluid-internal/mocha-test-setup": "~2.31.1",
|
|
73
73
|
"@fluid-tools/benchmark": "^0.50.0",
|
|
74
74
|
"@fluid-tools/build-cli": "^0.54.0",
|
|
75
75
|
"@fluidframework/build-common": "^2.0.3",
|
|
76
76
|
"@fluidframework/build-tools": "^0.54.0",
|
|
77
|
-
"@fluidframework/core-utils-previous": "npm:@fluidframework/core-utils@2.
|
|
77
|
+
"@fluidframework/core-utils-previous": "npm:@fluidframework/core-utils@2.31.0",
|
|
78
78
|
"@fluidframework/eslint-config-fluid": "^5.7.3",
|
|
79
|
-
"@microsoft/api-extractor": "7.
|
|
79
|
+
"@microsoft/api-extractor": "7.50.1",
|
|
80
80
|
"@types/mocha": "^10.0.10",
|
|
81
81
|
"@types/node": "^18.19.0",
|
|
82
82
|
"@types/sinon": "^17.0.3",
|
|
@@ -89,7 +89,6 @@
|
|
|
89
89
|
"mocha": "^10.8.2",
|
|
90
90
|
"mocha-multi-reporters": "^1.5.1",
|
|
91
91
|
"moment": "^2.21.0",
|
|
92
|
-
"prettier": "~3.0.3",
|
|
93
92
|
"rimraf": "^4.4.0",
|
|
94
93
|
"sinon": "^18.0.1",
|
|
95
94
|
"typescript": "~5.4.5"
|
|
@@ -124,7 +123,6 @@
|
|
|
124
123
|
"check:exports:esm:legacy": "api-extractor run --config api-extractor/api-extractor-lint-legacy.esm.json",
|
|
125
124
|
"check:exports:esm:public": "api-extractor run --config api-extractor/api-extractor-lint-public.esm.json",
|
|
126
125
|
"check:format": "npm run check:biome",
|
|
127
|
-
"check:prettier": "prettier --check . --cache --ignore-path ../../../.prettierignore",
|
|
128
126
|
"ci:build:api-reports": "concurrently \"npm:ci:build:api-reports:*\"",
|
|
129
127
|
"ci:build:api-reports:current": "api-extractor run --config api-extractor/api-extractor.current.json",
|
|
130
128
|
"ci:build:api-reports:legacy": "api-extractor run --config api-extractor/api-extractor.legacy.json",
|
|
@@ -134,7 +132,6 @@
|
|
|
134
132
|
"eslint:fix": "eslint --format stylish src --fix --fix-type problem,suggestion,layout",
|
|
135
133
|
"format": "npm run format:biome",
|
|
136
134
|
"format:biome": "biome check . --write",
|
|
137
|
-
"format:prettier": "prettier --write . --cache --ignore-path ../../../.prettierignore",
|
|
138
135
|
"lint": "fluid-build . --task lint",
|
|
139
136
|
"lint:fix": "fluid-build . --task eslint:fix --task format",
|
|
140
137
|
"test": "npm run test:mocha",
|
package/src/assert.ts
CHANGED
|
@@ -51,9 +51,61 @@ export function assert(condition: boolean, message: string | number): asserts co
|
|
|
51
51
|
* @internal
|
|
52
52
|
*/
|
|
53
53
|
export function fail(message: string | number): never {
|
|
54
|
-
|
|
54
|
+
const error = new Error(
|
|
55
55
|
typeof message === "number" ? `0x${message.toString(16).padStart(3, "0")}` : message,
|
|
56
56
|
);
|
|
57
|
+
onAssertionError(error);
|
|
58
|
+
throw error;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function onAssertionError(error: Error): void {
|
|
62
|
+
for (const handler of firstChanceAssertionHandler) {
|
|
63
|
+
handler(error);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const firstChanceAssertionHandler = new Set<(error: Error) => void>();
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Add a callback which can be used to report an assertion before it is thrown.
|
|
71
|
+
* @param handler - Called when an assertion occurs before the exception is thrown.
|
|
72
|
+
* @returns a function to remove the handler.
|
|
73
|
+
* @remarks
|
|
74
|
+
* The callback runs just before the exception is thrown, which makes it a better place to report telemetry for Fluid Framework bugs than a catch block or an event like `window.onerror`.
|
|
75
|
+
* Using this API to report telemetry is preferred over those approaches since it eliminates the risk of the exception being swallowed or obfuscated by an intermediate stack frame's catch block
|
|
76
|
+
* or missed due to not having the right catch block or event handler.
|
|
77
|
+
*
|
|
78
|
+
* This does not replace the need for error handling elsewhere since errors (even bugs in Fluid) can cause other kinds of exceptions which this cannot run the callback for.
|
|
79
|
+
* @example
|
|
80
|
+
* ```ts
|
|
81
|
+
* import { onAssertionFailure } from "fluid-framework/alpha";
|
|
82
|
+
*
|
|
83
|
+
* let firstAssertion: Error | undefined;
|
|
84
|
+
*
|
|
85
|
+
* onAssertionFailure((error: Error) => {
|
|
86
|
+
* const priorErrorNote =
|
|
87
|
+
* firstAssertion === undefined
|
|
88
|
+
* ? "Please report this bug."
|
|
89
|
+
* : `Might be caused due to prior error ${JSON.stringify(firstAssertion.message)} which should be investigated first.`;
|
|
90
|
+
* const message = `Encountered Bug in Fluid Framework: ${error.message}\n${priorErrorNote}\n${error.stack}`;
|
|
91
|
+
* console.error(message);
|
|
92
|
+
*
|
|
93
|
+
* debugger;
|
|
94
|
+
* firstAssertion ??= error;
|
|
95
|
+
* });
|
|
96
|
+
* ```
|
|
97
|
+
* @alpha
|
|
98
|
+
*/
|
|
99
|
+
export function onAssertionFailure(handler: (error: Error) => void): () => void {
|
|
100
|
+
// To avoid issues if the same callback is registered twice (mainly it not triggering twice and the first unregister removing it),
|
|
101
|
+
// generate a wrapper around the handler.
|
|
102
|
+
const wrapper = (error: Error): void => {
|
|
103
|
+
handler(error);
|
|
104
|
+
};
|
|
105
|
+
firstChanceAssertionHandler.add(wrapper);
|
|
106
|
+
return () => {
|
|
107
|
+
firstChanceAssertionHandler.delete(wrapper);
|
|
108
|
+
};
|
|
57
109
|
}
|
|
58
110
|
|
|
59
111
|
/**
|
|
@@ -97,7 +149,9 @@ export function debugAssert(predicate: () => true | { toString(): string }): voi
|
|
|
97
149
|
const result = predicate();
|
|
98
150
|
if (result !== true) {
|
|
99
151
|
debugger;
|
|
100
|
-
|
|
152
|
+
const error = new Error(`Debug assert failed: ${result.toString()}`);
|
|
153
|
+
onAssertionError(error);
|
|
154
|
+
throw error;
|
|
101
155
|
}
|
|
102
156
|
}
|
|
103
157
|
});
|
package/src/index.ts
CHANGED