@fluidframework/core-utils 2.0.0-dev-rc.1.0.0.224419

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/.eslintrc.js +36 -0
  2. package/.mocharc.js +13 -0
  3. package/CHANGELOG.md +81 -0
  4. package/LICENSE +21 -0
  5. package/README.md +75 -0
  6. package/api-extractor-lint.json +4 -0
  7. package/api-extractor.json +4 -0
  8. package/api-report/core-utils.api.md +147 -0
  9. package/dist/assert.d.ts +17 -0
  10. package/dist/assert.d.ts.map +1 -0
  11. package/dist/assert.js +25 -0
  12. package/dist/assert.js.map +1 -0
  13. package/dist/compare.d.ts +16 -0
  14. package/dist/compare.d.ts.map +1 -0
  15. package/dist/compare.js +28 -0
  16. package/dist/compare.js.map +1 -0
  17. package/dist/core-utils-alpha.d.ts +191 -0
  18. package/dist/core-utils-beta.d.ts +41 -0
  19. package/dist/core-utils-public.d.ts +41 -0
  20. package/dist/core-utils-untrimmed.d.ts +414 -0
  21. package/dist/delay.d.ts +11 -0
  22. package/dist/delay.d.ts.map +1 -0
  23. package/dist/delay.js +15 -0
  24. package/dist/delay.js.map +1 -0
  25. package/dist/heap.d.ts +86 -0
  26. package/dist/heap.d.ts.map +1 -0
  27. package/dist/heap.js +144 -0
  28. package/dist/heap.js.map +1 -0
  29. package/dist/index.d.ts +14 -0
  30. package/dist/index.d.ts.map +1 -0
  31. package/dist/index.js +30 -0
  32. package/dist/index.js.map +1 -0
  33. package/dist/lazy.d.ts +44 -0
  34. package/dist/lazy.d.ts.map +1 -0
  35. package/dist/lazy.js +84 -0
  36. package/dist/lazy.js.map +1 -0
  37. package/dist/promiseCache.d.ts +89 -0
  38. package/dist/promiseCache.d.ts.map +1 -0
  39. package/dist/promiseCache.js +148 -0
  40. package/dist/promiseCache.js.map +1 -0
  41. package/dist/promises.d.ts +38 -0
  42. package/dist/promises.d.ts.map +1 -0
  43. package/dist/promises.js +60 -0
  44. package/dist/promises.js.map +1 -0
  45. package/dist/timer.d.ts +115 -0
  46. package/dist/timer.d.ts.map +1 -0
  47. package/dist/timer.js +189 -0
  48. package/dist/timer.js.map +1 -0
  49. package/dist/tsdoc-metadata.json +11 -0
  50. package/dist/unreachable.d.ts +22 -0
  51. package/dist/unreachable.d.ts.map +1 -0
  52. package/dist/unreachable.js +28 -0
  53. package/dist/unreachable.js.map +1 -0
  54. package/lib/assert.d.ts +17 -0
  55. package/lib/assert.d.ts.map +1 -0
  56. package/lib/assert.js +25 -0
  57. package/lib/assert.js.map +1 -0
  58. package/lib/compare.d.ts +16 -0
  59. package/lib/compare.d.ts.map +1 -0
  60. package/lib/compare.js +28 -0
  61. package/lib/compare.js.map +1 -0
  62. package/lib/core-utils-alpha.d.ts +191 -0
  63. package/lib/core-utils-beta.d.ts +41 -0
  64. package/lib/core-utils-public.d.ts +41 -0
  65. package/lib/core-utils-untrimmed.d.ts +414 -0
  66. package/lib/delay.d.ts +11 -0
  67. package/lib/delay.d.ts.map +1 -0
  68. package/lib/delay.js +15 -0
  69. package/lib/delay.js.map +1 -0
  70. package/lib/heap.d.ts +86 -0
  71. package/lib/heap.d.ts.map +1 -0
  72. package/lib/heap.js +144 -0
  73. package/lib/heap.js.map +1 -0
  74. package/lib/index.d.ts +14 -0
  75. package/lib/index.d.ts.map +1 -0
  76. package/lib/index.js +30 -0
  77. package/lib/index.js.map +1 -0
  78. package/lib/lazy.d.ts +44 -0
  79. package/lib/lazy.d.ts.map +1 -0
  80. package/lib/lazy.js +84 -0
  81. package/lib/lazy.js.map +1 -0
  82. package/lib/promiseCache.d.ts +89 -0
  83. package/lib/promiseCache.d.ts.map +1 -0
  84. package/lib/promiseCache.js +148 -0
  85. package/lib/promiseCache.js.map +1 -0
  86. package/lib/promises.d.ts +38 -0
  87. package/lib/promises.d.ts.map +1 -0
  88. package/lib/promises.js +60 -0
  89. package/lib/promises.js.map +1 -0
  90. package/lib/timer.d.ts +115 -0
  91. package/lib/timer.d.ts.map +1 -0
  92. package/lib/timer.js +189 -0
  93. package/lib/timer.js.map +1 -0
  94. package/lib/unreachable.d.ts +22 -0
  95. package/lib/unreachable.d.ts.map +1 -0
  96. package/lib/unreachable.js +28 -0
  97. package/lib/unreachable.js.map +1 -0
  98. package/package.json +111 -0
  99. package/prettier.config.cjs +8 -0
  100. package/src/assert.ts +23 -0
  101. package/src/compare.ts +33 -0
  102. package/src/delay.ts +12 -0
  103. package/src/heap.ts +182 -0
  104. package/src/index.ts +21 -0
  105. package/src/lazy.ts +88 -0
  106. package/src/promiseCache.ts +205 -0
  107. package/src/promises.ts +63 -0
  108. package/src/timer.ts +289 -0
  109. package/src/unreachable.ts +24 -0
  110. package/tsconfig.esnext.json +6 -0
  111. package/tsconfig.json +12 -0
