@fluidframework/core-utils 2.20.0 → 2.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # @fluidframework/core-utils
2
2
 
3
+ ## 2.21.0
4
+
5
+ Dependency updates only.
6
+
3
7
  ## 2.20.0
4
8
 
5
9
  Dependency updates only.
package/dist/assert.d.ts CHANGED
@@ -3,16 +3,82 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
  /**
6
- * A browser friendly assert library.
7
- * Use this instead of the 'assert' package, which has a big impact on bundle sizes.
6
+ * Asserts the specified condition.
7
+ *
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
11
  * A number should not be specified manually: use a string.
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
+ * @remarks
15
+ * Use this instead of the node 'assert' package, which requires polyfills and has a big impact on bundle sizes.
16
+ *
17
+ * Assertions using this API will be included in all configurations: there is no option to disable or optimize them out.
18
+ * Thus this API is suitable for detecting conditions that should terminate the application and produce a useful diagnostic message.
19
+ * It can be used to ensure bad states are detected early and to avoid data corruption or harder to debug errors.
20
+ *
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 `debugAssert` instead
22
+ * to optimize bundle size.
23
+ *
24
+ * 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
+ * @privateRemarks
26
+ * This should be deprecated (as a non internal API) then moved to purely internal.
27
+ * When done the `debugAssert` reference above should be turned into a link.
14
28
  * @legacy
15
29
  * @alpha
16
30
  */
17
31
  export declare function assert(condition: boolean, message: string | number): asserts condition;
32
+ /**
33
+ * Asserts that can be conditionally enabled in debug/development builds but will be optimized out of production builds.
34
+ *
35
+ * Disabled by default.
36
+ *
37
+ * If the assert must be enforced/checked in production or enabled by default, use {@link assert} instead.
38
+ *
39
+ * @param predicate - A pure function that should return true if the condition holds, or a string or object describing the condition that failed.
40
+ * 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}.
41
+ * @remarks
42
+ * 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.
43
+ *
44
+ * 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.
45
+ * 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.
46
+ *
47
+ * 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.
48
+ * Additionally, this ensures that apps that use a bundler which does not remove `__PURE__` will not incur the runtime cost of calling the predicate.
49
+ * These asserts can be can be enabled by calling `configureDebugAsserts(true)`: see {@link configureDebugAsserts}.
50
+ *
51
+ * @privateRemarks
52
+ * This design was chosen to accomplish two main goals:
53
+ *
54
+ * 1. Make it easy to compile debug asserts fully out of production builds.
55
+ * For webpack this happens by default, avoiding the need for customers to do special configuration.
56
+ * This is important for both performance and bundle size.
57
+ *
58
+ * 2. Make it easy to test (both manually and automated) with and without the predicates running.
59
+ * 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.
60
+ *
61
+ * 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.
62
+ * @internal
63
+ */
64
+ export declare function debugAssert(predicate: () => true | {
65
+ toString(): string;
66
+ }): void;
67
+ /**
68
+ * Enables {@link debugAssert} validation.
69
+ * @remarks
70
+ * Throws if debugAsserts have been optimized out.
71
+ * @returns The previous state of debugAsserts.
72
+ * @internal
73
+ */
74
+ export declare function configureDebugAsserts(enabled: boolean): boolean;
75
+ /**
76
+ * Checks if non-production conditional code like {@link debugAssert} is included in this build.
77
+ * @remarks
78
+ * Such code can be optimized out by bundlers: this checks if that has occurred.
79
+ * @privateRemarks
80
+ * See {@link skipInProduction}.
81
+ * @internal
82
+ */
83
+ export declare function nonProductionConditionalsIncluded(): boolean;
18
84
  //# sourceMappingURL=assert.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"assert.d.ts","sourceRoot":"","sources":["../src/assert.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;;;;;;GAWG;AACH,wBAAgB,MAAM,CAAC,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,SAAS,CAMtF"}
1
+ {"version":3,"file":"assert.d.ts","sourceRoot":"","sources":["../src/assert.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,MAAM,CAAC,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,SAAS,CAMtF;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,IAAI,GAAG;IAAE,QAAQ,IAAI,MAAM,CAAA;CAAE,GAAG,IAAI,CAahF;AAID;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAQ/D;AAED;;;;;;;GAOG;AACH,wBAAgB,iCAAiC,IAAI,OAAO,CAM3D"}
package/dist/assert.js CHANGED
@@ -4,16 +4,30 @@
4
4
  * Licensed under the MIT License.
5
5
  */
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.assert = void 0;
7
+ exports.nonProductionConditionalsIncluded = exports.configureDebugAsserts = exports.debugAssert = exports.assert = void 0;
8
8
  /**
9
- * A browser friendly assert library.
10
- * Use this instead of the 'assert' package, which has a big impact on bundle sizes.
9
+ * Asserts the specified condition.
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
14
  * A number should not be specified manually: use a string.
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
+ * @remarks
18
+ * Use this instead of the node 'assert' package, which requires polyfills and has a big impact on bundle sizes.
19
+ *
20
+ * Assertions using this API will be included in all configurations: there is no option to disable or optimize them out.
21
+ * Thus this API is suitable for detecting conditions that should terminate the application and produce a useful diagnostic message.
22
+ * It can be used to ensure bad states are detected early and to avoid data corruption or harder to debug errors.
23
+ *
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 `debugAssert` instead
25
+ * to optimize bundle size.
26
+ *
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.
28
+ * @privateRemarks
29
+ * This should be deprecated (as a non internal API) then moved to purely internal.
30
+ * When done the `debugAssert` reference above should be turned into a link.
17
31
  * @legacy
18
32
  * @alpha
19
33
  */
@@ -23,4 +37,108 @@ function assert(condition, message) {
23
37
  }
24
38
  }
25
39
  exports.assert = assert;
