@tldraw/utils 4.6.0-next.5a871ec02ff3 → 4.6.0-next.6594d48ace27

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 (51) hide show
  1. package/dist-cjs/index.d.ts +20 -1
  2. package/dist-cjs/index.js +3 -1
  3. package/dist-cjs/index.js.map +2 -2
  4. package/dist-cjs/lib/ExecutionQueue.js +1 -0
  5. package/dist-cjs/lib/ExecutionQueue.js.map +1 -1
  6. package/dist-cjs/lib/LruCache.js +52 -0
  7. package/dist-cjs/lib/LruCache.js.map +7 -0
  8. package/dist-cjs/lib/PerformanceTracker.js +1 -1
  9. package/dist-cjs/lib/PerformanceTracker.js.map +1 -1
  10. package/dist-cjs/lib/bind.js.map +1 -1
  11. package/dist-cjs/lib/debounce.js +1 -0
  12. package/dist-cjs/lib/debounce.js.map +2 -2
  13. package/dist-cjs/lib/network.js.map +1 -1
  14. package/dist-cjs/lib/retry.js.map +2 -2
  15. package/dist-cjs/lib/storage.js.map +1 -1
  16. package/dist-cjs/lib/timers.js.map +1 -1
  17. package/dist-cjs/lib/types.js.map +1 -1
  18. package/dist-cjs/lib/version.js.map +1 -1
  19. package/dist-esm/index.d.mts +20 -1
  20. package/dist-esm/index.mjs +3 -1
  21. package/dist-esm/index.mjs.map +2 -2
  22. package/dist-esm/lib/ExecutionQueue.mjs +1 -0
  23. package/dist-esm/lib/ExecutionQueue.mjs.map +1 -1
  24. package/dist-esm/lib/LruCache.mjs +32 -0
  25. package/dist-esm/lib/LruCache.mjs.map +7 -0
  26. package/dist-esm/lib/PerformanceTracker.mjs +1 -1
  27. package/dist-esm/lib/PerformanceTracker.mjs.map +1 -1
  28. package/dist-esm/lib/bind.mjs.map +1 -1
  29. package/dist-esm/lib/debounce.mjs +1 -0
  30. package/dist-esm/lib/debounce.mjs.map +2 -2
  31. package/dist-esm/lib/network.mjs.map +1 -1
  32. package/dist-esm/lib/retry.mjs.map +2 -2
  33. package/dist-esm/lib/storage.mjs.map +1 -1
  34. package/dist-esm/lib/timers.mjs.map +1 -1
  35. package/dist-esm/lib/version.mjs.map +1 -1
  36. package/package.json +1 -1
  37. package/src/index.ts +2 -0
  38. package/src/lib/ExecutionQueue.test.ts +1 -1
  39. package/src/lib/LruCache.test.ts +89 -0
  40. package/src/lib/LruCache.ts +32 -0
  41. package/src/lib/PerformanceTracker.test.ts +1 -1
  42. package/src/lib/PerformanceTracker.ts +1 -1
  43. package/src/lib/bind.ts +1 -1
  44. package/src/lib/debounce.ts +5 -1
  45. package/src/lib/network.ts +2 -2
  46. package/src/lib/retry.ts +1 -0
  47. package/src/lib/storage.test.ts +2 -0
  48. package/src/lib/storage.tsx +1 -1
  49. package/src/lib/timers.ts +1 -1
  50. package/src/lib/types.ts +8 -0
  51. package/src/lib/version.ts +1 -1
@@ -13,6 +13,14 @@ import { default as uniq } from 'lodash.uniq';
13
13
 
14
14
  /* Excluded from this release type: assertExists */
15
15
 
16
+ /**
17
+ * A value that may be returned synchronously or as a `Promise` / `PromiseLike`.
18
+ * Use with `await` or `Promise.resolve(...)` to normalize to a single `Promise`.
19
+ *
20
+ * @public
21
+ */
22
+ export declare type Awaitable<T> = PromiseLike<T> | T;
23
+
16
24
  /**
17
25
  * Decorator that binds a method to its class instance (legacy stage-2 TypeScript decorators).
18
26
  * When applied to a class method, ensures `this` always refers to the class instance,
@@ -109,7 +117,7 @@ export declare function bind<This extends object, T extends (...args: any[]) =>
109
117
  * @public
110
118
  * @see source - https://gist.github.com/ca0v/73a31f57b397606c9813472f7493a940
111
119
  */
