@openrails/contentkit-ui 0.0.0-placeholder → 0.68.0

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 (367) hide show
  1. package/README.md +424 -1
  2. package/dist/appearance.d.ts +38 -0
  3. package/dist/appearance.js +15 -0
  4. package/dist/appearance.js.map +1 -0
  5. package/dist/assets/hash.worker-BYfo-7MQ.js.map +1 -0
  6. package/dist/client/aspect.d.ts +6 -0
  7. package/dist/client/aspect.js +17 -0
  8. package/dist/client/aspect.js.map +1 -0
  9. package/dist/client/client.d.ts +51 -0
  10. package/dist/client/client.js +35 -0
  11. package/dist/client/client.js.map +1 -0
  12. package/dist/client/codes.d.ts +12 -0
  13. package/dist/client/codes.js +19 -0
  14. package/dist/client/codes.js.map +1 -0
  15. package/dist/client/content/bans.d.ts +15 -0
  16. package/dist/client/content/bans.js +40 -0
  17. package/dist/client/content/bans.js.map +1 -0
  18. package/dist/client/content/comments.d.ts +36 -0
  19. package/dist/client/content/comments.js +93 -0
  20. package/dist/client/content/comments.js.map +1 -0
  21. package/dist/client/content/favorites.d.ts +14 -0
  22. package/dist/client/content/favorites.js +42 -0
  23. package/dist/client/content/favorites.js.map +1 -0
  24. package/dist/client/content/moderation.d.ts +18 -0
  25. package/dist/client/content/moderation.js +37 -0
  26. package/dist/client/content/moderation.js.map +1 -0
  27. package/dist/client/content/polls.d.ts +47 -0
  28. package/dist/client/content/polls.js +148 -0
  29. package/dist/client/content/polls.js.map +1 -0
  30. package/dist/client/content/posts.d.ts +44 -0
  31. package/dist/client/content/posts.js +91 -0
  32. package/dist/client/content/posts.js.map +1 -0
  33. package/dist/client/content/reactions.d.ts +12 -0
  34. package/dist/client/content/reactions.js +36 -0
  35. package/dist/client/content/reactions.js.map +1 -0
  36. package/dist/client/content/types.d.ts +91 -0
  37. package/dist/client/content/types.js +8 -0
  38. package/dist/client/content/types.js.map +1 -0
  39. package/dist/client/crop.d.ts +32 -0
  40. package/dist/client/crop.js +103 -0
  41. package/dist/client/crop.js.map +1 -0
  42. package/dist/client/errors.d.ts +54 -0
  43. package/dist/client/errors.js +80 -0
  44. package/dist/client/errors.js.map +1 -0
  45. package/dist/client/gallery.d.ts +43 -0
  46. package/dist/client/gallery.js +86 -0
  47. package/dist/client/gallery.js.map +1 -0
  48. package/dist/client/generated/error-codes.d.ts +5 -0
  49. package/dist/client/generated/error-codes.js +131 -0
  50. package/dist/client/generated/error-codes.js.map +1 -0
  51. package/dist/client/generated/routes.d.ts +1086 -0
  52. package/dist/client/generated/routes.js +1400 -0
  53. package/dist/client/generated/routes.js.map +1 -0
  54. package/dist/client/generated/wire.d.ts +630 -0
  55. package/dist/client/generated/wire.js +6 -0
  56. package/dist/client/generated/wire.js.map +1 -0
  57. package/dist/client/http.d.ts +79 -0
  58. package/dist/client/http.js +74 -0
  59. package/dist/client/http.js.map +1 -0
  60. package/dist/client/image.d.ts +24 -0
  61. package/dist/client/image.js +65 -0
  62. package/dist/client/image.js.map +1 -0
  63. package/dist/client/index.d.ts +33 -0
  64. package/dist/client/media/api.d.ts +36 -0
  65. package/dist/client/media/api.js +61 -0
  66. package/dist/client/media/api.js.map +1 -0
  67. package/dist/client/media/client.d.ts +247 -0
  68. package/dist/client/media/client.js +407 -0
  69. package/dist/client/media/client.js.map +1 -0
  70. package/dist/client/media/encode.d.ts +3 -0
  71. package/dist/client/media/encode.js +8 -0
  72. package/dist/client/media/encode.js.map +1 -0
  73. package/dist/client/media/hash-stream.d.ts +6 -0
  74. package/dist/client/media/hash-stream.js +16 -0
  75. package/dist/client/media/hash-stream.js.map +1 -0
  76. package/dist/client/media/hash.d.ts +12 -0
  77. package/dist/client/media/hash.js +48 -0
  78. package/dist/client/media/hash.js.map +1 -0
  79. package/dist/client/media/hash.worker-inline.js +18 -0
  80. package/dist/client/media/hash.worker-inline.js.map +1 -0
  81. package/dist/client/media/pacer.d.ts +21 -0
  82. package/dist/client/media/pacer.js +25 -0
  83. package/dist/client/media/pacer.js.map +1 -0
  84. package/dist/client/media/queue.d.ts +112 -0
  85. package/dist/client/media/queue.js +274 -0
  86. package/dist/client/media/queue.js.map +1 -0
  87. package/dist/client/media/ref.d.ts +6 -0
  88. package/dist/client/media/ref.js +10 -0
  89. package/dist/client/media/ref.js.map +1 -0
  90. package/dist/client/media/rules.d.ts +52 -0
  91. package/dist/client/media/rules.js +139 -0
  92. package/dist/client/media/rules.js.map +1 -0
  93. package/dist/client/media/transport.d.ts +13 -0
  94. package/dist/client/media/transport.js +33 -0
  95. package/dist/client/media/transport.js.map +1 -0
  96. package/dist/client/media/windows.d.ts +15 -0
  97. package/dist/client/media/windows.js +39 -0
  98. package/dist/client/media/windows.js.map +1 -0
  99. package/dist/client/playback.d.ts +200 -0
  100. package/dist/client/playback.js +142 -0
  101. package/dist/client/playback.js.map +1 -0
  102. package/dist/client/player.d.ts +102 -0
  103. package/dist/client/player.js +155 -0
  104. package/dist/client/player.js.map +1 -0
  105. package/dist/client/public.d.ts +25 -0
  106. package/dist/client/public.js +18 -0
  107. package/dist/client/public.js.map +1 -0
  108. package/dist/client/rendition.d.ts +19 -0
  109. package/dist/client/rendition.js +16 -0
  110. package/dist/client/rendition.js.map +1 -0
  111. package/dist/client/route.d.ts +39 -0
  112. package/dist/client/route.js +31 -0
  113. package/dist/client/route.js.map +1 -0
  114. package/dist/client/taxonomy.d.ts +30 -0
  115. package/dist/client/taxonomy.js +77 -0
  116. package/dist/client/taxonomy.js.map +1 -0
  117. package/dist/client.js +33 -0
  118. package/dist/components/encode-progress.d.ts +15 -0
  119. package/dist/components/encode-progress.js +49 -0
  120. package/dist/components/encode-progress.js.map +1 -0
  121. package/dist/components/hover-preview.d.ts +29 -0
  122. package/dist/components/hover-preview.js +49 -0
  123. package/dist/components/hover-preview.js.map +1 -0
  124. package/dist/components/image-crop-dialog.d.ts +48 -0
  125. package/dist/components/image-crop-dialog.js +359 -0
  126. package/dist/components/image-crop-dialog.js.map +1 -0
  127. package/dist/components/lazy-mount.d.ts +10 -0
  128. package/dist/components/lazy-mount.js +22 -0
  129. package/dist/components/lazy-mount.js.map +1 -0
  130. package/dist/components/media-folder-editor.d.ts +45 -0
  131. package/dist/components/media-folder-editor.js +564 -0
  132. package/dist/components/media-folder-editor.js.map +1 -0
  133. package/dist/components/media-gallery.d.ts +54 -0
  134. package/dist/components/media-gallery.js +597 -0
  135. package/dist/components/media-gallery.js.map +1 -0
  136. package/dist/components/media-readiness-notice.d.ts +24 -0
  137. package/dist/components/media-readiness-notice.js +61 -0
  138. package/dist/components/media-readiness-notice.js.map +1 -0
  139. package/dist/components/player-controls.d.ts +71 -0
  140. package/dist/components/player-controls.js +264 -0
  141. package/dist/components/player-controls.js.map +1 -0
  142. package/dist/components/progress.d.ts +5 -0
  143. package/dist/components/progress.js +14 -0
  144. package/dist/components/progress.js.map +1 -0
  145. package/dist/components/rendition-img.d.ts +34 -0
  146. package/dist/components/rendition-img.js +78 -0
  147. package/dist/components/rendition-img.js.map +1 -0
  148. package/dist/components/slot-editor.d.ts +95 -0
  149. package/dist/components/slot-editor.js +182 -0
  150. package/dist/components/slot-editor.js.map +1 -0
  151. package/dist/components/slot-image.d.ts +23 -0
  152. package/dist/components/slot-image.js +48 -0
  153. package/dist/components/slot-image.js.map +1 -0
  154. package/dist/components/slot-upload.d.ts +41 -0
  155. package/dist/components/slot-upload.js +213 -0
  156. package/dist/components/slot-upload.js.map +1 -0
  157. package/dist/components/social/bans.d.ts +42 -0
  158. package/dist/components/social/bans.js +300 -0
  159. package/dist/components/social/bans.js.map +1 -0
  160. package/dist/components/social/comments.d.ts +42 -0
  161. package/dist/components/social/comments.js +517 -0
  162. package/dist/components/social/comments.js.map +1 -0
  163. package/dist/components/social/engagement.d.ts +45 -0
  164. package/dist/components/social/engagement.js +129 -0
  165. package/dist/components/social/engagement.js.map +1 -0
  166. package/dist/components/social/index.d.ts +6 -0
  167. package/dist/components/social/moderation.d.ts +32 -0
  168. package/dist/components/social/moderation.js +383 -0
  169. package/dist/components/social/moderation.js.map +1 -0
  170. package/dist/components/social/parts.d.ts +50 -0
  171. package/dist/components/social/parts.js +106 -0
  172. package/dist/components/social/parts.js.map +1 -0
  173. package/dist/components/social/poll-editor.d.ts +34 -0
  174. package/dist/components/social/poll-editor.js +567 -0
  175. package/dist/components/social/poll-editor.js.map +1 -0
  176. package/dist/components/social/poll.d.ts +30 -0
  177. package/dist/components/social/poll.js +252 -0
  178. package/dist/components/social/poll.js.map +1 -0
  179. package/dist/components/sortable-list.d.ts +29 -0
  180. package/dist/components/sortable-list.js +121 -0
  181. package/dist/components/sortable-list.js.map +1 -0
  182. package/dist/components/video-mini-player.d.ts +35 -0
  183. package/dist/components/video-mini-player.js +236 -0
  184. package/dist/components/video-mini-player.js.map +1 -0
  185. package/dist/components/video-player.d.ts +146 -0
  186. package/dist/components/video-player.js +630 -0
  187. package/dist/components/video-player.js.map +1 -0
  188. package/dist/components/video-poster-picker.d.ts +59 -0
  189. package/dist/components/video-poster-picker.js +335 -0
  190. package/dist/components/video-poster-picker.js.map +1 -0
  191. package/dist/components/video-poster.d.ts +24 -0
  192. package/dist/components/video-poster.js +46 -0
  193. package/dist/components/video-poster.js.map +1 -0
  194. package/dist/i18n/context.d.ts +4 -0
  195. package/dist/i18n/context.js +11 -0
  196. package/dist/i18n/context.js.map +1 -0
  197. package/dist/i18n/messages.d.ts +40 -0
  198. package/dist/i18n/messages.js +94 -0
  199. package/dist/i18n/messages.js.map +1 -0
  200. package/dist/index.d.ts +23 -0
  201. package/dist/index.js +41 -0
  202. package/dist/locales/de.d.ts +2 -0
  203. package/dist/locales/de.js +271 -0
  204. package/dist/locales/de.js.map +1 -0
  205. package/dist/locales/en.d.ts +457 -0
  206. package/dist/locales/en.js +271 -0
  207. package/dist/locales/en.js.map +1 -0
  208. package/dist/locales/es.d.ts +2 -0
  209. package/dist/locales/es.js +271 -0
  210. package/dist/locales/es.js.map +1 -0
  211. package/dist/locales/ja.d.ts +2 -0
  212. package/dist/locales/ja.js +271 -0
  213. package/dist/locales/ja.js.map +1 -0
  214. package/dist/locales/ko.d.ts +2 -0
  215. package/dist/locales/ko.js +271 -0
  216. package/dist/locales/ko.js.map +1 -0
  217. package/dist/locales/social/de.d.ts +2 -0
  218. package/dist/locales/social/de.js +198 -0
  219. package/dist/locales/social/de.js.map +1 -0
  220. package/dist/locales/social/en.d.ts +195 -0
  221. package/dist/locales/social/en.js +198 -0
  222. package/dist/locales/social/en.js.map +1 -0
  223. package/dist/locales/social/es.d.ts +2 -0
  224. package/dist/locales/social/es.js +198 -0
  225. package/dist/locales/social/es.js.map +1 -0
  226. package/dist/locales/social/ja.d.ts +2 -0
  227. package/dist/locales/social/ja.js +198 -0
  228. package/dist/locales/social/ja.js.map +1 -0
  229. package/dist/locales/social/ko.d.ts +2 -0
  230. package/dist/locales/social/ko.js +198 -0
  231. package/dist/locales/social/ko.js.map +1 -0
  232. package/dist/locales/social/zh.d.ts +2 -0
  233. package/dist/locales/social/zh.js +198 -0
  234. package/dist/locales/social/zh.js.map +1 -0
  235. package/dist/locales/zh.d.ts +2 -0
  236. package/dist/locales/zh.js +271 -0
  237. package/dist/locales/zh.js.map +1 -0
  238. package/dist/provider.d.ts +22 -0
  239. package/dist/provider.js +59 -0
  240. package/dist/provider.js.map +1 -0
  241. package/dist/react/canonical.d.ts +33 -0
  242. package/dist/react/canonical.js +88 -0
  243. package/dist/react/canonical.js.map +1 -0
  244. package/dist/react/comments.d.ts +82 -0
  245. package/dist/react/comments.js +147 -0
  246. package/dist/react/comments.js.map +1 -0
  247. package/dist/react/config.d.ts +13 -0
  248. package/dist/react/config.js +14 -0
  249. package/dist/react/config.js.map +1 -0
  250. package/dist/react/context.d.ts +48 -0
  251. package/dist/react/context.js +37 -0
  252. package/dist/react/context.js.map +1 -0
  253. package/dist/react/crop.d.ts +49 -0
  254. package/dist/react/crop.js +107 -0
  255. package/dist/react/crop.js.map +1 -0
  256. package/dist/react/engagement.d.ts +38 -0
  257. package/dist/react/engagement.js +88 -0
  258. package/dist/react/engagement.js.map +1 -0
  259. package/dist/react/folder.d.ts +83 -0
  260. package/dist/react/folder.js +183 -0
  261. package/dist/react/folder.js.map +1 -0
  262. package/dist/react/gallery.d.ts +140 -0
  263. package/dist/react/gallery.js +477 -0
  264. package/dist/react/gallery.js.map +1 -0
  265. package/dist/react/index.d.ts +21 -0
  266. package/dist/react/inline-preview.d.ts +24 -0
  267. package/dist/react/inline-preview.js +76 -0
  268. package/dist/react/inline-preview.js.map +1 -0
  269. package/dist/react/player.d.ts +70 -0
  270. package/dist/react/player.js +214 -0
  271. package/dist/react/player.js.map +1 -0
  272. package/dist/react/polls.d.ts +74 -0
  273. package/dist/react/polls.js +176 -0
  274. package/dist/react/polls.js.map +1 -0
  275. package/dist/react/posts.d.ts +59 -0
  276. package/dist/react/posts.js +149 -0
  277. package/dist/react/posts.js.map +1 -0
  278. package/dist/react/provider.d.ts +23 -0
  279. package/dist/react/provider.js +35 -0
  280. package/dist/react/provider.js.map +1 -0
  281. package/dist/react/public.d.ts +39 -0
  282. package/dist/react/public.js +70 -0
  283. package/dist/react/public.js.map +1 -0
  284. package/dist/react/read.d.ts +52 -0
  285. package/dist/react/read.js +143 -0
  286. package/dist/react/read.js.map +1 -0
  287. package/dist/react/resources.d.ts +70 -0
  288. package/dist/react/resources.js +247 -0
  289. package/dist/react/resources.js.map +1 -0
  290. package/dist/react/slot.d.ts +102 -0
  291. package/dist/react/slot.js +181 -0
  292. package/dist/react/slot.js.map +1 -0
  293. package/dist/react/store.d.ts +51 -0
  294. package/dist/react/store.js +164 -0
  295. package/dist/react/store.js.map +1 -0
  296. package/dist/react/upload.d.ts +85 -0
  297. package/dist/react/upload.js +150 -0
  298. package/dist/react/upload.js.map +1 -0
  299. package/dist/react/use-resource.d.ts +43 -0
  300. package/dist/react/use-resource.js +66 -0
  301. package/dist/react/use-resource.js.map +1 -0
  302. package/dist/react/video.d.ts +118 -0
  303. package/dist/react/video.js +184 -0
  304. package/dist/react/video.js.map +1 -0
  305. package/dist/react/viewport.d.ts +19 -0
  306. package/dist/react/viewport.js +30 -0
  307. package/dist/react/viewport.js.map +1 -0
  308. package/dist/react.js +21 -0
  309. package/dist/scope.d.ts +14 -0
  310. package/dist/scope.js +30 -0
  311. package/dist/scope.js.map +1 -0
  312. package/dist/styles.css +3 -0
  313. package/dist/ui/alert-dialog.d.ts +18 -0
  314. package/dist/ui/alert-dialog.js +86 -0
  315. package/dist/ui/alert-dialog.js.map +1 -0
  316. package/dist/ui/alert.d.ts +10 -0
  317. package/dist/ui/alert.js +38 -0
  318. package/dist/ui/alert.js.map +1 -0
  319. package/dist/ui/avatar.d.ts +11 -0
  320. package/dist/ui/avatar.js +31 -0
  321. package/dist/ui/avatar.js.map +1 -0
  322. package/dist/ui/badge.d.ts +7 -0
  323. package/dist/ui/badge.js +31 -0
  324. package/dist/ui/badge.js.map +1 -0
  325. package/dist/ui/button.d.ts +8 -0
  326. package/dist/ui/button.js +46 -0
  327. package/dist/ui/button.js.map +1 -0
  328. package/dist/ui/dialog.d.ts +17 -0
  329. package/dist/ui/dialog.js +91 -0
  330. package/dist/ui/dialog.js.map +1 -0
  331. package/dist/ui/dropdown-menu.d.ts +15 -0
  332. package/dist/ui/dropdown-menu.js +105 -0
  333. package/dist/ui/dropdown-menu.js.map +1 -0
  334. package/dist/ui/input.d.ts +3 -0
  335. package/dist/ui/input.js +17 -0
  336. package/dist/ui/input.js.map +1 -0
  337. package/dist/ui/label.d.ts +3 -0
  338. package/dist/ui/label.js +15 -0
  339. package/dist/ui/label.js.map +1 -0
  340. package/dist/ui/progress.d.ts +7 -0
  341. package/dist/ui/progress.js +31 -0
  342. package/dist/ui/progress.js.map +1 -0
  343. package/dist/ui/select.d.ts +15 -0
  344. package/dist/ui/select.js +98 -0
  345. package/dist/ui/select.js.map +1 -0
  346. package/dist/ui/skeleton.d.ts +2 -0
  347. package/dist/ui/skeleton.js +14 -0
  348. package/dist/ui/skeleton.js.map +1 -0
  349. package/dist/ui/slider.d.ts +3 -0
  350. package/dist/ui/slider.js +35 -0
  351. package/dist/ui/slider.js.map +1 -0
  352. package/dist/ui/switch.d.ts +5 -0
  353. package/dist/ui/switch.js +20 -0
  354. package/dist/ui/switch.js.map +1 -0
  355. package/dist/ui/tabs.d.ts +10 -0
  356. package/dist/ui/tabs.js +47 -0
  357. package/dist/ui/tabs.js.map +1 -0
  358. package/dist/ui/textarea.d.ts +3 -0
  359. package/dist/ui/textarea.js +15 -0
  360. package/dist/ui/textarea.js.map +1 -0
  361. package/dist/ui/toggle-group.d.ts +5 -0
  362. package/dist/ui/toggle-group.js +23 -0
  363. package/dist/ui/toggle-group.js.map +1 -0
  364. package/dist/urls/index.d.ts +100 -0
  365. package/dist/urls.js +118 -0
  366. package/dist/urls.js.map +1 -0
  367. package/package.json +113 -3
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","names":[],"sources":["../../../src/client/media/client.ts"],"sourcesContent":["import { ContentKitError, aborted, failureError, throwIfAborted, toContentKitError } from \"../errors.js\";\nimport type { CommitBody, Edit, FileInfo, InlineImage, Op, PresetRule, PresignReply, ReadResult, RefBody, RequestReply } from \"../generated/wire.js\";\nimport type { Http } from \"../http.js\";\nimport { call } from \"../route.js\";\nimport { MediaApi, type ReadOptions } from \"./api.js\";\nimport { sha256Hex } from \"./hash.js\";\nimport { Pacer } from \"./pacer.js\";\nimport { defaultTransport, type Transport } from \"./transport.js\";\n\n/** Upload tuning for the media module. */\nexport interface MediaOptions {\n /** Sends presigned PUTs. Default XMLHttpRequest (upload progress), else fetch. */\n transport?: Transport;\n /** Parallel parts per multipart upload (the pacer may run fewer). Default 4. */\n concurrency?: number;\n /** Retries per part and per idempotent API call. Default 5. */\n retries?: number;\n /** Delay before retry attempt (0-based); retryAfter is the server's hint in seconds. */\n retryDelay?: (attempt: number, retryAfter?: number) => number;\n /** Seconds one part should take. Default 30. */\n targetPartSeconds?: number;\n}\n\nexport interface Progress {\n /** processing: committed, waiting for the worker (put). */\n phase: \"hashing\" | \"uploading\" | \"completing\" | \"processing\";\n loaded: number;\n total: number;\n}\n\nexport interface UploadOptions {\n ref: RefBody;\n /** The upload path: a literal (\"cover\") or under a pattern (\"originals/001.png\"); the server cleans it and adds the extension. */\n path: string;\n /** Content type; default the file's. */\n type?: string;\n /** Aborting pauses: a multipart upload stays resumable from the last state. */\n signal?: AbortSignal;\n onProgress?: (p: Progress) => void;\n /** State saved from onState, to resume a multipart upload of the same file. */\n resume?: UploadState;\n /** Resumable state after every change; null once the upload completed. Persist it to survive a reload. */\n onState?: (s: UploadState | null) => void;\n}\n\n/** An upload, ready for a put op. */\nexport interface UploadedFile {\n /** The path to commit: cleaned, with an extension, named by the server for a Named upload. */\n path: string;\n /**\n * The name to commit: the folder's identical blob (\"sha256-{hex}-{uuid}\") when\n * exists, else the staged upload (\"u-{uuid}\") the worker hashes and places\n * after the commit.\n */\n blob: string;\n type: string;\n size: number;\n /** The identical file was already in the item's folder; nothing was sent. */\n exists: boolean;\n /** The host processes files on upload: commit it unattached now (the queue does). */\n processOnUpload?: boolean;\n}\n\n/** The put op's fields besides path and blob. */\nexport interface PutOptions {\n /** Refuse to replace an existing upload. Default false. */\n createOnly?: boolean;\n edit?: Edit | null;\n meta?: Record<string, unknown>;\n index?: number;\n /** Wait until the worker processed the upload (waitFor). Default true. */\n wait?: boolean;\n /** How long to wait, ms. */\n timeout?: number;\n /** The upload, before the commit. */\n onUploaded?: (up: UploadedFile) => void;\n}\n\nexport interface WaitOptions {\n signal?: AbortSignal;\n /** Poll interval, ms. Default 1000. */\n interval?: number;\n /** ms; then render_timeout. Default 120000. */\n timeout?: number;\n}\n\nexport interface PlannedPart {\n number: number;\n offset: number;\n size: number;\n sha256?: string;\n}\n\n/** A multipart upload in flight; JSON-serializable. */\nexport interface UploadState {\n ticket: string;\n path: string;\n /** The staged upload (\"u-{uuid}\") the parts write. */\n blob: string;\n type: string;\n size: number;\n ref: RefBody;\n file: { name?: string; lastModified?: number; size: number };\n limits: { minPartSize: number; maxPartSize: number; maxParts: number };\n parts: PlannedPart[];\n processOnUpload?: boolean;\n}\n\n/** An upload to a server-named (Named) path: a post's or poll's image. */\nexport interface NamedOptions {\n /** The requested path; the server names the file. Default the file's name. */\n path?: string;\n signal?: AbortSignal;\n onProgress?: (p: Progress) => void;\n /** Wait until the worker processed it. Default true. */\n wait?: boolean;\n /** How long to wait, ms. */\n timeout?: number;\n}\n\n/** An upload to a Named path, committed. */\nexport interface NamedUpload {\n /** The server's name for it (\"i-{uuid}\"), which content routes take. */\n name: string;\n path: string;\n file: FileInfo;\n}\n\n/** The media kinds of post and poll folders (content.Media.PostKind and PollKind). */\nexport interface ContentFolders {\n /** Default \"post\". */\n post?: string;\n /** Default \"poll\". */\n poll?: string;\n}\n\nconst named = /^i-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;\n\n/** A file to upload again when its blob is gone at commit; type defaults to the file's. */\nexport type CommitSource = Blob | { file: Blob; type?: string };\n\nexport interface CommitOptions {\n signal?: AbortSignal;\n /** UUID for this complete batch. Keep it with unchanged ops for a later retry; default a new UUID. */\n operationID?: string;\n /** Persist the active batch before sending it, including any replacement after re-upload. Resume with its ops and operation_id. */\n onState?: (batch: CommitBody) => void;\n /** Blob (\"sha256-…\") → the file it was uploaded from. */\n sources?: Record<string, CommitSource>;\n}\n\ntype Uploadable = Blob & { name?: string; lastModified?: number };\n\n/** A path names a file by its full path or, for an upload, its stem (\"cover\" for \"cover.png\"). */\nexport const samePath = (file: string, path: string) => file === path || stem(file) === path;\n\n/** The path without its extension. */\nexport const stem = (path: string) => {\n const i = path.lastIndexOf(\".\");\n return i > path.lastIndexOf(\"/\") + 1 ? path.slice(0, i) : path;\n};\n\n/** The media module: uploads, commits, reads, frames and HLS. */\nexport class MediaClient {\n readonly api: MediaApi;\n private readonly transport: Transport;\n private readonly concurrency: number;\n private readonly retries: number;\n private readonly delay: (attempt: number, retryAfter?: number) => number;\n private readonly targetSeconds: number;\n private presetList?: Promise<PresetRule[]>;\n\n private readonly folders: Required<ContentFolders>;\n\n constructor(\n private readonly http: Http,\n o: MediaOptions = {},\n folders: ContentFolders = {},\n ) {\n this.folders = { post: folders.post ?? \"post\", poll: folders.poll ?? \"poll\" };\n this.api = new MediaApi(http);\n this.transport = o.transport ?? defaultTransport;\n this.concurrency = o.concurrency ?? 4;\n this.retries = o.retries ?? 5;\n this.delay = o.retryDelay ?? backoff;\n this.targetSeconds = o.targetPartSeconds ?? 30;\n }\n\n /**\n * Uploads file to the item's folder: it hashes the whole file (off the\n * main thread) and presigns {path, type, size, sha256}, then sends nothing\n * (the folder holds the identical blob), or stages it: one checksum-bound\n * PUT up to 64 MiB, resumable multipart above. A refused presign throws\n * before any bytes move. Commit the result with a put op (or use put());\n * the worker then places a staged upload at the hash of its bytes.\n */\n async upload(file: Uploadable, o: UploadOptions): Promise<UploadedFile> {\n const type = o.type || file.type;\n if (!type) throw new ContentKitError(\"invalid_request\", \"the file has no content type\");\n throwIfAborted(o.signal);\n if (o.resume) return this.resumeMultipart(file, o.resume, o);\n const total = file.size;\n const sha256 = await sha256Hex(file, { signal: o.signal, onProgress: (loaded) => o.onProgress?.({ phase: \"hashing\", loaded, total }) });\n const presign = () => this.api.presign({ ref: o.ref, path: o.path, type, size: total, sha256 }, o.signal);\n let p = await presign();\n const out = (p: PresignReply): UploadedFile => ({ path: p.path, blob: p.blob, type, size: total, exists: !!p.exists, processOnUpload: !!p.process_on_upload });\n if (p.exists) return out(p);\n if (p.multipart) {\n const m = p.multipart;\n const state: UploadState = {\n ticket: m.ticket,\n path: p.path,\n blob: p.blob,\n type,\n size: total,\n ref: o.ref,\n file: fingerprint(file),\n limits: { minPartSize: m.min_part_size, maxPartSize: m.max_part_size, maxParts: m.max_parts },\n parts: [],\n processOnUpload: !!p.process_on_upload,\n };\n return this.multipart(file, state, new Set(), o);\n }\n await this.retry(\n async () => {\n if (p.exists) return;\n await this.transport(p.put!, file, { signal: o.signal, onProgress: (loaded) => o.onProgress?.({ phase: \"uploading\", loaded, total }) });\n },\n o.signal,\n async (err) => {\n // An expired URL: presign again (a Named upload gets a new name).\n if (err.code === \"storage\" && err.status === 403) p = await presign();\n },\n );\n return out(p);\n }\n\n /**\n * Uploads file and commits it as a put to its path (with edit, meta and\n * index), then by default waits until the worker processed it; resolves\n * with the upload as an editor reads it.\n */\n async put(file: Uploadable, o: UploadOptions & PutOptions): Promise<FileInfo> {\n const up = await this.upload(file, o);\n o.onUploaded?.(up);\n const op: Op = { op: \"put\", path: up.path, blob: up.blob };\n if (o.createOnly) op.create_id = crypto.randomUUID();\n if (o.edit) op.edit = o.edit;\n if (o.meta) op.meta = o.meta;\n if (o.index !== undefined) op.index = o.index;\n const files = await this.commit(o.ref, [op], { signal: o.signal, sources: { [up.blob]: { file, type: up.type } } });\n if (o.wait === false) return files.find((f) => f.path === up.path) ?? { path: up.path, type: up.type };\n o.onProgress?.({ phase: \"processing\", loaded: up.size, total: up.size });\n return this.waitFor(o.ref, up.path, { signal: o.signal, timeout: o.timeout });\n }\n\n /**\n * Applies ops to the item in one conditional write; returns its uploads as\n * an editor reads them. With sources (blob → the file uploaded as it), a\n * not_uploaded refusal (the blob expired or is due for cleanup) uploads\n * the affected files again and retries the commit once.\n */\n async commit(ref: RefBody, ops: Op[], o: CommitOptions = {}): Promise<FileInfo[]> {\n // Snapshot the batch once: retries must not pick up edits to the caller's\n // ops while the first request or its response is still in flight.\n const body: CommitBody = structuredClone({ ref, ops, operation_id: o.operationID ?? crypto.randomUUID() });\n o.onState?.(structuredClone(body));\n let files: FileInfo[];\n try {\n files = (await this.retry(() => this.api.commit(body, o.signal), o.signal)).files;\n } catch (e) {\n if (!(e instanceof ContentKitError) || e.code !== \"not_uploaded\") throw e;\n const sources = o.sources ?? {};\n const puts = body.ops.filter((op) => op.op === \"put\" && op.blob && op.blob in sources);\n const stale = [...new Set(puts.map((op) => op.blob!))].filter((b) => !e.blobs || e.blobs.includes(b));\n if (stale.length === 0) throw e;\n const renamed = new Map<string, string>();\n for (const blob of stale) {\n const src = sources[blob]!;\n const file = src instanceof Blob ? src : src.file;\n const type = src instanceof Blob ? undefined : src.type;\n const op = puts.find((p) => p.blob === blob)!;\n renamed.set(blob, (await this.upload(file, { ref: body.ref, path: op.path!, type, signal: o.signal })).blob);\n }\n const retried = body.ops.map((op) => (op.blob && renamed.has(op.blob) ? { ...op, blob: renamed.get(op.blob) } : op));\n // Re-uploading changes the inputs. This is a new batch, not a retry\n // under the old ID; transport retries retain its new identity.\n const replacement = { ref: body.ref, ops: retried, operation_id: crypto.randomUUID() };\n o.onState?.(structuredClone(replacement));\n files = (await this.retry(() => this.api.commit(replacement, o.signal), o.signal)).files;\n }\n this.http.emit({ type: \"media.committed\", ref: { kind: ref.kind, id: ref.id }, files });\n return files;\n }\n\n /** The read API: the item's files under prefix in manifest order, with signed URLs for what this viewer may have. */\n async read(ref: RefBody, o: ReadOptions & { signal?: AbortSignal } = {}): Promise<ReadResult> {\n this.api.itemURL(ref); // a bad ref fails at once\n return this.retry(() => this.api.read(ref, o, o.signal), o.signal);\n }\n\n /**\n * Polls an editor read until the upload at path (or with that stem) is\n * processed: placed (not staged), nothing pending and its blob present (a\n * frame grabbed).\n * Rejects with its failure, not_found once it is gone, too_large while the\n * item is full (nothing is processed until uploads are removed),\n * render_timeout after the timeout.\n */\n async waitFor(ref: RefBody, path: string, o: WaitOptions = {}): Promise<FileInfo> {\n const until = Date.now() + (o.timeout ?? 120_000);\n for (;;) {\n const r = await this.read(ref, { editor: true, prefix: stem(path), signal: o.signal });\n const f = r.files.find((x) => x.upload && samePath(x.path, path));\n if (!f) throw new ContentKitError(\"not_found\", `no upload ${path}`, { status: 404 });\n if (f.failed) throw failureError(f.failed);\n if (!f.pending?.length && !f.staged && (f.size ?? 0) > 0) {\n this.http.emit({ type: \"media.processed\", ref: { kind: ref.kind, id: ref.id }, file: f });\n return f;\n }\n if (r.full) throw new ContentKitError(\"too_large\", `${path} waits: the item is full; remove uploads to process more`, { status: 413 });\n if (Date.now() >= until) throw new ContentKitError(\"render_timeout\", `${path} is still processing`);\n await sleep(o.interval ?? 1000, o.signal);\n }\n }\n\n /**\n * The upload's editor view, for re-cropping: its URL and the upload's\n * oriented size. Waits while it renders (an editor read asks for it);\n * not_found when the path has no upload, render_timeout after the timeout\n * (default 30 s).\n */\n async editorView(ref: RefBody, path: string, o: WaitOptions = {}): Promise<{ url: string; width: number; height: number; edit?: Edit }> {\n const until = Date.now() + (o.timeout ?? 30_000);\n for (;;) {\n const r = await this.read(ref, { editor: true, prefix: stem(path), signal: o.signal });\n const f = r.files.find((x) => x.upload && samePath(x.path, path));\n if (!f) throw new ContentKitError(\"not_found\", `no upload ${path}`, { status: 404 });\n if (f.editor_url && f.w && f.h) return { url: f.editor_url, width: f.w, height: f.h, ...(f.edit ? { edit: f.edit } : {}) };\n if (f.failed) throw failureError(f.failed);\n if (Date.now() >= until) throw new ContentKitError(\"render_timeout\", `the editor view of ${path} is still rendering`);\n await sleep(o.interval ?? 1000, o.signal);\n }\n }\n\n /**\n * Every kind's public presets, fetched once per client: crop bounds,\n * widths and default images. An item's current image is in its read's\n * `public`, never built from a preset. Shared by every caller, so it takes\n * no signal; a failed fetch is retried on the next call.\n */\n presets(): Promise<PresetRule[]> {\n this.presetList ??= this.retry(() => this.api.presets()).catch((e: unknown) => {\n this.presetList = undefined;\n throw e;\n });\n return this.presetList;\n }\n\n /** A JPEG still of the video upload at path, t seconds in, w pixels wide (default the frame's). */\n getFrame(ref: RefBody, path: string, t: number, w?: number, signal?: AbortSignal): Promise<Blob> {\n return this.retry(() => this.api.frame(ref, path, t, w, signal), signal);\n }\n\n /** The folder of an HLS ladder from a read's `hls`: `{media}/{kind}/{id}/hls/{dir}`; master.m3u8 and sprite.vtt resolve under it. */\n hlsBase(ref: RefBody, dir: string): string {\n return `${this.api.itemURL(ref)}/hls/${dir}`;\n }\n\n /** hls.js `xhrSetup` for playlists on the API (the client's token and credentials); pass it to players as is. */\n get xhrSetup(): (xhr: XMLHttpRequest, url: string) => Promise<void> {\n return this.http.xhrSetup;\n }\n\n /**\n * Uploads an image to the item's server-named upload path and commits it\n * (by default waiting until it is processed); resolves with the name content\n * routes take: a poll or option image, a post cover.\n */\n async uploadNamed(ref: RefBody, file: Uploadable, o: NamedOptions = {}): Promise<NamedUpload> {\n const f = await this.put(file, { ref, path: o.path ?? (file.name || \"image\"), signal: o.signal, onProgress: o.onProgress, wait: o.wait, timeout: o.timeout });\n const name = stem(f.path.slice(f.path.lastIndexOf(\"/\") + 1));\n if (!named.test(name)) throw new ContentKitError(\"invalid_request\", `${f.path} is not a server-named upload path of ${ref.kind}`);\n return { name, path: f.path, file: f };\n }\n\n /** An image for a post's rich-text body: uploaded to the post's folder; resolves with the URL to place in the body. */\n async uploadInline(post: string, file: Uploadable, o: NamedOptions = {}): Promise<NamedUpload & { url: string }> {\n const up = await this.uploadNamed(this.postRef(post), file, o);\n const r: InlineImage = await call(this.http, \"POST\", \"/posts/{id}/images\", { params: { id: post }, body: { image: up.name }, signal: o.signal });\n return { ...up, url: r.url };\n }\n\n /** The media folder of a post. */\n postRef(id: string): RefBody {\n return { kind: this.folders.post, id };\n }\n\n /** The media folder of a poll. */\n pollRef(id: string): RefBody {\n return { kind: this.folders.poll, id };\n }\n\n /** Discards a paused multipart upload. */\n async discard(state: UploadState): Promise<void> {\n await this.api.abort(state.ref, { ticket: state.ticket });\n }\n\n private async resumeMultipart(file: Uploadable, state: UploadState, o: UploadOptions): Promise<UploadedFile> {\n const fp = fingerprint(file);\n if (fp.size !== state.size || fp.name !== state.file.name || fp.lastModified !== state.file.lastModified) {\n throw new ContentKitError(\"resume_mismatch\", \"the saved upload is for a different file\");\n }\n let listed;\n try {\n listed = await this.retry(() => this.api.listParts(state.ref, { ticket: state.ticket }, o.signal), o.signal);\n } catch (err) {\n // Gone: already completed (Complete is idempotent) or expired.\n if (err instanceof ContentKitError && err.code === \"not_found\") return this.complete(state, o);\n throw err;\n }\n const landed = new Set<number>();\n for (const l of listed.parts) {\n const plan = state.parts.find((p) => p.number === l.number);\n if (!plan || plan.size !== l.size || (l.sha256 && plan.sha256 !== l.sha256)) {\n // Parts this state does not describe: the upload cannot be finished consistently.\n await this.discard(state).catch(() => {});\n o.onState?.(null);\n return this.upload(file, { ...o, resume: undefined });\n }\n landed.add(l.number);\n }\n return this.multipart(file, structuredClone(state), landed, o);\n }\n\n private async multipart(file: Uploadable, state: UploadState, landed: Set<number>, o: UploadOptions): Promise<UploadedFile> {\n const pacer = new Pacer({ ...state.limits, maxConcurrency: this.concurrency, targetSeconds: this.targetSeconds });\n const emitState = () => o.onState?.(structuredClone(state));\n const sending = new Map<number, number>();\n let sent = state.parts.filter((p) => landed.has(p.number)).reduce((n, p) => n + p.size, 0);\n const emitProgress = () => {\n let loaded = sent;\n for (const n of sending.values()) loaded += n;\n o.onProgress?.({ phase: \"uploading\", loaded, total: state.size });\n };\n const pending = state.parts.filter((p) => !landed.has(p.number));\n let offset = state.parts.reduce((end, p) => Math.max(end, p.offset + p.size), 0);\n const next = (): PlannedPart | undefined => {\n const p = pending.shift();\n if (p || offset >= state.size) return p;\n const part: PlannedPart = {\n number: state.parts.length + 1,\n offset,\n size: Math.min(pacer.partSize(), state.size - offset),\n };\n offset += part.size;\n state.parts.push(part);\n return part;\n };\n\n const stop = new AbortController();\n const signal = o.signal ? AbortSignal.any([o.signal, stop.signal]) : stop.signal;\n // Parts hash and presign ahead of the PUT slots, so a freed slot starts\n // its next PUT at once; pacer.concurrency bounds only the PUTs.\n let putting = 0;\n const waiting: (() => void)[] = [];\n const acquire = () =>\n new Promise<void>((resolve, reject) => {\n const take = () => {\n signal.removeEventListener(\"abort\", drop);\n putting++;\n resolve();\n };\n const drop = () => {\n waiting.splice(waiting.indexOf(take) >>> 0, 1);\n reject(aborted(signal));\n };\n if (putting < pacer.concurrency) return take();\n if (signal.aborted) return reject(aborted(signal));\n signal.addEventListener(\"abort\", drop, { once: true });\n waiting.push(take);\n });\n const release = () => {\n putting--;\n while (putting < pacer.concurrency && waiting.length) waiting.shift()!();\n };\n const run = async (part: PlannedPart) => {\n const body = file.slice(part.offset, part.offset + part.size);\n await this.retry(async () => {\n if (!part.sha256) {\n part.sha256 = await sha256Hex(body, { signal });\n emitState();\n }\n const { parts } = await this.api.parts(\n state.ref,\n { ticket: state.ticket, parts: [{ number: part.number, size: part.size, sha256: part.sha256 }] },\n signal,\n );\n const req = parts[0]?.request as RequestReply;\n await acquire();\n const began = performance.now();\n const alongside = putting;\n try {\n await this.transport(req, body, {\n signal,\n onProgress: (n) => {\n sending.set(part.number, n);\n emitProgress();\n },\n });\n } finally {\n sending.delete(part.number);\n release();\n }\n pacer.record(part.size, performance.now() - began, alongside);\n sent += part.size;\n emitProgress();\n }, signal);\n };\n\n emitState();\n emitProgress();\n const inFlight = new Set<Promise<void>>();\n let failure: unknown;\n for (;;) {\n let part: PlannedPart | undefined;\n while (failure === undefined && inFlight.size < pacer.concurrency + 2 && (part = next())) {\n emitState();\n const job: Promise<void> = run(part).then(\n () => void inFlight.delete(job),\n (err) => {\n inFlight.delete(job);\n if (failure === undefined) {\n failure = err;\n stop.abort();\n }\n },\n );\n inFlight.add(job);\n }\n if (inFlight.size === 0) break;\n await Promise.race(inFlight);\n }\n if (failure !== undefined) {\n throw o.signal?.aborted ? aborted(o.signal) : failure;\n }\n return this.complete(state, o);\n }\n\n private async complete(state: UploadState, o: UploadOptions): Promise<UploadedFile> {\n o.onProgress?.({ phase: \"completing\", loaded: state.size, total: state.size });\n const c = await this.retry(() => this.api.complete(state.ref, { ticket: state.ticket }, o.signal), o.signal);\n o.onState?.(null);\n return { path: state.path, blob: c.blob, type: c.type, size: c.size, exists: false, processOnUpload: state.processOnUpload };\n }\n\n private async retry<T>(fn: () => Promise<T>, signal?: AbortSignal, before?: (err: ContentKitError) => Promise<void>): Promise<T> {\n for (let attempt = 0; ; attempt++) {\n try {\n return await fn();\n } catch (e) {\n const err = toContentKitError(e);\n if (signal?.aborted) throw aborted(signal);\n if (!err.transient || attempt >= this.retries) throw err;\n await sleep(this.delay(attempt, err.retryAfter), signal);\n await before?.(err);\n }\n }\n }\n}\n\nfunction fingerprint(f: Uploadable): UploadState[\"file\"] {\n return { name: f.name, lastModified: f.lastModified, size: f.size };\n}\n\n/** Exponential backoff from 1 s to 30 s with jitter, or the server's Retry-After. */\nexport function backoff(attempt: number, retryAfter?: number): number {\n if (retryAfter) return retryAfter * 1000;\n const base = Math.min(30_000, 1000 * 2 ** attempt);\n return base * (0.75 + Math.random() * 0.5);\n}\n\nexport function sleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal?.aborted) return reject(aborted(signal));\n const t = setTimeout(() => {\n signal?.removeEventListener(\"abort\", onAbort);\n resolve();\n }, ms);\n const onAbort = () => {\n clearTimeout(t);\n reject(aborted(signal));\n };\n signal?.addEventListener(\"abort\", onAbort, { once: true });\n });\n}\n"],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { EncodeProgress } from '../generated/wire.js';
2
+ /** Seconds left of an encode, counted down from when its report arrived (local clock; the server's `at` may be skewed). */
3
+ export declare function encodeRemaining(p: EncodeProgress | null | undefined, receivedAt: number, now: number): number | undefined;
@@ -0,0 +1,8 @@
1
+ //#region src/client/media/encode.ts
2
+ function e(e, t, n) {
3
+ if (e?.eta && !e.stalled) return Math.max(0, e.eta - (n - t) / 1e3);
4
+ }
5
+ //#endregion
6
+ export { e as encodeRemaining };
7
+
8
+ //# sourceMappingURL=encode.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"encode.js","names":[],"sources":["../../../src/client/media/encode.ts"],"sourcesContent":["import type { EncodeProgress } from \"../generated/wire.js\";\n\n/** Seconds left of an encode, counted down from when its report arrived (local clock; the server's `at` may be skewed). */\nexport function encodeRemaining(p: EncodeProgress | null | undefined, receivedAt: number, now: number): number | undefined {\n if (!p?.eta || p.stalled) return undefined;\n return Math.max(0, p.eta - (now - receivedAt) / 1000);\n}\n"],"mappings":";AAGA,SAAgB,EAAgB,GAAsC,GAAoB,GAAiC;CACrH,IAAC,GAAG,OAAO,GAAE,SACjB,OAAO,KAAK,IAAI,GAAG,EAAE,OAAO,IAAM,KAAc,GAAI;AACtD"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Lowercase hex SHA-256 of blob, read in 4 MiB slices through @noble/hashes,
3
+ * so memory stays bounded for any size. Shared by the hashing worker and
4
+ * its in-thread fallback.
5
+ */
6
+ export declare function streamSha256(blob: Blob, onProgress?: (hashed: number) => void, aborted?: () => boolean): Promise<string>;
@@ -0,0 +1,16 @@
1
+ import { bytesToHex as e } from "@noble/hashes/utils.js";
2
+ import { sha256 as t } from "@noble/hashes/sha2.js";
3
+ //#region src/client/media/hash-stream.ts
4
+ var n = 4 << 20;
5
+ async function r(r, i, a) {
6
+ let o = t.create();
7
+ for (let e = 0; e < r.size; e += n) {
8
+ if (a?.()) throw new DOMException("aborted", "AbortError");
9
+ o.update(new Uint8Array(await r.slice(e, e + n).arrayBuffer())), i?.(Math.min(e + n, r.size));
10
+ }
11
+ return e(o.digest());
12
+ }
13
+ //#endregion
14
+ export { r as streamSha256 };
15
+
16
+ //# sourceMappingURL=hash-stream.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hash-stream.js","names":[],"sources":["../../../src/client/media/hash-stream.ts"],"sourcesContent":["import { sha256 } from \"@noble/hashes/sha2.js\";\nimport { bytesToHex } from \"@noble/hashes/utils.js\";\n\nconst CHUNK = 4 << 20;\n\n/**\n * Lowercase hex SHA-256 of blob, read in 4 MiB slices through @noble/hashes,\n * so memory stays bounded for any size. Shared by the hashing worker and\n * its in-thread fallback.\n */\nexport async function streamSha256(blob: Blob, onProgress?: (hashed: number) => void, aborted?: () => boolean): Promise<string> {\n const h = sha256.create();\n for (let off = 0; off < blob.size; off += CHUNK) {\n if (aborted?.()) throw new DOMException(\"aborted\", \"AbortError\");\n h.update(new Uint8Array(await blob.slice(off, off + CHUNK).arrayBuffer()));\n onProgress?.(Math.min(off + CHUNK, blob.size));\n }\n return bytesToHex(h.digest());\n}\n"],"mappings":";;;AAGA,IAAM,IAAQ,KAAK;AAOnB,eAAsB,EAAa,GAAY,GAAuC,GAA0C;CAC9H,IAAM,IAAI,EAAO,OAAO;CACxB,KAAK,IAAI,IAAM,GAAG,IAAM,EAAK,MAAM,KAAO,GAAO;EAC/C,IAAI,IAAU,GAAG,MAAM,IAAI,aAAa,WAAW,YAAY;EAE/D,AADA,EAAE,OAAO,IAAI,WAAW,MAAM,EAAK,MAAM,GAAK,IAAM,CAAK,CAAC,CAAC,YAAY,CAAC,CAAC,GACzE,IAAa,KAAK,IAAI,IAAM,GAAO,EAAK,IAAI,CAAC;CAC/C;CACA,OAAO,EAAW,EAAE,OAAO,CAAC;AAC9B"}
@@ -0,0 +1,12 @@
1
+ export interface HashOptions {
2
+ signal?: AbortSignal;
3
+ onProgress?: (hashed: number) => void;
4
+ }
5
+ /**
6
+ * Lowercase hex SHA-256 of blob, off the main thread. Up to 64 MiB (every
7
+ * part and single PUT) it is one WebCrypto digest: native, so parts hash in
8
+ * parallel. Larger files stream through @noble/hashes in a Web Worker,
9
+ * since WebCrypto cannot hash incrementally; without workers (or WebCrypto)
10
+ * the same stream runs here.
11
+ */
12
+ export declare function sha256Hex(blob: Blob, o?: HashOptions): Promise<string>;
@@ -0,0 +1,48 @@
1
+ import { aborted as e, throwIfAborted as t } from "../errors.js";
2
+ import { streamSha256 as n } from "./hash-stream.js";
3
+ import r from "./hash.worker-inline.js";
4
+ import "../generated/wire.js";
5
+ import { bytesToHex as i } from "@noble/hashes/utils.js";
6
+ //#region src/client/media/hash.ts
7
+ async function a(r, a = {}) {
8
+ t(a.signal);
9
+ let s = globalThis.crypto?.subtle;
10
+ if (s && r.size <= 67108864) {
11
+ let e = await s.digest("SHA-256", await r.arrayBuffer());
12
+ return t(a.signal), a.onProgress?.(r.size), i(new Uint8Array(e));
13
+ }
14
+ let c = typeof Worker == "function" ? await o(r, a) : void 0;
15
+ if (c !== void 0) return c;
16
+ try {
17
+ return await n(r, a.onProgress, () => !!a.signal?.aborted);
18
+ } catch (t) {
19
+ throw a.signal?.aborted ? e(a.signal) : t;
20
+ }
21
+ }
22
+ function o(t, n) {
23
+ return new Promise((i, a) => {
24
+ let o;
25
+ try {
26
+ o = new r();
27
+ } catch {
28
+ return i(void 0);
29
+ }
30
+ let s = !1, c = () => {
31
+ o.terminate(), n.signal?.removeEventListener("abort", l);
32
+ }, l = () => {
33
+ c(), a(e(n.signal));
34
+ };
35
+ n.signal?.addEventListener("abort", l, { once: !0 }), o.onerror = (e) => {
36
+ e.preventDefault(), c(), s ? a(/* @__PURE__ */ Error("hashing failed: " + e.message)) : i(void 0);
37
+ }, o.onmessage = (e) => {
38
+ s = !0;
39
+ let t = e.data;
40
+ if (t.hashed !== void 0) return n.onProgress?.(t.hashed);
41
+ c(), t.hex ? i(t.hex) : a(/* @__PURE__ */ Error("hashing failed: " + t.error));
42
+ }, o.postMessage(t);
43
+ });
44
+ }
45
+ //#endregion
46
+ export { a as sha256Hex };
47
+
48
+ //# sourceMappingURL=hash.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hash.js","names":[],"sources":["../../../src/client/media/hash.ts"],"sourcesContent":["import { bytesToHex } from \"@noble/hashes/utils.js\";\nimport { aborted, throwIfAborted } from \"../errors.js\";\nimport { streamSha256 } from \"./hash-stream.js\";\nimport HashWorker from \"./hash.worker.ts?worker&inline\";\nimport { MAX_SINGLE_PUT } from \"../generated/wire.js\";\n\nexport interface HashOptions {\n signal?: AbortSignal;\n onProgress?: (hashed: number) => void;\n}\n\n/**\n * Lowercase hex SHA-256 of blob, off the main thread. Up to 64 MiB (every\n * part and single PUT) it is one WebCrypto digest: native, so parts hash in\n * parallel. Larger files stream through @noble/hashes in a Web Worker,\n * since WebCrypto cannot hash incrementally; without workers (or WebCrypto)\n * the same stream runs here.\n */\nexport async function sha256Hex(blob: Blob, o: HashOptions = {}): Promise<string> {\n throwIfAborted(o.signal);\n const subtle = globalThis.crypto?.subtle;\n if (subtle && blob.size <= MAX_SINGLE_PUT) {\n const digest = await subtle.digest(\"SHA-256\", await blob.arrayBuffer());\n throwIfAborted(o.signal);\n o.onProgress?.(blob.size);\n return bytesToHex(new Uint8Array(digest));\n }\n const worked = typeof Worker === \"function\" ? await inWorker(blob, o) : undefined;\n if (worked !== undefined) return worked;\n try {\n return await streamSha256(blob, o.onProgress, () => !!o.signal?.aborted);\n } catch (err) {\n throw o.signal?.aborted ? aborted(o.signal) : err;\n }\n}\n\n/** The hash from a worker; undefined when no worker can run (a CSP without blob: or data: workers). */\nfunction inWorker(blob: Blob, o: HashOptions): Promise<string | undefined> {\n return new Promise((resolve, reject) => {\n let w: Worker;\n try {\n w = new HashWorker();\n } catch {\n return resolve(undefined);\n }\n let started = false;\n const done = () => {\n w.terminate();\n o.signal?.removeEventListener(\"abort\", stop);\n };\n const stop = () => {\n done();\n reject(aborted(o.signal));\n };\n o.signal?.addEventListener(\"abort\", stop, { once: true });\n w.onerror = (e) => {\n e.preventDefault();\n done();\n if (started) reject(new Error(\"hashing failed: \" + e.message));\n else resolve(undefined);\n };\n w.onmessage = (e: MessageEvent<{ hashed?: number; hex?: string; error?: string }>) => {\n started = true;\n const m = e.data;\n if (m.hashed !== undefined) return o.onProgress?.(m.hashed);\n done();\n if (m.hex) resolve(m.hex);\n else reject(new Error(\"hashing failed: \" + m.error));\n };\n w.postMessage(blob);\n });\n}\n"],"mappings":";;;;;;AAkBA,eAAsB,EAAU,GAAY,IAAiB,CAAC,GAAoB;CAChF,EAAe,EAAE,MAAM;CACvB,IAAM,IAAS,WAAW,QAAQ;CAClC,IAAI,KAAU,EAAK,QAAA,UAAwB;EACzC,IAAM,IAAS,MAAM,EAAO,OAAO,WAAW,MAAM,EAAK,YAAY,CAAC;EAGtE,OAFA,EAAe,EAAE,MAAM,GACvB,EAAE,aAAa,EAAK,IAAI,GACjB,EAAW,IAAI,WAAW,CAAM,CAAC;CAC1C;CACA,IAAM,IAAS,OAAO,UAAW,aAAa,MAAM,EAAS,GAAM,CAAC,IAAI,KAAA;CACxE,IAAI,MAAW,KAAA,GAAW,OAAO;CACjC,IAAI;EACF,OAAO,MAAM,EAAa,GAAM,EAAE,kBAAkB,CAAC,CAAC,EAAE,QAAQ,OAAO;CACzE,SAAS,GAAK;EACZ,MAAM,EAAE,QAAQ,UAAU,EAAQ,EAAE,MAAM,IAAI;CAChD;AACF;AAGA,SAAS,EAAS,GAAY,GAA6C;CACzE,OAAO,IAAI,SAAS,GAAS,MAAW;EACtC,IAAI;EACJ,IAAI;GACF,IAAI,IAAI,EAAW;EACrB,QAAQ;GACN,OAAO,EAAQ,KAAA,CAAS;EAC1B;EACA,IAAI,IAAU,IACR,UAAa;GAEjB,AADA,EAAE,UAAU,GACZ,EAAE,QAAQ,oBAAoB,SAAS,CAAI;EAC7C,GACM,UAAa;GAEjB,AADA,EAAK,GACL,EAAO,EAAQ,EAAE,MAAM,CAAC;EAC1B;EAgBA,AAfA,EAAE,QAAQ,iBAAiB,SAAS,GAAM,EAAE,MAAM,GAAK,CAAC,GACxD,EAAE,WAAW,MAAM;GAGjB,AAFA,EAAE,eAAe,GACjB,EAAK,GACD,IAAS,EAAO,gBAAI,MAAM,qBAAqB,EAAE,OAAO,CAAC,IACxD,EAAQ,KAAA,CAAS;EACxB,GACA,EAAE,aAAa,MAAuE;GACpF,IAAU;GACV,IAAM,IAAI,EAAE;GACZ,IAAI,EAAE,WAAW,KAAA,GAAW,OAAO,EAAE,aAAa,EAAE,MAAM;GAE1D,AADA,EAAK,GACD,EAAE,MAAK,EAAQ,EAAE,GAAG,IACnB,EAAO,gBAAI,MAAM,qBAAqB,EAAE,KAAK,CAAC;EACrD,GACA,EAAE,YAAY,CAAI;CACpB,CAAC;AACH"}
@@ -0,0 +1,18 @@
1
+ //#region src/client/media/hash.worker.ts?worker&inline
2
+ var e = "(function(){let e=e=>e/2**32|0,t=e=>e>>>0;function n(n,r,i,a){let o=e(i),s=t(i);n.setUint32(r,a?s:o,a),n.setUint32(r+4,a?o:s,a)}function r(e){return e instanceof Uint8Array||ArrayBuffer.isView(e)&&e.constructor.name===`Uint8Array`&&`BYTES_PER_ELEMENT`in e&&e.BYTES_PER_ELEMENT===1}let i=e=>e?`\"${e}\" `:``;function a(e,t=``){if(typeof e!=`number`)throw TypeError(i(t)+`expected number, got `+typeof e);if(!Number.isSafeInteger(e)||e<0)throw RangeError(i(t)+`expected integer >= 0, got `+e);return e}function o(e,t,n=``){if(r(e)&&(t===void 0||e.length===t))return e;t!==void 0&&a(t,`length`);let o=r(e),s=t===void 0?``:` of length ${t}`,c=o?`length=${e.length}`:`type=${typeof e}`,l=i(n)+`expected Uint8Array`+s+`, got `+c;throw o?RangeError(l):TypeError(l)}let s=(e,t)=>{if(typeof e!=`object`||!e||Array.isArray(e))throw TypeError((t===`object`?``:`\"${t}\" `)+`expected object, got type=`+typeof e)},c=(e,t)=>{s(e,t);let n=Object.getPrototypeOf(e);if(n!==Object.prototype&&n!==null)throw TypeError(`\"${t}\" expected plain object`);if(Object.hasOwn(e,`__proto__`))throw TypeError(`\"${t}.__proto__\" is not allowed`)};function l(e,t=!0){if(e.destroyed)throw Error(`hash was destroyed`);if(t&&e.finished)throw Error(`digest() was already called`)}function u(e,t){o(e,void 0,`output`);let n=t.outputLen;if(!(e.length>=n))throw RangeError(`\"output\" expected length >= `+n)}function d(...e){for(let t=0;t<e.length;t++)e[t].fill(0)}function f(e){return new DataView(e.buffer,e.byteOffset,e.byteLength)}function p(e,t){return e<<32-t|e>>>t}let m=typeof Uint8Array.from([]).toHex==`function`&&typeof Uint8Array.fromHex==`function`,h=/* @__PURE__ */ Array.from({length:256},(e,t)=>t.toString(16).padStart(2,`0`));function g(e){if(o(e),m)return e.toHex();let t=``;for(let n=0;n<e.length;n++)t+=h[e[n]];return t}function _(e,t,n=`opts`){return c(e,`defaults`),t!==void 0&&c(t,n),Object.assign(Object.create(null),e,t)}function v(e,t={}){if(typeof e!=`function`)throw TypeError(`\"hashCons\" expected function, got type=`+typeof e);t=_({},t,`info`);let n=(t,n)=>e(n).update(t).digest(),r=e(void 0);return n.outputLen=r.outputLen,n.blockLen=r.blockLen,n.canXOF=r.canXOF,n.create=t=>e(t),Object.assign(n,t),Object.freeze(n)}let y=e=>({oid:Uint8Array.from([6,9,96,134,72,1,101,3,4,2,e])});function b(e,t,n){return e&t^~e&n}function x(e,t,n){return e&t^e&n^t&n}var S=class{blockLen;outputLen;canXOF=!1;padOffset;isLE;buffer;view;finished=!1;length=0;pos=0;destroyed=!1;constructor(e,t,n,r){this.blockLen=e,this.outputLen=t,this.padOffset=n,this.isLE=r,this.buffer=new Uint8Array(e),this.view=f(this.buffer)}update(e){l(this),o(e);let{view:t,buffer:n,blockLen:r}=this,i=e.length,a=!1;for(let o=0;o<i;){let s=Math.min(r-this.pos,i-o);if(s===r){let t=f(e);for(;r<=i-o;o+=r)this.process(t,o);a=!0;continue}n.set(o===0&&s===i?e:e.subarray(o,o+s),this.pos),this.pos+=s,o+=s,this.pos===r&&(this.process(t,0),this.pos=0,a=!0)}return this.length+=e.length,a&&this.roundClean(),this}digestInto(e){l(this),u(e,this),this.finished=!0;let{buffer:t,view:r,blockLen:i,isLE:a}=this,{pos:o}=this;t[o++]=128,t.fill(0,o),this.padOffset>i-o&&(this.process(r,0),t.fill(0)),n(r,i-8,this.length*8,a),this.process(r,0),this.roundClean();let s=e===t?r:f(e),c=this.outputLen,d=c/4,p=this.get();if(c%4||d>p.length)throw Error(`invalid outputLen`);for(let e=0;e<d;e++)s.setUint32(4*e,p[e],a)}digest(){let{buffer:e,outputLen:t}=this;this.digestInto(e);let n=e.slice(0,t);return this.destroy(),n}_cloneIntoMeta(e){let{buffer:t,length:n,finished:r,destroyed:i,pos:a}=this;return e.destroyed=i,e.finished=r,e.length=n,e.pos=a,a&&e.buffer.set(t),e}clone(){return this._cloneInto()}};let C=/* @__PURE__ */ Uint32Array.from([1779033703,3144134277,1013904242,2773480762,1359893119,2600822924,528734635,1541459225]),w=/* @__PURE__ */ Uint32Array.from([1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298]),T=/* @__PURE__ */ new Uint32Array(64);var E=class extends S{A=0;B=0;C=0;D=0;E=0;F=0;G=0;H=0;constructor(e,t){super(64,e,8,!1),this.A=t[0]|0,this.B=t[1]|0,this.C=t[2]|0,this.D=t[3]|0,this.E=t[4]|0,this.F=t[5]|0,this.G=t[6]|0,this.H=t[7]|0}get(){let{A:e,B:t,C:n,D:r,E:i,F:a,G:o,H:s}=this;return[e,t,n,r,i,a,o,s]}set(e,t,n,r,i,a,o,s){this.A=e|0,this.B=t|0,this.C=n|0,this.D=r|0,this.E=i|0,this.F=a|0,this.G=o|0,this.H=s|0}_cloneInto(e){return(e||=new this.constructor).set(...this.get()),this._cloneIntoMeta(e)}process(e,t){for(let n=0;n<16;n++,t+=4)T[n]=e.getUint32(t,!1);for(let e=16;e<64;e++){let t=T[e-15],n=T[e-2],r=p(t,7)^p(t,18)^t>>>3,i=p(n,17)^p(n,19)^n>>>10;T[e]=i+T[e-7]+r+T[e-16]|0}let{A:n,B:r,C:i,D:a,E:o,F:s,G:c,H:l}=this;for(let e=0;e<64;e++){let t=p(o,6)^p(o,11)^p(o,25),u=l+t+b(o,s,c)+w[e]+T[e]|0,d=(p(n,2)^p(n,13)^p(n,22))+x(n,r,i)|0;l=c,c=s,s=o,o=a+u|0,a=i,i=r,r=n,n=u+d|0}n=n+this.A|0,r=r+this.B|0,i=i+this.C|0,a=a+this.D|0,o=o+this.E|0,s=s+this.F|0,c=c+this.G|0,l=l+this.H|0,this.set(n,r,i,a,o,s,c,l)}roundClean(){d(T)}destroy(){this.destroyed=!0,this.set(0,0,0,0,0,0,0,0),d(this.buffer)}},D=class extends E{constructor(){super(32,C)}};let O=/* @__PURE__ */ v(()=>new D,/* @__PURE__ */ y(1)),k=4<<20;async function A(e,t,n){let r=O.create();for(let i=0;i<e.size;i+=k){if(n?.())throw new DOMException(`aborted`,`AbortError`);r.update(new Uint8Array(await e.slice(i,i+k).arrayBuffer())),t?.(Math.min(i+k,e.size))}return g(r.digest())}let j=self;j.onmessage=e=>{A(e.data,e=>j.postMessage({hashed:e})).then(e=>j.postMessage({hex:e}),e=>j.postMessage({error:String(e)}))}})();\n//# sourceMappingURL=hash.worker-BYfo-7MQ.js.map", t = typeof self < "u" && self.Blob && new Blob(["(self.URL || self.webkitURL).revokeObjectURL(self.location.href);", e], { type: "text/javascript;charset=utf-8" });
3
+ function n(n) {
4
+ let r;
5
+ try {
6
+ if (r = t && (self.URL || self.webkitURL).createObjectURL(t), !r) throw "";
7
+ let e = new Worker(r, { name: n?.name });
8
+ return e.addEventListener("error", () => {
9
+ (self.URL || self.webkitURL).revokeObjectURL(r);
10
+ }), e;
11
+ } catch {
12
+ return new Worker("data:text/javascript;charset=utf-8," + encodeURIComponent(e), { name: n?.name });
13
+ }
14
+ }
15
+ //#endregion
16
+ export { n as default };
17
+
18
+ //# sourceMappingURL=hash.worker-inline.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hash.worker-inline.js","names":[],"sources":[],"mappings":""}
@@ -0,0 +1,21 @@
1
+ export interface PacerOptions {
2
+ minPartSize: number;
3
+ maxPartSize: number;
4
+ maxConcurrency: number;
5
+ /** Seconds one part should take; keeps parts well inside proxy timeouts. */
6
+ targetSeconds: number;
7
+ }
8
+ /**
9
+ * Adapts multipart part size and concurrency to measured throughput. Until the
10
+ * first part lands it sends one min-size part at a time, so a slow link never
11
+ * starts several parts that each outlast the proxy timeout.
12
+ */
13
+ export declare class Pacer {
14
+ private readonly o;
15
+ private rate?;
16
+ constructor(o: PacerOptions);
17
+ /** A part of bytes took ms with inFlight parts running alongside it (itself included). */
18
+ record(bytes: number, ms: number, inFlight: number): void;
19
+ get concurrency(): number;
20
+ partSize(): number;
21
+ }
@@ -0,0 +1,25 @@
1
+ //#region src/client/media/pacer.ts
2
+ var e = 1 << 20, t = class {
3
+ o;
4
+ rate;
5
+ constructor(e) {
6
+ this.o = e;
7
+ }
8
+ record(e, t, n) {
9
+ let r = e / Math.max(t, 1) * 1e3 * Math.max(n, 1);
10
+ this.rate = this.rate === void 0 ? r : .5 * this.rate + .5 * r;
11
+ }
12
+ get concurrency() {
13
+ return this.rate === void 0 ? 1 : n(Math.floor(this.rate * this.o.targetSeconds / this.o.minPartSize), 1, this.o.maxConcurrency);
14
+ }
15
+ partSize() {
16
+ return this.rate === void 0 ? this.o.minPartSize : n(Math.floor(this.rate / this.concurrency * this.o.targetSeconds / e) * e, this.o.minPartSize, this.o.maxPartSize);
17
+ }
18
+ };
19
+ function n(e, t, n) {
20
+ return Math.min(Math.max(e, t), n);
21
+ }
22
+ //#endregion
23
+ export { t as Pacer };
24
+
25
+ //# sourceMappingURL=pacer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pacer.js","names":[],"sources":["../../../src/client/media/pacer.ts"],"sourcesContent":["const MiB = 1 << 20;\n\nexport interface PacerOptions {\n minPartSize: number;\n maxPartSize: number;\n maxConcurrency: number;\n /** Seconds one part should take; keeps parts well inside proxy timeouts. */\n targetSeconds: number;\n}\n\n/**\n * Adapts multipart part size and concurrency to measured throughput. Until the\n * first part lands it sends one min-size part at a time, so a slow link never\n * starts several parts that each outlast the proxy timeout.\n */\nexport class Pacer {\n private rate?: number; // aggregate bytes/s, smoothed\n\n constructor(private readonly o: PacerOptions) {}\n\n /** A part of bytes took ms with inFlight parts running alongside it (itself included). */\n record(bytes: number, ms: number, inFlight: number): void {\n const agg = (bytes / Math.max(ms, 1)) * 1000 * Math.max(inFlight, 1);\n this.rate = this.rate === undefined ? agg : 0.5 * this.rate + 0.5 * agg;\n }\n\n get concurrency(): number {\n if (this.rate === undefined) return 1;\n const fit = Math.floor((this.rate * this.o.targetSeconds) / this.o.minPartSize);\n return clamp(fit, 1, this.o.maxConcurrency);\n }\n\n partSize(): number {\n if (this.rate === undefined) return this.o.minPartSize;\n const size = Math.floor(((this.rate / this.concurrency) * this.o.targetSeconds) / MiB) * MiB;\n return clamp(size, this.o.minPartSize, this.o.maxPartSize);\n }\n}\n\nfunction clamp(v: number, lo: number, hi: number): number {\n return Math.min(Math.max(v, lo), hi);\n}\n"],"mappings":";AAAA,IAAM,IAAM,KAAK,IAeJ,IAAb,MAAmB;CAGY;CAF7B;CAEA,YAAY,GAAkC;EAAjB,KAAA,IAAA;CAAkB;CAG/C,OAAO,GAAe,GAAY,GAAwB;EACxD,IAAM,IAAO,IAAQ,KAAK,IAAI,GAAI,CAAC,IAAK,MAAO,KAAK,IAAI,GAAU,CAAC;EACnE,KAAK,OAAO,KAAK,SAAS,KAAA,IAAY,IAAM,KAAM,KAAK,OAAO,KAAM;CACtE;CAEA,IAAI,cAAsB;EAGxB,OAFI,KAAK,SAAS,KAAA,IAAkB,IAE7B,EADK,KAAK,MAAO,KAAK,OAAO,KAAK,EAAE,gBAAiB,KAAK,EAAE,WACtD,GAAK,GAAG,KAAK,EAAE,cAAc;CAC5C;CAEA,WAAmB;EAGjB,OAFI,KAAK,SAAS,KAAA,IAAkB,KAAK,EAAE,cAEpC,EADM,KAAK,MAAQ,KAAK,OAAO,KAAK,cAAe,KAAK,EAAE,gBAAiB,CAAG,IAAI,GACtE,KAAK,EAAE,aAAa,KAAK,EAAE,WAAW;CAC3D;AACF;AAEA,SAAS,EAAM,GAAW,GAAY,GAAoB;CACxD,OAAO,KAAK,IAAI,KAAK,IAAI,GAAG,CAAE,GAAG,CAAE;AACrC"}
@@ -0,0 +1,112 @@
1
+ import { ContentKitError } from '../errors.js';
2
+ import { FileInfo, RefBody } from '../generated/wire.js';
3
+ import { MediaClient, Progress, UploadedFile, UploadState } from './client.js';
4
+ export type ItemStatus = "queued" | "uploading" | "uploaded" | "failed" | "committed";
5
+ export interface QueueItem {
6
+ readonly id: string;
7
+ readonly file: File;
8
+ /** The upload path the commit puts; the server's canonical one once uploaded. */
9
+ path: string;
10
+ meta?: Record<string, unknown>;
11
+ status: ItemStatus;
12
+ progress?: Progress;
13
+ error?: ContentKitError;
14
+ result?: UploadedFile;
15
+ /** Resumable multipart state while uploading or after a failure. */
16
+ state?: UploadState;
17
+ /**
18
+ * Committed unattached for processing on upload (the host's
19
+ * ProcessOnUpload): commit() attaches it, remove() discards it.
20
+ */
21
+ unattached?: boolean;
22
+ /** The server's view of an unattached upload while it processes (editor read): dims, pending, failed, progress. */
23
+ processing?: FileInfo;
24
+ /** Processing finished (placed and outputs written, or failed). */
25
+ processed?: boolean;
26
+ }
27
+ export interface QueueSnapshot {
28
+ readonly items: readonly QueueItem[];
29
+ /** A rate, quota or permission refusal: no new uploads start until start(). */
30
+ readonly blocked?: ContentKitError;
31
+ /** Every item is uploaded (or committed) and none is in flight. */
32
+ readonly ready: boolean;
33
+ }
34
+ export interface QueueOptions {
35
+ ref: RefBody;
36
+ /** Each file's upload path: a template whose {name} is the file's name, e.g. "originals/{name}". */
37
+ path: string;
38
+ /** Files uploading at once. Default 2. */
39
+ concurrency?: number;
40
+ /** Start uploads as files are added. Default true. */
41
+ autoStart?: boolean;
42
+ /** Called with every item state change, to persist resumable state. */
43
+ onState?: (item: QueueItem, state: UploadState | null) => void;
44
+ /** Milliseconds between processing polls of unattached uploads. Default 2000. */
45
+ pollInterval?: number;
46
+ }
47
+ /**
48
+ * An ordered file queue for one item: uploads in the background, keeps the
49
+ * order the user arranges, and commits the uploads as puts appended in that
50
+ * order. Framework-free; the React hooks wrap it.
51
+ */
52
+ export declare class UploadQueue {
53
+ private readonly client;
54
+ private readonly o;
55
+ private items;
56
+ private blocked?;
57
+ private running;
58
+ private listeners;
59
+ private snap;
60
+ private started;
61
+ /** In-flight unattached commits, by item id. */
62
+ private staging;
63
+ private poll?;
64
+ private disposed;
65
+ private batch?;
66
+ constructor(client: MediaClient, o: QueueOptions);
67
+ subscribe: (fn: () => void) => (() => void);
68
+ getSnapshot: () => QueueSnapshot;
69
+ /** Queues files; state restores a multipart upload saved through onState before a reload. */
70
+ add(files: Iterable<File>, opts?: {
71
+ path?: (f: File) => string;
72
+ state?: (f: File) => UploadState | undefined;
73
+ }): QueueItem[];
74
+ /** Moves an item to index (before commit). */
75
+ move(id: string, index: number): void;
76
+ update(id: string, patch: Pick<Partial<QueueItem>, "path" | "meta">): void;
77
+ /**
78
+ * Removes an item, discarding its multipart upload if one is open, or its
79
+ * unattached upload (the commit removes it and its outputs).
80
+ */
81
+ remove(id: string): void;
82
+ /** Re-queues a failed item; a multipart upload resumes from its state. */
83
+ retry(id: string): void;
84
+ /** Starts uploading (autoStart false) or lifts a block after a refusal. */
85
+ start(): void;
86
+ /** Stops starting uploads and pauses running ones (they stay resumable). */
87
+ pause(): void;
88
+ /**
89
+ * Commits every uploaded item, in queue order, after the item's uploads:
90
+ * unattached ones are attached (no reprocessing), the rest put. With head,
91
+ * only the uploaded items before the first one still uploading or failed
92
+ * (a draft committing files as they finish). Returns the item's attached
93
+ * uploads in order.
94
+ */
95
+ commit(signal?: AbortSignal, o?: {
96
+ head?: boolean;
97
+ }): Promise<FileInfo[]>;
98
+ /** Aborts running uploads and stops polling; call on unmount. */
99
+ dispose(): void;
100
+ /** Commits an uploaded file unattached, so the host processes it now. */
101
+ private stage;
102
+ private schedulePoll;
103
+ /** Reads the processing state of unattached uploads still processing. */
104
+ private refresh;
105
+ private find;
106
+ private patch;
107
+ private set;
108
+ private changed;
109
+ private publish;
110
+ private pump;
111
+ private run;
112
+ }