40
+ /**
41
+ * Asserts that can be conditionally enabled in debug/development builds but will be optimized out of production builds.
42
+ *
43
+ * Disabled by default.
44
+ *
45
+ * If the assert must be enforced/checked in production or enabled by default, use {@link assert} instead.
46
+ *
47
+ * @param predicate - A pure function that should return true if the condition holds, or a string or object describing the condition that failed.
48
+ * 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}.
49
+ * @remarks
50
+ * 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.
51
+ *
52
+ * 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.
53
+ * 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.
54
+ *
55
+ * 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.
56
+ * Additionally, this ensures that apps that use a bundler which does not remove `__PURE__` will not incur the runtime cost of calling the predicate.
57
+ * These asserts can be can be enabled by calling `configureDebugAsserts(true)`: see {@link configureDebugAsserts}.
58
+ *
59
+ * @privateRemarks
60
+ * This design was chosen to accomplish two main goals:
61
+ *
62
+ * 1. Make it easy to compile debug asserts fully out of production builds.
63
+ * For webpack this happens by default, avoiding the need for customers to do special configuration.
64
+ * This is important for both performance and bundle size.
65
+ *
66
+ * 2. Make it easy to test (both manually and automated) with and without the predicates running.
67
+ * 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.
68
+ *
69
+ * 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.
70
+ * @internal
71
+ */
72
+ function debugAssert(predicate) {
73
+ // 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:
74
+ // 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.
75
+ // Production scenarios, where pure code is removed, should never hit a failing predicate, and thus this code should be side effect free.
76
+ skipInProduction(() => {
77
+ if (debugAssertsEnabled) {
78
+ const result = predicate();
79
+ if (result !== true) {
80
+ debugger;
81
+ throw new Error(`Debug assert failed: ${result.toString()}`);
82
+ }
83
+ }
84
+ });
85
+ }
86
+ exports.debugAssert = debugAssert;
87
+ let debugAssertsEnabled = false;
88
+ /**
89
+ * Enables {@link debugAssert} validation.
90
+ * @remarks
91
+ * Throws if debugAsserts have been optimized out.
92
+ * @returns The previous state of debugAsserts.
93
+ * @internal
94
+ */
95
+ function configureDebugAsserts(enabled) {
96
+ assert(nonProductionConditionalsIncluded(), 0xab1 /* Debug asserts cannot be configured since they have been optimized out. */);
97
+ const old = debugAssertsEnabled;
98
+ debugAssertsEnabled = enabled;
99
+ return old;
100
+ }
101
+ exports.configureDebugAsserts = configureDebugAsserts;
102
+ /**
103
+ * Checks if non-production conditional code like {@link debugAssert} is included in this build.
104
+ * @remarks
105
+ * Such code can be optimized out by bundlers: this checks if that has occurred.
106
+ * @privateRemarks
107
+ * See {@link skipInProduction}.
108
+ * @internal
109
+ */
110
+ function nonProductionConditionalsIncluded() {
111
+ let included = false;
112
+ skipInProduction(() => {
113
+ included = true;
114
+ });
115
+ return included;
116
+ }
117
+ exports.nonProductionConditionalsIncluded = nonProductionConditionalsIncluded;
118
+ /**
119
+ * Run `conditional` only in debug/development (non optimized/minified) builds, but optimize it out of production builds.
120
+ *
121
+ * @param conditional - This function will only be run in some configurations so it should be pure (at least in production scenarios).
122
+ * 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.
123
+ * @remarks
124
+ * Great care must be taken when using this to ensure that bugs are not introduced which only occur when `conditional` is not run.
125
+ * 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:
126
+ * {@link debugAssert} uses this pattern.
127
+ *
128
+ * @privateRemarks
129
+ * 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.
130
+ * There are some additional details about syntax and bundler support in https://github.com/javascript-compiler-hints/compiler-notations-spec/tree/main .
131
+ * This code uses both NO_SIDE_EFFECTS and PURE to maximize compatibility: for any bundler supporting both they are redundant.
132
+ */
133
+ // 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.
134
+ // eslint-disable-next-line spaced-comment
135
+ /*#__NO_SIDE_EFFECTS__*/
136
+ function skipInProduction(conditional) {
137
+ // Here __PURE__ annotation is used to indicate that is is safe to optimize out this call.
138
+ // This is valid since the contract for this function is that "conditional" should be side effect free if it were run in production scenarios
139
+ // See https://webpack.js.org/guides/tree-shaking/#mark-a-function-call-as-side-effect-free for documentation on this annotation.
140
+ // 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.
141
+ // eslint-disable-next-line spaced-comment
142
+ /*#__PURE__*/ conditional();
143
+ }
26
144
  //# sourceMappingURL=assert.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"assert.js","sourceRoot":"","sources":["../src/assert.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAEH;;;;;;;;;;;GAWG;AACH,SAAgB,MAAM,CAAC,SAAkB,EAAE,OAAwB;IAClE,IAAI,CAAC,SAAS,EAAE,CAAC;QAChB,MAAM,IAAI,KAAK,CACd,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CACpF,CAAC;IACH,CAAC;AACF,CAAC;AAND,wBAMC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\n/**\n * A browser friendly assert library.\n * Use this instead of the 'assert' package, which has a big impact on bundle sizes.\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 * @legacy\n * @alpha\n */\nexport function assert(condition: boolean, message: string | number): asserts condition {\n\tif (!condition) {\n\t\tthrow new Error(\n\t\t\ttypeof message === \"number\" ? `0x${message.toString(16).padStart(3, \"0\")}` : message,\n\t\t);\n\t}\n}\n"]}
1
+ {"version":3,"file":"assert.js","sourceRoot":"","sources":["../src/assert.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,SAAgB,MAAM,CAAC,SAAkB,EAAE,OAAwB;IAClE,IAAI,CAAC,SAAS,EAAE,CAAC;QAChB,MAAM,IAAI,KAAK,CACd,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CACpF,CAAC;IACH,CAAC;AACF,CAAC;AAND,wBAMC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,SAAgB,WAAW,CAAC,SAA8C;IACzE,uJAAuJ;IACvJ,yJAAyJ;IACzJ,yIAAyI;IACzI,gBAAgB,CAAC,GAAG,EAAE;QACrB,IAAI,mBAAmB,EAAE,CAAC;YACzB,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;YAC3B,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;gBACrB,QAAQ,CAAC;gBACT,MAAM,IAAI,KAAK,CAAC,wBAAwB,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;YAC9D,CAAC;QACF,CAAC;IACF,CAAC,CAAC,CAAC;AACJ,CAAC;AAbD,kCAaC;AAED,IAAI,mBAAmB,GAAG,KAAK,CAAC;AAEhC;;;;;;GAMG;AACH,SAAgB,qBAAqB,CAAC,OAAgB;IACrD,MAAM,CACL,iCAAiC,EAAE,EACnC,KAAK,CAAC,4EAA4E,CAClF,CAAC;IACF,MAAM,GAAG,GAAG,mBAAmB,CAAC;IAChC,mBAAmB,GAAG,OAAO,CAAC;IAC9B,OAAO,GAAG,CAAC;AACZ,CAAC;AARD,sDAQC;AAED;;;;;;;GAOG;AACH,SAAgB,iCAAiC;IAChD,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,gBAAgB,CAAC,GAAG,EAAE;QACrB,QAAQ,GAAG,IAAI,CAAC;IACjB,CAAC,CAAC,CAAC;IACH,OAAO,QAAQ,CAAC;AACjB,CAAC;AAND,8EAMC;AAED;;;;;;;;;;;;;;GAcG;AACH,iMAAiM;AACjM,0CAA0C;AAC1C,wBAAwB;AACxB,SAAS,gBAAgB,CAAC,WAAuB;IAChD,0FAA0F;IAC1F,6IAA6I;IAC7I,iIAAiI;IAEjI,sKAAsK;IACtK,0CAA0C;IAC1C,aAAa,CAAC,WAAW,EAAE,CAAC;AAC7B,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\n/**\n * Asserts the specified condition.\n *\n * @param condition - The condition that should be true, if the condition is false an error will be thrown.\n * Only use this API when `false` indicates a logic error in the problem and thus a bug that should be fixed.\n * @param message - The message to include in the error when the condition does not hold.\n * A number should not be specified manually: use a string.\n * Before a release, policy-check should be run, which will convert any asserts still using strings to\n * use numbered error codes instead.\n * @remarks\n * Use this instead of the node 'assert' package, which requires polyfills and has a big impact on bundle sizes.\n *\n * Assertions using this API will be included in all configurations: there is no option to disable or optimize them out.\n * Thus this API is suitable for detecting conditions that should terminate the application and produce a useful diagnostic message.\n * It can be used to ensure bad states are detected early and to avoid data corruption or harder to debug errors.\n *\n * In cases where the assert is very unlikely to have an impact on production code but is still useful as documentation and for debugging, consider using `debugAssert` instead\n * to optimize bundle size.\n *\n * This API is not intended for use outside of the Fluid Framework client codebase: it will most likely be made internal in the future.\n * @privateRemarks\n * This should be deprecated (as a non internal API) then moved to purely internal.\n * When done the `debugAssert` reference above should be turned into a link.\n * @legacy\n * @alpha\n */\nexport function assert(condition: boolean, message: string | number): asserts condition {\n\tif (!condition) {\n\t\tthrow new Error(\n\t\t\ttypeof message === \"number\" ? `0x${message.toString(16).padStart(3, \"0\")}` : message,\n\t\t);\n\t}\n}\n\n/**\n * Asserts that can be conditionally enabled in debug/development builds but will be optimized out of production builds.\n *\n * Disabled by default.\n *\n * If the assert must be enforced/checked in production or enabled by default, use {@link assert} instead.\n *\n * @param predicate - A pure function that should return true if the condition holds, or a string or object describing the condition that failed.\n * This function will only be run in some configurations so it should be pure, and only used to detect bugs (when debugAssert are enabled), and must not be relied on to enforce the condition is true: for that use {@link assert}.\n * @remarks\n * Optimizing the asserts out of the bundle requires a bundler like webpack which leverages `__PURE__` annotations like https://webpack.js.org/guides/tree-shaking/#mark-a-function-call-as-side-effect-free.\n *\n * Exceptions thrown by this function must never be caught in production code, as that will result in different behavior when testing and when running optimized builds.\n * The `predicate` function must be pure (have no side-effects) to ensure that the behavior of code is the same regardless of if the asserts are disabled, enabled or optimized out.\n *\n * These asserts are disabled by default, even in debug builds to ensure that by default code will be tested as production runs, with them disabled.\n * Additionally, this ensures that apps that use a bundler which does not remove `__PURE__` will not incur the runtime cost of calling the predicate.\n * These asserts can be can be enabled by calling `configureDebugAsserts(true)`: see {@link configureDebugAsserts}.\n *\n * @privateRemarks\n * This design was chosen to accomplish two main goals:\n *\n * 1. Make it easy to compile debug asserts fully out of production builds.\n * For webpack this happens by default, avoiding the need for customers to do special configuration.\n * This is important for both performance and bundle size.\n *\n * 2. Make it easy to test (both manually and automated) with and without the predicates running.\n * This ensures it is possible to benefit from the asserts when enabled, but also test with them disabled to ensure this disablement doesn't cause bugs.\n *\n * The default behavior of having debugAsserts disabled helps ensure that tests which don't know about debug asserts will still run in a way that is most similar to production.\n * @internal\n */\nexport function debugAssert(predicate: () => true | { toString(): string }): void {\n\t// This is valid since the contract for this function is that \"predicate\" should be side effect free and never return non true in production scenarios:\n\t// it returning non-true indicates a bug is present, and that the validation it does to detect the bug is only desired in specific test/debug situations.\n\t// Production scenarios, where pure code is removed, should never hit a failing predicate, and thus this code should be side effect free.\n\tskipInProduction(() => {\n\t\tif (debugAssertsEnabled) {\n\t\t\tconst result = predicate();\n\t\t\tif (result !== true) {\n\t\t\t\tdebugger;\n\t\t\t\tthrow new Error(`Debug assert failed: ${result.toString()}`);\n\t\t\t}\n\t\t}\n\t});\n}\n\nlet debugAssertsEnabled = false;\n\n/**\n * Enables {@link debugAssert} validation.\n * @remarks\n * Throws if debugAsserts have been optimized out.\n * @returns The previous state of debugAsserts.\n * @internal\n */\nexport function configureDebugAsserts(enabled: boolean): boolean {\n\tassert(\n\t\tnonProductionConditionalsIncluded(),\n\t\t0xab1 /* Debug asserts cannot be configured since they have been optimized out. */,\n\t);\n\tconst old = debugAssertsEnabled;\n\tdebugAssertsEnabled = enabled;\n\treturn old;\n}\n\n/**\n * Checks if non-production conditional code like {@link debugAssert} is included in this build.\n * @remarks\n * Such code can be optimized out by bundlers: this checks if that has occurred.\n * @privateRemarks\n * See {@link skipInProduction}.\n * @internal\n */\nexport function nonProductionConditionalsIncluded(): boolean {\n\tlet included = false;\n\tskipInProduction(() => {\n\t\tincluded = true;\n\t});\n\treturn included;\n}\n\n/**\n * Run `conditional` only in debug/development (non optimized/minified) builds, but optimize it out of production builds.\n *\n * @param conditional - This function will only be run in some configurations so it should be pure (at least in production scenarios).\n * It can be used to interact with debug only functionality that is also removed in production builds, or to do validation/testing/debugging that can be assumed to be sideeffect free in production where it might be removed.\n * @remarks\n * Great care must be taken when using this to ensure that bugs are not introduced which only occur when `conditional` is not run.\n * One way to do this is to provide an alternative way to disable the effects of `conditional` in development builds so both configurations can be tested:\n * {@link debugAssert} uses this pattern.\n *\n * @privateRemarks\n * Since this function has no built in option for toggling it in development for testing, it is not exported and is only used as a building block for other testable options.\n * There are some additional details about syntax and bundler support in https://github.com/javascript-compiler-hints/compiler-notations-spec/tree/main .\n * This code uses both NO_SIDE_EFFECTS and PURE to maximize compatibility: for any bundler supporting both they are redundant.\n */\n// Using the exact syntax from https://github.com/javascript-compiler-hints/compiler-notations-spec/blob/main/no-side-effects-notation-spec.md to maximize compatibility with tree-shaking tools.\n// eslint-disable-next-line spaced-comment\n/*#__NO_SIDE_EFFECTS__*/\nfunction skipInProduction(conditional: () => void): void {\n\t// Here __PURE__ annotation is used to indicate that is is safe to optimize out this call.\n\t// This is valid since the contract for this function is that \"conditional\" should be side effect free if it were run in production scenarios\n\t// See https://webpack.js.org/guides/tree-shaking/#mark-a-function-call-as-side-effect-free for documentation on this annotation.\n\n\t// Using the exact syntax from https://webpack.js.org/guides/tree-shaking/#mark-a-function-call-as-side-effect-free to maximize compatibility with tree-shaking tools.\n\t// eslint-disable-next-line spaced-comment\n\t/*#__PURE__*/ conditional();\n}\n"]}
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 } from "./assert.js";
5
+ export { assert, debugAssert, configureDebugAsserts, nonProductionConditionalsIncluded, } 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";
@@ -11,6 +11,7 @@ export { Lazy, LazyPromise } from "./lazy.js";
11
11
  export type { PromiseCacheExpiry, PromiseCacheOptions } from "./promiseCache.js";
