@openrails/contentkit-ui 0.80.0 → 0.82.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 (295) hide show
  1. package/README.md +82 -54
  2. package/dist/client/beacons.d.ts +53 -0
  3. package/dist/client/beacons.js +72 -0
  4. package/dist/client/beacons.js.map +1 -0
  5. package/dist/client/catalog.d.ts +2 -2
  6. package/dist/client/catalog.js +1 -1
  7. package/dist/client/catalog.js.map +1 -1
  8. package/dist/client/client.d.ts +24 -11
  9. package/dist/client/client.js +37 -29
  10. package/dist/client/client.js.map +1 -1
  11. package/dist/client/content/comments.d.ts +12 -8
  12. package/dist/client/content/comments.js +33 -20
  13. package/dist/client/content/comments.js.map +1 -1
  14. package/dist/client/content/favorites.d.ts +24 -9
  15. package/dist/client/content/favorites.js +21 -22
  16. package/dist/client/content/favorites.js.map +1 -1
  17. package/dist/client/content/moderation.d.ts +1 -1
  18. package/dist/client/content/moderation.js.map +1 -1
  19. package/dist/client/content/reactions.d.ts +11 -6
  20. package/dist/client/content/reactions.js +23 -18
  21. package/dist/client/content/reactions.js.map +1 -1
  22. package/dist/client/content/types.d.ts +40 -21
  23. package/dist/client/content/types.js +5 -2
  24. package/dist/client/content/types.js.map +1 -1
  25. package/dist/client/contents.d.ts +55 -0
  26. package/dist/client/contents.js +144 -0
  27. package/dist/client/contents.js.map +1 -0
  28. package/dist/client/discovery.d.ts +126 -0
  29. package/dist/client/discovery.js +125 -0
  30. package/dist/client/discovery.js.map +1 -0
  31. package/dist/client/generated/error-codes.d.ts +1 -1
  32. package/dist/client/generated/error-codes.js +8 -0
  33. package/dist/client/generated/error-codes.js.map +1 -1
  34. package/dist/client/generated/routes.d.ts +1244 -370
  35. package/dist/client/generated/routes.js +1861 -554
  36. package/dist/client/generated/routes.js.map +1 -1
  37. package/dist/client/generated/wire.d.ts +960 -134
  38. package/dist/client/generated/wire.js.map +1 -1
  39. package/dist/client/group-fields.d.ts +25 -0
  40. package/dist/client/group-fields.js +63 -0
  41. package/dist/client/group-fields.js.map +1 -0
  42. package/dist/client/groups.d.ts +58 -0
  43. package/dist/client/groups.js +119 -0
  44. package/dist/client/groups.js.map +1 -0
  45. package/dist/client/http.d.ts +24 -12
  46. package/dist/client/http.js +20 -24
  47. package/dist/client/http.js.map +1 -1
  48. package/dist/client/index.d.ts +11 -6
  49. package/dist/client/media/api.d.ts +1 -1
  50. package/dist/client/media/api.js +4 -2
  51. package/dist/client/media/api.js.map +1 -1
  52. package/dist/client/media/client.d.ts +8 -9
  53. package/dist/client/media/client.js +58 -71
  54. package/dist/client/media/client.js.map +1 -1
  55. package/dist/client/media/ref.d.ts +30 -0
  56. package/dist/client/media/ref.js +10 -3
  57. package/dist/client/media/ref.js.map +1 -1
  58. package/dist/client/offers.d.ts +41 -0
  59. package/dist/client/offers.js +66 -0
  60. package/dist/client/offers.js.map +1 -0
  61. package/dist/client/route.d.ts +2 -0
  62. package/dist/client/route.js +2 -1
  63. package/dist/client/route.js.map +1 -1
  64. package/dist/client/versions.d.ts +20 -0
  65. package/dist/client/versions.js +53 -0
  66. package/dist/client/versions.js.map +1 -0
  67. package/dist/client.js +35 -30
  68. package/dist/components/admin/content-manager.d.ts +80 -0
  69. package/dist/components/admin/content-manager.js +591 -0
  70. package/dist/components/admin/content-manager.js.map +1 -0
  71. package/dist/components/admin/group-manager.d.ts +24 -0
  72. package/dist/components/admin/group-manager.js +244 -0
  73. package/dist/components/admin/group-manager.js.map +1 -0
  74. package/dist/components/admin/media-item-panel.js +1 -1
  75. package/dist/components/admin/media-item-panel.js.map +1 -1
  76. package/dist/components/content/card.d.ts +44 -0
  77. package/dist/components/content/card.js +285 -0
  78. package/dist/components/content/card.js.map +1 -0
  79. package/dist/components/content/editors.d.ts +40 -0
  80. package/dist/components/content/editors.js +764 -0
  81. package/dist/components/content/editors.js.map +1 -0
  82. package/dist/components/content/filters.d.ts +28 -0
  83. package/dist/components/content/filters.js +134 -0
  84. package/dist/components/content/filters.js.map +1 -0
  85. package/dist/components/content/grid.d.ts +62 -0
  86. package/dist/components/content/grid.js +233 -0
  87. package/dist/components/content/grid.js.map +1 -0
  88. package/dist/components/content/index.d.ts +8 -0
  89. package/dist/components/content/listing.d.ts +20 -0
  90. package/dist/components/content/listing.js +150 -0
  91. package/dist/components/content/listing.js.map +1 -0
  92. package/dist/components/content/offer-fields.d.ts +8 -0
  93. package/dist/components/content/offer-fields.js +127 -0
  94. package/dist/components/content/offer-fields.js.map +1 -0
  95. package/dist/components/content/paywall.d.ts +53 -0
  96. package/dist/components/content/paywall.js +221 -0
  97. package/dist/components/content/paywall.js.map +1 -0
  98. package/dist/components/content/search.d.ts +27 -0
  99. package/dist/components/content/search.js +164 -0
  100. package/dist/components/content/search.js.map +1 -0
  101. package/dist/components/groups/banner.d.ts +51 -0
  102. package/dist/components/groups/banner.js +194 -0
  103. package/dist/components/groups/banner.js.map +1 -0
  104. package/dist/components/groups/directory.d.ts +37 -0
  105. package/dist/components/groups/directory.js +117 -0
  106. package/dist/components/groups/directory.js.map +1 -0
  107. package/dist/components/groups/editor.d.ts +44 -0
  108. package/dist/components/groups/editor.js +860 -0
  109. package/dist/components/groups/editor.js.map +1 -0
  110. package/dist/components/groups/fields.d.ts +13 -0
  111. package/dist/components/groups/fields.js +68 -0
  112. package/dist/components/groups/fields.js.map +1 -0
  113. package/dist/components/groups/header.d.ts +25 -0
  114. package/dist/components/groups/header.js +131 -0
  115. package/dist/components/groups/header.js.map +1 -0
  116. package/dist/components/groups/index.d.ts +6 -0
  117. package/dist/components/groups/memberships.d.ts +55 -0
  118. package/dist/components/groups/memberships.js +527 -0
  119. package/dist/components/groups/memberships.js.map +1 -0
  120. package/dist/components/groups/parts.d.ts +49 -0
  121. package/dist/components/groups/parts.js +172 -0
  122. package/dist/components/groups/parts.js.map +1 -0
  123. package/dist/components/media-folder-editor.js +153 -152
  124. package/dist/components/media-folder-editor.js.map +1 -1
  125. package/dist/components/social/comments.d.ts +7 -5
  126. package/dist/components/social/comments.js +6 -2
  127. package/dist/components/social/comments.js.map +1 -1
  128. package/dist/components/social/engagement.d.ts +4 -3
  129. package/dist/components/social/engagement.js.map +1 -1
  130. package/dist/components/social/moderation.d.ts +1 -1
  131. package/dist/components/social/moderation.js +3 -3
  132. package/dist/components/social/moderation.js.map +1 -1
  133. package/dist/editor/rich-text-editor.d.ts +4 -4
  134. package/dist/editor/rich-text-editor.js.map +1 -1
  135. package/dist/index.d.ts +4 -2
  136. package/dist/index.js +23 -9
  137. package/dist/locales/admin/de.js +5 -66
  138. package/dist/locales/admin/de.js.map +1 -1
  139. package/dist/locales/admin/en.d.ts +5 -66
  140. package/dist/locales/admin/en.js +19 -80
  141. package/dist/locales/admin/en.js.map +1 -1
  142. package/dist/locales/admin/es.js +5 -66
  143. package/dist/locales/admin/es.js.map +1 -1
  144. package/dist/locales/admin/ja.js +5 -66
  145. package/dist/locales/admin/ja.js.map +1 -1
  146. package/dist/locales/admin/ko.js +5 -66
  147. package/dist/locales/admin/ko.js.map +1 -1
  148. package/dist/locales/admin/zh.js +5 -66
  149. package/dist/locales/admin/zh.js.map +1 -1
  150. package/dist/locales/content/de.d.ts +2 -0
  151. package/dist/locales/content/de.js +98 -0
  152. package/dist/locales/content/de.js.map +1 -0
  153. package/dist/locales/content/en.d.ts +93 -0
  154. package/dist/locales/content/en.js +98 -0
  155. package/dist/locales/content/en.js.map +1 -0
  156. package/dist/locales/content/es.d.ts +2 -0
  157. package/dist/locales/content/es.js +98 -0
  158. package/dist/locales/content/es.js.map +1 -0
  159. package/dist/locales/content/ja.d.ts +2 -0
  160. package/dist/locales/content/ja.js +98 -0
  161. package/dist/locales/content/ja.js.map +1 -0
  162. package/dist/locales/content/ko.d.ts +2 -0
  163. package/dist/locales/content/ko.js +98 -0
  164. package/dist/locales/content/ko.js.map +1 -0
  165. package/dist/locales/content/zh.d.ts +2 -0
  166. package/dist/locales/content/zh.js +98 -0
  167. package/dist/locales/content/zh.js.map +1 -0
  168. package/dist/locales/de.js +10 -2
  169. package/dist/locales/de.js.map +1 -1
  170. package/dist/locales/discovery/de.d.ts +2 -0
  171. package/dist/locales/discovery/de.js +68 -0
  172. package/dist/locales/discovery/de.js.map +1 -0
  173. package/dist/locales/discovery/en.d.ts +63 -0
  174. package/dist/locales/discovery/en.js +68 -0
  175. package/dist/locales/discovery/en.js.map +1 -0
  176. package/dist/locales/discovery/es.d.ts +2 -0
  177. package/dist/locales/discovery/es.js +68 -0
  178. package/dist/locales/discovery/es.js.map +1 -0
  179. package/dist/locales/discovery/ja.d.ts +2 -0
  180. package/dist/locales/discovery/ja.js +68 -0
  181. package/dist/locales/discovery/ja.js.map +1 -0
  182. package/dist/locales/discovery/ko.d.ts +2 -0
  183. package/dist/locales/discovery/ko.js +68 -0
  184. package/dist/locales/discovery/ko.js.map +1 -0
  185. package/dist/locales/discovery/zh.d.ts +2 -0
  186. package/dist/locales/discovery/zh.js +68 -0
  187. package/dist/locales/discovery/zh.js.map +1 -0
  188. package/dist/locales/en.d.ts +287 -67
  189. package/dist/locales/en.js +10 -2
  190. package/dist/locales/en.js.map +1 -1
  191. package/dist/locales/es.js +10 -2
  192. package/dist/locales/es.js.map +1 -1
  193. package/dist/locales/groups/de.d.ts +2 -0
  194. package/dist/locales/groups/de.js +133 -0
  195. package/dist/locales/groups/de.js.map +1 -0
  196. package/dist/locales/groups/en.d.ts +128 -0
  197. package/dist/locales/groups/en.js +133 -0
  198. package/dist/locales/groups/en.js.map +1 -0
  199. package/dist/locales/groups/es.d.ts +2 -0
  200. package/dist/locales/groups/es.js +133 -0
  201. package/dist/locales/groups/es.js.map +1 -0
  202. package/dist/locales/groups/ja.d.ts +2 -0
  203. package/dist/locales/groups/ja.js +133 -0
  204. package/dist/locales/groups/ja.js.map +1 -0
  205. package/dist/locales/groups/ko.d.ts +2 -0
  206. package/dist/locales/groups/ko.js +133 -0
  207. package/dist/locales/groups/ko.js.map +1 -0
  208. package/dist/locales/groups/zh.d.ts +2 -0
  209. package/dist/locales/groups/zh.js +133 -0
  210. package/dist/locales/groups/zh.js.map +1 -0
  211. package/dist/locales/ja.js +10 -2
  212. package/dist/locales/ja.js.map +1 -1
  213. package/dist/locales/ko.js +10 -2
  214. package/dist/locales/ko.js.map +1 -1
  215. package/dist/locales/social/de.js +2 -1
  216. package/dist/locales/social/de.js.map +1 -1
  217. package/dist/locales/social/en.d.ts +2 -1
  218. package/dist/locales/social/en.js +2 -1
  219. package/dist/locales/social/en.js.map +1 -1
  220. package/dist/locales/social/es.js +2 -1
  221. package/dist/locales/social/es.js.map +1 -1
  222. package/dist/locales/social/ja.js +2 -1
  223. package/dist/locales/social/ja.js.map +1 -1
  224. package/dist/locales/social/ko.js +2 -1
  225. package/dist/locales/social/ko.js.map +1 -1
  226. package/dist/locales/social/zh.js +2 -1
  227. package/dist/locales/social/zh.js.map +1 -1
  228. package/dist/locales/zh.js +10 -2
  229. package/dist/locales/zh.js.map +1 -1
  230. package/dist/react/admin.d.ts +1 -1
  231. package/dist/react/admin.js +8 -6
  232. package/dist/react/admin.js.map +1 -1
  233. package/dist/react/comments.d.ts +9 -7
  234. package/dist/react/comments.js +42 -27
  235. package/dist/react/comments.js.map +1 -1
  236. package/dist/react/contents.d.ts +57 -0
  237. package/dist/react/contents.js +214 -0
  238. package/dist/react/contents.js.map +1 -0
  239. package/dist/react/context.d.ts +1 -1
  240. package/dist/react/context.js.map +1 -1
  241. package/dist/react/crop.js +45 -44
  242. package/dist/react/crop.js.map +1 -1
  243. package/dist/react/detail.d.ts +30 -0
  244. package/dist/react/detail.js +42 -0
  245. package/dist/react/detail.js.map +1 -0
  246. package/dist/react/discovery.d.ts +138 -0
  247. package/dist/react/discovery.js +299 -0
  248. package/dist/react/discovery.js.map +1 -0
  249. package/dist/react/engagement.d.ts +5 -5
  250. package/dist/react/engagement.js +55 -41
  251. package/dist/react/engagement.js.map +1 -1
  252. package/dist/react/folder.js +54 -53
  253. package/dist/react/folder.js.map +1 -1
  254. package/dist/react/groups.d.ts +115 -0
  255. package/dist/react/groups.js +229 -0
  256. package/dist/react/groups.js.map +1 -0
  257. package/dist/react/index.d.ts +5 -1
  258. package/dist/react/public.d.ts +6 -5
  259. package/dist/react/public.js +19 -22
  260. package/dist/react/public.js.map +1 -1
  261. package/dist/react/read.js +105 -106
  262. package/dist/react/read.js.map +1 -1
  263. package/dist/react/resources.d.ts +2 -2
  264. package/dist/react/resources.js +69 -40
  265. package/dist/react/resources.js.map +1 -1
  266. package/dist/react/store.js +41 -39
  267. package/dist/react/store.js.map +1 -1
  268. package/dist/react/upload.js +54 -53
  269. package/dist/react/upload.js.map +1 -1
  270. package/dist/react/use-resource.d.ts +2 -1
  271. package/dist/react/use-resource.js +1 -0
  272. package/dist/react/use-resource.js.map +1 -1
  273. package/dist/react/versions.d.ts +57 -0
  274. package/dist/react/versions.js +94 -0
  275. package/dist/react/versions.js.map +1 -0
  276. package/dist/react/video.js +87 -86
  277. package/dist/react/video.js.map +1 -1
  278. package/dist/react.js +9 -5
  279. package/dist/styles.css +1 -1
  280. package/package.json +1 -1
  281. package/dist/client/content/posts.d.ts +0 -52
  282. package/dist/client/content/posts.js +0 -84
  283. package/dist/client/content/posts.js.map +0 -1
  284. package/dist/client/taxonomy.d.ts +0 -34
  285. package/dist/client/taxonomy.js +0 -84
  286. package/dist/client/taxonomy.js.map +0 -1
  287. package/dist/components/admin/post-manager.d.ts +0 -45
  288. package/dist/components/admin/post-manager.js +0 -602
  289. package/dist/components/admin/post-manager.js.map +0 -1
  290. package/dist/components/admin/taxonomy-manager.d.ts +0 -54
  291. package/dist/components/admin/taxonomy-manager.js +0 -976
  292. package/dist/components/admin/taxonomy-manager.js.map +0 -1
  293. package/dist/react/posts.d.ts +0 -61
  294. package/dist/react/posts.js +0 -183
  295. package/dist/react/posts.js.map +0 -1
