@tldraw/utils 5.1.0 → 5.2.0-next.b91d4a4551c9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-cjs/index.d.ts +14 -4
- package/dist-cjs/index.js +1 -1
- package/dist-cjs/lib/debounce.js +4 -0
- package/dist-cjs/lib/debounce.js.map +2 -2
- package/dist-cjs/lib/function.js +2 -2
- package/dist-cjs/lib/function.js.map +2 -2
- package/dist-cjs/lib/media/avif.js +2 -2
- package/dist-cjs/lib/media/avif.js.map +2 -2
- package/dist-cjs/lib/network.js +2 -2
- package/dist-cjs/lib/network.js.map +2 -2
- package/dist-cjs/lib/url.js +2 -2
- package/dist-cjs/lib/url.js.map +2 -2
- package/dist-esm/index.d.mts +14 -4
- package/dist-esm/index.mjs +1 -1
- package/dist-esm/lib/debounce.mjs +4 -0
- package/dist-esm/lib/debounce.mjs.map +2 -2
- package/dist-esm/lib/function.mjs +2 -2
- package/dist-esm/lib/function.mjs.map +2 -2
- package/dist-esm/lib/media/avif.mjs +2 -2
- package/dist-esm/lib/media/avif.mjs.map +2 -2
- package/dist-esm/lib/network.mjs +2 -2
- package/dist-esm/lib/network.mjs.map +2 -2
- package/dist-esm/lib/url.mjs +2 -2
- package/dist-esm/lib/url.mjs.map +2 -2
- package/package.json +1 -1
- package/src/lib/debounce.test.ts +60 -0
- package/src/lib/debounce.ts +21 -3
- package/src/lib/function.ts +1 -1
- package/src/lib/media/avif.ts +1 -1
- package/src/lib/network.ts +1 -1
- package/src/lib/url.ts +1 -1
package/dist-cjs/index.d.ts
CHANGED
|
@@ -88,6 +88,12 @@ export declare function bind<This extends object, T extends (...args: any[]) =>
|
|
|
88
88
|
* function returns a Promise that resolves with the result of the original function. Includes a
|
|
89
89
|
* cancel method to prevent execution if needed.
|
|
90
90
|
*
|
|
91
|
+
* Calling `cancel()` while a call is pending rejects the returned promise with an `Error`
|
|
92
|
+
* whose message is `'Debounced function was cancelled'`. Callers that discard the promise
|
|
93
|
+
* are not affected — internal handling suppresses the unhandled-rejection warning — but
|
|
94
|
+
* any code that `await`s or chains `.then()` on the promise must be prepared to handle the
|
|
95
|
+
* rejection.
|
|
96
|
+
*
|
|
91
97
|
* @param callback - The function to debounce (can be sync or async)
|
|
92
98
|
* @param wait - The delay in milliseconds before executing the function
|
|
93
99
|
* @returns A debounced function that returns a Promise and includes a cancel method
|
|
@@ -103,15 +109,19 @@ export declare function bind<This extends object, T extends (...args: any[]) =>
|
|
|
103
109
|
* debouncedSearch('react hooks') // This cancels the previous call
|
|
104
110
|
* debouncedSearch('react typescript') // Only this will execute
|
|
105
111
|
*
|
|
106
|
-
* // Cancel pending execution
|
|
112
|
+
* // Cancel pending execution — any in-flight promise rejects
|
|
107
113
|
* debouncedSearch.cancel()
|
|
108
114
|
*
|
|
109
|
-
* // With async/await
|
|
115
|
+
* // With async/await — wrap in try/catch if you might cancel
|
|
110
116
|
* const saveData = debounce(async (data: any) => {
|
|
111
117
|
* return await api.save(data)
|
|
112
118
|
* }, 1000)
|
|
113
119
|
*
|
|
114
|
-
*
|
|
120
|
+
* try {
|
|
121
|
+
* const result = await saveData({name: 'John'})
|
|
122
|
+
* } catch (err) {
|
|
123
|
+
* // handle cancellation or callback error
|
|
124
|
+
* }
|
|
115
125
|
* ```
|
|
116
126
|
*
|
|
117
127
|
* @public
|
|
@@ -1658,7 +1668,7 @@ export declare function rotateArray<T>(arr: T[], offset: number): T[];
|
|
|
1658
1668
|
*
|
|
1659
1669
|
* @public
|
|
1660
1670
|
*/
|
|
1661
|
-
export declare
|
|
1671
|
+
export declare function safeParseUrl(url: string, baseUrl?: string | URL): undefined | URL;
|
|
1662
1672
|
|
|
1663
1673
|
/* Excluded from this release type: setInLocalStorage */
|
|
1664
1674
|
|
package/dist-cjs/index.js
CHANGED
|
@@ -171,7 +171,7 @@ var import_version2 = require("./lib/version");
|
|
|
171
171
|
var import_warn = require("./lib/warn");
|
|
172
172
|
(0, import_version.registerTldrawLibraryVersion)(
|
|
173
173
|
"@tldraw/utils",
|
|
174
|
-
"5.
|
|
174
|
+
"5.2.0-next.b91d4a4551c9",
|
|
175
175
|
"cjs"
|
|
176
176
|
);
|
|
177
177
|
//# sourceMappingURL=index.js.map
|
package/dist-cjs/lib/debounce.js
CHANGED
|
@@ -21,6 +21,7 @@ __export(debounce_exports, {
|
|
|
21
21
|
debounce: () => debounce
|
|
22
22
|
});
|
|
23
23
|
module.exports = __toCommonJS(debounce_exports);
|
|
24
|
+
var import_function = require("./function");
|
|
24
25
|
function debounce(callback, wait) {
|
|
25
26
|
let state = void 0;
|
|
26
27
|
const fn = (...args) => {
|
|
@@ -47,7 +48,10 @@ function debounce(callback, wait) {
|
|
|
47
48
|
fn.cancel = () => {
|
|
48
49
|
if (!state) return;
|
|
49
50
|
clearTimeout(state.timeout);
|
|
51
|
+
const s = state;
|
|
50
52
|
state = void 0;
|
|
53
|
+
s.promise.catch(import_function.noop);
|
|
54
|
+
s.reject(new Error("Debounced function was cancelled"));
|
|
51
55
|
};
|
|
52
56
|
return fn;
|
|
53
57
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/lib/debounce.ts"],
|
|
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;
|
|
4
|
+
"sourcesContent": ["import { noop } from './function'\nimport 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 * Calling `cancel()` while a call is pending rejects the returned promise with an `Error`\n * whose message is `'Debounced function was cancelled'`. Callers that discard the promise\n * are not affected \u2014 internal handling suppresses the unhandled-rejection warning \u2014 but\n * any code that `await`s or chains `.then()` on the promise must be prepared to handle the\n * rejection.\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 \u2014 any in-flight promise rejects\n * debouncedSearch.cancel()\n *\n * // With async/await \u2014 wrap in try/catch if you might cancel\n * const saveData = debounce(async (data: any) => {\n * return await api.save(data)\n * }, 1000)\n *\n * try {\n * const result = await saveData({name: 'John'})\n * } catch (err) {\n * // handle cancellation or callback error\n * }\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\tconst s = state\n\t\tstate = undefined\n\t\t// Attach a no-op handler so callers that discard the promise don't\n\t\t// trigger an unhandled-rejection warning. Consumers that did `.then`,\n\t\t// `.catch`, or `await` on the returned promise still observe the\n\t\t// rejection through their own chain.\n\t\ts.promise.catch(noop)\n\t\ts.reject(new Error('Debounced function was cancelled'))\n\t}\n\treturn fn\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,sBAAqB;AAkDd,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,UAAM,IAAI;AACV,YAAQ;AAKR,MAAE,QAAQ,MAAM,oBAAI;AACpB,MAAE,OAAO,IAAI,MAAM,kCAAkC,CAAC;AAAA,EACvD;AACA,SAAO;AACR;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist-cjs/lib/function.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/lib/function.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * When a function is wrapped in `omitFromStackTrace`, if it throws an error the stack trace won't\n * include the function itself or any stack frames above it. Useful for assertion-style function\n * where the error will ideally originate from the call-site rather than within the implementation\n * of the assert fn.\n *\n * Only works in platforms that support `Error.captureStackTrace` (ie v8).\n *\n * @param fn - The function to wrap and exclude from stack traces\n * @returns A wrapped version of the function that omits itself from error stack traces\n * @example\n * ```ts\n * const assertPositive = omitFromStackTrace((value: number) => {\n * if (value <= 0) throw new Error('Value must be positive')\n * return value\n * })\n *\n * assertPositive(-1) // Error stack trace will point to this line, not inside assertPositive\n * ```\n * @internal\n */\nexport function omitFromStackTrace<Args extends Array<unknown>, Return>(\n\tfn: (...args: Args) => Return\n): (...args: Args) => Return {\n\tconst wrappedFn = (...args: Args) => {\n\t\ttry {\n\t\t\treturn fn(...args)\n\t\t} catch (error) {\n\t\t\tif (error instanceof Error && Error.captureStackTrace) {\n\t\t\t\tError.captureStackTrace(error, wrappedFn)\n\t\t\t}\n\t\t\tthrow error\n\t\t}\n\t}\n\n\treturn wrappedFn\n}\n\n/**\n * Does nothing, but it's really really good at it.\n * @internal\n */\nexport
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAqBO,SAAS,mBACf,IAC4B;AAC5B,QAAM,YAAY,IAAI,SAAe;AACpC,QAAI;AACH,aAAO,GAAG,GAAG,IAAI;AAAA,IAClB,SAAS,OAAO;AACf,UAAI,iBAAiB,SAAS,MAAM,mBAAmB;AACtD,cAAM,kBAAkB,OAAO,SAAS;AAAA,MACzC;AACA,YAAM;AAAA,IACP;AAAA,EACD;AAEA,SAAO;AACR;AAMO,
|
|
4
|
+
"sourcesContent": ["/**\n * When a function is wrapped in `omitFromStackTrace`, if it throws an error the stack trace won't\n * include the function itself or any stack frames above it. Useful for assertion-style function\n * where the error will ideally originate from the call-site rather than within the implementation\n * of the assert fn.\n *\n * Only works in platforms that support `Error.captureStackTrace` (ie v8).\n *\n * @param fn - The function to wrap and exclude from stack traces\n * @returns A wrapped version of the function that omits itself from error stack traces\n * @example\n * ```ts\n * const assertPositive = omitFromStackTrace((value: number) => {\n * if (value <= 0) throw new Error('Value must be positive')\n * return value\n * })\n *\n * assertPositive(-1) // Error stack trace will point to this line, not inside assertPositive\n * ```\n * @internal\n */\nexport function omitFromStackTrace<Args extends Array<unknown>, Return>(\n\tfn: (...args: Args) => Return\n): (...args: Args) => Return {\n\tconst wrappedFn = (...args: Args) => {\n\t\ttry {\n\t\t\treturn fn(...args)\n\t\t} catch (error) {\n\t\t\tif (error instanceof Error && Error.captureStackTrace) {\n\t\t\t\tError.captureStackTrace(error, wrappedFn)\n\t\t\t}\n\t\t\tthrow error\n\t\t}\n\t}\n\n\treturn wrappedFn\n}\n\n/**\n * Does nothing, but it's really really good at it.\n * @internal\n */\nexport function noop(): void {}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAqBO,SAAS,mBACf,IAC4B;AAC5B,QAAM,YAAY,IAAI,SAAe;AACpC,QAAI;AACH,aAAO,GAAG,GAAG,IAAI;AAAA,IAClB,SAAS,OAAO;AACf,UAAI,iBAAiB,SAAS,MAAM,mBAAmB;AACtD,cAAM,kBAAkB,OAAO,SAAS;AAAA,MACzC;AACA,YAAM;AAAA,IACP;AAAA,EACD;AAEA,SAAO;AACR;AAMO,SAAS,OAAa;AAAC;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -21,8 +21,8 @@ __export(avif_exports, {
|
|
|
21
21
|
isAvifAnimated: () => isAvifAnimated
|
|
22
22
|
});
|
|
23
23
|
module.exports = __toCommonJS(avif_exports);
|
|
24
|
-
|
|
24
|
+
function isAvifAnimated(buffer) {
|
|
25
25
|
const view = new Uint8Array(buffer);
|
|
26
26
|
return view[3] === 44;
|
|
27
|
-
}
|
|
27
|
+
}
|
|
28
28
|
//# sourceMappingURL=avif.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/lib/media/avif.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Determines whether an ArrayBuffer contains an animated AVIF image.\n *\n * This function performs a simple check by examining the 4th byte of the buffer.\n * AVIF animation is indicated when the byte at index 3 equals 44.\n *\n * @param buffer - The ArrayBuffer containing the AVIF image data to analyze\n * @returns True if the buffer contains an animated AVIF, false otherwise\n *\n * @example\n * ```typescript\n * // Check if an AVIF file is animated\n * const response = await fetch('image.avif')\n * const buffer = await response.arrayBuffer()\n * const isAnimated = isAvifAnimated(buffer)\n * if (isAnimated) {\n * console.log('This AVIF contains animation!')\n * }\n * ```\n *\n * @example\n * ```typescript\n * // Use with file input\n * const fileInput = document.querySelector('input[type=\"file\"]')\n * fileInput.addEventListener('change', async (event) => {\n * const file = event.target.files[0]\n * const buffer = await file.arrayBuffer()\n * const hasAnimation = isAvifAnimated(buffer)\n * console.log(hasAnimation ? 'Animated AVIF' : 'Static AVIF')\n * })\n * ```\n *\n * @public\n */\nexport
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAkCO,
|
|
4
|
+
"sourcesContent": ["/**\n * Determines whether an ArrayBuffer contains an animated AVIF image.\n *\n * This function performs a simple check by examining the 4th byte of the buffer.\n * AVIF animation is indicated when the byte at index 3 equals 44.\n *\n * @param buffer - The ArrayBuffer containing the AVIF image data to analyze\n * @returns True if the buffer contains an animated AVIF, false otherwise\n *\n * @example\n * ```typescript\n * // Check if an AVIF file is animated\n * const response = await fetch('image.avif')\n * const buffer = await response.arrayBuffer()\n * const isAnimated = isAvifAnimated(buffer)\n * if (isAnimated) {\n * console.log('This AVIF contains animation!')\n * }\n * ```\n *\n * @example\n * ```typescript\n * // Use with file input\n * const fileInput = document.querySelector('input[type=\"file\"]')\n * fileInput.addEventListener('change', async (event) => {\n * const file = event.target.files[0]\n * const buffer = await file.arrayBuffer()\n * const hasAnimation = isAvifAnimated(buffer)\n * console.log(hasAnimation ? 'Animated AVIF' : 'Static AVIF')\n * })\n * ```\n *\n * @public\n */\nexport function isAvifAnimated(buffer: ArrayBuffer) {\n\tconst view = new Uint8Array(buffer)\n\treturn view[3] === 44\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAkCO,SAAS,eAAe,QAAqB;AACnD,QAAM,OAAO,IAAI,WAAW,MAAM;AAClC,SAAO,KAAK,CAAC,MAAM;AACpB;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist-cjs/lib/network.js
CHANGED
|
@@ -29,9 +29,9 @@ async function fetch(input, init) {
|
|
|
29
29
|
...init
|
|
30
30
|
});
|
|
31
31
|
}
|
|
32
|
-
|
|
32
|
+
function Image(width, height) {
|
|
33
33
|
const img = new window.Image(width, height);
|
|
34
34
|
img.referrerPolicy = "strict-origin-when-cross-origin";
|
|
35
35
|
return img;
|
|
36
|
-
}
|
|
36
|
+
}
|
|
37
37
|
//# sourceMappingURL=network.js.map
|
|
@@ -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 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
|
|
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,
|
|
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 function 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
|
+
"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,SAAS,MAAM,OAAgB,QAAiB;AAEtD,QAAM,MAAM,IAAI,OAAO,MAAM,OAAO,MAAM;AAC1C,MAAI,iBAAiB;AACrB,SAAO;AACR;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist-cjs/lib/url.js
CHANGED
|
@@ -21,11 +21,11 @@ __export(url_exports, {
|
|
|
21
21
|
safeParseUrl: () => safeParseUrl
|
|
22
22
|
});
|
|
23
23
|
module.exports = __toCommonJS(url_exports);
|
|
24
|
-
|
|
24
|
+
function safeParseUrl(url, baseUrl) {
|
|
25
25
|
try {
|
|
26
26
|
return new URL(url, baseUrl);
|
|
27
27
|
} catch {
|
|
28
28
|
return;
|
|
29
29
|
}
|
|
30
|
-
}
|
|
30
|
+
}
|
|
31
31
|
//# sourceMappingURL=url.js.map
|
package/dist-cjs/lib/url.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/lib/url.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Safely parses a URL string without throwing exceptions on invalid input.\n * Returns a URL object for valid URLs or undefined for invalid ones.\n *\n * @param url - The URL string to parse\n * @param baseUrl - Optional base URL to resolve relative URLs against\n * @returns A URL object if parsing succeeds, undefined if it fails\n *\n * @example\n * ```ts\n * // Valid absolute URL\n * const url1 = safeParseUrl('https://example.com')\n * if (url1) {\n * console.log(`Valid URL: ${url1.href}`) // \"Valid URL: https://example.com/\"\n * }\n *\n * // Invalid URL\n * const url2 = safeParseUrl('not-a-url')\n * console.log(url2) // undefined\n *\n * // Relative URL with base\n * const url3 = safeParseUrl('/path', 'https://example.com')\n * if (url3) {\n * console.log(url3.href) // \"https://example.com/path\"\n * }\n *\n * // Error handling\n * function handleUserUrl(input: string) {\n * const url = safeParseUrl(input)\n * if (url) {\n * return url\n * } else {\n * console.log('Invalid URL provided')\n * return null\n * }\n * }\n * ```\n *\n * @public\n */\nexport
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAwCO,
|
|
4
|
+
"sourcesContent": ["/**\n * Safely parses a URL string without throwing exceptions on invalid input.\n * Returns a URL object for valid URLs or undefined for invalid ones.\n *\n * @param url - The URL string to parse\n * @param baseUrl - Optional base URL to resolve relative URLs against\n * @returns A URL object if parsing succeeds, undefined if it fails\n *\n * @example\n * ```ts\n * // Valid absolute URL\n * const url1 = safeParseUrl('https://example.com')\n * if (url1) {\n * console.log(`Valid URL: ${url1.href}`) // \"Valid URL: https://example.com/\"\n * }\n *\n * // Invalid URL\n * const url2 = safeParseUrl('not-a-url')\n * console.log(url2) // undefined\n *\n * // Relative URL with base\n * const url3 = safeParseUrl('/path', 'https://example.com')\n * if (url3) {\n * console.log(url3.href) // \"https://example.com/path\"\n * }\n *\n * // Error handling\n * function handleUserUrl(input: string) {\n * const url = safeParseUrl(input)\n * if (url) {\n * return url\n * } else {\n * console.log('Invalid URL provided')\n * return null\n * }\n * }\n * ```\n *\n * @public\n */\nexport function safeParseUrl(url: string, baseUrl?: string | URL) {\n\ttry {\n\t\treturn new URL(url, baseUrl)\n\t} catch {\n\t\treturn\n\t}\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAwCO,SAAS,aAAa,KAAa,SAAwB;AACjE,MAAI;AACH,WAAO,IAAI,IAAI,KAAK,OAAO;AAAA,EAC5B,QAAQ;AACP;AAAA,EACD;AACD;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist-esm/index.d.mts
CHANGED
|
@@ -88,6 +88,12 @@ export declare function bind<This extends object, T extends (...args: any[]) =>
|
|
|
88
88
|
* function returns a Promise that resolves with the result of the original function. Includes a
|
|
89
89
|
* cancel method to prevent execution if needed.
|
|
90
90
|
*
|
|
91
|
+
* Calling `cancel()` while a call is pending rejects the returned promise with an `Error`
|
|
92
|
+
* whose message is `'Debounced function was cancelled'`. Callers that discard the promise
|
|
93
|
+
* are not affected — internal handling suppresses the unhandled-rejection warning — but
|
|
94
|
+
* any code that `await`s or chains `.then()` on the promise must be prepared to handle the
|
|
95
|
+
* rejection.
|
|
96
|
+
*
|
|
91
97
|
* @param callback - The function to debounce (can be sync or async)
|
|
92
98
|
* @param wait - The delay in milliseconds before executing the function
|
|
93
99
|
* @returns A debounced function that returns a Promise and includes a cancel method
|
|
@@ -103,15 +109,19 @@ export declare function bind<This extends object, T extends (...args: any[]) =>
|
|
|
103
109
|
* debouncedSearch('react hooks') // This cancels the previous call
|
|
104
110
|
* debouncedSearch('react typescript') // Only this will execute
|
|
105
111
|
*
|
|
106
|
-
* // Cancel pending execution
|
|
112
|
+
* // Cancel pending execution — any in-flight promise rejects
|
|
107
113
|
* debouncedSearch.cancel()
|
|
108
114
|
*
|
|
109
|
-
* // With async/await
|
|
115
|
+
* // With async/await — wrap in try/catch if you might cancel
|
|
110
116
|
* const saveData = debounce(async (data: any) => {
|
|
111
117
|
* return await api.save(data)
|
|
112
118
|
* }, 1000)
|
|
113
119
|
*
|
|
114
|
-
*
|
|
120
|
+
* try {
|
|
121
|
+
* const result = await saveData({name: 'John'})
|
|
122
|
+
* } catch (err) {
|
|
123
|
+
* // handle cancellation or callback error
|
|
124
|
+
* }
|
|
115
125
|
* ```
|
|
116
126
|
*
|
|
117
127
|
* @public
|
|
@@ -1658,7 +1668,7 @@ export declare function rotateArray<T>(arr: T[], offset: number): T[];
|
|
|
1658
1668
|
*
|
|
1659
1669
|
* @public
|
|
1660
1670
|
*/
|
|
1661
|
-
export declare
|
|
1671
|
+
export declare function safeParseUrl(url: string, baseUrl?: string | URL): undefined | URL;
|
|
1662
1672
|
|
|
1663
1673
|
/* Excluded from this release type: setInLocalStorage */
|
|
1664
1674
|
|
package/dist-esm/index.mjs
CHANGED
|
@@ -102,7 +102,7 @@ import { registerTldrawLibraryVersion as registerTldrawLibraryVersion2 } from ".
|
|
|
102
102
|
import { warnDeprecatedGetter, warnOnce } from "./lib/warn.mjs";
|
|
103
103
|
registerTldrawLibraryVersion(
|
|
104
104
|
"@tldraw/utils",
|
|
105
|
-
"5.
|
|
105
|
+
"5.2.0-next.b91d4a4551c9",
|
|
106
106
|
"esm"
|
|
107
107
|
);
|
|
108
108
|
export {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { noop } from "./function.mjs";
|
|
1
2
|
function debounce(callback, wait) {
|
|
2
3
|
let state = void 0;
|
|
3
4
|
const fn = (...args) => {
|
|
@@ -24,7 +25,10 @@ function debounce(callback, wait) {
|
|
|
24
25
|
fn.cancel = () => {
|
|
25
26
|
if (!state) return;
|
|
26
27
|
clearTimeout(state.timeout);
|
|
28
|
+
const s = state;
|
|
27
29
|
state = void 0;
|
|
30
|
+
s.promise.catch(noop);
|
|
31
|
+
s.reject(new Error("Debounced function was cancelled"));
|
|
28
32
|
};
|
|
29
33
|
return fn;
|
|
30
34
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/lib/debounce.ts"],
|
|
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": "
|
|
4
|
+
"sourcesContent": ["import { noop } from './function'\nimport 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 * Calling `cancel()` while a call is pending rejects the returned promise with an `Error`\n * whose message is `'Debounced function was cancelled'`. Callers that discard the promise\n * are not affected \u2014 internal handling suppresses the unhandled-rejection warning \u2014 but\n * any code that `await`s or chains `.then()` on the promise must be prepared to handle the\n * rejection.\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 \u2014 any in-flight promise rejects\n * debouncedSearch.cancel()\n *\n * // With async/await \u2014 wrap in try/catch if you might cancel\n * const saveData = debounce(async (data: any) => {\n * return await api.save(data)\n * }, 1000)\n *\n * try {\n * const result = await saveData({name: 'John'})\n * } catch (err) {\n * // handle cancellation or callback error\n * }\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\tconst s = state\n\t\tstate = undefined\n\t\t// Attach a no-op handler so callers that discard the promise don't\n\t\t// trigger an unhandled-rejection warning. Consumers that did `.then`,\n\t\t// `.catch`, or `await` on the returned promise still observe the\n\t\t// rejection through their own chain.\n\t\ts.promise.catch(noop)\n\t\ts.reject(new Error('Debounced function was cancelled'))\n\t}\n\treturn fn\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,YAAY;AAkDd,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,UAAM,IAAI;AACV,YAAQ;AAKR,MAAE,QAAQ,MAAM,IAAI;AACpB,MAAE,OAAO,IAAI,MAAM,kCAAkC,CAAC;AAAA,EACvD;AACA,SAAO;AACR;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/lib/function.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * When a function is wrapped in `omitFromStackTrace`, if it throws an error the stack trace won't\n * include the function itself or any stack frames above it. Useful for assertion-style function\n * where the error will ideally originate from the call-site rather than within the implementation\n * of the assert fn.\n *\n * Only works in platforms that support `Error.captureStackTrace` (ie v8).\n *\n * @param fn - The function to wrap and exclude from stack traces\n * @returns A wrapped version of the function that omits itself from error stack traces\n * @example\n * ```ts\n * const assertPositive = omitFromStackTrace((value: number) => {\n * if (value <= 0) throw new Error('Value must be positive')\n * return value\n * })\n *\n * assertPositive(-1) // Error stack trace will point to this line, not inside assertPositive\n * ```\n * @internal\n */\nexport function omitFromStackTrace<Args extends Array<unknown>, Return>(\n\tfn: (...args: Args) => Return\n): (...args: Args) => Return {\n\tconst wrappedFn = (...args: Args) => {\n\t\ttry {\n\t\t\treturn fn(...args)\n\t\t} catch (error) {\n\t\t\tif (error instanceof Error && Error.captureStackTrace) {\n\t\t\t\tError.captureStackTrace(error, wrappedFn)\n\t\t\t}\n\t\t\tthrow error\n\t\t}\n\t}\n\n\treturn wrappedFn\n}\n\n/**\n * Does nothing, but it's really really good at it.\n * @internal\n */\nexport
|
|
5
|
-
"mappings": "AAqBO,SAAS,mBACf,IAC4B;AAC5B,QAAM,YAAY,IAAI,SAAe;AACpC,QAAI;AACH,aAAO,GAAG,GAAG,IAAI;AAAA,IAClB,SAAS,OAAO;AACf,UAAI,iBAAiB,SAAS,MAAM,mBAAmB;AACtD,cAAM,kBAAkB,OAAO,SAAS;AAAA,MACzC;AACA,YAAM;AAAA,IACP;AAAA,EACD;AAEA,SAAO;AACR;AAMO,
|
|
4
|
+
"sourcesContent": ["/**\n * When a function is wrapped in `omitFromStackTrace`, if it throws an error the stack trace won't\n * include the function itself or any stack frames above it. Useful for assertion-style function\n * where the error will ideally originate from the call-site rather than within the implementation\n * of the assert fn.\n *\n * Only works in platforms that support `Error.captureStackTrace` (ie v8).\n *\n * @param fn - The function to wrap and exclude from stack traces\n * @returns A wrapped version of the function that omits itself from error stack traces\n * @example\n * ```ts\n * const assertPositive = omitFromStackTrace((value: number) => {\n * if (value <= 0) throw new Error('Value must be positive')\n * return value\n * })\n *\n * assertPositive(-1) // Error stack trace will point to this line, not inside assertPositive\n * ```\n * @internal\n */\nexport function omitFromStackTrace<Args extends Array<unknown>, Return>(\n\tfn: (...args: Args) => Return\n): (...args: Args) => Return {\n\tconst wrappedFn = (...args: Args) => {\n\t\ttry {\n\t\t\treturn fn(...args)\n\t\t} catch (error) {\n\t\t\tif (error instanceof Error && Error.captureStackTrace) {\n\t\t\t\tError.captureStackTrace(error, wrappedFn)\n\t\t\t}\n\t\t\tthrow error\n\t\t}\n\t}\n\n\treturn wrappedFn\n}\n\n/**\n * Does nothing, but it's really really good at it.\n * @internal\n */\nexport function noop(): void {}\n"],
|
|
5
|
+
"mappings": "AAqBO,SAAS,mBACf,IAC4B;AAC5B,QAAM,YAAY,IAAI,SAAe;AACpC,QAAI;AACH,aAAO,GAAG,GAAG,IAAI;AAAA,IAClB,SAAS,OAAO;AACf,UAAI,iBAAiB,SAAS,MAAM,mBAAmB;AACtD,cAAM,kBAAkB,OAAO,SAAS;AAAA,MACzC;AACA,YAAM;AAAA,IACP;AAAA,EACD;AAEA,SAAO;AACR;AAMO,SAAS,OAAa;AAAC;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/lib/media/avif.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Determines whether an ArrayBuffer contains an animated AVIF image.\n *\n * This function performs a simple check by examining the 4th byte of the buffer.\n * AVIF animation is indicated when the byte at index 3 equals 44.\n *\n * @param buffer - The ArrayBuffer containing the AVIF image data to analyze\n * @returns True if the buffer contains an animated AVIF, false otherwise\n *\n * @example\n * ```typescript\n * // Check if an AVIF file is animated\n * const response = await fetch('image.avif')\n * const buffer = await response.arrayBuffer()\n * const isAnimated = isAvifAnimated(buffer)\n * if (isAnimated) {\n * console.log('This AVIF contains animation!')\n * }\n * ```\n *\n * @example\n * ```typescript\n * // Use with file input\n * const fileInput = document.querySelector('input[type=\"file\"]')\n * fileInput.addEventListener('change', async (event) => {\n * const file = event.target.files[0]\n * const buffer = await file.arrayBuffer()\n * const hasAnimation = isAvifAnimated(buffer)\n * console.log(hasAnimation ? 'Animated AVIF' : 'Static AVIF')\n * })\n * ```\n *\n * @public\n */\nexport
|
|
5
|
-
"mappings": "AAkCO,
|
|
4
|
+
"sourcesContent": ["/**\n * Determines whether an ArrayBuffer contains an animated AVIF image.\n *\n * This function performs a simple check by examining the 4th byte of the buffer.\n * AVIF animation is indicated when the byte at index 3 equals 44.\n *\n * @param buffer - The ArrayBuffer containing the AVIF image data to analyze\n * @returns True if the buffer contains an animated AVIF, false otherwise\n *\n * @example\n * ```typescript\n * // Check if an AVIF file is animated\n * const response = await fetch('image.avif')\n * const buffer = await response.arrayBuffer()\n * const isAnimated = isAvifAnimated(buffer)\n * if (isAnimated) {\n * console.log('This AVIF contains animation!')\n * }\n * ```\n *\n * @example\n * ```typescript\n * // Use with file input\n * const fileInput = document.querySelector('input[type=\"file\"]')\n * fileInput.addEventListener('change', async (event) => {\n * const file = event.target.files[0]\n * const buffer = await file.arrayBuffer()\n * const hasAnimation = isAvifAnimated(buffer)\n * console.log(hasAnimation ? 'Animated AVIF' : 'Static AVIF')\n * })\n * ```\n *\n * @public\n */\nexport function isAvifAnimated(buffer: ArrayBuffer) {\n\tconst view = new Uint8Array(buffer)\n\treturn view[3] === 44\n}\n"],
|
|
5
|
+
"mappings": "AAkCO,SAAS,eAAe,QAAqB;AACnD,QAAM,OAAO,IAAI,WAAW,MAAM;AAClC,SAAO,KAAK,CAAC,MAAM;AACpB;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist-esm/lib/network.mjs
CHANGED
|
@@ -5,11 +5,11 @@ async function fetch(input, init) {
|
|
|
5
5
|
...init
|
|
6
6
|
});
|
|
7
7
|
}
|
|
8
|
-
|
|
8
|
+
function Image(width, height) {
|
|
9
9
|
const img = new window.Image(width, height);
|
|
10
10
|
img.referrerPolicy = "strict-origin-when-cross-origin";
|
|
11
11
|
return img;
|
|
12
|
-
}
|
|
12
|
+
}
|
|
13
13
|
export {
|
|
14
14
|
Image,
|
|
15
15
|
fetch
|
|
@@ -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 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
|
|
5
|
-
"mappings": "AAQA,eAAsB,MAAM,OAA0B,MAAuC;AAE5F,SAAO,OAAO,MAAM,OAAO;AAAA;AAAA,IAE1B,gBAAgB;AAAA,IAChB,GAAG;AAAA,EACJ,CAAC;AACF;AAWO,MAAM,
|
|
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 function 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
|
+
"mappings": "AAQA,eAAsB,MAAM,OAA0B,MAAuC;AAE5F,SAAO,OAAO,MAAM,OAAO;AAAA;AAAA,IAE1B,gBAAgB;AAAA,IAChB,GAAG;AAAA,EACJ,CAAC;AACF;AAWO,SAAS,MAAM,OAAgB,QAAiB;AAEtD,QAAM,MAAM,IAAI,OAAO,MAAM,OAAO,MAAM;AAC1C,MAAI,iBAAiB;AACrB,SAAO;AACR;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist-esm/lib/url.mjs
CHANGED
package/dist-esm/lib/url.mjs.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/lib/url.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Safely parses a URL string without throwing exceptions on invalid input.\n * Returns a URL object for valid URLs or undefined for invalid ones.\n *\n * @param url - The URL string to parse\n * @param baseUrl - Optional base URL to resolve relative URLs against\n * @returns A URL object if parsing succeeds, undefined if it fails\n *\n * @example\n * ```ts\n * // Valid absolute URL\n * const url1 = safeParseUrl('https://example.com')\n * if (url1) {\n * console.log(`Valid URL: ${url1.href}`) // \"Valid URL: https://example.com/\"\n * }\n *\n * // Invalid URL\n * const url2 = safeParseUrl('not-a-url')\n * console.log(url2) // undefined\n *\n * // Relative URL with base\n * const url3 = safeParseUrl('/path', 'https://example.com')\n * if (url3) {\n * console.log(url3.href) // \"https://example.com/path\"\n * }\n *\n * // Error handling\n * function handleUserUrl(input: string) {\n * const url = safeParseUrl(input)\n * if (url) {\n * return url\n * } else {\n * console.log('Invalid URL provided')\n * return null\n * }\n * }\n * ```\n *\n * @public\n */\nexport
|
|
5
|
-
"mappings": "AAwCO,
|
|
4
|
+
"sourcesContent": ["/**\n * Safely parses a URL string without throwing exceptions on invalid input.\n * Returns a URL object for valid URLs or undefined for invalid ones.\n *\n * @param url - The URL string to parse\n * @param baseUrl - Optional base URL to resolve relative URLs against\n * @returns A URL object if parsing succeeds, undefined if it fails\n *\n * @example\n * ```ts\n * // Valid absolute URL\n * const url1 = safeParseUrl('https://example.com')\n * if (url1) {\n * console.log(`Valid URL: ${url1.href}`) // \"Valid URL: https://example.com/\"\n * }\n *\n * // Invalid URL\n * const url2 = safeParseUrl('not-a-url')\n * console.log(url2) // undefined\n *\n * // Relative URL with base\n * const url3 = safeParseUrl('/path', 'https://example.com')\n * if (url3) {\n * console.log(url3.href) // \"https://example.com/path\"\n * }\n *\n * // Error handling\n * function handleUserUrl(input: string) {\n * const url = safeParseUrl(input)\n * if (url) {\n * return url\n * } else {\n * console.log('Invalid URL provided')\n * return null\n * }\n * }\n * ```\n *\n * @public\n */\nexport function safeParseUrl(url: string, baseUrl?: string | URL) {\n\ttry {\n\t\treturn new URL(url, baseUrl)\n\t} catch {\n\t\treturn\n\t}\n}\n"],
|
|
5
|
+
"mappings": "AAwCO,SAAS,aAAa,KAAa,SAAwB;AACjE,MAAI;AACH,WAAO,IAAI,IAAI,KAAK,OAAO;AAAA,EAC5B,QAAQ;AACP;AAAA,EACD;AACD;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/package.json
CHANGED
package/src/lib/debounce.test.ts
CHANGED
|
@@ -65,4 +65,64 @@ describe(debounce, () => {
|
|
|
65
65
|
expect(fn).toHaveBeenCalledTimes(2)
|
|
66
66
|
expect(await Promise.all([promiseC, promiseD])).toEqual(['gh', 'gh'])
|
|
67
67
|
})
|
|
68
|
+
|
|
69
|
+
it('rejects the pending promise when cancelled', async () => {
|
|
70
|
+
const fn = vi.fn()
|
|
71
|
+
const debounced = debounce(fn, 100)
|
|
72
|
+
const promiseA = debounced()
|
|
73
|
+
const promiseB = debounced()
|
|
74
|
+
|
|
75
|
+
debounced.cancel()
|
|
76
|
+
vi.advanceTimersByTime(200)
|
|
77
|
+
|
|
78
|
+
expect(fn).not.toHaveBeenCalled()
|
|
79
|
+
await expect(promiseA).rejects.toThrow('Debounced function was cancelled')
|
|
80
|
+
await expect(promiseB).rejects.toThrow('Debounced function was cancelled')
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
it('starts a fresh promise after cancel', async () => {
|
|
84
|
+
const fn = vi.fn((a, b) => a + b)
|
|
85
|
+
const debounced = debounce(fn, 100)
|
|
86
|
+
const cancelledPromise = debounced('a', 'b')
|
|
87
|
+
|
|
88
|
+
debounced.cancel()
|
|
89
|
+
await expect(cancelledPromise).rejects.toThrow('Debounced function was cancelled')
|
|
90
|
+
|
|
91
|
+
const freshPromise = debounced('c', 'd')
|
|
92
|
+
expect(freshPromise).not.toBe(cancelledPromise)
|
|
93
|
+
|
|
94
|
+
vi.advanceTimersByTime(200)
|
|
95
|
+
expect(fn).toHaveBeenCalledTimes(1)
|
|
96
|
+
expect(fn).toHaveBeenCalledWith('c', 'd')
|
|
97
|
+
expect(await freshPromise).toBe('cd')
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
it('cancel is a no-op when no call is pending', async () => {
|
|
101
|
+
const fn = vi.fn()
|
|
102
|
+
const debounced = debounce(fn, 100)
|
|
103
|
+
expect(() => debounced.cancel()).not.toThrow()
|
|
104
|
+
|
|
105
|
+
const promise = debounced()
|
|
106
|
+
debounced.cancel()
|
|
107
|
+
await expect(promise).rejects.toThrow('Debounced function was cancelled')
|
|
108
|
+
|
|
109
|
+
expect(() => debounced.cancel()).not.toThrow()
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
it('does not emit an unhandledrejection when the promise is discarded on cancel', async () => {
|
|
113
|
+
const handler = vi.fn()
|
|
114
|
+
process.on('unhandledRejection', handler)
|
|
115
|
+
try {
|
|
116
|
+
const fn = vi.fn()
|
|
117
|
+
const debounced = debounce(fn, 100)
|
|
118
|
+
debounced()
|
|
119
|
+
debounced.cancel()
|
|
120
|
+
|
|
121
|
+
await new Promise(process.nextTick)
|
|
122
|
+
|
|
123
|
+
expect(handler).not.toHaveBeenCalled()
|
|
124
|
+
} finally {
|
|
125
|
+
process.off('unhandledRejection', handler)
|
|
126
|
+
}
|
|
127
|
+
})
|
|
68
128
|
})
|
package/src/lib/debounce.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { noop } from './function'
|
|
1
2
|
import type { Awaitable } from './types'
|
|
2
3
|
|
|
3
4
|
/**
|
|
@@ -8,6 +9,12 @@ import type { Awaitable } from './types'
|
|
|
8
9
|
* function returns a Promise that resolves with the result of the original function. Includes a
|
|
9
10
|
* cancel method to prevent execution if needed.
|
|
10
11
|
*
|
|
12
|
+
* Calling `cancel()` while a call is pending rejects the returned promise with an `Error`
|
|
13
|
+
* whose message is `'Debounced function was cancelled'`. Callers that discard the promise
|
|
14
|
+
* are not affected — internal handling suppresses the unhandled-rejection warning — but
|
|
15
|
+
* any code that `await`s or chains `.then()` on the promise must be prepared to handle the
|
|
16
|
+
* rejection.
|
|
17
|
+
*
|
|
11
18
|
* @param callback - The function to debounce (can be sync or async)
|
|
12
19
|
* @param wait - The delay in milliseconds before executing the function
|
|
13
20
|
* @returns A debounced function that returns a Promise and includes a cancel method
|
|
@@ -23,15 +30,19 @@ import type { Awaitable } from './types'
|
|
|
23
30
|
* debouncedSearch('react hooks') // This cancels the previous call
|
|
24
31
|
* debouncedSearch('react typescript') // Only this will execute
|
|
25
32
|
*
|
|
26
|
-
* // Cancel pending execution
|
|
33
|
+
* // Cancel pending execution — any in-flight promise rejects
|
|
27
34
|
* debouncedSearch.cancel()
|
|
28
35
|
*
|
|
29
|
-
* // With async/await
|
|
36
|
+
* // With async/await — wrap in try/catch if you might cancel
|
|
30
37
|
* const saveData = debounce(async (data: any) => {
|
|
31
38
|
* return await api.save(data)
|
|
32
39
|
* }, 1000)
|
|
33
40
|
*
|
|
34
|
-
*
|
|
41
|
+
* try {
|
|
42
|
+
* const result = await saveData({name: 'John'})
|
|
43
|
+
* } catch (err) {
|
|
44
|
+
* // handle cancellation or callback error
|
|
45
|
+
* }
|
|
35
46
|
* ```
|
|
36
47
|
*
|
|
37
48
|
* @public
|
|
@@ -79,7 +90,14 @@ export function debounce<T extends unknown[], U>(
|
|
|
79
90
|
fn.cancel = () => {
|
|
80
91
|
if (!state) return
|
|
81
92
|
clearTimeout(state.timeout)
|
|
93
|
+
const s = state
|
|
82
94
|
state = undefined
|
|
95
|
+
// Attach a no-op handler so callers that discard the promise don't
|
|
96
|
+
// trigger an unhandled-rejection warning. Consumers that did `.then`,
|
|
97
|
+
// `.catch`, or `await` on the returned promise still observe the
|
|
98
|
+
// rejection through their own chain.
|
|
99
|
+
s.promise.catch(noop)
|
|
100
|
+
s.reject(new Error('Debounced function was cancelled'))
|
|
83
101
|
}
|
|
84
102
|
return fn
|
|
85
103
|
}
|
package/src/lib/function.ts
CHANGED
package/src/lib/media/avif.ts
CHANGED
package/src/lib/network.ts
CHANGED
|
@@ -24,7 +24,7 @@ export async function fetch(input: RequestInfo | URL, init?: RequestInit): Promi
|
|
|
24
24
|
* @returns HTMLImageElement with referrerPolicy set to 'strict-origin-when-cross-origin'
|
|
25
25
|
* @internal
|
|
26
26
|
*/
|
|
27
|
-
export
|
|
27
|
+
export function Image(width?: number, height?: number) {
|
|
28
28
|
// eslint-disable-next-line tldraw/no-restricted-properties
|
|
29
29
|
const img = new window.Image(width, height)
|
|
30
30
|
img.referrerPolicy = 'strict-origin-when-cross-origin'
|