12
12
  export { PromiseCache } from "./promiseCache.js";
13
13
  export { Deferred } from "./promises.js";
14
+ export { shallowCloneObject } from "./shallowClone.js";
14
15
  export type { IPromiseTimer, IPromiseTimerResult, ITimer } from "./timer.js";
15
16
  export { PromiseTimer, setLongTimeout, Timer } from "./timer.js";
16
17
  export { unreachableCase } from "./unreachable.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,YAAY,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtD,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AACjD,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAC9C,YAAY,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACjF,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,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,WAAW,EACX,qBAAqB,EACrB,iCAAiC,GACjC,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,YAAY,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtD,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AACjD,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAC9C,YAAY,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACjF,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,YAAY,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAC7E,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACjE,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAC3D,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC"}
package/dist/index.js CHANGED
@@ -4,9 +4,12 @@
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.Deferred = exports.PromiseCache = exports.LazyPromise = exports.Lazy = exports.NumberComparer = exports.Heap = exports.delay = exports.compareArrays = exports.assert = void 0;
7
+ exports.oob = exports.isPromiseLike = exports.isObject = exports.unreachableCase = exports.Timer = exports.setLongTimeout = exports.PromiseTimer = exports.shallowCloneObject = exports.Deferred = exports.PromiseCache = exports.LazyPromise = exports.Lazy = exports.NumberComparer = exports.Heap = exports.delay = exports.compareArrays = exports.nonProductionConditionalsIncluded = exports.configureDebugAsserts = exports.debugAssert = 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
+ Object.defineProperty(exports, "debugAssert", { enumerable: true, get: function () { return assert_js_1.debugAssert; } });
11
+ Object.defineProperty(exports, "configureDebugAsserts", { enumerable: true, get: function () { return assert_js_1.configureDebugAsserts; } });
12
+ Object.defineProperty(exports, "nonProductionConditionalsIncluded", { enumerable: true, get: function () { return assert_js_1.nonProductionConditionalsIncluded; } });
10
13
  var compare_js_1 = require("./compare.js");
11
14
  Object.defineProperty(exports, "compareArrays", { enumerable: true, get: function () { return compare_js_1.compareArrays; } });
12
15
  var delay_js_1 = require("./delay.js");
@@ -21,6 +24,8 @@ var promiseCache_js_1 = require("./promiseCache.js");
21
24
  Object.defineProperty(exports, "PromiseCache", { enumerable: true, get: function () { return promiseCache_js_1.PromiseCache; } });
22
25
  var promises_js_1 = require("./promises.js");
23
26
  Object.defineProperty(exports, "Deferred", { enumerable: true, get: function () { return promises_js_1.Deferred; } });
27
+ var shallowClone_js_1 = require("./shallowClone.js");
28
+ Object.defineProperty(exports, "shallowCloneObject", { enumerable: true, get: function () { return shallowClone_js_1.shallowCloneObject; } });
24
29
  var timer_js_1 = require("./timer.js");
25
30
  Object.defineProperty(exports, "PromiseTimer", { enumerable: true, get: function () { return timer_js_1.PromiseTimer; } });