@@ -1,17 +1,22 @@
1
1
  export { createContentKitClient, type ContentKitClient, type ContentKitClientOptions } from './client.js';
2
2
  export type { ContentKitChange, ContentKitModule, ContentKitMounts, Mount, TokenSource } from './http.js';
3
3
  export { CLIENT_ERROR_CODES, ContentKitError, ERROR_CODES, failureError, isContentKitError, readContentKitError, toContentKitError, type ContentKitErrorCode, type ContentKitErrorInit, } from './errors.js';
4
- export { IMAGE_SCHEME, imageRef, imageRefs, PostsClient, type PostAdminQuery, type PostQuery } from './content/posts.js';
4
+ export { BODY_SUFFIX, IMAGE_SCHEME, imageRef, imageRefs, ContentsClient, type ManageQuery } from './contents.js';
5
+ export { CatalogClient, type CatalogTotalsQuery } from './catalog.js';
5
6
  export { CommentsClient, type AdminCommentQuery, type CommentQuery } from './content/comments.js';
6
7
  export { ReactionsClient } from './content/reactions.js';
7
- export { FavoritesClient } from './content/favorites.js';
8
+ export { FavoritesClient, type MineQuery } from './content/favorites.js';
8
9
  export { PollsClient, type PollAdminQuery, type PollQuery, type PollState } from './content/polls.js';