112
- export declare function debounce<T extends unknown[], U>(callback: (...args: T) => PromiseLike<U> | U, wait: number): {
120
+ export declare function debounce<T extends unknown[], U>(callback: (...args: T) => Awaitable<U>, wait: number): {
113
121
  (...args: T): Promise<U>;
114
122
  cancel: () => void;
115
123
  };
@@ -878,6 +886,17 @@ export declare function lerp(a: number, b: number, t: number): number;
878
886
  */
879
887
  export declare function lns(str: string): string;
880
888
 
889
+ /** Simple LRU cache backed by a Map's insertion-order iteration. @public */
890
+ export declare class LruCache<K, V> {
891
+ private maxSize;
892
+ private map;
893
+ constructor(maxSize: number);
894
+ get(key: K): undefined | V;
895
+ set(key: K, value: V): void;
896
+ has(key: K): boolean;
897
+ get size(): number;
898
+ }
899
+
881
900
  /**
882
901
  * Automatically makes properties optional if their type includes `undefined`.
883
902
  * This transforms properties like `prop: string | undefined` to `prop?: string | undefined`,
package/dist-cjs/index.js CHANGED
@@ -36,6 +36,7 @@ __export(index_exports, {
36
36
  FileHelpers: () => import_file.FileHelpers,
37
37
  FpsScheduler: () => import_throttle.FpsScheduler,
38
38
  Image: () => import_network.Image,
39
+ LruCache: () => import_LruCache.LruCache,
39
40
  MediaHelpers: () => import_media.MediaHelpers,
40
41
  PerformanceTracker: () => import_PerformanceTracker.PerformanceTracker,
41
42
  PngHelpers: () => import_png.PngHelpers,
@@ -149,6 +150,7 @@ var import_function = require("./lib/function");
149
150
  var import_hash = require("./lib/hash");
150
151
  var import_id = require("./lib/id");
151
152
  var import_iterable = require("./lib/iterable");
153
+ var import_LruCache = require("./lib/LruCache");
152
154
  var import_media = require("./lib/media/media");
153
155
  var import_png = require("./lib/media/png");
154
156
  var import_network = require("./lib/network");
@@ -169,7 +171,7 @@ var import_version2 = require("./lib/version");
169
171
  var import_warn = require("./lib/warn");
170
172
  (0, import_version.registerTldrawLibraryVersion)(
171
173
  "@tldraw/utils",
172
- "4.6.0-next.5a871ec02ff3",
174
+ "4.6.0-next.6594d48ace27",
173
175
  "cjs"
174
176
  );
175
177
  //# sourceMappingURL=index.js.map
@@ -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 { 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;",
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 { LruCache } from './lib/LruCache'\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 Awaitable,\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;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;AACrC,sBAAyB;AAEzB,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;AAQvB,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
  }
@@ -43,6 +43,7 @@ class ExecutionQueue {
43
43
  constructor(timeout) {
44
44
  this.timeout = timeout;
45
45
  }
46
+ timeout;
46
47
  queue = [];
47
48
  running = false;
48
49
  /**
@@ -2,6 +2,6 @@
2
2
  "version": 3,
3
3
  "sources": ["../../src/lib/ExecutionQueue.ts"],
4
4
  "sourcesContent": ["import { sleep } from './control'\n\n/**\n * A queue that executes tasks sequentially with optional delay between tasks.\n *\n * ExecutionQueue ensures that tasks are executed one at a time in the order they were added,\n * with an optional timeout delay between each task execution. This is useful for rate limiting,\n * preventing race conditions, or controlling the flow of asynchronous operations.\n *\n * @example\n * ```ts\n * // Create a queue with 100ms delay between tasks\n * const queue = new ExecutionQueue(100)\n *\n * // Add tasks to the queue\n * const result1 = await queue.push(() => fetch('/api/data'))\n * const result2 = await queue.push(async () => {\n * const data = await processData()\n * return data\n * })\n *\n * // Check if queue is empty\n * if (queue.isEmpty()) {\n * console.log('All tasks completed')\n * }\n *\n * // Clean up\n * queue.close()\n * ```\n *\n * @internal\n */\nexport class ExecutionQueue {\n\tprivate queue: (() => Promise<any>)[] = []\n\tprivate running = false\n\n\t/**\n\t * Creates a new ExecutionQueue.\n\t *\n\t * Creates a new execution queue that will process tasks sequentially.\n\t * If a timeout is provided, there will be a delay between each task execution,\n\t * which is useful for rate limiting or controlling execution flow.\n\t *\n\t * timeout - Optional delay in milliseconds between task executions\n\t * @example\n\t * ```ts\n\t * // Create queue without delay\n\t * const fastQueue = new ExecutionQueue()\n\t *\n\t * // Create queue with 500ms delay between tasks\n\t * const slowQueue = new ExecutionQueue(500)\n\t * ```\n\t */\n\tconstructor(private readonly timeout?: number) {}\n\n\t/**\n\t * Checks if the queue is empty and not currently running a task.\n\t *\n\t * Determines whether the execution queue has completed all tasks and is idle.\n\t * Returns true only when there are no pending tasks in the queue AND no task is currently being executed.\n\t *\n\t * @returns True if the queue has no pending tasks and is not currently executing\n\t * @example\n\t * ```ts\n\t * const queue = new ExecutionQueue()\n\t *\n\t * console.log(queue.isEmpty()) // true - queue is empty\n\t *\n\t * queue.push(() => console.log('task'))\n\t * console.log(queue.isEmpty()) // false - task is running/pending\n\t * ```\n\t */\n\tisEmpty() {\n\t\treturn this.queue.length === 0 && !this.running\n\t}\n\n\tprivate async run() {\n\t\tif (this.running) return\n\t\ttry {\n\t\t\tthis.running = true\n\t\t\twhile (this.queue.length) {\n\t\t\t\tconst task = this.queue.shift()!\n\t\t\t\tawait task()\n\t\t\t\tif (this.timeout) {\n\t\t\t\t\tawait sleep(this.timeout)\n\t\t\t\t}\n\t\t\t}\n\t\t} finally {\n\t\t\t// this try/finally should not be needed because the tasks don't throw\n\t\t\t// but better safe than sorry\n\t\t\t// console.log('\\n\\n\\nrunning false\\n\\n\\n')\n\t\t\tthis.running = false\n\t\t}\n\t}\n\n\t/**\n\t * Adds a task to the queue and returns a promise that resolves with the task's result.\n\t *\n\t * Enqueues a task for sequential execution. The task will be executed after all\n\t * previously queued tasks have completed. If a timeout was specified in the constructor,\n\t * there will be a delay between this task and the next one.\n\t *\n\t * @param task - The function to execute (can be sync or async)\n\t * @returns Promise that resolves with the task's return value\n\t * @example\n\t * ```ts\n\t * const queue = new ExecutionQueue(100)\n\t *\n\t * // Add async task\n\t * const result = await queue.push(async () => {\n\t * const response = await fetch('/api/data')\n\t * return response.json()\n\t * })\n\t *\n\t * // Add sync task\n\t * const number = await queue.push(() => 42)\n\t * ```\n\t */\n\tasync push<T>(task: () => T): Promise<Awaited<T>> {\n\t\treturn new Promise<Awaited<T>>((resolve, reject) => {\n\t\t\tthis.queue.push(() => Promise.resolve(task()).then(resolve).catch(reject))\n\t\t\tthis.run()\n\t\t})\n\t}\n\n\t/**\n\t * Clears all pending tasks from the queue.\n\t *\n\t * Immediately removes all pending tasks from the queue. Any currently\n\t * running task will complete normally, but no additional tasks will be executed.\n\t * This method does not wait for the current task to finish.\n\t *\n\t * @returns void\n\t * @example\n\t * ```ts\n\t * const queue = new ExecutionQueue()\n\t *\n\t * // Add several tasks\n\t * queue.push(() => console.log('task 1'))\n\t * queue.push(() => console.log('task 2'))\n\t * queue.push(() => console.log('task 3'))\n\t *\n\t * // Clear all pending tasks\n\t * queue.close()\n\t * // Only 'task 1' will execute if it was already running\n\t * ```\n\t */\n\tclose() {\n\t\tthis.queue = []\n\t}\n}\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qBAAsB;AAgCf,MAAM,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAqB3B,YAA6B,SAAkB;AAAlB;AAAA,EAAmB;AAAA,EApBxC,QAAgC,CAAC;AAAA,EACjC,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAsClB,UAAU;AACT,WAAO,KAAK,MAAM,WAAW,KAAK,CAAC,KAAK;AAAA,EACzC;AAAA,EAEA,MAAc,MAAM;AACnB,QAAI,KAAK,QAAS;AAClB,QAAI;AACH,WAAK,UAAU;AACf,aAAO,KAAK,MAAM,QAAQ;AACzB,cAAM,OAAO,KAAK,MAAM,MAAM;AAC9B,cAAM,KAAK;AACX,YAAI,KAAK,SAAS;AACjB,oBAAM,sBAAM,KAAK,OAAO;AAAA,QACzB;AAAA,MACD;AAAA,IACD,UAAE;AAID,WAAK,UAAU;AAAA,IAChB;AAAA,EACD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAyBA,MAAM,KAAQ,MAAoC;AACjD,WAAO,IAAI,QAAoB,CAAC,SAAS,WAAW;AACnD,WAAK,MAAM,KAAK,MAAM,QAAQ,QAAQ,KAAK,CAAC,EAAE,KAAK,OAAO,EAAE,MAAM,MAAM,CAAC;AACzE,WAAK,IAAI;AAAA,IACV,CAAC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAwBA,QAAQ;AACP,SAAK,QAAQ,CAAC;AAAA,EACf;AACD;",
5
+ "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qBAAsB;AAgCf,MAAM,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAqB3B,YAA6B,SAAkB;AAAlB;AAAA,EAAmB;AAAA,EAAnB;AAAA,EApBrB,QAAgC,CAAC;AAAA,EACjC,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAsClB,UAAU;AACT,WAAO,KAAK,MAAM,WAAW,KAAK,CAAC,KAAK;AAAA,EACzC;AAAA,EAEA,MAAc,MAAM;AACnB,QAAI,KAAK,QAAS;AAClB,QAAI;AACH,WAAK,UAAU;AACf,aAAO,KAAK,MAAM,QAAQ;AACzB,cAAM,OAAO,KAAK,MAAM,MAAM;AAC9B,cAAM,KAAK;AACX,YAAI,KAAK,SAAS;AACjB,oBAAM,sBAAM,KAAK,OAAO;AAAA,QACzB;AAAA,MACD;AAAA,IACD,UAAE;AAID,WAAK,UAAU;AAAA,IAChB;AAAA,EACD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAyBA,MAAM,KAAQ,MAAoC;AACjD,WAAO,IAAI,QAAoB,CAAC,SAAS,WAAW;AACnD,WAAK,MAAM,KAAK,MAAM,QAAQ,QAAQ,KAAK,CAAC,EAAE,KAAK,OAAO,EAAE,MAAM,MAAM,CAAC;AACzE,WAAK,IAAI;AAAA,IACV,CAAC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAwBA,QAAQ;AACP,SAAK,QAAQ,CAAC;AAAA,EACf;AACD;",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var LruCache_exports = {};
20
+ __export(LruCache_exports, {
21
+ LruCache: () => LruCache
22
+ });
23
+ module.exports = __toCommonJS(LruCache_exports);
24
+ class LruCache {
25
+ constructor(maxSize) {
26
+ this.maxSize = maxSize;
27
+ }
28
+ maxSize;
29
+ map = /* @__PURE__ */ new Map();
30
+ get(key) {
31
+ if (!this.map.has(key)) return void 0;
32
+ const value = this.map.get(key);
33
+ this.map.delete(key);
34
+ this.map.set(key, value);
35
+ return value;
36
+ }
37
+ set(key, value) {
38
+ if (this.map.has(key)) this.map.delete(key);
39
+ this.map.set(key, value);
40
+ if (this.map.size > this.maxSize) {
41
+ this.map.delete(this.map.keys().next().value);
42
+ }
43
+ }
44
+ has(key) {
45
+ return this.map.has(key);
46
+ }
47
+ // eslint-disable-next-line tldraw/no-setter-getter
48
+ get size() {
49
+ return this.map.size;
50
+ }
51
+ }
52
+ //# sourceMappingURL=LruCache.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/lib/LruCache.ts"],
4
+ "sourcesContent": ["/** Simple LRU cache backed by a Map's insertion-order iteration. @public */\nexport class LruCache<K, V> {\n\tprivate map = new Map<K, V>()\n\tconstructor(private maxSize: number) {}\n\n\tget(key: K): V | undefined {\n\t\tif (!this.map.has(key)) return undefined\n\t\tconst value = this.map.get(key)!\n\t\t// Move to most-recent position\n\t\tthis.map.delete(key)\n\t\tthis.map.set(key, value)\n\t\treturn value\n\t}\n\n\tset(key: K, value: V): void {\n\t\tif (this.map.has(key)) this.map.delete(key)\n\t\tthis.map.set(key, value)\n\t\tif (this.map.size > this.maxSize) {\n\t\t\t// Evict oldest entry\n\t\t\tthis.map.delete(this.map.keys().next().value!)\n\t\t}\n\t}\n\n\thas(key: K): boolean {\n\t\treturn this.map.has(key)\n\t}\n\n\t// eslint-disable-next-line tldraw/no-setter-getter\n\tget size(): number {\n\t\treturn this.map.size\n\t}\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AACO,MAAM,SAAe;AAAA,EAE3B,YAAoB,SAAiB;AAAjB;AAAA,EAAkB;AAAA,EAAlB;AAAA,EADZ,MAAM,oBAAI,IAAU;AAAA,EAG5B,IAAI,KAAuB;AAC1B,QAAI,CAAC,KAAK,IAAI,IAAI,GAAG,EAAG,QAAO;AAC/B,UAAM,QAAQ,KAAK,IAAI,IAAI,GAAG;AAE9B,SAAK,IAAI,OAAO,GAAG;AACnB,SAAK,IAAI,IAAI,KAAK,KAAK;AACvB,WAAO;AAAA,EACR;AAAA,EAEA,IAAI,KAAQ,OAAgB;AAC3B,QAAI,KAAK,IAAI,IAAI,GAAG,EAAG,MAAK,IAAI,OAAO,GAAG;AAC1C,SAAK,IAAI,IAAI,KAAK,KAAK;AACvB,QAAI,KAAK,IAAI,OAAO,KAAK,SAAS;AAEjC,WAAK,IAAI,OAAO,KAAK,IAAI,KAAK,EAAE,KAAK,EAAE,KAAM;AAAA,IAC9C;AAAA,EACD;AAAA,EAEA,IAAI,KAAiB;AACpB,WAAO,KAAK,IAAI,IAAI,GAAG;AAAA,EACxB;AAAA;AAAA,EAGA,IAAI,OAAe;AAClB,WAAO,KAAK,IAAI;AAAA,EACjB;AACD;",
6
+ "names": []
7
+ }
@@ -32,7 +32,7 @@ class PerformanceTracker {
32
32
  * Records animation frames to calculate frame rate.
33
33
  * Called automatically during performance tracking.
34
34
  */
35
- // eslint-disable-next-line local/prefer-class-methods
35
+ // eslint-disable-next-line tldraw/prefer-class-methods
36
36
  recordFrame = () => {
37
37
  this.frames++;
38
38
  if (!this.started) return;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/lib/PerformanceTracker.ts"],
4
- "sourcesContent": ["import { PERFORMANCE_COLORS, PERFORMANCE_PREFIX_COLOR } from './perf'\n\n/**\n * A utility class for measuring and tracking frame rate performance during operations.\n * Provides visual feedback in the browser console with color-coded FPS indicators.\n *\n * @example\n * ```ts\n * const tracker = new PerformanceTracker()\n *\n * tracker.start('render')\n * renderShapes()\n * tracker.stop() // Logs performance info to console\n *\n * // Check if tracking is active\n * if (tracker.isStarted()) {\n * console.log('Still tracking performance')\n * }\n * ```\n *\n * @public\n */\nexport class PerformanceTracker {\n\tprivate startTime = 0\n\tprivate name = ''\n\tprivate frames = 0\n\tprivate started = false\n\tprivate frame: number | null = null\n\n\t/**\n\t * Records animation frames to calculate frame rate.\n\t * Called automatically during performance tracking.\n\t */\n\t// eslint-disable-next-line local/prefer-class-methods\n\trecordFrame = () => {\n\t\tthis.frames++\n\t\tif (!this.started) return\n\t\t// eslint-disable-next-line no-restricted-globals\n\t\tthis.frame = requestAnimationFrame(this.recordFrame)\n\t}\n\n\t/**\n\t * Starts performance tracking for a named operation.\n\t *\n\t * @param name - A descriptive name for the operation being tracked\n\t *\n\t * @example\n\t * ```ts\n\t * tracker.start('canvas-render')\n\t * // ... perform rendering operations\n\t * tracker.stop()\n\t * ```\n\t */\n\tstart(name: string) {\n\t\tthis.name = name\n\t\tthis.frames = 0\n\t\tthis.started = true\n\t\tif (this.frame !== null) cancelAnimationFrame(this.frame)\n\t\t// eslint-disable-next-line no-restricted-globals\n\t\tthis.frame = requestAnimationFrame(this.recordFrame)\n\t\tthis.startTime = performance.now()\n\t}\n\n\t/**\n\t * Stops performance tracking and logs results to the console.\n\t *\n\t * Displays the operation name, frame rate, and uses color coding:\n\t * - Green background: \\> 55 FPS (good performance)\n\t * - Yellow background: 30-55 FPS (moderate performance)\n\t * - Red background: \\< 30 FPS (poor performance)\n\t *\n\t * @example\n\t * ```ts\n\t * tracker.start('interaction')\n\t * handleUserInteraction()\n\t * tracker.stop() // Logs: \"Perf Interaction 60 fps\"\n\t * ```\n\t */\n\tstop() {\n\t\tthis.started = false\n\t\tif (this.frame !== null) cancelAnimationFrame(this.frame)\n\t\tconst duration = (performance.now() - this.startTime) / 1000\n\t\tconst fps = duration === 0 ? 0 : Math.floor(this.frames / duration)\n\t\tconst background =\n\t\t\tfps > 55\n\t\t\t\t? PERFORMANCE_COLORS.Good\n\t\t\t\t: fps > 30\n\t\t\t\t\t? PERFORMANCE_COLORS.Mid\n\t\t\t\t\t: PERFORMANCE_COLORS.Poor\n\t\tconst color = background === PERFORMANCE_COLORS.Mid ? 'black' : 'white'\n\t\tconst capitalized = this.name[0].toUpperCase() + this.name.slice(1)\n\t\t// eslint-disable-next-line no-console\n\t\tconsole.debug(\n\t\t\t`%cPerf%c ${capitalized} %c${fps}%c fps`,\n\t\t\t`color: white; background: ${PERFORMANCE_PREFIX_COLOR};padding: 2px;border-radius: 3px;`,\n\t\t\t'font-weight: normal',\n\t\t\t`font-weight: bold; padding: 2px; background: ${background};color: ${color};`,\n\t\t\t'font-weight: normal'\n\t\t)\n\t}\n\n\t/**\n\t * Checks whether performance tracking is currently active.\n\t *\n\t * @returns True if tracking is in progress, false otherwise\n\t *\n\t * @example\n\t * ```ts\n\t * if (!tracker.isStarted()) {\n\t * tracker.start('new-operation')\n\t * }\n\t * ```\n\t */\n\tisStarted() {\n\t\treturn this.started\n\t}\n}\n"],
4
+ "sourcesContent": ["import { PERFORMANCE_COLORS, PERFORMANCE_PREFIX_COLOR } from './perf'\n\n/**\n * A utility class for measuring and tracking frame rate performance during operations.\n * Provides visual feedback in the browser console with color-coded FPS indicators.\n *\n * @example\n * ```ts\n * const tracker = new PerformanceTracker()\n *\n * tracker.start('render')\n * renderShapes()\n * tracker.stop() // Logs performance info to console\n *\n * // Check if tracking is active\n * if (tracker.isStarted()) {\n * console.log('Still tracking performance')\n * }\n * ```\n *\n * @public\n */\nexport class PerformanceTracker {\n\tprivate startTime = 0\n\tprivate name = ''\n\tprivate frames = 0\n\tprivate started = false\n\tprivate frame: number | null = null\n\n\t/**\n\t * Records animation frames to calculate frame rate.\n\t * Called automatically during performance tracking.\n\t */\n\t// eslint-disable-next-line tldraw/prefer-class-methods\n\trecordFrame = () => {\n\t\tthis.frames++\n\t\tif (!this.started) return\n\t\t// eslint-disable-next-line no-restricted-globals\n\t\tthis.frame = requestAnimationFrame(this.recordFrame)\n\t}\n\n\t/**\n\t * Starts performance tracking for a named operation.\n\t *\n\t * @param name - A descriptive name for the operation being tracked\n\t *\n\t * @example\n\t * ```ts\n\t * tracker.start('canvas-render')\n\t * // ... perform rendering operations\n\t * tracker.stop()\n\t * ```\n\t */\n\tstart(name: string) {\n\t\tthis.name = name\n\t\tthis.frames = 0\n\t\tthis.started = true\n\t\tif (this.frame !== null) cancelAnimationFrame(this.frame)\n\t\t// eslint-disable-next-line no-restricted-globals\n\t\tthis.frame = requestAnimationFrame(this.recordFrame)\n\t\tthis.startTime = performance.now()\n\t}\n\n\t/**\n\t * Stops performance tracking and logs results to the console.\n\t *\n\t * Displays the operation name, frame rate, and uses color coding:\n\t * - Green background: \\> 55 FPS (good performance)\n\t * - Yellow background: 30-55 FPS (moderate performance)\n\t * - Red background: \\< 30 FPS (poor performance)\n\t *\n\t * @example\n\t * ```ts\n\t * tracker.start('interaction')\n\t * handleUserInteraction()\n\t * tracker.stop() // Logs: \"Perf Interaction 60 fps\"\n\t * ```\n\t */\n\tstop() {\n\t\tthis.started = false\n\t\tif (this.frame !== null) cancelAnimationFrame(this.frame)\n\t\tconst duration = (performance.now() - this.startTime) / 1000\n\t\tconst fps = duration === 0 ? 0 : Math.floor(this.frames / duration)\n\t\tconst background =\n\t\t\tfps > 55\n\t\t\t\t? PERFORMANCE_COLORS.Good\n\t\t\t\t: fps > 30\n\t\t\t\t\t? PERFORMANCE_COLORS.Mid\n\t\t\t\t\t: PERFORMANCE_COLORS.Poor\n\t\tconst color = background === PERFORMANCE_COLORS.Mid ? 'black' : 'white'\n\t\tconst capitalized = this.name[0].toUpperCase() + this.name.slice(1)\n\t\t// eslint-disable-next-line no-console\n\t\tconsole.debug(\n\t\t\t`%cPerf%c ${capitalized} %c${fps}%c fps`,\n\t\t\t`color: white; background: ${PERFORMANCE_PREFIX_COLOR};padding: 2px;border-radius: 3px;`,\n\t\t\t'font-weight: normal',\n\t\t\t`font-weight: bold; padding: 2px; background: ${background};color: ${color};`,\n\t\t\t'font-weight: normal'\n\t\t)\n\t}\n\n\t/**\n\t * Checks whether performance tracking is currently active.\n\t *\n\t * @returns True if tracking is in progress, false otherwise\n\t *\n\t * @example\n\t * ```ts\n\t * if (!tracker.isStarted()) {\n\t * tracker.start('new-operation')\n\t * }\n\t * ```\n\t */\n\tisStarted() {\n\t\treturn this.started\n\t}\n}\n"],
5
5
  "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,kBAA6D;AAsBtD,MAAM,mBAAmB;AAAA,EACvB,YAAY;AAAA,EACZ,OAAO;AAAA,EACP,SAAS;AAAA,EACT,UAAU;AAAA,EACV,QAAuB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAO/B,cAAc,MAAM;AACnB,SAAK;AACL,QAAI,CAAC,KAAK,QAAS;AAEnB,SAAK,QAAQ,sBAAsB,KAAK,WAAW;AAAA,EACpD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,MAAM,MAAc;AACnB,SAAK,OAAO;AACZ,SAAK,SAAS;AACd,SAAK,UAAU;AACf,QAAI,KAAK,UAAU,KAAM,sBAAqB,KAAK,KAAK;AAExD,SAAK,QAAQ,sBAAsB,KAAK,WAAW;AACnD,SAAK,YAAY,YAAY,IAAI;AAAA,EAClC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiBA,OAAO;AACN,SAAK,UAAU;AACf,QAAI,KAAK,UAAU,KAAM,sBAAqB,KAAK,KAAK;AACxD,UAAM,YAAY,YAAY,IAAI,IAAI,KAAK,aAAa;AACxD,UAAM,MAAM,aAAa,IAAI,IAAI,KAAK,MAAM,KAAK,SAAS,QAAQ;AAClE,UAAM,aACL,MAAM,KACH,+BAAmB,OACnB,MAAM,KACL,+BAAmB,MACnB,+BAAmB;AACxB,UAAM,QAAQ,eAAe,+BAAmB,MAAM,UAAU;AAChE,UAAM,cAAc,KAAK,KAAK,CAAC,EAAE,YAAY,IAAI,KAAK,KAAK,MAAM,CAAC;AAElE,YAAQ;AAAA,MACP,YAAY,WAAW,MAAM,GAAG;AAAA,MAChC,6BAA6B,oCAAwB;AAAA,MACrD;AAAA,MACA,gDAAgD,UAAU,WAAW,KAAK;AAAA,MAC1E;AAAA,IACD;AAAA,EACD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,YAAY;AACX,WAAO,KAAK;AAAA,EACb;AACD;",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/lib/bind.ts"],
4
- "sourcesContent": ["/*!\n * MIT License: https://github.com/NoHomey/bind-decorator/blob/master/License\n * Copyright (c) 2016 Ivo Stratev\n */\n\nimport { assert } from './control'\n\n/**\n * Decorator that binds a method to its class instance (legacy stage-2 TypeScript decorators).\n * When applied to a class method, ensures `this` always refers to the class instance,\n * even when the method is called as a callback or event handler.\n *\n * @param target - The prototype of the class being decorated\n * @param propertyKey - The name of the method being decorated\n * @param descriptor - The property descriptor for the method being decorated\n * @returns The modified property descriptor with bound method access\n * @example\n * ```typescript\n * class MyClass {\n * name = 'example';\n *\n * @bind\n * getName() {\n * return this.name;\n * }\n * }\n *\n * const instance = new MyClass();\n * const callback = instance.getName;\n * console.log(callback()); // 'example' (this is properly bound)\n * ```\n * @public\n */\nexport function bind<T extends (...args: any[]) => any>(\n\ttarget: object,\n\tpropertyKey: string,\n\tdescriptor: TypedPropertyDescriptor<T>\n): TypedPropertyDescriptor<T>\n\n/**\n * Decorator that binds a method to its class instance (TC39 decorators standard).\n * When applied to a class method, ensures `this` always refers to the class instance,\n * even when the method is called as a callback or event handler.\n *\n * @param originalMethod - The original method being decorated\n * @param context - The decorator context containing metadata about the method\n * @example\n * ```typescript\n * class EventHandler {\n * message = 'Hello World';\n *\n * @bind\n * handleClick() {\n * console.log(this.message);\n * }\n * }\n *\n * const handler = new EventHandler();\n * document.addEventListener('click', handler.handleClick); // 'this' is properly bound\n * ```\n * @public\n */\nexport function bind<This extends object, T extends (...args: any[]) => any>(\n\toriginalMethod: T,\n\tcontext: ClassMethodDecoratorContext<This, T>\n): void\n\n/**\n * Universal decorator implementation that handles both legacy stage-2 and TC39 decorator formats.\n * Automatically detects the decorator format based on the number of arguments and binds the\n * decorated method to the class instance, preventing common `this` context issues.\n *\n * @param args - Either legacy decorator arguments (target, propertyKey, descriptor) or TC39 decorator arguments (originalMethod, context)\n * @returns Property descriptor for legacy decorators, or void for TC39 decorators\n * @example\n * ```typescript\n * // Works with both decorator formats\n * class Calculator {\n * multiplier = 2;\n *\n * @bind\n * multiply(value: number) {\n * return value * this.multiplier;\n * }\n * }\n *\n * const calc = new Calculator();\n * const multiplyFn = calc.multiply;\n * console.log(multiplyFn(5)); // 10 (this.multiplier is accessible)\n *\n * // Useful for event handlers and callbacks\n * setTimeout(calc.multiply, 100, 3); // 6\n * ```\n * @public\n */\nexport function bind(\n\t...args: // legacy stage-2 typescript decorators\n\t| [_target: object, propertyKey: string, descriptor: PropertyDescriptor]\n\t\t// TC39 decorators\n\t\t| [originalMethod: (...args: any[]) => any, context: ClassMemberDecoratorContext]\n): PropertyDescriptor | void {\n\tif (args.length === 2) {\n\t\tconst [originalMethod, context] = args\n\t\tcontext.addInitializer(function initializeMethod(this: any) {\n\t\t\tassert(Reflect.isExtensible(this), 'Cannot bind to a non-extensible class.')\n\t\t\tconst value = originalMethod.bind(this)\n\t\t\tconst ok = Reflect.defineProperty(this, context.name, {\n\t\t\t\tvalue,\n\t\t\t\twritable: true,\n\t\t\t\tconfigurable: true,\n\t\t\t})\n\t\t\tassert(ok, 'Cannot bind a non-configurable class method.')\n\t\t})\n\t} else {\n\t\tconst [_target, propertyKey, descriptor] = args\n\t\tif (!descriptor || typeof descriptor.value !== 'function') {\n\t\t\tthrow new TypeError(\n\t\t\t\t`Only methods can be decorated with @bind. <${propertyKey}> is not a method!`\n\t\t\t)\n\t\t}\n\n\t\treturn {\n\t\t\tconfigurable: true,\n\t\t\tget(this: any): any {\n\t\t\t\tconst bound = descriptor.value!.bind(this)\n\t\t\t\t// Credits to https://github.com/andreypopp/autobind-decorator for memoizing the result of bind against a symbol on the instance.\n\t\t\t\tObject.defineProperty(this, propertyKey, {\n\t\t\t\t\tvalue: bound,\n\t\t\t\t\tconfigurable: true,\n\t\t\t\t\twritable: true,\n\t\t\t\t})\n\t\t\t\treturn bound\n\t\t\t},\n\t\t}\n\t}\n}\n"],
4
+ "sourcesContent": ["/*!\n * MIT License: https://github.com/NoHomey/bind-decorator/blob/master/License\n * Copyright (c) 2016 Ivo Stratev\n */\n\nimport { assert } from './control'\n\n/**\n * Decorator that binds a method to its class instance (legacy stage-2 TypeScript decorators).\n * When applied to a class method, ensures `this` always refers to the class instance,\n * even when the method is called as a callback or event handler.\n *\n * @param target - The prototype of the class being decorated\n * @param propertyKey - The name of the method being decorated\n * @param descriptor - The property descriptor for the method being decorated\n * @returns The modified property descriptor with bound method access\n * @example\n * ```typescript\n * class MyClass {\n * name = 'example';\n *\n * @bind\n * getName() {\n * return this.name;\n * }\n * }\n *\n * const instance = new MyClass();\n * const callback = instance.getName;\n * console.log(callback()); // 'example' (this is properly bound)\n * ```\n * @public\n */\nexport function bind<T extends (...args: any[]) => any>(\n\ttarget: object,\n\tpropertyKey: string,\n\tdescriptor: TypedPropertyDescriptor<T>\n): TypedPropertyDescriptor<T>\n\n/**\n * Decorator that binds a method to its class instance (TC39 decorators standard).\n * When applied to a class method, ensures `this` always refers to the class instance,\n * even when the method is called as a callback or event handler.\n *\n * @param originalMethod - The original method being decorated\n * @param context - The decorator context containing metadata about the method\n * @example\n * ```typescript\n * class EventHandler {\n * message = 'Hello World';\n *\n * @bind\n * handleClick() {\n * console.log(this.message);\n * }\n * }\n *\n * const handler = new EventHandler();\n * document.addEventListener('click', handler.handleClick); // 'this' is properly bound\n * ```\n * @public\n */\nexport function bind<This extends object, T extends (...args: any[]) => any>(\n\toriginalMethod: T,\n\tcontext: ClassMethodDecoratorContext<This, T>\n): void\n\n/**\n * Universal decorator implementation that handles both legacy stage-2 and TC39 decorator formats.\n * Automatically detects the decorator format based on the number of arguments and binds the\n * decorated method to the class instance, preventing common `this` context issues.\n *\n * @param args - Either legacy decorator arguments (target, propertyKey, descriptor) or TC39 decorator arguments (originalMethod, context)\n * @returns Property descriptor for legacy decorators, or void for TC39 decorators\n * @example\n * ```typescript\n * // Works with both decorator formats\n * class Calculator {\n * multiplier = 2;\n *\n * @bind\n * multiply(value: number) {\n * return value * this.multiplier;\n * }\n * }\n *\n * const calc = new Calculator();\n * const multiplyFn = calc.multiply;\n * console.log(multiplyFn(5)); // 10 (this.multiplier is accessible)\n *\n * // Useful for event handlers and callbacks\n * setTimeout(calc.multiply, 100, 3); // 6\n * ```\n * @public\n */\nexport function bind(\n\t...args: // legacy stage-2 typescript decorators\n\t\t| [_target: object, propertyKey: string, descriptor: PropertyDescriptor]\n\t\t// TC39 decorators\n\t\t| [originalMethod: (...args: any[]) => any, context: ClassMemberDecoratorContext]\n): PropertyDescriptor | void {\n\tif (args.length === 2) {\n\t\tconst [originalMethod, context] = args\n\t\tcontext.addInitializer(function initializeMethod(this: any) {\n\t\t\tassert(Reflect.isExtensible(this), 'Cannot bind to a non-extensible class.')\n\t\t\tconst value = originalMethod.bind(this)\n\t\t\tconst ok = Reflect.defineProperty(this, context.name, {\n\t\t\t\tvalue,\n\t\t\t\twritable: true,\n\t\t\t\tconfigurable: true,\n\t\t\t})\n\t\t\tassert(ok, 'Cannot bind a non-configurable class method.')\n\t\t})\n\t} else {\n\t\tconst [_target, propertyKey, descriptor] = args\n\t\tif (!descriptor || typeof descriptor.value !== 'function') {\n\t\t\tthrow new TypeError(\n\t\t\t\t`Only methods can be decorated with @bind. <${propertyKey}> is not a method!`\n\t\t\t)\n\t\t}\n\n\t\treturn {\n\t\t\tconfigurable: true,\n\t\t\tget(this: any): any {\n\t\t\t\tconst bound = descriptor.value!.bind(this)\n\t\t\t\t// Credits to https://github.com/andreypopp/autobind-decorator for memoizing the result of bind against a symbol on the instance.\n\t\t\t\tObject.defineProperty(this, propertyKey, {\n\t\t\t\t\tvalue: bound,\n\t\t\t\t\tconfigurable: true,\n\t\t\t\t\twritable: true,\n\t\t\t\t})\n\t\t\t\treturn bound\n\t\t\t},\n\t\t}\n\t}\n}\n"],
5
5
  "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAKA,qBAAuB;AALvB;AAAA;AAAA;AAAA;AA+FO,SAAS,QACZ,MAIyB;AAC5B,MAAI,KAAK,WAAW,GAAG;AACtB,UAAM,CAAC,gBAAgB,OAAO,IAAI;AAClC,YAAQ,eAAe,SAAS,mBAA4B;AAC3D,iCAAO,QAAQ,aAAa,IAAI,GAAG,wCAAwC;AAC3E,YAAM,QAAQ,eAAe,KAAK,IAAI;AACtC,YAAM,KAAK,QAAQ,eAAe,MAAM,QAAQ,MAAM;AAAA,QACrD;AAAA,QACA,UAAU;AAAA,QACV,cAAc;AAAA,MACf,CAAC;AACD,iCAAO,IAAI,8CAA8C;AAAA,IAC1D,CAAC;AAAA,EACF,OAAO;AACN,UAAM,CAAC,SAAS,aAAa,UAAU,IAAI;AAC3C,QAAI,CAAC,cAAc,OAAO,WAAW,UAAU,YAAY;AAC1D,YAAM,IAAI;AAAA,QACT,8CAA8C,WAAW;AAAA,MAC1D;AAAA,IACD;AAEA,WAAO;AAAA,MACN,cAAc;AAAA,MACd,MAAoB;AACnB,cAAM,QAAQ,WAAW,MAAO,KAAK,IAAI;AAEzC,eAAO,eAAe,MAAM,aAAa;AAAA,UACxC,OAAO;AAAA,UACP,cAAc;AAAA,UACd,UAAU;AAAA,QACX,CAAC;AACD,eAAO;AAAA,MACR;AAAA,IACD;AAAA,EACD;AACD;",
6
6
  "names": []
7
7
  }
@@ -47,6 +47,7 @@ function debounce(callback, wait) {
47
47
  fn.cancel = () => {
48
48
  if (!state) return;
49
49
  clearTimeout(state.timeout);
50
+ state = void 0;
50
51
  };
51
52
  return fn;
52
53
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/lib/debounce.ts"],
4
- "sourcesContent": ["/**\n * Create a debounced version of a function that delays execution until after a specified wait time.\n *\n * Debouncing ensures that a function is only executed once after a specified delay,\n * even if called multiple times in rapid succession. Each new call resets the timer. The debounced\n * function returns a Promise that resolves with the result of the original function. Includes a\n * cancel method to prevent execution if needed.\n *\n * @param callback - The function to debounce (can be sync or async)\n * @param wait - The delay in milliseconds before executing the function\n * @returns A debounced function that returns a Promise and includes a cancel method\n *\n * @example\n * ```ts\n * // Debounce a search function\n * const searchAPI = (query: string) => fetch(`/search?q=${query}`)\n * const debouncedSearch = debounce(searchAPI, 300)\n *\n * // Multiple rapid calls will only execute the last one after 300ms\n * debouncedSearch('react').then(result => console.log(result))\n * debouncedSearch('react hooks') // This cancels the previous call\n * debouncedSearch('react typescript') // Only this will execute\n *\n * // Cancel pending execution\n * debouncedSearch.cancel()\n *\n * // With async/await\n * const saveData = debounce(async (data: any) => {\n * return await api.save(data)\n * }, 1000)\n *\n * const result = await saveData({name: 'John'})\n * ```\n *\n * @public\n * @see source - https://gist.github.com/ca0v/73a31f57b397606c9813472f7493a940\n */\nexport function debounce<T extends unknown[], U>(\n\tcallback: (...args: T) => PromiseLike<U> | U,\n\twait: number\n) {\n\tlet state:\n\t\t| undefined\n\t\t| {\n\t\t\t\ttimeout: ReturnType<typeof setTimeout>\n\t\t\t\tpromise: Promise<U>\n\t\t\t\tresolve(value: U | PromiseLike<U>): void\n\t\t\t\treject(value: any): void\n\t\t\t\tlatestArgs: T\n\t\t } = undefined\n\n\tconst fn = (...args: T): Promise<U> => {\n\t\tif (!state) {\n\t\t\tstate = {} as any\n\t\t\tstate!.promise = new Promise((resolve, reject) => {\n\t\t\t\tstate!.resolve = resolve\n\t\t\t\tstate!.reject = reject\n\t\t\t})\n\t\t}\n\t\tclearTimeout(state!.timeout)\n\t\tstate!.latestArgs = args\n\t\t// It's up to the consumer of debounce to call `cancel`\n\t\t// eslint-disable-next-line no-restricted-globals\n\t\tstate!.timeout = setTimeout(() => {\n\t\t\tconst s = state!\n\t\t\tstate = undefined\n\t\t\ttry {\n\t\t\t\ts.resolve(callback(...s.latestArgs))\n\t\t\t} catch (e) {\n\t\t\t\ts.reject(e)\n\t\t\t}\n\t\t}, wait)\n\n\t\treturn state!.promise\n\t}\n\tfn.cancel = () => {\n\t\tif (!state) return\n\t\tclearTimeout(state.timeout)\n\t}\n\treturn fn\n}\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAqCO,SAAS,SACf,UACA,MACC;AACD,MAAI,QAQG;AAEP,QAAM,KAAK,IAAI,SAAwB;AACtC,QAAI,CAAC,OAAO;AACX,cAAQ,CAAC;AACT,YAAO,UAAU,IAAI,QAAQ,CAAC,SAAS,WAAW;AACjD,cAAO,UAAU;AACjB,cAAO,SAAS;AAAA,MACjB,CAAC;AAAA,IACF;AACA,iBAAa,MAAO,OAAO;AAC3B,UAAO,aAAa;AAGpB,UAAO,UAAU,WAAW,MAAM;AACjC,YAAM,IAAI;AACV,cAAQ;AACR,UAAI;AACH,UAAE,QAAQ,SAAS,GAAG,EAAE,UAAU,CAAC;AAAA,MACpC,SAAS,GAAG;AACX,UAAE,OAAO,CAAC;AAAA,MACX;AAAA,IACD,GAAG,IAAI;AAEP,WAAO,MAAO;AAAA,EACf;AACA,KAAG,SAAS,MAAM;AACjB,QAAI,CAAC,MAAO;AACZ,iBAAa,MAAM,OAAO;AAAA,EAC3B;AACA,SAAO;AACR;",
4
+ "sourcesContent": ["import type { Awaitable } from './types'\n\n/**\n * Create a debounced version of a function that delays execution until after a specified wait time.\n *\n * Debouncing ensures that a function is only executed once after a specified delay,\n * even if called multiple times in rapid succession. Each new call resets the timer. The debounced\n * function returns a Promise that resolves with the result of the original function. Includes a\n * cancel method to prevent execution if needed.\n *\n * @param callback - The function to debounce (can be sync or async)\n * @param wait - The delay in milliseconds before executing the function\n * @returns A debounced function that returns a Promise and includes a cancel method\n *\n * @example\n * ```ts\n * // Debounce a search function\n * const searchAPI = (query: string) => fetch(`/search?q=${query}`)\n * const debouncedSearch = debounce(searchAPI, 300)\n *\n * // Multiple rapid calls will only execute the last one after 300ms\n * debouncedSearch('react').then(result => console.log(result))\n * debouncedSearch('react hooks') // This cancels the previous call\n * debouncedSearch('react typescript') // Only this will execute\n *\n * // Cancel pending execution\n * debouncedSearch.cancel()\n *\n * // With async/await\n * const saveData = debounce(async (data: any) => {\n * return await api.save(data)\n * }, 1000)\n *\n * const result = await saveData({name: 'John'})\n * ```\n *\n * @public\n * @see source - https://gist.github.com/ca0v/73a31f57b397606c9813472f7493a940\n */\nexport function debounce<T extends unknown[], U>(\n\tcallback: (...args: T) => Awaitable<U>,\n\twait: number\n) {\n\tlet state:\n\t\t| undefined\n\t\t| {\n\t\t\t\t// eslint-disable-next-line no-restricted-globals\n\t\t\t\ttimeout: ReturnType<typeof setTimeout>\n\t\t\t\tpromise: Promise<U>\n\t\t\t\tresolve(value: U | PromiseLike<U>): void\n\t\t\t\treject(value: any): void\n\t\t\t\tlatestArgs: T\n\t\t } = undefined\n\n\tconst fn = (...args: T): Promise<U> => {\n\t\tif (!state) {\n\t\t\tstate = {} as any\n\t\t\tstate!.promise = new Promise((resolve, reject) => {\n\t\t\t\tstate!.resolve = resolve\n\t\t\t\tstate!.reject = reject\n\t\t\t})\n\t\t}\n\t\tclearTimeout(state!.timeout)\n\t\tstate!.latestArgs = args\n\t\t// It's up to the consumer of debounce to call `cancel`\n\t\t// eslint-disable-next-line no-restricted-globals\n\t\tstate!.timeout = setTimeout(() => {\n\t\t\tconst s = state!\n\t\t\tstate = undefined\n\t\t\ttry {\n\t\t\t\ts.resolve(callback(...s.latestArgs))\n\t\t\t} catch (e) {\n\t\t\t\ts.reject(e)\n\t\t\t}\n\t\t}, wait)\n\n\t\treturn state!.promise\n\t}\n\tfn.cancel = () => {\n\t\tif (!state) return\n\t\tclearTimeout(state.timeout)\n\t\tstate = undefined\n\t}\n\treturn fn\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAuCO,SAAS,SACf,UACA,MACC;AACD,MAAI,QASG;AAEP,QAAM,KAAK,IAAI,SAAwB;AACtC,QAAI,CAAC,OAAO;AACX,cAAQ,CAAC;AACT,YAAO,UAAU,IAAI,QAAQ,CAAC,SAAS,WAAW;AACjD,cAAO,UAAU;AACjB,cAAO,SAAS;AAAA,MACjB,CAAC;AAAA,IACF;AACA,iBAAa,MAAO,OAAO;AAC3B,UAAO,aAAa;AAGpB,UAAO,UAAU,WAAW,MAAM;AACjC,YAAM,IAAI;AACV,cAAQ;AACR,UAAI;AACH,UAAE,QAAQ,SAAS,GAAG,EAAE,UAAU,CAAC;AAAA,MACpC,SAAS,GAAG;AACX,UAAE,OAAO,CAAC;AAAA,MACX;AAAA,IACD,GAAG,IAAI;AAEP,WAAO,MAAO;AAAA,EACf;AACA,KAAG,SAAS,MAAM;AACjB,QAAI,CAAC,MAAO;AACZ,iBAAa,MAAM,OAAO;AAC1B,YAAQ;AAAA,EACT;AACA,SAAO;AACR;",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/lib/network.ts"],
4
- "sourcesContent": ["/**\n * Just a wrapper around `window.fetch` that sets the `referrerPolicy` to `strict-origin-when-cross-origin`.\n *\n * @param input - A Request object or string containing the URL to fetch\n * @param init - Optional request initialization options\n * @returns Promise that resolves to the Response object\n * @internal\n */\nexport async function fetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response> {\n\t// eslint-disable-next-line no-restricted-properties\n\treturn window.fetch(input, {\n\t\t// We want to make sure that the referrer is not sent to other domains.\n\t\treferrerPolicy: 'strict-origin-when-cross-origin',\n\t\t...init,\n\t})\n}\n\n/**\n * Just a wrapper around `new Image`, and yeah, it's a bit strange that it's in the network.ts file\n * but the main concern here is the referrerPolicy and setting it correctly.\n *\n * @param width - Optional width for the image element\n * @param height - Optional height for the image element\n * @returns HTMLImageElement with referrerPolicy set to 'strict-origin-when-cross-origin'\n * @internal\n */\nexport const Image = (width?: number, height?: number) => {\n\t// eslint-disable-next-line no-restricted-properties\n\tconst img = new window.Image(width, height)\n\timg.referrerPolicy = 'strict-origin-when-cross-origin'\n\treturn img\n}\n"],
4
+ "sourcesContent": ["/**\n * Just a wrapper around `window.fetch` that sets the `referrerPolicy` to `strict-origin-when-cross-origin`.\n *\n * @param input - A Request object or string containing the URL to fetch\n * @param init - Optional request initialization options\n * @returns Promise that resolves to the Response object\n * @internal\n */\nexport async function fetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response> {\n\t// eslint-disable-next-line tldraw/no-restricted-properties\n\treturn window.fetch(input, {\n\t\t// We want to make sure that the referrer is not sent to other domains.\n\t\treferrerPolicy: 'strict-origin-when-cross-origin',\n\t\t...init,\n\t})\n}\n\n/**\n * Just a wrapper around `new Image`, and yeah, it's a bit strange that it's in the network.ts file\n * but the main concern here is the referrerPolicy and setting it correctly.\n *\n * @param width - Optional width for the image element\n * @param height - Optional height for the image element\n * @returns HTMLImageElement with referrerPolicy set to 'strict-origin-when-cross-origin'\n * @internal\n */\nexport const Image = (width?: number, height?: number) => {\n\t// eslint-disable-next-line tldraw/no-restricted-properties\n\tconst img = new window.Image(width, height)\n\timg.referrerPolicy = 'strict-origin-when-cross-origin'\n\treturn img\n}\n"],
5
5
  "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAQA,eAAsB,MAAM,OAA0B,MAAuC;AAE5F,SAAO,OAAO,MAAM,OAAO;AAAA;AAAA,IAE1B,gBAAgB;AAAA,IAChB,GAAG;AAAA,EACJ,CAAC;AACF;AAWO,MAAM,QAAQ,CAAC,OAAgB,WAAoB;AAEzD,QAAM,MAAM,IAAI,OAAO,MAAM,OAAO,MAAM;AAC1C,MAAI,iBAAiB;AACrB,SAAO;AACR;",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/lib/retry.ts"],
4
- "sourcesContent": ["import { sleep } from './control'\n\n/**\n * Retries an async operation with configurable attempt count, wait duration, and error filtering.\n * Executes the provided async function repeatedly until it succeeds or the maximum number of attempts is reached.\n * Includes support for abort signals and custom error matching to determine which errors should trigger retries.\n *\n * @param fn - The async function to retry on failure\n * @param options - Configuration options for retry behavior:\n * - `attempts`: Maximum number of retry attempts (default: 3)\n * - `waitDuration`: Milliseconds to wait between retry attempts (default: 1000)\n * - `abortSignal`: Optional AbortSignal to cancel the retry operation\n * - `matchError`: Optional function to determine if an error should trigger a retry\n * @returns Promise that resolves with the function's return value on success\n *\n * @example\n * ```ts\n * // Basic retry with default settings (3 attempts, 1 second wait)\n * const data = await retry(async () => {\n * const response = await fetch('/api/data')\n * if (!response.ok) throw new Error('Network error')\n * return response.json()\n * })\n *\n * // Custom retry configuration\n * const result = await retry(\n * async () => unreliableApiCall(),\n * {\n * attempts: 5,\n * waitDuration: 2000,\n * matchError: (error) => error instanceof NetworkError\n * }\n * )\n *\n * // With abort signal for cancellation\n * const controller = new AbortController()\n * setTimeout(() => controller.abort(), 10000) // Cancel after 10 seconds\n *\n * const data = await retry(\n * async () => fetchData(),\n * {\n * attempts: 10,\n * abortSignal: controller.signal\n * }\n * )\n * ```\n *\n * @internal\n */\nexport async function retry<T>(\n\tfn: (args: { attempt: number; remaining: number; total: number }) => Promise<T>,\n\t{\n\t\tattempts = 3,\n\t\twaitDuration = 1000,\n\t\tabortSignal,\n\t\tmatchError,\n\t}: {\n\t\tattempts?: number\n\t\twaitDuration?: number\n\t\tabortSignal?: AbortSignal\n\t\tmatchError?(error: unknown): boolean\n\t} = {}\n): Promise<T> {\n\tlet error: unknown = null\n\tfor (let i = 0; i < attempts; i++) {\n\t\tif (abortSignal?.aborted) throw new Error('aborted')\n\t\ttry {\n\t\t\treturn await fn({ attempt: i, remaining: attempts - i, total: attempts })\n\t\t} catch (e) {\n\t\t\tif (matchError && !matchError(e)) throw e\n\t\t\terror = e\n\t\t\tawait sleep(waitDuration)\n\t\t}\n\t}\n\tthrow error\n}\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qBAAsB;AAiDtB,eAAsB,MACrB,IACA;AAAA,EACC,WAAW;AAAA,EACX,eAAe;AAAA,EACf;AAAA,EACA;AACD,IAKI,CAAC,GACQ;AACb,MAAI,QAAiB;AACrB,WAAS,IAAI,GAAG,IAAI,UAAU,KAAK;AAClC,QAAI,aAAa,QAAS,OAAM,IAAI,MAAM,SAAS;AACnD,QAAI;AACH,aAAO,MAAM,GAAG,EAAE,SAAS,GAAG,WAAW,WAAW,GAAG,OAAO,SAAS,CAAC;AAAA,IACzE,SAAS,GAAG;AACX,UAAI,cAAc,CAAC,WAAW,CAAC,EAAG,OAAM;AACxC,cAAQ;AACR,gBAAM,sBAAM,YAAY;AAAA,IACzB;AAAA,EACD;AACA,QAAM;AACP;",
4
+ "sourcesContent": ["import { sleep } from './control'\n\n/**\n * Retries an async operation with configurable attempt count, wait duration, and error filtering.\n * Executes the provided async function repeatedly until it succeeds or the maximum number of attempts is reached.\n * Includes support for abort signals and custom error matching to determine which errors should trigger retries.\n *\n * @param fn - The async function to retry on failure\n * @param options - Configuration options for retry behavior:\n * - `attempts`: Maximum number of retry attempts (default: 3)\n * - `waitDuration`: Milliseconds to wait between retry attempts (default: 1000)\n * - `abortSignal`: Optional AbortSignal to cancel the retry operation\n * - `matchError`: Optional function to determine if an error should trigger a retry\n * @returns Promise that resolves with the function's return value on success\n *\n * @example\n * ```ts\n * // Basic retry with default settings (3 attempts, 1 second wait)\n * const data = await retry(async () => {\n * const response = await fetch('/api/data')\n * if (!response.ok) throw new Error('Network error')\n * return response.json()\n * })\n *\n * // Custom retry configuration\n * const result = await retry(\n * async () => unreliableApiCall(),\n * {\n * attempts: 5,\n * waitDuration: 2000,\n * matchError: (error) => error instanceof NetworkError\n * }\n * )\n *\n * // With abort signal for cancellation\n * const controller = new AbortController()\n * setTimeout(() => controller.abort(), 10000) // Cancel after 10 seconds\n *\n * const data = await retry(\n * async () => fetchData(),\n * {\n * attempts: 10,\n * abortSignal: controller.signal\n * }\n * )\n * ```\n *\n * @internal\n */\nexport async function retry<T>(\n\tfn: (args: { attempt: number; remaining: number; total: number }) => Promise<T>,\n\t{\n\t\tattempts = 3,\n\t\twaitDuration = 1000,\n\t\tabortSignal,\n\t\tmatchError,\n\t}: {\n\t\tattempts?: number\n\t\twaitDuration?: number\n\t\tabortSignal?: AbortSignal\n\t\tmatchError?(error: unknown): boolean\n\t} = {}\n): Promise<T> {\n\tlet error: unknown = null\n\tfor (let i = 0; i < attempts; i++) {\n\t\tif (abortSignal?.aborted) throw new Error('aborted')\n\t\ttry {\n\t\t\treturn await fn({ attempt: i, remaining: attempts - i, total: attempts })\n\t\t} catch (e) {\n\t\t\tif (matchError && !matchError(e)) throw e\n\t\t\terror = e\n\t\t\tawait sleep(waitDuration)\n\t\t}\n\t}\n\t// eslint-disable-next-line no-throw-literal\n\tthrow error\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qBAAsB;AAiDtB,eAAsB,MACrB,IACA;AAAA,EACC,WAAW;AAAA,EACX,eAAe;AAAA,EACf;AAAA,EACA;AACD,IAKI,CAAC,GACQ;AACb,MAAI,QAAiB;AACrB,WAAS,IAAI,GAAG,IAAI,UAAU,KAAK;AAClC,QAAI,aAAa,QAAS,OAAM,IAAI,MAAM,SAAS;AACnD,QAAI;AACH,aAAO,MAAM,GAAG,EAAE,SAAS,GAAG,WAAW,WAAW,GAAG,OAAO,SAAS,CAAC;AAAA,IACzE,SAAS,GAAG;AACX,UAAI,cAAc,CAAC,WAAW,CAAC,EAAG,OAAM;AACxC,cAAQ;AACR,gBAAM,sBAAM,YAAY;AAAA,IACzB;AAAA,EACD;AAEA,QAAM;AACP;",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/lib/storage.tsx"],
4
- "sourcesContent": ["/* eslint-disable no-restricted-syntax */\n\n/**\n * Get a value from local storage.\n *\n * @param key - The key to get.\n * @returns The stored value as a string, or null if not found or storage is unavailable.\n * @example\n * ```ts\n * const userTheme = getFromLocalStorage('user-theme')\n * if (userTheme) {\n * console.log('Stored theme:', userTheme)\n * }\n * ```\n * @internal\n */\nexport function getFromLocalStorage(key: string) {\n\ttry {\n\t\treturn localStorage.getItem(key)\n\t} catch {\n\t\treturn null\n\t}\n}\n\n/**\n * Set a value in local storage. Will not throw an error if localStorage is not available.\n *\n * @param key - The key to set.\n * @param value - The value to set.\n * @returns void\n * @example\n * ```ts\n * const preferences = { theme: 'dark', language: 'en' }\n * setInLocalStorage('user-preferences', JSON.stringify(preferences))\n * ```\n * @internal\n */\nexport function setInLocalStorage(key: string, value: string) {\n\ttry {\n\t\tlocalStorage.setItem(key, value)\n\t} catch {\n\t\t// noop\n\t}\n}\n\n/**\n * Remove a value from local storage. Will not throw an error if localStorage is not available.\n *\n * @param key - The key to remove.\n * @returns void\n * @example\n * ```ts\n * deleteFromLocalStorage('user-preferences')\n * // Value is now removed from localStorage\n * ```\n * @internal\n */\nexport function deleteFromLocalStorage(key: string) {\n\ttry {\n\t\tlocalStorage.removeItem(key)\n\t} catch {\n\t\t// noop\n\t}\n}\n\n/**\n * Clear all values from local storage. Will not throw an error if localStorage is not available.\n *\n * @returns void\n * @example\n * ```ts\n * clearLocalStorage()\n * // All localStorage data is now cleared\n * ```\n * @internal\n */\nexport function clearLocalStorage() {\n\ttry {\n\t\tlocalStorage.clear()\n\t} catch {\n\t\t// noop\n\t}\n}\n\n/**\n * Get a value from session storage.\n *\n * @param key - The key to get.\n * @returns The stored value as a string, or null if not found or storage is unavailable.\n * @example\n * ```ts\n * const currentTool = getFromSessionStorage('current-tool')\n * if (currentTool) {\n * console.log('Active tool:', currentTool)\n * }\n * ```\n * @internal\n */\nexport function getFromSessionStorage(key: string) {\n\ttry {\n\t\treturn sessionStorage.getItem(key)\n\t} catch {\n\t\treturn null\n\t}\n}\n\n/**\n * Set a value in session storage. Will not throw an error if sessionStorage is not available.\n *\n * @param key - The key to set.\n * @param value - The value to set.\n * @returns void\n * @example\n * ```ts\n * setInSessionStorage('current-tool', 'select')\n * setInSessionStorage('temp-data', JSON.stringify({ x: 100, y: 200 }))\n * ```\n * @internal\n */\nexport function setInSessionStorage(key: string, value: string) {\n\ttry {\n\t\tsessionStorage.setItem(key, value)\n\t} catch {\n\t\t// noop\n\t}\n}\n\n/**\n * Remove a value from session storage. Will not throw an error if sessionStorage is not available.\n *\n * @param key - The key to remove.\n * @returns void\n * @example\n * ```ts\n * deleteFromSessionStorage('temp-data')\n * // Value is now removed from sessionStorage\n * ```\n * @internal\n */\nexport function deleteFromSessionStorage(key: string) {\n\ttry {\n\t\tsessionStorage.removeItem(key)\n\t} catch {\n\t\t// noop\n\t}\n}\n\n/**\n * Clear all values from session storage. Will not throw an error if sessionStorage is not available.\n *\n * @returns void\n * @example\n * ```ts\n * clearSessionStorage()\n * // All sessionStorage data is now cleared\n * ```\n * @internal\n */\nexport function clearSessionStorage() {\n\ttry {\n\t\tsessionStorage.clear()\n\t} catch {\n\t\t// noop\n\t}\n}\n"],
4
+ "sourcesContent": ["/* eslint-disable tldraw/no-direct-storage */\n\n/**\n * Get a value from local storage.\n *\n * @param key - The key to get.\n * @returns The stored value as a string, or null if not found or storage is unavailable.\n * @example\n * ```ts\n * const userTheme = getFromLocalStorage('user-theme')\n * if (userTheme) {\n * console.log('Stored theme:', userTheme)\n * }\n * ```\n * @internal\n */\nexport function getFromLocalStorage(key: string) {\n\ttry {\n\t\treturn localStorage.getItem(key)\n\t} catch {\n\t\treturn null\n\t}\n}\n\n/**\n * Set a value in local storage. Will not throw an error if localStorage is not available.\n *\n * @param key - The key to set.\n * @param value - The value to set.\n * @returns void\n * @example\n * ```ts\n * const preferences = { theme: 'dark', language: 'en' }\n * setInLocalStorage('user-preferences', JSON.stringify(preferences))\n * ```\n * @internal\n */\nexport function setInLocalStorage(key: string, value: string) {\n\ttry {\n\t\tlocalStorage.setItem(key, value)\n\t} catch {\n\t\t// noop\n\t}\n}\n\n/**\n * Remove a value from local storage. Will not throw an error if localStorage is not available.\n *\n * @param key - The key to remove.\n * @returns void\n * @example\n * ```ts\n * deleteFromLocalStorage('user-preferences')\n * // Value is now removed from localStorage\n * ```\n * @internal\n */\nexport function deleteFromLocalStorage(key: string) {\n\ttry {\n\t\tlocalStorage.removeItem(key)\n\t} catch {\n\t\t// noop\n\t}\n}\n\n/**\n * Clear all values from local storage. Will not throw an error if localStorage is not available.\n *\n * @returns void\n * @example\n * ```ts\n * clearLocalStorage()\n * // All localStorage data is now cleared\n * ```\n * @internal\n */\nexport function clearLocalStorage() {\n\ttry {\n\t\tlocalStorage.clear()\n\t} catch {\n\t\t// noop\n\t}\n}\n\n/**\n * Get a value from session storage.\n *\n * @param key - The key to get.\n * @returns The stored value as a string, or null if not found or storage is unavailable.\n * @example\n * ```ts\n * const currentTool = getFromSessionStorage('current-tool')\n * if (currentTool) {\n * console.log('Active tool:', currentTool)\n * }\n * ```\n * @internal\n */\nexport function getFromSessionStorage(key: string) {\n\ttry {\n\t\treturn sessionStorage.getItem(key)\n\t} catch {\n\t\treturn null\n\t}\n}\n\n/**\n * Set a value in session storage. Will not throw an error if sessionStorage is not available.\n *\n * @param key - The key to set.\n * @param value - The value to set.\n * @returns void\n * @example\n * ```ts\n * setInSessionStorage('current-tool', 'select')\n * setInSessionStorage('temp-data', JSON.stringify({ x: 100, y: 200 }))\n * ```\n * @internal\n */\nexport function setInSessionStorage(key: string, value: string) {\n\ttry {\n\t\tsessionStorage.setItem(key, value)\n\t} catch {\n\t\t// noop\n\t}\n}\n\n/**\n * Remove a value from session storage. Will not throw an error if sessionStorage is not available.\n *\n * @param key - The key to remove.\n * @returns void\n * @example\n * ```ts\n * deleteFromSessionStorage('temp-data')\n * // Value is now removed from sessionStorage\n * ```\n * @internal\n */\nexport function deleteFromSessionStorage(key: string) {\n\ttry {\n\t\tsessionStorage.removeItem(key)\n\t} catch {\n\t\t// noop\n\t}\n}\n\n/**\n * Clear all values from session storage. Will not throw an error if sessionStorage is not available.\n *\n * @returns void\n * @example\n * ```ts\n * clearSessionStorage()\n * // All sessionStorage data is now cleared\n * ```\n * @internal\n */\nexport function clearSessionStorage() {\n\ttry {\n\t\tsessionStorage.clear()\n\t} catch {\n\t\t// noop\n\t}\n}\n"],
5
5
  "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAgBO,SAAS,oBAAoB,KAAa;AAChD,MAAI;AACH,WAAO,aAAa,QAAQ,GAAG;AAAA,EAChC,QAAQ;AACP,WAAO;AAAA,EACR;AACD;AAeO,SAAS,kBAAkB,KAAa,OAAe;AAC7D,MAAI;AACH,iBAAa,QAAQ,KAAK,KAAK;AAAA,EAChC,QAAQ;AAAA,EAER;AACD;AAcO,SAAS,uBAAuB,KAAa;AACnD,MAAI;AACH,iBAAa,WAAW,GAAG;AAAA,EAC5B,QAAQ;AAAA,EAER;AACD;AAaO,SAAS,oBAAoB;AACnC,MAAI;AACH,iBAAa,MAAM;AAAA,EACpB,QAAQ;AAAA,EAER;AACD;AAgBO,SAAS,sBAAsB,KAAa;AAClD,MAAI;AACH,WAAO,eAAe,QAAQ,GAAG;AAAA,EAClC,QAAQ;AACP,WAAO;AAAA,EACR;AACD;AAeO,SAAS,oBAAoB,KAAa,OAAe;AAC/D,MAAI;AACH,mBAAe,QAAQ,KAAK,KAAK;AAAA,EAClC,QAAQ;AAAA,EAER;AACD;AAcO,SAAS,yBAAyB,KAAa;AACrD,MAAI;AACH,mBAAe,WAAW,GAAG;AAAA,EAC9B,QAAQ;AAAA,EAER;AACD;AAaO,SAAS,sBAAsB;AACrC,MAAI;AACH,mBAAe,MAAM;AAAA,EACtB,QAAQ;AAAA,EAER;AACD;",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/lib/timers.ts"],
4
- "sourcesContent": ["/* eslint-disable no-restricted-properties */\n\n/**\n * A utility class for managing timeouts, intervals, and animation frames with context-based organization and automatic cleanup.\n * Helps prevent memory leaks by organizing timers into named contexts that can be cleared together.\n * @example\n * ```ts\n * const timers = new Timers()\n *\n * // Set timers with context organization\n * timers.setTimeout('ui', () => console.log('Auto save'), 5000)\n * timers.setInterval('ui', () => console.log('Refresh'), 1000)\n * timers.requestAnimationFrame('ui', () => console.log('Render'))\n *\n * // Clear all timers for a context\n * timers.dispose('ui')\n *\n * // Or get context-bound functions\n * const uiTimers = timers.forContext('ui')\n * uiTimers.setTimeout(() => console.log('Contextual timeout'), 1000)\n * ```\n * @public\n */\nexport class Timers {\n\tprivate timeouts = new Map<string, number[]>()\n\tprivate intervals = new Map<string, number[]>()\n\tprivate rafs = new Map<string, number[]>()\n\n\t/**\n\t * Creates a new Timers instance with bound methods for safe callback usage.\n\t * @example\n\t * ```ts\n\t * const timers = new Timers()\n\t * // Methods are pre-bound, safe to use as callbacks\n\t * element.addEventListener('click', timers.dispose)\n\t * ```\n\t */\n\tconstructor() {\n\t\tthis.setTimeout = this.setTimeout.bind(this)\n\t\tthis.setInterval = this.setInterval.bind(this)\n\t\tthis.requestAnimationFrame = this.requestAnimationFrame.bind(this)\n\t\tthis.dispose = this.dispose.bind(this)\n\t}\n\n\t/**\n\t * Creates a timeout that will be tracked under the specified context.\n\t * @param contextId - The context identifier to group this timer under.\n\t * @param handler - The function to execute when the timeout expires.\n\t * @param timeout - The delay in milliseconds (default: 0).\n\t * @param args - Additional arguments to pass to the handler.\n\t * @returns The timer ID that can be used with clearTimeout.\n\t * @example\n\t * ```ts\n\t * const timers = new Timers()\n\t * const id = timers.setTimeout('autosave', () => save(), 5000)\n\t * // Timer will be automatically cleared when 'autosave' context is disposed\n\t * ```\n\t * @public\n\t */\n\tsetTimeout(contextId: string, handler: TimerHandler, timeout?: number, ...args: any[]): number {\n\t\tconst id = window.setTimeout(handler, timeout, args)\n\t\tconst current = this.timeouts.get(contextId) ?? []\n\t\tthis.timeouts.set(contextId, [...current, id])\n\t\treturn id\n\t}\n\n\t/**\n\t * Creates an interval that will be tracked under the specified context.\n\t * @param contextId - The context identifier to group this timer under.\n\t * @param handler - The function to execute repeatedly.\n\t * @param timeout - The delay in milliseconds between executions (default: 0).\n\t * @param args - Additional arguments to pass to the handler.\n\t * @returns The interval ID that can be used with clearInterval.\n\t * @example\n\t * ```ts\n\t * const timers = new Timers()\n\t * const id = timers.setInterval('refresh', () => updateData(), 1000)\n\t * // Interval will be automatically cleared when 'refresh' context is disposed\n\t * ```\n\t * @public\n\t */\n\tsetInterval(contextId: string, handler: TimerHandler, timeout?: number, ...args: any[]): number {\n\t\tconst id = window.setInterval(handler, timeout, args)\n\t\tconst current = this.intervals.get(contextId) ?? []\n\t\tthis.intervals.set(contextId, [...current, id])\n\t\treturn id\n\t}\n\n\t/**\n\t * Requests an animation frame that will be tracked under the specified context.\n\t * @param contextId - The context identifier to group this animation frame under.\n\t * @param callback - The function to execute on the next animation frame.\n\t * @returns The request ID that can be used with cancelAnimationFrame.\n\t * @example\n\t * ```ts\n\t * const timers = new Timers()\n\t * const id = timers.requestAnimationFrame('render', () => draw())\n\t * // Animation frame will be automatically cancelled when 'render' context is disposed\n\t * ```\n\t * @public\n\t */\n\trequestAnimationFrame(contextId: string, callback: FrameRequestCallback): number {\n\t\tconst id = window.requestAnimationFrame(callback)\n\t\tconst current = this.rafs.get(contextId) ?? []\n\t\tthis.rafs.set(contextId, [...current, id])\n\t\treturn id\n\t}\n\n\t/**\n\t * Disposes of all timers associated with the specified context.\n\t * Clears all timeouts, intervals, and animation frames for the given context ID.\n\t * @param contextId - The context identifier whose timers should be cleared.\n\t * @returns void\n\t * @example\n\t * ```ts\n\t * const timers = new Timers()\n\t * timers.setTimeout('ui', () => console.log('timeout'), 1000)\n\t * timers.setInterval('ui', () => console.log('interval'), 500)\n\t *\n\t * // Clear all 'ui' context timers\n\t * timers.dispose('ui')\n\t * ```\n\t * @public\n\t */\n\tdispose(contextId: string) {\n\t\tthis.timeouts.get(contextId)?.forEach((id) => clearTimeout(id))\n\t\tthis.intervals.get(contextId)?.forEach((id) => clearInterval(id))\n\t\tthis.rafs.get(contextId)?.forEach((id) => cancelAnimationFrame(id))\n\n\t\tthis.timeouts.delete(contextId)\n\t\tthis.intervals.delete(contextId)\n\t\tthis.rafs.delete(contextId)\n\t}\n\n\t/**\n\t * Disposes of all timers across all contexts.\n\t * Clears every timeout, interval, and animation frame managed by this instance.\n\t * @returns void\n\t * @example\n\t * ```ts\n\t * const timers = new Timers()\n\t * timers.setTimeout('ui', () => console.log('ui'), 1000)\n\t * timers.setTimeout('background', () => console.log('bg'), 2000)\n\t *\n\t * // Clear everything\n\t * timers.disposeAll()\n\t * ```\n\t * @public\n\t */\n\tdisposeAll() {\n\t\tfor (const contextId of this.timeouts.keys()) {\n\t\t\tthis.dispose(contextId)\n\t\t}\n\t}\n\n\t/**\n\t * Returns an object with timer methods bound to a specific context.\n\t * Convenient for getting context-specific timer functions without repeatedly passing the contextId.\n\t * @param contextId - The context identifier to bind the returned methods to.\n\t * @returns An object with setTimeout, setInterval, requestAnimationFrame, and dispose methods bound to the context.\n\t * @example\n\t * ```ts\n\t * const timers = new Timers()\n\t * const uiTimers = timers.forContext('ui')\n\t *\n\t * // These are equivalent to calling timers.setTimeout('ui', ...)\n\t * uiTimers.setTimeout(() => console.log('timeout'), 1000)\n\t * uiTimers.setInterval(() => console.log('interval'), 500)\n\t * uiTimers.requestAnimationFrame(() => console.log('frame'))\n\t *\n\t * // Dispose only this context\n\t * uiTimers.dispose()\n\t * ```\n\t * @public\n\t */\n\tforContext(contextId: string) {\n\t\treturn {\n\t\t\tsetTimeout: (handler: TimerHandler, timeout?: number, ...args: any[]) =>\n\t\t\t\tthis.setTimeout(contextId, handler, timeout, args),\n\t\t\tsetInterval: (handler: TimerHandler, timeout?: number, ...args: any[]) =>\n\t\t\t\tthis.setInterval(contextId, handler, timeout, args),\n\t\t\trequestAnimationFrame: (callback: FrameRequestCallback) =>\n\t\t\t\tthis.requestAnimationFrame(contextId, callback),\n\t\t\tdispose: () => this.dispose(contextId),\n\t\t}\n\t}\n}\n"],
4
+ "sourcesContent": ["/* eslint-disable tldraw/no-restricted-properties */\n\n/**\n * A utility class for managing timeouts, intervals, and animation frames with context-based organization and automatic cleanup.\n * Helps prevent memory leaks by organizing timers into named contexts that can be cleared together.\n * @example\n * ```ts\n * const timers = new Timers()\n *\n * // Set timers with context organization\n * timers.setTimeout('ui', () => console.log('Auto save'), 5000)\n * timers.setInterval('ui', () => console.log('Refresh'), 1000)\n * timers.requestAnimationFrame('ui', () => console.log('Render'))\n *\n * // Clear all timers for a context\n * timers.dispose('ui')\n *\n * // Or get context-bound functions\n * const uiTimers = timers.forContext('ui')\n * uiTimers.setTimeout(() => console.log('Contextual timeout'), 1000)\n * ```\n * @public\n */\nexport class Timers {\n\tprivate timeouts = new Map<string, number[]>()\n\tprivate intervals = new Map<string, number[]>()\n\tprivate rafs = new Map<string, number[]>()\n\n\t/**\n\t * Creates a new Timers instance with bound methods for safe callback usage.\n\t * @example\n\t * ```ts\n\t * const timers = new Timers()\n\t * // Methods are pre-bound, safe to use as callbacks\n\t * element.addEventListener('click', timers.dispose)\n\t * ```\n\t */\n\tconstructor() {\n\t\tthis.setTimeout = this.setTimeout.bind(this)\n\t\tthis.setInterval = this.setInterval.bind(this)\n\t\tthis.requestAnimationFrame = this.requestAnimationFrame.bind(this)\n\t\tthis.dispose = this.dispose.bind(this)\n\t}\n\n\t/**\n\t * Creates a timeout that will be tracked under the specified context.\n\t * @param contextId - The context identifier to group this timer under.\n\t * @param handler - The function to execute when the timeout expires.\n\t * @param timeout - The delay in milliseconds (default: 0).\n\t * @param args - Additional arguments to pass to the handler.\n\t * @returns The timer ID that can be used with clearTimeout.\n\t * @example\n\t * ```ts\n\t * const timers = new Timers()\n\t * const id = timers.setTimeout('autosave', () => save(), 5000)\n\t * // Timer will be automatically cleared when 'autosave' context is disposed\n\t * ```\n\t * @public\n\t */\n\tsetTimeout(contextId: string, handler: TimerHandler, timeout?: number, ...args: any[]): number {\n\t\tconst id = window.setTimeout(handler, timeout, args)\n\t\tconst current = this.timeouts.get(contextId) ?? []\n\t\tthis.timeouts.set(contextId, [...current, id])\n\t\treturn id\n\t}\n\n\t/**\n\t * Creates an interval that will be tracked under the specified context.\n\t * @param contextId - The context identifier to group this timer under.\n\t * @param handler - The function to execute repeatedly.\n\t * @param timeout - The delay in milliseconds between executions (default: 0).\n\t * @param args - Additional arguments to pass to the handler.\n\t * @returns The interval ID that can be used with clearInterval.\n\t * @example\n\t * ```ts\n\t * const timers = new Timers()\n\t * const id = timers.setInterval('refresh', () => updateData(), 1000)\n\t * // Interval will be automatically cleared when 'refresh' context is disposed\n\t * ```\n\t * @public\n\t */\n\tsetInterval(contextId: string, handler: TimerHandler, timeout?: number, ...args: any[]): number {\n\t\tconst id = window.setInterval(handler, timeout, args)\n\t\tconst current = this.intervals.get(contextId) ?? []\n\t\tthis.intervals.set(contextId, [...current, id])\n\t\treturn id\n\t}\n\n\t/**\n\t * Requests an animation frame that will be tracked under the specified context.\n\t * @param contextId - The context identifier to group this animation frame under.\n\t * @param callback - The function to execute on the next animation frame.\n\t * @returns The request ID that can be used with cancelAnimationFrame.\n\t * @example\n\t * ```ts\n\t * const timers = new Timers()\n\t * const id = timers.requestAnimationFrame('render', () => draw())\n\t * // Animation frame will be automatically cancelled when 'render' context is disposed\n\t * ```\n\t * @public\n\t */\n\trequestAnimationFrame(contextId: string, callback: FrameRequestCallback): number {\n\t\tconst id = window.requestAnimationFrame(callback)\n\t\tconst current = this.rafs.get(contextId) ?? []\n\t\tthis.rafs.set(contextId, [...current, id])\n\t\treturn id\n\t}\n\n\t/**\n\t * Disposes of all timers associated with the specified context.\n\t * Clears all timeouts, intervals, and animation frames for the given context ID.\n\t * @param contextId - The context identifier whose timers should be cleared.\n\t * @returns void\n\t * @example\n\t * ```ts\n\t * const timers = new Timers()\n\t * timers.setTimeout('ui', () => console.log('timeout'), 1000)\n\t * timers.setInterval('ui', () => console.log('interval'), 500)\n\t *\n\t * // Clear all 'ui' context timers\n\t * timers.dispose('ui')\n\t * ```\n\t * @public\n\t */\n\tdispose(contextId: string) {\n\t\tthis.timeouts.get(contextId)?.forEach((id) => clearTimeout(id))\n\t\tthis.intervals.get(contextId)?.forEach((id) => clearInterval(id))\n\t\tthis.rafs.get(contextId)?.forEach((id) => cancelAnimationFrame(id))\n\n\t\tthis.timeouts.delete(contextId)\n\t\tthis.intervals.delete(contextId)\n\t\tthis.rafs.delete(contextId)\n\t}\n\n\t/**\n\t * Disposes of all timers across all contexts.\n\t * Clears every timeout, interval, and animation frame managed by this instance.\n\t * @returns void\n\t * @example\n\t * ```ts\n\t * const timers = new Timers()\n\t * timers.setTimeout('ui', () => console.log('ui'), 1000)\n\t * timers.setTimeout('background', () => console.log('bg'), 2000)\n\t *\n\t * // Clear everything\n\t * timers.disposeAll()\n\t * ```\n\t * @public\n\t */\n\tdisposeAll() {\n\t\tfor (const contextId of this.timeouts.keys()) {\n\t\t\tthis.dispose(contextId)\n\t\t}\n\t}\n\n\t/**\n\t * Returns an object with timer methods bound to a specific context.\n\t * Convenient for getting context-specific timer functions without repeatedly passing the contextId.\n\t * @param contextId - The context identifier to bind the returned methods to.\n\t * @returns An object with setTimeout, setInterval, requestAnimationFrame, and dispose methods bound to the context.\n\t * @example\n\t * ```ts\n\t * const timers = new Timers()\n\t * const uiTimers = timers.forContext('ui')\n\t *\n\t * // These are equivalent to calling timers.setTimeout('ui', ...)\n\t * uiTimers.setTimeout(() => console.log('timeout'), 1000)\n\t * uiTimers.setInterval(() => console.log('interval'), 500)\n\t * uiTimers.requestAnimationFrame(() => console.log('frame'))\n\t *\n\t * // Dispose only this context\n\t * uiTimers.dispose()\n\t * ```\n\t * @public\n\t */\n\tforContext(contextId: string) {\n\t\treturn {\n\t\t\tsetTimeout: (handler: TimerHandler, timeout?: number, ...args: any[]) =>\n\t\t\t\tthis.setTimeout(contextId, handler, timeout, args),\n\t\t\tsetInterval: (handler: TimerHandler, timeout?: number, ...args: any[]) =>\n\t\t\t\tthis.setInterval(contextId, handler, timeout, args),\n\t\t\trequestAnimationFrame: (callback: FrameRequestCallback) =>\n\t\t\t\tthis.requestAnimationFrame(contextId, callback),\n\t\t\tdispose: () => this.dispose(contextId),\n\t\t}\n\t}\n}\n"],
5
5
  "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/lib/types.ts"],
4
- "sourcesContent": ["/**\n * Makes all properties in a type and all nested properties optional recursively.\n * This is useful for creating partial update objects where you only want to specify\n * some deeply nested properties while leaving others unchanged.\n *\n * @example\n * ```ts\n * interface User {\n * name: string\n * settings: {\n * theme: string\n * notifications: {\n * email: boolean\n * push: boolean\n * }\n * }\n * }\n *\n * type PartialUser = RecursivePartial<User>\n * // Result: {\n * // name?: string\n * // settings?: {\n * // theme?: string\n * // notifications?: {\n * // email?: boolean\n * // push?: boolean\n * // }\n * // }\n * // }\n *\n * const update: PartialUser = {\n * settings: {\n * notifications: {\n * email: false\n * }\n * }\n * }\n * ```\n *\n * @public\n */\nexport type RecursivePartial<T> = {\n\t[P in keyof T]?: RecursivePartial<T[P]>\n}\n\n/**\n * Expands a type definition to show its full structure in IDE tooltips and error messages.\n * This utility type forces TypeScript to resolve and display the complete type structure\n * instead of showing complex conditional types or intersections as-is.\n *\n * @example\n * ```ts\n * type User = { name: string }\n * type WithId = { id: string }\n * type UserWithId = User & WithId\n *\n * // Without Expand, IDE shows: User & WithId\n * // With Expand, IDE shows: { name: string; id: string }\n * type ExpandedUserWithId = Expand<UserWithId>\n *\n * // Useful for complex intersections\n * type ComplexType = Expand<BaseType & Mixin1 & Mixin2>\n * ```\n *\n * @public\n */\nexport type Expand<T> = T extends infer O ? { [K in keyof O]: O[K] } : never\n\n/**\n * Makes specified keys in a type required while keeping all other properties as-is.\n * This is useful when you need to ensure certain optional properties are provided\n * in specific contexts without affecting the entire type structure.\n *\n * @example\n * ```ts\n * interface Shape {\n * id: string\n * x?: number\n * y?: number\n * visible?: boolean\n * }\n *\n * // Make position properties required\n * type PositionedShape = Required<Shape, 'x' | 'y'>\n * // Result: {\n * // id: string\n * // x: number // now required\n * // y: number // now required\n * // visible?: boolean\n * // }\n *\n * const shape: PositionedShape = {\n * id: 'rect1',\n * x: 10, // must provide\n * y: 20, // must provide\n * // visible is still optional\n * }\n * ```\n *\n * @internal\n */\nexport type Required<T, K extends keyof T> = Expand<Omit<T, K> & { [P in K]-?: T[P] }>\n\n/**\n * Automatically makes properties optional if their type includes `undefined`.\n * This transforms properties like `prop: string | undefined` to `prop?: string | undefined`,\n * making the API more ergonomic by not requiring explicit undefined assignments.\n *\n * @example\n * ```ts\n * interface RawConfig {\n * name: string\n * theme: string | undefined\n * debug: boolean | undefined\n * version: number\n * }\n *\n * type Config = MakeUndefinedOptional<RawConfig>\n * // Result: {\n * // name: string\n * // theme?: string | undefined // now optional\n * // debug?: boolean | undefined // now optional\n * // version: number\n * // }\n *\n * const config: Config = {\n * name: 'MyApp',\n * version: 1\n * // theme and debug can be omitted instead of explicitly set to undefined\n * }\n * ```\n *\n * @public\n */\nexport type MakeUndefinedOptional<T extends object> = Expand<\n\t{\n\t\t[P in { [K in keyof T]: undefined extends T[K] ? never : K }[keyof T]]: T[P]\n\t} & {\n\t\t[P in { [K in keyof T]: undefined extends T[K] ? K : never }[keyof T]]?: T[P]\n\t}\n>\n"],
4
+ "sourcesContent": ["/**\n * Makes all properties in a type and all nested properties optional recursively.\n * This is useful for creating partial update objects where you only want to specify\n * some deeply nested properties while leaving others unchanged.\n *\n * @example\n * ```ts\n * interface User {\n * name: string\n * settings: {\n * theme: string\n * notifications: {\n * email: boolean\n * push: boolean\n * }\n * }\n * }\n *\n * type PartialUser = RecursivePartial<User>\n * // Result: {\n * // name?: string\n * // settings?: {\n * // theme?: string\n * // notifications?: {\n * // email?: boolean\n * // push?: boolean\n * // }\n * // }\n * // }\n *\n * const update: PartialUser = {\n * settings: {\n * notifications: {\n * email: false\n * }\n * }\n * }\n * ```\n *\n * @public\n */\nexport type RecursivePartial<T> = {\n\t[P in keyof T]?: RecursivePartial<T[P]>\n}\n\n/**\n * Expands a type definition to show its full structure in IDE tooltips and error messages.\n * This utility type forces TypeScript to resolve and display the complete type structure\n * instead of showing complex conditional types or intersections as-is.\n *\n * @example\n * ```ts\n * type User = { name: string }\n * type WithId = { id: string }\n * type UserWithId = User & WithId\n *\n * // Without Expand, IDE shows: User & WithId\n * // With Expand, IDE shows: { name: string; id: string }\n * type ExpandedUserWithId = Expand<UserWithId>\n *\n * // Useful for complex intersections\n * type ComplexType = Expand<BaseType & Mixin1 & Mixin2>\n * ```\n *\n * @public\n */\nexport type Expand<T> = T extends infer O ? { [K in keyof O]: O[K] } : never\n\n/**\n * A value that may be returned synchronously or as a `Promise` / `PromiseLike`.\n * Use with `await` or `Promise.resolve(...)` to normalize to a single `Promise`.\n *\n * @public\n */\nexport type Awaitable<T> = T | PromiseLike<T>\n\n/**\n * Makes specified keys in a type required while keeping all other properties as-is.\n * This is useful when you need to ensure certain optional properties are provided\n * in specific contexts without affecting the entire type structure.\n *\n * @example\n * ```ts\n * interface Shape {\n * id: string\n * x?: number\n * y?: number\n * visible?: boolean\n * }\n *\n * // Make position properties required\n * type PositionedShape = Required<Shape, 'x' | 'y'>\n * // Result: {\n * // id: string\n * // x: number // now required\n * // y: number // now required\n * // visible?: boolean\n * // }\n *\n * const shape: PositionedShape = {\n * id: 'rect1',\n * x: 10, // must provide\n * y: 20, // must provide\n * // visible is still optional\n * }\n * ```\n *\n * @internal\n */\nexport type Required<T, K extends keyof T> = Expand<Omit<T, K> & { [P in K]-?: T[P] }>\n\n/**\n * Automatically makes properties optional if their type includes `undefined`.\n * This transforms properties like `prop: string | undefined` to `prop?: string | undefined`,\n * making the API more ergonomic by not requiring explicit undefined assignments.\n *\n * @example\n * ```ts\n * interface RawConfig {\n * name: string\n * theme: string | undefined\n * debug: boolean | undefined\n * version: number\n * }\n *\n * type Config = MakeUndefinedOptional<RawConfig>\n * // Result: {\n * // name: string\n * // theme?: string | undefined // now optional\n * // debug?: boolean | undefined // now optional\n * // version: number\n * // }\n *\n * const config: Config = {\n * name: 'MyApp',\n * version: 1\n * // theme and debug can be omitted instead of explicitly set to undefined\n * }\n * ```\n *\n * @public\n */\nexport type MakeUndefinedOptional<T extends object> = Expand<\n\t{\n\t\t[P in { [K in keyof T]: undefined extends T[K] ? never : K }[keyof T]]: T[P]\n\t} & {\n\t\t[P in { [K in keyof T]: undefined extends T[K] ? K : never }[keyof T]]?: T[P]\n\t}\n>\n"],
5
5
  "mappings": ";;;;;;;;;;;;;;AAAA;AAAA;",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/lib/version.ts"],
4
- "sourcesContent": ["interface TldrawLibraryVersion {\n\tname: string\n\tversion: string\n\tmodules: string\n}\n\ninterface TldrawLibraryVersionInfo {\n\tversions: TldrawLibraryVersion[]\n\tdidWarn: boolean\n\tscheduledNotice: number | NodeJS.Timeout | null\n}\n\nconst TLDRAW_LIBRARY_VERSION_KEY = '__TLDRAW_LIBRARY_VERSIONS__' as const\n\n// eslint-disable-next-line @typescript-eslint/prefer-namespace-keyword, @typescript-eslint/no-namespace\ndeclare module globalThis {\n\texport const __TLDRAW_LIBRARY_VERSIONS__: TldrawLibraryVersionInfo\n}\n\nfunction getLibraryVersions(): TldrawLibraryVersionInfo {\n\tif (globalThis[TLDRAW_LIBRARY_VERSION_KEY]) {\n\t\treturn globalThis[TLDRAW_LIBRARY_VERSION_KEY]\n\t}\n\n\tconst info: TldrawLibraryVersionInfo = {\n\t\tversions: [],\n\t\tdidWarn: false,\n\t\tscheduledNotice: null,\n\t}\n\n\tObject.defineProperty(globalThis, TLDRAW_LIBRARY_VERSION_KEY, {\n\t\tvalue: info,\n\t\twritable: false,\n\t\tconfigurable: false,\n\t\tenumerable: false,\n\t})\n\n\treturn info\n}\n\n/**\n * Clears all registered library versions and resets warning state.\n * This function is intended for testing purposes only to reset the global version tracking state.\n * @returns void\n * @example\n * ```ts\n * // In a test setup\n * beforeEach(() => {\n * clearRegisteredVersionsForTests()\n * })\n *\n * // Now version tracking starts fresh for each test\n * registerTldrawLibraryVersion('@tldraw/editor', '2.0.0', 'esm')\n * ```\n * @internal\n */\nexport function clearRegisteredVersionsForTests() {\n\tconst info = getLibraryVersions()\n\tinfo.versions = []\n\tinfo.didWarn = false\n\tif (info.scheduledNotice) {\n\t\tclearTimeout(info.scheduledNotice)\n\t\tinfo.scheduledNotice = null\n\t}\n}\n\n/**\n * Registers a tldraw library version for conflict detection.\n * This function tracks different tldraw library versions to warn about potential conflicts\n * when multiple versions are loaded simultaneously.\n * @param name - The name of the tldraw library package (e.g., '\\@tldraw/editor').\n * @param version - The semantic version string (e.g., '2.0.0').\n * @param modules - The module system being used ('esm' or 'cjs').\n * @returns void\n * @example\n * ```ts\n * // Register a library version during package initialization\n * registerTldrawLibraryVersion('@tldraw/editor', '2.0.0', 'esm')\n * registerTldrawLibraryVersion('@tldraw/tldraw', '2.0.0', 'esm')\n *\n * // If conflicting versions are detected, warnings will be logged:\n * registerTldrawLibraryVersion('@tldraw/editor', '1.9.0', 'cjs')\n * // Console warning about version mismatch will appear\n * ```\n * @internal\n */\nexport function registerTldrawLibraryVersion(name?: string, version?: string, modules?: string) {\n\tif (!name || !version || !modules) {\n\t\tif ((globalThis as any).TLDRAW_LIBRARY_IS_BUILD) {\n\t\t\tthrow new Error('Missing name/version/module system in built version of tldraw library')\n\t\t}\n\t\treturn\n\t}\n\n\tconst info = getLibraryVersions()\n\n\t// In Next.js dev mode, Fast Refresh re-executes module-level code which causes\n\t// the same library to register multiple times, producing a false positive warning.\n\t// We skip exact duplicates only in Next.js dev to avoid hiding genuine issues.\n\tif (isNextjsDev()) {\n\t\tconst isDuplicate = info.versions.some(\n\t\t\t(v) => v.name === name && v.version === version && v.modules === modules\n\t\t)\n\t\tif (isDuplicate) return\n\t}\n\n\tinfo.versions.push({ name, version, modules })\n\n\tif (!info.scheduledNotice) {\n\t\ttry {\n\t\t\t// eslint-disable-next-line no-restricted-globals\n\t\t\tinfo.scheduledNotice = setTimeout(() => {\n\t\t\t\tinfo.scheduledNotice = null\n\t\t\t\tcheckLibraryVersions(info)\n\t\t\t}, 100)\n\t\t} catch {\n\t\t\t// some environments (e.g. cloudflare workers) don't support setTimeout immediately, only in a handler.\n\t\t\t// in this case, we'll just check immediately.\n\t\t\tcheckLibraryVersions(info)\n\t\t}\n\t}\n}\n\nfunction checkLibraryVersions(info: TldrawLibraryVersionInfo) {\n\tif (!info.versions.length) return\n\tif (info.didWarn) return\n\n\tconst sorted = info.versions.sort((a, b) => compareVersions(a.version, b.version))\n\tconst latestVersion = sorted[sorted.length - 1].version\n\n\tconst matchingVersions = new Set<string>()\n\tconst nonMatchingVersions = new Map<string, Set<string>>()\n\tfor (const lib of sorted) {\n\t\tif (nonMatchingVersions.has(lib.name)) {\n\t\t\tmatchingVersions.delete(lib.name)\n\t\t\tentry(nonMatchingVersions, lib.name, new Set()).add(lib.version)\n\t\t\tcontinue\n\t\t}\n\n\t\tif (lib.version === latestVersion) {\n\t\t\tmatchingVersions.add(lib.name)\n\t\t} else {\n\t\t\tmatchingVersions.delete(lib.name)\n\t\t\tentry(nonMatchingVersions, lib.name, new Set()).add(lib.version)\n\t\t}\n\t}\n\n\tif (nonMatchingVersions.size > 0) {\n\t\tconst message = [\n\t\t\t`${format('[tldraw]', ['bold', 'bgRed', 'textWhite'])} ${format('You have multiple versions of tldraw libraries installed. This can lead to bugs and unexpected behavior.', ['textRed', 'bold'])}`,\n\t\t\t'',\n\t\t\t`The latest version you have installed is ${format(`v${latestVersion}`, ['bold', 'textBlue'])}. The following libraries are on the latest version:`,\n\t\t\t...Array.from(matchingVersions, (name) => ` \u2022 \u2705 ${format(name, ['bold'])}`),\n\t\t\t'',\n\t\t\t`The following libraries are not on the latest version, or have multiple versions installed:`,\n\t\t\t...Array.from(nonMatchingVersions, ([name, versions]) => {\n\t\t\t\tconst sortedVersions = Array.from(versions)\n\t\t\t\t\t.sort(compareVersions)\n\t\t\t\t\t.map((v) => format(`v${v}`, v === latestVersion ? ['textGreen'] : ['textRed']))\n\t\t\t\treturn ` \u2022 \u274C ${format(name, ['bold'])} (${sortedVersions.join(', ')})`\n\t\t\t}),\n\t\t]\n\n\t\t// eslint-disable-next-line no-console\n\t\tconsole.log(message.join('\\n'))\n\t\tinfo.didWarn = true\n\t\treturn\n\t}\n\n\t// at this point, we know that everything has the same version. there may still be duplicates though!\n\tconst potentialDuplicates = new Map<string, { version: string; modules: string[] }>()\n\tfor (const lib of sorted) {\n\t\tentry(potentialDuplicates, lib.name, { version: lib.version, modules: [] }).modules.push(\n\t\t\tlib.modules\n\t\t)\n\t}\n\n\tconst duplicates = new Map<string, { version: string; modules: string[] }>()\n\tfor (const [name, lib] of potentialDuplicates) {\n\t\tif (lib.modules.length > 1) duplicates.set(name, lib)\n\t}\n\n\tif (duplicates.size > 0) {\n\t\tconst message = [\n\t\t\t`${format('[tldraw]', ['bold', 'bgRed', 'textWhite'])} ${format('You have multiple instances of some tldraw libraries active. This can lead to bugs and unexpected behavior. ', ['textRed', 'bold'])}`,\n\t\t\t'',\n\t\t\t'This usually means that your bundler is misconfigured, and is importing the same library multiple times - usually once as an ES Module, and once as a CommonJS module.',\n\t\t\t'',\n\t\t\t'The following libraries have been imported multiple times:',\n\t\t\t...Array.from(duplicates, ([name, lib]) => {\n\t\t\t\tconst modules = lib.modules\n\t\t\t\t\t.map((m, i) => (m === 'esm' ? ` ${i + 1}. ES Modules` : ` ${i + 1}. CommonJS`))\n\t\t\t\t\t.join('\\n')\n\t\t\t\treturn ` \u2022 \u274C ${format(name, ['bold'])} v${lib.version}: \\n${modules}`\n\t\t\t}),\n\t\t\t'',\n\t\t\t'You should configure your bundler to only import one version of each library.',\n\t\t]\n\n\t\t// eslint-disable-next-line no-console\n\t\tconsole.log(message.join('\\n'))\n\t\tinfo.didWarn = true\n\t\treturn\n\t}\n}\n\nfunction compareVersions(a: string, b: string) {\n\tconst aMatch = a.match(/^(\\d+)\\.(\\d+)\\.(\\d+)(?:-(\\w+))?$/)\n\tconst bMatch = b.match(/^(\\d+)\\.(\\d+)\\.(\\d+)(?:-(\\w+))?$/)\n\n\tif (!aMatch || !bMatch) return a.localeCompare(b)\n\tif (aMatch[1] !== bMatch[1]) return Number(aMatch[1]) - Number(bMatch[1])\n\tif (aMatch[2] !== bMatch[2]) return Number(aMatch[2]) - Number(bMatch[2])\n\tif (aMatch[3] !== bMatch[3]) return Number(aMatch[3]) - Number(bMatch[3])\n\tif (aMatch[4] && bMatch[4]) return aMatch[4].localeCompare(bMatch[4])\n\tif (aMatch[4]) return 1\n\tif (bMatch[4]) return -1\n\treturn 0\n}\n\nconst formats = {\n\tbold: '1',\n\ttextBlue: '94',\n\ttextRed: '31',\n\ttextGreen: '32',\n\tbgRed: '41',\n\ttextWhite: '97',\n} as const\nfunction format(value: string, formatters: (keyof typeof formats)[] = []) {\n\treturn `\\x1B[${formatters.map((f) => formats[f]).join(';')}m${value}\\x1B[m`\n}\n\nfunction isNextjsDev(): boolean {\n\ttry {\n\t\treturn process.env.NODE_ENV === 'development' && '__NEXT_DATA__' in globalThis\n\t} catch {\n\t\treturn false\n\t}\n}\n\nfunction entry<K, V>(map: Map<K, V>, key: K, defaultValue: V): V {\n\tif (map.has(key)) {\n\t\treturn map.get(key)!\n\t}\n\tmap.set(key, defaultValue)\n\treturn defaultValue\n}\n"],
4
+ "sourcesContent": ["interface TldrawLibraryVersion {\n\tname: string\n\tversion: string\n\tmodules: string\n}\n\ninterface TldrawLibraryVersionInfo {\n\tversions: TldrawLibraryVersion[]\n\tdidWarn: boolean\n\tscheduledNotice: number | NodeJS.Timeout | null\n}\n\nconst TLDRAW_LIBRARY_VERSION_KEY = '__TLDRAW_LIBRARY_VERSIONS__' as const\n\n// eslint-disable-next-line @typescript-eslint/prefer-namespace-keyword, @typescript-eslint/no-namespace, no-shadow-restricted-names\ndeclare module globalThis {\n\texport const __TLDRAW_LIBRARY_VERSIONS__: TldrawLibraryVersionInfo\n}\n\nfunction getLibraryVersions(): TldrawLibraryVersionInfo {\n\tif (globalThis[TLDRAW_LIBRARY_VERSION_KEY]) {\n\t\treturn globalThis[TLDRAW_LIBRARY_VERSION_KEY]\n\t}\n\n\tconst info: TldrawLibraryVersionInfo = {\n\t\tversions: [],\n\t\tdidWarn: false,\n\t\tscheduledNotice: null,\n\t}\n\n\tObject.defineProperty(globalThis, TLDRAW_LIBRARY_VERSION_KEY, {\n\t\tvalue: info,\n\t\twritable: false,\n\t\tconfigurable: false,\n\t\tenumerable: false,\n\t})\n\n\treturn info\n}\n\n/**\n * Clears all registered library versions and resets warning state.\n * This function is intended for testing purposes only to reset the global version tracking state.\n * @returns void\n * @example\n * ```ts\n * // In a test setup\n * beforeEach(() => {\n * clearRegisteredVersionsForTests()\n * })\n *\n * // Now version tracking starts fresh for each test\n * registerTldrawLibraryVersion('@tldraw/editor', '2.0.0', 'esm')\n * ```\n * @internal\n */\nexport function clearRegisteredVersionsForTests() {\n\tconst info = getLibraryVersions()\n\tinfo.versions = []\n\tinfo.didWarn = false\n\tif (info.scheduledNotice) {\n\t\tclearTimeout(info.scheduledNotice)\n\t\tinfo.scheduledNotice = null\n\t}\n}\n\n/**\n * Registers a tldraw library version for conflict detection.\n * This function tracks different tldraw library versions to warn about potential conflicts\n * when multiple versions are loaded simultaneously.\n * @param name - The name of the tldraw library package (e.g., '\\@tldraw/editor').\n * @param version - The semantic version string (e.g., '2.0.0').\n * @param modules - The module system being used ('esm' or 'cjs').\n * @returns void\n * @example\n * ```ts\n * // Register a library version during package initialization\n * registerTldrawLibraryVersion('@tldraw/editor', '2.0.0', 'esm')\n * registerTldrawLibraryVersion('@tldraw/tldraw', '2.0.0', 'esm')\n *\n * // If conflicting versions are detected, warnings will be logged:\n * registerTldrawLibraryVersion('@tldraw/editor', '1.9.0', 'cjs')\n * // Console warning about version mismatch will appear\n * ```\n * @internal\n */\nexport function registerTldrawLibraryVersion(name?: string, version?: string, modules?: string) {\n\tif (!name || !version || !modules) {\n\t\tif ((globalThis as any).TLDRAW_LIBRARY_IS_BUILD) {\n\t\t\tthrow new Error('Missing name/version/module system in built version of tldraw library')\n\t\t}\n\t\treturn\n\t}\n\n\tconst info = getLibraryVersions()\n\n\t// In Next.js dev mode, Fast Refresh re-executes module-level code which causes\n\t// the same library to register multiple times, producing a false positive warning.\n\t// We skip exact duplicates only in Next.js dev to avoid hiding genuine issues.\n\tif (isNextjsDev()) {\n\t\tconst isDuplicate = info.versions.some(\n\t\t\t(v) => v.name === name && v.version === version && v.modules === modules\n\t\t)\n\t\tif (isDuplicate) return\n\t}\n\n\tinfo.versions.push({ name, version, modules })\n\n\tif (!info.scheduledNotice) {\n\t\ttry {\n\t\t\t// eslint-disable-next-line no-restricted-globals\n\t\t\tinfo.scheduledNotice = setTimeout(() => {\n\t\t\t\tinfo.scheduledNotice = null\n\t\t\t\tcheckLibraryVersions(info)\n\t\t\t}, 100)\n\t\t} catch {\n\t\t\t// some environments (e.g. cloudflare workers) don't support setTimeout immediately, only in a handler.\n\t\t\t// in this case, we'll just check immediately.\n\t\t\tcheckLibraryVersions(info)\n\t\t}\n\t}\n}\n\nfunction checkLibraryVersions(info: TldrawLibraryVersionInfo) {\n\tif (!info.versions.length) return\n\tif (info.didWarn) return\n\n\tconst sorted = info.versions.sort((a, b) => compareVersions(a.version, b.version))\n\tconst latestVersion = sorted[sorted.length - 1].version\n\n\tconst matchingVersions = new Set<string>()\n\tconst nonMatchingVersions = new Map<string, Set<string>>()\n\tfor (const lib of sorted) {\n\t\tif (nonMatchingVersions.has(lib.name)) {\n\t\t\tmatchingVersions.delete(lib.name)\n\t\t\tentry(nonMatchingVersions, lib.name, new Set()).add(lib.version)\n\t\t\tcontinue\n\t\t}\n\n\t\tif (lib.version === latestVersion) {\n\t\t\tmatchingVersions.add(lib.name)\n\t\t} else {\n\t\t\tmatchingVersions.delete(lib.name)\n\t\t\tentry(nonMatchingVersions, lib.name, new Set()).add(lib.version)\n\t\t}\n\t}\n\n\tif (nonMatchingVersions.size > 0) {\n\t\tconst message = [\n\t\t\t`${format('[tldraw]', ['bold', 'bgRed', 'textWhite'])} ${format('You have multiple versions of tldraw libraries installed. This can lead to bugs and unexpected behavior.', ['textRed', 'bold'])}`,\n\t\t\t'',\n\t\t\t`The latest version you have installed is ${format(`v${latestVersion}`, ['bold', 'textBlue'])}. The following libraries are on the latest version:`,\n\t\t\t...Array.from(matchingVersions, (name) => ` \u2022 \u2705 ${format(name, ['bold'])}`),\n\t\t\t'',\n\t\t\t`The following libraries are not on the latest version, or have multiple versions installed:`,\n\t\t\t...Array.from(nonMatchingVersions, ([name, versions]) => {\n\t\t\t\tconst sortedVersions = Array.from(versions)\n\t\t\t\t\t.sort(compareVersions)\n\t\t\t\t\t.map((v) => format(`v${v}`, v === latestVersion ? ['textGreen'] : ['textRed']))\n\t\t\t\treturn ` \u2022 \u274C ${format(name, ['bold'])} (${sortedVersions.join(', ')})`\n\t\t\t}),\n\t\t]\n\n\t\t// eslint-disable-next-line no-console\n\t\tconsole.log(message.join('\\n'))\n\t\tinfo.didWarn = true\n\t\treturn\n\t}\n\n\t// at this point, we know that everything has the same version. there may still be duplicates though!\n\tconst potentialDuplicates = new Map<string, { version: string; modules: string[] }>()\n\tfor (const lib of sorted) {\n\t\tentry(potentialDuplicates, lib.name, { version: lib.version, modules: [] }).modules.push(\n\t\t\tlib.modules\n\t\t)\n\t}\n\n\tconst duplicates = new Map<string, { version: string; modules: string[] }>()\n\tfor (const [name, lib] of potentialDuplicates) {\n\t\tif (lib.modules.length > 1) duplicates.set(name, lib)\n\t}\n\n\tif (duplicates.size > 0) {\n\t\tconst message = [\n\t\t\t`${format('[tldraw]', ['bold', 'bgRed', 'textWhite'])} ${format('You have multiple instances of some tldraw libraries active. This can lead to bugs and unexpected behavior. ', ['textRed', 'bold'])}`,\n\t\t\t'',\n\t\t\t'This usually means that your bundler is misconfigured, and is importing the same library multiple times - usually once as an ES Module, and once as a CommonJS module.',\n\t\t\t'',\n\t\t\t'The following libraries have been imported multiple times:',\n\t\t\t...Array.from(duplicates, ([name, lib]) => {\n\t\t\t\tconst modules = lib.modules\n\t\t\t\t\t.map((m, i) => (m === 'esm' ? ` ${i + 1}. ES Modules` : ` ${i + 1}. CommonJS`))\n\t\t\t\t\t.join('\\n')\n\t\t\t\treturn ` \u2022 \u274C ${format(name, ['bold'])} v${lib.version}: \\n${modules}`\n\t\t\t}),\n\t\t\t'',\n\t\t\t'You should configure your bundler to only import one version of each library.',\n\t\t]\n\n\t\t// eslint-disable-next-line no-console\n\t\tconsole.log(message.join('\\n'))\n\t\tinfo.didWarn = true\n\t\treturn\n\t}\n}\n\nfunction compareVersions(a: string, b: string) {\n\tconst aMatch = a.match(/^(\\d+)\\.(\\d+)\\.(\\d+)(?:-(\\w+))?$/)\n\tconst bMatch = b.match(/^(\\d+)\\.(\\d+)\\.(\\d+)(?:-(\\w+))?$/)\n\n\tif (!aMatch || !bMatch) return a.localeCompare(b)\n\tif (aMatch[1] !== bMatch[1]) return Number(aMatch[1]) - Number(bMatch[1])\n\tif (aMatch[2] !== bMatch[2]) return Number(aMatch[2]) - Number(bMatch[2])\n\tif (aMatch[3] !== bMatch[3]) return Number(aMatch[3]) - Number(bMatch[3])\n\tif (aMatch[4] && bMatch[4]) return aMatch[4].localeCompare(bMatch[4])\n\tif (aMatch[4]) return 1\n\tif (bMatch[4]) return -1\n\treturn 0\n}\n\nconst formats = {\n\tbold: '1',\n\ttextBlue: '94',\n\ttextRed: '31',\n\ttextGreen: '32',\n\tbgRed: '41',\n\ttextWhite: '97',\n} as const\nfunction format(value: string, formatters: (keyof typeof formats)[] = []) {\n\treturn `\\x1B[${formatters.map((f) => formats[f]).join(';')}m${value}\\x1B[m`\n}\n\nfunction isNextjsDev(): boolean {\n\ttry {\n\t\treturn process.env.NODE_ENV === 'development' && '__NEXT_DATA__' in globalThis\n\t} catch {\n\t\treturn false\n\t}\n}\n\nfunction entry<K, V>(map: Map<K, V>, key: K, defaultValue: V): V {\n\tif (map.has(key)) {\n\t\treturn map.get(key)!\n\t}\n\tmap.set(key, defaultValue)\n\treturn defaultValue\n}\n"],
5
5
  "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -13,6 +13,14 @@ import { default as uniq } from 'lodash.uniq';
13
13
 
14
14
  /* Excluded from this release type: assertExists */
15
15
 
16
+ /**
17
+ * A value that may be returned synchronously or as a `Promise` / `PromiseLike`.
18
+ * Use with `await` or `Promise.resolve(...)` to normalize to a single `Promise`.
19
+ *
20
+ * @public
21
+ */
22
+ export declare type Awaitable<T> = PromiseLike<T> | T;
23
+
16
24
  /**
17
25
  * Decorator that binds a method to its class instance (legacy stage-2 TypeScript decorators).
18
26
  * When applied to a class method, ensures `this` always refers to the class instance,
@@ -109,7 +117,7 @@ export declare function bind<This extends object, T extends (...args: any[]) =>
109
117
  * @public
110
118
  * @see source - https://gist.github.com/ca0v/73a31f57b397606c9813472f7493a940
111
119
  */
112
- export declare function debounce<T extends unknown[], U>(callback: (...args: T) => PromiseLike<U> | U, wait: number): {
120
+ export declare function debounce<T extends unknown[], U>(callback: (...args: T) => Awaitable<U>, wait: number): {
113
121
  (...args: T): Promise<U>;
114
122
  cancel: () => void;
115
123
  };
@@ -878,6 +886,17 @@ export declare function lerp(a: number, b: number, t: number): number;
878
886
  */
879
887
  export declare function lns(str: string): string;
880
888
 
889
+ /** Simple LRU cache backed by a Map's insertion-order iteration. @public */
890
+ export declare class LruCache<K, V> {
891
+ private maxSize;
892
+ private map;
893
+ constructor(maxSize: number);
894
+ get(key: K): undefined | V;
895
+ set(key: K, value: V): void;
896
+ has(key: K): boolean;
897
+ get size(): number;
898
+ }
899
+
881
900
  /**
882
901
  * Automatically makes properties optional if their type includes `undefined`.
883
902
  * This transforms properties like `prop: string | undefined` to `prop?: string | undefined`,
@@ -32,6 +32,7 @@ import { noop, omitFromStackTrace } from "./lib/function.mjs";
32
32
  import { getHashForBuffer, getHashForObject, getHashForString, lns } from "./lib/hash.mjs";
33
33
  import { mockUniqueId, restoreUniqueId, uniqueId } from "./lib/id.mjs";
34
34
  import { getFirstFromIterable } from "./lib/iterable.mjs";
35
+ import { LruCache } from "./lib/LruCache.mjs";
35
36
  import {
36
37
  DEFAULT_SUPPORT_VIDEO_TYPES,
37
38
  DEFAULT_SUPPORTED_IMAGE_TYPES,
@@ -101,7 +102,7 @@ import { registerTldrawLibraryVersion as registerTldrawLibraryVersion2 } from ".
101
102
  import { warnDeprecatedGetter, warnOnce } from "./lib/warn.mjs";
102
103
  registerTldrawLibraryVersion(
103
104
  "@tldraw/utils",
104
- "4.6.0-next.5a871ec02ff3",
105
+ "4.6.0-next.6594d48ace27",
105
106
  "esm"
106
107
  );
107
108
  export {
@@ -113,6 +114,7 @@ export {
113
114
  FileHelpers,
114
115
  FpsScheduler,
115
116
  Image,
117
+ LruCache,
116
118
  MediaHelpers,
117
119
  PerformanceTracker,
118
120
  PngHelpers,