26
31
  Object.defineProperty(exports, "setLongTimeout", { enumerable: true, get: function () { return timer_js_1.setLongTimeout; } });
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAEH,yCAAqC;AAA5B,mGAAA,MAAM,OAAA;AACf,2CAA6C;AAApC,2GAAA,aAAa,OAAA;AACtB,uCAAmC;AAA1B,iGAAA,KAAK,OAAA;AAEd,qCAAiD;AAAxC,+FAAA,IAAI,OAAA;AAAE,yGAAA,cAAc,OAAA;AAC7B,qCAA8C;AAArC,+FAAA,IAAI,OAAA;AAAE,sGAAA,WAAW,OAAA;AAE1B,qDAAiD;AAAxC,+GAAA,YAAY,OAAA;AACrB,6CAAyC;AAAhC,uGAAA,QAAQ,OAAA;AAEjB,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 { assert } from \"./assert.js\";\nexport { compareArrays } from \"./compare.js\";\nexport { delay } from \"./delay.js\";\nexport type { IComparer, IHeapNode } from \"./heap.js\";\nexport { Heap, NumberComparer } from \"./heap.js\";\nexport { Lazy, LazyPromise } from \"./lazy.js\";\nexport type { PromiseCacheExpiry, PromiseCacheOptions } from \"./promiseCache.js\";\nexport { PromiseCache } from \"./promiseCache.js\";\nexport { Deferred } from \"./promises.js\";\nexport 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,yCAKqB;AAJpB,mGAAA,MAAM,OAAA;AACN,wGAAA,WAAW,OAAA;AACX,kHAAA,qBAAqB,OAAA;AACrB,8HAAA,iCAAiC,OAAA;AAElC,2CAA6C;AAApC,2GAAA,aAAa,OAAA;AACtB,uCAAmC;AAA1B,iGAAA,KAAK,OAAA;AAEd,qCAAiD;AAAxC,+FAAA,IAAI,OAAA;AAAE,yGAAA,cAAc,OAAA;AAC7B,qCAA8C;AAArC,+FAAA,IAAI,OAAA;AAAE,sGAAA,WAAW,OAAA;AAE1B,qDAAiD;AAAxC,+GAAA,YAAY,OAAA;AACrB,6CAAyC;AAAhC,uGAAA,QAAQ,OAAA;AACjB,qDAAuD;AAA9C,qHAAA,kBAAkB,OAAA;AAE3B,uCAAiE;AAAxD,wGAAA,YAAY,OAAA;AAAE,0GAAA,cAAc,OAAA;AAAE,iGAAA,KAAK,OAAA;AAC5C,mDAAmD;AAA1C,iHAAA,eAAe,OAAA;AACxB,mDAA2D;AAAlD,0GAAA,QAAQ,OAAA;AAAE,+GAAA,aAAa,OAAA;AAChC,mCAA+B;AAAtB,6FAAA,GAAG,OAAA","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nexport {\n\tassert,\n\tdebugAssert,\n\tconfigureDebugAsserts,\n\tnonProductionConditionalsIncluded,\n} from \"./assert.js\";\nexport { compareArrays } from \"./compare.js\";\nexport { delay } from \"./delay.js\";\nexport type { IComparer, IHeapNode } from \"./heap.js\";\nexport { Heap, NumberComparer } from \"./heap.js\";\nexport { Lazy, LazyPromise } from \"./lazy.js\";\nexport type { PromiseCacheExpiry, PromiseCacheOptions } from \"./promiseCache.js\";\nexport { PromiseCache } from \"./promiseCache.js\";\nexport { Deferred } from \"./promises.js\";\nexport { shallowCloneObject } from \"./shallowClone.js\";\nexport type { IPromiseTimer, IPromiseTimerResult, ITimer } from \"./timer.js\";\nexport { PromiseTimer, setLongTimeout, Timer } from \"./timer.js\";\nexport { unreachableCase } from \"./unreachable.js\";\nexport { isObject, isPromiseLike } from \"./typesGuards.js\";\nexport { oob } from \"./oob.js\";\n"]}
@@ -0,0 +1,13 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+ /**
6
+ * Shallow clone an object.
7
+ *
8
+ * @param value - The object to clone
9
+ * @returns A shallow clone of the input value
10
+ * @internal
11
+ */
12
+ export declare function shallowCloneObject<T extends object>(value: T): T;
13
+ //# sourceMappingURL=shallowClone.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shallowClone.d.ts","sourceRoot":"","sources":["../src/shallowClone.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,SAAS,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,CAKhE"}
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ /*!
3
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
4
+ * Licensed under the MIT License.
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.shallowCloneObject = void 0;
8
+ /**
9
+ * Shallow clone an object.
10
+ *
11
+ * @param value - The object to clone
12
+ * @returns A shallow clone of the input value
13
+ * @internal
14
+ */
15
+ function shallowCloneObject(value) {
16
+ if (Array.isArray(value)) {
17
+ return [...value];
18
+ }
19
+ return { ...value };
20
+ }
21
+ exports.shallowCloneObject = shallowCloneObject;
22
+ //# sourceMappingURL=shallowClone.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shallowClone.js","sourceRoot":"","sources":["../src/shallowClone.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAEH;;;;;;GAMG;AACH,SAAgB,kBAAkB,CAAmB,KAAQ;IAC5D,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO,CAAC,GAAG,KAAK,CAAM,CAAC;IACxB,CAAC;IACD,OAAO,EAAE,GAAG,KAAK,EAAE,CAAC;AACrB,CAAC;AALD,gDAKC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\n/**\n * Shallow clone an object.\n *\n * @param value - The object to clone\n * @returns A shallow clone of the input value\n * @internal\n */\nexport function shallowCloneObject<T extends object>(value: T): T {\n\tif (Array.isArray(value)) {\n\t\treturn [...value] as T;\n\t}\n\treturn { ...value };\n}\n"]}
package/lib/assert.d.ts CHANGED
@@ -3,16 +3,82 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
  /**
6
- * A browser friendly assert library.
7
- * Use this instead of the 'assert' package, which has a big impact on bundle sizes.
6
+ * Asserts the specified condition.
7
+ *
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
11
  * A number should not be specified manually: use a string.
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
+ * @remarks
15
+ * Use this instead of the node 'assert' package, which requires polyfills and has a big impact on bundle sizes.
16
+ *
17
+ * Assertions using this API will be included in all configurations: there is no option to disable or optimize them out.
18
+ * Thus this API is suitable for detecting conditions that should terminate the application and produce a useful diagnostic message.
19
+ * It can be used to ensure bad states are detected early and to avoid data corruption or harder to debug errors.
20
+ *
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 `debugAssert` instead
22
+ * to optimize bundle size.
23
+ *
24
+ * 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
+ * @privateRemarks
26
+ * This should be deprecated (as a non internal API) then moved to purely internal.
27
+ * When done the `debugAssert` reference above should be turned into a link.
14
28
  * @legacy
15
29
  * @alpha
16
30
  */
17
31
  export declare function assert(condition: boolean, message: string | number): asserts condition;
32
+ /**
33
+ * Asserts that can be conditionally enabled in debug/development builds but will be optimized out of production builds.
34
+ *
35
+ * Disabled by default.
36
+ *
37
+ * If the assert must be enforced/checked in production or enabled by default, use {@link assert} instead.
38
+ *
39
+ * @param predicate - A pure function that should return true if the condition holds, or a string or object describing the condition that failed.
40
+ * 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}.
41
+ * @remarks
42
+ * 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.
43
+ *
44
+ * 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.
45
+ * 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.
46
+ *
47
+ * 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.
48
+ * Additionally, this ensures that apps that use a bundler which does not remove `__PURE__` will not incur the runtime cost of calling the predicate.
49
+ * These asserts can be can be enabled by calling `configureDebugAsserts(true)`: see {@link configureDebugAsserts}.
50
+ *
51
+ * @privateRemarks
52
+ * This design was chosen to accomplish two main goals:
53
+ *
54
+ * 1. Make it easy to compile debug asserts fully out of production builds.
55
+ * For webpack this happens by default, avoiding the need for customers to do special configuration.
56
+ * This is important for both performance and bundle size.
57
+ *
58
+ * 2. Make it easy to test (both manually and automated) with and without the predicates running.
59
+ * 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.
60
+ *
61
+ * 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.
62
+ * @internal
63
+ */
64
+ export declare function debugAssert(predicate: () => true | {
65
+ toString(): string;
66
+ }): void;
67
+ /**
68
+ * Enables {@link debugAssert} validation.
69
+ * @remarks
70
+ * Throws if debugAsserts have been optimized out.
71
+ * @returns The previous state of debugAsserts.
72
+ * @internal
73
+ */
74
+ export declare function configureDebugAsserts(enabled: boolean): boolean;
75
+ /**
76
+ * Checks if non-production conditional code like {@link debugAssert} is included in this build.
77
+ * @remarks
78
+ * Such code can be optimized out by bundlers: this checks if that has occurred.
79
+ * @privateRemarks
80
+ * See {@link skipInProduction}.
81
+ * @internal
82
+ */
83
+ export declare function nonProductionConditionalsIncluded(): boolean;
18
84
  //# sourceMappingURL=assert.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"assert.d.ts","sourceRoot":"","sources":["../src/assert.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;;;;;;GAWG;AACH,wBAAgB,MAAM,CAAC,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,SAAS,CAMtF"}