9
10
  export { BansClient } from './content/bans.js';
10
11
  export { ModerationClient, type HeldQuery } from './content/moderation.js';
11
- export { reactionVerb, type BanScope, type ContentChange, type Decision, type HeldKind, type Reaction, type Sort } from './content/types.js';
12
- export { TaxonomyClient, type NodeAssignmentQuery, type NodeQuery } from './taxonomy.js';
12
+ export { reactionVerb, sameTarget, type BanScope, type ContentChange, type Decision, type HeldKind, type Reaction, type Sort, type Target } from './content/types.js';
13
+ export { GroupsClient, type GroupImageSlot, type GroupQuery, type MemberQuery, type MyGroupQuery } from './groups.js';
14
+ export { DiscoveryClient, type AccessMode, type CardQuery, type ContentFilterQuery, type ContentQuery, type HistoryQuery, type ListSort, type Period, type RailQuery, type ReaderQuery, type RecentSearchesQuery, type ReleaseDatesQuery, type SearchQuery, type TypeaheadQuery, } from './discovery.js';
15
+ export { VersionsClient, isPending } from './versions.js';
16
+ export { OffersClient } from './offers.js';
17
+ export { Beacons, eventId, readerSession, type Attribution, type BeaconOptions, type ViewSession } from './beacons.js';
18
+ export { LETTERS, attributeInput, fold, fromLocalInput, humanize, letterOf, parseAttribute, parseAttributes, slugify, toLocalInput } from './group-fields.js';
13
19
  export { CodesClient } from './codes.js';
14
- export { CatalogClient, type CatalogTotalsQuery } from './catalog.js';
15
20
  export { NotificationsClient, type NotificationItem } from './notifications.js';
16
21
  export { call, routeURL, type CallOptions, type Method, type PageQuery, type PathParams, type RouteQuery, type RoutePath } from './route.js';
17
22
  export { MediaApi, type ReadOptions } from './media/api.js';
@@ -20,7 +25,7 @@ export { ItemUploadQueue, type ItemStatus, type QueueItem, type QueueOptions, ty
20
25
  export { acceptOf, defaultImage, filesFor, isPattern, ratioLabel, ruleDir, ruleFor, screenFiles, uniqueName, uploadRules, withMediaType, type Screened, } from './media/rules.js';
21
26
  export { READ_CHUNK, chunkOffsets, isProcessing, mergeReads, processing } from './media/windows.js';
22
27
  export { defaultTransport, fetchTransport, xhrTransport, type Transport } from './media/transport.js';
23
- export { checkRef, isContentId } from './media/ref.js';
28
+ export { checkRef, isContentId, itemKey, sameItem } from './media/ref.js';
24
29
  export { encodeRemaining } from './media/encode.js';
25
30
  export { sha256Hex } from './media/hash.js';
26
31
  export { Pacer, type PacerOptions } from './media/pacer.js';
@@ -30,7 +30,7 @@ export declare class MediaApi {
30
30
  read(ref: RefBody, o?: ReadOptions, signal?: AbortSignal): Promise<ReadResult>;
31
31
  /** Every kind's public presets (the same for every viewer). */
32
32
  presets(signal?: AbortSignal): Promise<PresetRule[]>;
33
- /** The item under the read API: `{media}/{kind}/{id}`. */
33
+ /** The item under the read API: `{media}/{kind}/{id}`, a version's at `…/versions/{version}`. */
34
34
  itemURL(ref: RefBody): string;
35
35
  private post;
36
36
  }
@@ -31,7 +31,7 @@ var t = class {
31
31
  path: n,
32
32
  t: String(r)
33
33
  });