package/src/timer.ts ADDED
@@ -0,0 +1,289 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+ import { assert } from "./assert";
7
+ import { Deferred } from "./promises";
8
+
9
+ /**
10
+ * @internal
11
+ */
12
+ export interface ITimer {
13
+ /**
14
+ * True if timer is currently running
15
+ */
16
+ readonly hasTimer: boolean;
17
+
18
+ /**
19
+ * Starts the timer
20
+ */
21
+ start(): void;
22
+
23
+ /**
24
+ * Cancels the timer if already running
25
+ */
26
+ clear(): void;
27
+ }
28
+
29
+ interface ITimeout {
30
+ /**
31
+ * Tick that timeout was started.
32
+ */
33
+ startTick: number;
34
+
35
+ /**
36
+ * Timeout duration in ms.
37
+ */
38
+ duration: number;
39
+
40
+ /**
41
+ * Handler to execute when timeout ends.
42
+ */
43
+ handler: () => void;
44
+ }
45
+
46
+ interface IRunningTimerState extends ITimeout {
47
+ /**
48
+ * JavaScript Timeout object.
49
+ */
50
+ timeout: ReturnType<typeof setTimeout>;
51
+
52
+ /**
53
+ * Intended duration in ms.
54
+ */
55
+ intendedDuration: number;
56
+
57
+ /**
58
+ * Intended restart timeout.
59
+ */
60
+ restart?: ITimeout;
61
+ }
62
+
63
+ const maxSetTimeoutMs = 0x7fffffff; // setTimeout limit is MAX_INT32=(2^31-1).
64
+
65
+ /**
66
+ * Sets timeouts like the setTimeout function allowing timeouts to exceed the setTimeout's max timeout limit.
67
+ * Timeouts may not be exactly accurate due to browser implementations and the OS.
68
+ * https://stackoverflow.com/questions/21097421/what-is-the-reason-javascript-settimeout-is-so-inaccurate
69
+ * @param timeoutFn - Executed when the timeout expires
70
+ * @param timeoutMs - Duration of the timeout in milliseconds
71
+ * @param setTimeoutIdFn - Executed to update the timeout if multiple timeouts are required when
72
+ * timeoutMs greater than maxTimeout
73
+ * @returns The initial timeout
74
+ * @internal
75
+ */
76
+ export function setLongTimeout(
77
+ timeoutFn: () => void,
78
+ timeoutMs: number,
79
+ setTimeoutIdFn?: (timeoutId: ReturnType<typeof setTimeout>) => void,
80
+ ): ReturnType<typeof setTimeout> {
81
+ // The setTimeout max is 24.8 days before looping occurs.
82
+ let timeoutId: ReturnType<typeof setTimeout>;
83
+ if (timeoutMs > maxSetTimeoutMs) {
84
+ const newTimeoutMs = timeoutMs - maxSetTimeoutMs;
85
+ timeoutId = setTimeout(
86
+ () => setLongTimeout(timeoutFn, newTimeoutMs, setTimeoutIdFn),
87
+ maxSetTimeoutMs,
88
+ );
89
+ } else {
90
+ timeoutId = setTimeout(() => timeoutFn(), Math.max(timeoutMs, 0));
91
+ }
92
+
93
+ setTimeoutIdFn?.(timeoutId);
94
+ return timeoutId;
95
+ }
96
+
97
+ /**
98
+ * This class is a thin wrapper over setTimeout and clearTimeout which
99
+ * makes it simpler to keep track of recurring timeouts with the same
100
+ * or similar handlers and timeouts. This class supports long timeouts
101
+ * or timeouts exceeding (2^31)-1 ms or approximately 24.8 days.
102
+ * @internal
103
+ */
104
+ export class Timer implements ITimer {
105
+ /**
106
+ * Returns true if the timer is running.
107
+ */
108
+ public get hasTimer(): boolean {
109
+ return !!this.runningState;
110
+ }
111
+
112
+ private runningState: IRunningTimerState | undefined;
113
+
114
+ public constructor(
115
+ private readonly defaultTimeout: number,
116
+ private readonly defaultHandler: () => void,
117
+ private readonly getCurrentTick: () => number = (): number => Date.now(),
118
+ ) {}
119
+
120
+ /**
121
+ * Calls setTimeout and tracks the resulting timeout.
122
+ * @param ms - overrides default timeout in ms
123
+ * @param handler - overrides default handler
124
+ */
125
+ public start(
126
+ ms: number = this.defaultTimeout,
127
+ handler: () => void = this.defaultHandler,
128
+ ): void {
129
+ this.startCore(ms, handler, ms);
130
+ }
131
+
132
+ /**
133
+ * Calls clearTimeout on the underlying timeout if running.
134
+ */
135
+ public clear(): void {
136
+ if (!this.runningState) {
137
+ return;
138
+ }
139
+ clearTimeout(this.runningState.timeout);
140
+ this.runningState = undefined;
141
+ }
142
+
143
+ /**
144
+ * Restarts the timer with the new handler and duration.
145
+ * If a new handler is passed, the original handler may
146
+ * never execute.
147
+ * This is a potentially more efficient way to clear and start
148
+ * a new timer.
149
+ * @param ms - overrides previous or default timeout in ms
150
+ * @param handler - overrides previous or default handler
151
+ */
152
+ public restart(ms?: number, handler?: () => void): void {
153
+ if (this.runningState) {
154
+ const duration = ms ?? this.runningState.intendedDuration;
155
+ const handlerToUse =
156
+ handler ?? this.runningState.restart?.handler ?? this.runningState.handler;
157
+ const remainingTime = this.calculateRemainingTime(this.runningState);
158
+
159
+ if (duration < remainingTime) {
160
+ // If remaining time exceeds restart duration, do a hard restart.
161
+ // The existing timeout time is too long.
162
+ this.start(duration, handlerToUse);
163
+ } else if (duration === remainingTime) {
164
+ // The existing timeout time is perfect, just update handler and data.
165
+ this.runningState.handler = handlerToUse;
166
+ this.runningState.restart = undefined;
167
+ this.runningState.intendedDuration = duration;
168
+ } else {
169
+ // If restart duration exceeds remaining time, set restart info.
170
+ // Existing timeout will start a new timeout for remaining time.
171
+ this.runningState.restart = {
172
+ startTick: this.getCurrentTick(),
173
+ duration,
174
+ handler: handlerToUse,
175
+ };
176
+ }
177
+ } else {
178
+ // If restart is called first, it behaves as a call to start
179
+ this.start(ms, handler);
180
+ }
181
+ }
182
+
183
+ private startCore(duration: number, handler: () => void, intendedDuration: number): void {
184
+ this.clear();
185
+ this.runningState = {
186
+ startTick: this.getCurrentTick(),
187
+ duration,
188
+ intendedDuration,
189
+ handler,
190
+ timeout: setLongTimeout(
191
+ () => this.handler(),
192
+ duration,
193
+ (timer: number) => {
194
+ if (this.runningState !== undefined) {
195
+ this.runningState.timeout = timer;
196
+ }
197
+ },
198
+ ),
199
+ };
200
+ }
201
+
202
+ private handler(): void {
203
+ assert(!!this.runningState, 0x764 /* Running timer missing handler */);
204
+ const restart = this.runningState.restart;
205
+ if (restart === undefined) {
206
+ // Run clear first, in case the handler decides to start again
207
+ const handler = this.runningState.handler;
208
+ this.clear();
209
+ handler();
210
+ } else {
211
+ // Restart with remaining time
212
+ const remainingTime = this.calculateRemainingTime(restart);
213
+ this.startCore(remainingTime, () => restart.handler(), restart.duration);
214
+ }
215
+ }
216
+
217
+ private calculateRemainingTime(runningTimeout: ITimeout): number {
218
+ const elapsedTime = this.getCurrentTick() - runningTimeout.startTick;
219
+ return runningTimeout.duration - elapsedTime;
220
+ }
221
+ }
222
+
223
+ /**
224
+ * @internal
225
+ */
226
+ export interface IPromiseTimerResult {
227
+ timerResult: "timeout" | "cancel";
228
+ }
229
+
230
+ /**
231
+ * Timer which offers a promise that fulfills when the timer
232
+ * completes.
233
+ * @internal
234
+ */
235
+ export interface IPromiseTimer extends ITimer {
236
+ /**
237
+ * Starts the timer and returns a promise that
238
+ * resolves when the timer times out or is canceled.
239
+ */
240
+ start(): Promise<IPromiseTimerResult>;
241
+ }
242
+
243
+ /**
244
+ * This class is a wrapper over setTimeout and clearTimeout which
245
+ * makes it simpler to keep track of recurring timeouts with the
246
+ * same handlers and timeouts, while also providing a promise that
247
+ * resolves when it times out.
248
+ * @internal
249
+ */
250
+ export class PromiseTimer implements IPromiseTimer {
251
+ private deferred?: Deferred<IPromiseTimerResult>;
252
+ private readonly timer: Timer;
253
+
254
+ /**
255
+ * {@inheritDoc Timer.hasTimer}
256
+ */
257
+ public get hasTimer(): boolean {
258
+ return this.timer.hasTimer;
259
+ }
260
+
261
+ public constructor(defaultTimeout: number, defaultHandler: () => void) {
262
+ this.timer = new Timer(defaultTimeout, () => this.wrapHandler(defaultHandler));
263
+ }
264
+
265
+ /**
266
+ * {@inheritDoc IPromiseTimer.start}
267
+ */
268
+ public async start(ms?: number, handler?: () => void): Promise<IPromiseTimerResult> {
269
+ this.clear();
270
+ this.deferred = new Deferred<IPromiseTimerResult>();
271
+ this.timer.start(ms, handler ? (): void => this.wrapHandler(handler) : undefined);
272
+ return this.deferred.promise;
273
+ }
274
+
275
+ public clear(): void {
276
+ this.timer.clear();
277
+ if (this.deferred) {
278
+ this.deferred.resolve({ timerResult: "cancel" });
279
+ this.deferred = undefined;
280
+ }
281
+ }
282
+
283
+ protected wrapHandler(handler: () => void): void {
284
+ handler();
285
+ assert(!!this.deferred, 0x765 /* Handler executed without deferred */);
286
+ this.deferred.resolve({ timerResult: "timeout" });
287
+ this.deferred = undefined;
288
+ }
289
+ }
@@ -0,0 +1,24 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+ /**
7
+ * This function can be used to assert at compile time that a given value has type never.
8
+ * One common usage is in the default case of a switch block,
9
+ * to ensure that all cases are explicitly handled.
10
+ *
11
+ * Example:
12
+ * ```typescript
13
+ * const bool: true | false = ...;
14
+ * switch(bool) {
15
+ * case true: {...}
16
+ * case false: {...}
17
+ * default: unreachableCase(bool);
18
+ * }
19
+ * ```
20
+ * @internal
21
+ */
22
+ export function unreachableCase(_: never, message = "Unreachable Case"): never {
23
+ throw new Error(message);
24
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "extends": ["./tsconfig.json", "../../../common/build/build-common/tsconfig.esm.json"],
3
+ "compilerOptions": {
4
+ "outDir": "./lib",
5
+ },
6
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,12 @@
1
+ {
2
+ "extends": [
3
+ "../../../common/build/build-common/tsconfig.base.json",
4
+ "../../../common/build/build-common/tsconfig.cjs.json",
5
+ ],
6
+ "include": ["src/**/*"],
7
+ "exclude": ["src/test/**/*"],
8
+ "compilerOptions": {
9
+ "rootDir": "./src",
10
+ "outDir": "./dist",
11
+ },
12
+ }