1
+ {"version":3,"file":"assert.d.ts","sourceRoot":"","sources":["../src/assert.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,MAAM,CAAC,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,SAAS,CAMtF;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,IAAI,GAAG;IAAE,QAAQ,IAAI,MAAM,CAAA;CAAE,GAAG,IAAI,CAahF;AAID;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAQ/D;AAED;;;;;;;GAOG;AACH,wBAAgB,iCAAiC,IAAI,OAAO,CAM3D"}
package/lib/assert.js CHANGED
@@ -3,14 +3,28 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
  /**
6
- * A browser friendly assert library.
7
- * Use this instead of the 'assert' package, which has a big impact on bundle sizes.
6
+ * Asserts the specified condition.
7
+ *
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
11
  * A number should not be specified manually: use a string.
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
+ * @remarks
15
+ * Use this instead of the node 'assert' package, which requires polyfills and has a big impact on bundle sizes.
16
+ *
17
+ * Assertions using this API will be included in all configurations: there is no option to disable or optimize them out.
18
+ * Thus this API is suitable for detecting conditions that should terminate the application and produce a useful diagnostic message.
19
+ * It can be used to ensure bad states are detected early and to avoid data corruption or harder to debug errors.
20
+ *
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 `debugAssert` instead
22
+ * to optimize bundle size.
23
+ *
24
+ * 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
+ * @privateRemarks
26
+ * This should be deprecated (as a non internal API) then moved to purely internal.
27
+ * When done the `debugAssert` reference above should be turned into a link.
14
28
  * @legacy
15
29
  * @alpha
16
30
  */
@@ -19,4 +33,105 @@ export function assert(condition, message) {
19
33
  throw new Error(typeof message === "number" ? `0x${message.toString(16).padStart(3, "0")}` : message);
20
34
  }
21
35
  }
36
+ /**
37
+ * Asserts that can be conditionally enabled in debug/development builds but will be optimized out of production builds.
38
+ *
39
+ * Disabled by default.
40
+ *
41
+ * If the assert must be enforced/checked in production or enabled by default, use {@link assert} instead.
42
+ *
43
+ * @param predicate - A pure function that should return true if the condition holds, or a string or object describing the condition that failed.
44
+ * 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}.
45
+ * @remarks
46
+ * 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.
47
+ *
48
+ * 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.
49
+ * 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.
50
+ *
51
+ * 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.
52
+ * Additionally, this ensures that apps that use a bundler which does not remove `__PURE__` will not incur the runtime cost of calling the predicate.
53
+ * These asserts can be can be enabled by calling `configureDebugAsserts(true)`: see {@link configureDebugAsserts}.
54
+ *
55
+ * @privateRemarks
56
+ * This design was chosen to accomplish two main goals:
57
+ *
58
+ * 1. Make it easy to compile debug asserts fully out of production builds.
59
+ * For webpack this happens by default, avoiding the need for customers to do special configuration.
60
+ * This is important for both performance and bundle size.
61
+ *
62
+ * 2. Make it easy to test (both manually and automated) with and without the predicates running.
63
+ * 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.
64
+ *
65
+ * 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.
66
+ * @internal
67
+ */
68
+ export function debugAssert(predicate) {
69
+ // 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:
70
+ // 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.
71
+ // Production scenarios, where pure code is removed, should never hit a failing predicate, and thus this code should be side effect free.
72
+ skipInProduction(() => {
73
+ if (debugAssertsEnabled) {
74
+ const result = predicate();
75
+ if (result !== true) {
76
+ debugger;
77
+ throw new Error(`Debug assert failed: ${result.toString()}`);
78
+ }
79
+ }
80
+ });
81
+ }
82
+ let debugAssertsEnabled = false;
83
+ /**
84
+ * Enables {@link debugAssert} validation.
85
+ * @remarks
86
+ * Throws if debugAsserts have been optimized out.
87
+ * @returns The previous state of debugAsserts.
88
+ * @internal
89
+ */
90
+ export function configureDebugAsserts(enabled) {
91
+ assert(nonProductionConditionalsIncluded(), 0xab1 /* Debug asserts cannot be configured since they have been optimized out. */);
92
+ const old = debugAssertsEnabled;
93
+ debugAssertsEnabled = enabled;
94
+ return old;
95
+ }
96
+ /**
97
+ * Checks if non-production conditional code like {@link debugAssert} is included in this build.
98
+ * @remarks
99
+ * Such code can be optimized out by bundlers: this checks if that has occurred.
100
+ * @privateRemarks
101
+ * See {@link skipInProduction}.
102
+ * @internal
103
+ */
104
+ export function nonProductionConditionalsIncluded() {
105
+ let included = false;
106
+ skipInProduction(() => {
107
+ included = true;
108
+ });
109
+ return included;
110
+ }
111
+ /**
112
+ * Run `conditional` only in debug/development (non optimized/minified) builds, but optimize it out of production builds.
113
+ *
114
+ * @param conditional - This function will only be run in some configurations so it should be pure (at least in production scenarios).
115
+ * 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.
116
+ * @remarks
117
+ * Great care must be taken when using this to ensure that bugs are not introduced which only occur when `conditional` is not run.
118
+ * 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:
119
+ * {@link debugAssert} uses this pattern.
120
+ *
121
+ * @privateRemarks
122
+ * 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.
123
+ * There are some additional details about syntax and bundler support in https://github.com/javascript-compiler-hints/compiler-notations-spec/tree/main .
124
+ * This code uses both NO_SIDE_EFFECTS and PURE to maximize compatibility: for any bundler supporting both they are redundant.
125
+ */
126
+ // 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.
127
+ // eslint-disable-next-line spaced-comment
128
+ /*#__NO_SIDE_EFFECTS__*/
129
+ function skipInProduction(conditional) {
130
+ // Here __PURE__ annotation is used to indicate that is is safe to optimize out this call.
131
+ // This is valid since the contract for this function is that "conditional" should be side effect free if it were run in production scenarios
132
+ // See https://webpack.js.org/guides/tree-shaking/#mark-a-function-call-as-side-effect-free for documentation on this annotation.
133
+ // 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.
134
+ // eslint-disable-next-line spaced-comment
135
+ /*#__PURE__*/ conditional();
136
+ }
22
137
  //# sourceMappingURL=assert.js.map
