@fluidframework/core-utils 2.63.0-359461 → 2.63.0-359962
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/api-report/core-utils.legacy.beta.api.md +1 -1
- package/dist/assert.d.ts +62 -14
- package/dist/assert.d.ts.map +1 -1
- package/dist/assert.js +89 -19
- 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 +62 -14
- package/lib/assert.d.ts.map +1 -1
- package/lib/assert.js +87 -18
- 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/package.json +2 -2
- package/src/assert.ts +97 -20
- package/src/index.ts +1 -0
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
```ts
|
|
6
6
|
|
|
7
7
|
// @beta @legacy
|
|
8
|
-
export function assert(condition: boolean, message: string | number): asserts condition;
|
|
8
|
+
export function assert(condition: boolean, message: string | number, debugMessageBuilder?: () => string): asserts condition;
|
|
9
9
|
|
|
10
10
|
// @beta @legacy
|
|
11
11
|
export const compareArrays: <T>(left: readonly T[], right: readonly T[], comparator?: (leftItem: T, rightItem: T, index: number) => boolean) => boolean;
|
package/dist/assert.d.ts
CHANGED
|
@@ -8,9 +8,11 @@
|
|
|
8
8
|
* @param condition - The condition that should be true, if the condition is false an error will be thrown.
|
|
9
9
|
* Only use this API when `false` indicates a logic error in the problem and thus a bug that should be fixed.
|
|
10
10
|
* @param message - The message to include in the error when the condition does not hold.
|
|
11
|
-
* A number should not be specified manually: use a string.
|
|
11
|
+
* A number should not be specified manually: use a string literal instead.
|
|
12
12
|
* Before a release, policy-check should be run, which will convert any asserts still using strings to
|
|
13
13
|
* use numbered error codes instead.
|
|
14
|
+
* @param debugMessageBuilder - An optional function that can be used to build a debug message to include in the error in development builds.
|
|
15
|
+
* Only executed if `condition` is false. `debugMessageBuilder` is not executed in production builds, see `skipInProduction` for details.
|
|
14
16
|
* @remarks
|
|
15
17
|
* Use this instead of the node 'assert' package, which requires polyfills and has a big impact on bundle sizes.
|
|
16
18
|
*
|
|
@@ -18,16 +20,16 @@
|
|
|
18
20
|
* Thus this API is suitable for detecting conditions that should terminate the application and produce a useful diagnostic message.
|
|
19
21
|
* It can be used to ensure bad states are detected early and to avoid data corruption or harder to debug errors.
|
|
20
22
|
*
|
|
21
|
-
* 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
|
|
23
|
+
* 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 {@link debugAssert} instead
|
|
22
24
|
* to optimize bundle size.
|
|
23
25
|
*
|
|
24
26
|
* This API is not intended for use outside of the Fluid Framework client codebase: it will most likely be made internal in the future.
|
|
25
27
|
* @privateRemarks
|
|
26
28
|
* This should be deprecated (as a non internal API) then moved to purely internal.
|
|
27
|
-
* When done the `
|
|
29
|
+
* When done, the `skipInProduction` reference above should be turned into a link.
|
|
28
30
|
* @legacy @beta
|
|
29
31
|
*/
|
|
30
|
-
export declare function assert(condition: boolean, message: string | number): asserts condition;
|
|
32
|
+
export declare function assert(condition: boolean, message: string | number, debugMessageBuilder?: () => string): asserts condition;
|
|
31
33
|
/**
|
|
32
34
|
* Throw an error with a constant message.
|
|
33
35
|
* @remarks
|
|
@@ -41,9 +43,10 @@ export declare function assert(condition: boolean, message: string | number): as
|
|
|
41
43
|
* ```ts
|
|
42
44
|
* const x: number = numbersMap.get("foo") ?? fail("foo missing from map");
|
|
43
45
|
* ```
|
|
46
|
+
* @see {@link assert}
|
|
44
47
|
* @internal
|
|
45
48
|
*/
|
|
46
|
-
export declare function fail(message: string | number): never;
|
|
49
|
+
export declare function fail(message: string | number, debugMessageBuilder?: () => string): never;
|
|
47
50
|
/**
|
|
48
51
|
* Add a callback which can be used to report an assertion before it is thrown.
|
|
49
52
|
* @param handler - Called when an assertion occurs before the exception is thrown.
|
|
@@ -78,21 +81,22 @@ export declare function onAssertionFailure(handler: (error: Error) => void): ()
|
|
|
78
81
|
/**
|
|
79
82
|
* Asserts that can be conditionally enabled in debug/development builds but will be optimized out of production builds.
|
|
80
83
|
*
|
|
81
|
-
*
|
|
84
|
+
* Enabled when {@link nonProductionConditionalsIncluded} is true.
|
|
82
85
|
*
|
|
83
86
|
* If the assert must be enforced/checked in production or enabled by default, use {@link assert} instead.
|
|
84
87
|
*
|
|
85
88
|
* @param predicate - A pure function that should return true if the condition holds, or a string or object describing the condition that failed.
|
|
86
89
|
* 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}.
|
|
87
90
|
* @remarks
|
|
88
|
-
* 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.
|
|
89
|
-
*
|
|
90
91
|
* 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.
|
|
91
92
|
* 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.
|
|
92
93
|
*
|
|
93
|
-
* These asserts are
|
|
94
|
-
*
|
|
95
|
-
*
|
|
94
|
+
* These asserts are enabled by default in debug builds: this introduces risk that code may behave differently when they are disabled or optimized out.
|
|
95
|
+
* To mitigate this risk, these asserts can be disabled in debug builds by calling {@link configureDebugAsserts} or {@link emulateProductionBuild}.
|
|
96
|
+
* This allows testing with the asserts both enabled and disabled to help ensure that code does not depend on them being enabled.
|
|
97
|
+
*
|
|
98
|
+
* Apps (or other performance sensitive scenarios) packaged in a way that does not {@link nonProductionConditionalsIncluded|skip non-production code}
|
|
99
|
+
* can use the same approaches to disable these asserts to reduce performance overhead.
|
|
96
100
|
*
|
|
97
101
|
* @privateRemarks
|
|
98
102
|
* This design was chosen to accomplish two main goals:
|
|
@@ -104,7 +108,7 @@ export declare function onAssertionFailure(handler: (error: Error) => void): ()
|
|
|
104
108
|
* 2. Make it easy to test (both manually and automated) with and without the predicates running.
|
|
105
109
|
* 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.
|
|
106
110
|
*
|
|
107
|
-
* The default behavior of having debugAsserts
|
|
111
|
+
* The default behavior of having debugAsserts enabled helps ensure debugAsserts are effective at catching bugs during development and testing.
|
|
108
112
|
* @internal
|
|
109
113
|
*/
|
|
110
114
|
export declare function debugAssert(predicate: () => true | {
|
|
@@ -114,6 +118,14 @@ export declare function debugAssert(predicate: () => true | {
|
|
|
114
118
|
* Enables {@link debugAssert} validation.
|
|
115
119
|
* @remarks
|
|
116
120
|
* Throws if debugAsserts have been optimized out.
|
|
121
|
+
*
|
|
122
|
+
* Disabling debugAsserts has two main use cases:
|
|
123
|
+
*
|
|
124
|
+
* 1. Testing that the code behaves correctly in a more production like configuration.
|
|
125
|
+
* 2. Reducing performance overhead.
|
|
126
|
+
*
|
|
127
|
+
* Disabling debugAsserts does not make everything production like: see {@link emulateProductionBuild} for a way to disable more non-production code.
|
|
128
|
+
*
|
|
117
129
|
* @returns The previous state of debugAsserts.
|
|
118
130
|
* @internal
|
|
119
131
|
*/
|
|
@@ -121,10 +133,46 @@ export declare function configureDebugAsserts(enabled: boolean): boolean;
|
|
|
121
133
|
/**
|
|
122
134
|
* Checks if non-production conditional code like {@link debugAssert} is included in this build.
|
|
123
135
|
* @remarks
|
|
124
|
-
* Such code can be optimized out by bundlers: this checks if that has occurred.
|
|
136
|
+
* Such code can be optimized out by bundlers or by {@link emulateProductionBuild}: this checks if that has occurred.
|
|
137
|
+
*
|
|
138
|
+
* The non-production used by this library is annotated with `__PURE__` and `#__NO_SIDE_EFFECTS__` and has no return value and thus is removed by bundlers when optimizing based on these annotations.
|
|
139
|
+
* Typically this means that such code is removed in production builds.
|
|
140
|
+
* More details on these annotations can be found at {@link https://github.com/javascript-compiler-hints/compiler-notations-spec/tree/main}.
|
|
125
141
|
* @privateRemarks
|
|
126
|
-
* See {@link
|
|
142
|
+
* See {@link skipInProductionInner}.
|
|
127
143
|
* @internal
|
|
128
144
|
*/
|
|
129
145
|
export declare function nonProductionConditionalsIncluded(): boolean;
|
|
146
|
+
/**
|
|
147
|
+
* Overrides the behavior code which optimizes out non-production conditional code like {@link debugAssert} and {@link nonProductionConditionalsIncluded}.
|
|
148
|
+
*
|
|
149
|
+
* Can be called multiple times. Will emulate production builds if called with `true` more times than `false`.
|
|
150
|
+
* Emulation of production builds is disabled when enabled and disabled counts match (including at 0, by default).
|
|
151
|
+
* It is an error to disable this more than it was enabled.
|
|
152
|
+
*
|
|
153
|
+
* @remarks
|
|
154
|
+
* This is intended for testing that the code behaves correctly in production configurations.
|
|
155
|
+
* Since tools like {@link debugAssert} typically add additional validation to help catch more bugs, tests should generally be run with such checks enabled (and thus emulateProductionBuild in its default disabled state).
|
|
156
|
+
* However it is possible that some debugAsserts could accidentally change behavior and hide a bug.
|
|
157
|
+
* This function provides a way to globally disable the debugAsserts so it is possible to run test suites in a production like mode without having to do a production bundling of them.
|
|
158
|
+
*
|
|
159
|
+
* To avoid introducing additional risk that code does production-specific logic using this setting, the actual setting is not exposed.
|
|
160
|
+
* The intended use is that a pipeline could enable this before running the test suite (for example based on a CLI flag).
|
|
161
|
+
* Such a run may have to also use some filtering to skip any tests which explicity check development only tooling, possibly via {@link nonProductionConditionalsIncluded} or some other mechanism like a test tag.
|
|
162
|
+
*
|
|
163
|
+
* @privateRemarks
|
|
164
|
+
* See {@link skipInProduction}.
|
|
165
|
+
*
|
|
166
|
+
* This design, with a counter, was picked so that it's always safe for some scope to opt in when trying to test production behavior,
|
|
167
|
+
* and it should be basically impossible to accidentally fail to test the production mode when trying to.
|
|
168
|
+
* Some tests or test suites may want to run in production mode and they can use this API to opt in (via before and after hooks for example).
|
|
169
|
+
* Additionally something might want to opt into production mode at some other level (for example test running the entire test suite again with production mode enabled).
|
|
170
|
+
* In such setups, it's important that tests which were explicitly opting in don't accidentally disable production mode for the rest of the run when ending if something higher level enabled it.
|
|
171
|
+
*
|
|
172
|
+
* The approach taken with `configureDebugAsserts` is a bit more flexible, allowing both opt in and opt out, but also more error prone.
|
|
173
|
+
* This API, `emulateProductionBuild` provides a more restrictive but less error prone option targeted at being a final defense for detecting cases where production mode causes issues.
|
|
174
|
+
* It catches some cases `configureDebugAsserts` can't, like dependency on side effects of failing asserts debug message callback.
|
|
175
|
+
* @internal
|
|
176
|
+
*/
|
|
177
|
+
export declare function emulateProductionBuild(enable?: boolean): void;
|
|
130
178
|
//# sourceMappingURL=assert.d.ts.map
|
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
|
|
1
|
+
{"version":3,"file":"assert.d.ts","sourceRoot":"","sources":["../src/assert.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,MAAM,CACrB,SAAS,EAAE,OAAO,EAClB,OAAO,EAAE,MAAM,GAAG,MAAM,EACxB,mBAAmB,CAAC,EAAE,MAAM,MAAM,GAChC,OAAO,CAAC,SAAS,CAInB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,mBAAmB,CAAC,EAAE,MAAM,MAAM,GAAG,KAAK,CAaxF;AAUD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,GAAG,MAAM,IAAI,CAU9E;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,IAAI,GAAG;IAAE,QAAQ,IAAI,MAAM,CAAA;CAAE,GAAG,IAAI,CAehF;AAID;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAQ/D;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,iCAAiC,IAAI,OAAO,CAM3D;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,UAAO,GAAG,IAAI,CAM1D"}
|
package/dist/assert.js
CHANGED
|
@@ -4,16 +4,18 @@
|
|
|
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.onAssertionFailure = exports.fail = exports.assert = void 0;
|
|
7
|
+
exports.emulateProductionBuild = exports.nonProductionConditionalsIncluded = exports.configureDebugAsserts = exports.debugAssert = exports.onAssertionFailure = exports.fail = exports.assert = void 0;
|
|
8
8
|
/**
|
|
9
9
|
* Asserts the specified condition.
|
|
10
10
|
*
|
|
11
11
|
* @param condition - The condition that should be true, if the condition is false an error will be thrown.
|
|
12
12
|
* Only use this API when `false` indicates a logic error in the problem and thus a bug that should be fixed.
|
|
13
13
|
* @param message - The message to include in the error when the condition does not hold.
|
|
14
|
-
* A number should not be specified manually: use a string.
|
|
14
|
+
* A number should not be specified manually: use a string literal instead.
|
|
15
15
|
* Before a release, policy-check should be run, which will convert any asserts still using strings to
|
|
16
16
|
* use numbered error codes instead.
|
|
17
|
+
* @param debugMessageBuilder - An optional function that can be used to build a debug message to include in the error in development builds.
|
|
18
|
+
* Only executed if `condition` is false. `debugMessageBuilder` is not executed in production builds, see `skipInProduction` for details.
|
|
17
19
|
* @remarks
|
|
18
20
|
* Use this instead of the node 'assert' package, which requires polyfills and has a big impact on bundle sizes.
|
|
19
21
|
*
|
|
@@ -21,18 +23,18 @@ exports.nonProductionConditionalsIncluded = exports.configureDebugAsserts = expo
|
|
|
21
23
|
* Thus this API is suitable for detecting conditions that should terminate the application and produce a useful diagnostic message.
|
|
22
24
|
* It can be used to ensure bad states are detected early and to avoid data corruption or harder to debug errors.
|
|
23
25
|
*
|
|
24
|
-
* 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
|
|
26
|
+
* 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 {@link debugAssert} instead
|
|
25
27
|
* to optimize bundle size.
|
|
26
28
|
*
|
|
27
29
|
* This API is not intended for use outside of the Fluid Framework client codebase: it will most likely be made internal in the future.
|
|
28
30
|
* @privateRemarks
|
|
29
31
|
* This should be deprecated (as a non internal API) then moved to purely internal.
|
|
30
|
-
* When done the `
|
|
32
|
+
* When done, the `skipInProduction` reference above should be turned into a link.
|
|
31
33
|
* @legacy @beta
|
|
32
34
|
*/
|
|
33
|
-
function assert(condition, message) {
|
|
35
|
+
function assert(condition, message, debugMessageBuilder) {
|
|
34
36
|
if (!condition) {
|
|
35
|
-
fail(message);
|
|
37
|
+
fail(message, debugMessageBuilder);
|
|
36
38
|
}
|
|
37
39
|
}
|
|
38
40
|
exports.assert = assert;
|
|
@@ -49,10 +51,19 @@ exports.assert = assert;
|
|
|
49
51
|
* ```ts
|
|
50
52
|
* const x: number = numbersMap.get("foo") ?? fail("foo missing from map");
|
|
51
53
|
* ```
|
|
54
|
+
* @see {@link assert}
|
|
52
55
|
* @internal
|
|
53
56
|
*/
|
|
54
|
-
function fail(message) {
|
|
55
|
-
|
|
57
|
+
function fail(message, debugMessageBuilder) {
|
|
58
|
+
let messageString = typeof message === "number" ? `0x${message.toString(16).padStart(3, "0")}` : message;
|
|
59
|
+
skipInProduction(() => {
|
|
60
|
+
if (debugMessageBuilder !== undefined) {
|
|
61
|
+
messageString = `${messageString}\nDebug Message: ${debugMessageBuilder()}`;
|
|
62
|
+
}
|
|
63
|
+
// Using console.log instead of console.error or console.warn since the latter two may break downstream users.
|
|
64
|
+
console.log(`Bug in Fluid Framework: Failed Assertion: ${messageString}`);
|
|
65
|
+
});
|
|
66
|
+
const error = new Error(messageString);
|
|
56
67
|
onAssertionError(error);
|
|
57
68
|
throw error;
|
|
58
69
|
}
|
|
@@ -108,21 +119,22 @@ exports.onAssertionFailure = onAssertionFailure;
|
|
|
108
119
|
/**
|
|
109
120
|
* Asserts that can be conditionally enabled in debug/development builds but will be optimized out of production builds.
|
|
110
121
|
*
|
|
111
|
-
*
|
|
122
|
+
* Enabled when {@link nonProductionConditionalsIncluded} is true.
|
|
112
123
|
*
|
|
113
124
|
* If the assert must be enforced/checked in production or enabled by default, use {@link assert} instead.
|
|
114
125
|
*
|
|
115
126
|
* @param predicate - A pure function that should return true if the condition holds, or a string or object describing the condition that failed.
|
|
116
127
|
* 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}.
|
|
117
128
|
* @remarks
|
|
118
|
-
* 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.
|
|
119
|
-
*
|
|
120
129
|
* 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.
|
|
121
130
|
* 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.
|
|
122
131
|
*
|
|
123
|
-
* These asserts are
|
|
124
|
-
*
|
|
125
|
-
*
|
|
132
|
+
* These asserts are enabled by default in debug builds: this introduces risk that code may behave differently when they are disabled or optimized out.
|
|
133
|
+
* To mitigate this risk, these asserts can be disabled in debug builds by calling {@link configureDebugAsserts} or {@link emulateProductionBuild}.
|
|
134
|
+
* This allows testing with the asserts both enabled and disabled to help ensure that code does not depend on them being enabled.
|
|
135
|
+
*
|
|
136
|
+
* Apps (or other performance sensitive scenarios) packaged in a way that does not {@link nonProductionConditionalsIncluded|skip non-production code}
|
|
137
|
+
* can use the same approaches to disable these asserts to reduce performance overhead.
|
|
126
138
|
*
|
|
127
139
|
* @privateRemarks
|
|
128
140
|
* This design was chosen to accomplish two main goals:
|
|
@@ -134,7 +146,7 @@ exports.onAssertionFailure = onAssertionFailure;
|
|
|
134
146
|
* 2. Make it easy to test (both manually and automated) with and without the predicates running.
|
|
135
147
|
* 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.
|
|
136
148
|
*
|
|
137
|
-
* The default behavior of having debugAsserts
|
|
149
|
+
* The default behavior of having debugAsserts enabled helps ensure debugAsserts are effective at catching bugs during development and testing.
|
|
138
150
|
* @internal
|
|
139
151
|
*/
|
|
140
152
|
function debugAssert(predicate) {
|
|
@@ -154,11 +166,19 @@ function debugAssert(predicate) {
|
|
|
154
166
|
});
|
|
155
167
|
}
|
|
156
168
|
exports.debugAssert = debugAssert;
|
|
157
|
-
let debugAssertsEnabled =
|
|
169
|
+
let debugAssertsEnabled = true;
|
|
158
170
|
/**
|
|
159
171
|
* Enables {@link debugAssert} validation.
|
|
160
172
|
* @remarks
|
|
161
173
|
* Throws if debugAsserts have been optimized out.
|
|
174
|
+
*
|
|
175
|
+
* Disabling debugAsserts has two main use cases:
|
|
176
|
+
*
|
|
177
|
+
* 1. Testing that the code behaves correctly in a more production like configuration.
|
|
178
|
+
* 2. Reducing performance overhead.
|
|
179
|
+
*
|
|
180
|
+
* Disabling debugAsserts does not make everything production like: see {@link emulateProductionBuild} for a way to disable more non-production code.
|
|
181
|
+
*
|
|
162
182
|
* @returns The previous state of debugAsserts.
|
|
163
183
|
* @internal
|
|
164
184
|
*/
|
|
@@ -172,9 +192,13 @@ exports.configureDebugAsserts = configureDebugAsserts;
|
|
|
172
192
|
/**
|
|
173
193
|
* Checks if non-production conditional code like {@link debugAssert} is included in this build.
|
|
174
194
|
* @remarks
|
|
175
|
-
* Such code can be optimized out by bundlers: this checks if that has occurred.
|
|
195
|
+
* Such code can be optimized out by bundlers or by {@link emulateProductionBuild}: this checks if that has occurred.
|
|
196
|
+
*
|
|
197
|
+
* The non-production used by this library is annotated with `__PURE__` and `#__NO_SIDE_EFFECTS__` and has no return value and thus is removed by bundlers when optimizing based on these annotations.
|
|
198
|
+
* Typically this means that such code is removed in production builds.
|
|
199
|
+
* More details on these annotations can be found at {@link https://github.com/javascript-compiler-hints/compiler-notations-spec/tree/main}.
|
|
176
200
|
* @privateRemarks
|
|
177
|
-
* See {@link
|
|
201
|
+
* See {@link skipInProductionInner}.
|
|
178
202
|
* @internal
|
|
179
203
|
*/
|
|
180
204
|
function nonProductionConditionalsIncluded() {
|
|
@@ -185,6 +209,52 @@ function nonProductionConditionalsIncluded() {
|
|
|
185
209
|
return included;
|
|
186
210
|
}
|
|
187
211
|
exports.nonProductionConditionalsIncluded = nonProductionConditionalsIncluded;
|
|
212
|
+
/**
|
|
213
|
+
* Overrides the behavior code which optimizes out non-production conditional code like {@link debugAssert} and {@link nonProductionConditionalsIncluded}.
|
|
214
|
+
*
|
|
215
|
+
* Can be called multiple times. Will emulate production builds if called with `true` more times than `false`.
|
|
216
|
+
* Emulation of production builds is disabled when enabled and disabled counts match (including at 0, by default).
|
|
217
|
+
* It is an error to disable this more than it was enabled.
|
|
218
|
+
*
|
|
219
|
+
* @remarks
|
|
220
|
+
* This is intended for testing that the code behaves correctly in production configurations.
|
|
221
|
+
* Since tools like {@link debugAssert} typically add additional validation to help catch more bugs, tests should generally be run with such checks enabled (and thus emulateProductionBuild in its default disabled state).
|
|
222
|
+
* However it is possible that some debugAsserts could accidentally change behavior and hide a bug.
|
|
223
|
+
* This function provides a way to globally disable the debugAsserts so it is possible to run test suites in a production like mode without having to do a production bundling of them.
|
|
224
|
+
*
|
|
225
|
+
* To avoid introducing additional risk that code does production-specific logic using this setting, the actual setting is not exposed.
|
|
226
|
+
* The intended use is that a pipeline could enable this before running the test suite (for example based on a CLI flag).
|
|
227
|
+
* Such a run may have to also use some filtering to skip any tests which explicity check development only tooling, possibly via {@link nonProductionConditionalsIncluded} or some other mechanism like a test tag.
|
|
228
|
+
*
|
|
229
|
+
* @privateRemarks
|
|
230
|
+
* See {@link skipInProduction}.
|
|
231
|
+
*
|
|
232
|
+
* This design, with a counter, was picked so that it's always safe for some scope to opt in when trying to test production behavior,
|
|
233
|
+
* and it should be basically impossible to accidentally fail to test the production mode when trying to.
|
|
234
|
+
* Some tests or test suites may want to run in production mode and they can use this API to opt in (via before and after hooks for example).
|
|
235
|
+
* Additionally something might want to opt into production mode at some other level (for example test running the entire test suite again with production mode enabled).
|
|
236
|
+
* In such setups, it's important that tests which were explicitly opting in don't accidentally disable production mode for the rest of the run when ending if something higher level enabled it.
|
|
237
|
+
*
|
|
238
|
+
* The approach taken with `configureDebugAsserts` is a bit more flexible, allowing both opt in and opt out, but also more error prone.
|
|
239
|
+
* This API, `emulateProductionBuild` provides a more restrictive but less error prone option targeted at being a final defense for detecting cases where production mode causes issues.
|
|
240
|
+
* It catches some cases `configureDebugAsserts` can't, like dependency on side effects of failing asserts debug message callback.
|
|
241
|
+
* @internal
|
|
242
|
+
*/
|
|
243
|
+
function emulateProductionBuild(enable = true) {
|
|
244
|
+
emulateProductionBuildCount += enable ? 1 : -1;
|
|
245
|
+
assert(emulateProductionBuildCount >= 0, "emulateProductionBuild disabled more than it was enabled");
|
|
246
|
+
}
|
|
247
|
+
exports.emulateProductionBuild = emulateProductionBuild;
|
|
248
|
+
let emulateProductionBuildCount = 0;
|
|
249
|
+
/**
|
|
250
|
+
* {@link skipInProductionInner}, except can be disabled by {@link emulateProductionBuild}.
|
|
251
|
+
*/
|
|
252
|
+
function skipInProduction(conditional) {
|
|
253
|
+
skipInProductionInner(() => {
|
|
254
|
+
if (emulateProductionBuildCount === 0)
|
|
255
|
+
conditional();
|
|
256
|
+
});
|
|
257
|
+
}
|
|
188
258
|
/**
|
|
189
259
|
* Run `conditional` only in debug/development (non optimized/minified) builds, but optimize it out of production builds.
|
|
190
260
|
*
|
|
@@ -203,7 +273,7 @@ exports.nonProductionConditionalsIncluded = nonProductionConditionalsIncluded;
|
|
|
203
273
|
// 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.
|
|
204
274
|
// eslint-disable-next-line spaced-comment
|
|
205
275
|
/*#__NO_SIDE_EFFECTS__*/
|
|
206
|
-
function
|
|
276
|
+
function skipInProductionInner(conditional) {
|
|
207
277
|
// Here __PURE__ annotation is used to indicate that is is safe to optimize out this call.
|
|
208
278
|
// This is valid since the contract for this function is that "conditional" should be side effect free if it were run in production scenarios
|
|
209
279
|
// See https://webpack.js.org/guides/tree-shaking/#mark-a-function-call-as-side-effect-free for documentation on this annotation.
|
package/dist/assert.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assert.js","sourceRoot":"","sources":["../src/assert.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\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 @beta\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"]}
|
|
1
|
+
{"version":3,"file":"assert.js","sourceRoot":"","sources":["../src/assert.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\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 literal instead.\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 * @param debugMessageBuilder - An optional function that can be used to build a debug message to include in the error in development builds.\n * Only executed if `condition` is false. `debugMessageBuilder` is not executed in production builds, see `skipInProduction` for details.\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 {@link 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 `skipInProduction` reference above should be turned into a link.\n * @legacy @beta\n */\nexport function assert(\n\tcondition: boolean,\n\tmessage: string | number,\n\tdebugMessageBuilder?: () => string,\n): asserts condition {\n\tif (!condition) {\n\t\tfail(message, debugMessageBuilder);\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 * @see {@link assert}\n * @internal\n */\nexport function fail(message: string | number, debugMessageBuilder?: () => string): never {\n\tlet messageString =\n\t\ttypeof message === \"number\" ? `0x${message.toString(16).padStart(3, \"0\")}` : message;\n\tskipInProduction(() => {\n\t\tif (debugMessageBuilder !== undefined) {\n\t\t\tmessageString = `${messageString}\\nDebug Message: ${debugMessageBuilder()}`;\n\t\t}\n\t\t// Using console.log instead of console.error or console.warn since the latter two may break downstream users.\n\t\tconsole.log(`Bug in Fluid Framework: Failed Assertion: ${messageString}`);\n\t});\n\tconst error = new Error(messageString);\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 * Enabled when {@link nonProductionConditionalsIncluded} is true.\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 * 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 enabled by default in debug builds: this introduces risk that code may behave differently when they are disabled or optimized out.\n * To mitigate this risk, these asserts can be disabled in debug builds by calling {@link configureDebugAsserts} or {@link emulateProductionBuild}.\n * This allows testing with the asserts both enabled and disabled to help ensure that code does not depend on them being enabled.\n *\n * Apps (or other performance sensitive scenarios) packaged in a way that does not {@link nonProductionConditionalsIncluded|skip non-production code}\n * can use the same approaches to disable these asserts to reduce performance overhead.\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 enabled helps ensure debugAsserts are effective at catching bugs during development and testing.\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 = true;\n\n/**\n * Enables {@link debugAssert} validation.\n * @remarks\n * Throws if debugAsserts have been optimized out.\n *\n * Disabling debugAsserts has two main use cases:\n *\n * 1. Testing that the code behaves correctly in a more production like configuration.\n * 2. Reducing performance overhead.\n *\n * Disabling debugAsserts does not make everything production like: see {@link emulateProductionBuild} for a way to disable more non-production code.\n *\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 or by {@link emulateProductionBuild}: this checks if that has occurred.\n *\n * The non-production used by this library is annotated with `__PURE__` and `#__NO_SIDE_EFFECTS__` and has no return value and thus is removed by bundlers when optimizing based on these annotations.\n * Typically this means that such code is removed in production builds.\n * More details on these annotations can be found at {@link https://github.com/javascript-compiler-hints/compiler-notations-spec/tree/main}.\n * @privateRemarks\n * See {@link skipInProductionInner}.\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 * Overrides the behavior code which optimizes out non-production conditional code like {@link debugAssert} and {@link nonProductionConditionalsIncluded}.\n *\n * Can be called multiple times. Will emulate production builds if called with `true` more times than `false`.\n * Emulation of production builds is disabled when enabled and disabled counts match (including at 0, by default).\n * It is an error to disable this more than it was enabled.\n *\n * @remarks\n * This is intended for testing that the code behaves correctly in production configurations.\n * Since tools like {@link debugAssert} typically add additional validation to help catch more bugs, tests should generally be run with such checks enabled (and thus emulateProductionBuild in its default disabled state).\n * However it is possible that some debugAsserts could accidentally change behavior and hide a bug.\n * This function provides a way to globally disable the debugAsserts so it is possible to run test suites in a production like mode without having to do a production bundling of them.\n *\n * To avoid introducing additional risk that code does production-specific logic using this setting, the actual setting is not exposed.\n * The intended use is that a pipeline could enable this before running the test suite (for example based on a CLI flag).\n * Such a run may have to also use some filtering to skip any tests which explicity check development only tooling, possibly via {@link nonProductionConditionalsIncluded} or some other mechanism like a test tag.\n *\n * @privateRemarks\n * See {@link skipInProduction}.\n *\n * This design, with a counter, was picked so that it's always safe for some scope to opt in when trying to test production behavior,\n * and it should be basically impossible to accidentally fail to test the production mode when trying to.\n * Some tests or test suites may want to run in production mode and they can use this API to opt in (via before and after hooks for example).\n * Additionally something might want to opt into production mode at some other level (for example test running the entire test suite again with production mode enabled).\n * In such setups, it's important that tests which were explicitly opting in don't accidentally disable production mode for the rest of the run when ending if something higher level enabled it.\n *\n * The approach taken with `configureDebugAsserts` is a bit more flexible, allowing both opt in and opt out, but also more error prone.\n * This API, `emulateProductionBuild` provides a more restrictive but less error prone option targeted at being a final defense for detecting cases where production mode causes issues.\n * It catches some cases `configureDebugAsserts` can't, like dependency on side effects of failing asserts debug message callback.\n * @internal\n */\nexport function emulateProductionBuild(enable = true): void {\n\temulateProductionBuildCount += enable ? 1 : -1;\n\tassert(\n\t\temulateProductionBuildCount >= 0,\n\t\t\"emulateProductionBuild disabled more than it was enabled\",\n\t);\n}\n\nlet emulateProductionBuildCount = 0;\n\n/**\n * {@link skipInProductionInner}, except can be disabled by {@link emulateProductionBuild}.\n */\nfunction skipInProduction(conditional: () => void): void {\n\tskipInProductionInner(() => {\n\t\tif (emulateProductionBuildCount === 0) conditional();\n\t});\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 skipInProductionInner(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, onAssertionFailure, } from "./assert.js";
|
|
5
|
+
export { assert, fail, debugAssert, configureDebugAsserts, nonProductionConditionalsIncluded, emulateProductionBuild, 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,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,OAAO,EACN,gBAAgB,EAChB,KAAK,QAAQ,EACb,KAAK,aAAa,EAClB,sBAAsB,EACtB,QAAQ,GACR,MAAM,WAAW,CAAC;AACnB,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"}
|
|
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,sBAAsB,EACtB,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,OAAO,EACN,gBAAgB,EAChB,KAAK,QAAQ,EACb,KAAK,aAAa,EAClB,sBAAsB,EACtB,QAAQ,GACR,MAAM,WAAW,CAAC;AACnB,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.walkList = exports.iterateListValuesWhile = exports.DoublyLinkedList = 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;
|
|
7
|
+
exports.oob = exports.isPromiseLike = exports.isObject = exports.unreachableCase = exports.Timer = exports.setLongTimeout = exports.PromiseTimer = exports.shallowCloneObject = exports.Deferred = exports.PromiseCache = exports.walkList = exports.iterateListValuesWhile = exports.DoublyLinkedList = exports.LazyPromise = exports.Lazy = exports.NumberComparer = exports.Heap = exports.delay = exports.compareArrays = exports.onAssertionFailure = exports.emulateProductionBuild = 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, "emulateProductionBuild", { enumerable: true, get: function () { return assert_js_1.emulateProductionBuild; } });
|
|
14
15
|
Object.defineProperty(exports, "onAssertionFailure", { enumerable: true, get: function () { return assert_js_1.onAssertionFailure; } });
|
|
15
16
|
var compare_js_1 = require("./compare.js");
|
|
16
17
|
Object.defineProperty(exports, "compareArrays", { enumerable: true, get: function () { return compare_js_1.compareArrays; } });
|
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,yCAQqB;AAPpB,mGAAA,MAAM,OAAA;AACN,iGAAA,IAAI,OAAA;AACJ,wGAAA,WAAW,OAAA;AACX,kHAAA,qBAAqB,OAAA;AACrB,8HAAA,iCAAiC,OAAA;AACjC,mHAAA,sBAAsB,OAAA;AACtB,+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;AAC1B,qCAMmB;AALlB,2GAAA,gBAAgB,OAAA;AAGhB,iHAAA,sBAAsB,OAAA;AACtB,mGAAA,QAAQ,OAAA;AAGT,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\temulateProductionBuild,\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 {\n\tDoublyLinkedList,\n\ttype ListNode,\n\ttype ListNodeRange,\n\titerateListValuesWhile,\n\twalkList,\n} from \"./list.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
|
@@ -8,9 +8,11 @@
|
|
|
8
8
|
* @param condition - The condition that should be true, if the condition is false an error will be thrown.
|
|
9
9
|
* Only use this API when `false` indicates a logic error in the problem and thus a bug that should be fixed.
|
|
10
10
|
* @param message - The message to include in the error when the condition does not hold.
|
|
11
|
-
* A number should not be specified manually: use a string.
|
|
11
|
+
* A number should not be specified manually: use a string literal instead.
|
|
12
12
|
* Before a release, policy-check should be run, which will convert any asserts still using strings to
|
|
13
13
|
* use numbered error codes instead.
|
|
14
|
+
* @param debugMessageBuilder - An optional function that can be used to build a debug message to include in the error in development builds.
|
|
15
|
+
* Only executed if `condition` is false. `debugMessageBuilder` is not executed in production builds, see `skipInProduction` for details.
|
|
14
16
|
* @remarks
|
|
15
17
|
* Use this instead of the node 'assert' package, which requires polyfills and has a big impact on bundle sizes.
|
|
16
18
|
*
|
|
@@ -18,16 +20,16 @@
|
|
|
18
20
|
* Thus this API is suitable for detecting conditions that should terminate the application and produce a useful diagnostic message.
|
|
19
21
|
* It can be used to ensure bad states are detected early and to avoid data corruption or harder to debug errors.
|
|
20
22
|
*
|
|
21
|
-
* 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
|
|
23
|
+
* 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 {@link debugAssert} instead
|
|
22
24
|
* to optimize bundle size.
|
|
23
25
|
*
|
|
24
26
|
* This API is not intended for use outside of the Fluid Framework client codebase: it will most likely be made internal in the future.
|
|
25
27
|
* @privateRemarks
|
|
26
28
|
* This should be deprecated (as a non internal API) then moved to purely internal.
|
|
27
|
-
* When done the `
|
|
29
|
+
* When done, the `skipInProduction` reference above should be turned into a link.
|
|
28
30
|
* @legacy @beta
|
|
29
31
|
*/
|
|
30
|
-
export declare function assert(condition: boolean, message: string | number): asserts condition;
|
|
32
|
+
export declare function assert(condition: boolean, message: string | number, debugMessageBuilder?: () => string): asserts condition;
|
|
31
33
|
/**
|
|
32
34
|
* Throw an error with a constant message.
|
|
33
35
|
* @remarks
|
|
@@ -41,9 +43,10 @@ export declare function assert(condition: boolean, message: string | number): as
|
|
|
41
43
|
* ```ts
|
|
42
44
|
* const x: number = numbersMap.get("foo") ?? fail("foo missing from map");
|
|
43
45
|
* ```
|
|
46
|
+
* @see {@link assert}
|
|
44
47
|
* @internal
|
|
45
48
|
*/
|
|
46
|
-
export declare function fail(message: string | number): never;
|
|
49
|
+
export declare function fail(message: string | number, debugMessageBuilder?: () => string): never;
|
|
47
50
|
/**
|
|
48
51
|
* Add a callback which can be used to report an assertion before it is thrown.
|
|
49
52
|
* @param handler - Called when an assertion occurs before the exception is thrown.
|
|
@@ -78,21 +81,22 @@ export declare function onAssertionFailure(handler: (error: Error) => void): ()
|
|
|
78
81
|
/**
|
|
79
82
|
* Asserts that can be conditionally enabled in debug/development builds but will be optimized out of production builds.
|
|
80
83
|
*
|
|
81
|
-
*
|
|
84
|
+
* Enabled when {@link nonProductionConditionalsIncluded} is true.
|
|
82
85
|
*
|
|
83
86
|
* If the assert must be enforced/checked in production or enabled by default, use {@link assert} instead.
|
|
84
87
|
*
|
|
85
88
|
* @param predicate - A pure function that should return true if the condition holds, or a string or object describing the condition that failed.
|
|
86
89
|
* 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}.
|
|
87
90
|
* @remarks
|
|
88
|
-
* 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.
|
|
89
|
-
*
|
|
90
91
|
* 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.
|
|
91
92
|
* 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.
|
|
92
93
|
*
|
|
93
|
-
* These asserts are
|
|
94
|
-
*
|
|
95
|
-
*
|
|
94
|
+
* These asserts are enabled by default in debug builds: this introduces risk that code may behave differently when they are disabled or optimized out.
|
|
95
|
+
* To mitigate this risk, these asserts can be disabled in debug builds by calling {@link configureDebugAsserts} or {@link emulateProductionBuild}.
|
|
96
|
+
* This allows testing with the asserts both enabled and disabled to help ensure that code does not depend on them being enabled.
|
|
97
|
+
*
|
|
98
|
+
* Apps (or other performance sensitive scenarios) packaged in a way that does not {@link nonProductionConditionalsIncluded|skip non-production code}
|
|
99
|
+
* can use the same approaches to disable these asserts to reduce performance overhead.
|
|
96
100
|
*
|
|
97
101
|
* @privateRemarks
|
|
98
102
|
* This design was chosen to accomplish two main goals:
|
|
@@ -104,7 +108,7 @@ export declare function onAssertionFailure(handler: (error: Error) => void): ()
|
|
|
104
108
|
* 2. Make it easy to test (both manually and automated) with and without the predicates running.
|
|
105
109
|
* 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.
|
|
106
110
|
*
|
|
107
|
-
* The default behavior of having debugAsserts
|
|
111
|
+
* The default behavior of having debugAsserts enabled helps ensure debugAsserts are effective at catching bugs during development and testing.
|
|
108
112
|
* @internal
|
|
109
113
|
*/
|
|
110
114
|
export declare function debugAssert(predicate: () => true | {
|
|
@@ -114,6 +118,14 @@ export declare function debugAssert(predicate: () => true | {
|
|
|
114
118
|
* Enables {@link debugAssert} validation.
|
|
115
119
|
* @remarks
|
|
116
120
|
* Throws if debugAsserts have been optimized out.
|
|
121
|
+
*
|
|
122
|
+
* Disabling debugAsserts has two main use cases:
|
|
123
|
+
*
|
|
124
|
+
* 1. Testing that the code behaves correctly in a more production like configuration.
|
|
125
|
+
* 2. Reducing performance overhead.
|
|
126
|
+
*
|
|
127
|
+
* Disabling debugAsserts does not make everything production like: see {@link emulateProductionBuild} for a way to disable more non-production code.
|
|
128
|
+
*
|
|
117
129
|
* @returns The previous state of debugAsserts.
|
|
118
130
|
* @internal
|
|
119
131
|
*/
|
|
@@ -121,10 +133,46 @@ export declare function configureDebugAsserts(enabled: boolean): boolean;
|
|
|
121
133
|
/**
|
|
122
134
|
* Checks if non-production conditional code like {@link debugAssert} is included in this build.
|
|
123
135
|
* @remarks
|
|
124
|
-
* Such code can be optimized out by bundlers: this checks if that has occurred.
|
|
136
|
+
* Such code can be optimized out by bundlers or by {@link emulateProductionBuild}: this checks if that has occurred.
|
|
137
|
+
*
|
|
138
|
+
* The non-production used by this library is annotated with `__PURE__` and `#__NO_SIDE_EFFECTS__` and has no return value and thus is removed by bundlers when optimizing based on these annotations.
|
|
139
|
+
* Typically this means that such code is removed in production builds.
|
|
140
|
+
* More details on these annotations can be found at {@link https://github.com/javascript-compiler-hints/compiler-notations-spec/tree/main}.
|
|
125
141
|
* @privateRemarks
|
|
126
|
-
* See {@link
|
|
142
|
+
* See {@link skipInProductionInner}.
|
|
127
143
|
* @internal
|
|
128
144
|
*/
|
|
129
145
|
export declare function nonProductionConditionalsIncluded(): boolean;
|
|
146
|
+
/**
|
|
147
|
+
* Overrides the behavior code which optimizes out non-production conditional code like {@link debugAssert} and {@link nonProductionConditionalsIncluded}.
|
|
148
|
+
*
|
|
149
|
+
* Can be called multiple times. Will emulate production builds if called with `true` more times than `false`.
|
|
150
|
+
* Emulation of production builds is disabled when enabled and disabled counts match (including at 0, by default).
|
|
151
|
+
* It is an error to disable this more than it was enabled.
|
|
152
|
+
*
|
|
153
|
+
* @remarks
|
|
154
|
+
* This is intended for testing that the code behaves correctly in production configurations.
|
|
155
|
+
* Since tools like {@link debugAssert} typically add additional validation to help catch more bugs, tests should generally be run with such checks enabled (and thus emulateProductionBuild in its default disabled state).
|
|
156
|
+
* However it is possible that some debugAsserts could accidentally change behavior and hide a bug.
|
|
157
|
+
* This function provides a way to globally disable the debugAsserts so it is possible to run test suites in a production like mode without having to do a production bundling of them.
|
|
158
|
+
*
|
|
159
|
+
* To avoid introducing additional risk that code does production-specific logic using this setting, the actual setting is not exposed.
|
|
160
|
+
* The intended use is that a pipeline could enable this before running the test suite (for example based on a CLI flag).
|
|
161
|
+
* Such a run may have to also use some filtering to skip any tests which explicity check development only tooling, possibly via {@link nonProductionConditionalsIncluded} or some other mechanism like a test tag.
|
|
162
|
+
*
|
|
163
|
+
* @privateRemarks
|
|
164
|
+
* See {@link skipInProduction}.
|
|
165
|
+
*
|
|
166
|
+
* This design, with a counter, was picked so that it's always safe for some scope to opt in when trying to test production behavior,
|
|
167
|
+
* and it should be basically impossible to accidentally fail to test the production mode when trying to.
|
|
168
|
+
* Some tests or test suites may want to run in production mode and they can use this API to opt in (via before and after hooks for example).
|
|
169
|
+
* Additionally something might want to opt into production mode at some other level (for example test running the entire test suite again with production mode enabled).
|
|
170
|
+
* In such setups, it's important that tests which were explicitly opting in don't accidentally disable production mode for the rest of the run when ending if something higher level enabled it.
|
|
171
|
+
*
|
|
172
|
+
* The approach taken with `configureDebugAsserts` is a bit more flexible, allowing both opt in and opt out, but also more error prone.
|
|
173
|
+
* This API, `emulateProductionBuild` provides a more restrictive but less error prone option targeted at being a final defense for detecting cases where production mode causes issues.
|
|
174
|
+
* It catches some cases `configureDebugAsserts` can't, like dependency on side effects of failing asserts debug message callback.
|
|
175
|
+
* @internal
|
|
176
|
+
*/
|
|
177
|
+
export declare function emulateProductionBuild(enable?: boolean): void;
|
|
130
178
|
//# sourceMappingURL=assert.d.ts.map
|
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
|
|
1
|
+
{"version":3,"file":"assert.d.ts","sourceRoot":"","sources":["../src/assert.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,MAAM,CACrB,SAAS,EAAE,OAAO,EAClB,OAAO,EAAE,MAAM,GAAG,MAAM,EACxB,mBAAmB,CAAC,EAAE,MAAM,MAAM,GAChC,OAAO,CAAC,SAAS,CAInB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,mBAAmB,CAAC,EAAE,MAAM,MAAM,GAAG,KAAK,CAaxF;AAUD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,GAAG,MAAM,IAAI,CAU9E;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,IAAI,GAAG;IAAE,QAAQ,IAAI,MAAM,CAAA;CAAE,GAAG,IAAI,CAehF;AAID;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAQ/D;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,iCAAiC,IAAI,OAAO,CAM3D;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,UAAO,GAAG,IAAI,CAM1D"}
|
package/lib/assert.js
CHANGED
|
@@ -8,9 +8,11 @@
|
|
|
8
8
|
* @param condition - The condition that should be true, if the condition is false an error will be thrown.
|
|
9
9
|
* Only use this API when `false` indicates a logic error in the problem and thus a bug that should be fixed.
|
|
10
10
|
* @param message - The message to include in the error when the condition does not hold.
|
|
11
|
-
* A number should not be specified manually: use a string.
|
|
11
|
+
* A number should not be specified manually: use a string literal instead.
|
|
12
12
|
* Before a release, policy-check should be run, which will convert any asserts still using strings to
|
|
13
13
|
* use numbered error codes instead.
|
|
14
|
+
* @param debugMessageBuilder - An optional function that can be used to build a debug message to include in the error in development builds.
|
|
15
|
+
* Only executed if `condition` is false. `debugMessageBuilder` is not executed in production builds, see `skipInProduction` for details.
|
|
14
16
|
* @remarks
|
|
15
17
|
* Use this instead of the node 'assert' package, which requires polyfills and has a big impact on bundle sizes.
|
|
16
18
|
*
|
|
@@ -18,18 +20,18 @@
|
|
|
18
20
|
* Thus this API is suitable for detecting conditions that should terminate the application and produce a useful diagnostic message.
|
|
19
21
|
* It can be used to ensure bad states are detected early and to avoid data corruption or harder to debug errors.
|
|
20
22
|
*
|
|
21
|
-
* 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
|
|
23
|
+
* 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 {@link debugAssert} instead
|
|
22
24
|
* to optimize bundle size.
|
|
23
25
|
*
|
|
24
26
|
* This API is not intended for use outside of the Fluid Framework client codebase: it will most likely be made internal in the future.
|
|
25
27
|
* @privateRemarks
|
|
26
28
|
* This should be deprecated (as a non internal API) then moved to purely internal.
|
|
27
|
-
* When done the `
|
|
29
|
+
* When done, the `skipInProduction` reference above should be turned into a link.
|
|
28
30
|
* @legacy @beta
|
|
29
31
|
*/
|
|
30
|
-
export function assert(condition, message) {
|
|
32
|
+
export function assert(condition, message, debugMessageBuilder) {
|
|
31
33
|
if (!condition) {
|
|
32
|
-
fail(message);
|
|
34
|
+
fail(message, debugMessageBuilder);
|
|
33
35
|
}
|
|
34
36
|
}
|
|
35
37
|
/**
|
|
@@ -45,10 +47,19 @@ export function assert(condition, message) {
|
|
|
45
47
|
* ```ts
|
|
46
48
|
* const x: number = numbersMap.get("foo") ?? fail("foo missing from map");
|
|
47
49
|
* ```
|
|
50
|
+
* @see {@link assert}
|
|
48
51
|
* @internal
|
|
49
52
|
*/
|
|
50
|
-
export function fail(message) {
|
|
51
|
-
|
|
53
|
+
export function fail(message, debugMessageBuilder) {
|
|
54
|
+
let messageString = typeof message === "number" ? `0x${message.toString(16).padStart(3, "0")}` : message;
|
|
55
|
+
skipInProduction(() => {
|
|
56
|
+
if (debugMessageBuilder !== undefined) {
|
|
57
|
+
messageString = `${messageString}\nDebug Message: ${debugMessageBuilder()}`;
|
|
58
|
+
}
|
|
59
|
+
// Using console.log instead of console.error or console.warn since the latter two may break downstream users.
|
|
60
|
+
console.log(`Bug in Fluid Framework: Failed Assertion: ${messageString}`);
|
|
61
|
+
});
|
|
62
|
+
const error = new Error(messageString);
|
|
52
63
|
onAssertionError(error);
|
|
53
64
|
throw error;
|
|
54
65
|
}
|
|
@@ -102,21 +113,22 @@ export function onAssertionFailure(handler) {
|
|
|
102
113
|
/**
|
|
103
114
|
* Asserts that can be conditionally enabled in debug/development builds but will be optimized out of production builds.
|
|
104
115
|
*
|
|
105
|
-
*
|
|
116
|
+
* Enabled when {@link nonProductionConditionalsIncluded} is true.
|
|
106
117
|
*
|
|
107
118
|
* If the assert must be enforced/checked in production or enabled by default, use {@link assert} instead.
|
|
108
119
|
*
|
|
109
120
|
* @param predicate - A pure function that should return true if the condition holds, or a string or object describing the condition that failed.
|
|
110
121
|
* 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}.
|
|
111
122
|
* @remarks
|
|
112
|
-
* 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.
|
|
113
|
-
*
|
|
114
123
|
* 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.
|
|
115
124
|
* 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.
|
|
116
125
|
*
|
|
117
|
-
* These asserts are
|
|
118
|
-
*
|
|
119
|
-
*
|
|
126
|
+
* These asserts are enabled by default in debug builds: this introduces risk that code may behave differently when they are disabled or optimized out.
|
|
127
|
+
* To mitigate this risk, these asserts can be disabled in debug builds by calling {@link configureDebugAsserts} or {@link emulateProductionBuild}.
|
|
128
|
+
* This allows testing with the asserts both enabled and disabled to help ensure that code does not depend on them being enabled.
|
|
129
|
+
*
|
|
130
|
+
* Apps (or other performance sensitive scenarios) packaged in a way that does not {@link nonProductionConditionalsIncluded|skip non-production code}
|
|
131
|
+
* can use the same approaches to disable these asserts to reduce performance overhead.
|
|
120
132
|
*
|
|
121
133
|
* @privateRemarks
|
|
122
134
|
* This design was chosen to accomplish two main goals:
|
|
@@ -128,7 +140,7 @@ export function onAssertionFailure(handler) {
|
|
|
128
140
|
* 2. Make it easy to test (both manually and automated) with and without the predicates running.
|
|
129
141
|
* 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.
|
|
130
142
|
*
|
|
131
|
-
* The default behavior of having debugAsserts
|
|
143
|
+
* The default behavior of having debugAsserts enabled helps ensure debugAsserts are effective at catching bugs during development and testing.
|
|
132
144
|
* @internal
|
|
133
145
|
*/
|
|
134
146
|
export function debugAssert(predicate) {
|
|
@@ -147,11 +159,19 @@ export function debugAssert(predicate) {
|
|
|
147
159
|
}
|
|
148
160
|
});
|
|
149
161
|
}
|
|
150
|
-
let debugAssertsEnabled =
|
|
162
|
+
let debugAssertsEnabled = true;
|
|
151
163
|
/**
|
|
152
164
|
* Enables {@link debugAssert} validation.
|
|
153
165
|
* @remarks
|
|
154
166
|
* Throws if debugAsserts have been optimized out.
|
|
167
|
+
*
|
|
168
|
+
* Disabling debugAsserts has two main use cases:
|
|
169
|
+
*
|
|
170
|
+
* 1. Testing that the code behaves correctly in a more production like configuration.
|
|
171
|
+
* 2. Reducing performance overhead.
|
|
172
|
+
*
|
|
173
|
+
* Disabling debugAsserts does not make everything production like: see {@link emulateProductionBuild} for a way to disable more non-production code.
|
|
174
|
+
*
|
|
155
175
|
* @returns The previous state of debugAsserts.
|
|
156
176
|
* @internal
|
|
157
177
|
*/
|
|
@@ -164,9 +184,13 @@ export function configureDebugAsserts(enabled) {
|
|
|
164
184
|
/**
|
|
165
185
|
* Checks if non-production conditional code like {@link debugAssert} is included in this build.
|
|
166
186
|
* @remarks
|
|
167
|
-
* Such code can be optimized out by bundlers: this checks if that has occurred.
|
|
187
|
+
* Such code can be optimized out by bundlers or by {@link emulateProductionBuild}: this checks if that has occurred.
|
|
188
|
+
*
|
|
189
|
+
* The non-production used by this library is annotated with `__PURE__` and `#__NO_SIDE_EFFECTS__` and has no return value and thus is removed by bundlers when optimizing based on these annotations.
|
|
190
|
+
* Typically this means that such code is removed in production builds.
|
|
191
|
+
* More details on these annotations can be found at {@link https://github.com/javascript-compiler-hints/compiler-notations-spec/tree/main}.
|
|
168
192
|
* @privateRemarks
|
|
169
|
-
* See {@link
|
|
193
|
+
* See {@link skipInProductionInner}.
|
|
170
194
|
* @internal
|
|
171
195
|
*/
|
|
172
196
|
export function nonProductionConditionalsIncluded() {
|
|
@@ -176,6 +200,51 @@ export function nonProductionConditionalsIncluded() {
|
|
|
176
200
|
});
|
|
177
201
|
return included;
|
|
178
202
|
}
|
|
203
|
+
/**
|
|
204
|
+
* Overrides the behavior code which optimizes out non-production conditional code like {@link debugAssert} and {@link nonProductionConditionalsIncluded}.
|
|
205
|
+
*
|
|
206
|
+
* Can be called multiple times. Will emulate production builds if called with `true` more times than `false`.
|
|
207
|
+
* Emulation of production builds is disabled when enabled and disabled counts match (including at 0, by default).
|
|
208
|
+
* It is an error to disable this more than it was enabled.
|
|
209
|
+
*
|
|
210
|
+
* @remarks
|
|
211
|
+
* This is intended for testing that the code behaves correctly in production configurations.
|
|
212
|
+
* Since tools like {@link debugAssert} typically add additional validation to help catch more bugs, tests should generally be run with such checks enabled (and thus emulateProductionBuild in its default disabled state).
|
|
213
|
+
* However it is possible that some debugAsserts could accidentally change behavior and hide a bug.
|
|
214
|
+
* This function provides a way to globally disable the debugAsserts so it is possible to run test suites in a production like mode without having to do a production bundling of them.
|
|
215
|
+
*
|
|
216
|
+
* To avoid introducing additional risk that code does production-specific logic using this setting, the actual setting is not exposed.
|
|
217
|
+
* The intended use is that a pipeline could enable this before running the test suite (for example based on a CLI flag).
|
|
218
|
+
* Such a run may have to also use some filtering to skip any tests which explicity check development only tooling, possibly via {@link nonProductionConditionalsIncluded} or some other mechanism like a test tag.
|
|
219
|
+
*
|
|
220
|
+
* @privateRemarks
|
|
221
|
+
* See {@link skipInProduction}.
|
|
222
|
+
*
|
|
223
|
+
* This design, with a counter, was picked so that it's always safe for some scope to opt in when trying to test production behavior,
|
|
224
|
+
* and it should be basically impossible to accidentally fail to test the production mode when trying to.
|
|
225
|
+
* Some tests or test suites may want to run in production mode and they can use this API to opt in (via before and after hooks for example).
|
|
226
|
+
* Additionally something might want to opt into production mode at some other level (for example test running the entire test suite again with production mode enabled).
|
|
227
|
+
* In such setups, it's important that tests which were explicitly opting in don't accidentally disable production mode for the rest of the run when ending if something higher level enabled it.
|
|
228
|
+
*
|
|
229
|
+
* The approach taken with `configureDebugAsserts` is a bit more flexible, allowing both opt in and opt out, but also more error prone.
|
|
230
|
+
* This API, `emulateProductionBuild` provides a more restrictive but less error prone option targeted at being a final defense for detecting cases where production mode causes issues.
|
|
231
|
+
* It catches some cases `configureDebugAsserts` can't, like dependency on side effects of failing asserts debug message callback.
|
|
232
|
+
* @internal
|
|
233
|
+
*/
|
|
234
|
+
export function emulateProductionBuild(enable = true) {
|
|
235
|
+
emulateProductionBuildCount += enable ? 1 : -1;
|
|
236
|
+
assert(emulateProductionBuildCount >= 0, "emulateProductionBuild disabled more than it was enabled");
|
|
237
|
+
}
|
|
238
|
+
let emulateProductionBuildCount = 0;
|
|
239
|
+
/**
|
|
240
|
+
* {@link skipInProductionInner}, except can be disabled by {@link emulateProductionBuild}.
|
|
241
|
+
*/
|
|
242
|
+
function skipInProduction(conditional) {
|
|
243
|
+
skipInProductionInner(() => {
|
|
244
|
+
if (emulateProductionBuildCount === 0)
|
|
245
|
+
conditional();
|
|
246
|
+
});
|
|
247
|
+
}
|
|
179
248
|
/**
|
|
180
249
|
* Run `conditional` only in debug/development (non optimized/minified) builds, but optimize it out of production builds.
|
|
181
250
|
*
|
|
@@ -194,7 +263,7 @@ export function nonProductionConditionalsIncluded() {
|
|
|
194
263
|
// 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.
|
|
195
264
|
// eslint-disable-next-line spaced-comment
|
|
196
265
|
/*#__NO_SIDE_EFFECTS__*/
|
|
197
|
-
function
|
|
266
|
+
function skipInProductionInner(conditional) {
|
|
198
267
|
// Here __PURE__ annotation is used to indicate that is is safe to optimize out this call.
|
|
199
268
|
// This is valid since the contract for this function is that "conditional" should be side effect free if it were run in production scenarios
|
|
200
269
|
// See https://webpack.js.org/guides/tree-shaking/#mark-a-function-call-as-side-effect-free for documentation on this annotation.
|
package/lib/assert.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assert.js","sourceRoot":"","sources":["../src/assert.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;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 @beta\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"]}
|
|
1
|
+
{"version":3,"file":"assert.js","sourceRoot":"","sources":["../src/assert.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,UAAU,MAAM,CACrB,SAAkB,EAClB,OAAwB,EACxB,mBAAkC;IAElC,IAAI,CAAC,SAAS,EAAE,CAAC;QAChB,IAAI,CAAC,OAAO,EAAE,mBAAmB,CAAC,CAAC;IACpC,CAAC;AACF,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,IAAI,CAAC,OAAwB,EAAE,mBAAkC;IAChF,IAAI,aAAa,GAChB,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,CAAC;IACtF,gBAAgB,CAAC,GAAG,EAAE;QACrB,IAAI,mBAAmB,KAAK,SAAS,EAAE,CAAC;YACvC,aAAa,GAAG,GAAG,aAAa,oBAAoB,mBAAmB,EAAE,EAAE,CAAC;QAC7E,CAAC;QACD,8GAA8G;QAC9G,OAAO,CAAC,GAAG,CAAC,6CAA6C,aAAa,EAAE,CAAC,CAAC;IAC3E,CAAC,CAAC,CAAC;IACH,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,aAAa,CAAC,CAAC;IACvC,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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;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,IAAI,CAAC;AAE/B;;;;;;;;;;;;;;GAcG;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;;;;;;;;;;;GAWG;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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,MAAM,UAAU,sBAAsB,CAAC,MAAM,GAAG,IAAI;IACnD,2BAA2B,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/C,MAAM,CACL,2BAA2B,IAAI,CAAC,EAChC,0DAA0D,CAC1D,CAAC;AACH,CAAC;AAED,IAAI,2BAA2B,GAAG,CAAC,CAAC;AAEpC;;GAEG;AACH,SAAS,gBAAgB,CAAC,WAAuB;IAChD,qBAAqB,CAAC,GAAG,EAAE;QAC1B,IAAI,2BAA2B,KAAK,CAAC;YAAE,WAAW,EAAE,CAAC;IACtD,CAAC,CAAC,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,iMAAiM;AACjM,0CAA0C;AAC1C,wBAAwB;AACxB,SAAS,qBAAqB,CAAC,WAAuB;IACrD,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 literal instead.\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 * @param debugMessageBuilder - An optional function that can be used to build a debug message to include in the error in development builds.\n * Only executed if `condition` is false. `debugMessageBuilder` is not executed in production builds, see `skipInProduction` for details.\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 {@link 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 `skipInProduction` reference above should be turned into a link.\n * @legacy @beta\n */\nexport function assert(\n\tcondition: boolean,\n\tmessage: string | number,\n\tdebugMessageBuilder?: () => string,\n): asserts condition {\n\tif (!condition) {\n\t\tfail(message, debugMessageBuilder);\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 * @see {@link assert}\n * @internal\n */\nexport function fail(message: string | number, debugMessageBuilder?: () => string): never {\n\tlet messageString =\n\t\ttypeof message === \"number\" ? `0x${message.toString(16).padStart(3, \"0\")}` : message;\n\tskipInProduction(() => {\n\t\tif (debugMessageBuilder !== undefined) {\n\t\t\tmessageString = `${messageString}\\nDebug Message: ${debugMessageBuilder()}`;\n\t\t}\n\t\t// Using console.log instead of console.error or console.warn since the latter two may break downstream users.\n\t\tconsole.log(`Bug in Fluid Framework: Failed Assertion: ${messageString}`);\n\t});\n\tconst error = new Error(messageString);\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 * Enabled when {@link nonProductionConditionalsIncluded} is true.\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 * 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 enabled by default in debug builds: this introduces risk that code may behave differently when they are disabled or optimized out.\n * To mitigate this risk, these asserts can be disabled in debug builds by calling {@link configureDebugAsserts} or {@link emulateProductionBuild}.\n * This allows testing with the asserts both enabled and disabled to help ensure that code does not depend on them being enabled.\n *\n * Apps (or other performance sensitive scenarios) packaged in a way that does not {@link nonProductionConditionalsIncluded|skip non-production code}\n * can use the same approaches to disable these asserts to reduce performance overhead.\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 enabled helps ensure debugAsserts are effective at catching bugs during development and testing.\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 = true;\n\n/**\n * Enables {@link debugAssert} validation.\n * @remarks\n * Throws if debugAsserts have been optimized out.\n *\n * Disabling debugAsserts has two main use cases:\n *\n * 1. Testing that the code behaves correctly in a more production like configuration.\n * 2. Reducing performance overhead.\n *\n * Disabling debugAsserts does not make everything production like: see {@link emulateProductionBuild} for a way to disable more non-production code.\n *\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 or by {@link emulateProductionBuild}: this checks if that has occurred.\n *\n * The non-production used by this library is annotated with `__PURE__` and `#__NO_SIDE_EFFECTS__` and has no return value and thus is removed by bundlers when optimizing based on these annotations.\n * Typically this means that such code is removed in production builds.\n * More details on these annotations can be found at {@link https://github.com/javascript-compiler-hints/compiler-notations-spec/tree/main}.\n * @privateRemarks\n * See {@link skipInProductionInner}.\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 * Overrides the behavior code which optimizes out non-production conditional code like {@link debugAssert} and {@link nonProductionConditionalsIncluded}.\n *\n * Can be called multiple times. Will emulate production builds if called with `true` more times than `false`.\n * Emulation of production builds is disabled when enabled and disabled counts match (including at 0, by default).\n * It is an error to disable this more than it was enabled.\n *\n * @remarks\n * This is intended for testing that the code behaves correctly in production configurations.\n * Since tools like {@link debugAssert} typically add additional validation to help catch more bugs, tests should generally be run with such checks enabled (and thus emulateProductionBuild in its default disabled state).\n * However it is possible that some debugAsserts could accidentally change behavior and hide a bug.\n * This function provides a way to globally disable the debugAsserts so it is possible to run test suites in a production like mode without having to do a production bundling of them.\n *\n * To avoid introducing additional risk that code does production-specific logic using this setting, the actual setting is not exposed.\n * The intended use is that a pipeline could enable this before running the test suite (for example based on a CLI flag).\n * Such a run may have to also use some filtering to skip any tests which explicity check development only tooling, possibly via {@link nonProductionConditionalsIncluded} or some other mechanism like a test tag.\n *\n * @privateRemarks\n * See {@link skipInProduction}.\n *\n * This design, with a counter, was picked so that it's always safe for some scope to opt in when trying to test production behavior,\n * and it should be basically impossible to accidentally fail to test the production mode when trying to.\n * Some tests or test suites may want to run in production mode and they can use this API to opt in (via before and after hooks for example).\n * Additionally something might want to opt into production mode at some other level (for example test running the entire test suite again with production mode enabled).\n * In such setups, it's important that tests which were explicitly opting in don't accidentally disable production mode for the rest of the run when ending if something higher level enabled it.\n *\n * The approach taken with `configureDebugAsserts` is a bit more flexible, allowing both opt in and opt out, but also more error prone.\n * This API, `emulateProductionBuild` provides a more restrictive but less error prone option targeted at being a final defense for detecting cases where production mode causes issues.\n * It catches some cases `configureDebugAsserts` can't, like dependency on side effects of failing asserts debug message callback.\n * @internal\n */\nexport function emulateProductionBuild(enable = true): void {\n\temulateProductionBuildCount += enable ? 1 : -1;\n\tassert(\n\t\temulateProductionBuildCount >= 0,\n\t\t\"emulateProductionBuild disabled more than it was enabled\",\n\t);\n}\n\nlet emulateProductionBuildCount = 0;\n\n/**\n * {@link skipInProductionInner}, except can be disabled by {@link emulateProductionBuild}.\n */\nfunction skipInProduction(conditional: () => void): void {\n\tskipInProductionInner(() => {\n\t\tif (emulateProductionBuildCount === 0) conditional();\n\t});\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 skipInProductionInner(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, onAssertionFailure, } from "./assert.js";
|
|
5
|
+
export { assert, fail, debugAssert, configureDebugAsserts, nonProductionConditionalsIncluded, emulateProductionBuild, 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,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,OAAO,EACN,gBAAgB,EAChB,KAAK,QAAQ,EACb,KAAK,aAAa,EAClB,sBAAsB,EACtB,QAAQ,GACR,MAAM,WAAW,CAAC;AACnB,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"}
|
|
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,sBAAsB,EACtB,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,OAAO,EACN,gBAAgB,EAChB,KAAK,QAAQ,EACb,KAAK,aAAa,EAClB,sBAAsB,EACtB,QAAQ,GACR,MAAM,WAAW,CAAC;AACnB,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, onAssertionFailure, } from "./assert.js";
|
|
5
|
+
export { assert, fail, debugAssert, configureDebugAsserts, nonProductionConditionalsIncluded, emulateProductionBuild, 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,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;AAC9C,OAAO,EACN,gBAAgB,EAGhB,sBAAsB,EACtB,QAAQ,GACR,MAAM,WAAW,CAAC;AAEnB,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 {\n\tDoublyLinkedList,\n\ttype ListNode,\n\ttype ListNodeRange,\n\titerateListValuesWhile,\n\twalkList,\n} from \"./list.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"]}
|
|
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,sBAAsB,EACtB,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;AAC9C,OAAO,EACN,gBAAgB,EAGhB,sBAAsB,EACtB,QAAQ,GACR,MAAM,WAAW,CAAC;AAEnB,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\temulateProductionBuild,\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 {\n\tDoublyLinkedList,\n\ttype ListNode,\n\ttype ListNodeRange,\n\titerateListValuesWhile,\n\twalkList,\n} from \"./list.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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fluidframework/core-utils",
|
|
3
|
-
"version": "2.63.0-
|
|
3
|
+
"version": "2.63.0-359962",
|
|
4
4
|
"description": "Not intended for use outside the Fluid client repo.",
|
|
5
5
|
"homepage": "https://fluidframework.com",
|
|
6
6
|
"repository": {
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
"devDependencies": {
|
|
70
70
|
"@arethetypeswrong/cli": "^0.17.1",
|
|
71
71
|
"@biomejs/biome": "~1.9.3",
|
|
72
|
-
"@fluid-internal/mocha-test-setup": "2.63.0-
|
|
72
|
+
"@fluid-internal/mocha-test-setup": "2.63.0-359962",
|
|
73
73
|
"@fluid-tools/benchmark": "^0.51.0",
|
|
74
74
|
"@fluid-tools/build-cli": "^0.58.3",
|
|
75
75
|
"@fluidframework/build-common": "^2.0.3",
|
package/src/assert.ts
CHANGED
|
@@ -9,9 +9,11 @@
|
|
|
9
9
|
* @param condition - The condition that should be true, if the condition is false an error will be thrown.
|
|
10
10
|
* Only use this API when `false` indicates a logic error in the problem and thus a bug that should be fixed.
|
|
11
11
|
* @param message - The message to include in the error when the condition does not hold.
|
|
12
|
-
* A number should not be specified manually: use a string.
|
|
12
|
+
* A number should not be specified manually: use a string literal instead.
|
|
13
13
|
* Before a release, policy-check should be run, which will convert any asserts still using strings to
|
|
14
14
|
* use numbered error codes instead.
|
|
15
|
+
* @param debugMessageBuilder - An optional function that can be used to build a debug message to include in the error in development builds.
|
|
16
|
+
* Only executed if `condition` is false. `debugMessageBuilder` is not executed in production builds, see `skipInProduction` for details.
|
|
15
17
|
* @remarks
|
|
16
18
|
* Use this instead of the node 'assert' package, which requires polyfills and has a big impact on bundle sizes.
|
|
17
19
|
*
|
|
@@ -19,18 +21,22 @@
|
|
|
19
21
|
* Thus this API is suitable for detecting conditions that should terminate the application and produce a useful diagnostic message.
|
|
20
22
|
* It can be used to ensure bad states are detected early and to avoid data corruption or harder to debug errors.
|
|
21
23
|
*
|
|
22
|
-
* 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
|
|
24
|
+
* 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 {@link debugAssert} instead
|
|
23
25
|
* to optimize bundle size.
|
|
24
26
|
*
|
|
25
27
|
* This API is not intended for use outside of the Fluid Framework client codebase: it will most likely be made internal in the future.
|
|
26
28
|
* @privateRemarks
|
|
27
29
|
* This should be deprecated (as a non internal API) then moved to purely internal.
|
|
28
|
-
* When done the `
|
|
30
|
+
* When done, the `skipInProduction` reference above should be turned into a link.
|
|
29
31
|
* @legacy @beta
|
|
30
32
|
*/
|
|
31
|
-
export function assert(
|
|
33
|
+
export function assert(
|
|
34
|
+
condition: boolean,
|
|
35
|
+
message: string | number,
|
|
36
|
+
debugMessageBuilder?: () => string,
|
|
37
|
+
): asserts condition {
|
|
32
38
|
if (!condition) {
|
|
33
|
-
fail(message);
|
|
39
|
+
fail(message, debugMessageBuilder);
|
|
34
40
|
}
|
|
35
41
|
}
|
|
36
42
|
|
|
@@ -47,12 +53,20 @@ export function assert(condition: boolean, message: string | number): asserts co
|
|
|
47
53
|
* ```ts
|
|
48
54
|
* const x: number = numbersMap.get("foo") ?? fail("foo missing from map");
|
|
49
55
|
* ```
|
|
56
|
+
* @see {@link assert}
|
|
50
57
|
* @internal
|
|
51
58
|
*/
|
|
52
|
-
export function fail(message: string | number): never {
|
|
53
|
-
|
|
54
|
-
typeof message === "number" ? `0x${message.toString(16).padStart(3, "0")}` : message
|
|
55
|
-
)
|
|
59
|
+
export function fail(message: string | number, debugMessageBuilder?: () => string): never {
|
|
60
|
+
let messageString =
|
|
61
|
+
typeof message === "number" ? `0x${message.toString(16).padStart(3, "0")}` : message;
|
|
62
|
+
skipInProduction(() => {
|
|
63
|
+
if (debugMessageBuilder !== undefined) {
|
|
64
|
+
messageString = `${messageString}\nDebug Message: ${debugMessageBuilder()}`;
|
|
65
|
+
}
|
|
66
|
+
// Using console.log instead of console.error or console.warn since the latter two may break downstream users.
|
|
67
|
+
console.log(`Bug in Fluid Framework: Failed Assertion: ${messageString}`);
|
|
68
|
+
});
|
|
69
|
+
const error = new Error(messageString);
|
|
56
70
|
onAssertionError(error);
|
|
57
71
|
throw error;
|
|
58
72
|
}
|
|
@@ -110,21 +124,22 @@ export function onAssertionFailure(handler: (error: Error) => void): () => void
|
|
|
110
124
|
/**
|
|
111
125
|
* Asserts that can be conditionally enabled in debug/development builds but will be optimized out of production builds.
|
|
112
126
|
*
|
|
113
|
-
*
|
|
127
|
+
* Enabled when {@link nonProductionConditionalsIncluded} is true.
|
|
114
128
|
*
|
|
115
129
|
* If the assert must be enforced/checked in production or enabled by default, use {@link assert} instead.
|
|
116
130
|
*
|
|
117
131
|
* @param predicate - A pure function that should return true if the condition holds, or a string or object describing the condition that failed.
|
|
118
132
|
* 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}.
|
|
119
133
|
* @remarks
|
|
120
|
-
* 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.
|
|
121
|
-
*
|
|
122
134
|
* 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.
|
|
123
135
|
* 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.
|
|
124
136
|
*
|
|
125
|
-
* These asserts are
|
|
126
|
-
*
|
|
127
|
-
*
|
|
137
|
+
* These asserts are enabled by default in debug builds: this introduces risk that code may behave differently when they are disabled or optimized out.
|
|
138
|
+
* To mitigate this risk, these asserts can be disabled in debug builds by calling {@link configureDebugAsserts} or {@link emulateProductionBuild}.
|
|
139
|
+
* This allows testing with the asserts both enabled and disabled to help ensure that code does not depend on them being enabled.
|
|
140
|
+
*
|
|
141
|
+
* Apps (or other performance sensitive scenarios) packaged in a way that does not {@link nonProductionConditionalsIncluded|skip non-production code}
|
|
142
|
+
* can use the same approaches to disable these asserts to reduce performance overhead.
|
|
128
143
|
*
|
|
129
144
|
* @privateRemarks
|
|
130
145
|
* This design was chosen to accomplish two main goals:
|
|
@@ -136,7 +151,7 @@ export function onAssertionFailure(handler: (error: Error) => void): () => void
|
|
|
136
151
|
* 2. Make it easy to test (both manually and automated) with and without the predicates running.
|
|
137
152
|
* 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.
|
|
138
153
|
*
|
|
139
|
-
* The default behavior of having debugAsserts
|
|
154
|
+
* The default behavior of having debugAsserts enabled helps ensure debugAsserts are effective at catching bugs during development and testing.
|
|
140
155
|
* @internal
|
|
141
156
|
*/
|
|
142
157
|
export function debugAssert(predicate: () => true | { toString(): string }): void {
|
|
@@ -156,12 +171,20 @@ export function debugAssert(predicate: () => true | { toString(): string }): voi
|
|
|
156
171
|
});
|
|
157
172
|
}
|
|
158
173
|
|
|
159
|
-
let debugAssertsEnabled =
|
|
174
|
+
let debugAssertsEnabled = true;
|
|
160
175
|
|
|
161
176
|
/**
|
|
162
177
|
* Enables {@link debugAssert} validation.
|
|
163
178
|
* @remarks
|
|
164
179
|
* Throws if debugAsserts have been optimized out.
|
|
180
|
+
*
|
|
181
|
+
* Disabling debugAsserts has two main use cases:
|
|
182
|
+
*
|
|
183
|
+
* 1. Testing that the code behaves correctly in a more production like configuration.
|
|
184
|
+
* 2. Reducing performance overhead.
|
|
185
|
+
*
|
|
186
|
+
* Disabling debugAsserts does not make everything production like: see {@link emulateProductionBuild} for a way to disable more non-production code.
|
|
187
|
+
*
|
|
165
188
|
* @returns The previous state of debugAsserts.
|
|
166
189
|
* @internal
|
|
167
190
|
*/
|
|
@@ -178,9 +201,13 @@ export function configureDebugAsserts(enabled: boolean): boolean {
|
|
|
178
201
|
/**
|
|
179
202
|
* Checks if non-production conditional code like {@link debugAssert} is included in this build.
|
|
180
203
|
* @remarks
|
|
181
|
-
* Such code can be optimized out by bundlers: this checks if that has occurred.
|
|
204
|
+
* Such code can be optimized out by bundlers or by {@link emulateProductionBuild}: this checks if that has occurred.
|
|
205
|
+
*
|
|
206
|
+
* The non-production used by this library is annotated with `__PURE__` and `#__NO_SIDE_EFFECTS__` and has no return value and thus is removed by bundlers when optimizing based on these annotations.
|
|
207
|
+
* Typically this means that such code is removed in production builds.
|
|
208
|
+
* More details on these annotations can be found at {@link https://github.com/javascript-compiler-hints/compiler-notations-spec/tree/main}.
|
|
182
209
|
* @privateRemarks
|
|
183
|
-
* See {@link
|
|
210
|
+
* See {@link skipInProductionInner}.
|
|
184
211
|
* @internal
|
|
185
212
|
*/
|
|
186
213
|
export function nonProductionConditionalsIncluded(): boolean {
|
|
@@ -191,6 +218,56 @@ export function nonProductionConditionalsIncluded(): boolean {
|
|
|
191
218
|
return included;
|
|
192
219
|
}
|
|
193
220
|
|
|
221
|
+
/**
|
|
222
|
+
* Overrides the behavior code which optimizes out non-production conditional code like {@link debugAssert} and {@link nonProductionConditionalsIncluded}.
|
|
223
|
+
*
|
|
224
|
+
* Can be called multiple times. Will emulate production builds if called with `true` more times than `false`.
|
|
225
|
+
* Emulation of production builds is disabled when enabled and disabled counts match (including at 0, by default).
|
|
226
|
+
* It is an error to disable this more than it was enabled.
|
|
227
|
+
*
|
|
228
|
+
* @remarks
|
|
229
|
+
* This is intended for testing that the code behaves correctly in production configurations.
|
|
230
|
+
* Since tools like {@link debugAssert} typically add additional validation to help catch more bugs, tests should generally be run with such checks enabled (and thus emulateProductionBuild in its default disabled state).
|
|
231
|
+
* However it is possible that some debugAsserts could accidentally change behavior and hide a bug.
|
|
232
|
+
* This function provides a way to globally disable the debugAsserts so it is possible to run test suites in a production like mode without having to do a production bundling of them.
|
|
233
|
+
*
|
|
234
|
+
* To avoid introducing additional risk that code does production-specific logic using this setting, the actual setting is not exposed.
|
|
235
|
+
* The intended use is that a pipeline could enable this before running the test suite (for example based on a CLI flag).
|
|
236
|
+
* Such a run may have to also use some filtering to skip any tests which explicity check development only tooling, possibly via {@link nonProductionConditionalsIncluded} or some other mechanism like a test tag.
|
|
237
|
+
*
|
|
238
|
+
* @privateRemarks
|
|
239
|
+
* See {@link skipInProduction}.
|
|
240
|
+
*
|
|
241
|
+
* This design, with a counter, was picked so that it's always safe for some scope to opt in when trying to test production behavior,
|
|
242
|
+
* and it should be basically impossible to accidentally fail to test the production mode when trying to.
|
|
243
|
+
* Some tests or test suites may want to run in production mode and they can use this API to opt in (via before and after hooks for example).
|
|
244
|
+
* Additionally something might want to opt into production mode at some other level (for example test running the entire test suite again with production mode enabled).
|
|
245
|
+
* In such setups, it's important that tests which were explicitly opting in don't accidentally disable production mode for the rest of the run when ending if something higher level enabled it.
|
|
246
|
+
*
|
|
247
|
+
* The approach taken with `configureDebugAsserts` is a bit more flexible, allowing both opt in and opt out, but also more error prone.
|
|
248
|
+
* This API, `emulateProductionBuild` provides a more restrictive but less error prone option targeted at being a final defense for detecting cases where production mode causes issues.
|
|
249
|
+
* It catches some cases `configureDebugAsserts` can't, like dependency on side effects of failing asserts debug message callback.
|
|
250
|
+
* @internal
|
|
251
|
+
*/
|
|
252
|
+
export function emulateProductionBuild(enable = true): void {
|
|
253
|
+
emulateProductionBuildCount += enable ? 1 : -1;
|
|
254
|
+
assert(
|
|
255
|
+
emulateProductionBuildCount >= 0,
|
|
256
|
+
"emulateProductionBuild disabled more than it was enabled",
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
let emulateProductionBuildCount = 0;
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* {@link skipInProductionInner}, except can be disabled by {@link emulateProductionBuild}.
|
|
264
|
+
*/
|
|
265
|
+
function skipInProduction(conditional: () => void): void {
|
|
266
|
+
skipInProductionInner(() => {
|
|
267
|
+
if (emulateProductionBuildCount === 0) conditional();
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
|
|
194
271
|
/**
|
|
195
272
|
* Run `conditional` only in debug/development (non optimized/minified) builds, but optimize it out of production builds.
|
|
196
273
|
*
|
|
@@ -209,7 +286,7 @@ export function nonProductionConditionalsIncluded(): boolean {
|
|
|
209
286
|
// 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.
|
|
210
287
|
// eslint-disable-next-line spaced-comment
|
|
211
288
|
/*#__NO_SIDE_EFFECTS__*/
|
|
212
|
-
function
|
|
289
|
+
function skipInProductionInner(conditional: () => void): void {
|
|
213
290
|
// Here __PURE__ annotation is used to indicate that is is safe to optimize out this call.
|
|
214
291
|
// This is valid since the contract for this function is that "conditional" should be side effect free if it were run in production scenarios
|
|
215
292
|
// See https://webpack.js.org/guides/tree-shaking/#mark-a-function-call-as-side-effect-free for documentation on this annotation.
|