@anvilkit/plugin-asset-manager 0.1.8 → 0.1.10

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 (235) hide show
  1. package/README.md +96 -3
  2. package/dist/adapters/data-url.d.cts +1 -0
  3. package/dist/adapters/data-url.d.cts.map +1 -1
  4. package/dist/adapters/data-url.d.ts +1 -0
  5. package/dist/adapters/data-url.d.ts.map +1 -1
  6. package/dist/adapters/s3-multipart.cjs +425 -0
  7. package/dist/adapters/s3-multipart.d.cts +43 -0
  8. package/dist/adapters/s3-multipart.d.cts.map +1 -0
  9. package/dist/adapters/s3-multipart.d.ts +43 -0
  10. package/dist/adapters/s3-multipart.d.ts.map +1 -0
  11. package/dist/adapters/s3-multipart.js +387 -0
  12. package/dist/adapters/s3-presigned.d.cts +2 -0
  13. package/dist/adapters/s3-presigned.d.cts.map +1 -1
  14. package/dist/adapters/s3-presigned.d.ts +2 -0
  15. package/dist/adapters/s3-presigned.d.ts.map +1 -1
  16. package/dist/i18n/provider.d.cts +1 -0
  17. package/dist/i18n/provider.d.cts.map +1 -1
  18. package/dist/i18n/provider.d.ts +1 -0
  19. package/dist/i18n/provider.d.ts.map +1 -1
  20. package/dist/index.cjs +14 -0
  21. package/dist/index.d.cts +9 -3
  22. package/dist/index.d.cts.map +1 -1
  23. package/dist/index.d.ts +9 -3
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +3 -1
  26. package/dist/plugin.cjs +152 -12
  27. package/dist/plugin.d.cts +49 -1
  28. package/dist/plugin.d.cts.map +1 -1
  29. package/dist/plugin.d.ts +49 -1
  30. package/dist/plugin.d.ts.map +1 -1
  31. package/dist/plugin.js +147 -13
  32. package/dist/sources/composite-source.cjs +3 -0
  33. package/dist/sources/composite-source.d.cts.map +1 -1
  34. package/dist/sources/composite-source.d.ts.map +1 -1
  35. package/dist/sources/composite-source.js +3 -0
  36. package/dist/sources/federated-search.cjs +45 -7
  37. package/dist/sources/federated-search.d.cts.map +1 -1
  38. package/dist/sources/federated-search.d.ts.map +1 -1
  39. package/dist/sources/federated-search.js +45 -7
  40. package/dist/sources/provider.d.cts +5 -0
  41. package/dist/sources/provider.d.cts.map +1 -1
  42. package/dist/sources/provider.d.ts +5 -0
  43. package/dist/sources/provider.d.ts.map +1 -1
  44. package/dist/sources/unsplash/index.d.cts +1 -0
  45. package/dist/sources/unsplash/index.d.cts.map +1 -1
  46. package/dist/sources/unsplash/index.d.ts +1 -0
  47. package/dist/sources/unsplash/index.d.ts.map +1 -1
  48. package/dist/testing/index.d.cts +4 -0
  49. package/dist/testing/index.d.cts.map +1 -1
  50. package/dist/testing/index.d.ts +4 -0
  51. package/dist/testing/index.d.ts.map +1 -1
  52. package/dist/types/categories.d.cts +3 -0
  53. package/dist/types/categories.d.cts.map +1 -1
  54. package/dist/types/categories.d.ts +3 -0
  55. package/dist/types/categories.d.ts.map +1 -1
  56. package/dist/types/data-source.d.cts +9 -0
  57. package/dist/types/data-source.d.cts.map +1 -1
  58. package/dist/types/data-source.d.ts +9 -0
  59. package/dist/types/data-source.d.ts.map +1 -1
  60. package/dist/types/filter.d.cts +11 -0
  61. package/dist/types/filter.d.cts.map +1 -1
  62. package/dist/types/filter.d.ts +11 -0
  63. package/dist/types/filter.d.ts.map +1 -1
  64. package/dist/types/folders.d.cts +2 -0
  65. package/dist/types/folders.d.cts.map +1 -1
  66. package/dist/types/folders.d.ts +2 -0
  67. package/dist/types/folders.d.ts.map +1 -1
  68. package/dist/types/options.d.cts +57 -1
  69. package/dist/types/options.d.cts.map +1 -1
  70. package/dist/types/options.d.ts +57 -1
  71. package/dist/types/options.d.ts.map +1 -1
  72. package/dist/types/resumable.cjs +42 -0
  73. package/dist/types/resumable.d.cts +204 -0
  74. package/dist/types/resumable.d.cts.map +1 -0
  75. package/dist/types/resumable.d.ts +204 -0
  76. package/dist/types/resumable.d.ts.map +1 -0
  77. package/dist/types/resumable.js +4 -0
  78. package/dist/types/transform.cjs +18 -0
  79. package/dist/types/transform.d.cts +45 -0
  80. package/dist/types/transform.d.cts.map +1 -0
  81. package/dist/types/transform.d.ts +45 -0
  82. package/dist/types/transform.d.ts.map +1 -0
  83. package/dist/types/transform.js +1 -0
  84. package/dist/types/types.d.cts +17 -0
  85. package/dist/types/types.d.cts.map +1 -1
  86. package/dist/types/types.d.ts +17 -0
  87. package/dist/types/types.d.ts.map +1 -1
  88. package/dist/types/unsplash.d.cts +2 -0
  89. package/dist/types/unsplash.d.cts.map +1 -1
  90. package/dist/types/unsplash.d.ts +2 -0
  91. package/dist/types/unsplash.d.ts.map +1 -1
  92. package/dist/ui/AssetBrowser.d.cts +3 -1
  93. package/dist/ui/AssetBrowser.d.cts.map +1 -1
  94. package/dist/ui/AssetBrowser.d.ts +3 -1
  95. package/dist/ui/AssetBrowser.d.ts.map +1 -1
  96. package/dist/ui/AssetCommandPalette.d.cts +4 -1
  97. package/dist/ui/AssetCommandPalette.d.cts.map +1 -1
  98. package/dist/ui/AssetCommandPalette.d.ts +4 -1
  99. package/dist/ui/AssetCommandPalette.d.ts.map +1 -1
  100. package/dist/ui/AssetManagerUI.cjs +3 -1
  101. package/dist/ui/AssetManagerUI.d.cts +11 -2
  102. package/dist/ui/AssetManagerUI.d.cts.map +1 -1
  103. package/dist/ui/AssetManagerUI.d.ts +11 -2
  104. package/dist/ui/AssetManagerUI.d.ts.map +1 -1
  105. package/dist/ui/AssetManagerUI.js +3 -1
  106. package/dist/ui/DeleteAssetDialog.d.cts +4 -1
  107. package/dist/ui/DeleteAssetDialog.d.cts.map +1 -1
  108. package/dist/ui/DeleteAssetDialog.d.ts +4 -1
  109. package/dist/ui/DeleteAssetDialog.d.ts.map +1 -1
  110. package/dist/ui/DeleteFolderDialog.d.cts +4 -1
  111. package/dist/ui/DeleteFolderDialog.d.cts.map +1 -1
  112. package/dist/ui/DeleteFolderDialog.d.ts +4 -1
  113. package/dist/ui/DeleteFolderDialog.d.ts.map +1 -1
  114. package/dist/ui/EmptyFolderState.d.cts +4 -1
  115. package/dist/ui/EmptyFolderState.d.cts.map +1 -1
  116. package/dist/ui/EmptyFolderState.d.ts +4 -1
  117. package/dist/ui/EmptyFolderState.d.ts.map +1 -1
  118. package/dist/ui/FolderBreadcrumb.d.cts +4 -1
  119. package/dist/ui/FolderBreadcrumb.d.cts.map +1 -1
  120. package/dist/ui/FolderBreadcrumb.d.ts +4 -1
  121. package/dist/ui/FolderBreadcrumb.d.ts.map +1 -1
  122. package/dist/ui/FolderNameDialog.d.cts +3 -1
  123. package/dist/ui/FolderNameDialog.d.cts.map +1 -1
  124. package/dist/ui/FolderNameDialog.d.ts +3 -1
  125. package/dist/ui/FolderNameDialog.d.ts.map +1 -1
  126. package/dist/ui/FolderTree.d.cts +4 -1
  127. package/dist/ui/FolderTree.d.cts.map +1 -1
  128. package/dist/ui/FolderTree.d.ts +4 -1
  129. package/dist/ui/FolderTree.d.ts.map +1 -1
  130. package/dist/ui/MetadataPanel.d.cts +4 -1
  131. package/dist/ui/MetadataPanel.d.cts.map +1 -1
  132. package/dist/ui/MetadataPanel.d.ts +4 -1
  133. package/dist/ui/MetadataPanel.d.ts.map +1 -1
  134. package/dist/ui/MoveTargetPicker.d.cts +3 -1
  135. package/dist/ui/MoveTargetPicker.d.cts.map +1 -1
  136. package/dist/ui/MoveTargetPicker.d.ts +3 -1
  137. package/dist/ui/MoveTargetPicker.d.ts.map +1 -1
  138. package/dist/ui/ReplaceAssetDialog.cjs +7 -2
  139. package/dist/ui/ReplaceAssetDialog.d.cts +5 -2
  140. package/dist/ui/ReplaceAssetDialog.d.cts.map +1 -1
  141. package/dist/ui/ReplaceAssetDialog.d.ts +5 -2
  142. package/dist/ui/ReplaceAssetDialog.d.ts.map +1 -1
  143. package/dist/ui/ReplaceAssetDialog.js +7 -2
  144. package/dist/ui/UnsplashPanel.d.cts +4 -1
  145. package/dist/ui/UnsplashPanel.d.cts.map +1 -1
  146. package/dist/ui/UnsplashPanel.d.ts +4 -1
  147. package/dist/ui/UnsplashPanel.d.ts.map +1 -1
  148. package/dist/ui/UploadButton.cjs +7 -2
  149. package/dist/ui/UploadButton.d.cts +6 -2
  150. package/dist/ui/UploadButton.d.cts.map +1 -1
  151. package/dist/ui/UploadButton.d.ts +6 -2
  152. package/dist/ui/UploadButton.d.ts.map +1 -1
  153. package/dist/ui/UploadButton.js +7 -2
  154. package/dist/utils/asset-reference.cjs +87 -4
  155. package/dist/utils/asset-reference.d.cts +30 -6
  156. package/dist/utils/asset-reference.d.cts.map +1 -1
  157. package/dist/utils/asset-reference.d.ts +30 -6
  158. package/dist/utils/asset-reference.d.ts.map +1 -1
  159. package/dist/utils/asset-reference.js +83 -3
  160. package/dist/utils/csp.cjs +16 -0
  161. package/dist/utils/csp.d.cts +23 -0
  162. package/dist/utils/csp.d.cts.map +1 -1
  163. package/dist/utils/csp.d.ts +23 -0
  164. package/dist/utils/csp.d.ts.map +1 -1
  165. package/dist/utils/csp.js +16 -0
  166. package/dist/utils/data-source.cjs +19 -5
  167. package/dist/utils/data-source.d.cts +5 -1
  168. package/dist/utils/data-source.d.cts.map +1 -1
  169. package/dist/utils/data-source.d.ts +5 -1
  170. package/dist/utils/data-source.d.ts.map +1 -1
  171. package/dist/utils/data-source.js +19 -5
  172. package/dist/utils/errors.d.cts +5 -0
  173. package/dist/utils/errors.d.cts.map +1 -1
  174. package/dist/utils/errors.d.ts +5 -0
  175. package/dist/utils/errors.d.ts.map +1 -1
  176. package/dist/utils/query-param-transform.cjs +101 -0
  177. package/dist/utils/query-param-transform.d.cts +46 -0
  178. package/dist/utils/query-param-transform.d.cts.map +1 -0
  179. package/dist/utils/query-param-transform.d.ts +46 -0
  180. package/dist/utils/query-param-transform.d.ts.map +1 -0
  181. package/dist/utils/query-param-transform.js +60 -0
  182. package/dist/utils/registry.cjs +3 -0
  183. package/dist/utils/registry.d.cts +8 -0
  184. package/dist/utils/registry.d.cts.map +1 -1
  185. package/dist/utils/registry.d.ts +8 -0
  186. package/dist/utils/registry.d.ts.map +1 -1
  187. package/dist/utils/registry.js +3 -0
  188. package/dist/utils/resolver.cjs +19 -11
  189. package/dist/utils/resolver.d.cts +24 -0
  190. package/dist/utils/resolver.d.cts.map +1 -1
  191. package/dist/utils/resolver.d.ts +24 -0
  192. package/dist/utils/resolver.d.ts.map +1 -1
  193. package/dist/utils/resolver.js +19 -11
  194. package/dist/utils/retry.d.cts +2 -0
  195. package/dist/utils/retry.d.cts.map +1 -1
  196. package/dist/utils/retry.d.ts +2 -0
  197. package/dist/utils/retry.d.ts.map +1 -1
  198. package/dist/utils/run-resumable-upload.cjs +160 -0
  199. package/dist/utils/run-resumable-upload.d.cts +56 -0
  200. package/dist/utils/run-resumable-upload.d.cts.map +1 -0
  201. package/dist/utils/run-resumable-upload.d.ts +56 -0
  202. package/dist/utils/run-resumable-upload.d.ts.map +1 -0
  203. package/dist/utils/run-resumable-upload.js +122 -0
  204. package/dist/utils/sniff-file-type.cjs +209 -0
  205. package/dist/utils/sniff-file-type.d.cts +25 -0
  206. package/dist/utils/sniff-file-type.d.cts.map +1 -0
  207. package/dist/utils/sniff-file-type.d.ts +25 -0
  208. package/dist/utils/sniff-file-type.d.ts.map +1 -0
  209. package/dist/utils/sniff-file-type.js +164 -0
  210. package/dist/utils/studio-asset-source.cjs +11 -6
  211. package/dist/utils/studio-asset-source.d.cts +5 -1
  212. package/dist/utils/studio-asset-source.d.cts.map +1 -1
  213. package/dist/utils/studio-asset-source.d.ts +5 -1
  214. package/dist/utils/studio-asset-source.d.ts.map +1 -1
  215. package/dist/utils/studio-asset-source.js +11 -6
  216. package/dist/utils/upload-session-store.cjs +125 -0
  217. package/dist/utils/upload-session-store.d.cts +55 -0
  218. package/dist/utils/upload-session-store.d.cts.map +1 -0
  219. package/dist/utils/upload-session-store.d.ts +55 -0
  220. package/dist/utils/upload-session-store.d.ts.map +1 -0
  221. package/dist/utils/upload-session-store.js +84 -0
  222. package/dist/utils/validate-upload-result.cjs +9 -1
  223. package/dist/utils/validate-upload-result.d.cts +1 -0
  224. package/dist/utils/validate-upload-result.d.cts.map +1 -1
  225. package/dist/utils/validate-upload-result.d.ts +1 -0
  226. package/dist/utils/validate-upload-result.d.ts.map +1 -1
  227. package/dist/utils/validate-upload-result.js +9 -1
  228. package/dist/version.cjs +1 -1
  229. package/dist/version.d.cts +1 -1
  230. package/dist/version.d.cts.map +1 -1
  231. package/dist/version.d.ts +1 -1
  232. package/dist/version.d.ts.map +1 -1
  233. package/dist/version.js +1 -1
  234. package/meta/config.json +1 -1
  235. package/package.json +42 -12