package/lib/assert.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"assert.js","sourceRoot":"","sources":["../src/assert.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,MAAM,CAAC,SAAkB,EAAE,OAAwB;IAClE,IAAI,CAAC,SAAS,EAAE,CAAC;QAChB,MAAM,IAAI,KAAK,CACd,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CACpF,CAAC;IACH,CAAC;AACF,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\n/**\n * A browser friendly assert library.\n * Use this instead of the 'assert' package, which has a big impact on bundle sizes.\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 * @legacy\n * @alpha\n */\nexport function assert(condition: boolean, message: string | number): asserts condition {\n\tif (!condition) {\n\t\tthrow new Error(\n\t\t\ttypeof message === \"number\" ? `0x${message.toString(16).padStart(3, \"0\")}` : message,\n\t\t);\n\t}\n}\n"]}
1
+ {"version":3,"file":"assert.js","sourceRoot":"","sources":["../src/assert.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,UAAU,MAAM,CAAC,SAAkB,EAAE,OAAwB;IAClE,IAAI,CAAC,SAAS,EAAE,CAAC;QAChB,MAAM,IAAI,KAAK,CACd,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CACpF,CAAC;IACH,CAAC;AACF,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,MAAM,UAAU,WAAW,CAAC,SAA8C;IACzE,uJAAuJ;IACvJ,yJAAyJ;IACzJ,yIAAyI;IACzI,gBAAgB,CAAC,GAAG,EAAE;QACrB,IAAI,mBAAmB,EAAE,CAAC;YACzB,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;YAC3B,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;gBACrB,QAAQ,CAAC;gBACT,MAAM,IAAI,KAAK,CAAC,wBAAwB,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;YAC9D,CAAC;QACF,CAAC;IACF,CAAC,CAAC,CAAC;AACJ,CAAC;AAED,IAAI,mBAAmB,GAAG,KAAK,CAAC;AAEhC;;;;;;GAMG;AACH,MAAM,UAAU,qBAAqB,CAAC,OAAgB;IACrD,MAAM,CACL,iCAAiC,EAAE,EACnC,KAAK,CAAC,4EAA4E,CAClF,CAAC;IACF,MAAM,GAAG,GAAG,mBAAmB,CAAC;IAChC,mBAAmB,GAAG,OAAO,CAAC;IAC9B,OAAO,GAAG,CAAC;AACZ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,iCAAiC;IAChD,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,gBAAgB,CAAC,GAAG,EAAE;QACrB,QAAQ,GAAG,IAAI,CAAC;IACjB,CAAC,CAAC,CAAC;IACH,OAAO,QAAQ,CAAC;AACjB,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,iMAAiM;AACjM,0CAA0C;AAC1C,wBAAwB;AACxB,SAAS,gBAAgB,CAAC,WAAuB;IAChD,0FAA0F;IAC1F,6IAA6I;IAC7I,iIAAiI;IAEjI,sKAAsK;IACtK,0CAA0C;IAC1C,aAAa,CAAC,WAAW,EAAE,CAAC;AAC7B,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\n/**\n * Asserts the specified condition.\n *\n * @param condition - The condition that should be true, if the condition is false an error will be thrown.\n * Only use this API when `false` indicates a logic error in the problem and thus a bug that should be fixed.\n * @param message - The message to include in the error when the condition does not hold.\n * A number should not be specified manually: use a string.\n * Before a release, policy-check should be run, which will convert any asserts still using strings to\n * use numbered error codes instead.\n * @remarks\n * Use this instead of the node 'assert' package, which requires polyfills and has a big impact on bundle sizes.\n *\n * Assertions using this API will be included in all configurations: there is no option to disable or optimize them out.\n * Thus this API is suitable for detecting conditions that should terminate the application and produce a useful diagnostic message.\n * It can be used to ensure bad states are detected early and to avoid data corruption or harder to debug errors.\n *\n * In cases where the assert is very unlikely to have an impact on production code but is still useful as documentation and for debugging, consider using `debugAssert` instead\n * to optimize bundle size.\n *\n * This API is not intended for use outside of the Fluid Framework client codebase: it will most likely be made internal in the future.\n * @privateRemarks\n * This should be deprecated (as a non internal API) then moved to purely internal.\n * When done the `debugAssert` reference above should be turned into a link.\n * @legacy\n * @alpha\n */\nexport function assert(condition: boolean, message: string | number): asserts condition {\n\tif (!condition) {\n\t\tthrow new Error(\n\t\t\ttypeof message === \"number\" ? `0x${message.toString(16).padStart(3, \"0\")}` : message,\n\t\t);\n\t}\n}\n\n/**\n * Asserts that can be conditionally enabled in debug/development builds but will be optimized out of production builds.\n *\n * Disabled by default.\n *\n * If the assert must be enforced/checked in production or enabled by default, use {@link assert} instead.\n *\n * @param predicate - A pure function that should return true if the condition holds, or a string or object describing the condition that failed.\n * This function will only be run in some configurations so it should be pure, and only used to detect bugs (when debugAssert are enabled), and must not be relied on to enforce the condition is true: for that use {@link assert}.\n * @remarks\n * Optimizing the asserts out of the bundle requires a bundler like webpack which leverages `__PURE__` annotations like https://webpack.js.org/guides/tree-shaking/#mark-a-function-call-as-side-effect-free.\n *\n * Exceptions thrown by this function must never be caught in production code, as that will result in different behavior when testing and when running optimized builds.\n * The `predicate` function must be pure (have no side-effects) to ensure that the behavior of code is the same regardless of if the asserts are disabled, enabled or optimized out.\n *\n * These asserts are disabled by default, even in debug builds to ensure that by default code will be tested as production runs, with them disabled.\n * Additionally, this ensures that apps that use a bundler which does not remove `__PURE__` will not incur the runtime cost of calling the predicate.\n * These asserts can be can be enabled by calling `configureDebugAsserts(true)`: see {@link configureDebugAsserts}.\n *\n * @privateRemarks\n * This design was chosen to accomplish two main goals:\n *\n * 1. Make it easy to compile debug asserts fully out of production builds.\n * For webpack this happens by default, avoiding the need for customers to do special configuration.\n * This is important for both performance and bundle size.\n *\n * 2. Make it easy to test (both manually and automated) with and without the predicates running.\n * This ensures it is possible to benefit from the asserts when enabled, but also test with them disabled to ensure this disablement doesn't cause bugs.\n *\n * The default behavior of having debugAsserts disabled helps ensure that tests which don't know about debug asserts will still run in a way that is most similar to production.\n * @internal\n */\nexport function debugAssert(predicate: () => true | { toString(): string }): void {\n\t// This is valid since the contract for this function is that \"predicate\" should be side effect free and never return non true in production scenarios:\n\t// it returning non-true indicates a bug is present, and that the validation it does to detect the bug is only desired in specific test/debug situations.\n\t// Production scenarios, where pure code is removed, should never hit a failing predicate, and thus this code should be side effect free.\n\tskipInProduction(() => {\n\t\tif (debugAssertsEnabled) {\n\t\t\tconst result = predicate();\n\t\t\tif (result !== true) {\n\t\t\t\tdebugger;\n\t\t\t\tthrow new Error(`Debug assert failed: ${result.toString()}`);\n\t\t\t}\n\t\t}\n\t});\n}\n\nlet debugAssertsEnabled = false;\n\n/**\n * Enables {@link debugAssert} validation.\n * @remarks\n * Throws if debugAsserts have been optimized out.\n * @returns The previous state of debugAsserts.\n * @internal\n */\nexport function configureDebugAsserts(enabled: boolean): boolean {\n\tassert(\n\t\tnonProductionConditionalsIncluded(),\n\t\t0xab1 /* Debug asserts cannot be configured since they have been optimized out. */,\n\t);\n\tconst old = debugAssertsEnabled;\n\tdebugAssertsEnabled = enabled;\n\treturn old;\n}\n\n/**\n * Checks if non-production conditional code like {@link debugAssert} is included in this build.\n * @remarks\n * Such code can be optimized out by bundlers: this checks if that has occurred.\n * @privateRemarks\n * See {@link skipInProduction}.\n * @internal\n */\nexport function nonProductionConditionalsIncluded(): boolean {\n\tlet included = false;\n\tskipInProduction(() => {\n\t\tincluded = true;\n\t});\n\treturn included;\n}\n\n/**\n * Run `conditional` only in debug/development (non optimized/minified) builds, but optimize it out of production builds.\n *\n * @param conditional - This function will only be run in some configurations so it should be pure (at least in production scenarios).\n * It can be used to interact with debug only functionality that is also removed in production builds, or to do validation/testing/debugging that can be assumed to be sideeffect free in production where it might be removed.\n * @remarks\n * Great care must be taken when using this to ensure that bugs are not introduced which only occur when `conditional` is not run.\n * One way to do this is to provide an alternative way to disable the effects of `conditional` in development builds so both configurations can be tested:\n * {@link debugAssert} uses this pattern.\n *\n * @privateRemarks\n * Since this function has no built in option for toggling it in development for testing, it is not exported and is only used as a building block for other testable options.\n * There are some additional details about syntax and bundler support in https://github.com/javascript-compiler-hints/compiler-notations-spec/tree/main .\n * This code uses both NO_SIDE_EFFECTS and PURE to maximize compatibility: for any bundler supporting both they are redundant.\n */\n// Using the exact syntax from https://github.com/javascript-compiler-hints/compiler-notations-spec/blob/main/no-side-effects-notation-spec.md to maximize compatibility with tree-shaking tools.\n// eslint-disable-next-line spaced-comment\n/*#__NO_SIDE_EFFECTS__*/\nfunction skipInProduction(conditional: () => void): void {\n\t// Here __PURE__ annotation is used to indicate that is is safe to optimize out this call.\n\t// This is valid since the contract for this function is that \"conditional\" should be side effect free if it were run in production scenarios\n\t// See https://webpack.js.org/guides/tree-shaking/#mark-a-function-call-as-side-effect-free for documentation on this annotation.\n\n\t// Using the exact syntax from https://webpack.js.org/guides/tree-shaking/#mark-a-function-call-as-side-effect-free to maximize compatibility with tree-shaking tools.\n\t// eslint-disable-next-line spaced-comment\n\t/*#__PURE__*/ conditional();\n}\n"]}
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 } from "./assert.js";
5
+ export { assert, debugAssert, configureDebugAsserts, nonProductionConditionalsIncluded, } 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";
@@ -11,6 +11,7 @@ export { Lazy, LazyPromise } from "./lazy.js";
11
11
  export type { PromiseCacheExpiry, PromiseCacheOptions } from "./promiseCache.js";
