@tldraw/utils 4.1.0-next.b6dfe9bccde9 → 4.1.0-next.b73a0d46b63f

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 (160) hide show
  1. package/dist-cjs/index.d.ts +1350 -80
  2. package/dist-cjs/index.js +5 -5
  3. package/dist-cjs/lib/ExecutionQueue.js +79 -0
  4. package/dist-cjs/lib/ExecutionQueue.js.map +2 -2
  5. package/dist-cjs/lib/PerformanceTracker.js +43 -0
  6. package/dist-cjs/lib/PerformanceTracker.js.map +2 -2
  7. package/dist-cjs/lib/array.js +3 -1
  8. package/dist-cjs/lib/array.js.map +2 -2
  9. package/dist-cjs/lib/bind.js.map +2 -2
  10. package/dist-cjs/lib/cache.js +27 -5
  11. package/dist-cjs/lib/cache.js.map +2 -2
  12. package/dist-cjs/lib/control.js +12 -0
  13. package/dist-cjs/lib/control.js.map +2 -2
  14. package/dist-cjs/lib/debounce.js.map +2 -2
  15. package/dist-cjs/lib/error.js.map +2 -2
  16. package/dist-cjs/lib/file.js +76 -11
  17. package/dist-cjs/lib/file.js.map +2 -2
  18. package/dist-cjs/lib/function.js.map +2 -2
  19. package/dist-cjs/lib/hash.js.map +2 -2
  20. package/dist-cjs/lib/id.js.map +2 -2
  21. package/dist-cjs/lib/iterable.js.map +2 -2
  22. package/dist-cjs/lib/json-value.js.map +1 -1
  23. package/dist-cjs/lib/media/apng.js.map +2 -2
  24. package/dist-cjs/lib/media/avif.js.map +2 -2
  25. package/dist-cjs/lib/media/gif.js.map +2 -2
  26. package/dist-cjs/lib/media/media.js +130 -4
  27. package/dist-cjs/lib/media/media.js.map +2 -2
  28. package/dist-cjs/lib/media/png.js +141 -0
  29. package/dist-cjs/lib/media/png.js.map +2 -2
  30. package/dist-cjs/lib/media/webp.js +1 -0
  31. package/dist-cjs/lib/media/webp.js.map +2 -2
  32. package/dist-cjs/lib/network.js.map +2 -2
  33. package/dist-cjs/lib/number.js.map +2 -2
  34. package/dist-cjs/lib/object.js +1 -1
  35. package/dist-cjs/lib/object.js.map +2 -2
  36. package/dist-cjs/lib/perf.js.map +2 -2
  37. package/dist-cjs/lib/reordering.js.map +2 -2
  38. package/dist-cjs/lib/retry.js.map +2 -2
  39. package/dist-cjs/lib/sort.js.map +2 -2
  40. package/dist-cjs/lib/storage.js.map +2 -2
  41. package/dist-cjs/lib/stringEnum.js.map +2 -2
  42. package/dist-cjs/lib/throttle.js.map +2 -2
  43. package/dist-cjs/lib/timers.js +103 -4
  44. package/dist-cjs/lib/timers.js.map +2 -2
  45. package/dist-cjs/lib/types.js.map +1 -1
  46. package/dist-cjs/lib/url.js.map +2 -2
  47. package/dist-cjs/lib/value.js.map +2 -2
  48. package/dist-cjs/lib/version.js.map +2 -2
  49. package/dist-cjs/lib/warn.js.map +2 -2
  50. package/dist-esm/index.d.mts +1350 -80
  51. package/dist-esm/index.mjs +1 -1
  52. package/dist-esm/lib/ExecutionQueue.mjs +79 -0
  53. package/dist-esm/lib/ExecutionQueue.mjs.map +2 -2
  54. package/dist-esm/lib/PerformanceTracker.mjs +43 -0
  55. package/dist-esm/lib/PerformanceTracker.mjs.map +2 -2
  56. package/dist-esm/lib/array.mjs +3 -1
  57. package/dist-esm/lib/array.mjs.map +2 -2
  58. package/dist-esm/lib/bind.mjs.map +2 -2
  59. package/dist-esm/lib/cache.mjs +27 -5
  60. package/dist-esm/lib/cache.mjs.map +2 -2
  61. package/dist-esm/lib/control.mjs +12 -0
  62. package/dist-esm/lib/control.mjs.map +2 -2
  63. package/dist-esm/lib/debounce.mjs.map +2 -2
  64. package/dist-esm/lib/error.mjs.map +2 -2
  65. package/dist-esm/lib/file.mjs +76 -11
  66. package/dist-esm/lib/file.mjs.map +2 -2
  67. package/dist-esm/lib/function.mjs.map +2 -2
  68. package/dist-esm/lib/hash.mjs.map +2 -2
  69. package/dist-esm/lib/id.mjs.map +2 -2
  70. package/dist-esm/lib/iterable.mjs.map +2 -2
  71. package/dist-esm/lib/media/apng.mjs.map +2 -2
  72. package/dist-esm/lib/media/avif.mjs.map +2 -2
  73. package/dist-esm/lib/media/gif.mjs.map +2 -2
  74. package/dist-esm/lib/media/media.mjs +130 -4
  75. package/dist-esm/lib/media/media.mjs.map +2 -2
  76. package/dist-esm/lib/media/png.mjs +141 -0
  77. package/dist-esm/lib/media/png.mjs.map +2 -2
  78. package/dist-esm/lib/media/webp.mjs +1 -0
  79. package/dist-esm/lib/media/webp.mjs.map +2 -2
  80. package/dist-esm/lib/network.mjs.map +2 -2
  81. package/dist-esm/lib/number.mjs.map +2 -2
  82. package/dist-esm/lib/object.mjs.map +2 -2
  83. package/dist-esm/lib/perf.mjs.map +2 -2
  84. package/dist-esm/lib/reordering.mjs.map +2 -2
  85. package/dist-esm/lib/retry.mjs.map +2 -2
  86. package/dist-esm/lib/sort.mjs.map +2 -2
  87. package/dist-esm/lib/storage.mjs.map +2 -2
  88. package/dist-esm/lib/stringEnum.mjs.map +2 -2
  89. package/dist-esm/lib/throttle.mjs.map +2 -2
  90. package/dist-esm/lib/timers.mjs +103 -4
  91. package/dist-esm/lib/timers.mjs.map +2 -2
  92. package/dist-esm/lib/url.mjs.map +2 -2
  93. package/dist-esm/lib/value.mjs.map +2 -2
  94. package/dist-esm/lib/version.mjs.map +2 -2
  95. package/dist-esm/lib/warn.mjs.map +2 -2
  96. package/package.json +1 -1
  97. package/src/lib/ExecutionQueue.test.ts +162 -20
  98. package/src/lib/ExecutionQueue.ts +110 -1
  99. package/src/lib/PerformanceTracker.test.ts +124 -0
  100. package/src/lib/PerformanceTracker.ts +63 -1
  101. package/src/lib/array.test.ts +263 -1
  102. package/src/lib/array.ts +183 -14
  103. package/src/lib/bind.test.ts +47 -0
  104. package/src/lib/bind.ts +69 -4
  105. package/src/lib/cache.test.ts +73 -0
  106. package/src/lib/cache.ts +47 -6
  107. package/src/lib/control.test.ts +50 -0
  108. package/src/lib/control.ts +198 -9
  109. package/src/lib/debounce.ts +28 -3
  110. package/src/lib/error.test.ts +60 -0
  111. package/src/lib/error.ts +27 -1
  112. package/src/lib/file.test.ts +49 -0
  113. package/src/lib/file.ts +117 -12
  114. package/src/lib/function.ts +11 -0
  115. package/src/lib/hash.test.ts +99 -0
  116. package/src/lib/hash.ts +69 -2
  117. package/src/lib/id.test.ts +32 -0
  118. package/src/lib/id.ts +53 -5
  119. package/src/lib/iterable.test.ts +25 -0
  120. package/src/lib/iterable.ts +4 -5
  121. package/src/lib/json-value.ts +71 -4
  122. package/src/lib/media/apng.test.ts +67 -0
  123. package/src/lib/media/apng.ts +38 -21
  124. package/src/lib/media/avif.test.ts +26 -0
  125. package/src/lib/media/avif.ts +34 -0
  126. package/src/lib/media/gif.test.ts +52 -0
  127. package/src/lib/media/gif.ts +25 -2
  128. package/src/lib/media/media.test.ts +58 -0
  129. package/src/lib/media/media.ts +220 -11
  130. package/src/lib/media/png.ts +162 -1
  131. package/src/lib/media/webp.test.ts +81 -0
  132. package/src/lib/media/webp.ts +33 -1
  133. package/src/lib/network.test.ts +38 -0
  134. package/src/lib/network.ts +6 -0
  135. package/src/lib/number.test.ts +74 -0
  136. package/src/lib/number.ts +29 -5
  137. package/src/lib/object.test.ts +236 -0
  138. package/src/lib/object.ts +194 -14
  139. package/src/lib/perf.ts +75 -3
  140. package/src/lib/reordering.test.ts +168 -0
  141. package/src/lib/reordering.ts +62 -4
  142. package/src/lib/retry.test.ts +77 -0
  143. package/src/lib/retry.ts +47 -1
  144. package/src/lib/sort.test.ts +36 -0
  145. package/src/lib/sort.ts +22 -1
  146. package/src/lib/storage.test.ts +130 -0
  147. package/src/lib/storage.tsx +54 -8
  148. package/src/lib/stringEnum.ts +20 -1
  149. package/src/lib/throttle.ts +46 -8
  150. package/src/lib/timers.test.ts +75 -0
  151. package/src/lib/timers.ts +124 -5
  152. package/src/lib/types.ts +126 -4
  153. package/src/lib/url.test.ts +44 -0
  154. package/src/lib/url.ts +40 -1
  155. package/src/lib/value.test.ts +102 -0
  156. package/src/lib/value.ts +67 -3
  157. package/src/lib/version.test.ts +494 -56
  158. package/src/lib/version.ts +36 -1
  159. package/src/lib/warn.test.ts +64 -0
  160. package/src/lib/warn.ts +43 -2
@@ -24,31 +24,87 @@ module.exports = __toCommonJS(file_exports);
24
24
  var import_network = require("./network");