34
- return i && o.set("w", String(i)), this.http.request(`${this.http.mount("upload", t)}/frame?${o}`, {
34
+ return t.version && o.set("version", t.version), i && o.set("w", String(i)), this.http.request(`${this.http.mount("upload", t)}/frame?${o}`, {
35
35
  signal: a,
36
36
  blob: !0
37
37
  });
@@ -46,7 +46,9 @@ var t = class {
46
46
  return this.http.request(`${this.http.mount("media")}/presets`, { signal: e });
47
47
  }
48
48
  itemURL(t) {
49
- return e(t), `${this.http.mount("media", t)}/${encodeURIComponent(t.kind)}/${encodeURIComponent(t.id)}`;
49
+ e(t);
50
+ let n = `${this.http.mount("media", t)}/${encodeURIComponent(t.kind)}/${encodeURIComponent(t.id)}`;
51
+ return t.version ? `${n}/versions/${encodeURIComponent(t.version)}` : n;
50
52
  }
51
53
  async post(t, n, r, i) {
52
54
  return e(t), this.http.request(this.http.mount("upload", t) + n, {
@@ -1 +1 @@
1
- {"version":3,"file":"api.js","names":[],"sources":["../../../src/client/media/api.ts"],"sourcesContent":["import type { Http } from \"../http.js\";\nimport type {\n CommitBody,\n CommitReply,\n CompleteReply,\n PartsBody,\n PartsReply,\n PresetRule,\n PresignBody,\n PresignReply,\n ReadResult,\n RefBody,\n TicketBody,\n} from \"../generated/wire.js\";\nimport { checkRef } from \"./ref.js\";\n\n/** What a read returns (Reader.Read's ReadOptions). */\nexport interface ReadOptions {\n /** Only files under this path prefix, e.g. \"low-res/\". */\n prefix?: string;\n /** The range of files that get URLs. */\n offset?: number;\n limit?: number;\n /** Sign each file's download name into its URL. */\n download?: boolean;\n /** Editors also get the uploads: edit, frame, pending, failure and editor view. */\n editor?: boolean;\n}\n\n/**\n * The upload API (media.UploadHandler) and read API (Reader.Handler), one\n * method per route. Each takes the item, whose mount the call goes to.\n */\nexport class MediaApi {\n constructor(private readonly http: Http) {}\n\n presign(b: PresignBody, signal?: AbortSignal) {\n return this.post<PresignReply>(b.ref, \"/presign\", b, signal);\n }\n parts(ref: RefBody, b: PartsBody, signal?: AbortSignal) {\n return this.post<PartsReply>(ref, \"/parts\", b, signal);\n }\n listParts(ref: RefBody, b: TicketBody, signal?: AbortSignal) {\n return this.post<PartsReply>(ref, \"/parts/list\", b, signal);\n }\n complete(ref: RefBody, b: TicketBody, signal?: AbortSignal) {\n return this.post<CompleteReply>(ref, \"/complete\", b, signal);\n }\n abort(ref: RefBody, b: TicketBody, signal?: AbortSignal) {\n return this.post<void>(ref, \"/abort\", b, signal);\n }\n commit(b: CommitBody, signal?: AbortSignal) {\n return this.post<CommitReply>(b.ref, \"/commit\", b, signal);\n }\n /** A JPEG still of the video upload at path, t seconds in, w pixels wide (0: the frame's). */\n async frame(ref: RefBody, path: string, t: number, w?: number, signal?: AbortSignal) {\n checkRef(ref);\n const q = new URLSearchParams({ kind: ref.kind, id: ref.id, path, t: String(t) });\n if (w) q.set(\"w\", String(w));\n return this.http.request<Blob>(`${this.http.mount(\"upload\", ref)}/frame?${q}`, { signal, blob: true });\n }\n\n async read(ref: RefBody, o: ReadOptions = {}, signal?: AbortSignal) {\n const q = new URLSearchParams();\n if (o.prefix) q.set(\"prefix\", o.prefix);\n if (o.offset) q.set(\"offset\", String(o.offset));\n if (o.limit) q.set(\"limit\", String(o.limit));\n if (o.download) q.set(\"download\", \"1\");\n if (o.editor) q.set(\"editor\", \"1\");\n const s = q.toString();\n return this.http.request<ReadResult>(this.itemURL(ref) + (s ? \"?\" + s : \"\"), { signal });\n }\n\n /** Every kind's public presets (the same for every viewer). */\n presets(signal?: AbortSignal) {\n return this.http.request<PresetRule[]>(`${this.http.mount(\"media\")}/presets`, { signal });\n }\n\n /** The item under the read API: `{media}/{kind}/{id}`. */\n itemURL(ref: RefBody): string {\n checkRef(ref);\n return `${this.http.mount(\"media\", ref)}/${encodeURIComponent(ref.kind)}/${encodeURIComponent(ref.id)}`;\n }\n\n private async post<T>(ref: RefBody, path: string, body: object, signal?: AbortSignal) {\n checkRef(ref);\n return this.http.request<T>(this.http.mount(\"upload\", ref) + path, { body, signal });\n }\n}\n"],"mappings":";;AAiCA,IAAa,IAAb,MAAsB;CACS;CAA7B,YAAY,GAA6B;EAAZ,KAAA,OAAA;CAAa;CAE1C,QAAQ,GAAgB,GAAsB;EAC5C,OAAO,KAAK,KAAmB,EAAE,KAAK,YAAY,GAAG,CAAM;CAC7D;CACA,MAAM,GAAc,GAAc,GAAsB;EACtD,OAAO,KAAK,KAAiB,GAAK,UAAU,GAAG,CAAM;CACvD;CACA,UAAU,GAAc,GAAe,GAAsB;EAC3D,OAAO,KAAK,KAAiB,GAAK,eAAe,GAAG,CAAM;CAC5D;CACA,SAAS,GAAc,GAAe,GAAsB;EAC1D,OAAO,KAAK,KAAoB,GAAK,aAAa,GAAG,CAAM;CAC7D;CACA,MAAM,GAAc,GAAe,GAAsB;EACvD,OAAO,KAAK,KAAW,GAAK,UAAU,GAAG,CAAM;CACjD;CACA,OAAO,GAAe,GAAsB;EAC1C,OAAO,KAAK,KAAkB,EAAE,KAAK,WAAW,GAAG,CAAM;CAC3D;CAEA,MAAM,MAAM,GAAc,GAAc,GAAW,GAAY,GAAsB;EACnF,EAAS,CAAG;EACZ,IAAM,IAAI,IAAI,gBAAgB;GAAE,MAAM,EAAI;GAAM,IAAI,EAAI;GAAI;GAAM,GAAG,OAAO,CAAC;EAAE,CAAC;EAEhF,OADI,KAAG,EAAE,IAAI,KAAK,OAAO,CAAC,CAAC,GACpB,KAAK,KAAK,QAAc,GAAG,KAAK,KAAK,MAAM,UAAU,CAAG,EAAE,SAAS,KAAK;GAAE;GAAQ,MAAM;EAAK,CAAC;CACvG;CAEA,MAAM,KAAK,GAAc,IAAiB,CAAC,GAAG,GAAsB;EAClE,IAAM,IAAI,IAAI,gBAAgB;EAK9B,AAJI,EAAE,UAAQ,EAAE,IAAI,UAAU,EAAE,MAAM,GAClC,EAAE,UAAQ,EAAE,IAAI,UAAU,OAAO,EAAE,MAAM,CAAC,GAC1C,EAAE,SAAO,EAAE,IAAI,SAAS,OAAO,EAAE,KAAK,CAAC,GACvC,EAAE,YAAU,EAAE,IAAI,YAAY,GAAG,GACjC,EAAE,UAAQ,EAAE,IAAI,UAAU,GAAG;EACjC,IAAM,IAAI,EAAE,SAAS;EACrB,OAAO,KAAK,KAAK,QAAoB,KAAK,QAAQ,CAAG,KAAK,IAAI,MAAM,IAAI,KAAK,EAAE,UAAO,CAAC;CACzF;CAGA,QAAQ,GAAsB;EAC5B,OAAO,KAAK,KAAK,QAAsB,GAAG,KAAK,KAAK,MAAM,OAAO,EAAE,WAAW,EAAE,UAAO,CAAC;CAC1F;CAGA,QAAQ,GAAsB;EAE5B,OADA,EAAS,CAAG,GACL,GAAG,KAAK,KAAK,MAAM,SAAS,CAAG,EAAE,GAAG,mBAAmB,EAAI,IAAI,EAAE,GAAG,mBAAmB,EAAI,EAAE;CACtG;CAEA,MAAc,KAAQ,GAAc,GAAc,GAAc,GAAsB;EAEpF,OADA,EAAS,CAAG,GACL,KAAK,KAAK,QAAW,KAAK,KAAK,MAAM,UAAU,CAAG,IAAI,GAAM;GAAE;GAAM;EAAO,CAAC;CACrF;AACF"}
1
+ {"version":3,"file":"api.js","names":[],"sources":["../../../src/client/media/api.ts"],"sourcesContent":["import type { Http } from \"../http.js\";\nimport type {\n CommitBody,\n CommitReply,\n CompleteReply,\n PartsBody,\n PartsReply,\n PresetRule,\n PresignBody,\n PresignReply,\n ReadResult,\n RefBody,\n TicketBody,\n} from \"../generated/wire.js\";\nimport { checkRef } from \"./ref.js\";\n\n/** What a read returns (Reader.Read's ReadOptions). */\nexport interface ReadOptions {\n /** Only files under this path prefix, e.g. \"low-res/\". */\n prefix?: string;\n /** The range of files that get URLs. */\n offset?: number;\n limit?: number;\n /** Sign each file's download name into its URL. */\n download?: boolean;\n /** Editors also get the uploads: edit, frame, pending, failure and editor view. */\n editor?: boolean;\n}\n\n/**\n * The upload API (media.UploadHandler) and read API (Reader.Handler), one\n * method per route. Each takes the item, whose mount the call goes to.\n */\nexport class MediaApi {\n constructor(private readonly http: Http) {}\n\n presign(b: PresignBody, signal?: AbortSignal) {\n return this.post<PresignReply>(b.ref, \"/presign\", b, signal);\n }\n parts(ref: RefBody, b: PartsBody, signal?: AbortSignal) {\n return this.post<PartsReply>(ref, \"/parts\", b, signal);\n }\n listParts(ref: RefBody, b: TicketBody, signal?: AbortSignal) {\n return this.post<PartsReply>(ref, \"/parts/list\", b, signal);\n }\n complete(ref: RefBody, b: TicketBody, signal?: AbortSignal) {\n return this.post<CompleteReply>(ref, \"/complete\", b, signal);\n }\n abort(ref: RefBody, b: TicketBody, signal?: AbortSignal) {\n return this.post<void>(ref, \"/abort\", b, signal);\n }\n commit(b: CommitBody, signal?: AbortSignal) {\n return this.post<CommitReply>(b.ref, \"/commit\", b, signal);\n }\n /** A JPEG still of the video upload at path, t seconds in, w pixels wide (0: the frame's). */\n async frame(ref: RefBody, path: string, t: number, w?: number, signal?: AbortSignal) {\n checkRef(ref);\n const q = new URLSearchParams({ kind: ref.kind, id: ref.id, path, t: String(t) });\n if (ref.version) q.set(\"version\", ref.version);\n if (w) q.set(\"w\", String(w));\n return this.http.request<Blob>(`${this.http.mount(\"upload\", ref)}/frame?${q}`, { signal, blob: true });\n }\n\n async read(ref: RefBody, o: ReadOptions = {}, signal?: AbortSignal) {\n const q = new URLSearchParams();\n if (o.prefix) q.set(\"prefix\", o.prefix);\n if (o.offset) q.set(\"offset\", String(o.offset));\n if (o.limit) q.set(\"limit\", String(o.limit));\n if (o.download) q.set(\"download\", \"1\");\n if (o.editor) q.set(\"editor\", \"1\");\n const s = q.toString();\n return this.http.request<ReadResult>(this.itemURL(ref) + (s ? \"?\" + s : \"\"), { signal });\n }\n\n /** Every kind's public presets (the same for every viewer). */\n presets(signal?: AbortSignal) {\n return this.http.request<PresetRule[]>(`${this.http.mount(\"media\")}/presets`, { signal });\n }\n\n /** The item under the read API: `{media}/{kind}/{id}`, a version's at `…/versions/{version}`. */\n itemURL(ref: RefBody): string {\n checkRef(ref);\n const item = `${this.http.mount(\"media\", ref)}/${encodeURIComponent(ref.kind)}/${encodeURIComponent(ref.id)}`;\n return ref.version ? `${item}/versions/${encodeURIComponent(ref.version)}` : item;\n }\n\n private async post<T>(ref: RefBody, path: string, body: object, signal?: AbortSignal) {\n checkRef(ref);\n return this.http.request<T>(this.http.mount(\"upload\", ref) + path, { body, signal });\n }\n}\n"],"mappings":";;AAiCA,IAAa,IAAb,MAAsB;CACS;CAA7B,YAAY,GAA6B;EAAZ,KAAA,OAAA;CAAa;CAE1C,QAAQ,GAAgB,GAAsB;EAC5C,OAAO,KAAK,KAAmB,EAAE,KAAK,YAAY,GAAG,CAAM;CAC7D;CACA,MAAM,GAAc,GAAc,GAAsB;EACtD,OAAO,KAAK,KAAiB,GAAK,UAAU,GAAG,CAAM;CACvD;CACA,UAAU,GAAc,GAAe,GAAsB;EAC3D,OAAO,KAAK,KAAiB,GAAK,eAAe,GAAG,CAAM;CAC5D;CACA,SAAS,GAAc,GAAe,GAAsB;EAC1D,OAAO,KAAK,KAAoB,GAAK,aAAa,GAAG,CAAM;CAC7D;CACA,MAAM,GAAc,GAAe,GAAsB;EACvD,OAAO,KAAK,KAAW,GAAK,UAAU,GAAG,CAAM;CACjD;CACA,OAAO,GAAe,GAAsB;EAC1C,OAAO,KAAK,KAAkB,EAAE,KAAK,WAAW,GAAG,CAAM;CAC3D;CAEA,MAAM,MAAM,GAAc,GAAc,GAAW,GAAY,GAAsB;EACnF,EAAS,CAAG;EACZ,IAAM,IAAI,IAAI,gBAAgB;GAAE,MAAM,EAAI;GAAM,IAAI,EAAI;GAAI;GAAM,GAAG,OAAO,CAAC;EAAE,CAAC;EAGhF,OAFI,EAAI,WAAS,EAAE,IAAI,WAAW,EAAI,OAAO,GACzC,KAAG,EAAE,IAAI,KAAK,OAAO,CAAC,CAAC,GACpB,KAAK,KAAK,QAAc,GAAG,KAAK,KAAK,MAAM,UAAU,CAAG,EAAE,SAAS,KAAK;GAAE;GAAQ,MAAM;EAAK,CAAC;CACvG;CAEA,MAAM,KAAK,GAAc,IAAiB,CAAC,GAAG,GAAsB;EAClE,IAAM,IAAI,IAAI,gBAAgB;EAK9B,AAJI,EAAE,UAAQ,EAAE,IAAI,UAAU,EAAE,MAAM,GAClC,EAAE,UAAQ,EAAE,IAAI,UAAU,OAAO,EAAE,MAAM,CAAC,GAC1C,EAAE,SAAO,EAAE,IAAI,SAAS,OAAO,EAAE,KAAK,CAAC,GACvC,EAAE,YAAU,EAAE,IAAI,YAAY,GAAG,GACjC,EAAE,UAAQ,EAAE,IAAI,UAAU,GAAG;EACjC,IAAM,IAAI,EAAE,SAAS;EACrB,OAAO,KAAK,KAAK,QAAoB,KAAK,QAAQ,CAAG,KAAK,IAAI,MAAM,IAAI,KAAK,EAAE,UAAO,CAAC;CACzF;CAGA,QAAQ,GAAsB;EAC5B,OAAO,KAAK,KAAK,QAAsB,GAAG,KAAK,KAAK,MAAM,OAAO,EAAE,WAAW,EAAE,UAAO,CAAC;CAC1F;CAGA,QAAQ,GAAsB;EAC5B,EAAS,CAAG;EACZ,IAAM,IAAO,GAAG,KAAK,KAAK,MAAM,SAAS,CAAG,EAAE,GAAG,mBAAmB,EAAI,IAAI,EAAE,GAAG,mBAAmB,EAAI,EAAE;EAC1G,OAAO,EAAI,UAAU,GAAG,EAAK,YAAY,mBAAmB,EAAI,OAAO,MAAM;CAC/E;CAEA,MAAc,KAAQ,GAAc,GAAc,GAAc,GAAsB;EAEpF,OADA,EAAS,CAAG,GACL,KAAK,KAAK,QAAW,KAAK,KAAK,MAAM,UAAU,CAAG,IAAI,GAAM;GAAE;GAAM;EAAO,CAAC;CACrF;AACF"}
@@ -111,6 +111,8 @@ export interface NamedOptions {
111
111
  wait?: boolean;
112
112
  /** How long to wait, ms. */
113
113
  timeout?: number;
114
+ /** A crop or rotation the file is committed with. */
115
+ edit?: Edit;
114
116
  }
115
117
  /** An upload to a Named path, committed. */
116
118
  export interface NamedUpload {
@@ -119,10 +121,8 @@ export interface NamedUpload {
119
121
  path: string;
120
122
  file: FileInfo;
121
123
  }
122
- /** The media kinds of post and poll folders (content.Media.PostKind and PollKind). */
124
+ /** The media kind of poll folders (content.Media.PollKind). A content item's folder is its kind's. */
123
125
  export interface ContentFolders {
124
- /** Default "post". */
125
- post?: string;
126
126
  /** Default "poll". */
127
127
  poll?: string;
128
128
  }
@@ -227,13 +227,12 @@ export declare class MediaClient {
227
227
  */
228
228
  uploadNamed(ref: RefBody, file: Uploadable, o?: NamedOptions): Promise<NamedUpload>;
229
229
  /**
230
- * An image for a post's rich-text body, uploaded to the post's folder;
231
- * resolves with its reference to store in the body (`ref`) and a URL that
232
- * shows it to the editor now (`url`, null while none exists).
230
+ * An image for a content item's rich-text body, uploaded to the item's body
231
+ * folder ({kind}.body and its id, BODY_SUFFIX), which only viewers who open
232
+ * the item see; resolves with its reference to store in the body (`ref`) and
233
+ * a URL that shows it to the editor now (`url`, null while none exists).
233
234
  */
234
- uploadInline(post: string, file: Uploadable, o?: NamedOptions): Promise<NamedUpload & InlineImage>;
235
- /** The media folder of a post. */
236
- postRef(id: string): RefBody;
235
+ uploadInline(item: RefBody, file: Uploadable, o?: NamedOptions): Promise<NamedUpload & InlineImage>;
237
236
  /** The media folder of a poll. */
238
237
  pollRef(id: string): RefBody;
239
238
  /** Discards a paused multipart upload. */
@@ -1,14 +1,15 @@
1
1
  import { ContentKitError as e, aborted as t, failureError as n, throwIfAborted as r, toContentKitError as i } from "../errors.js";
2
2
  import { call as a } from "../route.js";
3
- import { MediaApi as o } from "./api.js";
4
- import { sha256Hex as s } from "./hash.js";
5
- import { Pacer as c } from "./pacer.js";
6
- import { defaultTransport as l } from "./transport.js";
3
+ import { itemRef as o } from "./ref.js";
4
+ import { MediaApi as s } from "./api.js";
5
+ import { sha256Hex as c } from "./hash.js";
6
+ import { Pacer as l } from "./pacer.js";
7
+ import { defaultTransport as u } from "./transport.js";
7
8
  //#region src/client/media/client.ts
8
- var u = /^i-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/, d = (e, t) => e === t || f(e) === t, f = (e) => {
9
+ var d = /^i-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/, f = (e, t) => e === t || p(e) === t, p = (e) => {
9
10
  let t = e.lastIndexOf(".");
10
11
  return t > e.lastIndexOf("/") + 1 ? e.slice(0, t) : e;
11
- }, p = class {
12
+ }, m = class {
12
13
  http;
13
14
  api;
14
15
  transport;
@@ -19,29 +20,26 @@ var u = /^i-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/, d =
19
20
  presetList;
20
21
  folders;
21
22
  constructor(e, t = {}, n = {}) {
22
- this.http = e, this.folders = {
23
- post: n.post ?? "post",
24
- poll: n.poll ?? "poll"
25
- }, this.api = new o(e), this.transport = t.transport ?? l, this.concurrency = t.concurrency ?? 4, this.retries = t.retries ?? 5, this.delay = t.retryDelay ?? h, this.targetSeconds = t.targetPartSeconds ?? 30;
23
+ this.http = e, this.folders = { poll: n.poll ?? "poll" }, this.api = new s(e), this.transport = t.transport ?? u, this.concurrency = t.concurrency ?? 4, this.retries = t.retries ?? 5, this.delay = t.retryDelay ?? g, this.targetSeconds = t.targetPartSeconds ?? 30;
26
24
  }
27
25
  async upload(t, n) {
28
26
  let i = n.type || t.type;
29
27
  if (!i) throw new e("invalid_request", "the file has no content type");
30
28
  if (r(n.signal), n.resume) return this.resumeMultipart(t, n.resume, n);
31
- let a = t.size, o = await s(t, {
29
+ let a = t.size, o = await c(t, {
32
30
  signal: n.signal,
33
31
  onProgress: (e) => n.onProgress?.({
34
32
  phase: "hashing",
35
33
  loaded: e,
36
34
  total: a
37
35
  })
38
- }), c = () => this.api.presign({
36
+ }), s = () => this.api.presign({
39
37
  ref: n.ref,
40
38
  path: n.path,
41
39
  type: i,
42
40
  size: a,
43
41
  sha256: o
44
- }, n.signal), l = await c(), u = (e) => ({
42
+ }, n.signal), l = await s(), u = (e) => ({
45
43
  path: e.path,
46
44
  blob: e.blob,
47
45
  type: i,
@@ -58,7 +56,7 @@ var u = /^i-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/, d =
58
56
  type: i,
59
57
  size: a,
60
58
  ref: n.ref,
61
- file: m(t),
59
+ file: h(t),
62
60
  limits: {
63
61
  minPartSize: e.min_part_size,
64
62
  maxPartSize: e.max_part_size,
@@ -79,7 +77,7 @@ var u = /^i-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/, d =
79
77
  })
80
78
  });
81
79
  }, n.signal, async (e) => {
82
- e.code === "storage" && e.status === 403 && (l = await c());
80
+ e.code === "storage" && e.status === 403 && (l = await s());
83
81
  }), u(l);
84
82
  }
85
83
  async put(e, t) {
@@ -117,15 +115,15 @@ var u = /^i-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/, d =
117
115
  operation_id: r.operationID ?? crypto.randomUUID()
118
116
  });
119
117
  r.onState?.(structuredClone(a));
120
- let o;
118
+ let s;
121
119
  try {
122
- o = (await this.retry(() => this.api.commit(a, r.signal), r.signal)).files;
120
+ s = (await this.retry(() => this.api.commit(a, r.signal), r.signal)).files;
123
121
  } catch (t) {
124
122
  if (!(t instanceof e) || t.code !== "not_uploaded") throw t;
125
- let n = r.sources ?? {}, i = a.ops.filter((e) => e.op === "put" && e.blob && e.blob in n), s = [...new Set(i.map((e) => e.blob))].filter((e) => !t.blobs || t.blobs.includes(e));
126
- if (s.length === 0) throw t;
123
+ let n = r.sources ?? {}, i = a.ops.filter((e) => e.op === "put" && e.blob && e.blob in n), o = [...new Set(i.map((e) => e.blob))].filter((e) => !t.blobs || t.blobs.includes(e));
124
+ if (o.length === 0) throw t;
127
125
  let c = /* @__PURE__ */ new Map();
128
- for (let e of s) {
126
+ for (let e of o) {
129
127
  let t = n[e], o = t instanceof Blob ? t : t.file, s = t instanceof Blob ? void 0 : t.type, l = i.find((t) => t.blob === e);
130
128
  c.set(e, (await this.upload(o, {
131
129
  ref: a.ref,
@@ -142,41 +140,35 @@ var u = /^i-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/, d =
142
140
  ops: l,
143
141
  operation_id: crypto.randomUUID()
144
142
  };
145
- r.onState?.(structuredClone(u)), o = (await this.retry(() => this.api.commit(u, r.signal), r.signal)).files;
143
+ r.onState?.(structuredClone(u)), s = (await this.retry(() => this.api.commit(u, r.signal), r.signal)).files;
146
144
  }
147
145
  return i({
148
146
  type: "media.committed",
149
- ref: {
150
- kind: t.kind,
151
- id: t.id
152
- },
153
- files: o
154
- }), o;
147
+ ref: o(t),
148
+ files: s
149
+ }), s;
155
150
  }
156
151
  async read(e, t = {}) {
157
152
  return this.api.itemURL(e), this.retry(() => this.api.read(e, t, t.signal), t.signal);
158
153
  }
159
154
  async waitFor(t, r, i = {}) {
160
- let a = this.http.captureChanges(), o = Date.now() + (i.timeout ?? 12e4);
155
+ let a = this.http.captureChanges(), s = Date.now() + (i.timeout ?? 12e4);
161
156
  for (;;) {
162
- let s = await this.read(t, {
157
+ let c = await this.read(t, {
163
158
  editor: !0,
164
- prefix: f(r),
159
+ prefix: p(r),
165
160
  signal: i.signal
166
- }), c = s.files.find((e) => e.upload && d(e.path, r));
167
- if (!c) throw new e("not_found", `no upload ${r}`, { status: 404 });
168
- if (c.failed) throw n(c.failed);
169
- if (!c.pending?.length && !c.staged && (c.size ?? 0) > 0) return a({
161
+ }), l = c.files.find((e) => e.upload && f(e.path, r));
162
+ if (!l) throw new e("not_found", `no upload ${r}`, { status: 404 });
163
+ if (l.failed) throw n(l.failed);
164
+ if (!l.pending?.length && !l.staged && (l.size ?? 0) > 0) return a({
170
165
  type: "media.processed",
171
- ref: {
172
- kind: t.kind,
173
- id: t.id
174
- },
175
- file: c
176
- }), c;
177
- if (s.full) throw new e("too_large", `${r} waits: the item is full; remove uploads to process more`, { status: 413 });
178
- if (Date.now() >= o) throw new e("render_timeout", `${r} is still processing`);
179
- await g(i.interval ?? 1e3, i.signal);
166
+ ref: o(t),
167
+ file: l
168
+ }), l;
169
+ if (c.full) throw new e("too_large", `${r} waits: the item is full; remove uploads to process more`, { status: 413 });
170
+ if (Date.now() >= s) throw new e("render_timeout", `${r} is still processing`);
171
+ await _(i.interval ?? 1e3, i.signal);
180
172
  }
181
173
  }
182
174
  async editorView(t, r, i = {}) {
@@ -184,9 +176,9 @@ var u = /^i-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/, d =
184
176
  for (;;) {
185
177
  let o = (await this.read(t, {
186
178
  editor: !0,
187
- prefix: f(r),
179
+ prefix: p(r),
188
180
  signal: i.signal
189
- })).files.find((e) => e.upload && d(e.path, r));
181
+ })).files.find((e) => e.upload && f(e.path, r));
190
182
  if (!o) throw new e("not_found", `no upload ${r}`, { status: 404 });
191
183
  if (o.editor_url && o.w && o.h) return {
192
184
  url: o.editor_url,
@@ -196,7 +188,7 @@ var u = /^i-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/, d =
196
188
  };
197
189
  if (o.failed) throw n(o.failed);
198
190
  if (Date.now() >= a) throw new e("render_timeout", `the editor view of ${r} is still rendering`);
199
- await g(i.interval ?? 1e3, i.signal);
191
+ await _(i.interval ?? 1e3, i.signal);
200
192
  }
201
193
  }
202
194
  presets() {
@@ -220,9 +212,10 @@ var u = /^i-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/, d =
220
212
  signal: r.signal,
221
213
  onProgress: r.onProgress,
222
214
  wait: r.wait,
223
- timeout: r.timeout
224
- }), a = f(i.path.slice(i.path.lastIndexOf("/") + 1));
225
- if (!u.test(a)) throw new e("invalid_request", `${i.path} is not a server-named upload path of ${t.kind}`);
215
+ timeout: r.timeout,
216
+ edit: r.edit
217
+ }), a = p(i.path.slice(i.path.lastIndexOf("/") + 1));
218
+ if (!d.test(a)) throw new e("invalid_request", `${i.path} is not a server-named upload path of ${t.kind}`);
226
219
  return {
227
220
  name: a,
228
221
  path: i.path,
@@ -230,8 +223,8 @@ var u = /^i-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/, d =
230
223
  };
231
224
  }
232
225
  async uploadInline(e, t, n = {}) {
233
- let r = await this.uploadNamed(this.postRef(e), t, n), i = await a(this.http, "POST", "/posts/{id}/images", {
234
- params: { id: e },
226
+ let r = await this.uploadNamed(e, t, n), i = await a(this.http, "POST", "/contents/{id}/images", {
227
+ params: { id: e.id },
235
228
  body: { image: r.name },
236
229
  signal: n.signal
237
230
  });
@@ -240,12 +233,6 @@ var u = /^i-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/, d =
240
233
  ...i
241
234
  };
242
235
  }
243
- postRef(e) {
244
- return {
245
- kind: this.folders.post,
246
- id: e
247
- };
248
- }
249
236
  pollRef(e) {
250
237
  return {
251
238
  kind: this.folders.poll,
@@ -256,7 +243,7 @@ var u = /^i-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/, d =
256
243
  await this.api.abort(e.ref, { ticket: e.ticket });
257
244
  }
258
245
  async resumeMultipart(t, n, r) {
259
- let i = m(t);
246
+ let i = h(t);
260
247
  if (i.size !== n.size || i.name !== n.file.name || i.lastModified !== n.file.lastModified) throw new e("resume_mismatch", "the saved upload is for a different file");
261
248
  let a;
262
249
  try {
@@ -277,13 +264,13 @@ var u = /^i-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/, d =
277
264
  return this.multipart(t, structuredClone(n), o, r);
278
265
  }
279
266
  async multipart(e, n, r, i) {
280
- let a = new c({
267
+ let a = new l({
281
268
  ...n.limits,
282
269
  maxConcurrency: this.concurrency,
283
270
  targetSeconds: this.targetSeconds
284
- }), o = () => i.onState?.(structuredClone(n)), l = /* @__PURE__ */ new Map(), u = n.parts.filter((e) => r.has(e.number)).reduce((e, t) => e + t.size, 0), d = () => {
271
+ }), o = () => i.onState?.(structuredClone(n)), s = /* @__PURE__ */ new Map(), u = n.parts.filter((e) => r.has(e.number)).reduce((e, t) => e + t.size, 0), d = () => {
285
272
  let e = u;
286
- for (let t of l.values()) e += t;
273
+ for (let t of s.values()) e += t;
287
274
  i.onProgress?.({
288
275
  phase: "uploading",
289
276
  loaded: e,
@@ -312,7 +299,7 @@ var u = /^i-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/, d =
312
299
  }, x = async (t) => {
313
300
  let r = e.slice(t.offset, t.offset + t.size);
314
301
  await this.retry(async () => {
315
- t.sha256 || (t.sha256 = await s(r, { signal: g }), o());
302
+ t.sha256 || (t.sha256 = await c(r, { signal: g }), o());
316
303
  let { parts: e } = await this.api.parts(n.ref, {
317
304
  ticket: n.ticket,
318
305
  parts: [{
@@ -322,18 +309,18 @@ var u = /^i-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/, d =
322
309
  }]
323
310
  }, g), i = e[0]?.request;
324
311
  await y();
325
- let c = performance.now(), f = _;
312
+ let l = performance.now(), f = _;
326
313
  try {
327
314
  await this.transport(i, r, {
328
315
  signal: g,
329
316
  onProgress: (e) => {
330
- l.set(t.number, e), d();
317
+ s.set(t.number, e), d();
331
318
  }
332
319
  });
333
320
  } finally {
334
- l.delete(t.number), b();
321
+ s.delete(t.number), b();
335
322
  }
336
- a.record(t.size, performance.now() - c, f), u += t.size, d();
323
+ a.record(t.size, performance.now() - l, f), u += t.size, d();
337
324
  }, g);
338
325
  };
339
326
  o(), d();
@@ -376,21 +363,21 @@ var u = /^i-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/, d =
376
363
  let o = i(e);
377
364
  if (n?.aborted) throw t(n);
378
365
  if (!o.transient || a >= this.retries) throw o;
379
- await g(this.delay(a, o.retryAfter), n), await r?.(o);
366
+ await _(this.delay(a, o.retryAfter), n), await r?.(o);
380
367
  }
381
368
  }
382
369
  };
383
- function m(e) {
370
+ function h(e) {
384
371
  return {
385
372
  name: e.name,
386
373
  lastModified: e.lastModified,
387
374
  size: e.size
388
375
  };
389
376
  }
390
- function h(e, t) {
377
+ function g(e, t) {
391
378
  return t ? t * 1e3 : Math.min(3e4, 1e3 * 2 ** e) * (.75 + Math.random() * .5);
392
379
  }
393
- function g(e, n) {
380
+ function _(e, n) {
394
381
  return new Promise((r, i) => {
395
382
  if (n?.aborted) return i(t(n));
396
383
  let a = setTimeout(() => {
@@ -402,6 +389,6 @@ function g(e, n) {
402
389
  });
403
390
  }
404
391
  //#endregion
405
- export { p as MediaClient, h as backoff, d as samePath, g as sleep, f as stem };
392
+ export { m as MediaClient, g as backoff, f as samePath, _ as sleep, p as stem };
406
393
 
407
394
  //# sourceMappingURL=client.js.map
@@ -1 +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 const emit = this.http.captureChanges();\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 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 emit = this.http.captureChanges();\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 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 /**\n * An image for a post's rich-text body, uploaded to the post's folder;\n * resolves with its reference to store in the body (`ref`) and a URL that\n * shows it to the editor now (`url`, null while none exists).\n */\n async uploadInline(post: string, file: Uploadable, o: NamedOptions = {}): Promise<NamedUpload & InlineImage> {\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, ...r };\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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"}
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 { itemRef } from \"./ref.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 /** A crop or rotation the file is committed with. */\n edit?: Edit;\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 kind of poll folders (content.Media.PollKind). A content item's folder is its kind's. */\nexport interface ContentFolders {\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 = { 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 const emit = this.http.captureChanges();\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 emit({ type: \"media.committed\", ref: itemRef(ref), 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 emit = this.http.captureChanges();\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 emit({ type: \"media.processed\", ref: itemRef(ref), 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, edit: o.edit });\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 /**\n * An image for a content item's rich-text body, uploaded to the item's body\n * folder ({kind}.body and its id, BODY_SUFFIX), which only viewers who open\n * the item see; resolves with its reference to store in the body (`ref`) and\n * a URL that shows it to the editor now (`url`, null while none exists).\n */\n async uploadInline(item: RefBody, file: Uploadable, o: NamedOptions = {}): Promise<NamedUpload & InlineImage> {\n const up = await this.uploadNamed(item, file, o);\n const r: InlineImage = await call(this.http, \"POST\", \"/contents/{id}/images\", { params: { id: item.id }, body: { image: up.name }, signal: o.signal });\n return { ...up, ...r };\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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"}