12
12
  export { PromiseCache } from "./promiseCache.js";
13
13
  export { Deferred } from "./promises.js";
14
+ export { shallowCloneObject } from "./shallowClone.js";
14
15
  export type { IPromiseTimer, IPromiseTimerResult, ITimer } from "./timer.js";
15
16
  export { PromiseTimer, setLongTimeout, Timer } from "./timer.js";
16
17
  export { unreachableCase } from "./unreachable.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,YAAY,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtD,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AACjD,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAC9C,YAAY,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACjF,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,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,WAAW,EACX,qBAAqB,EACrB,iCAAiC,GACjC,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,YAAY,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtD,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AACjD,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAC9C,YAAY,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACjF,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,YAAY,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAC7E,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACjE,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAC3D,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC"}
package/lib/index.js CHANGED
@@ -2,13 +2,14 @@
2
2
  * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
3
  * Licensed under the MIT License.
4
4
  */
5
- export { assert } from "./assert.js";
5
+ export { assert, debugAssert, configureDebugAsserts, nonProductionConditionalsIncluded, } 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";
9
9
  export { Lazy, LazyPromise } from "./lazy.js";
10
10
  export { PromiseCache } from "./promiseCache.js";
11
11
  export { Deferred } from "./promises.js";
12
+ export { shallowCloneObject } from "./shallowClone.js";
12
13
  export { PromiseTimer, setLongTimeout, Timer } from "./timer.js";
13
14
  export { unreachableCase } from "./unreachable.js";
14
15
  export { isObject, isPromiseLike } from "./typesGuards.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,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAEnC,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AACjD,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAE9C,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAEzC,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 { assert } from \"./assert.js\";\nexport { compareArrays } from \"./compare.js\";\nexport { delay } from \"./delay.js\";\nexport type { IComparer, IHeapNode } from \"./heap.js\";\nexport { Heap, NumberComparer } from \"./heap.js\";\nexport { Lazy, LazyPromise } from \"./lazy.js\";\nexport type { PromiseCacheExpiry, PromiseCacheOptions } from \"./promiseCache.js\";\nexport { PromiseCache } from \"./promiseCache.js\";\nexport { Deferred } from \"./promises.js\";\nexport 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,WAAW,EACX,qBAAqB,EACrB,iCAAiC,GACjC,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAEnC,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AACjD,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAE9C,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAEvD,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACjE,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAC3D,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nexport {\n\tassert,\n\tdebugAssert,\n\tconfigureDebugAsserts,\n\tnonProductionConditionalsIncluded,\n} from \"./assert.js\";\nexport { compareArrays } from \"./compare.js\";\nexport { delay } from \"./delay.js\";\nexport type { IComparer, IHeapNode } from \"./heap.js\";\nexport { Heap, NumberComparer } from \"./heap.js\";\nexport { Lazy, LazyPromise } from \"./lazy.js\";\nexport type { PromiseCacheExpiry, PromiseCacheOptions } from \"./promiseCache.js\";\nexport { PromiseCache } from \"./promiseCache.js\";\nexport { Deferred } from \"./promises.js\";\nexport { shallowCloneObject } from \"./shallowClone.js\";\nexport type { IPromiseTimer, IPromiseTimerResult, ITimer } from \"./timer.js\";\nexport { PromiseTimer, setLongTimeout, Timer } from \"./timer.js\";\nexport { unreachableCase } from \"./unreachable.js\";\nexport { isObject, isPromiseLike } from \"./typesGuards.js\";\nexport { oob } from \"./oob.js\";\n"]}
@@ -0,0 +1,13 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+ /**
6
+ * Shallow clone an object.
7
+ *
8
+ * @param value - The object to clone
9
+ * @returns A shallow clone of the input value
10
+ * @internal
11
+ */
12
+ export declare function shallowCloneObject<T extends object>(value: T): T;
13
+ //# sourceMappingURL=shallowClone.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shallowClone.d.ts","sourceRoot":"","sources":["../src/shallowClone.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,SAAS,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,CAKhE"}
@@ -0,0 +1,18 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+ /**
6
+ * Shallow clone an object.
7
+ *
8
+ * @param value - The object to clone
9
+ * @returns A shallow clone of the input value
10
+ * @internal
11
+ */
12
+ export function shallowCloneObject(value) {
13
+ if (Array.isArray(value)) {
14
+ return [...value];
15
+ }
16
+ return { ...value };
17
+ }
18
+ //# sourceMappingURL=shallowClone.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shallowClone.js","sourceRoot":"","sources":["../src/shallowClone.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB,CAAmB,KAAQ;IAC5D,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO,CAAC,GAAG,KAAK,CAAM,CAAC;IACxB,CAAC;IACD,OAAO,EAAE,GAAG,KAAK,EAAE,CAAC;AACrB,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\n/**\n * Shallow clone an object.\n *\n * @param value - The object to clone\n * @returns A shallow clone of the input value\n * @internal\n */\nexport function shallowCloneObject<T extends object>(value: T): T {\n\tif (Array.isArray(value)) {\n\t\treturn [...value] as T;\n\t}\n\treturn { ...value };\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fluidframework/core-utils",
3
- "version": "2.20.0",
3
+ "version": "2.21.0",
4
4
  "description": "Not intended for use outside the Fluid client repo.",
5
5
  "homepage": "https://fluidframework.com",