25
25
  class FileHelpers {
26
26
  /**
27
- * @param url - The url of the file.
27
+ * Converts a URL to an ArrayBuffer by fetching the resource.
28
+ *
29
+ * Fetches the resource at the given URL and returns its content as an ArrayBuffer.
30
+ * This is useful for loading binary data like images, videos, or other file types.
31
+ *
32
+ * @param url - The URL of the file to fetch
33
+ * @returns Promise that resolves to the file content as an ArrayBuffer
34
+ * @example
35
+ * ```ts
36
+ * const buffer = await FileHelpers.urlToArrayBuffer('https://example.com/image.png')
37
+ * console.log(buffer.byteLength) // Size of the file in bytes
38
+ * ```
39
+ * @public
28
40
  */
29
41
  static async urlToArrayBuffer(url) {
30
42
  const response = await (0, import_network.fetch)(url);
31
43
  return await response.arrayBuffer();
32
44
  }
45
+ /**
46
+ * Converts a URL to a Blob by fetching the resource.
47
+ *
48
+ * Fetches the resource at the given URL and returns its content as a Blob object.
49
+ * Blobs are useful for handling file data in web applications.
50
+ *
51
+ * @param url - The URL of the file to fetch
52
+ * @returns Promise that resolves to the file content as a Blob
53
+ * @example
54
+ * ```ts
55
+ * const blob = await FileHelpers.urlToBlob('https://example.com/document.pdf')
56
+ * console.log(blob.type) // 'application/pdf'
57
+ * console.log(blob.size) // Size in bytes
58
+ * ```
59
+ * @public
60
+ */
33
61
  static async urlToBlob(url) {
34
62
  const response = await (0, import_network.fetch)(url);
35
63
  return await response.blob();
36
64
  }
65
+ /**
66
+ * Converts a URL to a data URL by fetching the resource.
67
+ *
68
+ * Fetches the resource at the given URL and converts it to a base64-encoded data URL.
69
+ * If the URL is already a data URL, it returns the URL unchanged. This is useful for embedding
70
+ * resources directly in HTML or CSS.
71
+ *
72
+ * @param url - The URL of the file to convert, or an existing data URL
73
+ * @returns Promise that resolves to a data URL string
74
+ * @example
75
+ * ```ts
76
+ * const dataUrl = await FileHelpers.urlToDataUrl('https://example.com/image.jpg')
77
+ * // Returns: 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEA...'
78
+ *
79
+ * const existing = await FileHelpers.urlToDataUrl('data:text/plain;base64,SGVsbG8=')
80
+ * // Returns the same data URL unchanged
81
+ * ```
82
+ * @public
83
+ */
37
84
  static async urlToDataUrl(url) {
38
85
  if (url.startsWith("data:")) return url;
39
86
  const blob = await FileHelpers.urlToBlob(url);
40
87
  return await FileHelpers.blobToDataUrl(blob);
41
88
  }
42
89
  /**
43
- * Convert a file to a base64 encoded data url.
90
+ * Convert a Blob to a base64 encoded data URL.
44
91
  *
45
- * @example
92
+ * Converts a Blob object to a base64-encoded data URL using the FileReader API.
93
+ * This is useful for displaying images or embedding file content directly in HTML.
46
94
  *
95
+ * @param file - The Blob object to convert
96
+ * @returns Promise that resolves to a base64-encoded data URL string
97
+ * @example
47
98
  * ```ts
48
- * const A = FileHelpers.toDataUrl(myImageFile)
49
- * ```
99
+ * const blob = new Blob(['Hello World'], { type: 'text/plain' })
100
+ * const dataUrl = await FileHelpers.blobToDataUrl(blob)
101
+ * // Returns: 'data:text/plain;base64,SGVsbG8gV29ybGQ='
50
102
  *
51
- * @param file - The file as a blob.
103
+ * // With an image file
104
+ * const imageDataUrl = await FileHelpers.blobToDataUrl(myImageFile)
105
+ * // Can be used directly in img src attribute
106
+ * ```
107
+ * @public
52
108
  */
53
109
  static async blobToDataUrl(file) {
54
110
  return await new Promise((resolve, reject) => {
@@ -62,15 +118,24 @@ class FileHelpers {
62
118
  });
63
119
  }
64
120
  /**
65
- * Convert a file to a unicode text string.
121
+ * Convert a Blob to a unicode text string.
66
122
  *
67
- * @example
123
+ * Reads the content of a Blob object as a UTF-8 text string using the FileReader API.
124
+ * This is useful for reading text files or extracting text content from blobs.
68
125
  *
126
+ * @param file - The Blob object to convert to text
127
+ * @returns Promise that resolves to the text content as a string
128
+ * @example
69
129
  * ```ts
70
- * const A = FileHelpers.fileToDataUrl(myTextFile)
71
- * ```
130
+ * const textBlob = new Blob(['Hello World'], { type: 'text/plain' })
131
+ * const text = await FileHelpers.blobToText(textBlob)
132
+ * console.log(text) // 'Hello World'
72
133
  *
73
- * @param file - The file as a blob.
134
+ * // With a text file from user input
135
+ * const content = await FileHelpers.blobToText(myTextFile)
136
+ * console.log(content) // File content as string
137
+ * ```
138
+ * @public
74
139
  */
75
140
  static async blobToText(file) {
76
141
  return await new Promise((resolve, reject) => {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/lib/file.ts"],
4
- "sourcesContent": ["import { fetch } from './network'\n\n/**\n * Helpers for files\n *\n * @public\n */\nexport class FileHelpers {\n\t/**\n\t * @param url - The url of the file.\n\t */\n\tstatic async urlToArrayBuffer(url: string) {\n\t\tconst response = await fetch(url)\n\t\treturn await response.arrayBuffer()\n\t}\n\n\tstatic async urlToBlob(url: string) {\n\t\tconst response = await fetch(url)\n\t\treturn await response.blob()\n\t}\n\n\tstatic async urlToDataUrl(url: string) {\n\t\tif (url.startsWith('data:')) return url\n\t\tconst blob = await FileHelpers.urlToBlob(url)\n\t\treturn await FileHelpers.blobToDataUrl(blob)\n\t}\n\n\t/**\n\t * Convert a file to a base64 encoded data url.\n\t *\n\t * @example\n\t *\n\t * ```ts\n\t * const A = FileHelpers.toDataUrl(myImageFile)\n\t * ```\n\t *\n\t * @param file - The file as a blob.\n\t */\n\tstatic async blobToDataUrl(file: Blob): Promise<string> {\n\t\treturn await new Promise((resolve, reject) => {\n\t\t\tif (file) {\n\t\t\t\tconst reader = new FileReader()\n\t\t\t\treader.onload = () => resolve(reader.result as string)\n\t\t\t\treader.onerror = (error) => reject(error)\n\t\t\t\treader.onabort = (error) => reject(error)\n\t\t\t\treader.readAsDataURL(file)\n\t\t\t}\n\t\t})\n\t}\n\n\t/**\n\t * Convert a file to a unicode text string.\n\t *\n\t * @example\n\t *\n\t * ```ts\n\t * const A = FileHelpers.fileToDataUrl(myTextFile)\n\t * ```\n\t *\n\t * @param file - The file as a blob.\n\t */\n\tstatic async blobToText(file: Blob): Promise<string> {\n\t\treturn await new Promise((resolve, reject) => {\n\t\t\tif (file) {\n\t\t\t\tconst reader = new FileReader()\n\t\t\t\treader.onload = () => resolve(reader.result as string)\n\t\t\t\treader.onerror = (error) => reject(error)\n\t\t\t\treader.onabort = (error) => reject(error)\n\t\t\t\treader.readAsText(file)\n\t\t\t}\n\t\t})\n\t}\n\n\tstatic rewriteMimeType(blob: Blob, newMimeType: string): Blob\n\tstatic rewriteMimeType(blob: File, newMimeType: string): File\n\tstatic rewriteMimeType(blob: Blob | File, newMimeType: string): Blob | File {\n\t\tif (blob.type === newMimeType) return blob\n\t\tif (blob instanceof File) {\n\t\t\treturn new File([blob], blob.name, { type: newMimeType })\n\t\t}\n\t\treturn new Blob([blob], { type: newMimeType })\n\t}\n}\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qBAAsB;AAOf,MAAM,YAAY;AAAA;AAAA;AAAA;AAAA,EAIxB,aAAa,iBAAiB,KAAa;AAC1C,UAAM,WAAW,UAAM,sBAAM,GAAG;AAChC,WAAO,MAAM,SAAS,YAAY;AAAA,EACnC;AAAA,EAEA,aAAa,UAAU,KAAa;AACnC,UAAM,WAAW,UAAM,sBAAM,GAAG;AAChC,WAAO,MAAM,SAAS,KAAK;AAAA,EAC5B;AAAA,EAEA,aAAa,aAAa,KAAa;AACtC,QAAI,IAAI,WAAW,OAAO,EAAG,QAAO;AACpC,UAAM,OAAO,MAAM,YAAY,UAAU,GAAG;AAC5C,WAAO,MAAM,YAAY,cAAc,IAAI;AAAA,EAC5C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,aAAa,cAAc,MAA6B;AACvD,WAAO,MAAM,IAAI,QAAQ,CAAC,SAAS,WAAW;AAC7C,UAAI,MAAM;AACT,cAAM,SAAS,IAAI,WAAW;AAC9B,eAAO,SAAS,MAAM,QAAQ,OAAO,MAAgB;AACrD,eAAO,UAAU,CAAC,UAAU,OAAO,KAAK;AACxC,eAAO,UAAU,CAAC,UAAU,OAAO,KAAK;AACxC,eAAO,cAAc,IAAI;AAAA,MAC1B;AAAA,IACD,CAAC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,aAAa,WAAW,MAA6B;AACpD,WAAO,MAAM,IAAI,QAAQ,CAAC,SAAS,WAAW;AAC7C,UAAI,MAAM;AACT,cAAM,SAAS,IAAI,WAAW;AAC9B,eAAO,SAAS,MAAM,QAAQ,OAAO,MAAgB;AACrD,eAAO,UAAU,CAAC,UAAU,OAAO,KAAK;AACxC,eAAO,UAAU,CAAC,UAAU,OAAO,KAAK;AACxC,eAAO,WAAW,IAAI;AAAA,MACvB;AAAA,IACD,CAAC;AAAA,EACF;AAAA,EAIA,OAAO,gBAAgB,MAAmB,aAAkC;AAC3E,QAAI,KAAK,SAAS,YAAa,QAAO;AACtC,QAAI,gBAAgB,MAAM;AACzB,aAAO,IAAI,KAAK,CAAC,IAAI,GAAG,KAAK,MAAM,EAAE,MAAM,YAAY,CAAC;AAAA,IACzD;AACA,WAAO,IAAI,KAAK,CAAC,IAAI,GAAG,EAAE,MAAM,YAAY,CAAC;AAAA,EAC9C;AACD;",
4
+ "sourcesContent": ["import { fetch } from './network'\n\n/**\n * Utility class providing helper methods for file and blob operations.\n *\n * FileHelpers contains static methods for common file operations including\n * URL fetching, format conversion, and MIME type manipulation. All methods work with\n * web APIs like fetch, FileReader, and Blob/File objects.\n *\n * @example\n * ```ts\n * // Fetch and convert a remote image to data URL\n * const dataUrl = await FileHelpers.urlToDataUrl('https://example.com/image.png')\n *\n * // Convert user-selected file to text\n * const text = await FileHelpers.blobToText(userFile)\n *\n * // Change file MIME type\n * const newFile = FileHelpers.rewriteMimeType(originalFile, 'application/json')\n * ```\n *\n * @public\n */\nexport class FileHelpers {\n\t/**\n\t * Converts a URL to an ArrayBuffer by fetching the resource.\n\t *\n\t * Fetches the resource at the given URL and returns its content as an ArrayBuffer.\n\t * This is useful for loading binary data like images, videos, or other file types.\n\t *\n\t * @param url - The URL of the file to fetch\n\t * @returns Promise that resolves to the file content as an ArrayBuffer\n\t * @example\n\t * ```ts\n\t * const buffer = await FileHelpers.urlToArrayBuffer('https://example.com/image.png')\n\t * console.log(buffer.byteLength) // Size of the file in bytes\n\t * ```\n\t * @public\n\t */\n\tstatic async urlToArrayBuffer(url: string) {\n\t\tconst response = await fetch(url)\n\t\treturn await response.arrayBuffer()\n\t}\n\n\t/**\n\t * Converts a URL to a Blob by fetching the resource.\n\t *\n\t * Fetches the resource at the given URL and returns its content as a Blob object.\n\t * Blobs are useful for handling file data in web applications.\n\t *\n\t * @param url - The URL of the file to fetch\n\t * @returns Promise that resolves to the file content as a Blob\n\t * @example\n\t * ```ts\n\t * const blob = await FileHelpers.urlToBlob('https://example.com/document.pdf')\n\t * console.log(blob.type) // 'application/pdf'\n\t * console.log(blob.size) // Size in bytes\n\t * ```\n\t * @public\n\t */\n\tstatic async urlToBlob(url: string) {\n\t\tconst response = await fetch(url)\n\t\treturn await response.blob()\n\t}\n\n\t/**\n\t * Converts a URL to a data URL by fetching the resource.\n\t *\n\t * Fetches the resource at the given URL and converts it to a base64-encoded data URL.\n\t * If the URL is already a data URL, it returns the URL unchanged. This is useful for embedding\n\t * resources directly in HTML or CSS.\n\t *\n\t * @param url - The URL of the file to convert, or an existing data URL\n\t * @returns Promise that resolves to a data URL string\n\t * @example\n\t * ```ts\n\t * const dataUrl = await FileHelpers.urlToDataUrl('https://example.com/image.jpg')\n\t * // Returns: 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEA...'\n\t *\n\t * const existing = await FileHelpers.urlToDataUrl('data:text/plain;base64,SGVsbG8=')\n\t * // Returns the same data URL unchanged\n\t * ```\n\t * @public\n\t */\n\tstatic async urlToDataUrl(url: string) {\n\t\tif (url.startsWith('data:')) return url\n\t\tconst blob = await FileHelpers.urlToBlob(url)\n\t\treturn await FileHelpers.blobToDataUrl(blob)\n\t}\n\n\t/**\n\t * Convert a Blob to a base64 encoded data URL.\n\t *\n\t * Converts a Blob object to a base64-encoded data URL using the FileReader API.\n\t * This is useful for displaying images or embedding file content directly in HTML.\n\t *\n\t * @param file - The Blob object to convert\n\t * @returns Promise that resolves to a base64-encoded data URL string\n\t * @example\n\t * ```ts\n\t * const blob = new Blob(['Hello World'], { type: 'text/plain' })\n\t * const dataUrl = await FileHelpers.blobToDataUrl(blob)\n\t * // Returns: 'data:text/plain;base64,SGVsbG8gV29ybGQ='\n\t *\n\t * // With an image file\n\t * const imageDataUrl = await FileHelpers.blobToDataUrl(myImageFile)\n\t * // Can be used directly in img src attribute\n\t * ```\n\t * @public\n\t */\n\tstatic async blobToDataUrl(file: Blob): Promise<string> {\n\t\treturn await new Promise((resolve, reject) => {\n\t\t\tif (file) {\n\t\t\t\tconst reader = new FileReader()\n\t\t\t\treader.onload = () => resolve(reader.result as string)\n\t\t\t\treader.onerror = (error) => reject(error)\n\t\t\t\treader.onabort = (error) => reject(error)\n\t\t\t\treader.readAsDataURL(file)\n\t\t\t}\n\t\t})\n\t}\n\n\t/**\n\t * Convert a Blob to a unicode text string.\n\t *\n\t * Reads the content of a Blob object as a UTF-8 text string using the FileReader API.\n\t * This is useful for reading text files or extracting text content from blobs.\n\t *\n\t * @param file - The Blob object to convert to text\n\t * @returns Promise that resolves to the text content as a string\n\t * @example\n\t * ```ts\n\t * const textBlob = new Blob(['Hello World'], { type: 'text/plain' })\n\t * const text = await FileHelpers.blobToText(textBlob)\n\t * console.log(text) // 'Hello World'\n\t *\n\t * // With a text file from user input\n\t * const content = await FileHelpers.blobToText(myTextFile)\n\t * console.log(content) // File content as string\n\t * ```\n\t * @public\n\t */\n\tstatic async blobToText(file: Blob): Promise<string> {\n\t\treturn await new Promise((resolve, reject) => {\n\t\t\tif (file) {\n\t\t\t\tconst reader = new FileReader()\n\t\t\t\treader.onload = () => resolve(reader.result as string)\n\t\t\t\treader.onerror = (error) => reject(error)\n\t\t\t\treader.onabort = (error) => reject(error)\n\t\t\t\treader.readAsText(file)\n\t\t\t}\n\t\t})\n\t}\n\n\t/**\n\t * Creates a new Blob or File with a different MIME type.\n\t *\n\t * Creates a copy of the given Blob or File with a new MIME type while preserving\n\t * all other properties. If the current MIME type already matches the new one, returns the\n\t * original object unchanged. For File objects, preserves the filename.\n\t *\n\t * @param blob - The Blob or File object to modify\n\t * @param newMimeType - The new MIME type to assign\n\t * @returns A new Blob or File with the updated MIME type\n\t * @example\n\t * ```ts\n\t * // Change a generic blob to a specific image type\n\t * const blob = new Blob([imageData])\n\t * const imageBlob = FileHelpers.rewriteMimeType(blob, 'image/png')\n\t *\n\t * // Change a file's MIME type while preserving filename\n\t * const file = new File([data], 'document.txt', { type: 'text/plain' })\n\t * const jsonFile = FileHelpers.rewriteMimeType(file, 'application/json')\n\t * console.log(jsonFile.name) // 'document.txt' (preserved)\n\t * console.log(jsonFile.type) // 'application/json' (updated)\n\t * ```\n\t * @public\n\t */\n\tstatic rewriteMimeType(blob: Blob, newMimeType: string): Blob\n\tstatic rewriteMimeType(blob: File, newMimeType: string): File\n\tstatic rewriteMimeType(blob: Blob | File, newMimeType: string): Blob | File {\n\t\tif (blob.type === newMimeType) return blob\n\t\tif (blob instanceof File) {\n\t\t\treturn new File([blob], blob.name, { type: newMimeType })\n\t\t}\n\t\treturn new Blob([blob], { type: newMimeType })\n\t}\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qBAAsB;AAuBf,MAAM,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgBxB,aAAa,iBAAiB,KAAa;AAC1C,UAAM,WAAW,UAAM,sBAAM,GAAG;AAChC,WAAO,MAAM,SAAS,YAAY;AAAA,EACnC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkBA,aAAa,UAAU,KAAa;AACnC,UAAM,WAAW,UAAM,sBAAM,GAAG;AAChC,WAAO,MAAM,SAAS,KAAK;AAAA,EAC5B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAqBA,aAAa,aAAa,KAAa;AACtC,QAAI,IAAI,WAAW,OAAO,EAAG,QAAO;AACpC,UAAM,OAAO,MAAM,YAAY,UAAU,GAAG;AAC5C,WAAO,MAAM,YAAY,cAAc,IAAI;AAAA,EAC5C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAsBA,aAAa,cAAc,MAA6B;AACvD,WAAO,MAAM,IAAI,QAAQ,CAAC,SAAS,WAAW;AAC7C,UAAI,MAAM;AACT,cAAM,SAAS,IAAI,WAAW;AAC9B,eAAO,SAAS,MAAM,QAAQ,OAAO,MAAgB;AACrD,eAAO,UAAU,CAAC,UAAU,OAAO,KAAK;AACxC,eAAO,UAAU,CAAC,UAAU,OAAO,KAAK;AACxC,eAAO,cAAc,IAAI;AAAA,MAC1B;AAAA,IACD,CAAC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAsBA,aAAa,WAAW,MAA6B;AACpD,WAAO,MAAM,IAAI,QAAQ,CAAC,SAAS,WAAW;AAC7C,UAAI,MAAM;AACT,cAAM,SAAS,IAAI,WAAW;AAC9B,eAAO,SAAS,MAAM,QAAQ,OAAO,MAAgB;AACrD,eAAO,UAAU,CAAC,UAAU,OAAO,KAAK;AACxC,eAAO,UAAU,CAAC,UAAU,OAAO,KAAK;AACxC,eAAO,WAAW,IAAI;AAAA,MACvB;AAAA,IACD,CAAC;AAAA,EACF;AAAA,EA4BA,OAAO,gBAAgB,MAAmB,aAAkC;AAC3E,QAAI,KAAK,SAAS,YAAa,QAAO;AACtC,QAAI,gBAAgB,MAAM;AACzB,aAAO,IAAI,KAAK,CAAC,IAAI,GAAG,KAAK,MAAM,EAAE,MAAM,YAAY,CAAC;AAAA,IACzD;AACA,WAAO,IAAI,KAAK,CAAC,IAAI,GAAG,EAAE,MAAM,YAAY,CAAC;AAAA,EAC9C;AACD;",
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 * @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 const noop: () => void = () => {}\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAUO,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,MAAM,OAAmB,MAAM;AAAC;",
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 const 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,MAAM,OAAmB,MAAM;AAAC;",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/lib/hash.ts"],
4
- "sourcesContent": ["/**\n * Hash a string using the FNV-1a algorithm.\n *\n * @public\n */\nexport function getHashForString(string: string) {\n\tlet hash = 0\n\tfor (let i = 0; i < string.length; i++) {\n\t\thash = (hash << 5) - hash + string.charCodeAt(i)\n\t\thash |= 0 // Convert to 32bit integer\n\t}\n\treturn hash + ''\n}\n\n/**\n * Hash a string using the FNV-1a algorithm.\n *\n * @public\n */\nexport function getHashForObject(obj: any) {\n\treturn getHashForString(JSON.stringify(obj))\n}\n\n/**\n * Hash an ArrayBuffer using the FNV-1a algorithm.\n *\n * @public\n */\nexport function getHashForBuffer(buffer: ArrayBuffer) {\n\tconst view = new DataView(buffer)\n\tlet hash = 0\n\tfor (let i = 0; i < view.byteLength; i++) {\n\t\thash = (hash << 5) - hash + view.getUint8(i)\n\t\thash |= 0 // Convert to 32bit integer\n\t}\n\treturn hash + ''\n}\n\n/** @public */\nexport function lns(str: string) {\n\tconst result = str.split('')\n\tresult.push(...result.splice(0, Math.round(result.length / 5)))\n\tresult.push(...result.splice(0, Math.round(result.length / 4)))\n\tresult.push(...result.splice(0, Math.round(result.length / 3)))\n\tresult.push(...result.splice(0, Math.round(result.length / 2)))\n\treturn result\n\t\t.reverse()\n\t\t.map((n) => (+n ? (+n < 5 ? 5 + +n : +n > 5 ? +n - 5 : n) : n))\n\t\t.join('')\n}\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAKO,SAAS,iBAAiB,QAAgB;AAChD,MAAI,OAAO;AACX,WAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;AACvC,YAAQ,QAAQ,KAAK,OAAO,OAAO,WAAW,CAAC;AAC/C,YAAQ;AAAA,EACT;AACA,SAAO,OAAO;AACf;AAOO,SAAS,iBAAiB,KAAU;AAC1C,SAAO,iBAAiB,KAAK,UAAU,GAAG,CAAC;AAC5C;AAOO,SAAS,iBAAiB,QAAqB;AACrD,QAAM,OAAO,IAAI,SAAS,MAAM;AAChC,MAAI,OAAO;AACX,WAAS,IAAI,GAAG,IAAI,KAAK,YAAY,KAAK;AACzC,YAAQ,QAAQ,KAAK,OAAO,KAAK,SAAS,CAAC;AAC3C,YAAQ;AAAA,EACT;AACA,SAAO,OAAO;AACf;AAGO,SAAS,IAAI,KAAa;AAChC,QAAM,SAAS,IAAI,MAAM,EAAE;AAC3B,SAAO,KAAK,GAAG,OAAO,OAAO,GAAG,KAAK,MAAM,OAAO,SAAS,CAAC,CAAC,CAAC;AAC9D,SAAO,KAAK,GAAG,OAAO,OAAO,GAAG,KAAK,MAAM,OAAO,SAAS,CAAC,CAAC,CAAC;AAC9D,SAAO,KAAK,GAAG,OAAO,OAAO,GAAG,KAAK,MAAM,OAAO,SAAS,CAAC,CAAC,CAAC;AAC9D,SAAO,KAAK,GAAG,OAAO,OAAO,GAAG,KAAK,MAAM,OAAO,SAAS,CAAC,CAAC,CAAC;AAC9D,SAAO,OACL,QAAQ,EACR,IAAI,CAAC,MAAO,CAAC,IAAK,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,IAAI,IAAK,CAAE,EAC7D,KAAK,EAAE;AACV;",
4
+ "sourcesContent": ["/**\n * Hash a string using the FNV-1a algorithm.\n *\n * Generates a deterministic hash value for a given string using a variant of the FNV-1a\n * (Fowler-Noll-Vo) algorithm. The hash is returned as a string representation of a 32-bit integer.\n *\n * @param string - The input string to hash\n * @returns A string representation of the 32-bit hash value\n * @example\n * ```ts\n * const hash = getHashForString('hello world')\n * console.log(hash) // '-862545276'\n *\n * // Same input always produces same hash\n * const hash2 = getHashForString('hello world')\n * console.log(hash === hash2) // true\n * ```\n * @public\n */\nexport function getHashForString(string: string) {\n\tlet hash = 0\n\tfor (let i = 0; i < string.length; i++) {\n\t\thash = (hash << 5) - hash + string.charCodeAt(i)\n\t\thash |= 0 // Convert to 32bit integer\n\t}\n\treturn hash + ''\n}\n\n/**\n * Hash an object by converting it to JSON and then hashing the resulting string.\n *\n * Converts the object to a JSON string using JSON.stringify and then applies the same\n * hashing algorithm as getHashForString. Useful for creating consistent hash values\n * for objects, though the hash depends on JSON serialization order.\n *\n * @param obj - The object to hash (any JSON-serializable value)\n * @returns A string representation of the 32-bit hash value\n * @example\n * ```ts\n * const hash1 = getHashForObject({ name: 'John', age: 30 })\n * const hash2 = getHashForObject({ name: 'John', age: 30 })\n * console.log(hash1 === hash2) // true\n *\n * // Arrays work too\n * const arrayHash = getHashForObject([1, 2, 3, 'hello'])\n * console.log(arrayHash) // '-123456789'\n * ```\n * @public\n */\nexport function getHashForObject(obj: any) {\n\treturn getHashForString(JSON.stringify(obj))\n}\n\n/**\n * Hash an ArrayBuffer using the FNV-1a algorithm.\n *\n * Generates a deterministic hash value for binary data stored in an ArrayBuffer.\n * Processes the buffer byte by byte using the same hashing algorithm as getHashForString.\n * Useful for creating consistent identifiers for binary data like images or files.\n *\n * @param buffer - The ArrayBuffer containing binary data to hash\n * @returns A string representation of the 32-bit hash value\n * @example\n * ```ts\n * // Hash some binary data\n * const data = new Uint8Array([1, 2, 3, 4, 5])\n * const hash = getHashForBuffer(data.buffer)\n * console.log(hash) // '123456789'\n *\n * // Hash image file data\n * const fileBuffer = await file.arrayBuffer()\n * const fileHash = getHashForBuffer(fileBuffer)\n * console.log(fileHash) // Unique hash for the file\n * ```\n * @public\n */\nexport function getHashForBuffer(buffer: ArrayBuffer) {\n\tconst view = new DataView(buffer)\n\tlet hash = 0\n\tfor (let i = 0; i < view.byteLength; i++) {\n\t\thash = (hash << 5) - hash + view.getUint8(i)\n\t\thash |= 0 // Convert to 32bit integer\n\t}\n\treturn hash + ''\n}\n\n/**\n * Applies a string transformation algorithm that rearranges and modifies characters.\n *\n * Performs a series of character manipulations on the input string including\n * character repositioning through splicing operations and numeric character transformations.\n * This appears to be a custom encoding/obfuscation function.\n *\n * @param str - The input string to transform\n * @returns The transformed string after applying all manipulations\n * @example\n * ```ts\n * const result = lns('hello123')\n * console.log(result) // Transformed string (exact output depends on algorithm)\n *\n * // Can be used for simple string obfuscation\n * const obfuscated = lns('sensitive-data')\n * console.log(obfuscated) // Obfuscated version\n * ```\n * @public\n */\nexport function lns(str: string) {\n\tconst result = str.split('')\n\tresult.push(...result.splice(0, Math.round(result.length / 5)))\n\tresult.push(...result.splice(0, Math.round(result.length / 4)))\n\tresult.push(...result.splice(0, Math.round(result.length / 3)))\n\tresult.push(...result.splice(0, Math.round(result.length / 2)))\n\treturn result\n\t\t.reverse()\n\t\t.map((n) => (+n ? (+n < 5 ? 5 + +n : +n > 5 ? +n - 5 : n) : n))\n\t\t.join('')\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAmBO,SAAS,iBAAiB,QAAgB;AAChD,MAAI,OAAO;AACX,WAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;AACvC,YAAQ,QAAQ,KAAK,OAAO,OAAO,WAAW,CAAC;AAC/C,YAAQ;AAAA,EACT;AACA,SAAO,OAAO;AACf;AAuBO,SAAS,iBAAiB,KAAU;AAC1C,SAAO,iBAAiB,KAAK,UAAU,GAAG,CAAC;AAC5C;AAyBO,SAAS,iBAAiB,QAAqB;AACrD,QAAM,OAAO,IAAI,SAAS,MAAM;AAChC,MAAI,OAAO;AACX,WAAS,IAAI,GAAG,IAAI,KAAK,YAAY,KAAK;AACzC,YAAQ,QAAQ,KAAK,OAAO,KAAK,SAAS,CAAC;AAC3C,YAAQ;AAAA,EACT;AACA,SAAO,OAAO;AACf;AAsBO,SAAS,IAAI,KAAa;AAChC,QAAM,SAAS,IAAI,MAAM,EAAE;AAC3B,SAAO,KAAK,GAAG,OAAO,OAAO,GAAG,KAAK,MAAM,OAAO,SAAS,CAAC,CAAC,CAAC;AAC9D,SAAO,KAAK,GAAG,OAAO,OAAO,GAAG,KAAK,MAAM,OAAO,SAAS,CAAC,CAAC,CAAC;AAC9D,SAAO,KAAK,GAAG,OAAO,OAAO,GAAG,KAAK,MAAM,OAAO,SAAS,CAAC,CAAC,CAAC;AAC9D,SAAO,KAAK,GAAG,OAAO,OAAO,GAAG,KAAK,MAAM,OAAO,SAAS,CAAC,CAAC,CAAC;AAC9D,SAAO,OACL,QAAQ,EACR,IAAI,CAAC,MAAO,CAAC,IAAK,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,IAAI,IAAK,CAAE,EAC7D,KAAK,EAAE;AACV;",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/lib/id.ts"],
4
- "sourcesContent": ["/*!\n * MIT License: https://github.com/ai/nanoid/blob/main/LICENSE\n * Modified code originally from <https://github.com/ai/nanoid>\n * Copyright 2017 Andrey Sitnik <andrey@sitnik.ru>\n *\n * `nanoid` is currently only distributed as an ES module. Some tools (jest, playwright) don't\n * properly support ESM-only code yet, and tldraw itself is distributed as both an ES module and a\n * CommonJS module. By including nanoid here, we can make sure it works well in every environment\n * where tldraw is used. We can also remove some unused features like custom alphabets.\n */\n\n// all environments that tldraw runs in (browser, workers, recent node versions) have global\n// `crypto`\nconst crypto = globalThis.crypto\n\n// This alphabet uses `A-Za-z0-9_-` symbols.\n// The order of characters is optimized for better gzip and brotli compression.\n// Same as in non-secure/index.js\nconst urlAlphabet = 'useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict'\n\n// It is best to make fewer, larger requests to the crypto module to\n// avoid system call overhead. So, random numbers are generated in a\n// pool. The pool is a Buffer that is larger than the initial random\n// request size by this multiplier. The pool is enlarged if subsequent\n// requests exceed the maximum buffer size.\nconst POOL_SIZE_MULTIPLIER = 128\nlet pool: Uint8Array, poolOffset: number\n\nfunction fillPool(bytes: number) {\n\tif (!pool || pool.length < bytes) {\n\t\tpool = new Uint8Array(bytes * POOL_SIZE_MULTIPLIER)\n\t\tcrypto.getRandomValues(pool)\n\t\tpoolOffset = 0\n\t} else if (poolOffset + bytes > pool.length) {\n\t\tcrypto.getRandomValues(pool)\n\t\tpoolOffset = 0\n\t}\n\tpoolOffset += bytes\n}\n\nfunction nanoid(size = 21) {\n\t// `-=` convert `size` to number to prevent `valueOf` abusing\n\tfillPool((size -= 0))\n\tlet id = ''\n\t// We are reading directly from the random pool to avoid creating new array\n\tfor (let i = poolOffset - size; i < poolOffset; i++) {\n\t\t// It is incorrect to use bytes exceeding the alphabet size.\n\t\t// The following mask reduces the random byte in the 0-255 value\n\t\t// range to the 0-63 value range. Therefore, adding hacks, such\n\t\t// as empty string fallback or magic numbers, is unnecessary because\n\t\t// the bitmask trims bytes down to the alphabet size.\n\t\tid += urlAlphabet[pool[i] & 63]\n\t}\n\treturn id\n}\n\nlet impl = nanoid\n/** @internal */\nexport function mockUniqueId(fn: (size?: number) => string) {\n\timpl = fn\n}\n\n/** @internal */\nexport function restoreUniqueId() {\n\timpl = nanoid\n}\n\n/**\n * Generate a unique id.\n *\n * @example\n *\n * ```ts\n * const id = uniqueId()\n * ```\n *\n * @public\n */\nexport function uniqueId(size?: number): string {\n\treturn impl(size)\n}\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAaA,MAAM,SAAS,WAAW;AAK1B,MAAM,cAAc;AAOpB,MAAM,uBAAuB;AAC7B,IAAI,MAAkB;AAEtB,SAAS,SAAS,OAAe;AAChC,MAAI,CAAC,QAAQ,KAAK,SAAS,OAAO;AACjC,WAAO,IAAI,WAAW,QAAQ,oBAAoB;AAClD,WAAO,gBAAgB,IAAI;AAC3B,iBAAa;AAAA,EACd,WAAW,aAAa,QAAQ,KAAK,QAAQ;AAC5C,WAAO,gBAAgB,IAAI;AAC3B,iBAAa;AAAA,EACd;AACA,gBAAc;AACf;AAEA,SAAS,OAAO,OAAO,IAAI;AAE1B,WAAU,QAAQ,CAAE;AACpB,MAAI,KAAK;AAET,WAAS,IAAI,aAAa,MAAM,IAAI,YAAY,KAAK;AAMpD,UAAM,YAAY,KAAK,CAAC,IAAI,EAAE;AAAA,EAC/B;AACA,SAAO;AACR;AAEA,IAAI,OAAO;AAEJ,SAAS,aAAa,IAA+B;AAC3D,SAAO;AACR;AAGO,SAAS,kBAAkB;AACjC,SAAO;AACR;AAaO,SAAS,SAAS,MAAuB;AAC/C,SAAO,KAAK,IAAI;AACjB;",
4
+ "sourcesContent": ["/*!\n * MIT License: https://github.com/ai/nanoid/blob/main/LICENSE\n * Modified code originally from <https://github.com/ai/nanoid>\n * Copyright 2017 Andrey Sitnik <andrey@sitnik.ru>\n *\n * `nanoid` is currently only distributed as an ES module. Some tools (jest, playwright) don't\n * properly support ESM-only code yet, and tldraw itself is distributed as both an ES module and a\n * CommonJS module. By including nanoid here, we can make sure it works well in every environment\n * where tldraw is used. We can also remove some unused features like custom alphabets.\n */\n\n// all environments that tldraw runs in (browser, workers, recent node versions) have global\n// `crypto`\nconst crypto = globalThis.crypto\n\n// This alphabet uses `A-Za-z0-9_-` symbols.\n// The order of characters is optimized for better gzip and brotli compression.\n// Same as in non-secure/index.js\nconst urlAlphabet = 'useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict'\n\n// It is best to make fewer, larger requests to the crypto module to\n// avoid system call overhead. So, random numbers are generated in a\n// pool. The pool is a Buffer that is larger than the initial random\n// request size by this multiplier. The pool is enlarged if subsequent\n// requests exceed the maximum buffer size.\nconst POOL_SIZE_MULTIPLIER = 128\nlet pool: Uint8Array, poolOffset: number\n\nfunction fillPool(bytes: number) {\n\tif (!pool || pool.length < bytes) {\n\t\tpool = new Uint8Array(bytes * POOL_SIZE_MULTIPLIER)\n\t\tcrypto.getRandomValues(pool)\n\t\tpoolOffset = 0\n\t} else if (poolOffset + bytes > pool.length) {\n\t\tcrypto.getRandomValues(pool)\n\t\tpoolOffset = 0\n\t}\n\tpoolOffset += bytes\n}\n\nfunction nanoid(size = 21) {\n\t// `-=` convert `size` to number to prevent `valueOf` abusing\n\tfillPool((size -= 0))\n\tlet id = ''\n\t// We are reading directly from the random pool to avoid creating new array\n\tfor (let i = poolOffset - size; i < poolOffset; i++) {\n\t\t// It is incorrect to use bytes exceeding the alphabet size.\n\t\t// The following mask reduces the random byte in the 0-255 value\n\t\t// range to the 0-63 value range. Therefore, adding hacks, such\n\t\t// as empty string fallback or magic numbers, is unnecessary because\n\t\t// the bitmask trims bytes down to the alphabet size.\n\t\tid += urlAlphabet[pool[i] & 63]\n\t}\n\treturn id\n}\n\nlet impl = nanoid\n/**\n * Mock the unique ID generator with a custom implementation for testing.\n *\n * Replaces the internal ID generation function with a custom one. This is useful\n * for testing scenarios where you need predictable or deterministic IDs.\n *\n * @param fn - The mock function that should return a string ID. Takes optional size parameter.\n * @example\n * ```ts\n * // Mock with predictable IDs for testing\n * mockUniqueId((size = 21) => 'test-id-' + size)\n * console.log(uniqueId()) // 'test-id-21'\n * console.log(uniqueId(10)) // 'test-id-10'\n *\n * // Restore original implementation when done\n * restoreUniqueId()\n * ```\n * @internal\n */\nexport function mockUniqueId(fn: (size?: number) => string) {\n\timpl = fn\n}\n\n/**\n * Restore the original unique ID generator after mocking.\n *\n * Resets the ID generation function back to the original nanoid implementation.\n * This should be called after testing to restore normal ID generation behavior.\n *\n * @example\n * ```ts\n * // After mocking for tests\n * mockUniqueId(() => 'mock-id')\n *\n * // Restore original behavior\n * restoreUniqueId()\n * console.log(uniqueId()) // Now generates real random IDs again\n * ```\n * @internal\n */\nexport function restoreUniqueId() {\n\timpl = nanoid\n}\n\n/**\n * Generate a unique ID using a modified nanoid algorithm.\n *\n * Generates a cryptographically secure random string ID using URL-safe characters.\n * The default size is 21 characters, which provides a good balance of uniqueness\n * and brevity. Uses the global crypto API for secure random number generation.\n *\n * @param size - Optional length of the generated ID (defaults to 21 characters)\n * @returns A unique string identifier\n * @example\n * ```ts\n * // Generate default 21-character ID\n * const id = uniqueId()\n * console.log(id) // 'V1StGXR8_Z5jdHi6B-myT'\n *\n * // Generate shorter ID\n * const shortId = uniqueId(10)\n * console.log(shortId) // 'V1StGXR8_Z'\n *\n * // Generate longer ID\n * const longId = uniqueId(32)\n * console.log(longId) // 'V1StGXR8_Z5jdHi6B-myTVKahvjdx...'\n * ```\n * @public\n */\nexport function uniqueId(size?: number): string {\n\treturn impl(size)\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAaA,MAAM,SAAS,WAAW;AAK1B,MAAM,cAAc;AAOpB,MAAM,uBAAuB;AAC7B,IAAI,MAAkB;AAEtB,SAAS,SAAS,OAAe;AAChC,MAAI,CAAC,QAAQ,KAAK,SAAS,OAAO;AACjC,WAAO,IAAI,WAAW,QAAQ,oBAAoB;AAClD,WAAO,gBAAgB,IAAI;AAC3B,iBAAa;AAAA,EACd,WAAW,aAAa,QAAQ,KAAK,QAAQ;AAC5C,WAAO,gBAAgB,IAAI;AAC3B,iBAAa;AAAA,EACd;AACA,gBAAc;AACf;AAEA,SAAS,OAAO,OAAO,IAAI;AAE1B,WAAU,QAAQ,CAAE;AACpB,MAAI,KAAK;AAET,WAAS,IAAI,aAAa,MAAM,IAAI,YAAY,KAAK;AAMpD,UAAM,YAAY,KAAK,CAAC,IAAI,EAAE;AAAA,EAC/B;AACA,SAAO;AACR;AAEA,IAAI,OAAO;AAoBJ,SAAS,aAAa,IAA+B;AAC3D,SAAO;AACR;AAmBO,SAAS,kBAAkB;AACjC,SAAO;AACR;AA2BO,SAAS,SAAS,MAAuB;AAC/C,SAAO,KAAK,IAAI;AACjB;",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/lib/iterable.ts"],
4
- "sourcesContent": ["/**\n * Get the first item from an iterable Set or Map.\n *\n * @example\n *\n * ```ts\n * const A = getFirstItem(new Set([1, 2, 3])) // 1\n * const B = getFirstItem(\n * \tnew Map([\n * \t\t['a', 1],\n * \t\t['b', 2],\n * \t])\n * ) // 1\n * ```\n *\n * @param value - The iterable Set or Map.\n * @public\n */\nexport function getFirstFromIterable<T = unknown>(set: Set<T> | Map<any, T>): T {\n\treturn set.values().next().value!\n}\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAkBO,SAAS,qBAAkC,KAA8B;AAC/E,SAAO,IAAI,OAAO,EAAE,KAAK,EAAE;AAC5B;",
4
+ "sourcesContent": ["/**\n * Get the first item from an iterable Set or Map.\n *\n * @param value - The iterable Set or Map to get the first item from\n * @returns The first value from the Set or Map\n * @example\n * ```ts\n * const A = getFirstFromIterable(new Set([1, 2, 3])) // 1\n * const B = getFirstFromIterable(\n * \tnew Map([\n * \t\t['a', 1],\n * \t\t['b', 2],\n * \t])\n * ) // 1\n * ```\n * @public\n */\nexport function getFirstFromIterable<T = unknown>(set: Set<T> | Map<any, T>): T {\n\treturn set.values().next().value!\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAiBO,SAAS,qBAAkC,KAA8B;AAC/E,SAAO,IAAI,OAAO,EAAE,KAAK,EAAE;AAC5B;",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/lib/json-value.ts"],
4
- "sourcesContent": ["/** @public */\nexport type JsonValue = JsonPrimitive | JsonArray | JsonObject\n/** @public */\nexport type JsonPrimitive = boolean | null | string | number\n/** @public */\nexport type JsonArray = JsonValue[]\n/** @public */\nexport interface JsonObject {\n\t[key: string]: JsonValue | undefined\n}\n"],
4
+ "sourcesContent": ["/**\n * A type that represents any valid JSON value. This includes primitives (boolean, null, string, number),\n * arrays of JSON values, and objects with string keys and JSON values.\n *\n * @example\n * ```ts\n * const jsonData: JsonValue = {\n * name: \"Alice\",\n * age: 30,\n * active: true,\n * tags: [\"user\", \"premium\"],\n * metadata: null\n * }\n * ```\n *\n * @public\n */\nexport type JsonValue = JsonPrimitive | JsonArray | JsonObject\n\n/**\n * A type representing JSON primitive values: boolean, null, string, or number.\n * These are the atomic values that can appear in JSON data.\n *\n * @example\n * ```ts\n * const primitives: JsonPrimitive[] = [\n * true,\n * null,\n * \"hello\",\n * 42\n * ]\n * ```\n *\n * @public\n */\nexport type JsonPrimitive = boolean | null | string | number\n\n/**\n * A type representing a JSON array containing any valid JSON values.\n * Arrays can contain mixed types of JSON values including nested arrays and objects.\n *\n * @example\n * ```ts\n * const jsonArray: JsonArray = [\n * \"text\",\n * 123,\n * true,\n * { nested: \"object\" },\n * [1, 2, 3]\n * ]\n * ```\n *\n * @public\n */\nexport type JsonArray = JsonValue[]\n\n/**\n * A type representing a JSON object with string keys and JSON values.\n * Object values can be undefined to handle optional properties safely.\n *\n * @example\n * ```ts\n * const jsonObject: JsonObject = {\n * required: \"value\",\n * optional: undefined,\n * nested: {\n * deep: \"property\"\n * },\n * array: [1, 2, 3]\n * }\n * ```\n *\n * @public\n */\nexport interface JsonObject {\n\t[key: string]: JsonValue | undefined\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/media/apng.ts"],
4
- "sourcesContent": ["/*!\n * MIT License: https://github.com/vHeemstra/is-apng/blob/main/license\n * Copyright (c) Philip van Heemstra\n */\n\nexport function isApngAnimated(buffer: ArrayBuffer): boolean {\n\tconst view = new Uint8Array(buffer)\n\n\tif (\n\t\t!view ||\n\t\t!((typeof Buffer !== 'undefined' && Buffer.isBuffer(view)) || view instanceof Uint8Array) ||\n\t\tview.length < 16\n\t) {\n\t\treturn false\n\t}\n\n\tconst isPNG =\n\t\tview[0] === 0x89 &&\n\t\tview[1] === 0x50 &&\n\t\tview[2] === 0x4e &&\n\t\tview[3] === 0x47 &&\n\t\tview[4] === 0x0d &&\n\t\tview[5] === 0x0a &&\n\t\tview[6] === 0x1a &&\n\t\tview[7] === 0x0a\n\n\tif (!isPNG) {\n\t\treturn false\n\t}\n\n\t/**\n\t * Returns the index of the first occurrence of a sequence in an typed array, or -1 if it is not present.\n\t *\n\t * Works similar to `Array.prototype.indexOf()`, but it searches for a sequence of array values (bytes).\n\t * The bytes in the `haystack` array are decoded (UTF-8) and then used to search for `needle`.\n\t *\n\t * @param haystack `Uint8Array`\n\t * Array to search in.\n\t *\n\t * @param needle `string | RegExp`\n\t * The value to locate in the array.\n\t *\n\t * @param fromIndex `number`\n\t * The array index at which to begin the search.\n\t *\n\t * @param upToIndex `number`\n\t * The array index up to which to search.\n\t * If omitted, search until the end.\n\t *\n\t * @param chunksize `number`\n\t * Size of the chunks used when searching (default 1024).\n\t *\n\t * @returns boolean\n\t * Whether the array holds Animated PNG data.\n\t */\n\tfunction indexOfSubstring(\n\t\thaystack: Uint8Array,\n\t\tneedle: string | RegExp,\n\t\tfromIndex: number,\n\t\tupToIndex?: number,\n\t\tchunksize = 1024 /* Bytes */\n\t) {\n\t\t/**\n\t\t * Adopted from: https://stackoverflow.com/a/67771214/2142071\n\t\t */\n\n\t\tif (!needle) {\n\t\t\treturn -1\n\t\t}\n\t\tneedle = new RegExp(needle, 'g')\n\n\t\t// The needle could get split over two chunks.\n\t\t// So, at every chunk we prepend the last few characters\n\t\t// of the last chunk.\n\t\tconst needle_length = needle.source.length\n\t\tconst decoder = new TextDecoder()\n\n\t\t// Handle search offset in line with\n\t\t// `Array.prototype.indexOf()` and `TypedArray.prototype.subarray()`.\n\t\tconst full_haystack_length = haystack.length\n\t\tif (typeof upToIndex === 'undefined') {\n\t\t\tupToIndex = full_haystack_length\n\t\t}\n\t\tif (fromIndex >= full_haystack_length || upToIndex <= 0 || fromIndex >= upToIndex) {\n\t\t\treturn -1\n\t\t}\n\t\thaystack = haystack.subarray(fromIndex, upToIndex)\n\n\t\tlet position = -1\n\t\tlet current_index = 0\n\t\tlet full_length = 0\n\t\tlet needle_buffer = ''\n\n\t\touter: while (current_index < haystack.length) {\n\t\t\tconst next_index = current_index + chunksize\n\t\t\t// subarray doesn't copy\n\t\t\tconst chunk = haystack.subarray(current_index, next_index)\n\t\t\tconst decoded = decoder.decode(chunk, { stream: true })\n\n\t\t\tconst text = needle_buffer + decoded\n\n\t\t\tlet match: RegExpExecArray | null\n\t\t\tlet last_index = -1\n\t\t\twhile ((match = needle.exec(text)) !== null) {\n\t\t\t\tlast_index = match.index - needle_buffer.length\n\t\t\t\tposition = full_length + last_index\n\t\t\t\tbreak outer\n\t\t\t}\n\n\t\t\tcurrent_index = next_index\n\t\t\tfull_length += decoded.length\n\n\t\t\t// Check that the buffer doesn't itself include the needle\n\t\t\t// this would cause duplicate finds (we could also use a Set to avoid that).\n\t\t\tconst needle_index =\n\t\t\t\tlast_index > -1 ? last_index + needle_length : decoded.length - needle_length\n\t\t\tneedle_buffer = decoded.slice(needle_index)\n\t\t}\n\n\t\t// Correct for search offset.\n\t\tif (position >= 0) {\n\t\t\tposition += fromIndex >= 0 ? fromIndex : full_haystack_length + fromIndex\n\t\t}\n\n\t\treturn position\n\t}\n\n\t// APNGs have an animation control chunk ('acTL') preceding the IDATs.\n\t// See: https://en.wikipedia.org/wiki/APNG#File_format\n\tconst idatIdx = indexOfSubstring(view, 'IDAT', 12)\n\tif (idatIdx >= 12) {\n\t\tconst actlIdx = indexOfSubstring(view, 'acTL', 8, idatIdx)\n\t\treturn actlIdx >= 8\n\t}\n\n\treturn false\n}\n\n// globalThis.isApng = isApng\n\n// (new TextEncoder()).encode('IDAT')\n// Decimal: [73, 68, 65, 84]\n// Hex: [0x49, 0x44, 0x41, 0x54]\n\n// (new TextEncoder()).encode('acTL')\n// Decimal: [97, 99, 84, 76]\n// Hex: [0x61, 0x63, 0x54, 0x4C]\n\n// const idatIdx = buffer.indexOf('IDAT')\n// const actlIdx = buffer.indexOf('acTL')\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAKO,SAAS,eAAe,QAA8B;AAC5D,QAAM,OAAO,IAAI,WAAW,MAAM;AAElC,MACC,CAAC,QACD,EAAG,OAAO,WAAW,eAAe,OAAO,SAAS,IAAI,KAAM,gBAAgB,eAC9E,KAAK,SAAS,IACb;AACD,WAAO;AAAA,EACR;AAEA,QAAM,QACL,KAAK,CAAC,MAAM,OACZ,KAAK,CAAC,MAAM,MACZ,KAAK,CAAC,MAAM,MACZ,KAAK,CAAC,MAAM,MACZ,KAAK,CAAC,MAAM,MACZ,KAAK,CAAC,MAAM,MACZ,KAAK,CAAC,MAAM,MACZ,KAAK,CAAC,MAAM;AAEb,MAAI,CAAC,OAAO;AACX,WAAO;AAAA,EACR;AA2BA,WAAS,iBACR,UACA,QACA,WACA,WACA,YAAY,MACX;AAKD,QAAI,CAAC,QAAQ;AACZ,aAAO;AAAA,IACR;AACA,aAAS,IAAI,OAAO,QAAQ,GAAG;AAK/B,UAAM,gBAAgB,OAAO,OAAO;AACpC,UAAM,UAAU,IAAI,YAAY;AAIhC,UAAM,uBAAuB,SAAS;AACtC,QAAI,OAAO,cAAc,aAAa;AACrC,kBAAY;AAAA,IACb;AACA,QAAI,aAAa,wBAAwB,aAAa,KAAK,aAAa,WAAW;AAClF,aAAO;AAAA,IACR;AACA,eAAW,SAAS,SAAS,WAAW,SAAS;AAEjD,QAAI,WAAW;AACf,QAAI,gBAAgB;AACpB,QAAI,cAAc;AAClB,QAAI,gBAAgB;AAEpB,UAAO,QAAO,gBAAgB,SAAS,QAAQ;AAC9C,YAAM,aAAa,gBAAgB;AAEnC,YAAM,QAAQ,SAAS,SAAS,eAAe,UAAU;AACzD,YAAM,UAAU,QAAQ,OAAO,OAAO,EAAE,QAAQ,KAAK,CAAC;AAEtD,YAAM,OAAO,gBAAgB;AAE7B,UAAI;AACJ,UAAI,aAAa;AACjB,cAAQ,QAAQ,OAAO,KAAK,IAAI,OAAO,MAAM;AAC5C,qBAAa,MAAM,QAAQ,cAAc;AACzC,mBAAW,cAAc;AACzB,cAAM;AAAA,MACP;AAEA,sBAAgB;AAChB,qBAAe,QAAQ;AAIvB,YAAM,eACL,aAAa,KAAK,aAAa,gBAAgB,QAAQ,SAAS;AACjE,sBAAgB,QAAQ,MAAM,YAAY;AAAA,IAC3C;AAGA,QAAI,YAAY,GAAG;AAClB,kBAAY,aAAa,IAAI,YAAY,uBAAuB;AAAA,IACjE;AAEA,WAAO;AAAA,EACR;AAIA,QAAM,UAAU,iBAAiB,MAAM,QAAQ,EAAE;AACjD,MAAI,WAAW,IAAI;AAClB,UAAM,UAAU,iBAAiB,MAAM,QAAQ,GAAG,OAAO;AACzD,WAAO,WAAW;AAAA,EACnB;AAEA,SAAO;AACR;",
4
+ "sourcesContent": ["/*!\n * MIT License: https://github.com/vHeemstra/is-apng/blob/main/license\n * Copyright (c) Philip van Heemstra\n */\n\n/**\n * Determines whether an ArrayBuffer contains an animated PNG (APNG) image.\n *\n * This function checks if the provided buffer contains a valid PNG file with animation\n * control chunks (acTL) that precede the image data chunks (IDAT), which indicates\n * it's an animated PNG rather than a static PNG.\n *\n * @param buffer - The ArrayBuffer containing the image data to analyze\n * @returns True if the buffer contains an animated PNG, false otherwise\n *\n * @example\n * ```typescript\n * // Check if an uploaded file contains an animated PNG\n * if (file.type === 'image/apng') {\n * const isAnimated = isApngAnimated(await file.arrayBuffer())\n * console.log(isAnimated ? 'Animated PNG' : 'Static PNG')\n * }\n * ```\n *\n * @example\n * ```typescript\n * // Use with fetch to check remote images\n * const response = await fetch('image.png')\n * const buffer = await response.arrayBuffer()\n * const hasAnimation = isApngAnimated(buffer)\n * ```\n *\n * @public\n */\nexport function isApngAnimated(buffer: ArrayBuffer): boolean {\n\tconst view = new Uint8Array(buffer)\n\n\tif (\n\t\t!view ||\n\t\t!((typeof Buffer !== 'undefined' && Buffer.isBuffer(view)) || view instanceof Uint8Array) ||\n\t\tview.length < 16\n\t) {\n\t\treturn false\n\t}\n\n\tconst isPNG =\n\t\tview[0] === 0x89 &&\n\t\tview[1] === 0x50 &&\n\t\tview[2] === 0x4e &&\n\t\tview[3] === 0x47 &&\n\t\tview[4] === 0x0d &&\n\t\tview[5] === 0x0a &&\n\t\tview[6] === 0x1a &&\n\t\tview[7] === 0x0a\n\n\tif (!isPNG) {\n\t\treturn false\n\t}\n\n\t/**\n\t * Returns the index of the first occurrence of a string pattern in a Uint8Array, or -1 if not found.\n\t *\n\t * Searches for a string pattern by decoding chunks of the byte array to UTF-8 text and using\n\t * regular expression matching. Handles cases where the pattern might be split across chunk boundaries.\n\t *\n\t * @param haystack - The Uint8Array to search in\n\t * @param needle - The string or RegExp pattern to locate\n\t * @param fromIndex - The array index at which to begin the search\n\t * @param upToIndex - The array index up to which to search (optional, defaults to array end)\n\t * @param chunksize - Size of the chunks used when searching (default 1024 bytes)\n\t * @returns The index position of the first match, or -1 if not found\n\t */\n\tfunction indexOfSubstring(\n\t\thaystack: Uint8Array,\n\t\tneedle: string | RegExp,\n\t\tfromIndex: number,\n\t\tupToIndex?: number,\n\t\tchunksize = 1024 /* Bytes */\n\t) {\n\t\t/**\n\t\t * Adopted from: https://stackoverflow.com/a/67771214/2142071\n\t\t */\n\n\t\tif (!needle) {\n\t\t\treturn -1\n\t\t}\n\t\tneedle = new RegExp(needle, 'g')\n\n\t\t// The needle could get split over two chunks.\n\t\t// So, at every chunk we prepend the last few characters\n\t\t// of the last chunk.\n\t\tconst needle_length = needle.source.length\n\t\tconst decoder = new TextDecoder()\n\n\t\t// Handle search offset in line with\n\t\t// `Array.prototype.indexOf()` and `TypedArray.prototype.subarray()`.\n\t\tconst full_haystack_length = haystack.length\n\t\tif (typeof upToIndex === 'undefined') {\n\t\t\tupToIndex = full_haystack_length\n\t\t}\n\t\tif (fromIndex >= full_haystack_length || upToIndex <= 0 || fromIndex >= upToIndex) {\n\t\t\treturn -1\n\t\t}\n\t\thaystack = haystack.subarray(fromIndex, upToIndex)\n\n\t\tlet position = -1\n\t\tlet current_index = 0\n\t\tlet full_length = 0\n\t\tlet needle_buffer = ''\n\n\t\touter: while (current_index < haystack.length) {\n\t\t\tconst next_index = current_index + chunksize\n\t\t\t// subarray doesn't copy\n\t\t\tconst chunk = haystack.subarray(current_index, next_index)\n\t\t\tconst decoded = decoder.decode(chunk, { stream: true })\n\n\t\t\tconst text = needle_buffer + decoded\n\n\t\t\tlet match: RegExpExecArray | null\n\t\t\tlet last_index = -1\n\t\t\twhile ((match = needle.exec(text)) !== null) {\n\t\t\t\tlast_index = match.index - needle_buffer.length\n\t\t\t\tposition = full_length + last_index\n\t\t\t\tbreak outer\n\t\t\t}\n\n\t\t\tcurrent_index = next_index\n\t\t\tfull_length += decoded.length\n\n\t\t\t// Check that the buffer doesn't itself include the needle\n\t\t\t// this would cause duplicate finds (we could also use a Set to avoid that).\n\t\t\tconst needle_index =\n\t\t\t\tlast_index > -1 ? last_index + needle_length : decoded.length - needle_length\n\t\t\tneedle_buffer = decoded.slice(needle_index)\n\t\t}\n\n\t\t// Correct for search offset.\n\t\tif (position >= 0) {\n\t\t\tposition += fromIndex >= 0 ? fromIndex : full_haystack_length + fromIndex\n\t\t}\n\n\t\treturn position\n\t}\n\n\t// APNGs have an animation control chunk ('acTL') preceding the IDATs.\n\t// See: https://en.wikipedia.org/wiki/APNG#File_format\n\tconst idatIdx = indexOfSubstring(view, 'IDAT', 12)\n\tif (idatIdx >= 12) {\n\t\tconst actlIdx = indexOfSubstring(view, 'acTL', 8, idatIdx)\n\t\treturn actlIdx >= 8\n\t}\n\n\treturn false\n}\n\n// globalThis.isApng = isApng\n\n// (new TextEncoder()).encode('IDAT')\n// Decimal: [73, 68, 65, 84]\n// Hex: [0x49, 0x44, 0x41, 0x54]\n\n// (new TextEncoder()).encode('acTL')\n// Decimal: [97, 99, 84, 76]\n// Hex: [0x61, 0x63, 0x54, 0x4C]\n\n// const idatIdx = buffer.indexOf('IDAT')\n// const actlIdx = buffer.indexOf('acTL')\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAkCO,SAAS,eAAe,QAA8B;AAC5D,QAAM,OAAO,IAAI,WAAW,MAAM;AAElC,MACC,CAAC,QACD,EAAG,OAAO,WAAW,eAAe,OAAO,SAAS,IAAI,KAAM,gBAAgB,eAC9E,KAAK,SAAS,IACb;AACD,WAAO;AAAA,EACR;AAEA,QAAM,QACL,KAAK,CAAC,MAAM,OACZ,KAAK,CAAC,MAAM,MACZ,KAAK,CAAC,MAAM,MACZ,KAAK,CAAC,MAAM,MACZ,KAAK,CAAC,MAAM,MACZ,KAAK,CAAC,MAAM,MACZ,KAAK,CAAC,MAAM,MACZ,KAAK,CAAC,MAAM;AAEb,MAAI,CAAC,OAAO;AACX,WAAO;AAAA,EACR;AAeA,WAAS,iBACR,UACA,QACA,WACA,WACA,YAAY,MACX;AAKD,QAAI,CAAC,QAAQ;AACZ,aAAO;AAAA,IACR;AACA,aAAS,IAAI,OAAO,QAAQ,GAAG;AAK/B,UAAM,gBAAgB,OAAO,OAAO;AACpC,UAAM,UAAU,IAAI,YAAY;AAIhC,UAAM,uBAAuB,SAAS;AACtC,QAAI,OAAO,cAAc,aAAa;AACrC,kBAAY;AAAA,IACb;AACA,QAAI,aAAa,wBAAwB,aAAa,KAAK,aAAa,WAAW;AAClF,aAAO;AAAA,IACR;AACA,eAAW,SAAS,SAAS,WAAW,SAAS;AAEjD,QAAI,WAAW;AACf,QAAI,gBAAgB;AACpB,QAAI,cAAc;AAClB,QAAI,gBAAgB;AAEpB,UAAO,QAAO,gBAAgB,SAAS,QAAQ;AAC9C,YAAM,aAAa,gBAAgB;AAEnC,YAAM,QAAQ,SAAS,SAAS,eAAe,UAAU;AACzD,YAAM,UAAU,QAAQ,OAAO,OAAO,EAAE,QAAQ,KAAK,CAAC;AAEtD,YAAM,OAAO,gBAAgB;AAE7B,UAAI;AACJ,UAAI,aAAa;AACjB,cAAQ,QAAQ,OAAO,KAAK,IAAI,OAAO,MAAM;AAC5C,qBAAa,MAAM,QAAQ,cAAc;AACzC,mBAAW,cAAc;AACzB,cAAM;AAAA,MACP;AAEA,sBAAgB;AAChB,qBAAe,QAAQ;AAIvB,YAAM,eACL,aAAa,KAAK,aAAa,gBAAgB,QAAQ,SAAS;AACjE,sBAAgB,QAAQ,MAAM,YAAY;AAAA,IAC3C;AAGA,QAAI,YAAY,GAAG;AAClB,kBAAY,aAAa,IAAI,YAAY,uBAAuB;AAAA,IACjE;AAEA,WAAO;AAAA,EACR;AAIA,QAAM,UAAU,iBAAiB,MAAM,QAAQ,EAAE;AACjD,MAAI,WAAW,IAAI;AAClB,UAAM,UAAU,iBAAiB,MAAM,QAAQ,GAAG,OAAO;AACzD,WAAO,WAAW;AAAA,EACnB;AAEA,SAAO;AACR;",
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": ["export const isAvifAnimated = (buffer: ArrayBuffer) => {\n\tconst view = new Uint8Array(buffer)\n\treturn view[3] === 44\n}\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAO,MAAM,iBAAiB,CAAC,WAAwB;AACtD,QAAM,OAAO,IAAI,WAAW,MAAM;AAClC,SAAO,KAAK,CAAC,MAAM;AACpB;",
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 const isAvifAnimated = (buffer: ArrayBuffer) => {\n\tconst view = new Uint8Array(buffer)\n\treturn view[3] === 44\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAkCO,MAAM,iBAAiB,CAAC,WAAwB;AACtD,QAAM,OAAO,IAAI,WAAW,MAAM;AAClC,SAAO,KAAK,CAAC,MAAM;AACpB;",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/lib/media/gif.ts"],
4
- "sourcesContent": ["/*!\n * MIT License\n * Modified code originally from <https://github.com/qzb/is-animated>\n * Copyright (c) 2016 J\u00F3zef Soko\u0142owski <j.k.sokolowski@gmail.com>\n */\n\n/** Returns total length of data blocks sequence */\nfunction getDataBlocksLength(buffer: Uint8Array, offset: number): number {\n\tlet length = 0\n\n\twhile (buffer[offset + length]) {\n\t\tlength += buffer[offset + length] + 1\n\t}\n\n\treturn length + 1\n}\n\n/**\n * Checks if buffer contains GIF image\n *\n * @public\n */\nexport function isGIF(buffer: ArrayBuffer): boolean {\n\tconst enc = new TextDecoder('ascii')\n\tconst header = enc.decode(buffer.slice(0, 3))\n\treturn header === 'GIF'\n}\n\n/**\n * Checks if buffer contains animated GIF image\n *\n * @public\n */\nexport function isGifAnimated(buffer: ArrayBuffer): boolean {\n\tconst view = new Uint8Array(buffer)\n\tlet hasColorTable, colorTableSize\n\tlet offset = 0\n\tlet imagesCount = 0\n\n\t// Check if this is this image has valid GIF header.\n\t// If not return false. Chrome, FF and IE doesn't handle GIFs with invalid version.\n\tif (!isGIF(buffer)) {\n\t\treturn false\n\t}\n\n\t// Skip header, logical screen descriptor and global color table\n\n\thasColorTable = view[10] & 0x80 // 0b10000000\n\tcolorTableSize = view[10] & 0x07 // 0b00000111\n\n\toffset += 6 // skip header\n\toffset += 7 // skip logical screen descriptor\n\toffset += hasColorTable ? 3 * Math.pow(2, colorTableSize + 1) : 0 // skip global color table\n\n\t// Find if there is more than one image descriptor\n\n\twhile (imagesCount < 2 && offset < view.length) {\n\t\tswitch (view[offset]) {\n\t\t\t// Image descriptor block. According to specification there could be any\n\t\t\t// number of these blocks (even zero). When there is more than one image\n\t\t\t// descriptor browsers will display animation (they shouldn't when there\n\t\t\t// is no delays defined, but they do it anyway).\n\t\t\tcase 0x2c:\n\t\t\t\timagesCount += 1\n\n\t\t\t\thasColorTable = view[offset + 9] & 0x80 // 0b10000000\n\t\t\t\tcolorTableSize = view[offset + 9] & 0x07 // 0b00000111\n\n\t\t\t\toffset += 10 // skip image descriptor\n\t\t\t\toffset += hasColorTable ? 3 * Math.pow(2, colorTableSize + 1) : 0 // skip local color table\n\t\t\t\toffset += getDataBlocksLength(view, offset + 1) + 1 // skip image data\n\n\t\t\t\tbreak\n\n\t\t\t// Skip all extension blocks. In theory this \"plain text extension\" blocks\n\t\t\t// could be frames of animation, but no browser renders them.\n\t\t\tcase 0x21:\n\t\t\t\toffset += 2 // skip introducer and label\n\t\t\t\toffset += getDataBlocksLength(view, offset) // skip this block and following data blocks\n\n\t\t\t\tbreak\n\n\t\t\t// Stop processing on trailer block,\n\t\t\t// all data after this point will is ignored by decoders\n\t\t\tcase 0x3b:\n\t\t\t\toffset = view.length // fast forward to end of buffer\n\t\t\t\tbreak\n\n\t\t\t// Oops! This GIF seems to be invalid\n\t\t\tdefault:\n\t\t\t\t// fast forward to end of buffer\n\t\t\t\toffset = view.length\n\t\t\t\tbreak\n\t\t}\n\t}\n\n\treturn imagesCount > 1\n}\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAOA,SAAS,oBAAoB,QAAoB,QAAwB;AACxE,MAAI,SAAS;AAEb,SAAO,OAAO,SAAS,MAAM,GAAG;AAC/B,cAAU,OAAO,SAAS,MAAM,IAAI;AAAA,EACrC;AAEA,SAAO,SAAS;AACjB;AAOO,SAAS,MAAM,QAA8B;AACnD,QAAM,MAAM,IAAI,YAAY,OAAO;AACnC,QAAM,SAAS,IAAI,OAAO,OAAO,MAAM,GAAG,CAAC,CAAC;AAC5C,SAAO,WAAW;AACnB;AAOO,SAAS,cAAc,QAA8B;AAC3D,QAAM,OAAO,IAAI,WAAW,MAAM;AAClC,MAAI,eAAe;AACnB,MAAI,SAAS;AACb,MAAI,cAAc;AAIlB,MAAI,CAAC,MAAM,MAAM,GAAG;AACnB,WAAO;AAAA,EACR;AAIA,kBAAgB,KAAK,EAAE,IAAI;AAC3B,mBAAiB,KAAK,EAAE,IAAI;AAE5B,YAAU;AACV,YAAU;AACV,YAAU,gBAAgB,IAAI,KAAK,IAAI,GAAG,iBAAiB,CAAC,IAAI;AAIhE,SAAO,cAAc,KAAK,SAAS,KAAK,QAAQ;AAC/C,YAAQ,KAAK,MAAM,GAAG;AAAA;AAAA;AAAA;AAAA;AAAA,MAKrB,KAAK;AACJ,uBAAe;AAEf,wBAAgB,KAAK,SAAS,CAAC,IAAI;AACnC,yBAAiB,KAAK,SAAS,CAAC,IAAI;AAEpC,kBAAU;AACV,kBAAU,gBAAgB,IAAI,KAAK,IAAI,GAAG,iBAAiB,CAAC,IAAI;AAChE,kBAAU,oBAAoB,MAAM,SAAS,CAAC,IAAI;AAElD;AAAA;AAAA;AAAA,MAID,KAAK;AACJ,kBAAU;AACV,kBAAU,oBAAoB,MAAM,MAAM;AAE1C;AAAA;AAAA;AAAA,MAID,KAAK;AACJ,iBAAS,KAAK;AACd;AAAA;AAAA,MAGD;AAEC,iBAAS,KAAK;AACd;AAAA,IACF;AAAA,EACD;AAEA,SAAO,cAAc;AACtB;",
4
+ "sourcesContent": ["/*!\n * MIT License\n * Modified code originally from <https://github.com/qzb/is-animated>\n * Copyright (c) 2016 J\u00F3zef Soko\u0142owski <j.k.sokolowski@gmail.com>\n */\n\n/** Returns total length of data blocks sequence */\nfunction getDataBlocksLength(buffer: Uint8Array, offset: number): number {\n\tlet length = 0\n\n\twhile (buffer[offset + length]) {\n\t\tlength += buffer[offset + length] + 1\n\t}\n\n\treturn length + 1\n}\n\n/**\n * Checks if buffer contains GIF image by examining the file header.\n *\n * @param buffer - The ArrayBuffer containing the image data to check\n * @returns True if the buffer contains a GIF image, false otherwise\n * @example\n * ```ts\n * // Check a file from user input\n * const file = event.target.files[0]\n * const buffer = await file.arrayBuffer()\n * const isGif = isGIF(buffer)\n * console.log(isGif ? 'GIF image' : 'Not a GIF')\n * ```\n * @public\n */\nexport function isGIF(buffer: ArrayBuffer): boolean {\n\tconst enc = new TextDecoder('ascii')\n\tconst header = enc.decode(buffer.slice(0, 3))\n\treturn header === 'GIF'\n}\n\n/**\n * Checks if buffer contains animated GIF image by parsing the GIF structure and counting image descriptors.\n * A GIF is considered animated if it contains more than one image descriptor block.\n *\n * @param buffer - The ArrayBuffer containing the GIF image data\n * @returns True if the GIF is animated (contains multiple frames), false otherwise\n * @example\n * ```ts\n * // Check if a GIF file is animated\n * const file = event.target.files[0]\n * if (file.type === 'image/gif') {\n * const buffer = await file.arrayBuffer()\n * const animated = isGifAnimated(buffer)\n * console.log(animated ? 'Animated GIF' : 'Static GIF')\n * }\n * ```\n * @public\n */\nexport function isGifAnimated(buffer: ArrayBuffer): boolean {\n\tconst view = new Uint8Array(buffer)\n\tlet hasColorTable, colorTableSize\n\tlet offset = 0\n\tlet imagesCount = 0\n\n\t// Check if this is this image has valid GIF header.\n\t// If not return false. Chrome, FF and IE doesn't handle GIFs with invalid version.\n\tif (!isGIF(buffer)) {\n\t\treturn false\n\t}\n\n\t// Skip header, logical screen descriptor and global color table\n\n\thasColorTable = view[10] & 0x80 // 0b10000000\n\tcolorTableSize = view[10] & 0x07 // 0b00000111\n\n\toffset += 6 // skip header\n\toffset += 7 // skip logical screen descriptor\n\toffset += hasColorTable ? 3 * Math.pow(2, colorTableSize + 1) : 0 // skip global color table\n\n\t// Find if there is more than one image descriptor\n\n\twhile (imagesCount < 2 && offset < view.length) {\n\t\tswitch (view[offset]) {\n\t\t\t// Image descriptor block. According to specification there could be any\n\t\t\t// number of these blocks (even zero). When there is more than one image\n\t\t\t// descriptor browsers will display animation (they shouldn't when there\n\t\t\t// is no delays defined, but they do it anyway).\n\t\t\tcase 0x2c:\n\t\t\t\timagesCount += 1\n\n\t\t\t\thasColorTable = view[offset + 9] & 0x80 // 0b10000000\n\t\t\t\tcolorTableSize = view[offset + 9] & 0x07 // 0b00000111\n\n\t\t\t\toffset += 10 // skip image descriptor\n\t\t\t\toffset += hasColorTable ? 3 * Math.pow(2, colorTableSize + 1) : 0 // skip local color table\n\t\t\t\toffset += getDataBlocksLength(view, offset + 1) + 1 // skip image data\n\n\t\t\t\tbreak\n\n\t\t\t// Skip all extension blocks. In theory this \"plain text extension\" blocks\n\t\t\t// could be frames of animation, but no browser renders them.\n\t\t\tcase 0x21:\n\t\t\t\toffset += 2 // skip introducer and label\n\t\t\t\toffset += getDataBlocksLength(view, offset) // skip this block and following data blocks\n\n\t\t\t\tbreak\n\n\t\t\t// Stop processing on trailer block,\n\t\t\t// all data after this point will is ignored by decoders\n\t\t\tcase 0x3b:\n\t\t\t\toffset = view.length // fast forward to end of buffer\n\t\t\t\tbreak\n\n\t\t\t// Oops! This GIF seems to be invalid\n\t\t\tdefault:\n\t\t\t\t// fast forward to end of buffer\n\t\t\t\toffset = view.length\n\t\t\t\tbreak\n\t\t}\n\t}\n\n\treturn imagesCount > 1\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAOA,SAAS,oBAAoB,QAAoB,QAAwB;AACxE,MAAI,SAAS;AAEb,SAAO,OAAO,SAAS,MAAM,GAAG;AAC/B,cAAU,OAAO,SAAS,MAAM,IAAI;AAAA,EACrC;AAEA,SAAO,SAAS;AACjB;AAiBO,SAAS,MAAM,QAA8B;AACnD,QAAM,MAAM,IAAI,YAAY,OAAO;AACnC,QAAM,SAAS,IAAI,OAAO,OAAO,MAAM,GAAG,CAAC,CAAC;AAC5C,SAAO,WAAW;AACnB;AAoBO,SAAS,cAAc,QAA8B;AAC3D,QAAM,OAAO,IAAI,WAAW,MAAM;AAClC,MAAI,eAAe;AACnB,MAAI,SAAS;AACb,MAAI,cAAc;AAIlB,MAAI,CAAC,MAAM,MAAM,GAAG;AACnB,WAAO;AAAA,EACR;AAIA,kBAAgB,KAAK,EAAE,IAAI;AAC3B,mBAAiB,KAAK,EAAE,IAAI;AAE5B,YAAU;AACV,YAAU;AACV,YAAU,gBAAgB,IAAI,KAAK,IAAI,GAAG,iBAAiB,CAAC,IAAI;AAIhE,SAAO,cAAc,KAAK,SAAS,KAAK,QAAQ;AAC/C,YAAQ,KAAK,MAAM,GAAG;AAAA;AAAA;AAAA;AAAA;AAAA,MAKrB,KAAK;AACJ,uBAAe;AAEf,wBAAgB,KAAK,SAAS,CAAC,IAAI;AACnC,yBAAiB,KAAK,SAAS,CAAC,IAAI;AAEpC,kBAAU;AACV,kBAAU,gBAAgB,IAAI,KAAK,IAAI,GAAG,iBAAiB,CAAC,IAAI;AAChE,kBAAU,oBAAoB,MAAM,SAAS,CAAC,IAAI;AAElD;AAAA;AAAA;AAAA,MAID,KAAK;AACJ,kBAAU;AACV,kBAAU,oBAAoB,MAAM,MAAM;AAE1C;AAAA;AAAA;AAAA,MAID,KAAK;AACJ,iBAAS,KAAK;AACd;AAAA;AAAA,MAGD;AAEC,iBAAS,KAAK;AACd;AAAA,IACF;AAAA,EACD;AAEA,SAAO,cAAc;AACtB;",
6
6
  "names": []
7
7
  }
@@ -63,7 +63,15 @@ const DEFAULT_SUPPORTED_MEDIA_TYPES = Object.freeze([
63
63
  const DEFAULT_SUPPORTED_MEDIA_TYPE_LIST = DEFAULT_SUPPORTED_MEDIA_TYPES.join(",");
64
64
  class MediaHelpers {
65
65
  /**
66
- * Load a video from a url.
66
+ * Load a video element from a URL with cross-origin support.
67
+ *
68
+ * @param src - The URL of the video to load
69
+ * @returns Promise that resolves to the loaded HTMLVideoElement
70
+ * @example
71
+ * ```ts
72
+ * const video = await MediaHelpers.loadVideo('https://example.com/video.mp4')
73
+ * console.log(`Video dimensions: ${video.videoWidth}x${video.videoHeight}`)
74
+ * ```
67
75
  * @public
68
76
  */
69
77
  static loadVideo(src) {
@@ -78,6 +86,22 @@ class MediaHelpers {
78
86
  video.src = src;
79
87
  });
80
88
  }
89
+ /**
90
+ * Extract a frame from a video element as a data URL.
91
+ *
92
+ * @param video - The HTMLVideoElement to extract frame from
93
+ * @param time - The time in seconds to extract the frame from (default: 0)
94
+ * @returns Promise that resolves to a data URL of the video frame
95
+ * @example
96
+ * ```ts
97
+ * const video = await MediaHelpers.loadVideo('https://example.com/video.mp4')
98
+ * const frameDataUrl = await MediaHelpers.getVideoFrameAsDataUrl(video, 5.0)
99
+ * // Use frameDataUrl as image thumbnail
100
+ * const img = document.createElement('img')
101
+ * img.src = frameDataUrl
102
+ * ```
103
+ * @public
104
+ */
81
105
  static async getVideoFrameAsDataUrl(video, time = 0) {
82
106
  const promise = (0, import_control.promiseWithResolve)();
83
107
  let didSetTime = false;
@@ -125,7 +149,17 @@ class MediaHelpers {
125
149
  }
126
150
  }
127
151
  /**
128
- * Load an image from a url.
152
+ * Load an image from a URL and get its dimensions along with the image element.
153
+ *
154
+ * @param src - The URL of the image to load
155
+ * @returns Promise that resolves to an object with width, height, and the image element
156
+ * @example
157
+ * ```ts
158
+ * const { w, h, image } = await MediaHelpers.getImageAndDimensions('https://example.com/image.png')
159
+ * console.log(`Image size: ${w}x${h}`)
160
+ * // Image is ready to use
161
+ * document.body.appendChild(image)
162
+ * ```
129
163
  * @public
130
164
  */
131
165
  static getImageAndDimensions(src) {
@@ -164,7 +198,14 @@ class MediaHelpers {
164
198
  /**
165
199
  * Get the size of a video blob
166
200
  *
167
- * @param blob - A SharedBlob containing the video
201
+ * @param blob - A Blob containing the video
202
+ * @returns Promise that resolves to an object with width and height properties
203
+ * @example
204
+ * ```ts
205
+ * const file = new File([...], 'video.mp4', { type: 'video/mp4' })
206
+ * const { w, h } = await MediaHelpers.getVideoSize(file)
207
+ * console.log(`Video dimensions: ${w}x${h}`)
208
+ * ```
168
209
  * @public
169
210
  */
170
211
  static async getVideoSize(blob) {
@@ -176,7 +217,14 @@ class MediaHelpers {
176
217
  /**
177
218
  * Get the size of an image blob
178
219
  *
179
- * @param blob - A Blob containing the image.
220
+ * @param blob - A Blob containing the image
221
+ * @returns Promise that resolves to an object with width and height properties
222
+ * @example
223
+ * ```ts
224
+ * const file = new File([...], 'image.png', { type: 'image/png' })
225
+ * const { w, h } = await MediaHelpers.getImageSize(file)
226
+ * console.log(`Image dimensions: ${w}x${h}`)
227
+ * ```
180
228
  * @public
181
229
  */
182
230
  static async getImageSize(blob) {
@@ -205,6 +253,19 @@ class MediaHelpers {
205
253
  }
206
254
  return { w, h };
207
255
  }
256
+ /**
257
+ * Check if a media file blob contains animation data.
258
+ *
259
+ * @param file - The Blob to check for animation
260
+ * @returns Promise that resolves to true if the file is animated, false otherwise
261
+ * @example
262
+ * ```ts
263
+ * const file = new File([...], 'animation.gif', { type: 'image/gif' })
264
+ * const animated = await MediaHelpers.isAnimated(file)
265
+ * console.log(animated ? 'Animated' : 'Static')
266
+ * ```
267
+ * @public
268
+ */
208
269
  static async isAnimated(file) {
209
270
  if (file.type === "image/gif") {
210
271
  return (0, import_gif.isGifAnimated)(await file.arrayBuffer());
@@ -220,18 +281,83 @@ class MediaHelpers {
220
281
  }
221
282
  return false;
222
283
  }
284
+ /**
285
+ * Check if a MIME type represents an animated image format.
286
+ *
287
+ * @param mimeType - The MIME type to check
288
+ * @returns True if the MIME type is an animated image format, false otherwise
289
+ * @example
290
+ * ```ts
291
+ * const isAnimated = MediaHelpers.isAnimatedImageType('image/gif')
292
+ * console.log(isAnimated) // true
293
+ * ```
294
+ * @public
295
+ */
223
296
  static isAnimatedImageType(mimeType) {
224
297
  return DEFAULT_SUPPORTED_ANIMATED_IMAGE_TYPES.includes(mimeType || "");
225
298
  }
299
+ /**
300
+ * Check if a MIME type represents a static (non-animated) image format.
301
+ *
302
+ * @param mimeType - The MIME type to check
303
+ * @returns True if the MIME type is a static image format, false otherwise
304
+ * @example
305
+ * ```ts
306
+ * const isStatic = MediaHelpers.isStaticImageType('image/jpeg')
307
+ * console.log(isStatic) // true
308
+ * ```
309
+ * @public
310
+ */
226
311
  static isStaticImageType(mimeType) {
227
312
  return DEFAULT_SUPPORTED_STATIC_IMAGE_TYPES.includes(mimeType || "");
228
313
  }
314
+ /**
315
+ * Check if a MIME type represents a vector image format.
316
+ *
317
+ * @param mimeType - The MIME type to check
318
+ * @returns True if the MIME type is a vector image format, false otherwise
319
+ * @example
320
+ * ```ts
321
+ * const isVector = MediaHelpers.isVectorImageType('image/svg+xml')
322
+ * console.log(isVector) // true
323
+ * ```
324
+ * @public
325
+ */
229
326
  static isVectorImageType(mimeType) {
230
327
  return DEFAULT_SUPPORTED_VECTOR_IMAGE_TYPES.includes(mimeType || "");
231
328
  }
329
+ /**
330
+ * Check if a MIME type represents any supported image format (static, animated, or vector).
331
+ *
332
+ * @param mimeType - The MIME type to check
333
+ * @returns True if the MIME type is a supported image format, false otherwise
334
+ * @example
335
+ * ```ts
336
+ * const isImage = MediaHelpers.isImageType('image/png')
337
+ * console.log(isImage) // true
338
+ * ```
339
+ * @public
340
+ */
232
341
  static isImageType(mimeType) {
233
342
  return DEFAULT_SUPPORTED_IMAGE_TYPES.includes(mimeType || "");
234
343
  }
344
+ /**
345
+ * Utility function to create an object URL from a blob, execute a function with it, and automatically clean it up.
346
+ *
347
+ * @param blob - The Blob to create an object URL for
348
+ * @param fn - Function to execute with the object URL
349
+ * @returns Promise that resolves to the result of the function
350
+ * @example
351
+ * ```ts
352
+ * const result = await MediaHelpers.usingObjectURL(imageBlob, async (url) => {
353
+ * const { w, h } = await MediaHelpers.getImageAndDimensions(url)
354
+ * return { width: w, height: h }
355
+ * })
356
+ * // Object URL is automatically revoked after function completes
357
+ * console.log(`Image dimensions: ${result.width}x${result.height}`)
358
+ * ```
359
+ * @public
360
+ */
235
361
  static async usingObjectURL(blob, fn) {
236
362
  const url = URL.createObjectURL(blob);
237
363
  try {