@@ -0,0 +1,160 @@
1
+ "use strict";
2
+ var __webpack_require__ = {};
3
+ (()=>{
4
+ __webpack_require__.d = (exports1, getters, values)=>{
5
+ var define = (defs, kind)=>{
6
+ for(var key in defs)if (__webpack_require__.o(defs, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
7
+ enumerable: true,
8
+ [kind]: defs[key]
9
+ });
10
+ };
11
+ define(getters, "get");
12
+ define(values, "value");
13
+ };
14
+ })();
15
+ (()=>{
16
+ __webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
17
+ })();
18
+ (()=>{
19
+ __webpack_require__.r = (exports1)=>{
20
+ if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
21
+ value: 'Module'
22
+ });
23
+ Object.defineProperty(exports1, '__esModule', {
24
+ value: true
25
+ });
26
+ };
27
+ })();
28
+ var __webpack_exports__ = {};
29
+ __webpack_require__.r(__webpack_exports__);
30
+ __webpack_require__.d(__webpack_exports__, {
31
+ runResumableUpload: ()=>runResumableUpload
32
+ });
33
+ const external_errors_cjs_namespaceObject = require("./errors.cjs");
34
+ const external_retry_cjs_namespaceObject = require("./retry.cjs");
35
+ const external_upload_session_store_cjs_namespaceObject = require("./upload-session-store.cjs");
36
+ const DEFAULT_PART_SIZE = 8388608;
37
+ async function runResumableUpload(adapter, file, options = {}) {
38
+ const sessionStore = options.sessionStore ?? (0, external_upload_session_store_cjs_namespaceObject.createUploadSessionStore)();
39
+ const { signal } = options;
40
+ const callOptions = signal ? {
41
+ signal
42
+ } : void 0;
43
+ throwIfAborted(signal);
44
+ const persisted = await sessionStore.load(file) ?? void 0;
45
+ const session = await adapter.begin(file, persisted, callOptions);
46
+ const resumedParts = session.parts ?? [];
47
+ let effectivePartSize;
48
+ if (void 0 !== persisted && resumedParts.length > 0) {
49
+ const locked = persisted.partSize;
50
+ if (void 0 !== session.partSize && session.partSize !== locked) throw new external_errors_cjs_namespaceObject.AssetValidationError("PART_SIZE_MISMATCH", `runResumableUpload: resumed session changed part size (${locked} → ${session.partSize}); refusing to corrupt already-uploaded parts.`);
51
+ effectivePartSize = locked;
52
+ } else effectivePartSize = session.partSize ?? options.partSize ?? DEFAULT_PART_SIZE;
53
+ if (!Number.isSafeInteger(effectivePartSize) || effectivePartSize <= 0) throw new external_errors_cjs_namespaceObject.AssetValidationError("INVALID_PART_SIZE", `runResumableUpload: part size must be a positive integer (got ${effectivePartSize}).`);
54
+ const plan = planParts(file, effectivePartSize);
55
+ const sizeByPart = new Map(plan.map((p)=>[
56
+ p.partNumber,
57
+ p.end - p.start
58
+ ]));
59
+ const completed = new Map();
60
+ for (const tag of resumedParts)if (sizeByPart.has(tag.partNumber)) completed.set(tag.partNumber, tag);
61
+ const emitProgress = ()=>{
62
+ options.onProgress?.({
63
+ uploadedBytes: sumCompletedBytes(completed, sizeByPart),
64
+ totalBytes: file.size,
65
+ uploadedParts: completed.size,
66
+ totalParts: plan.length
67
+ });
68
+ };
69
+ emitProgress();
70
+ try {
71
+ for (const part of plan){
72
+ throwIfAborted(signal);
73
+ if (completed.has(part.partNumber)) continue;
74
+ const tag = await (0, external_retry_cjs_namespaceObject.withRetry)(()=>adapter.uploadPart(session, part, callOptions), {
75
+ ...options.retry ?? {},
76
+ ...signal ? {
77
+ signal
78
+ } : {}
79
+ });
80
+ completed.set(part.partNumber, tag);
81
+ await sessionStore.save(file, toPersisted(session, effectivePartSize, completed));
82
+ emitProgress();
83
+ }
84
+ const orderedTags = plan.map((part)=>{
85
+ const tag = completed.get(part.partNumber);
86
+ if (void 0 === tag) throw new Error(`runResumableUpload: missing tag for part ${part.partNumber}.`);
87
+ return tag;
88
+ });
89
+ const result = await adapter.complete(session, orderedTags, callOptions);
90
+ await sessionStore.clear(file);
91
+ return result;
92
+ } catch (error) {
93
+ if (isAbort(error, signal)) {
94
+ await safeAbort(adapter, session);
95
+ await sessionStore.clear(file);
96
+ }
97
+ throw error;
98
+ }
99
+ }
100
+ function planParts(file, partSize) {
101
+ const parts = [];
102
+ const size = file.size;
103
+ let partNumber = 1;
104
+ for(let start = 0; start < size; start += partSize){
105
+ const end = Math.min(start + partSize, size);
106
+ parts.push({
107
+ partNumber,
108
+ start,
109
+ end,
110
+ blob: file.slice(start, end)
111
+ });
112
+ partNumber += 1;
113
+ }
114
+ return parts;
115
+ }
116
+ function sumCompletedBytes(completed, sizeByPart) {
117
+ let total = 0;
118
+ for (const partNumber of completed.keys())total += sizeByPart.get(partNumber) ?? 0;
119
+ return total;
120
+ }
121
+ function toPersisted(session, partSize, completed) {
122
+ const parts = [
123
+ ...completed.values()
124
+ ].sort((a, b)=>a.partNumber - b.partNumber);
125
+ return {
126
+ uploadId: session.uploadId,
127
+ partSize,
128
+ parts,
129
+ ...session.meta ? {
130
+ meta: session.meta
131
+ } : {}
132
+ };
133
+ }
134
+ async function safeAbort(adapter, session) {
135
+ try {
136
+ await adapter.abort(session);
137
+ } catch {}
138
+ }
139
+ function isAbort(error, signal) {
140
+ if (signal?.aborted) return true;
141
+ return null !== error && "object" == typeof error && "AbortError" === error.name;
142
+ }
143
+ function throwIfAborted(signal) {
144
+ if (signal?.aborted) throw toAbortError(signal);
145
+ }
146
+ function toAbortError(signal) {
147
+ const reason = signal.reason;
148
+ if (reason instanceof Error) return reason;
149
+ if ("u" > typeof DOMException) return new DOMException("Aborted", "AbortError");
150
+ const error = new Error("Aborted");
151
+ error.name = "AbortError";
152
+ return error;
153
+ }
154
+ exports.runResumableUpload = __webpack_exports__.runResumableUpload;
155
+ for(var __rspack_i in __webpack_exports__)if (-1 === [
156
+ "runResumableUpload"
157
+ ].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
158
+ Object.defineProperty(exports, '__esModule', {
159
+ value: true
160
+ });
@@ -0,0 +1,56 @@
1
+ /**
2
+ * @file Adapter-agnostic resumable-upload runner (PRD 0004 §5.1 — M3).
3
+ *
4
+ * Drives any {@link ResumableUploadAdapter} through a full multipart lifecycle:
5
+ * slice the file into ordered parts, resume the ones a prior run already
6
+ * uploaded, upload the rest with per-part retry, emit progress, finalize, and
7
+ * tear down on abort. It is deliberately backend-neutral — it never assumes S3;
8
+ * the S3 specifics live in the adapter (M4).
9
+ *
10
+ * Trust boundary: this returns the adapter's raw {@link UploadResult}. The
11
+ * pipeline (M5) runs it through `validateUploadResult()` exactly as it does for
12
+ * single-shot uploads, so the runner stays a pure orchestrator.
13
+ *
14
+ * @experimental Public surface may change before v1.0.
15
+ */
16
+ import type { ResumableUploadAdapter, UploadSessionStore } from "../types/resumable.js";
17
+ import type { UploadResult } from "../types/types.js";
18
+ import { type RetryOptions } from "./retry.js";
19
+ /** Progress snapshot emitted as parts complete. */
20
+ export interface ResumableUploadProgress {
21
+ /** Bytes confirmed stored so far (sum of completed part sizes). */
22
+ readonly uploadedBytes: number;
23
+ /** Total bytes in the source file. */
24
+ readonly totalBytes: number;
25
+ /** Number of parts confirmed stored. */
26
+ readonly uploadedParts: number;
27
+ /** Total number of parts in the plan. */
28
+ readonly totalParts: number;
29
+ }
30
+ /** Options for {@link runResumableUpload}. */
31
+ export interface RunResumableUploadOptions {
32
+ /**
33
+ * Bytes per part for a *fresh* upload. Ignored when resuming — the locked
34
+ * size from the persisted/echoed session is used instead. Defaults to 8 MiB.
35
+ */
36
+ readonly partSize?: number;
37
+ /** Aborts the upload (and any in-flight part + retry sleeps). */
38
+ readonly signal?: AbortSignal;
39
+ /** Called after `begin` (with resumed progress) and after each part completes. */
40
+ readonly onProgress?: (progress: ResumableUploadProgress) => void;
41
+ /**
42
+ * Where progress is persisted for resume. Defaults to the built-in
43
+ * `createUploadSessionStore()` (localStorage, in-memory fallback).
44
+ */
45
+ readonly sessionStore?: UploadSessionStore;
46
+ /** Forwarded to `withRetry()` for each part upload (retry-by-part). */
47
+ readonly retry?: RetryOptions;
48
+ }
49
+ /**
50
+ * Upload `file` through `adapter` as a resumable multipart upload. Returns the
51
+ * adapter's `UploadResult` (unvalidated — the caller validates). Rejects on
52
+ * abort (after best-effort backend teardown) or when a part's retries are
53
+ * exhausted (leaving the session persisted so a later call can resume).
54
+ */
55
+ export declare function runResumableUpload(adapter: ResumableUploadAdapter, file: File, options?: RunResumableUploadOptions): Promise<UploadResult>;
56
+ //# sourceMappingURL=run-resumable-upload.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-resumable-upload.d.cts","sourceRoot":"","sources":["../../src/utils/run-resumable-upload.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAGX,sBAAsB,EAGtB,kBAAkB,EAClB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEtD,OAAO,EAAE,KAAK,YAAY,EAAa,MAAM,YAAY,CAAC;AAM1D,mDAAmD;AACnD,MAAM,WAAW,uBAAuB;IACvC,mEAAmE;IACnE,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,sCAAsC;IACtC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,wCAAwC;IACxC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,yCAAyC;IACzC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC5B;AAED,8CAA8C;AAC9C,MAAM,WAAW,yBAAyB;IACzC;;;OAGG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,iEAAiE;IACjE,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;IAC9B,kFAAkF;IAClF,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,uBAAuB,KAAK,IAAI,CAAC;IAClE;;;OAGG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,kBAAkB,CAAC;IAC3C,uEAAuE;IACvE,QAAQ,CAAC,KAAK,CAAC,EAAE,YAAY,CAAC;CAC9B;AAED;;;;;GAKG;AACH,wBAAsB,kBAAkB,CACvC,OAAO,EAAE,sBAAsB,EAC/B,IAAI,EAAE,IAAI,EACV,OAAO,GAAE,yBAA8B,GACrC,OAAO,CAAC,YAAY,CAAC,CAwGvB"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * @file Adapter-agnostic resumable-upload runner (PRD 0004 §5.1 — M3).
3
+ *
4
+ * Drives any {@link ResumableUploadAdapter} through a full multipart lifecycle:
5
+ * slice the file into ordered parts, resume the ones a prior run already
6
+ * uploaded, upload the rest with per-part retry, emit progress, finalize, and
7
+ * tear down on abort. It is deliberately backend-neutral — it never assumes S3;
8
+ * the S3 specifics live in the adapter (M4).
9
+ *
10
+ * Trust boundary: this returns the adapter's raw {@link UploadResult}. The
11
+ * pipeline (M5) runs it through `validateUploadResult()` exactly as it does for
12
+ * single-shot uploads, so the runner stays a pure orchestrator.
13
+ *
14
+ * @experimental Public surface may change before v1.0.
15
+ */
16
+ import type { ResumableUploadAdapter, UploadSessionStore } from "../types/resumable.js";
17
+ import type { UploadResult } from "../types/types.js";
18
+ import { type RetryOptions } from "./retry.js";
19
+ /** Progress snapshot emitted as parts complete. */
20
+ export interface ResumableUploadProgress {
21
+ /** Bytes confirmed stored so far (sum of completed part sizes). */
22
+ readonly uploadedBytes: number;
23
+ /** Total bytes in the source file. */
24
+ readonly totalBytes: number;
25
+ /** Number of parts confirmed stored. */
26
+ readonly uploadedParts: number;
27
+ /** Total number of parts in the plan. */
28
+ readonly totalParts: number;
29
+ }
30
+ /** Options for {@link runResumableUpload}. */
31
+ export interface RunResumableUploadOptions {
32
+ /**
33
+ * Bytes per part for a *fresh* upload. Ignored when resuming — the locked
34
+ * size from the persisted/echoed session is used instead. Defaults to 8 MiB.
35
+ */
36
+ readonly partSize?: number;
37
+ /** Aborts the upload (and any in-flight part + retry sleeps). */
38
+ readonly signal?: AbortSignal;
39
+ /** Called after `begin` (with resumed progress) and after each part completes. */
40
+ readonly onProgress?: (progress: ResumableUploadProgress) => void;
41
+ /**
42
+ * Where progress is persisted for resume. Defaults to the built-in
43
+ * `createUploadSessionStore()` (localStorage, in-memory fallback).
44
+ */
45
+ readonly sessionStore?: UploadSessionStore;
46
+ /** Forwarded to `withRetry()` for each part upload (retry-by-part). */
47
+ readonly retry?: RetryOptions;
48
+ }
49
+ /**
50
+ * Upload `file` through `adapter` as a resumable multipart upload. Returns the
51
+ * adapter's `UploadResult` (unvalidated — the caller validates). Rejects on
52
+ * abort (after best-effort backend teardown) or when a part's retries are
53
+ * exhausted (leaving the session persisted so a later call can resume).
54
+ */
55
+ export declare function runResumableUpload(adapter: ResumableUploadAdapter, file: File, options?: RunResumableUploadOptions): Promise<UploadResult>;
56
+ //# sourceMappingURL=run-resumable-upload.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-resumable-upload.d.ts","sourceRoot":"","sources":["../../src/utils/run-resumable-upload.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAGX,sBAAsB,EAGtB,kBAAkB,EAClB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEtD,OAAO,EAAE,KAAK,YAAY,EAAa,MAAM,YAAY,CAAC;AAM1D,mDAAmD;AACnD,MAAM,WAAW,uBAAuB;IACvC,mEAAmE;IACnE,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,sCAAsC;IACtC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,wCAAwC;IACxC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,yCAAyC;IACzC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC5B;AAED,8CAA8C;AAC9C,MAAM,WAAW,yBAAyB;IACzC;;;OAGG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,iEAAiE;IACjE,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;IAC9B,kFAAkF;IAClF,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,uBAAuB,KAAK,IAAI,CAAC;IAClE;;;OAGG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,kBAAkB,CAAC;IAC3C,uEAAuE;IACvE,QAAQ,CAAC,KAAK,CAAC,EAAE,YAAY,CAAC;CAC9B;AAED;;;;;GAKG;AACH,wBAAsB,kBAAkB,CACvC,OAAO,EAAE,sBAAsB,EAC/B,IAAI,EAAE,IAAI,EACV,OAAO,GAAE,yBAA8B,GACrC,OAAO,CAAC,YAAY,CAAC,CAwGvB"}
@@ -0,0 +1,122 @@
1
+ import { AssetValidationError } from "./errors.js";
2
+ import { withRetry } from "./retry.js";
3
+ import { createUploadSessionStore } from "./upload-session-store.js";
4
+ const DEFAULT_PART_SIZE = 8388608;
5
+ async function runResumableUpload(adapter, file, options = {}) {
6
+ const sessionStore = options.sessionStore ?? createUploadSessionStore();
7
+ const { signal } = options;
8
+ const callOptions = signal ? {
9
+ signal
10
+ } : void 0;
11
+ throwIfAborted(signal);
12
+ const persisted = await sessionStore.load(file) ?? void 0;
13
+ const session = await adapter.begin(file, persisted, callOptions);
14
+ const resumedParts = session.parts ?? [];
15
+ let effectivePartSize;
16
+ if (void 0 !== persisted && resumedParts.length > 0) {
17
+ const locked = persisted.partSize;
18
+ if (void 0 !== session.partSize && session.partSize !== locked) throw new AssetValidationError("PART_SIZE_MISMATCH", `runResumableUpload: resumed session changed part size (${locked} → ${session.partSize}); refusing to corrupt already-uploaded parts.`);
19
+ effectivePartSize = locked;
20
+ } else effectivePartSize = session.partSize ?? options.partSize ?? DEFAULT_PART_SIZE;
21
+ if (!Number.isSafeInteger(effectivePartSize) || effectivePartSize <= 0) throw new AssetValidationError("INVALID_PART_SIZE", `runResumableUpload: part size must be a positive integer (got ${effectivePartSize}).`);
22
+ const plan = planParts(file, effectivePartSize);
23
+ const sizeByPart = new Map(plan.map((p)=>[
24
+ p.partNumber,
25
+ p.end - p.start
26
+ ]));
27
+ const completed = new Map();
28
+ for (const tag of resumedParts)if (sizeByPart.has(tag.partNumber)) completed.set(tag.partNumber, tag);
29
+ const emitProgress = ()=>{
30
+ options.onProgress?.({
31
+ uploadedBytes: sumCompletedBytes(completed, sizeByPart),
32
+ totalBytes: file.size,
33
+ uploadedParts: completed.size,
34
+ totalParts: plan.length
35
+ });
36
+ };
37
+ emitProgress();
38
+ try {
39
+ for (const part of plan){
40
+ throwIfAborted(signal);
41
+ if (completed.has(part.partNumber)) continue;
42
+ const tag = await withRetry(()=>adapter.uploadPart(session, part, callOptions), {
43
+ ...options.retry ?? {},
44
+ ...signal ? {
45
+ signal
46
+ } : {}
47
+ });
48
+ completed.set(part.partNumber, tag);
49
+ await sessionStore.save(file, toPersisted(session, effectivePartSize, completed));
50
+ emitProgress();
51
+ }
52
+ const orderedTags = plan.map((part)=>{
53
+ const tag = completed.get(part.partNumber);
54
+ if (void 0 === tag) throw new Error(`runResumableUpload: missing tag for part ${part.partNumber}.`);
55
+ return tag;
56
+ });
57
+ const result = await adapter.complete(session, orderedTags, callOptions);
58
+ await sessionStore.clear(file);
59
+ return result;
60
+ } catch (error) {
61
+ if (isAbort(error, signal)) {
62
+ await safeAbort(adapter, session);
63
+ await sessionStore.clear(file);
64
+ }
65
+ throw error;
66
+ }
67
+ }
68
+ function planParts(file, partSize) {
69
+ const parts = [];
70
+ const size = file.size;
71
+ let partNumber = 1;
72
+ for(let start = 0; start < size; start += partSize){
73
+ const end = Math.min(start + partSize, size);
74
+ parts.push({
75
+ partNumber,
76
+ start,
77
+ end,
78
+ blob: file.slice(start, end)
79
+ });
80
+ partNumber += 1;
81
+ }
82
+ return parts;
83
+ }
84
+ function sumCompletedBytes(completed, sizeByPart) {
85
+ let total = 0;
86
+ for (const partNumber of completed.keys())total += sizeByPart.get(partNumber) ?? 0;
87
+ return total;
88
+ }
89
+ function toPersisted(session, partSize, completed) {
90
+ const parts = [
91
+ ...completed.values()
92
+ ].sort((a, b)=>a.partNumber - b.partNumber);
93
+ return {
94
+ uploadId: session.uploadId,
95
+ partSize,
96
+ parts,
97
+ ...session.meta ? {
98
+ meta: session.meta
99
+ } : {}
100
+ };
101
+ }
102
+ async function safeAbort(adapter, session) {
103
+ try {
104
+ await adapter.abort(session);
105
+ } catch {}
106
+ }
107
+ function isAbort(error, signal) {
108
+ if (signal?.aborted) return true;
109
+ return null !== error && "object" == typeof error && "AbortError" === error.name;
110
+ }
111
+ function throwIfAborted(signal) {
112
+ if (signal?.aborted) throw toAbortError(signal);
113
+ }
114
+ function toAbortError(signal) {
115
+ const reason = signal.reason;
116
+ if (reason instanceof Error) return reason;
117
+ if ("u" > typeof DOMException) return new DOMException("Aborted", "AbortError");
118
+ const error = new Error("Aborted");
119
+ error.name = "AbortError";
120
+ return error;
121
+ }
122
+ export { runResumableUpload };
@@ -0,0 +1,209 @@
1
+ "use strict";
2
+ var __webpack_require__ = {};
3
+ (()=>{
4
+ __webpack_require__.d = (exports1, getters, values)=>{
5
+ var define = (defs, kind)=>{
6
+ for(var key in defs)if (__webpack_require__.o(defs, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
7
+ enumerable: true,
8
+ [kind]: defs[key]
9
+ });
10
+ };
11
+ define(getters, "get");
12
+ define(values, "value");
13
+ };
14
+ })();
15
+ (()=>{
16
+ __webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
17
+ })();
18
+ (()=>{
19
+ __webpack_require__.r = (exports1)=>{
20
+ if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
21
+ value: 'Module'
22
+ });
23
+ Object.defineProperty(exports1, '__esModule', {
24
+ value: true
25
+ });
26
+ };
27
+ })();
28
+ var __webpack_exports__ = {};
29
+ __webpack_require__.r(__webpack_exports__);
30
+ const ascii = (text)=>Array.from(text, (ch)=>ch.charCodeAt(0));
31
+ const SIGNATURES = [
32
+ {
33
+ mime: "image/png",
34
+ parts: [
35
+ {
36
+ offset: 0,
37
+ bytes: [
38
+ 0x89,
39
+ 0x50,
40
+ 0x4e,
41
+ 0x47,
42
+ 0x0d,
43
+ 0x0a,
44
+ 0x1a,
45
+ 0x0a
46
+ ]
47
+ }
48
+ ]
49
+ },
50
+ {
51
+ mime: "image/jpeg",
52
+ parts: [
53
+ {
54
+ offset: 0,
55
+ bytes: [
56
+ 0xff,
57
+ 0xd8,
58
+ 0xff
59
+ ]
60
+ }
61
+ ]
62
+ },
63
+ {
64
+ mime: "image/gif",
65
+ parts: [
66
+ {
67
+ offset: 0,
68
+ bytes: ascii("GIF8")
69
+ }
70
+ ]
71
+ },
72
+ {
73
+ mime: "image/bmp",
74
+ parts: [
75
+ {
76
+ offset: 0,
77
+ bytes: ascii("BM")
78
+ }
79
+ ]
80
+ },
81
+ {
82
+ mime: "image/webp",
83
+ parts: [
84
+ {
85
+ offset: 0,
86
+ bytes: ascii("RIFF")
87
+ },
88
+ {
89
+ offset: 8,
90
+ bytes: ascii("WEBP")
91
+ }
92
+ ]
93
+ },
94
+ {
95
+ mime: "audio/wav",
96
+ parts: [
97
+ {
98
+ offset: 0,
99
+ bytes: ascii("RIFF")
100
+ },
101
+ {
102
+ offset: 8,
103
+ bytes: ascii("WAVE")
104
+ }
105
+ ]
106
+ },
107
+ {
108
+ mime: "video/webm",
109
+ parts: [
110
+ {
111
+ offset: 0,
112
+ bytes: [
113
+ 0x1a,
114
+ 0x45,
115
+ 0xdf,
116
+ 0xa3
117
+ ]
118
+ }
119
+ ]
120
+ },
121
+ {
122
+ mime: "audio/ogg",
123
+ parts: [
124
+ {
125
+ offset: 0,
126
+ bytes: ascii("OggS")
127
+ }
128
+ ]
129
+ },
130
+ {
131
+ mime: "audio/mpeg",
132
+ parts: [
133
+ {
134
+ offset: 0,
135
+ bytes: ascii("ID3")
136
+ }
137
+ ]
138
+ },
139
+ {
140
+ mime: "application/pdf",
141
+ parts: [
142
+ {
143
+ offset: 0,
144
+ bytes: ascii("%PDF")
145
+ }
146
+ ]
147
+ }
148
+ ];
149
+ const FTYP_BRAND_MIME = {
150
+ avif: "image/avif",
151
+ avis: "image/avif",
152
+ heic: "image/heic",
153
+ heix: "image/heic",
154
+ heim: "image/heic",
155
+ heis: "image/heic",
156
+ mif1: "image/heic",
157
+ isom: "video/mp4",
158
+ iso2: "video/mp4",
159
+ mp41: "video/mp4",
160
+ mp42: "video/mp4",
161
+ avc1: "video/mp4",
162
+ dash: "video/mp4",
163
+ "qt ": "video/quicktime"
164
+ };
165
+ const FTYP = ascii("ftyp");
166
+ const SNIFF_BYTE_COUNT = 16;
167
+ function detectMimeFromBytes(bytes) {
168
+ if (matchesAt(bytes, {
169
+ offset: 4,
170
+ bytes: FTYP
171
+ })) {
172
+ const brand = readAscii(bytes, 8, 4);
173
+ return void 0 !== brand ? FTYP_BRAND_MIME[brand] : void 0;
174
+ }
175
+ for (const signature of SIGNATURES)if (signature.parts.every((part)=>matchesAt(bytes, part))) return signature.mime;
176
+ }
177
+ function readAscii(bytes, offset, length) {
178
+ if (offset + length > bytes.length) return;
179
+ let out = "";
180
+ for(let i = 0; i < length; i += 1)out += String.fromCharCode(bytes[offset + i]);
181
+ return out;
182
+ }
183
+ function matchesAt(bytes, part) {
184
+ if (part.offset + part.bytes.length > bytes.length) return false;
185
+ for(let i = 0; i < part.bytes.length; i += 1)if (bytes[part.offset + i] !== part.bytes[i]) return false;
186
+ return true;
187
+ }
188
+ async function sniffFileMime(file) {
189
+ if ("function" != typeof file.slice) return;
190
+ const buffer = await file.slice(0, SNIFF_BYTE_COUNT).arrayBuffer();
191
+ return detectMimeFromBytes(new Uint8Array(buffer));
192
+ }
193
+ __webpack_require__.d(__webpack_exports__, {
194
+ detectMimeFromBytes: ()=>detectMimeFromBytes,
195
+ sniffFileMime: ()=>sniffFileMime
196
+ }, {
197
+ SNIFF_BYTE_COUNT: SNIFF_BYTE_COUNT
198
+ });
199
+ exports.SNIFF_BYTE_COUNT = __webpack_exports__.SNIFF_BYTE_COUNT;
200
+ exports.detectMimeFromBytes = __webpack_exports__.detectMimeFromBytes;
201
+ exports.sniffFileMime = __webpack_exports__.sniffFileMime;
202
+ for(var __rspack_i in __webpack_exports__)if (-1 === [
203
+ "SNIFF_BYTE_COUNT",
204
+ "detectMimeFromBytes",
205
+ "sniffFileMime"
206
+ ].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
207
+ Object.defineProperty(exports, '__esModule', {
208
+ value: true
209
+ });
@@ -0,0 +1,25 @@
1
+ /**
2
+ * @file Magic-byte file-type sniffer (PRD 0004 — review #5 residual).
3
+ *
4
+ * Browser `File.type` is host-set and often empty or spoofable, so the MIME /
5
+ * extension allowlists in `validateSelectedFile` can be lied to. This detects a
6
+ * file's REAL type from its leading bytes (a small signature table covering the
7
+ * common image / video / audio / document formats) so the upload pipeline can
8
+ * reject content that contradicts its declared type.
9
+ *
10
+ * Lazy-loaded (imported only when `sniffContent` is enabled) so the signature
11
+ * table never enters the eager headless entry.
12
+ *
13
+ * @experimental Public surface may change before v1.0.
14
+ */
15
+ /** Number of leading bytes the table needs (max offset + part length). */
16
+ export declare const SNIFF_BYTE_COUNT = 16;
17
+ /**
18
+ * Detect a MIME type from a file's leading bytes, or `undefined` when no known
19
+ * signature matches (many valid types — SVG, plain text, etc. — have no fixed
20
+ * magic bytes, so the caller must treat `undefined` as "unknown", not "bad").
21
+ */
22
+ export declare function detectMimeFromBytes(bytes: Uint8Array): string | undefined;
23
+ /** Read a file's leading bytes and detect its type (see {@link detectMimeFromBytes}). */
24
+ export declare function sniffFileMime(file: File): Promise<string | undefined>;
25
+ //# sourceMappingURL=sniff-file-type.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sniff-file-type.d.cts","sourceRoot":"","sources":["../../src/utils/sniff-file-type.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AA8EH,0EAA0E;AAC1E,eAAO,MAAM,gBAAgB,KAAK,CAAC;AAEnC;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,GAAG,SAAS,CAYzE;AAuBD,yFAAyF;AACzF,wBAAsB,aAAa,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAI3E"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * @file Magic-byte file-type sniffer (PRD 0004 — review #5 residual).
3
+ *
4
+ * Browser `File.type` is host-set and often empty or spoofable, so the MIME /
5
+ * extension allowlists in `validateSelectedFile` can be lied to. This detects a
6
+ * file's REAL type from its leading bytes (a small signature table covering the
7
+ * common image / video / audio / document formats) so the upload pipeline can
8
+ * reject content that contradicts its declared type.
9
+ *
10
+ * Lazy-loaded (imported only when `sniffContent` is enabled) so the signature
11
+ * table never enters the eager headless entry.
12
+ *
13
+ * @experimental Public surface may change before v1.0.
14
+ */
15
+ /** Number of leading bytes the table needs (max offset + part length). */
16
+ export declare const SNIFF_BYTE_COUNT = 16;
17
+ /**
18
+ * Detect a MIME type from a file's leading bytes, or `undefined` when no known
19
+ * signature matches (many valid types — SVG, plain text, etc. — have no fixed
20
+ * magic bytes, so the caller must treat `undefined` as "unknown", not "bad").
21
+ */
22
+ export declare function detectMimeFromBytes(bytes: Uint8Array): string | undefined;
23
+ /** Read a file's leading bytes and detect its type (see {@link detectMimeFromBytes}). */
24
+ export declare function sniffFileMime(file: File): Promise<string | undefined>;
25
+ //# sourceMappingURL=sniff-file-type.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sniff-file-type.d.ts","sourceRoot":"","sources":["../../src/utils/sniff-file-type.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AA8EH,0EAA0E;AAC1E,eAAO,MAAM,gBAAgB,KAAK,CAAC;AAEnC;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,GAAG,SAAS,CAYzE;AAuBD,yFAAyF;AACzF,wBAAsB,aAAa,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAI3E"}