6
6
  "repository": {
@@ -69,13 +69,13 @@
69
69
  "devDependencies": {
70
70
  "@arethetypeswrong/cli": "^0.17.1",
71
71
  "@biomejs/biome": "~1.9.3",
72
- "@fluid-internal/mocha-test-setup": "~2.20.0",
72
+ "@fluid-internal/mocha-test-setup": "~2.21.0",
73
73
  "@fluid-tools/benchmark": "^0.50.0",
74
74
  "@fluid-tools/build-cli": "^0.51.0",
75
75
  "@fluidframework/build-common": "^2.0.3",
76
76
  "@fluidframework/build-tools": "^0.51.0",
77
- "@fluidframework/core-utils-previous": "npm:@fluidframework/core-utils@2.13.0",
78
- "@fluidframework/eslint-config-fluid": "^5.6.0",
77
+ "@fluidframework/core-utils-previous": "npm:@fluidframework/core-utils@2.20.0",
78
+ "@fluidframework/eslint-config-fluid": "^5.7.3",
79
79
  "@microsoft/api-extractor": "7.47.8",
80
80
  "@types/mocha": "^10.0.10",
81
81
  "@types/node": "^18.19.0",
@@ -129,7 +129,7 @@
129
129
  "ci:build:api-reports:current": "api-extractor run --config api-extractor/api-extractor.current.json",
130
130
  "ci:build:api-reports:legacy": "api-extractor run --config api-extractor/api-extractor.legacy.json",
131
131
  "ci:build:docs": "api-extractor run",
132
- "clean": "rimraf --glob dist lib \"*.d.ts\" \"**/*.tsbuildinfo\" \"**/*.build.log\" _api-extractor-temp nyc",
132
+ "clean": "rimraf --glob dist lib {alpha,beta,internal,legacy}.d.ts \"**/*.tsbuildinfo\" \"**/*.build.log\" _api-extractor-temp nyc",
133
133
  "eslint": "eslint --format stylish src",
134
134
  "eslint:fix": "eslint --format stylish src --fix --fix-type problem,suggestion,layout",
135
135
  "format": "npm run format:biome",
package/src/assert.ts CHANGED
@@ -4,14 +4,28 @@
4
4
  */
5
5
 
6
6
  /**
7
- * A browser friendly assert library.
8
- * Use this instead of the 'assert' package, which has a big impact on bundle sizes.
7
+ * Asserts the specified condition.
8
+ *
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
12
  * A number should not be specified manually: use a string.
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
+ * @remarks
16
+ * Use this instead of the node 'assert' package, which requires polyfills and has a big impact on bundle sizes.
17
+ *
18
+ * Assertions using this API will be included in all configurations: there is no option to disable or optimize them out.
19
+ * Thus this API is suitable for detecting conditions that should terminate the application and produce a useful diagnostic message.
20
+ * It can be used to ensure bad states are detected early and to avoid data corruption or harder to debug errors.
21
+ *
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 `debugAssert` instead
23
+ * to optimize bundle size.
24
+ *
25
+ * 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
+ * @privateRemarks
27
+ * This should be deprecated (as a non internal API) then moved to purely internal.
28
+ * When done the `debugAssert` reference above should be turned into a link.
15
29
  * @legacy
16
30
  * @alpha
17
31
  */
@@ -22,3 +36,113 @@ export function assert(condition: boolean, message: string | number): asserts co
22
36
  );
23
37
  }
24
38
  }
39
+
40
+ /**
41
+ * Asserts that can be conditionally enabled in debug/development builds but will be optimized out of production builds.
42
+ *
43
+ * Disabled by default.
44
+ *
45
+ * If the assert must be enforced/checked in production or enabled by default, use {@link assert} instead.
46
+ *
47
+ * @param predicate - A pure function that should return true if the condition holds, or a string or object describing the condition that failed.
48
+ * 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}.
49
+ * @remarks
50
+ * 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.
51
+ *
52
+ * 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.
53
+ * 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.
54
+ *
55
+ * 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.
56
+ * Additionally, this ensures that apps that use a bundler which does not remove `__PURE__` will not incur the runtime cost of calling the predicate.
57
+ * These asserts can be can be enabled by calling `configureDebugAsserts(true)`: see {@link configureDebugAsserts}.
58
+ *
59
+ * @privateRemarks
60
+ * This design was chosen to accomplish two main goals:
61
+ *
62
+ * 1. Make it easy to compile debug asserts fully out of production builds.
63
+ * For webpack this happens by default, avoiding the need for customers to do special configuration.
64
+ * This is important for both performance and bundle size.
65
+ *
66
+ * 2. Make it easy to test (both manually and automated) with and without the predicates running.
67
+ * 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.
68
+ *
69
+ * 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.
70
+ * @internal
71
+ */
72
+ export function debugAssert(predicate: () => true | { toString(): string }): void {
73
+ // 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:
74
+ // 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.
75
+ // Production scenarios, where pure code is removed, should never hit a failing predicate, and thus this code should be side effect free.
76
+ skipInProduction(() => {
77
+ if (debugAssertsEnabled) {
78
+ const result = predicate();
79
+ if (result !== true) {
80
+ debugger;
81
+ throw new Error(`Debug assert failed: ${result.toString()}`);
82
+ }
83
+ }
84
+ });
85
+ }
86
+
87
+ let debugAssertsEnabled = false;
88
+
89
+ /**
90
+ * Enables {@link debugAssert} validation.
91
+ * @remarks
92
+ * Throws if debugAsserts have been optimized out.
93
+ * @returns The previous state of debugAsserts.
94
+ * @internal
95
+ */
96
+ export function configureDebugAsserts(enabled: boolean): boolean {
97
+ assert(
98
+ nonProductionConditionalsIncluded(),
99
+ 0xab1 /* Debug asserts cannot be configured since they have been optimized out. */,
100
+ );
101
+ const old = debugAssertsEnabled;
102
+ debugAssertsEnabled = enabled;
103
+ return old;
104
+ }
105
+
106
+ /**
107
+ * Checks if non-production conditional code like {@link debugAssert} is included in this build.
108
+ * @remarks
109
+ * Such code can be optimized out by bundlers: this checks if that has occurred.
110
+ * @privateRemarks
111
+ * See {@link skipInProduction}.
112
+ * @internal
113
+ */
114
+ export function nonProductionConditionalsIncluded(): boolean {
115
+ let included = false;
116
+ skipInProduction(() => {
117
+ included = true;
118
+ });
119
+ return included;
120
+ }
121
+
122
+ /**
123
+ * Run `conditional` only in debug/development (non optimized/minified) builds, but optimize it out of production builds.
124
+ *
125
+ * @param conditional - This function will only be run in some configurations so it should be pure (at least in production scenarios).
126
+ * 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.
127
+ * @remarks
128
+ * Great care must be taken when using this to ensure that bugs are not introduced which only occur when `conditional` is not run.
129
+ * 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:
130
+ * {@link debugAssert} uses this pattern.
131
+ *
132
+ * @privateRemarks
133
+ * 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.
134
+ * There are some additional details about syntax and bundler support in https://github.com/javascript-compiler-hints/compiler-notations-spec/tree/main .
135
+ * This code uses both NO_SIDE_EFFECTS and PURE to maximize compatibility: for any bundler supporting both they are redundant.
136
+ */
137
+ // 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.
138
+ // eslint-disable-next-line spaced-comment
139
+ /*#__NO_SIDE_EFFECTS__*/
140
+ function skipInProduction(conditional: () => void): void {
141
+ // Here __PURE__ annotation is used to indicate that is is safe to optimize out this call.
142
+ // This is valid since the contract for this function is that "conditional" should be side effect free if it were run in production scenarios
143
+ // See https://webpack.js.org/guides/tree-shaking/#mark-a-function-call-as-side-effect-free for documentation on this annotation.
144
+
145
+ // 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.
146
+ // eslint-disable-next-line spaced-comment
147
+ /*#__PURE__*/ conditional();
148
+ }
package/src/index.ts CHANGED
@@ -3,7 +3,12 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
 
6
- export { assert } from "./assert.js";
6
+ export {
7
+ assert,
8
+ debugAssert,
9
+ configureDebugAsserts,
10
+ nonProductionConditionalsIncluded,
11
+ } from "./assert.js";
7
12
  export { compareArrays } from "./compare.js";
8
13
  export { delay } from "./delay.js";
9
14
  export type { IComparer, IHeapNode } from "./heap.js";
@@ -12,6 +17,7 @@ export { Lazy, LazyPromise } from "./lazy.js";
12
17
  export type { PromiseCacheExpiry, PromiseCacheOptions } from "./promiseCache.js";
13
18
  export { PromiseCache } from "./promiseCache.js";
14
19
  export { Deferred } from "./promises.js";
20
+ export { shallowCloneObject } from "./shallowClone.js";
15
21
  export type { IPromiseTimer, IPromiseTimerResult, ITimer } from "./timer.js";
16
22
  export { PromiseTimer, setLongTimeout, Timer } from "./timer.js";
17
23
  export { unreachableCase } from "./unreachable.js";
@@ -0,0 +1,18 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+ /**
7
+ * Shallow clone an object.
8
+ *
9
+ * @param value - The object to clone
10
+ * @returns A shallow clone of the input value
11
+ * @internal
12
+ */
13
+ export function shallowCloneObject<T extends object>(value: T): T {
14
+ if (Array.isArray(value)) {
15
+ return [...value] as T;
16
+ }
17
+ return { ...value };
18
+ }