@tldraw/utils 4.3.0-canary.bf705d8133cd → 4.3.0-canary.c08047039e53
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/dist-cjs/index.d.ts +61 -1
- package/dist-cjs/index.js +2 -1
- package/dist-cjs/index.js.map +2 -2
- package/dist-cjs/lib/control.js +11 -0
- package/dist-cjs/lib/control.js.map +2 -2
- package/dist-cjs/lib/number.js.map +2 -2
- package/dist-cjs/lib/object.js +6 -5
- package/dist-cjs/lib/object.js.map +2 -2
- package/dist-cjs/lib/throttle.js +113 -69
- package/dist-cjs/lib/throttle.js.map +2 -2
- package/dist-esm/index.d.mts +61 -1
- package/dist-esm/index.mjs +3 -2
- package/dist-esm/index.mjs.map +2 -2
- package/dist-esm/lib/control.mjs +11 -0
- package/dist-esm/lib/control.mjs.map +2 -2
- package/dist-esm/lib/number.mjs.map +2 -2
- package/dist-esm/lib/object.mjs +6 -5
- package/dist-esm/lib/object.mjs.map +2 -2
- package/dist-esm/lib/throttle.mjs +113 -69
- package/dist-esm/lib/throttle.mjs.map +2 -2
- package/package.json +1 -1
- package/src/index.ts +1 -1
- package/src/lib/control.ts +14 -0
- package/src/lib/number.ts +3 -1
- package/src/lib/object.ts +6 -5
- package/src/lib/throttle.test.ts +443 -0
- package/src/lib/throttle.ts +156 -86
package/dist-cjs/index.d.ts
CHANGED
|
@@ -407,6 +407,55 @@ export declare class FileHelpers {
|
|
|
407
407
|
|
|
408
408
|
/* Excluded from this release type: filterEntries */
|
|
409
409
|
|
|
410
|
+
/**
|
|
411
|
+
* A scheduler class that manages a queue of functions to be executed at a target frame rate.
|
|
412
|
+
* Each instance maintains its own queue and state, allowing for separate throttling contexts
|
|
413
|
+
* (e.g., UI operations vs network sync operations).
|
|
414
|
+
*
|
|
415
|
+
* @public
|
|
416
|
+
*/
|
|
417
|
+
export declare class FpsScheduler {
|
|
418
|
+
private targetFps;
|
|
419
|
+
private targetTimePerFrame;
|
|
420
|
+
private fpsQueue;
|
|
421
|
+
private frameRaf;
|
|
422
|
+
private flushRaf;
|
|
423
|
+
private lastFlushTime;
|
|
424
|
+
constructor(targetFps?: number);
|
|
425
|
+
updateTargetFps(targetFps: number): void;
|
|
426
|
+
private flush;
|
|
427
|
+
private tick;
|
|
428
|
+
/**
|
|
429
|
+
* Creates a throttled version of a function that executes at most once per frame.
|
|
430
|
+
* The default target frame rate is set by the FpsScheduler instance.
|
|
431
|
+
* Subsequent calls within the same frame are ignored, ensuring smooth performance
|
|
432
|
+
* for high-frequency events like mouse movements or scroll events.
|
|
433
|
+
*
|
|
434
|
+
* @param fn - The function to throttle, optionally with a cancel method
|
|
435
|
+
* @returns A throttled function with an optional cancel method to remove pending calls
|
|
436
|
+
*
|
|
437
|
+
* @public
|
|
438
|
+
*/
|
|
439
|
+
fpsThrottle(fn: {
|
|
440
|
+
(): void;
|
|
441
|
+
cancel?(): void;
|
|
442
|
+
}): {
|
|
443
|
+
(): void;
|
|
444
|
+
cancel?(): void;
|
|
445
|
+
};
|
|
446
|
+
/**
|
|
447
|
+
* Schedules a function to execute on the next animation frame.
|
|
448
|
+
* If the same function is passed multiple times before the frame executes,
|
|
449
|
+
* it will only be called once, effectively batching multiple calls.
|
|
450
|
+
*
|
|
451
|
+
* @param fn - The function to execute on the next frame
|
|
452
|
+
* @returns A cancel function that can prevent execution if called before the next frame
|
|
453
|
+
*
|
|
454
|
+
* @public
|
|
455
|
+
*/
|
|
456
|
+
throttleToNextFrame(fn: () => void): () => void;
|
|
457
|
+
}
|
|
458
|
+
|
|
410
459
|
/* Excluded from this release type: fpsThrottle */
|
|
411
460
|
|
|
412
461
|
/* Excluded from this release type: getChangedKeys */
|
|
@@ -1067,7 +1116,9 @@ export declare class MediaHelpers {
|
|
|
1067
1116
|
* @example
|
|
1068
1117
|
*
|
|
1069
1118
|
* ```ts
|
|
1070
|
-
* const A = modulate(0, [0, 1], [0, 100])
|
|
1119
|
+
* const A = modulate(0, [0, 1], [0, 100]) // 0
|
|
1120
|
+
* const B = modulate(0.5, [0, 1], [0, 100]) // 50
|
|
1121
|
+
* const C = modulate(1, [0, 1], [0, 100]) // 100
|
|
1071
1122
|
* ```
|
|
1072
1123
|
*
|
|
1073
1124
|
* @param value - The interpolation value.
|
|
@@ -1487,6 +1538,15 @@ export declare const Result: {
|
|
|
1487
1538
|
* @returns An OkResult containing the value
|
|
1488
1539
|
*/
|
|
1489
1540
|
ok<T>(value: T): OkResult<T>;
|
|
1541
|
+
/**
|
|
1542
|
+
* Create a successful result containing an array of values.
|
|
1543
|
+
*
|
|
1544
|
+
* If any of the results are errors, the returned result will be an error containing the first error.
|
|
1545
|
+
*
|
|
1546
|
+
* @param results - The array of results to wrap
|
|
1547
|
+
* @returns An OkResult containing the array of values
|
|
1548
|
+
*/
|
|
1549
|
+
all<T>(results: Result<T, any>[]): Result<T[], any>;
|
|
1490
1550
|
};
|
|
1491
1551
|
|
|
1492
1552
|
/* Excluded from this release type: retry */
|
package/dist-cjs/index.js
CHANGED
|
@@ -34,6 +34,7 @@ __export(index_exports, {
|
|
|
34
34
|
DEFAULT_SUPPORT_VIDEO_TYPES: () => import_media.DEFAULT_SUPPORT_VIDEO_TYPES,
|
|
35
35
|
ExecutionQueue: () => import_ExecutionQueue.ExecutionQueue,
|
|
36
36
|
FileHelpers: () => import_file.FileHelpers,
|
|
37
|
+
FpsScheduler: () => import_throttle.FpsScheduler,
|
|
37
38
|
Image: () => import_network.Image,
|
|
38
39
|
MediaHelpers: () => import_media.MediaHelpers,
|
|
39
40
|
PerformanceTracker: () => import_PerformanceTracker.PerformanceTracker,
|
|
@@ -168,7 +169,7 @@ var import_version2 = require("./lib/version");
|
|
|
168
169
|
var import_warn = require("./lib/warn");
|
|
169
170
|
(0, import_version.registerTldrawLibraryVersion)(
|
|
170
171
|
"@tldraw/utils",
|
|
171
|
-
"4.3.0-canary.
|
|
172
|
+
"4.3.0-canary.c08047039e53",
|
|
172
173
|
"cjs"
|
|
173
174
|
);
|
|
174
175
|
//# sourceMappingURL=index.js.map
|
package/dist-cjs/index.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/index.ts"],
|
|
4
|
-
"sourcesContent": ["import { registerTldrawLibraryVersion } from './lib/version'\n\nexport { default as isEqual } from 'lodash.isequal'\nexport { default as isEqualWith } from 'lodash.isequalwith'\nexport { default as throttle } from 'lodash.throttle'\nexport { default as uniq } from 'lodash.uniq'\nexport {\n\tareArraysShallowEqual,\n\tcompact,\n\tdedupe,\n\tlast,\n\tmaxBy,\n\tmergeArraysAndReplaceDefaults,\n\tminBy,\n\tpartition,\n\trotateArray,\n} from './lib/array'\nexport { bind } from './lib/bind'\nexport { WeakCache } from './lib/cache'\nexport {\n\tassert,\n\tassertExists,\n\texhaustiveSwitchError,\n\tpromiseWithResolve,\n\tResult,\n\tsleep,\n\ttype ErrorResult,\n\ttype OkResult,\n} from './lib/control'\nexport { debounce } from './lib/debounce'\nexport { annotateError, getErrorAnnotations, type ErrorAnnotations } from './lib/error'\nexport { ExecutionQueue } from './lib/ExecutionQueue'\nexport { FileHelpers } from './lib/file'\nexport { noop, omitFromStackTrace } from './lib/function'\nexport { getHashForBuffer, getHashForObject, getHashForString, lns } from './lib/hash'\nexport { mockUniqueId, restoreUniqueId, uniqueId } from './lib/id'\nexport { getFirstFromIterable } from './lib/iterable'\nexport type { JsonArray, JsonObject, JsonPrimitive, JsonValue } from './lib/json-value'\nexport {\n\tDEFAULT_SUPPORT_VIDEO_TYPES,\n\tDEFAULT_SUPPORTED_IMAGE_TYPES,\n\tDEFAULT_SUPPORTED_MEDIA_TYPE_LIST,\n\tDEFAULT_SUPPORTED_MEDIA_TYPES,\n\tMediaHelpers,\n} from './lib/media/media'\nexport { PngHelpers } from './lib/media/png'\nexport { fetch, Image } from './lib/network'\nexport { invLerp, lerp, modulate, rng } from './lib/number'\nexport {\n\tareObjectsShallowEqual,\n\tfilterEntries,\n\tgetChangedKeys,\n\tgetOwnProperty,\n\tgroupBy,\n\thasOwnProperty,\n\tisEqualAllowingForFloatingPointErrors,\n\tmapObjectMapValues,\n\tobjectMapEntries,\n\tobjectMapEntriesIterable,\n\tobjectMapFromEntries,\n\tobjectMapKeys,\n\tobjectMapValues,\n\tomit,\n} from './lib/object'\nexport { measureAverageDuration, measureCbDuration, measureDuration } from './lib/perf'\nexport { PerformanceTracker } from './lib/PerformanceTracker'\nexport {\n\tgetIndexAbove,\n\tgetIndexBelow,\n\tgetIndexBetween,\n\tgetIndices,\n\tgetIndicesAbove,\n\tgetIndicesBelow,\n\tgetIndicesBetween,\n\tsortByIndex,\n\tsortByMaybeIndex,\n\tvalidateIndexKey,\n\tZERO_INDEX_KEY,\n\ttype IndexKey,\n} from './lib/reordering'\nexport { retry } from './lib/retry'\nexport { sortById } from './lib/sort'\nexport {\n\tclearLocalStorage,\n\tclearSessionStorage,\n\tdeleteFromLocalStorage,\n\tdeleteFromSessionStorage,\n\tgetFromLocalStorage,\n\tgetFromSessionStorage,\n\tsetInLocalStorage,\n\tsetInSessionStorage,\n} from './lib/storage'\nexport { stringEnum } from './lib/stringEnum'\nexport { fpsThrottle, throttleToNextFrame } from './lib/throttle'\nexport { Timers } from './lib/timers'\nexport {\n\ttype Expand,\n\ttype MakeUndefinedOptional,\n\ttype RecursivePartial,\n\ttype Required,\n} from './lib/types'\nexport { safeParseUrl } from './lib/url'\nexport {\n\tisDefined,\n\tisNativeStructuredClone,\n\tisNonNull,\n\tisNonNullish,\n\tSTRUCTURED_CLONE_OBJECT_PROTOTYPE,\n\tstructuredClone,\n} from './lib/value'\nexport { registerTldrawLibraryVersion } from './lib/version'\nexport { warnDeprecatedGetter, warnOnce } from './lib/warn'\n\nregisterTldrawLibraryVersion(\n\t(globalThis as any).TLDRAW_LIBRARY_NAME,\n\t(globalThis as any).TLDRAW_LIBRARY_VERSION,\n\t(globalThis as any).TLDRAW_LIBRARY_MODULES\n)\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qBAA6C;AAE7C,oBAAmC;AACnC,IAAAA,iBAAuC;AACvC,IAAAA,iBAAoC;AACpC,IAAAA,iBAAgC;AAChC,mBAUO;AACP,kBAAqB;AACrB,mBAA0B;AAC1B,qBASO;AACP,sBAAyB;AACzB,mBAA0E;AAC1E,4BAA+B;AAC/B,kBAA4B;AAC5B,sBAAyC;AACzC,kBAA0E;AAC1E,gBAAwD;AACxD,sBAAqC;AAErC,mBAMO;AACP,iBAA2B;AAC3B,qBAA6B;AAC7B,oBAA6C;AAC7C,oBAeO;AACP,kBAA2E;AAC3E,gCAAmC;AACnC,wBAaO;AACP,mBAAsB;AACtB,kBAAyB;AACzB,qBASO;AACP,wBAA2B;AAC3B,
|
|
4
|
+
"sourcesContent": ["import { registerTldrawLibraryVersion } from './lib/version'\n\nexport { default as isEqual } from 'lodash.isequal'\nexport { default as isEqualWith } from 'lodash.isequalwith'\nexport { default as throttle } from 'lodash.throttle'\nexport { default as uniq } from 'lodash.uniq'\nexport {\n\tareArraysShallowEqual,\n\tcompact,\n\tdedupe,\n\tlast,\n\tmaxBy,\n\tmergeArraysAndReplaceDefaults,\n\tminBy,\n\tpartition,\n\trotateArray,\n} from './lib/array'\nexport { bind } from './lib/bind'\nexport { WeakCache } from './lib/cache'\nexport {\n\tassert,\n\tassertExists,\n\texhaustiveSwitchError,\n\tpromiseWithResolve,\n\tResult,\n\tsleep,\n\ttype ErrorResult,\n\ttype OkResult,\n} from './lib/control'\nexport { debounce } from './lib/debounce'\nexport { annotateError, getErrorAnnotations, type ErrorAnnotations } from './lib/error'\nexport { ExecutionQueue } from './lib/ExecutionQueue'\nexport { FileHelpers } from './lib/file'\nexport { noop, omitFromStackTrace } from './lib/function'\nexport { getHashForBuffer, getHashForObject, getHashForString, lns } from './lib/hash'\nexport { mockUniqueId, restoreUniqueId, uniqueId } from './lib/id'\nexport { getFirstFromIterable } from './lib/iterable'\nexport type { JsonArray, JsonObject, JsonPrimitive, JsonValue } from './lib/json-value'\nexport {\n\tDEFAULT_SUPPORT_VIDEO_TYPES,\n\tDEFAULT_SUPPORTED_IMAGE_TYPES,\n\tDEFAULT_SUPPORTED_MEDIA_TYPE_LIST,\n\tDEFAULT_SUPPORTED_MEDIA_TYPES,\n\tMediaHelpers,\n} from './lib/media/media'\nexport { PngHelpers } from './lib/media/png'\nexport { fetch, Image } from './lib/network'\nexport { invLerp, lerp, modulate, rng } from './lib/number'\nexport {\n\tareObjectsShallowEqual,\n\tfilterEntries,\n\tgetChangedKeys,\n\tgetOwnProperty,\n\tgroupBy,\n\thasOwnProperty,\n\tisEqualAllowingForFloatingPointErrors,\n\tmapObjectMapValues,\n\tobjectMapEntries,\n\tobjectMapEntriesIterable,\n\tobjectMapFromEntries,\n\tobjectMapKeys,\n\tobjectMapValues,\n\tomit,\n} from './lib/object'\nexport { measureAverageDuration, measureCbDuration, measureDuration } from './lib/perf'\nexport { PerformanceTracker } from './lib/PerformanceTracker'\nexport {\n\tgetIndexAbove,\n\tgetIndexBelow,\n\tgetIndexBetween,\n\tgetIndices,\n\tgetIndicesAbove,\n\tgetIndicesBelow,\n\tgetIndicesBetween,\n\tsortByIndex,\n\tsortByMaybeIndex,\n\tvalidateIndexKey,\n\tZERO_INDEX_KEY,\n\ttype IndexKey,\n} from './lib/reordering'\nexport { retry } from './lib/retry'\nexport { sortById } from './lib/sort'\nexport {\n\tclearLocalStorage,\n\tclearSessionStorage,\n\tdeleteFromLocalStorage,\n\tdeleteFromSessionStorage,\n\tgetFromLocalStorage,\n\tgetFromSessionStorage,\n\tsetInLocalStorage,\n\tsetInSessionStorage,\n} from './lib/storage'\nexport { stringEnum } from './lib/stringEnum'\nexport { FpsScheduler, fpsThrottle, throttleToNextFrame } from './lib/throttle'\nexport { Timers } from './lib/timers'\nexport {\n\ttype Expand,\n\ttype MakeUndefinedOptional,\n\ttype RecursivePartial,\n\ttype Required,\n} from './lib/types'\nexport { safeParseUrl } from './lib/url'\nexport {\n\tisDefined,\n\tisNativeStructuredClone,\n\tisNonNull,\n\tisNonNullish,\n\tSTRUCTURED_CLONE_OBJECT_PROTOTYPE,\n\tstructuredClone,\n} from './lib/value'\nexport { registerTldrawLibraryVersion } from './lib/version'\nexport { warnDeprecatedGetter, warnOnce } from './lib/warn'\n\nregisterTldrawLibraryVersion(\n\t(globalThis as any).TLDRAW_LIBRARY_NAME,\n\t(globalThis as any).TLDRAW_LIBRARY_VERSION,\n\t(globalThis as any).TLDRAW_LIBRARY_MODULES\n)\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qBAA6C;AAE7C,oBAAmC;AACnC,IAAAA,iBAAuC;AACvC,IAAAA,iBAAoC;AACpC,IAAAA,iBAAgC;AAChC,mBAUO;AACP,kBAAqB;AACrB,mBAA0B;AAC1B,qBASO;AACP,sBAAyB;AACzB,mBAA0E;AAC1E,4BAA+B;AAC/B,kBAA4B;AAC5B,sBAAyC;AACzC,kBAA0E;AAC1E,gBAAwD;AACxD,sBAAqC;AAErC,mBAMO;AACP,iBAA2B;AAC3B,qBAA6B;AAC7B,oBAA6C;AAC7C,oBAeO;AACP,kBAA2E;AAC3E,gCAAmC;AACnC,wBAaO;AACP,mBAAsB;AACtB,kBAAyB;AACzB,qBASO;AACP,wBAA2B;AAC3B,sBAA+D;AAC/D,oBAAuB;AAOvB,iBAA6B;AAC7B,mBAOO;AACP,IAAAC,kBAA6C;AAC7C,kBAA+C;AAAA,IAE/C;AAAA,EACE;AAAA,EACA;AAAA,EACA;AACF;",
|
|
6
6
|
"names": ["import_lodash", "import_version"]
|
|
7
7
|
}
|
package/dist-cjs/lib/control.js
CHANGED
|
@@ -45,6 +45,17 @@ const Result = {
|
|
|
45
45
|
*/
|
|
46
46
|
err(error) {
|
|
47
47
|
return { ok: false, error };
|
|
48
|
+
},
|
|
49
|
+
/**
|
|
50
|
+
* Create a successful result containing an array of values.
|
|
51
|
+
*
|
|
52
|
+
* If any of the results are errors, the returned result will be an error containing the first error.
|
|
53
|
+
*
|
|
54
|
+
* @param results - The array of results to wrap
|
|
55
|
+
* @returns An OkResult containing the array of values
|
|
56
|
+
*/
|
|
57
|
+
all(results) {
|
|
58
|
+
return results.every((result) => result.ok) ? Result.ok(results.map((result) => result.value)) : Result.err(results.find((result) => !result.ok)?.error);
|
|
48
59
|
}
|
|
49
60
|
};
|
|
50
61
|
function exhaustiveSwitchError(value, property) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/lib/control.ts"],
|
|
4
|
-
"sourcesContent": ["import { omitFromStackTrace } from './function'\n\n/**\n * Represents a successful result containing a value.\n *\n * Interface for the success case of a Result type, containing the computed value.\n * Used in conjunction with ErrorResult to create a discriminated union for error handling.\n *\n * @example\n * ```ts\n * const success: OkResult<string> = { ok: true, value: 'Hello World' }\n * if (success.ok) {\n * console.log(success.value) // 'Hello World'\n * }\n * ```\n * @public\n */\nexport interface OkResult<T> {\n\treadonly ok: true\n\treadonly value: T\n}\n/**\n * Represents a failed result containing an error.\n *\n * Interface for the error case of a Result type, containing the error information.\n * Used in conjunction with OkResult to create a discriminated union for error handling.\n *\n * @example\n * ```ts\n * const failure: ErrorResult<string> = { ok: false, error: 'Something went wrong' }\n * if (!failure.ok) {\n * console.error(failure.error) // 'Something went wrong'\n * }\n * ```\n * @public\n */\nexport interface ErrorResult<E> {\n\treadonly ok: false\n\treadonly error: E\n}\n/**\n * A discriminated union type for handling success and error cases.\n *\n * Represents either a successful result with a value or a failed result with an error.\n * This pattern provides type-safe error handling without throwing exceptions. The 'ok' property\n * serves as the discriminant for type narrowing.\n *\n * @example\n * ```ts\n * function divide(a: number, b: number): Result<number, string> {\n * if (b === 0) {\n * return Result.err('Division by zero')\n * }\n * return Result.ok(a / b)\n * }\n *\n * const result = divide(10, 2)\n * if (result.ok) {\n * console.log(`Result: ${result.value}`) // Result: 5\n * } else {\n * console.error(`Error: ${result.error}`)\n * }\n * ```\n * @public\n */\nexport type Result<T, E> = OkResult<T> | ErrorResult<E>\n\n/**\n * Utility object for creating Result instances.\n *\n * Provides factory methods for creating OkResult and ErrorResult instances.\n * This is the preferred way to construct Result values for consistent structure.\n *\n * @example\n * ```ts\n * // Create success result\n * const success = Result.ok(42)\n * // success: OkResult<number> = { ok: true, value: 42 }\n *\n * // Create error result\n * const failure = Result.err('Invalid input')\n * // failure: ErrorResult<string> = { ok: false, error: 'Invalid input' }\n * ```\n * @public\n */\nexport const Result = {\n\t/**\n\t * Create a successful result containing a value.\n\t *\n\t * @param value - The success value to wrap\n\t * @returns An OkResult containing the value\n\t */\n\tok<T>(value: T): OkResult<T> {\n\t\treturn { ok: true, value }\n\t},\n\t/**\n\t * Create a failed result containing an error.\n\t *\n\t * @param error - The error value to wrap\n\t * @returns An ErrorResult containing the error\n\t */\n\terr<E>(error: E): ErrorResult<E> {\n\t\treturn { ok: false, error }\n\t},\n}\n\n/**\n * Throws an error for unhandled switch cases in exhaustive switch statements.\n *\n * Utility function to ensure exhaustive handling of discriminated unions in switch\n * statements. When called, it indicates a programming error where a case was not handled.\n * The TypeScript 'never' type ensures this function is only reachable if all cases aren't covered.\n *\n * @param value - The unhandled value (typed as 'never' for exhaustiveness checking)\n * @param property - Optional property name to extract from the value for better error messages\n * @returns Never returns (always throws)\n *\n * @example\n * ```ts\n * type Shape = 'circle' | 'square' | 'triangle'\n *\n * function getArea(shape: Shape): number {\n * switch (shape) {\n * case 'circle': return Math.PI * 5 * 5\n * case 'square': return 10 * 10\n * case 'triangle': return 0.5 * 10 * 8\n * default: return exhaustiveSwitchError(shape)\n * }\n * }\n * ```\n * @internal\n */\nexport function exhaustiveSwitchError(value: never, property?: string): never {\n\tconst debugValue =\n\t\tproperty && value && typeof value === 'object' && property in value ? value[property] : value\n\tthrow new Error(`Unknown switch case ${debugValue}`)\n}\n\n/**\n * Assert that a value is truthy, throwing an error if it's not.\n *\n * TypeScript assertion function that throws an error if the provided value is falsy.\n * After this function executes successfully, TypeScript narrows the type to exclude falsy values.\n * Stack trace is omitted from the error for cleaner debugging.\n *\n * @param value - The value to assert as truthy\n * @param message - Optional custom error message\n *\n * @example\n * ```ts\n * const user = getUser() // User | null\n * assert(user, 'User must be logged in')\n * // TypeScript now knows user is non-null\n * console.log(user.name) // Safe to access properties\n * ```\n * @internal\n */\nexport const assert: (value: unknown, message?: string) => asserts value = omitFromStackTrace(\n\t(value, message) => {\n\t\tif (!value) {\n\t\t\tthrow new Error(message || 'Assertion Error')\n\t\t}\n\t}\n)\n\n/**\n * Assert that a value is not null or undefined.\n *\n * Throws an error if the value is null or undefined, otherwise returns the value\n * with a refined type that excludes null and undefined. Stack trace is omitted for cleaner debugging.\n *\n * @param value - The value to check for null/undefined\n * @param message - Optional custom error message\n * @returns The value with null and undefined excluded from the type\n *\n * @example\n * ```ts\n * const element = document.getElementById('my-id') // HTMLElement | null\n * const safeElement = assertExists(element, 'Element not found')\n * // TypeScript now knows safeElement is HTMLElement (not null)\n * safeElement.addEventListener('click', handler) // Safe to call methods\n * ```\n * @internal\n */\nexport const assertExists = omitFromStackTrace(<T>(value: T, message?: string): NonNullable<T> => {\n\t// note that value == null is equivalent to value === null || value === undefined\n\tif (value == null) {\n\t\tthrow new Error(message ?? 'value must be defined')\n\t}\n\treturn value as NonNullable<T>\n})\n\n/**\n * Create a Promise with externally accessible resolve and reject functions.\n *\n * Creates a Promise along with its resolve and reject functions exposed as\n * properties on the returned object. This allows external code to control when the\n * Promise resolves or rejects, useful for coordination between async operations.\n *\n * @returns A Promise object with additional resolve and reject methods\n *\n * @example\n * ```ts\n * const deferred = promiseWithResolve<string>()\n *\n * // Set up the promise consumer\n * deferred.then(value => console.log(`Resolved: ${value}`))\n * deferred.catch(error => console.error(`Rejected: ${error}`))\n *\n * // Later, resolve from external code\n * setTimeout(() => {\n * deferred.resolve('Hello World')\n * }, 1000)\n * ```\n * @internal\n */\nexport function promiseWithResolve<T>(): Promise<T> & {\n\tresolve(value: T): void\n\treject(reason?: any): void\n} {\n\tlet resolve: (value: T) => void\n\tlet reject: (reason?: any) => void\n\tconst promise = new Promise<T>((res, rej) => {\n\t\tresolve = res\n\t\treject = rej\n\t})\n\treturn Object.assign(promise, {\n\t\tresolve: resolve!,\n\t\treject: reject!,\n\t})\n}\n\n/**\n * Create a Promise that resolves after a specified delay.\n *\n * Utility function for introducing delays in async code. Returns a Promise\n * that resolves with undefined after the specified number of milliseconds. Useful for\n * implementing timeouts, rate limiting, or adding delays in testing scenarios.\n *\n * @param ms - The delay in milliseconds\n * @returns A Promise that resolves after the specified delay\n *\n * @example\n * ```ts\n * async function delayedOperation() {\n * console.log('Starting...')\n * await sleep(1000) // Wait 1 second\n * console.log('Done!')\n * }\n *\n * // Can also be used with .then()\n * sleep(500).then(() => {\n * console.log('Half second has passed')\n * })\n * ```\n * @internal\n */\nexport function sleep(ms: number): Promise<void> {\n\t// eslint-disable-next-line no-restricted-globals\n\treturn new Promise((resolve) => setTimeout(resolve, ms))\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,sBAAmC;AAqF5B,MAAM,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOrB,GAAM,OAAuB;AAC5B,WAAO,EAAE,IAAI,MAAM,MAAM;AAAA,EAC1B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,IAAO,OAA0B;AAChC,WAAO,EAAE,IAAI,OAAO,MAAM;AAAA,EAC3B;AACD;AA4BO,SAAS,sBAAsB,OAAc,UAA0B;AAC7E,QAAM,aACL,YAAY,SAAS,OAAO,UAAU,YAAY,YAAY,QAAQ,MAAM,QAAQ,IAAI;AACzF,QAAM,IAAI,MAAM,uBAAuB,UAAU,EAAE;AACpD;AAqBO,MAAM,aAA8D;AAAA,EAC1E,CAAC,OAAO,YAAY;AACnB,QAAI,CAAC,OAAO;AACX,YAAM,IAAI,MAAM,WAAW,iBAAiB;AAAA,IAC7C;AAAA,EACD;AACD;AAqBO,MAAM,mBAAe,oCAAmB,CAAI,OAAU,YAAqC;AAEjG,MAAI,SAAS,MAAM;AAClB,UAAM,IAAI,MAAM,WAAW,uBAAuB;AAAA,EACnD;AACA,SAAO;AACR,CAAC;AA0BM,SAAS,qBAGd;AACD,MAAI;AACJ,MAAI;AACJ,QAAM,UAAU,IAAI,QAAW,CAAC,KAAK,QAAQ;AAC5C,cAAU;AACV,aAAS;AAAA,EACV,CAAC;AACD,SAAO,OAAO,OAAO,SAAS;AAAA,IAC7B;AAAA,IACA;AAAA,EACD,CAAC;AACF;AA2BO,SAAS,MAAM,IAA2B;AAEhD,SAAO,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,EAAE,CAAC;AACxD;",
|
|
4
|
+
"sourcesContent": ["import { omitFromStackTrace } from './function'\n\n/**\n * Represents a successful result containing a value.\n *\n * Interface for the success case of a Result type, containing the computed value.\n * Used in conjunction with ErrorResult to create a discriminated union for error handling.\n *\n * @example\n * ```ts\n * const success: OkResult<string> = { ok: true, value: 'Hello World' }\n * if (success.ok) {\n * console.log(success.value) // 'Hello World'\n * }\n * ```\n * @public\n */\nexport interface OkResult<T> {\n\treadonly ok: true\n\treadonly value: T\n}\n/**\n * Represents a failed result containing an error.\n *\n * Interface for the error case of a Result type, containing the error information.\n * Used in conjunction with OkResult to create a discriminated union for error handling.\n *\n * @example\n * ```ts\n * const failure: ErrorResult<string> = { ok: false, error: 'Something went wrong' }\n * if (!failure.ok) {\n * console.error(failure.error) // 'Something went wrong'\n * }\n * ```\n * @public\n */\nexport interface ErrorResult<E> {\n\treadonly ok: false\n\treadonly error: E\n}\n/**\n * A discriminated union type for handling success and error cases.\n *\n * Represents either a successful result with a value or a failed result with an error.\n * This pattern provides type-safe error handling without throwing exceptions. The 'ok' property\n * serves as the discriminant for type narrowing.\n *\n * @example\n * ```ts\n * function divide(a: number, b: number): Result<number, string> {\n * if (b === 0) {\n * return Result.err('Division by zero')\n * }\n * return Result.ok(a / b)\n * }\n *\n * const result = divide(10, 2)\n * if (result.ok) {\n * console.log(`Result: ${result.value}`) // Result: 5\n * } else {\n * console.error(`Error: ${result.error}`)\n * }\n * ```\n * @public\n */\nexport type Result<T, E> = OkResult<T> | ErrorResult<E>\n\n/**\n * Utility object for creating Result instances.\n *\n * Provides factory methods for creating OkResult and ErrorResult instances.\n * This is the preferred way to construct Result values for consistent structure.\n *\n * @example\n * ```ts\n * // Create success result\n * const success = Result.ok(42)\n * // success: OkResult<number> = { ok: true, value: 42 }\n *\n * // Create error result\n * const failure = Result.err('Invalid input')\n * // failure: ErrorResult<string> = { ok: false, error: 'Invalid input' }\n * ```\n * @public\n */\nexport const Result = {\n\t/**\n\t * Create a successful result containing a value.\n\t *\n\t * @param value - The success value to wrap\n\t * @returns An OkResult containing the value\n\t */\n\tok<T>(value: T): OkResult<T> {\n\t\treturn { ok: true, value }\n\t},\n\t/**\n\t * Create a failed result containing an error.\n\t *\n\t * @param error - The error value to wrap\n\t * @returns An ErrorResult containing the error\n\t */\n\terr<E>(error: E): ErrorResult<E> {\n\t\treturn { ok: false, error }\n\t},\n\n\t/**\n\t * Create a successful result containing an array of values.\n\t *\n\t * If any of the results are errors, the returned result will be an error containing the first error.\n\t *\n\t * @param results - The array of results to wrap\n\t * @returns An OkResult containing the array of values\n\t */\n\tall<T>(results: Result<T, any>[]): Result<T[], any> {\n\t\treturn results.every((result) => result.ok)\n\t\t\t? Result.ok(results.map((result) => result.value))\n\t\t\t: Result.err(results.find((result) => !result.ok)?.error)\n\t},\n}\n\n/**\n * Throws an error for unhandled switch cases in exhaustive switch statements.\n *\n * Utility function to ensure exhaustive handling of discriminated unions in switch\n * statements. When called, it indicates a programming error where a case was not handled.\n * The TypeScript 'never' type ensures this function is only reachable if all cases aren't covered.\n *\n * @param value - The unhandled value (typed as 'never' for exhaustiveness checking)\n * @param property - Optional property name to extract from the value for better error messages\n * @returns Never returns (always throws)\n *\n * @example\n * ```ts\n * type Shape = 'circle' | 'square' | 'triangle'\n *\n * function getArea(shape: Shape): number {\n * switch (shape) {\n * case 'circle': return Math.PI * 5 * 5\n * case 'square': return 10 * 10\n * case 'triangle': return 0.5 * 10 * 8\n * default: return exhaustiveSwitchError(shape)\n * }\n * }\n * ```\n * @internal\n */\nexport function exhaustiveSwitchError(value: never, property?: string): never {\n\tconst debugValue =\n\t\tproperty && value && typeof value === 'object' && property in value ? value[property] : value\n\tthrow new Error(`Unknown switch case ${debugValue}`)\n}\n\n/**\n * Assert that a value is truthy, throwing an error if it's not.\n *\n * TypeScript assertion function that throws an error if the provided value is falsy.\n * After this function executes successfully, TypeScript narrows the type to exclude falsy values.\n * Stack trace is omitted from the error for cleaner debugging.\n *\n * @param value - The value to assert as truthy\n * @param message - Optional custom error message\n *\n * @example\n * ```ts\n * const user = getUser() // User | null\n * assert(user, 'User must be logged in')\n * // TypeScript now knows user is non-null\n * console.log(user.name) // Safe to access properties\n * ```\n * @internal\n */\nexport const assert: (value: unknown, message?: string) => asserts value = omitFromStackTrace(\n\t(value, message) => {\n\t\tif (!value) {\n\t\t\tthrow new Error(message || 'Assertion Error')\n\t\t}\n\t}\n)\n\n/**\n * Assert that a value is not null or undefined.\n *\n * Throws an error if the value is null or undefined, otherwise returns the value\n * with a refined type that excludes null and undefined. Stack trace is omitted for cleaner debugging.\n *\n * @param value - The value to check for null/undefined\n * @param message - Optional custom error message\n * @returns The value with null and undefined excluded from the type\n *\n * @example\n * ```ts\n * const element = document.getElementById('my-id') // HTMLElement | null\n * const safeElement = assertExists(element, 'Element not found')\n * // TypeScript now knows safeElement is HTMLElement (not null)\n * safeElement.addEventListener('click', handler) // Safe to call methods\n * ```\n * @internal\n */\nexport const assertExists = omitFromStackTrace(<T>(value: T, message?: string): NonNullable<T> => {\n\t// note that value == null is equivalent to value === null || value === undefined\n\tif (value == null) {\n\t\tthrow new Error(message ?? 'value must be defined')\n\t}\n\treturn value as NonNullable<T>\n})\n\n/**\n * Create a Promise with externally accessible resolve and reject functions.\n *\n * Creates a Promise along with its resolve and reject functions exposed as\n * properties on the returned object. This allows external code to control when the\n * Promise resolves or rejects, useful for coordination between async operations.\n *\n * @returns A Promise object with additional resolve and reject methods\n *\n * @example\n * ```ts\n * const deferred = promiseWithResolve<string>()\n *\n * // Set up the promise consumer\n * deferred.then(value => console.log(`Resolved: ${value}`))\n * deferred.catch(error => console.error(`Rejected: ${error}`))\n *\n * // Later, resolve from external code\n * setTimeout(() => {\n * deferred.resolve('Hello World')\n * }, 1000)\n * ```\n * @internal\n */\nexport function promiseWithResolve<T>(): Promise<T> & {\n\tresolve(value: T): void\n\treject(reason?: any): void\n} {\n\tlet resolve: (value: T) => void\n\tlet reject: (reason?: any) => void\n\tconst promise = new Promise<T>((res, rej) => {\n\t\tresolve = res\n\t\treject = rej\n\t})\n\treturn Object.assign(promise, {\n\t\tresolve: resolve!,\n\t\treject: reject!,\n\t})\n}\n\n/**\n * Create a Promise that resolves after a specified delay.\n *\n * Utility function for introducing delays in async code. Returns a Promise\n * that resolves with undefined after the specified number of milliseconds. Useful for\n * implementing timeouts, rate limiting, or adding delays in testing scenarios.\n *\n * @param ms - The delay in milliseconds\n * @returns A Promise that resolves after the specified delay\n *\n * @example\n * ```ts\n * async function delayedOperation() {\n * console.log('Starting...')\n * await sleep(1000) // Wait 1 second\n * console.log('Done!')\n * }\n *\n * // Can also be used with .then()\n * sleep(500).then(() => {\n * console.log('Half second has passed')\n * })\n * ```\n * @internal\n */\nexport function sleep(ms: number): Promise<void> {\n\t// eslint-disable-next-line no-restricted-globals\n\treturn new Promise((resolve) => setTimeout(resolve, ms))\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,sBAAmC;AAqF5B,MAAM,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOrB,GAAM,OAAuB;AAC5B,WAAO,EAAE,IAAI,MAAM,MAAM;AAAA,EAC1B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,IAAO,OAA0B;AAChC,WAAO,EAAE,IAAI,OAAO,MAAM;AAAA,EAC3B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,IAAO,SAA6C;AACnD,WAAO,QAAQ,MAAM,CAAC,WAAW,OAAO,EAAE,IACvC,OAAO,GAAG,QAAQ,IAAI,CAAC,WAAW,OAAO,KAAK,CAAC,IAC/C,OAAO,IAAI,QAAQ,KAAK,CAAC,WAAW,CAAC,OAAO,EAAE,GAAG,KAAK;AAAA,EAC1D;AACD;AA4BO,SAAS,sBAAsB,OAAc,UAA0B;AAC7E,QAAM,aACL,YAAY,SAAS,OAAO,UAAU,YAAY,YAAY,QAAQ,MAAM,QAAQ,IAAI;AACzF,QAAM,IAAI,MAAM,uBAAuB,UAAU,EAAE;AACpD;AAqBO,MAAM,aAA8D;AAAA,EAC1E,CAAC,OAAO,YAAY;AACnB,QAAI,CAAC,OAAO;AACX,YAAM,IAAI,MAAM,WAAW,iBAAiB;AAAA,IAC7C;AAAA,EACD;AACD;AAqBO,MAAM,mBAAe,oCAAmB,CAAI,OAAU,YAAqC;AAEjG,MAAI,SAAS,MAAM;AAClB,UAAM,IAAI,MAAM,WAAW,uBAAuB;AAAA,EACnD;AACA,SAAO;AACR,CAAC;AA0BM,SAAS,qBAGd;AACD,MAAI;AACJ,MAAI;AACJ,QAAM,UAAU,IAAI,QAAW,CAAC,KAAK,QAAQ;AAC5C,cAAU;AACV,aAAS;AAAA,EACV,CAAC;AACD,SAAO,OAAO,OAAO,SAAS;AAAA,IAC7B;AAAA,IACA;AAAA,EACD,CAAC;AACF;AA2BO,SAAS,MAAM,IAA2B;AAEhD,SAAO,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,EAAE,CAAC;AACxD;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/lib/number.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Linear interpolate between two values.\n *\n * @param a - The start value\n * @param b - The end value\n * @param t - The interpolation factor (0-1)\n * @returns The interpolated value\n * @example\n * ```ts\n * const halfway = lerp(0, 100, 0.5) // 50\n * const quarter = lerp(10, 20, 0.25) // 12.5\n * ```\n * @public\n */\nexport function lerp(a: number, b: number, t: number) {\n\treturn a + (b - a) * t\n}\n\n/**\n * Inverse lerp between two values. Given a value `t` in the range [a, b], returns a number between\n * 0 and 1.\n *\n * @param a - The start value of the range\n * @param b - The end value of the range\n * @param t - The value within the range [a, b]\n * @returns The normalized position (0-1) of t within the range [a, b]\n * @example\n * ```ts\n * const position = invLerp(0, 100, 25) // 0.25\n * const normalized = invLerp(10, 20, 15) // 0.5\n * ```\n * @public\n */\nexport function invLerp(a: number, b: number, t: number) {\n\treturn (t - a) / (b - a)\n}\n\n/**\n * Seeded random number generator, using [xorshift](https://en.wikipedia.org/wiki/Xorshift). The\n * result will always be between -1 and 1.\n *\n * Adapted from [seedrandom](https://github.com/davidbau/seedrandom).\n *\n * @param seed - The seed string for deterministic random generation (defaults to empty string)\n * @returns A function that will return a random number between -1 and 1 each time it is called\n * @example\n * ```ts\n * const random = rng('my-seed')\n * const num1 = random() // Always the same for this seed\n * const num2 = random() // Next number in sequence\n *\n * // Different seed produces different sequence\n * const otherRandom = rng('other-seed')\n * const different = otherRandom() // Different value\n * ```\n * @public\n */\nexport function rng(seed = '') {\n\tlet x = 0\n\tlet y = 0\n\tlet z = 0\n\tlet w = 0\n\n\tfunction next() {\n\t\tconst t = x ^ (x << 11)\n\t\tx = y\n\t\ty = z\n\t\tz = w\n\t\tw ^= ((w >>> 19) ^ t ^ (t >>> 8)) >>> 0\n\t\treturn (w / 0x100000000) * 2\n\t}\n\n\tfor (let k = 0; k < seed.length + 64; k++) {\n\t\tx ^= seed.charCodeAt(k) | 0\n\t\tnext()\n\t}\n\n\treturn next\n}\n\n/**\n * Modulate a value between two ranges.\n *\n * @example\n *\n * ```ts\n * const A = modulate(0, [0, 1], [0, 100])\n * ```\n *\n * @param value - The interpolation value.\n * @param rangeA - From [low, high]\n * @param rangeB - To [low, high]\n * @param clamp - Whether to clamp the the result to [low, high]\n * @public\n */\nexport function modulate(value: number, rangeA: number[], rangeB: number[], clamp = false): number {\n\tconst [fromLow, fromHigh] = rangeA\n\tconst [v0, v1] = rangeB\n\tconst result = v0 + ((value - fromLow) / (fromHigh - fromLow)) * (v1 - v0)\n\n\treturn clamp\n\t\t? v0 < v1\n\t\t\t? Math.max(Math.min(result, v1), v0)\n\t\t\t: Math.max(Math.min(result, v0), v1)\n\t\t: result\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAcO,SAAS,KAAK,GAAW,GAAW,GAAW;AACrD,SAAO,KAAK,IAAI,KAAK;AACtB;AAiBO,SAAS,QAAQ,GAAW,GAAW,GAAW;AACxD,UAAQ,IAAI,MAAM,IAAI;AACvB;AAsBO,SAAS,IAAI,OAAO,IAAI;AAC9B,MAAI,IAAI;AACR,MAAI,IAAI;AACR,MAAI,IAAI;AACR,MAAI,IAAI;AAER,WAAS,OAAO;AACf,UAAM,IAAI,IAAK,KAAK;AACpB,QAAI;AACJ,QAAI;AACJ,QAAI;AACJ,UAAO,MAAM,KAAM,IAAK,MAAM,OAAQ;AACtC,WAAQ,IAAI,aAAe;AAAA,EAC5B;AAEA,WAAS,IAAI,GAAG,IAAI,KAAK,SAAS,IAAI,KAAK;AAC1C,SAAK,KAAK,WAAW,CAAC,IAAI;AAC1B,SAAK;AAAA,EACN;AAEA,SAAO;AACR;
|
|
4
|
+
"sourcesContent": ["/**\n * Linear interpolate between two values.\n *\n * @param a - The start value\n * @param b - The end value\n * @param t - The interpolation factor (0-1)\n * @returns The interpolated value\n * @example\n * ```ts\n * const halfway = lerp(0, 100, 0.5) // 50\n * const quarter = lerp(10, 20, 0.25) // 12.5\n * ```\n * @public\n */\nexport function lerp(a: number, b: number, t: number) {\n\treturn a + (b - a) * t\n}\n\n/**\n * Inverse lerp between two values. Given a value `t` in the range [a, b], returns a number between\n * 0 and 1.\n *\n * @param a - The start value of the range\n * @param b - The end value of the range\n * @param t - The value within the range [a, b]\n * @returns The normalized position (0-1) of t within the range [a, b]\n * @example\n * ```ts\n * const position = invLerp(0, 100, 25) // 0.25\n * const normalized = invLerp(10, 20, 15) // 0.5\n * ```\n * @public\n */\nexport function invLerp(a: number, b: number, t: number) {\n\treturn (t - a) / (b - a)\n}\n\n/**\n * Seeded random number generator, using [xorshift](https://en.wikipedia.org/wiki/Xorshift). The\n * result will always be between -1 and 1.\n *\n * Adapted from [seedrandom](https://github.com/davidbau/seedrandom).\n *\n * @param seed - The seed string for deterministic random generation (defaults to empty string)\n * @returns A function that will return a random number between -1 and 1 each time it is called\n * @example\n * ```ts\n * const random = rng('my-seed')\n * const num1 = random() // Always the same for this seed\n * const num2 = random() // Next number in sequence\n *\n * // Different seed produces different sequence\n * const otherRandom = rng('other-seed')\n * const different = otherRandom() // Different value\n * ```\n * @public\n */\nexport function rng(seed = '') {\n\tlet x = 0\n\tlet y = 0\n\tlet z = 0\n\tlet w = 0\n\n\tfunction next() {\n\t\tconst t = x ^ (x << 11)\n\t\tx = y\n\t\ty = z\n\t\tz = w\n\t\tw ^= ((w >>> 19) ^ t ^ (t >>> 8)) >>> 0\n\t\treturn (w / 0x100000000) * 2\n\t}\n\n\tfor (let k = 0; k < seed.length + 64; k++) {\n\t\tx ^= seed.charCodeAt(k) | 0\n\t\tnext()\n\t}\n\n\treturn next\n}\n\n/**\n * Modulate a value between two ranges.\n *\n * @example\n *\n * ```ts\n * const A = modulate(0, [0, 1], [0, 100]) // 0\n * const B = modulate(0.5, [0, 1], [0, 100]) // 50\n * const C = modulate(1, [0, 1], [0, 100]) // 100\n * ```\n *\n * @param value - The interpolation value.\n * @param rangeA - From [low, high]\n * @param rangeB - To [low, high]\n * @param clamp - Whether to clamp the the result to [low, high]\n * @public\n */\nexport function modulate(value: number, rangeA: number[], rangeB: number[], clamp = false): number {\n\tconst [fromLow, fromHigh] = rangeA\n\tconst [v0, v1] = rangeB\n\tconst result = v0 + ((value - fromLow) / (fromHigh - fromLow)) * (v1 - v0)\n\n\treturn clamp\n\t\t? v0 < v1\n\t\t\t? Math.max(Math.min(result, v1), v0)\n\t\t\t: Math.max(Math.min(result, v0), v1)\n\t\t: result\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAcO,SAAS,KAAK,GAAW,GAAW,GAAW;AACrD,SAAO,KAAK,IAAI,KAAK;AACtB;AAiBO,SAAS,QAAQ,GAAW,GAAW,GAAW;AACxD,UAAQ,IAAI,MAAM,IAAI;AACvB;AAsBO,SAAS,IAAI,OAAO,IAAI;AAC9B,MAAI,IAAI;AACR,MAAI,IAAI;AACR,MAAI,IAAI;AACR,MAAI,IAAI;AAER,WAAS,OAAO;AACf,UAAM,IAAI,IAAK,KAAK;AACpB,QAAI;AACJ,QAAI;AACJ,QAAI;AACJ,UAAO,MAAM,KAAM,IAAK,MAAM,OAAQ;AACtC,WAAQ,IAAI,aAAe;AAAA,EAC5B;AAEA,WAAS,IAAI,GAAG,IAAI,KAAK,SAAS,IAAI,KAAK;AAC1C,SAAK,KAAK,WAAW,CAAC,IAAI;AAC1B,SAAK;AAAA,EACN;AAEA,SAAO;AACR;AAmBO,SAAS,SAAS,OAAe,QAAkB,QAAkB,QAAQ,OAAe;AAClG,QAAM,CAAC,SAAS,QAAQ,IAAI;AAC5B,QAAM,CAAC,IAAI,EAAE,IAAI;AACjB,QAAM,SAAS,MAAO,QAAQ,YAAY,WAAW,YAAa,KAAK;AAEvE,SAAO,QACJ,KAAK,KACJ,KAAK,IAAI,KAAK,IAAI,QAAQ,EAAE,GAAG,EAAE,IACjC,KAAK,IAAI,KAAK,IAAI,QAAQ,EAAE,GAAG,EAAE,IAClC;AACJ;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist-cjs/lib/object.js
CHANGED
|
@@ -75,7 +75,9 @@ function objectMapFromEntries(entries) {
|
|
|
75
75
|
function filterEntries(object, predicate) {
|
|
76
76
|
const result = {};
|
|
77
77
|
let didChange = false;
|
|
78
|
-
for (const
|
|
78
|
+
for (const key in object) {
|
|
79
|
+
if (!Object.prototype.hasOwnProperty.call(object, key)) continue;
|
|
80
|
+
const value = object[key];
|
|
79
81
|
if (predicate(key, value)) {
|
|
80
82
|
result[key] = value;
|
|
81
83
|
} else {
|
|
@@ -94,11 +96,10 @@ function mapObjectMapValues(object, mapper) {
|
|
|
94
96
|
}
|
|
95
97
|
function areObjectsShallowEqual(obj1, obj2) {
|
|
96
98
|
if (obj1 === obj2) return true;
|
|
97
|
-
const keys1 =
|
|
98
|
-
|
|
99
|
-
if (keys1.size !== keys2.size) return false;
|
|
99
|
+
const keys1 = Object.keys(obj1);
|
|
100
|
+
if (keys1.length !== Object.keys(obj2).length) return false;
|
|
100
101
|
for (const key of keys1) {
|
|
101
|
-
if (!
|
|
102
|
+
if (!hasOwnProperty(obj2, key)) return false;
|
|
102
103
|
if (!Object.is(obj1[key], obj2[key])) return false;
|
|
103
104
|
}
|
|
104
105
|
return true;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/lib/object.ts"],
|
|
4
|
-
"sourcesContent": ["import isEqualWith from 'lodash.isequalwith'\n\n/**\n * Safely checks if an object has a specific property as its own property (not inherited).\n * Uses Object.prototype.hasOwnProperty.call to avoid issues with objects that have null prototype\n * or have overridden the hasOwnProperty method.\n *\n * @param obj - The object to check\n * @param key - The property key to check for\n * @returns True if the object has the property as its own property, false otherwise\n * @example\n * ```ts\n * const obj = { name: 'Alice', age: 30 }\n * hasOwnProperty(obj, 'name') // true\n * hasOwnProperty(obj, 'toString') // false (inherited)\n * hasOwnProperty(obj, 'unknown') // false\n * ```\n * @internal\n */\nexport function hasOwnProperty(obj: object, key: string): boolean {\n\treturn Object.prototype.hasOwnProperty.call(obj, key)\n}\n\n/**\n * Safely gets an object's own property value (not inherited). Returns undefined if the property\n * doesn't exist as an own property. Provides type-safe access with proper TypeScript inference.\n *\n * @param obj - The object to get the property from\n * @param key - The property key to retrieve\n * @returns The property value if it exists as an own property, undefined otherwise\n * @example\n * ```ts\n * const user = { name: 'Alice', age: 30 }\n * const name = getOwnProperty(user, 'name') // 'Alice'\n * const missing = getOwnProperty(user, 'unknown') // undefined\n * const inherited = getOwnProperty(user, 'toString') // undefined (inherited)\n * ```\n * @internal\n */\nexport function getOwnProperty<K extends string, V>(\n\tobj: Partial<Record<K, V>>,\n\tkey: K\n): V | undefined\n/** @internal */\nexport function getOwnProperty<O extends object>(obj: O, key: string): O[keyof O] | undefined\n/** @internal */\nexport function getOwnProperty(obj: object, key: string): unknown\n/** @internal */\nexport function getOwnProperty(obj: object, key: string): unknown {\n\tif (!hasOwnProperty(obj, key)) {\n\t\treturn undefined\n\t}\n\t// @ts-expect-error we know the property exists\n\treturn obj[key]\n}\n\n/**\n * An alias for `Object.keys` that treats the object as a map and so preserves the type of the keys.\n * Unlike standard Object.keys which returns string[], this maintains the specific string literal types.\n *\n * @param object - The object to get keys from\n * @returns Array of keys with preserved string literal types\n * @example\n * ```ts\n * const config = { theme: 'dark', lang: 'en' } as const\n * const keys = objectMapKeys(config)\n * // keys is Array<'theme' | 'lang'> instead of string[]\n * ```\n * @internal\n */\nexport function objectMapKeys<Key extends string>(object: {\n\treadonly [K in Key]: unknown\n}): Array<Key> {\n\treturn Object.keys(object) as Key[]\n}\n\n/**\n * An alias for `Object.values` that treats the object as a map and so preserves the type of the\n * values. Unlike standard Object.values which returns unknown[], this maintains the specific value types.\n *\n * @param object - The object to get values from\n * @returns Array of values with preserved types\n * @example\n * ```ts\n * const scores = { alice: 85, bob: 92, charlie: 78 }\n * const values = objectMapValues(scores)\n * // values is Array<number> instead of unknown[]\n * ```\n * @internal\n */\nexport function objectMapValues<Key extends string, Value>(object: {\n\t[K in Key]: Value\n}): Array<Value> {\n\treturn Object.values(object) as Value[]\n}\n\n/**\n * An alias for `Object.entries` that treats the object as a map and so preserves the type of the\n * keys and values. Unlike standard Object.entries which returns `Array<[string, unknown]>`, this maintains specific types.\n *\n * @param object - The object to get entries from\n * @returns Array of key-value pairs with preserved types\n * @example\n * ```ts\n * const user = { name: 'Alice', age: 30 }\n * const entries = objectMapEntries(user)\n * // entries is Array<['name' | 'age', string | number]>\n * ```\n * @internal\n */\nexport function objectMapEntries<Obj extends object>(\n\tobject: Obj\n): Array<[keyof Obj, Obj[keyof Obj]]> {\n\treturn Object.entries(object) as [keyof Obj, Obj[keyof Obj]][]\n}\n\n/**\n * Returns the entries of an object as an iterable iterator.\n * Useful when working with large collections, to avoid allocating an array.\n * Only yields own properties (not inherited ones).\n *\n * @param object - The object to iterate over\n * @returns Iterator yielding key-value pairs with preserved types\n * @example\n * ```ts\n * const largeMap = { a: 1, b: 2, c: 3 } // Imagine thousands of entries\n * for (const [key, value] of objectMapEntriesIterable(largeMap)) {\n * // Process entries one at a time without creating a large array\n * console.log(key, value)\n * }\n * ```\n * @internal\n */\nexport function* objectMapEntriesIterable<Key extends string, Value>(object: {\n\t[K in Key]: Value\n}): IterableIterator<[Key, Value]> {\n\tfor (const key in object) {\n\t\tif (!Object.prototype.hasOwnProperty.call(object, key)) continue\n\t\tyield [key, object[key]]\n\t}\n}\n\n/**\n * An alias for `Object.fromEntries` that treats the object as a map and so preserves the type of the\n * keys and values. Creates an object from key-value pairs with proper TypeScript typing.\n *\n * @param entries - Array of key-value pairs to convert to an object\n * @returns Object with preserved key and value types\n * @example\n * ```ts\n * const pairs: Array<['name' | 'age', string | number]> = [['name', 'Alice'], ['age', 30]]\n * const obj = objectMapFromEntries(pairs)\n * // obj is { name: string | number, age: string | number }\n * ```\n * @internal\n */\nexport function objectMapFromEntries<Key extends string, Value>(\n\tentries: ReadonlyArray<readonly [Key, Value]>\n): { [K in Key]: Value } {\n\treturn Object.fromEntries(entries) as { [K in Key]: Value }\n}\n\n/**\n * Filters an object using a predicate function, returning a new object with only the entries\n * that pass the predicate. Optimized to return the original object if no changes are needed.\n *\n * @param object - The object to filter\n * @param predicate - Function that tests each key-value pair\n * @returns A new object with only the entries that pass the predicate, or the original object if unchanged\n * @example\n * ```ts\n * const scores = { alice: 85, bob: 92, charlie: 78 }\n * const passing = filterEntries(scores, (name, score) => score >= 80)\n * // { alice: 85, bob: 92 }\n * ```\n * @internal\n */\nexport function filterEntries<Key extends string, Value>(\n\tobject: { [K in Key]: Value },\n\tpredicate: (key: Key, value: Value) => boolean\n): { [K in Key]: Value } {\n\tconst result: { [K in Key]?: Value } = {}\n\tlet didChange = false\n\tfor (const [key, value] of objectMapEntries(object)) {\n\t\tif (predicate(key, value)) {\n\t\t\tresult[key] = value\n\t\t} else {\n\t\t\tdidChange = true\n\t\t}\n\t}\n\treturn didChange ? (result as { [K in Key]: Value }) : object\n}\n\n/**\n * Maps the values of an object to new values using a mapper function, preserving keys.\n * The mapper function receives both the key and value for each entry.\n *\n * @param object - The object whose values to transform\n * @param mapper - Function that transforms each value (receives key and value)\n * @returns A new object with the same keys but transformed values\n * @example\n * ```ts\n * const prices = { apple: 1.50, banana: 0.75, orange: 2.00 }\n * const withTax = mapObjectMapValues(prices, (fruit, price) => price * 1.08)\n * // { apple: 1.62, banana: 0.81, orange: 2.16 }\n * ```\n * @internal\n */\nexport function mapObjectMapValues<Key extends string, ValueBefore, ValueAfter>(\n\tobject: { readonly [K in Key]: ValueBefore },\n\tmapper: (key: Key, value: ValueBefore) => ValueAfter\n): { [K in Key]: ValueAfter } {\n\tconst result = {} as { [K in Key]: ValueAfter }\n\tfor (const key in object) {\n\t\tif (!Object.prototype.hasOwnProperty.call(object, key)) continue\n\t\tresult[key] = mapper(key, object[key])\n\t}\n\treturn result\n}\n\n/**\n * Performs a shallow equality check between two objects. Compares all enumerable own properties\n * using Object.is for value comparison. Returns true if both objects have the same keys and values.\n *\n * @param obj1 - First object to compare\n * @param obj2 - Second object to compare\n * @returns True if objects are shallow equal, false otherwise\n * @example\n * ```ts\n * const a = { x: 1, y: 2 }\n * const b = { x: 1, y: 2 }\n * const c = { x: 1, y: 3 }\n * areObjectsShallowEqual(a, b) // true\n * areObjectsShallowEqual(a, c) // false\n * areObjectsShallowEqual(a, a) // true (same reference)\n * ```\n * @internal\n */\nexport function areObjectsShallowEqual<T extends object>(obj1: T, obj2: T): boolean {\n\tif (obj1 === obj2) return true\n\tconst keys1 = new Set(Object.keys(obj1))\n\tconst keys2 = new Set(Object.keys(obj2))\n\tif (keys1.size !== keys2.size) return false\n\tfor (const key of keys1) {\n\t\tif (!keys2.has(key)) return false\n\t\tif (!Object.is((obj1 as any)[key], (obj2 as any)[key])) return false\n\t}\n\treturn true\n}\n\n/**\n * Groups an array of values into a record by a key extracted from each value.\n * The key selector function is called for each element to determine the grouping key.\n *\n * @param array - The array to group\n * @param keySelector - Function that extracts the grouping key from each value\n * @returns A record where keys are the extracted keys and values are arrays of grouped items\n * @example\n * ```ts\n * const people = [\n * { name: 'Alice', age: 25 },\n * { name: 'Bob', age: 30 },\n * { name: 'Charlie', age: 25 }\n * ]\n * const byAge = groupBy(people, person => `age-${person.age}`)\n * // { 'age-25': [Alice, Charlie], 'age-30': [Bob] }\n * ```\n * @internal\n */\nexport function groupBy<K extends string, V>(\n\tarray: ReadonlyArray<V>,\n\tkeySelector: (value: V) => K\n): Record<K, V[]> {\n\tconst result: Record<K, V[]> = {} as any\n\tfor (const value of array) {\n\t\tconst key = keySelector(value)\n\t\tif (!result[key]) result[key] = []\n\t\tresult[key].push(value)\n\t}\n\treturn result\n}\n\n/**\n * Creates a new object with specified keys omitted from the original object.\n * Uses shallow copying and then deletes the unwanted keys.\n *\n * @param obj - The source object\n * @param keys - Array of key names to omit from the result\n * @returns A new object without the specified keys\n * @example\n * ```ts\n * const user = { id: '123', name: 'Alice', password: 'secret', email: 'alice@example.com' }\n * const publicUser = omit(user, ['password'])\n * // { id: '123', name: 'Alice', email: 'alice@example.com' }\n * ```\n * @internal\n */\nexport function omit(\n\tobj: Record<string, unknown>,\n\tkeys: ReadonlyArray<string>\n): Record<string, unknown> {\n\tconst result = { ...obj }\n\tfor (const key of keys) {\n\t\tdelete result[key]\n\t}\n\treturn result\n}\n\n/**\n * Compares two objects and returns an array of keys where the values differ.\n * Uses Object.is for comparison, which handles NaN and -0/+0 correctly.\n * Only checks keys present in the first object.\n *\n * @param obj1 - The first object (keys to check come from this object)\n * @param obj2 - The second object to compare against\n * @returns Array of keys where values differ between the objects\n * @example\n * ```ts\n * const before = { name: 'Alice', age: 25, city: 'NYC' }\n * const after = { name: 'Alice', age: 26, city: 'NYC' }\n * const changed = getChangedKeys(before, after)\n * // ['age']\n * ```\n * @internal\n */\nexport function getChangedKeys<T extends object>(obj1: T, obj2: T): (keyof T)[] {\n\tconst result: (keyof T)[] = []\n\tfor (const key in obj1) {\n\t\tif (!Object.is(obj1[key], obj2[key])) {\n\t\t\tresult.push(key)\n\t\t}\n\t}\n\treturn result\n}\n\n/**\n * Deep equality comparison that allows for floating-point precision errors.\n * Numbers are considered equal if they differ by less than the threshold.\n * Uses lodash.isequalwith internally for the deep comparison logic.\n *\n * @param obj1 - First object to compare\n * @param obj2 - Second object to compare\n * @param threshold - Maximum difference allowed between numbers (default: 0.000001)\n * @returns True if objects are deeply equal with floating-point tolerance\n * @example\n * ```ts\n * const a = { x: 0.1 + 0.2 } // 0.30000000000000004\n * const b = { x: 0.3 }\n * isEqualAllowingForFloatingPointErrors(a, b) // true\n *\n * const c = { coords: [1.0000001, 2.0000001] }\n * const d = { coords: [1.0000002, 2.0000002] }\n * isEqualAllowingForFloatingPointErrors(c, d) // true\n * ```\n * @internal\n */\nexport function isEqualAllowingForFloatingPointErrors(\n\tobj1: object,\n\tobj2: object,\n\tthreshold = 0.000001\n): boolean {\n\treturn isEqualWith(obj1, obj2, (value1, value2) => {\n\t\tif (typeof value1 === 'number' && typeof value2 === 'number') {\n\t\t\treturn Math.abs(value1 - value2) < threshold\n\t\t}\n\t\treturn undefined\n\t})\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oBAAwB;AAmBjB,SAAS,eAAe,KAAa,KAAsB;AACjE,SAAO,OAAO,UAAU,eAAe,KAAK,KAAK,GAAG;AACrD;AA2BO,SAAS,eAAe,KAAa,KAAsB;AACjE,MAAI,CAAC,eAAe,KAAK,GAAG,GAAG;AAC9B,WAAO;AAAA,EACR;AAEA,SAAO,IAAI,GAAG;AACf;AAgBO,SAAS,cAAkC,QAEnC;AACd,SAAO,OAAO,KAAK,MAAM;AAC1B;AAgBO,SAAS,gBAA2C,QAE1C;AAChB,SAAO,OAAO,OAAO,MAAM;AAC5B;AAgBO,SAAS,iBACf,QACqC;AACrC,SAAO,OAAO,QAAQ,MAAM;AAC7B;AAmBO,UAAU,yBAAoD,QAElC;AAClC,aAAW,OAAO,QAAQ;AACzB,QAAI,CAAC,OAAO,UAAU,eAAe,KAAK,QAAQ,GAAG,EAAG;AACxD,UAAM,CAAC,KAAK,OAAO,GAAG,CAAC;AAAA,EACxB;AACD;AAgBO,SAAS,qBACf,SACwB;AACxB,SAAO,OAAO,YAAY,OAAO;AAClC;AAiBO,SAAS,cACf,QACA,WACwB;AACxB,QAAM,SAAiC,CAAC;AACxC,MAAI,YAAY;AAChB,aAAW,CAAC,
|
|
4
|
+
"sourcesContent": ["import isEqualWith from 'lodash.isequalwith'\n\n/**\n * Safely checks if an object has a specific property as its own property (not inherited).\n * Uses Object.prototype.hasOwnProperty.call to avoid issues with objects that have null prototype\n * or have overridden the hasOwnProperty method.\n *\n * @param obj - The object to check\n * @param key - The property key to check for\n * @returns True if the object has the property as its own property, false otherwise\n * @example\n * ```ts\n * const obj = { name: 'Alice', age: 30 }\n * hasOwnProperty(obj, 'name') // true\n * hasOwnProperty(obj, 'toString') // false (inherited)\n * hasOwnProperty(obj, 'unknown') // false\n * ```\n * @internal\n */\nexport function hasOwnProperty(obj: object, key: string): boolean {\n\treturn Object.prototype.hasOwnProperty.call(obj, key)\n}\n\n/**\n * Safely gets an object's own property value (not inherited). Returns undefined if the property\n * doesn't exist as an own property. Provides type-safe access with proper TypeScript inference.\n *\n * @param obj - The object to get the property from\n * @param key - The property key to retrieve\n * @returns The property value if it exists as an own property, undefined otherwise\n * @example\n * ```ts\n * const user = { name: 'Alice', age: 30 }\n * const name = getOwnProperty(user, 'name') // 'Alice'\n * const missing = getOwnProperty(user, 'unknown') // undefined\n * const inherited = getOwnProperty(user, 'toString') // undefined (inherited)\n * ```\n * @internal\n */\nexport function getOwnProperty<K extends string, V>(\n\tobj: Partial<Record<K, V>>,\n\tkey: K\n): V | undefined\n/** @internal */\nexport function getOwnProperty<O extends object>(obj: O, key: string): O[keyof O] | undefined\n/** @internal */\nexport function getOwnProperty(obj: object, key: string): unknown\n/** @internal */\nexport function getOwnProperty(obj: object, key: string): unknown {\n\tif (!hasOwnProperty(obj, key)) {\n\t\treturn undefined\n\t}\n\t// @ts-expect-error we know the property exists\n\treturn obj[key]\n}\n\n/**\n * An alias for `Object.keys` that treats the object as a map and so preserves the type of the keys.\n * Unlike standard Object.keys which returns string[], this maintains the specific string literal types.\n *\n * @param object - The object to get keys from\n * @returns Array of keys with preserved string literal types\n * @example\n * ```ts\n * const config = { theme: 'dark', lang: 'en' } as const\n * const keys = objectMapKeys(config)\n * // keys is Array<'theme' | 'lang'> instead of string[]\n * ```\n * @internal\n */\nexport function objectMapKeys<Key extends string>(object: {\n\treadonly [K in Key]: unknown\n}): Array<Key> {\n\treturn Object.keys(object) as Key[]\n}\n\n/**\n * An alias for `Object.values` that treats the object as a map and so preserves the type of the\n * values. Unlike standard Object.values which returns unknown[], this maintains the specific value types.\n *\n * @param object - The object to get values from\n * @returns Array of values with preserved types\n * @example\n * ```ts\n * const scores = { alice: 85, bob: 92, charlie: 78 }\n * const values = objectMapValues(scores)\n * // values is Array<number> instead of unknown[]\n * ```\n * @internal\n */\nexport function objectMapValues<Key extends string, Value>(object: {\n\t[K in Key]: Value\n}): Array<Value> {\n\treturn Object.values(object) as Value[]\n}\n\n/**\n * An alias for `Object.entries` that treats the object as a map and so preserves the type of the\n * keys and values. Unlike standard Object.entries which returns `Array<[string, unknown]>`, this maintains specific types.\n *\n * @param object - The object to get entries from\n * @returns Array of key-value pairs with preserved types\n * @example\n * ```ts\n * const user = { name: 'Alice', age: 30 }\n * const entries = objectMapEntries(user)\n * // entries is Array<['name' | 'age', string | number]>\n * ```\n * @internal\n */\nexport function objectMapEntries<Obj extends object>(\n\tobject: Obj\n): Array<[keyof Obj, Obj[keyof Obj]]> {\n\treturn Object.entries(object) as [keyof Obj, Obj[keyof Obj]][]\n}\n\n/**\n * Returns the entries of an object as an iterable iterator.\n * Useful when working with large collections, to avoid allocating an array.\n * Only yields own properties (not inherited ones).\n *\n * @param object - The object to iterate over\n * @returns Iterator yielding key-value pairs with preserved types\n * @example\n * ```ts\n * const largeMap = { a: 1, b: 2, c: 3 } // Imagine thousands of entries\n * for (const [key, value] of objectMapEntriesIterable(largeMap)) {\n * // Process entries one at a time without creating a large array\n * console.log(key, value)\n * }\n * ```\n * @internal\n */\nexport function* objectMapEntriesIterable<Key extends string, Value>(object: {\n\t[K in Key]: Value\n}): IterableIterator<[Key, Value]> {\n\tfor (const key in object) {\n\t\tif (!Object.prototype.hasOwnProperty.call(object, key)) continue\n\t\tyield [key, object[key]]\n\t}\n}\n\n/**\n * An alias for `Object.fromEntries` that treats the object as a map and so preserves the type of the\n * keys and values. Creates an object from key-value pairs with proper TypeScript typing.\n *\n * @param entries - Array of key-value pairs to convert to an object\n * @returns Object with preserved key and value types\n * @example\n * ```ts\n * const pairs: Array<['name' | 'age', string | number]> = [['name', 'Alice'], ['age', 30]]\n * const obj = objectMapFromEntries(pairs)\n * // obj is { name: string | number, age: string | number }\n * ```\n * @internal\n */\nexport function objectMapFromEntries<Key extends string, Value>(\n\tentries: ReadonlyArray<readonly [Key, Value]>\n): { [K in Key]: Value } {\n\treturn Object.fromEntries(entries) as { [K in Key]: Value }\n}\n\n/**\n * Filters an object using a predicate function, returning a new object with only the entries\n * that pass the predicate. Optimized to return the original object if no changes are needed.\n *\n * @param object - The object to filter\n * @param predicate - Function that tests each key-value pair\n * @returns A new object with only the entries that pass the predicate, or the original object if unchanged\n * @example\n * ```ts\n * const scores = { alice: 85, bob: 92, charlie: 78 }\n * const passing = filterEntries(scores, (name, score) => score >= 80)\n * // { alice: 85, bob: 92 }\n * ```\n * @internal\n */\nexport function filterEntries<Key extends string, Value>(\n\tobject: { [K in Key]: Value },\n\tpredicate: (key: Key, value: Value) => boolean\n): { [K in Key]: Value } {\n\tconst result: { [K in Key]?: Value } = {}\n\tlet didChange = false\n\tfor (const key in object) {\n\t\tif (!Object.prototype.hasOwnProperty.call(object, key)) continue\n\t\tconst value = object[key]\n\t\tif (predicate(key, value)) {\n\t\t\tresult[key] = value\n\t\t} else {\n\t\t\tdidChange = true\n\t\t}\n\t}\n\treturn didChange ? (result as { [K in Key]: Value }) : object\n}\n\n/**\n * Maps the values of an object to new values using a mapper function, preserving keys.\n * The mapper function receives both the key and value for each entry.\n *\n * @param object - The object whose values to transform\n * @param mapper - Function that transforms each value (receives key and value)\n * @returns A new object with the same keys but transformed values\n * @example\n * ```ts\n * const prices = { apple: 1.50, banana: 0.75, orange: 2.00 }\n * const withTax = mapObjectMapValues(prices, (fruit, price) => price * 1.08)\n * // { apple: 1.62, banana: 0.81, orange: 2.16 }\n * ```\n * @internal\n */\nexport function mapObjectMapValues<Key extends string, ValueBefore, ValueAfter>(\n\tobject: { readonly [K in Key]: ValueBefore },\n\tmapper: (key: Key, value: ValueBefore) => ValueAfter\n): { [K in Key]: ValueAfter } {\n\tconst result = {} as { [K in Key]: ValueAfter }\n\tfor (const key in object) {\n\t\tif (!Object.prototype.hasOwnProperty.call(object, key)) continue\n\t\tresult[key] = mapper(key, object[key])\n\t}\n\treturn result\n}\n\n/**\n * Performs a shallow equality check between two objects. Compares all enumerable own properties\n * using Object.is for value comparison. Returns true if both objects have the same keys and values.\n *\n * @param obj1 - First object to compare\n * @param obj2 - Second object to compare\n * @returns True if objects are shallow equal, false otherwise\n * @example\n * ```ts\n * const a = { x: 1, y: 2 }\n * const b = { x: 1, y: 2 }\n * const c = { x: 1, y: 3 }\n * areObjectsShallowEqual(a, b) // true\n * areObjectsShallowEqual(a, c) // false\n * areObjectsShallowEqual(a, a) // true (same reference)\n * ```\n * @internal\n */\nexport function areObjectsShallowEqual<T extends object>(obj1: T, obj2: T): boolean {\n\tif (obj1 === obj2) return true\n\tconst keys1 = Object.keys(obj1)\n\tif (keys1.length !== Object.keys(obj2).length) return false\n\tfor (const key of keys1) {\n\t\tif (!hasOwnProperty(obj2, key)) return false\n\t\tif (!Object.is((obj1 as any)[key], (obj2 as any)[key])) return false\n\t}\n\treturn true\n}\n\n/**\n * Groups an array of values into a record by a key extracted from each value.\n * The key selector function is called for each element to determine the grouping key.\n *\n * @param array - The array to group\n * @param keySelector - Function that extracts the grouping key from each value\n * @returns A record where keys are the extracted keys and values are arrays of grouped items\n * @example\n * ```ts\n * const people = [\n * { name: 'Alice', age: 25 },\n * { name: 'Bob', age: 30 },\n * { name: 'Charlie', age: 25 }\n * ]\n * const byAge = groupBy(people, person => `age-${person.age}`)\n * // { 'age-25': [Alice, Charlie], 'age-30': [Bob] }\n * ```\n * @internal\n */\nexport function groupBy<K extends string, V>(\n\tarray: ReadonlyArray<V>,\n\tkeySelector: (value: V) => K\n): Record<K, V[]> {\n\tconst result: Record<K, V[]> = {} as any\n\tfor (const value of array) {\n\t\tconst key = keySelector(value)\n\t\tif (!result[key]) result[key] = []\n\t\tresult[key].push(value)\n\t}\n\treturn result\n}\n\n/**\n * Creates a new object with specified keys omitted from the original object.\n * Uses shallow copying and then deletes the unwanted keys.\n *\n * @param obj - The source object\n * @param keys - Array of key names to omit from the result\n * @returns A new object without the specified keys\n * @example\n * ```ts\n * const user = { id: '123', name: 'Alice', password: 'secret', email: 'alice@example.com' }\n * const publicUser = omit(user, ['password'])\n * // { id: '123', name: 'Alice', email: 'alice@example.com' }\n * ```\n * @internal\n */\nexport function omit(\n\tobj: Record<string, unknown>,\n\tkeys: ReadonlyArray<string>\n): Record<string, unknown> {\n\tconst result = { ...obj }\n\tfor (const key of keys) {\n\t\tdelete result[key]\n\t}\n\treturn result\n}\n\n/**\n * Compares two objects and returns an array of keys where the values differ.\n * Uses Object.is for comparison, which handles NaN and -0/+0 correctly.\n * Only checks keys present in the first object.\n *\n * @param obj1 - The first object (keys to check come from this object)\n * @param obj2 - The second object to compare against\n * @returns Array of keys where values differ between the objects\n * @example\n * ```ts\n * const before = { name: 'Alice', age: 25, city: 'NYC' }\n * const after = { name: 'Alice', age: 26, city: 'NYC' }\n * const changed = getChangedKeys(before, after)\n * // ['age']\n * ```\n * @internal\n */\nexport function getChangedKeys<T extends object>(obj1: T, obj2: T): (keyof T)[] {\n\tconst result: (keyof T)[] = []\n\tfor (const key in obj1) {\n\t\tif (!Object.is(obj1[key], obj2[key])) {\n\t\t\tresult.push(key)\n\t\t}\n\t}\n\treturn result\n}\n\n/**\n * Deep equality comparison that allows for floating-point precision errors.\n * Numbers are considered equal if they differ by less than the threshold.\n * Uses lodash.isequalwith internally for the deep comparison logic.\n *\n * @param obj1 - First object to compare\n * @param obj2 - Second object to compare\n * @param threshold - Maximum difference allowed between numbers (default: 0.000001)\n * @returns True if objects are deeply equal with floating-point tolerance\n * @example\n * ```ts\n * const a = { x: 0.1 + 0.2 } // 0.30000000000000004\n * const b = { x: 0.3 }\n * isEqualAllowingForFloatingPointErrors(a, b) // true\n *\n * const c = { coords: [1.0000001, 2.0000001] }\n * const d = { coords: [1.0000002, 2.0000002] }\n * isEqualAllowingForFloatingPointErrors(c, d) // true\n * ```\n * @internal\n */\nexport function isEqualAllowingForFloatingPointErrors(\n\tobj1: object,\n\tobj2: object,\n\tthreshold = 0.000001\n): boolean {\n\treturn isEqualWith(obj1, obj2, (value1, value2) => {\n\t\tif (typeof value1 === 'number' && typeof value2 === 'number') {\n\t\t\treturn Math.abs(value1 - value2) < threshold\n\t\t}\n\t\treturn undefined\n\t})\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["isEqualWith"]
|
|
7
7
|
}
|
package/dist-cjs/lib/throttle.js
CHANGED
|
@@ -18,91 +18,135 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
18
18
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
19
|
var throttle_exports = {};
|
|
20
20
|
__export(throttle_exports, {
|
|
21
|
+
FpsScheduler: () => FpsScheduler,
|
|
21
22
|
fpsThrottle: () => fpsThrottle,
|
|
22
23
|
throttleToNextFrame: () => throttleToNextFrame
|
|
23
24
|
});
|
|
24
25
|
module.exports = __toCommonJS(throttle_exports);
|
|
25
26
|
const isTest = () => typeof process !== "undefined" && process.env.NODE_ENV === "test" && // @ts-expect-error
|
|
26
27
|
!globalThis.__FORCE_RAF_IN_TESTS__;
|
|
27
|
-
const
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
28
|
+
const timingVarianceFactor = 0.9;
|
|
29
|
+
const getTargetTimePerFrame = (targetFps) => Math.floor(1e3 / targetFps) * timingVarianceFactor;
|
|
30
|
+
class FpsScheduler {
|
|
31
|
+
targetFps;
|
|
32
|
+
targetTimePerFrame;
|
|
33
|
+
fpsQueue = [];
|
|
34
|
+
frameRaf;
|
|
35
|
+
flushRaf;
|
|
36
|
+
lastFlushTime;
|
|
37
|
+
constructor(targetFps = 120) {
|
|
38
|
+
this.targetFps = targetFps;
|
|
39
|
+
this.targetTimePerFrame = getTargetTimePerFrame(targetFps);
|
|
40
|
+
this.lastFlushTime = -this.targetTimePerFrame;
|
|
37
41
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
if (elapsed < targetTimePerFrame) {
|
|
44
|
-
frameRaf = requestAnimationFrame(() => {
|
|
45
|
-
frameRaf = void 0;
|
|
46
|
-
tick(true);
|
|
47
|
-
});
|
|
48
|
-
return;
|
|
42
|
+
updateTargetFps(targetFps) {
|
|
43
|
+
if (targetFps === this.targetFps) return;
|
|
44
|
+
this.targetFps = targetFps;
|
|
45
|
+
this.targetTimePerFrame = getTargetTimePerFrame(targetFps);
|
|
46
|
+
this.lastFlushTime = -this.targetTimePerFrame;
|
|
49
47
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
if (flushRaf) return;
|
|
56
|
-
flushRaf = requestAnimationFrame(() => {
|
|
57
|
-
flushRaf = void 0;
|
|
58
|
-
lastFlushTime = now;
|
|
59
|
-
flush();
|
|
60
|
-
});
|
|
48
|
+
flush() {
|
|
49
|
+
const queue = this.fpsQueue.splice(0, this.fpsQueue.length);
|
|
50
|
+
for (const fn of queue) {
|
|
51
|
+
fn();
|
|
52
|
+
}
|
|
61
53
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
frameRaf = void 0;
|
|
54
|
+
tick(isOnNextFrame = false) {
|
|
55
|
+
if (this.frameRaf) return;
|
|
56
|
+
const now = Date.now();
|
|
57
|
+
const elapsed = now - this.lastFlushTime;
|
|
58
|
+
if (elapsed < this.targetTimePerFrame) {
|
|
59
|
+
this.frameRaf = requestAnimationFrame(() => {
|
|
60
|
+
this.frameRaf = void 0;
|
|
61
|
+
this.tick(true);
|
|
62
|
+
});
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
if (isOnNextFrame) {
|
|
66
|
+
if (this.flushRaf) return;
|
|
67
|
+
this.lastFlushTime = now;
|
|
68
|
+
this.flush();
|
|
69
|
+
} else {
|
|
70
|
+
if (this.flushRaf) return;
|
|
71
|
+
this.flushRaf = requestAnimationFrame(() => {
|
|
72
|
+
this.flushRaf = void 0;
|
|
73
|
+
this.lastFlushTime = Date.now();
|
|
74
|
+
this.flush();
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Creates a throttled version of a function that executes at most once per frame.
|
|
80
|
+
* The default target frame rate is set by the FpsScheduler instance.
|
|
81
|
+
* Subsequent calls within the same frame are ignored, ensuring smooth performance
|
|
82
|
+
* for high-frequency events like mouse movements or scroll events.
|
|
83
|
+
*
|
|
84
|
+
* @param fn - The function to throttle, optionally with a cancel method
|
|
85
|
+
* @returns A throttled function with an optional cancel method to remove pending calls
|
|
86
|
+
*
|
|
87
|
+
* @public
|
|
88
|
+
*/
|
|
89
|
+
fpsThrottle(fn) {
|
|
90
|
+
if (isTest()) {
|
|
91
|
+
fn.cancel = () => {
|
|
92
|
+
if (this.frameRaf) {
|
|
93
|
+
cancelAnimationFrame(this.frameRaf);
|
|
94
|
+
this.frameRaf = void 0;
|
|
95
|
+
}
|
|
96
|
+
if (this.flushRaf) {
|
|
97
|
+
cancelAnimationFrame(this.flushRaf);
|
|
98
|
+
this.flushRaf = void 0;
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
return fn;
|
|
102
|
+
}
|
|
103
|
+
const throttledFn = () => {
|
|
104
|
+
if (this.fpsQueue.includes(fn)) {
|
|
105
|
+
return;
|
|
69
106
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
107
|
+
this.fpsQueue.push(fn);
|
|
108
|
+
this.tick();
|
|
109
|
+
};
|
|
110
|
+
throttledFn.cancel = () => {
|
|
111
|
+
const index = this.fpsQueue.indexOf(fn);
|
|
112
|
+
if (index > -1) {
|
|
113
|
+
this.fpsQueue.splice(index, 1);
|
|
73
114
|
}
|
|
74
115
|
};
|
|
75
|
-
return
|
|
116
|
+
return throttledFn;
|
|
76
117
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
118
|
+
/**
|
|
119
|
+
* Schedules a function to execute on the next animation frame.
|
|
120
|
+
* If the same function is passed multiple times before the frame executes,
|
|
121
|
+
* it will only be called once, effectively batching multiple calls.
|
|
122
|
+
*
|
|
123
|
+
* @param fn - The function to execute on the next frame
|
|
124
|
+
* @returns A cancel function that can prevent execution if called before the next frame
|
|
125
|
+
*
|
|
126
|
+
* @public
|
|
127
|
+
*/
|
|
128
|
+
throttleToNextFrame(fn) {
|
|
129
|
+
if (isTest()) {
|
|
130
|
+
fn();
|
|
131
|
+
return () => void 0;
|
|
80
132
|
}
|
|
81
|
-
fpsQueue.
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
throttledFn.cancel = () => {
|
|
85
|
-
const index = fpsQueue.indexOf(fn);
|
|
86
|
-
if (index > -1) {
|
|
87
|
-
fpsQueue.splice(index, 1);
|
|
133
|
+
if (!this.fpsQueue.includes(fn)) {
|
|
134
|
+
this.fpsQueue.push(fn);
|
|
135
|
+
this.tick();
|
|
88
136
|
}
|
|
89
|
-
|
|
90
|
-
|
|
137
|
+
return () => {
|
|
138
|
+
const index = this.fpsQueue.indexOf(fn);
|
|
139
|
+
if (index > -1) {
|
|
140
|
+
this.fpsQueue.splice(index, 1);
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
const defaultScheduler = new FpsScheduler(120);
|
|
146
|
+
function fpsThrottle(fn) {
|
|
147
|
+
return defaultScheduler.fpsThrottle(fn);
|
|
91
148
|
}
|
|
92
149
|
function throttleToNextFrame(fn) {
|
|
93
|
-
|
|
94
|
-
fn();
|
|
95
|
-
return () => void 0;
|
|
96
|
-
}
|
|
97
|
-
if (!fpsQueue.includes(fn)) {
|
|
98
|
-
fpsQueue.push(fn);
|
|
99
|
-
tick();
|
|
100
|
-
}
|
|
101
|
-
return () => {
|
|
102
|
-
const index = fpsQueue.indexOf(fn);
|
|
103
|
-
if (index > -1) {
|
|
104
|
-
fpsQueue.splice(index, 1);
|
|
105
|
-
}
|
|
106
|
-
};
|
|
150
|
+
return defaultScheduler.throttleToNextFrame(fn);
|
|
107
151
|
}
|
|
108
152
|
//# sourceMappingURL=throttle.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/lib/throttle.ts"],
|
|
4
|
-
"sourcesContent": ["const isTest = () =>\n\ttypeof process !== 'undefined' &&\n\tprocess.env.NODE_ENV === 'test' &&\n\t// @ts-expect-error\n\t!globalThis.__FORCE_RAF_IN_TESTS__\n\
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAAM,SAAS,MACd,OAAO,YAAY,eACnB,QAAQ,IAAI,aAAa;AAEzB,CAAC,WAAW;
|
|
4
|
+
"sourcesContent": ["const isTest = () =>\n\ttypeof process !== 'undefined' &&\n\tprocess.env.NODE_ENV === 'test' &&\n\t// @ts-expect-error\n\t!globalThis.__FORCE_RAF_IN_TESTS__\n\n// Browsers aren't precise with frame timing - this factor prevents skipping frames unnecessarily\n// by aiming slightly below the theoretical frame duration (e.g., ~7.5ms instead of 8.33ms for 120fps)\nconst timingVarianceFactor = 0.9\nconst getTargetTimePerFrame = (targetFps: number) =>\n\tMath.floor(1000 / targetFps) * timingVarianceFactor\n\n/**\n * A scheduler class that manages a queue of functions to be executed at a target frame rate.\n * Each instance maintains its own queue and state, allowing for separate throttling contexts\n * (e.g., UI operations vs network sync operations).\n *\n * @public\n */\nexport class FpsScheduler {\n\tprivate targetFps: number\n\tprivate targetTimePerFrame: number\n\tprivate fpsQueue: Array<() => void> = []\n\tprivate frameRaf: undefined | number\n\tprivate flushRaf: undefined | number\n\tprivate lastFlushTime: number\n\n\tconstructor(targetFps: number = 120) {\n\t\tthis.targetFps = targetFps\n\t\tthis.targetTimePerFrame = getTargetTimePerFrame(targetFps)\n\t\tthis.lastFlushTime = -this.targetTimePerFrame\n\t}\n\n\tupdateTargetFps(targetFps: number) {\n\t\tif (targetFps === this.targetFps) return\n\t\tthis.targetFps = targetFps\n\t\tthis.targetTimePerFrame = getTargetTimePerFrame(targetFps)\n\t\tthis.lastFlushTime = -this.targetTimePerFrame\n\t}\n\n\tprivate flush() {\n\t\tconst queue = this.fpsQueue.splice(0, this.fpsQueue.length)\n\t\tfor (const fn of queue) {\n\t\t\tfn()\n\t\t}\n\t}\n\n\tprivate tick(isOnNextFrame = false) {\n\t\tif (this.frameRaf) return\n\n\t\tconst now = Date.now()\n\t\tconst elapsed = now - this.lastFlushTime\n\n\t\tif (elapsed < this.targetTimePerFrame) {\n\t\t\t// If we're too early to flush, we need to wait until the next frame to try and flush again.\n\t\t\t// eslint-disable-next-line no-restricted-globals\n\t\t\tthis.frameRaf = requestAnimationFrame(() => {\n\t\t\t\tthis.frameRaf = undefined\n\t\t\t\tthis.tick(true)\n\t\t\t})\n\t\t\treturn\n\t\t}\n\n\t\tif (isOnNextFrame) {\n\t\t\t// If we've already waited for the next frame to run the tick, then we can flush immediately\n\t\t\tif (this.flushRaf) return // ...though if there's a flush raf, that means we'll be flushing on this frame already, so we can do nothing here.\n\t\t\tthis.lastFlushTime = now\n\t\t\tthis.flush()\n\t\t} else {\n\t\t\t// If we haven't already waited for the next frame to run the tick, we need to wait until the next frame to flush.\n\t\t\tif (this.flushRaf) return // ...though if there's a flush raf, that means we'll be flushing on the next frame already, so we can do nothing here.\n\t\t\t// eslint-disable-next-line no-restricted-globals\n\t\t\tthis.flushRaf = requestAnimationFrame(() => {\n\t\t\t\tthis.flushRaf = undefined\n\t\t\t\tthis.lastFlushTime = Date.now()\n\t\t\t\tthis.flush()\n\t\t\t})\n\t\t}\n\t}\n\n\t/**\n\t * Creates a throttled version of a function that executes at most once per frame.\n\t * The default target frame rate is set by the FpsScheduler instance.\n\t * Subsequent calls within the same frame are ignored, ensuring smooth performance\n\t * for high-frequency events like mouse movements or scroll events.\n\t *\n\t * @param fn - The function to throttle, optionally with a cancel method\n\t * @returns A throttled function with an optional cancel method to remove pending calls\n\t *\n\t * @public\n\t */\n\tfpsThrottle(fn: { (): void; cancel?(): void }): {\n\t\t(): void\n\t\tcancel?(): void\n\t} {\n\t\tif (isTest()) {\n\t\t\tfn.cancel = () => {\n\t\t\t\tif (this.frameRaf) {\n\t\t\t\t\tcancelAnimationFrame(this.frameRaf)\n\t\t\t\t\tthis.frameRaf = undefined\n\t\t\t\t}\n\t\t\t\tif (this.flushRaf) {\n\t\t\t\t\tcancelAnimationFrame(this.flushRaf)\n\t\t\t\t\tthis.flushRaf = undefined\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn fn\n\t\t}\n\n\t\tconst throttledFn = () => {\n\t\t\tif (this.fpsQueue.includes(fn)) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.fpsQueue.push(fn)\n\t\t\tthis.tick()\n\t\t}\n\t\tthrottledFn.cancel = () => {\n\t\t\tconst index = this.fpsQueue.indexOf(fn)\n\t\t\tif (index > -1) {\n\t\t\t\tthis.fpsQueue.splice(index, 1)\n\t\t\t}\n\t\t}\n\t\treturn throttledFn\n\t}\n\n\t/**\n\t * Schedules a function to execute on the next animation frame.\n\t * If the same function is passed multiple times before the frame executes,\n\t * it will only be called once, effectively batching multiple calls.\n\t *\n\t * @param fn - The function to execute on the next frame\n\t * @returns A cancel function that can prevent execution if called before the next frame\n\t *\n\t * @public\n\t */\n\tthrottleToNextFrame(fn: () => void): () => void {\n\t\tif (isTest()) {\n\t\t\tfn()\n\t\t\treturn () => void null // noop\n\t\t}\n\n\t\tif (!this.fpsQueue.includes(fn)) {\n\t\t\tthis.fpsQueue.push(fn)\n\t\t\tthis.tick()\n\t\t}\n\n\t\treturn () => {\n\t\t\tconst index = this.fpsQueue.indexOf(fn)\n\t\t\tif (index > -1) {\n\t\t\t\tthis.fpsQueue.splice(index, 1)\n\t\t\t}\n\t\t}\n\t}\n}\n\n// Default instance for UI operations\nconst defaultScheduler = new FpsScheduler(120)\n\n/**\n * Creates a throttled version of a function that executes at most once per frame.\n * The default target frame rate is 120fps, but can be customized per function.\n * Subsequent calls within the same frame are ignored, ensuring smooth performance\n * for high-frequency events like mouse movements or scroll events.\n *\n * Uses the default throttle instance for UI operations. If you need a separate\n * throttling queue (e.g., for network operations), create your own Throttle instance.\n *\n * @param fn - The function to throttle, optionally with a cancel method\n * @returns A throttled function with an optional cancel method to remove pending calls\n *\n * @example\n * ```ts\n * // Default 120fps throttling\n * const updateCanvas = fpsThrottle(() => {\n * // This will run at most once per frame (~8.33ms)\n * redrawCanvas()\n * })\n *\n * // Call as often as you want - automatically throttled to 120fps\n * document.addEventListener('mousemove', updateCanvas)\n *\n * // Cancel pending calls if needed\n * updateCanvas.cancel?.()\n * ```\n *\n * @internal\n */\nexport function fpsThrottle(fn: { (): void; cancel?(): void }): {\n\t(): void\n\tcancel?(): void\n} {\n\treturn defaultScheduler.fpsThrottle(fn)\n}\n\n/**\n * Schedules a function to execute on the next animation frame, targeting 120fps.\n * If the same function is passed multiple times before the frame executes,\n * it will only be called once, effectively batching multiple calls.\n *\n * Uses the default throttle instance for UI operations.\n *\n * @param fn - The function to execute on the next frame\n * @returns A cancel function that can prevent execution if called before the next frame\n *\n * @example\n * ```ts\n * const updateUI = throttleToNextFrame(() => {\n * // Batches multiple calls into the next animation frame\n * updateStatusBar()\n * refreshToolbar()\n * })\n *\n * // Multiple calls within the same frame are batched\n * updateUI() // Will execute\n * updateUI() // Ignored (same function already queued)\n * updateUI() // Ignored (same function already queued)\n *\n * // Get cancel function to prevent execution\n * const cancel = updateUI()\n * cancel() // Prevents execution if called before next frame\n * ```\n *\n * @internal\n */\nexport function throttleToNextFrame(fn: () => void): () => void {\n\treturn defaultScheduler.throttleToNextFrame(fn)\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|