@openrails/contentkit-ui 0.83.0 → 0.86.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 (219) hide show
  1. package/README.md +18 -7
  2. package/dist/client/beacons.d.ts +18 -6
  3. package/dist/client/beacons.js +26 -11
  4. package/dist/client/beacons.js.map +1 -1
  5. package/dist/client/client.d.ts +3 -0
  6. package/dist/client/client.js +29 -27
  7. package/dist/client/client.js.map +1 -1
  8. package/dist/client/codes.d.ts +6 -1
  9. package/dist/client/codes.js +4 -1
  10. package/dist/client/codes.js.map +1 -1
  11. package/dist/client/content/moderation.d.ts +14 -2
  12. package/dist/client/content/moderation.js +34 -0
  13. package/dist/client/content/moderation.js.map +1 -1
  14. package/dist/client/content/types.d.ts +13 -0
  15. package/dist/client/content/types.js.map +1 -1
  16. package/dist/client/discovery.d.ts +6 -0
  17. package/dist/client/discovery.js.map +1 -1
  18. package/dist/client/generated/error-codes.d.ts +1 -1
  19. package/dist/client/generated/error-codes.js +9 -1
  20. package/dist/client/generated/error-codes.js.map +1 -1
  21. package/dist/client/generated/routes.d.ts +91 -13
  22. package/dist/client/generated/routes.js +123 -2
  23. package/dist/client/generated/routes.js.map +1 -1
  24. package/dist/client/generated/wire.d.ts +81 -0
  25. package/dist/client/generated/wire.js.map +1 -1
  26. package/dist/client/index.d.ts +4 -2
  27. package/dist/client/media/queue.d.ts +4 -2
  28. package/dist/client/media/queue.js +18 -12
  29. package/dist/client/media/queue.js.map +1 -1
  30. package/dist/client/media/rules.d.ts +7 -1
  31. package/dist/client/media/rules.js +27 -21
  32. package/dist/client/media/rules.js.map +1 -1
  33. package/dist/client/reader-meta.d.ts +27 -0
  34. package/dist/client/reader-meta.js +99 -0
  35. package/dist/client/reader-meta.js.map +1 -0
  36. package/dist/client/stats.d.ts +32 -0
  37. package/dist/client/stats.js +35 -0
  38. package/dist/client/stats.js.map +1 -0
  39. package/dist/client/versions.d.ts +1 -1
  40. package/dist/client/versions.js +7 -2
  41. package/dist/client/versions.js.map +1 -1
  42. package/dist/client.js +30 -28
  43. package/dist/components/admin/content-manager.js +206 -206
  44. package/dist/components/admin/content-manager.js.map +1 -1
  45. package/dist/components/admin/group-manager.js +71 -70
  46. package/dist/components/admin/group-manager.js.map +1 -1
  47. package/dist/components/admin/media-item-panel.d.ts +24 -7
  48. package/dist/components/admin/media-item-panel.js +209 -197
  49. package/dist/components/admin/media-item-panel.js.map +1 -1
  50. package/dist/components/admin/poll-manager.js +25 -25
  51. package/dist/components/admin/reader-manifest.d.ts +18 -0
  52. package/dist/components/admin/reader-manifest.js +490 -0
  53. package/dist/components/admin/reader-manifest.js.map +1 -0
  54. package/dist/components/admin/stats-dashboard.d.ts +22 -0
  55. package/dist/components/admin/stats-dashboard.js +257 -0
  56. package/dist/components/admin/stats-dashboard.js.map +1 -0
  57. package/dist/components/content/card.js +43 -43
  58. package/dist/components/content/card.js.map +1 -1
  59. package/dist/components/content/editors.d.ts +2 -1
  60. package/dist/components/content/editors.js +479 -416
  61. package/dist/components/content/editors.js.map +1 -1
  62. package/dist/components/content/grid.js +47 -47
  63. package/dist/components/content/index.d.ts +2 -1
  64. package/dist/components/content/listing.js +34 -34
  65. package/dist/components/content/listing.js.map +1 -1
  66. package/dist/components/content/paywall.d.ts +17 -5
  67. package/dist/components/content/paywall.js +115 -100
  68. package/dist/components/content/paywall.js.map +1 -1
  69. package/dist/components/content/reader-stats.d.ts +12 -0
  70. package/dist/components/content/reader-stats.js +47 -0
  71. package/dist/components/content/reader-stats.js.map +1 -0
  72. package/dist/components/groups/banner.d.ts +1 -1
  73. package/dist/components/groups/banner.js +34 -34
  74. package/dist/components/groups/banner.js.map +1 -1
  75. package/dist/components/groups/directory.js +11 -11
  76. package/dist/components/groups/editor.js +294 -294
  77. package/dist/components/groups/editor.js.map +1 -1
  78. package/dist/components/groups/header.js +27 -27
  79. package/dist/components/groups/header.js.map +1 -1
  80. package/dist/components/groups/memberships.js +180 -180
  81. package/dist/components/groups/parts.d.ts +2 -4
  82. package/dist/components/groups/parts.js +69 -88
  83. package/dist/components/groups/parts.js.map +1 -1
  84. package/dist/components/media-folder-editor.js +239 -189
  85. package/dist/components/media-folder-editor.js.map +1 -1
  86. package/dist/components/notifications.js +17 -17
  87. package/dist/components/slot-upload.d.ts +2 -2
  88. package/dist/components/slot-upload.js +84 -79
  89. package/dist/components/slot-upload.js.map +1 -1
  90. package/dist/components/social/bans.js +34 -34
  91. package/dist/components/social/comments.js +59 -45
  92. package/dist/components/social/comments.js.map +1 -1
  93. package/dist/components/social/index.d.ts +1 -0
  94. package/dist/components/social/moderation.d.ts +5 -4
  95. package/dist/components/social/moderation.js +177 -160
  96. package/dist/components/social/moderation.js.map +1 -1
  97. package/dist/components/social/parts.js +22 -14
  98. package/dist/components/social/parts.js.map +1 -1
  99. package/dist/components/social/rules.d.ts +23 -0
  100. package/dist/components/social/rules.js +261 -0
  101. package/dist/components/social/rules.js.map +1 -0
  102. package/dist/components/video-player.d.ts +2 -0
  103. package/dist/components/video-player.js +318 -314
  104. package/dist/components/video-player.js.map +1 -1
  105. package/dist/i18n/context.d.ts +2 -0
  106. package/dist/i18n/context.js +23 -5
  107. package/dist/i18n/context.js.map +1 -1
  108. package/dist/index.d.ts +3 -1
  109. package/dist/index.js +28 -24
  110. package/dist/locales/admin/de.js +105 -0
  111. package/dist/locales/admin/de.js.map +1 -1
  112. package/dist/locales/admin/en.d.ts +105 -0
  113. package/dist/locales/admin/en.js +105 -0
  114. package/dist/locales/admin/en.js.map +1 -1
  115. package/dist/locales/admin/es.js +105 -0
  116. package/dist/locales/admin/es.js.map +1 -1
  117. package/dist/locales/admin/ja.js +105 -0
  118. package/dist/locales/admin/ja.js.map +1 -1
  119. package/dist/locales/admin/ko.js +105 -0
  120. package/dist/locales/admin/ko.js.map +1 -1
  121. package/dist/locales/admin/zh.js +105 -0
  122. package/dist/locales/admin/zh.js.map +1 -1
  123. package/dist/locales/content/de.js +6 -0
  124. package/dist/locales/content/de.js.map +1 -1
  125. package/dist/locales/content/en.d.ts +6 -0
  126. package/dist/locales/content/en.js +6 -0
  127. package/dist/locales/content/en.js.map +1 -1
  128. package/dist/locales/content/es.js +6 -0
  129. package/dist/locales/content/es.js.map +1 -1
  130. package/dist/locales/content/ja.js +6 -0
  131. package/dist/locales/content/ja.js.map +1 -1
  132. package/dist/locales/content/ko.js +6 -0
  133. package/dist/locales/content/ko.js.map +1 -1
  134. package/dist/locales/content/zh.js +6 -0
  135. package/dist/locales/content/zh.js.map +1 -1
  136. package/dist/locales/de.js +4 -1
  137. package/dist/locales/de.js.map +1 -1
  138. package/dist/locales/discovery/de.js +1 -0
  139. package/dist/locales/discovery/de.js.map +1 -1
  140. package/dist/locales/discovery/en.d.ts +1 -0
  141. package/dist/locales/discovery/en.js +1 -0
  142. package/dist/locales/discovery/en.js.map +1 -1
  143. package/dist/locales/discovery/es.js +1 -0
  144. package/dist/locales/discovery/es.js.map +1 -1
  145. package/dist/locales/discovery/ja.js +1 -0
  146. package/dist/locales/discovery/ja.js.map +1 -1
  147. package/dist/locales/discovery/ko.js +1 -0
  148. package/dist/locales/discovery/ko.js.map +1 -1
  149. package/dist/locales/discovery/zh.js +1 -0
  150. package/dist/locales/discovery/zh.js.map +1 -1
  151. package/dist/locales/en.d.ts +118 -0
  152. package/dist/locales/en.js +4 -1
  153. package/dist/locales/en.js.map +1 -1
  154. package/dist/locales/es.js +4 -1
  155. package/dist/locales/es.js.map +1 -1
  156. package/dist/locales/ja.js +4 -1
  157. package/dist/locales/ja.js.map +1 -1
  158. package/dist/locales/ko.js +4 -1
  159. package/dist/locales/ko.js.map +1 -1
  160. package/dist/locales/social/de.js +3 -0
  161. package/dist/locales/social/de.js.map +1 -1
  162. package/dist/locales/social/en.d.ts +3 -0
  163. package/dist/locales/social/en.js +3 -0
  164. package/dist/locales/social/en.js.map +1 -1
  165. package/dist/locales/social/es.js +3 -0
  166. package/dist/locales/social/es.js.map +1 -1
  167. package/dist/locales/social/ja.js +3 -0
  168. package/dist/locales/social/ja.js.map +1 -1
  169. package/dist/locales/social/ko.js +3 -0
  170. package/dist/locales/social/ko.js.map +1 -1
  171. package/dist/locales/social/zh.js +3 -0
  172. package/dist/locales/social/zh.js.map +1 -1
  173. package/dist/locales/zh.js +4 -1
  174. package/dist/locales/zh.js.map +1 -1
  175. package/dist/react/admin.d.ts +1 -1
  176. package/dist/react/admin.js +24 -23
  177. package/dist/react/admin.js.map +1 -1
  178. package/dist/react/canonical.d.ts +4 -3
  179. package/dist/react/canonical.js +34 -33
  180. package/dist/react/canonical.js.map +1 -1
  181. package/dist/react/comments.d.ts +29 -1
  182. package/dist/react/comments.js +30 -2
  183. package/dist/react/comments.js.map +1 -1
  184. package/dist/react/contents.js +144 -144
  185. package/dist/react/contents.js.map +1 -1
  186. package/dist/react/context.d.ts +1 -1
  187. package/dist/react/context.js.map +1 -1
  188. package/dist/react/detail.d.ts +12 -6
  189. package/dist/react/detail.js +31 -24
  190. package/dist/react/detail.js.map +1 -1
  191. package/dist/react/discovery.js +1 -1
  192. package/dist/react/discovery.js.map +1 -1
  193. package/dist/react/folder.d.ts +7 -2
  194. package/dist/react/folder.js +165 -147
  195. package/dist/react/folder.js.map +1 -1
  196. package/dist/react/groups.d.ts +0 -1
  197. package/dist/react/groups.js +13 -12
  198. package/dist/react/groups.js.map +1 -1
  199. package/dist/react/index.d.ts +5 -3
  200. package/dist/react/location.d.ts +11 -0
  201. package/dist/react/location.js +18 -0
  202. package/dist/react/location.js.map +1 -0
  203. package/dist/react/public.d.ts +8 -0
  204. package/dist/react/public.js +32 -27
  205. package/dist/react/public.js.map +1 -1
  206. package/dist/react/resources.js +6 -0
  207. package/dist/react/resources.js.map +1 -1
  208. package/dist/react/stats.d.ts +24 -0
  209. package/dist/react/stats.js +28 -0
  210. package/dist/react/stats.js.map +1 -0
  211. package/dist/react/use-resource.js +8 -7
  212. package/dist/react/use-resource.js.map +1 -1
  213. package/dist/react/versions.js +20 -20
  214. package/dist/react.js +26 -24
  215. package/dist/styles.css +1 -1
  216. package/dist/urls/index.d.ts +35 -10
  217. package/dist/urls.js +100 -70
  218. package/dist/urls.js.map +1 -1
  219. package/package.json +1 -1
@@ -13,6 +13,15 @@ export type ContentState = "draft" | "published" | "deleted";
13
13
  export type VersionState = "draft" | "published";
14
14
  export type Listing = "listed" | "members" | "unlisted";
15
15
  export type AliasStatus = "found" | "not_found" | "gone";
16
+ export interface Activity {
17
+ days: ActivityDay[];
18
+ }
19
+ export interface ActivityDay {
20
+ date: string;
21
+ views: number;
22
+ viewers: number;
23
+ users: number;
24
+ }
16
25
  export interface AdminComment {
17
26
  id: string;
18
27
  reply_to_id?: string;
@@ -138,6 +147,7 @@ export interface Batch {
138
147
  votes?: VoteRecord[];
139
148
  answers?: AnswerRecord[];
140
149
  signals?: SignalRecord[];
150
+ deletes?: DeleteRecord[];
141
151
  }
142
152
  export interface Card {
143
153
  id: string;
@@ -167,6 +177,7 @@ export interface CardAccess {
167
177
  }
168
178
  export interface CardVersion {
169
179
  id: string;
180
+ code: string;
170
181
  languages: string[];
171
182
  traits: string[];
172
183
  label?: string;
@@ -244,6 +255,7 @@ export interface CommentRecord {
244
255
  export interface CommentStanding {
245
256
  can_comment: boolean;
246
257
  comments: string;
258
+ locked: boolean;
247
259
  ban?: BanNotice;
248
260
  anonymous: boolean;
249
261
  max_length: number;
@@ -295,6 +307,8 @@ export interface Content {
295
307
  creators: Creator[];
296
308
  groups: GroupSummary[];
297
309
  versions: Version[];
310
+ views: number;
311
+ viewer?: ViewerState;
298
312
  editor: boolean;
299
313
  text?: Record<string, CatalogText>;
300
314
  moderation?: ModerationState;
@@ -353,6 +367,8 @@ export interface ContentModel {
353
367
  kinds: Record<string, Kind>;
354
368
  group_kinds: Record<string, GroupKind>;
355
369
  content_filters: ContentFilter[];
370
+ signals: boolean;
371
+ avatars?: PresetRule;
356
372
  }
357
373
  export interface ContentPage {
358
374
  contents: Card[];
@@ -454,6 +470,10 @@ export interface Decision {
454
470
  free_after?: string;
455
471
  offers?: Sale[];
456
472
  }
473
+ export interface DeleteRecord {
474
+ entity: string;
475
+ external_id: string;
476
+ }
457
477
  export interface Edge {
458
478
  from: string;
459
479
  relation: string;
@@ -525,6 +545,28 @@ export interface Exposures {
525
545
  session?: string;
526
546
  exposures: ExposureInput[];
527
547
  }
548
+ export interface ExternalID {
549
+ external_id: string;
550
+ id: string;
551
+ deleted: boolean;
552
+ }
553
+ export interface ExternalIDList {
554
+ entity: string;
555
+ cursor?: string;
556
+ limit?: number;
557
+ }
558
+ export interface ExternalIDPage {
559
+ entity: string;
560
+ ids: ExternalID[];
561
+ next_cursor?: string;
562
+ }
563
+ export interface ExternalIDs {
564
+ lists: ExternalIDPage[];
565
+ }
566
+ export interface ExternalIDsQuery {
567
+ source: string;
568
+ lists: ExternalIDList[];
569
+ }
528
570
  export interface Failure {
529
571
  of: string;
530
572
  message: string;
@@ -672,6 +714,7 @@ export interface GroupDetail {
672
714
  edges: Edge[];
673
715
  counts: Count[];
674
716
  access?: GroupAccess;
717
+ offer?: Offer;
675
718
  }
676
719
  export interface GroupInput {
677
720
  id?: string;
@@ -741,6 +784,8 @@ export interface GroupSummary {
741
784
  kind: string;
742
785
  slug: string;
743
786
  name: string;
787
+ code?: string;
788
+ path?: string;
744
789
  }
745
790
  export interface GroupUpdate {
746
791
  slug?: string;
@@ -852,6 +897,7 @@ export interface Kind {
852
897
  groups?: string[];
853
898
  attributes?: Record<string, Attribute>;
854
899
  sort?: string;
900
+ preview?: Preview;
855
901
  draft_ttl?: number;
856
902
  }
857
903
  export interface LanguageTotal {
@@ -947,6 +993,12 @@ export interface MyGroupPage {
947
993
  groups: MyGroup[];
948
994
  next_cursor?: string;
949
995
  }
996
+ export interface MyStats {
997
+ completed: Record<string, number>;
998
+ comments: number;
999
+ comment_likes: number;
1000
+ comment_dislikes: number;
1001
+ }
950
1002
  export interface Name {
951
1003
  language: string;
952
1004
  kind: "alias" | "name";
@@ -1158,6 +1210,7 @@ export interface PresetRule {
1158
1210
  min_width?: number;
1159
1211
  first?: number;
1160
1212
  default: boolean;
1213
+ stable?: boolean;
1161
1214
  }
1162
1215
  export interface PresignBody {
1163
1216
  ref: RefBody;
@@ -1204,6 +1257,12 @@ export interface PublishInput {
1204
1257
  export interface Published {
1205
1258
  items: Item[];
1206
1259
  }
1260
+ export interface RankedContent {
1261
+ id: string;
1262
+ title: string;
1263
+ path?: string;
1264
+ value: number;
1265
+ }
1207
1266
  export interface ReactionCounts {
1208
1267
  likes: number;
1209
1268
  dislikes: number;
@@ -1309,6 +1368,7 @@ export interface Resolved {
1309
1368
  slug: string;
1310
1369
  slugs?: Record<string, string>;
1311
1370
  path: string;
1371
+ version_id?: string;
1312
1372
  }
1313
1373
  export interface Restored {
1314
1374
  restored: boolean;
@@ -1376,6 +1436,21 @@ export interface Signals {
1376
1436
  session?: string;
1377
1437
  signals: SignalInput[];
1378
1438
  }
1439
+ export interface SitemapAlias {
1440
+ language: string;
1441
+ path: string;
1442
+ }
1443
+ export interface SitemapEntry {
1444
+ id: string;
1445
+ code: string;
1446
+ path: string;
1447
+ lastmod: string;
1448
+ languages: SitemapAlias[];
1449
+ }
1450
+ export interface SitemapPage {
1451
+ entries: SitemapEntry[];
1452
+ next_cursor?: string;
1453
+ }
1379
1454
  export interface StagedCopy {
1380
1455
  path: string;
1381
1456
  blob: string;
@@ -1401,6 +1476,9 @@ export interface Terms {
1401
1476
  export interface TicketBody {
1402
1477
  ticket: string;
1403
1478
  }
1479
+ export interface TopContents {
1480
+ contents: RankedContent[];
1481
+ }
1404
1482
  export interface Total {
1405
1483
  public: number;
1406
1484
  tiers: Record<string, number>;
@@ -1433,6 +1511,7 @@ export interface UploadRule {
1433
1511
  }
1434
1512
  export interface Version {
1435
1513
  id: string;
1514
+ code: string;
1436
1515
  position: number;
1437
1516
  languages: string[];
1438
1517
  traits: string[];
@@ -1455,6 +1534,7 @@ export interface Version {
1455
1534
  decision: Decision;
1456
1535
  text?: Record<string, VersionText>;
1457
1536
  offer?: Offer;
1537
+ viewer?: ViewerState;
1458
1538
  }
1459
1539
  export interface VersionFork {
1460
1540
  from: string;
@@ -1478,6 +1558,7 @@ export interface VersionInput {
1478
1558
  export interface VersionRecord {
1479
1559
  external_id: string;
1480
1560
  id?: string;
1561
+ code?: string;
1481
1562
  languages: string[];
1482
1563
  traits?: string[];
1483
1564
  label?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"wire.js","names":[],"sources":["../../../src/client/generated/wire.ts"],"sourcesContent":["// Generated by `go generate ./internal/contract` from ContentKit's route catalog. Do not edit.\n\nimport type { ErrorCode } from \"./error-codes.js\";\n\nexport type { ErrorCode };\n\n// Upload sizes: one PUT up to MAX_SINGLE_PUT bytes, else multipart parts.\nexport const MAX_SINGLE_PUT = 67108864;\nexport const MIN_PART_SIZE = 8388608;\nexport const MAX_PART_SIZE = 16777216;\n\nexport type OpName = \"put\" | \"edit\" | \"move\" | \"rename\" | \"remove\" | \"attach\" | \"copy\" | \"frame\" | \"meta\" | \"regenerate\";\nexport type Access = \"full\" | \"none\";\nexport type ItemState = \"ready\" | \"processing\" | \"failed\";\nexport type EncodePhase = \"queued\" | \"downloading\" | \"probing\" | \"encoding\" | \"muxing\" | \"uploading\" | \"publishing\" | \"images\";\nexport type PollKind = \"multiple_choice\" | \"free_text\";\nexport type ModerationState = \"approved\" | \"held\" | \"rejected\";\nexport type ContentState = \"draft\" | \"published\" | \"deleted\";\nexport type VersionState = \"draft\" | \"published\";\nexport type Listing = \"listed\" | \"members\" | \"unlisted\";\nexport type AliasStatus = \"found\" | \"not_found\" | \"gone\";\n\nexport interface AdminComment {\n id: string;\n reply_to_id?: string;\n user_id?: string;\n anon_name?: string;\n language?: string;\n body: string;\n deleted: boolean;\n likes: number;\n dislikes: number;\n mine: number;\n reply_count: number;\n author?: PublicUser;\n moderation?: ModerationState;\n moderation_reason?: string;\n created_at: string;\n updated_at: string;\n tenant_id: string;\n content_kind: string;\n content_id: string;\n content_version_id?: string;\n deleted_at?: string;\n}\n\nexport interface AliasKey {\n source: string;\n kind: string;\n key: string;\n}\n\nexport interface AliasLookup {\n aliases: AliasKey[];\n}\n\nexport interface AliasRecord {\n kind: string;\n key: string;\n locator?: string;\n target: TargetRef;\n}\n\nexport interface AliasResult {\n source: string;\n kind: string;\n key: string;\n status: AliasStatus;\n resolved?: Resolved;\n locator?: string;\n}\n\nexport interface AliasResults {\n aliases: AliasResult[];\n}\n\nexport interface Anchor {\n id: string;\n page: string;\n offset_percent: number;\n duration_seconds?: number;\n}\n\nexport interface Anonymous {\n comments: boolean;\n reactions: boolean;\n votes: boolean;\n}\n\nexport interface AnswerGroup {\n id: string;\n label: string;\n count: number;\n}\n\nexport interface AnswerRecord {\n poll: Ref;\n user_id: string;\n text: string;\n created_at?: string;\n updated_at?: string;\n}\n\nexport interface Attribute {\n type: string;\n values?: string[];\n filterable?: boolean;\n sortable?: boolean;\n}\n\nexport interface Avatar {\n image?: string;\n renditions?: Rendition[];\n}\n\nexport interface BackgroundCue {\n file: string;\n from_page?: string;\n to_page?: string;\n from_anchor?: string;\n to_anchor?: string;\n loop?: boolean;\n layer?: string;\n}\n\nexport interface BanInput {\n reason?: string;\n until?: string;\n}\n\nexport interface BanNotice {\n scope: string;\n reason?: string;\n until?: string;\n}\n\nexport interface BannedWord {\n word: string;\n action: \"mask\" | \"reject\";\n created_by: string;\n created_at: string;\n}\n\nexport interface BannedWordInput {\n word: string;\n action?: \"mask\" | \"reject\";\n}\n\nexport interface Batch {\n source: string;\n preference_floor?: number;\n provision_scopes?: boolean;\n groups?: GroupRecord[];\n contents?: ContentRecord[];\n memberships?: MembershipSet[];\n offers?: OfferRecord[];\n aliases?: AliasRecord[];\n comments?: CommentRecord[];\n reactions?: ReactionRecord[];\n favorites?: FavoriteRecord[];\n polls?: PollRecord[];\n votes?: VoteRecord[];\n answers?: AnswerRecord[];\n signals?: SignalRecord[];\n}\n\nexport interface Card {\n id: string;\n kind: string;\n code: string;\n path: string;\n title: string;\n language: string;\n cover: PublicRendition[];\n version: CardVersion;\n groups: Record<string, Chip[]>;\n owner?: GroupCard;\n counts: Counts;\n access: CardAccess;\n viewer?: ViewerState;\n published_at?: string;\n attributes: Record<string, unknown>;\n}\n\nexport interface CardAccess {\n allowed: boolean;\n unknown?: boolean;\n sell?: string[];\n requires?: string;\n preview?: Preview;\n free_after?: string;\n offers?: Sale[];\n}\n\nexport interface CardVersion {\n id: string;\n languages: string[];\n traits: string[];\n label?: string;\n pages?: number;\n duration_ms?: number;\n}\n\nexport interface CatalogCover {\n image: string;\n url?: string;\n}\n\nexport interface CatalogText {\n title: string;\n slug?: string;\n description?: string;\n body?: string;\n sort_key?: string;\n aliases?: string[];\n}\n\nexport interface Chip {\n id: string;\n slug: string;\n name: string;\n path?: string;\n}\n\nexport interface Comment {\n id: string;\n reply_to_id?: string;\n user_id?: string;\n anon_name?: string;\n language?: string;\n body: string;\n deleted: boolean;\n likes: number;\n dislikes: number;\n mine: number;\n reply_count: number;\n author?: PublicUser;\n moderation?: ModerationState;\n moderation_reason?: string;\n created_at: string;\n updated_at: string;\n}\n\nexport interface CommentBan {\n user_id: string;\n user?: PublicUser;\n scope: string;\n reason?: string;\n until?: string;\n banned_by: string;\n banned_at: string;\n expired: boolean;\n}\n\nexport interface CommentEdit {\n body: string;\n}\n\nexport interface CommentInput {\n body: string;\n reply_to_id?: string;\n anon_name?: string;\n language?: string;\n}\n\nexport interface CommentRecord {\n external_id: string;\n id?: string;\n target: TargetRef;\n language?: string;\n reply_to?: Ref;\n user_id?: string;\n anon_name?: string;\n body: string;\n deleted_at?: string;\n created_at?: string;\n updated_at?: string;\n}\n\nexport interface CommentStanding {\n can_comment: boolean;\n comments: string;\n ban?: BanNotice;\n anonymous: boolean;\n max_length: number;\n user_id?: string;\n moderate: boolean;\n ban_scopes: (\"global\" | \"owner\")[];\n}\n\nexport interface CommitBody {\n ref: RefBody;\n operation_id: string;\n ops: Op[];\n}\n\nexport interface CommitReply {\n files: FileInfo[];\n}\n\nexport interface CompleteReply {\n blob: string;\n type: string;\n size: number;\n}\n\nexport interface Config {\n anonymous: Anonymous;\n comment_max_length: number;\n}\n\nexport interface Content {\n id: string;\n kind: string;\n code: string;\n path: string;\n language: string;\n title: string;\n slug?: string;\n description?: string;\n body?: string;\n decision: Decision;\n images?: Record<string, string>;\n cover?: CatalogCover;\n state: ContentState;\n listing: Listing;\n comments: string;\n publish_at?: string;\n published_at?: string;\n live: boolean;\n access: Rule;\n restrictions: string[];\n attributes: Record<string, unknown>;\n owner_group_id?: string;\n owner?: GroupCard;\n creators: Creator[];\n groups: GroupSummary[];\n versions: Version[];\n editor: boolean;\n text?: Record<string, CatalogText>;\n moderation?: ModerationState;\n moderation_reason?: string;\n ever_sold?: boolean;\n offer?: Offer;\n revision: number;\n created_at: string;\n updated_at: string;\n deleted_at?: string;\n}\n\nexport interface ContentFilter {\n group: GroupRef;\n default_visible: boolean;\n labels?: Record<string, string>;\n}\n\nexport interface ContentFilterState {\n group: GroupCard;\n label: string;\n default_visible: boolean;\n visible: boolean;\n overridden: boolean;\n}\n\nexport interface ContentFilters {\n filters: ContentFilterState[];\n include_deleted: boolean;\n}\n\nexport interface ContentFiltersUpdate {\n filters: FilterSetting[];\n include_deleted: boolean;\n}\n\nexport interface ContentGroups {\n kinds?: string[];\n memberships: Membership[];\n}\n\nexport interface ContentInput {\n kind: string;\n text?: Record<string, CatalogText>;\n listing?: Listing;\n comments?: string;\n access?: Rule;\n restrictions?: string[];\n attributes?: Record<string, unknown>;\n owner_group_id?: string;\n creators?: Creator[];\n versions?: VersionInput[];\n}\n\nexport interface ContentMemberships {\n content_id: string;\n memberships: Membership[];\n groups: GroupCard[];\n}\n\nexport interface ContentModel {\n languages: string[];\n tiers: string[];\n kinds: Record<string, Kind>;\n group_kinds: Record<string, GroupKind>;\n content_filters: ContentFilter[];\n}\n\nexport interface ContentPage {\n contents: Card[];\n next_cursor?: string;\n}\n\nexport interface ContentPatch {\n listing?: Listing;\n comments?: string;\n access?: Rule;\n restrictions?: string[];\n attributes?: Record<string, unknown>;\n creators?: Creator[];\n cover?: string;\n revision?: number;\n}\n\nexport interface ContentRecord {\n external_id: string;\n id?: string;\n kind: string;\n state: string;\n publish_at?: string;\n published_at?: string;\n deleted_at?: string;\n listing?: string;\n comments?: string;\n view_baseline?: number;\n access?: Rule;\n restrictions?: string[];\n attributes?: Record<string, unknown>;\n owner_group?: Ref;\n cover?: string;\n creators?: Creator[];\n text?: Record<string, CatalogText>;\n versions?: VersionRecord[];\n code?: string;\n created_at?: string;\n updated_at?: string;\n}\n\nexport interface ContentRef {\n tenant_id: string;\n content_kind: string;\n content_id: string;\n content_version_id?: string;\n}\n\nexport interface CopyBody {\n ref: RefBody;\n files: CopyRequest[];\n}\n\nexport interface CopyFrom {\n id: string;\n version?: string;\n path: string;\n}\n\nexport interface CopyReply {\n files: StagedCopy[];\n}\n\nexport interface CopyRequest {\n path: string;\n type: string;\n from: string;\n size?: number;\n}\n\nexport interface Count {\n content_kind: string;\n language: string;\n count: number;\n}\n\nexport interface Counts {\n likes: number;\n dislikes: number;\n favorites: number;\n views: number;\n}\n\nexport interface Creator {\n user_id: string;\n role?: string;\n position: number;\n}\n\nexport interface CreatorsInput {\n creators: Creator[];\n}\n\nexport interface Crop {\n x: number;\n y: number;\n w: number;\n h: number;\n}\n\nexport interface DateCount {\n date: string;\n count: number;\n free: number;\n}\n\nexport interface Decision {\n allowed: boolean;\n unknown?: boolean;\n sell?: string[];\n requires?: string;\n preview?: Preview;\n free_after?: string;\n offers?: Sale[];\n}\n\nexport interface Edge {\n from: string;\n relation: string;\n to: string;\n}\n\nexport interface EdgeInput {\n relation: string;\n to: string;\n}\n\nexport interface EdgeRecord {\n relation: string;\n to: Ref;\n}\n\nexport interface Edit {\n crop?: Crop;\n rotate?: number;\n}\n\nexport interface EncodeProgress {\n phase: EncodePhase;\n queue_position?: number;\n segments_done?: number;\n segments_total?: number;\n percent: number;\n speed?: number;\n eta?: number;\n at: number;\n stalled?: boolean;\n stage?: number;\n stages?: number;\n}\n\nexport interface ErrorDetails {\n width?: number;\n height?: number;\n min_width?: number;\n max_pixels?: number;\n type?: string;\n allowed?: string[];\n size?: number;\n max_bytes?: number;\n frames?: number;\n max_frames?: number;\n seconds?: number;\n max_seconds?: number;\n min_aspect?: number;\n max_aspect?: number;\n max?: number;\n}\n\nexport interface ErrorReply {\n error: string;\n code: ErrorCode;\n retry_after?: number;\n action?: \"comment\" | \"comment_reaction\" | \"favorite\" | \"poll_vote\" | \"reaction\";\n ban?: BanNotice;\n blobs?: string[];\n details?: ErrorDetails;\n}\n\nexport interface ExposureInput {\n render_id: string;\n stage: string;\n revision?: number;\n query_id?: string;\n surface?: string;\n ranker?: string;\n language?: string;\n occurred_at?: string;\n shown: Placement[];\n}\n\nexport interface Exposures {\n session?: string;\n exposures: ExposureInput[];\n}\n\nexport interface Failure {\n of: string;\n message: string;\n code?: ErrorCode;\n details?: ErrorDetails;\n}\n\nexport interface FavoriteRecord {\n target: TargetRef;\n user_id: string;\n created_at?: string;\n}\n\nexport interface FavoriteState {\n favorited: boolean;\n count: number;\n}\n\nexport interface FeedItem {\n id: string;\n reply_to_id?: string;\n user_id?: string;\n anon_name?: string;\n language?: string;\n body: string;\n deleted: boolean;\n likes: number;\n dislikes: number;\n mine: number;\n reply_count: number;\n author?: PublicUser;\n moderation?: ModerationState;\n moderation_reason?: string;\n created_at: string;\n updated_at: string;\n tenant_id: string;\n content_kind: string;\n content_id: string;\n content_version_id?: string;\n}\n\nexport interface FileInfo {\n path: string;\n type: string;\n size?: number;\n w?: number;\n h?: number;\n dur?: number;\n download?: string;\n url?: string;\n locked?: boolean;\n upload?: boolean;\n staged?: boolean;\n from?: string;\n edit?: Edit;\n frame?: Frame;\n meta?: Record<string, unknown>;\n unattached?: boolean;\n pending?: string[];\n failed?: Failure;\n editor_url?: string;\n progress?: EncodeProgress;\n}\n\nexport interface FilterSetting {\n group_id: string;\n visible: boolean;\n}\n\nexport interface ForegroundCue {\n file: string;\n page?: string;\n anchor?: string;\n}\n\nexport interface ForkBody {\n from: string;\n playback?: boolean;\n segments?: boolean;\n}\n\nexport interface Frame {\n t?: number;\n auto?: boolean;\n of?: string;\n}\n\nexport interface Group {\n id: string;\n kind: string;\n slug: string;\n scope_id?: string;\n parent_id?: string;\n state: \"active\" | \"deleted\" | \"merged\";\n merged_into?: string;\n attributes: Record<string, unknown>;\n cover?: GroupsCover;\n avatar?: Avatar;\n owner_user_id?: string;\n auth_scope?: string;\n created_at: string;\n updated_at: string;\n revision: number;\n}\n\nexport interface GroupAccess {\n key: string;\n offer?: Sale;\n member: boolean;\n editor?: boolean;\n unknown?: boolean;\n}\n\nexport interface GroupCard {\n id: string;\n kind: string;\n slug: string;\n scope_id?: string;\n parent_id?: string;\n name: string;\n name_language?: string;\n code?: string;\n path?: string;\n cover?: GroupsCover;\n avatar?: Avatar;\n count: number;\n attributes: Record<string, unknown>;\n access?: GroupAccess;\n created_at: string;\n}\n\nexport interface GroupDetail {\n id: string;\n kind: string;\n slug: string;\n scope_id?: string;\n parent_id?: string;\n state: \"active\" | \"deleted\" | \"merged\";\n merged_into?: string;\n attributes: Record<string, unknown>;\n cover?: GroupsCover;\n avatar?: Avatar;\n owner_user_id?: string;\n auth_scope?: string;\n created_at: string;\n updated_at: string;\n revision: number;\n name: string;\n name_language?: string;\n code?: string;\n path?: string;\n names: Name[];\n text: GroupsText[];\n parent?: GroupCard;\n scope?: GroupCard;\n children: GroupCard[];\n edges: Edge[];\n counts: Count[];\n access?: GroupAccess;\n}\n\nexport interface GroupInput {\n id?: string;\n kind: string;\n slug: string;\n scope_id?: string;\n parent_id?: string;\n attributes?: Record<string, unknown>;\n cover?: GroupsCover;\n avatar?: Avatar;\n owner_user_id?: string;\n auth_scope?: string;\n names?: Name[];\n text?: GroupsText[];\n}\n\nexport interface GroupJoin {\n group_id: string;\n key: string;\n member: boolean;\n}\n\nexport interface GroupKind {\n route?: string;\n ordered?: boolean;\n parent?: string;\n scoped_by?: string;\n owner?: string;\n max_per_owner?: number;\n uploads?: UploadLimits;\n sellable?: boolean;\n auth_scope?: boolean;\n windows?: boolean;\n version_level?: boolean;\n roles?: string[];\n attributes?: Record<string, Attribute>;\n}\n\nexport interface GroupPage {\n groups: GroupCard[];\n next_cursor?: string;\n}\n\nexport interface GroupRecord {\n external_id: string;\n id?: string;\n kind: string;\n slug: string;\n scope?: Ref;\n parent?: Ref;\n state?: string;\n names?: Name[];\n text?: GroupsText[];\n attributes?: Record<string, unknown>;\n cover_image?: string;\n cover_content?: Ref;\n avatar?: string;\n owner_user_id?: string;\n auth_scope?: string;\n edges?: EdgeRecord[];\n code?: string;\n created_at?: string;\n updated_at?: string;\n}\n\nexport interface GroupRef {\n kind: string;\n slug: string;\n}\n\nexport interface GroupSummary {\n id: string;\n kind: string;\n slug: string;\n name: string;\n}\n\nexport interface GroupUpdate {\n slug?: string;\n scope_id?: string;\n parent_id?: string;\n state?: \"active\" | \"deleted\" | \"merged\";\n attributes?: Record<string, unknown>;\n cover?: GroupsCover;\n avatar?: Avatar;\n owner_user_id?: string;\n auth_scope?: string;\n names?: Name[];\n text?: GroupsText[];\n}\n\nexport interface GroupsCover {\n image?: string;\n content_id?: string;\n auto?: boolean;\n renditions?: Rendition[];\n}\n\nexport interface GroupsText {\n language: string;\n description?: string;\n sort_key?: string;\n}\n\nexport interface HeldItem {\n kind: string;\n revision: number;\n id: string;\n ref: ContentRef;\n author_id?: string;\n author?: PublicUser;\n anon_name?: string;\n title?: string;\n body: string;\n reason: string;\n model?: string;\n prompt_version?: string;\n confidence?: number;\n error?: string;\n held_at: string;\n created_at: string;\n}\n\nexport interface HeldPage {\n items: HeldItem[];\n next?: string;\n}\n\nexport interface IPBan {\n network: string;\n reason?: string;\n until?: string;\n banned_by: string;\n banned_at: string;\n expired: boolean;\n}\n\nexport interface IPBanInput {\n network: string;\n reason?: string;\n until?: string;\n}\n\nexport interface ImageInput {\n image?: string;\n}\n\nexport interface InlineImage {\n ref: string;\n url: string | null;\n}\n\nexport interface Input {\n recipient: Recipient;\n origin: Origin;\n kind: string;\n data: unknown;\n created_at: string;\n status?: string;\n action_requested?: boolean;\n severity?: string;\n}\n\nexport interface Interaction {\n content?: Card;\n group?: GroupCard;\n value: number;\n at: string;\n}\n\nexport interface InteractionPage {\n items: Interaction[];\n next_cursor?: string;\n}\n\nexport interface Item {\n id: string;\n recipient: Recipient;\n origin: Origin;\n kind: string;\n data: unknown;\n created_at: string;\n status?: string;\n action_requested?: boolean;\n severity?: string;\n read_at: string | null;\n dismissed_at: string | null;\n resolved_at: string | null;\n updated_at: string;\n revision: number;\n}\n\nexport interface Kind {\n route: string;\n presentation: string;\n versions: VersionRules;\n segments?: boolean;\n groups?: string[];\n attributes?: Record<string, Attribute>;\n sort?: string;\n draft_ttl?: number;\n}\n\nexport interface LanguageTotal {\n public: number;\n tiers: Record<string, number>;\n}\n\nexport interface LibraryItem {\n content_id: string;\n kind: string;\n title: string;\n language?: string;\n deleted: boolean;\n whole: boolean;\n version_ids: string[];\n}\n\nexport interface LibraryPage {\n items: LibraryItem[];\n next_cursor?: string;\n complete: boolean;\n}\n\nexport interface MemberPage {\n members: Membership[];\n next_cursor?: string;\n}\n\nexport interface MemberRef {\n content_id: string;\n version_id?: string;\n}\n\nexport interface Membership {\n group_id?: string;\n content_id?: string;\n version_id?: string;\n role?: string;\n position?: number;\n active_from?: string;\n active_until?: string;\n state?: \"active\" | \"proposed\";\n}\n\nexport interface MembershipRecord {\n group: Ref;\n version?: Ref;\n role?: string;\n position?: number;\n active_from?: string;\n active_until?: string;\n state?: string;\n}\n\nexport interface MembershipSet {\n content: Ref;\n kinds: string[];\n memberships: MembershipRecord[];\n}\n\nexport interface MergeInput {\n into: string;\n}\n\nexport interface MergeReport {\n from: string;\n into: string;\n memberships_moved: number;\n names_moved: number;\n edges_moved: number;\n groups_moved: number;\n}\n\nexport interface MultipartReply {\n ticket: string;\n min_part_size: number;\n max_part_size: number;\n max_parts: number;\n}\n\nexport interface MyGroup {\n id: string;\n kind: string;\n slug: string;\n scope_id?: string;\n parent_id?: string;\n name: string;\n name_language?: string;\n code?: string;\n path?: string;\n cover?: GroupsCover;\n avatar?: Avatar;\n count: number;\n attributes: Record<string, unknown>;\n access?: GroupAccess;\n created_at: string;\n owner: boolean;\n editor: boolean;\n writer: boolean;\n moderator: boolean;\n member: boolean;\n}\n\nexport interface MyGroupPage {\n groups: MyGroup[];\n next_cursor?: string;\n}\n\nexport interface Name {\n language: string;\n kind: \"alias\" | \"name\";\n name: string;\n}\n\nexport interface Offer {\n id: string;\n target: Target;\n key: string;\n terms: Terms;\n state: \"active\" | \"withdrawn\";\n synced: boolean;\n product_id?: string;\n price_id?: string;\n last_error?: string;\n revision: number;\n updated_at: string;\n}\n\nexport interface OfferPage {\n offers: Offer[];\n next_cursor?: string;\n}\n\nexport interface OfferRecord {\n target: TargetRef;\n terms: Terms;\n}\n\nexport interface Op {\n op: OpName;\n path?: string;\n blob?: string;\n create_id?: string;\n index?: number;\n meta?: Record<string, unknown>;\n edit?: Edit;\n unattached?: boolean;\n to?: string;\n from?: CopyFrom;\n t?: number;\n auto?: boolean;\n preset?: string;\n force?: boolean;\n takedown?: boolean;\n}\n\nexport interface Order {\n members: MemberRef[];\n}\n\nexport interface OrderInput {\n ids: string[];\n}\n\nexport interface Origin {\n system: string;\n scope: string;\n id: string;\n subject?: string;\n}\n\nexport interface Page {\n items: Item[];\n next_cursor?: string;\n}\n\nexport interface PartBody {\n number: number;\n size: number;\n sha256: string;\n}\n\nexport interface PartReply {\n number: number;\n size?: number;\n sha256?: string;\n request?: RequestReply;\n}\n\nexport interface PartsBody {\n ticket: string;\n parts: PartBody[];\n}\n\nexport interface PartsReply {\n parts: PartReply[];\n}\n\nexport interface Pending {\n status: string;\n}\n\nexport interface Placement {\n content_id: string;\n position: number;\n}\n\nexport interface PlanPage {\n name: string;\n w: number;\n h: number;\n files: Record<string, string>;\n}\n\nexport interface PlanPlayback {\n item: RefBody;\n tracks: PlanTrack[];\n}\n\nexport interface PlanPreview {\n index: number;\n name: string;\n url: string;\n w: number;\n h: number;\n}\n\nexport interface PlanSegment {\n position?: number;\n title?: string;\n start: number;\n end: number;\n released: boolean;\n release_at?: string;\n locked: boolean;\n item?: RefBody;\n pages?: PlanPage[];\n}\n\nexport interface PlanTrack {\n name: string;\n path: string;\n duration?: number;\n}\n\nexport interface Poll {\n id: string;\n kind: PollKind;\n question: string;\n language: string;\n is_active: boolean;\n image_url?: string;\n live_at: string;\n closes_at?: string;\n closed: boolean;\n total_votes: number;\n options: PollOption[];\n voted: boolean;\n my_option?: string;\n answer_count?: number;\n groups?: AnswerGroup[];\n my_answer?: PollAnswer;\n}\n\nexport interface PollAnswer {\n id: string;\n text: string;\n classified: boolean;\n updated_at: string;\n}\n\nexport interface PollAnswerInput {\n text: string;\n}\n\nexport interface PollImage {\n image_url: string | null;\n}\n\nexport interface PollInput {\n kind?: PollKind;\n question: string;\n language: string;\n live_at?: string;\n closes_at?: string;\n options?: PollOptionInput[];\n}\n\nexport interface PollOption {\n id: string;\n label: string;\n image_url?: string;\n position: number;\n vote_count: number;\n}\n\nexport interface PollOptionInput {\n label: string;\n position: number;\n}\n\nexport interface PollOptionPatch {\n label?: string;\n position?: number;\n}\n\nexport interface PollOptionRecord {\n external_id: string;\n id?: string;\n label: string;\n position: number;\n}\n\nexport interface PollRecord {\n external_id: string;\n id?: string;\n kind?: string;\n question: string;\n language?: string;\n active?: boolean;\n live_at?: string;\n closes_at?: string;\n deleted_at?: string;\n created_at?: string;\n updated_at?: string;\n options?: PollOptionRecord[];\n}\n\nexport interface PollUpdate {\n question?: string;\n is_active?: boolean;\n live_at?: string;\n closes_at?: string | null;\n}\n\nexport interface PollVote {\n option_id: string;\n}\n\nexport interface PresetRule {\n kind: string;\n name: string;\n from: string;\n base: string;\n namespace: string;\n to: string;\n widths: number[];\n aspect?: string;\n min_width?: number;\n first?: number;\n default: boolean;\n}\n\nexport interface PresignBody {\n ref: RefBody;\n path: string;\n type: string;\n size: number;\n sha256: string;\n}\n\nexport interface PresignReply {\n path: string;\n blob: string;\n exists?: boolean;\n put?: RequestReply;\n multipart?: MultipartReply;\n process_on_upload?: boolean;\n}\n\nexport interface Preview {\n pages?: number;\n segments?: number;\n}\n\nexport interface PublicImage {\n from: string;\n preset: string;\n renditions: PublicRendition[];\n}\n\nexport interface PublicRendition {\n url: string;\n w: number;\n h: number;\n}\n\nexport interface PublicUser {\n id: string;\n name?: string;\n username: string;\n avatar?: string;\n avatar_srcset?: string;\n}\n\nexport interface Publication {\n notifications: Input[];\n}\n\nexport interface PublishInput {\n publish_at?: string;\n}\n\nexport interface Published {\n items: Item[];\n}\n\nexport interface ReactionCounts {\n likes: number;\n dislikes: number;\n mine: number;\n}\n\nexport interface ReactionRecord {\n target: TargetRef;\n user_id?: string;\n ip?: string;\n value: number;\n created_at?: string;\n updated_at?: string;\n}\n\nexport interface ReadResult {\n access: Access;\n expires: number;\n meta?: Record<string, unknown>;\n previews?: string[];\n public?: PublicImage[];\n total: number;\n offset: number;\n limit: number;\n hls?: string[];\n state?: ItemState;\n full?: boolean;\n uploads?: UploadRule[];\n files: FileInfo[];\n}\n\nexport interface ReaderDefaults {\n auto_advance_seconds?: number;\n}\n\nexport interface ReaderMeta {\n schema_version: number;\n defaults?: ReaderDefaults;\n anchors?: Anchor[];\n background?: BackgroundCue[];\n foreground?: ForegroundCue[];\n}\n\nexport interface ReaderPlan {\n content_id: string;\n version_id: string;\n publication_id: string;\n pages: number;\n segments: PlanSegment[];\n previews: PlanPreview[];\n playback?: PlanPlayback;\n reader?: ReaderMeta;\n}\n\nexport interface RecentSearch {\n query: string;\n results: number;\n searched_at: string;\n}\n\nexport interface RecentSearches {\n searches: RecentSearch[];\n}\n\nexport interface Recipient {\n audience: string;\n id: string;\n}\n\nexport interface RecordFailure {\n section: string;\n index: number;\n external_id?: string;\n code: string;\n error: string;\n}\n\nexport interface Ref {\n id?: string;\n external_id?: string;\n}\n\nexport interface RefBody {\n kind: string;\n id: string;\n version?: string;\n}\n\nexport interface ReleaseDates {\n dates: DateCount[];\n next_cursor?: string;\n}\n\nexport interface Rendition {\n url: string;\n w: number;\n h: number;\n}\n\nexport interface RequestReply {\n method: string;\n url: string;\n headers: Record<string, string>;\n expires: string;\n}\n\nexport interface ResolveInput {\n decision: \"approve\" | \"reject\" | \"review\";\n revision: number;\n reason?: string;\n}\n\nexport interface Resolved {\n tenant_id: string;\n content_kind: string;\n content_id: string;\n content_version_id?: string;\n code: string;\n slug: string;\n slugs?: Record<string, string>;\n path: string;\n}\n\nexport interface Restored {\n restored: boolean;\n}\n\nexport interface Result {\n written: Record<string, number>;\n skipped?: Record<string, number>;\n failed: RecordFailure[];\n ids: Record<string, Record<string, string>>;\n}\n\nexport interface ReviewOutcome {\n decision: \"approve\" | \"reject\" | \"review\";\n}\n\nexport interface Rule {\n level: \"group\" | \"group_ppv\" | \"ppv\" | \"public\" | \"tier\";\n tiers?: string[];\n group_id?: string;\n preview?: Preview;\n free_after?: string;\n}\n\nexport interface Sale {\n key: string;\n target: Target;\n terms: Terms;\n price_id?: string;\n}\n\nexport interface Segment {\n position: number;\n title?: string;\n start: number;\n end: number;\n release_at?: string;\n released: boolean;\n}\n\nexport interface SegmentsInput {\n segments: Segment[];\n}\n\nexport interface SignalInput {\n content_id: string;\n version_id?: string;\n type: string;\n event_id: string;\n revision?: number;\n occurred_at: string;\n duration_s?: number;\n progress?: number;\n resume?: string;\n render_id?: string;\n surface?: string;\n position?: number;\n}\n\nexport interface SignalRecord {\n target: TargetRef;\n user_id?: string;\n anon_key?: string;\n type: string;\n event_id: string;\n revision?: number;\n occurred_at: string;\n duration_s?: number;\n progress?: number;\n resume?: string;\n}\n\nexport interface Signals {\n session?: string;\n signals: SignalInput[];\n}\n\nexport interface StagedCopy {\n path: string;\n blob: string;\n size: number;\n}\n\nexport interface Target {\n kind: \"content\" | \"group\" | \"version\";\n id: string;\n content_id?: string;\n}\n\nexport interface TargetRef {\n content?: Ref;\n version?: Ref;\n group?: Ref;\n comment?: Ref;\n}\n\nexport interface Terms {\n amount: string;\n currency: string;\n access_hours?: number;\n renew_hours?: number;\n}\n\nexport interface TicketBody {\n ticket: string;\n}\n\nexport interface Total {\n public: number;\n tiers: Record<string, number>;\n languages: Record<string, LanguageTotal>;\n}\n\nexport interface Totals {\n kinds: Record<string, Total>;\n}\n\nexport interface Typeahead {\n contents: Card[];\n groups: GroupCard[];\n}\n\nexport interface UploadLimits {\n files_per_hour?: number;\n bytes_per_day?: number;\n quota_bytes?: number;\n}\n\nexport interface UploadRule {\n path: string;\n types: string[];\n max_bytes: number;\n max?: number;\n named?: boolean;\n frames?: string;\n aspect?: string;\n min_width?: number;\n video?: VideoLimits;\n min_aspect?: number;\n max_aspect?: number;\n}\n\nexport interface Version {\n id: string;\n position: number;\n languages: string[];\n traits: string[];\n label?: string;\n title?: string;\n description?: string;\n state?: ContentState;\n publish_at?: string;\n published_at?: string;\n live: boolean;\n access?: Rule;\n attributes: Record<string, unknown>;\n pages?: number;\n duration_ms?: number;\n width?: number;\n height?: number;\n has_audio: boolean;\n ordinal?: number;\n segments: Segment[];\n decision: Decision;\n text?: Record<string, VersionText>;\n offer?: Offer;\n}\n\nexport interface VersionFork {\n from: string;\n playback?: boolean;\n segments?: boolean;\n}\n\nexport interface VersionInput {\n languages?: string[];\n traits?: string[];\n label?: string;\n state?: VersionState;\n publish_at?: string;\n access?: Rule;\n clear_access?: boolean;\n attributes?: Record<string, unknown>;\n text?: Record<string, VersionText>;\n segments?: Segment[];\n ordinal?: number;\n fork?: VersionFork;\n}\n\nexport interface VersionRecord {\n external_id: string;\n id?: string;\n languages: string[];\n traits?: string[];\n label?: string;\n state?: string;\n publish_at?: string;\n published_at?: string;\n deleted_at?: string;\n access?: Rule;\n attributes?: Record<string, unknown>;\n ordinal?: number;\n text?: Record<string, VersionText>;\n segments?: Segment[];\n created_at?: string;\n updated_at?: string;\n}\n\nexport interface VersionRules {\n languages: string;\n traits?: string[];\n hidden?: boolean;\n}\n\nexport interface VersionText {\n title?: string;\n description?: string;\n}\n\nexport interface VideoLimits {\n max_seconds?: number;\n max_fps?: number;\n max_pixels?: number;\n max_work?: number;\n}\n\nexport interface ViewerState {\n favorited: boolean;\n seen: boolean;\n completed: boolean;\n progress: number;\n progress_max: number;\n resume?: string;\n last_view_at?: string;\n}\n\nexport interface VoteRecord {\n poll: Ref;\n option: Ref;\n user_id?: string;\n ip?: string;\n created_at?: string;\n}\n\nexport interface Written {\n count: number;\n}\n"],"mappings":";AAOA,IAAa,IAAiB,UACjB,IAAgB,SAChB,IAAgB"}
1
+ {"version":3,"file":"wire.js","names":[],"sources":["../../../src/client/generated/wire.ts"],"sourcesContent":["// Generated by `go generate ./internal/contract` from ContentKit's route catalog. Do not edit.\n\nimport type { ErrorCode } from \"./error-codes.js\";\n\nexport type { ErrorCode };\n\n// Upload sizes: one PUT up to MAX_SINGLE_PUT bytes, else multipart parts.\nexport const MAX_SINGLE_PUT = 67108864;\nexport const MIN_PART_SIZE = 8388608;\nexport const MAX_PART_SIZE = 16777216;\n\nexport type OpName = \"put\" | \"edit\" | \"move\" | \"rename\" | \"remove\" | \"attach\" | \"copy\" | \"frame\" | \"meta\" | \"regenerate\";\nexport type Access = \"full\" | \"none\";\nexport type ItemState = \"ready\" | \"processing\" | \"failed\";\nexport type EncodePhase = \"queued\" | \"downloading\" | \"probing\" | \"encoding\" | \"muxing\" | \"uploading\" | \"publishing\" | \"images\";\nexport type PollKind = \"multiple_choice\" | \"free_text\";\nexport type ModerationState = \"approved\" | \"held\" | \"rejected\";\nexport type ContentState = \"draft\" | \"published\" | \"deleted\";\nexport type VersionState = \"draft\" | \"published\";\nexport type Listing = \"listed\" | \"members\" | \"unlisted\";\nexport type AliasStatus = \"found\" | \"not_found\" | \"gone\";\n\nexport interface Activity {\n days: ActivityDay[];\n}\n\nexport interface ActivityDay {\n date: string;\n views: number;\n viewers: number;\n users: number;\n}\n\nexport interface AdminComment {\n id: string;\n reply_to_id?: string;\n user_id?: string;\n anon_name?: string;\n language?: string;\n body: string;\n deleted: boolean;\n likes: number;\n dislikes: number;\n mine: number;\n reply_count: number;\n author?: PublicUser;\n moderation?: ModerationState;\n moderation_reason?: string;\n created_at: string;\n updated_at: string;\n tenant_id: string;\n content_kind: string;\n content_id: string;\n content_version_id?: string;\n deleted_at?: string;\n}\n\nexport interface AliasKey {\n source: string;\n kind: string;\n key: string;\n}\n\nexport interface AliasLookup {\n aliases: AliasKey[];\n}\n\nexport interface AliasRecord {\n kind: string;\n key: string;\n locator?: string;\n target: TargetRef;\n}\n\nexport interface AliasResult {\n source: string;\n kind: string;\n key: string;\n status: AliasStatus;\n resolved?: Resolved;\n locator?: string;\n}\n\nexport interface AliasResults {\n aliases: AliasResult[];\n}\n\nexport interface Anchor {\n id: string;\n page: string;\n offset_percent: number;\n duration_seconds?: number;\n}\n\nexport interface Anonymous {\n comments: boolean;\n reactions: boolean;\n votes: boolean;\n}\n\nexport interface AnswerGroup {\n id: string;\n label: string;\n count: number;\n}\n\nexport interface AnswerRecord {\n poll: Ref;\n user_id: string;\n text: string;\n created_at?: string;\n updated_at?: string;\n}\n\nexport interface Attribute {\n type: string;\n values?: string[];\n filterable?: boolean;\n sortable?: boolean;\n}\n\nexport interface Avatar {\n image?: string;\n renditions?: Rendition[];\n}\n\nexport interface BackgroundCue {\n file: string;\n from_page?: string;\n to_page?: string;\n from_anchor?: string;\n to_anchor?: string;\n loop?: boolean;\n layer?: string;\n}\n\nexport interface BanInput {\n reason?: string;\n until?: string;\n}\n\nexport interface BanNotice {\n scope: string;\n reason?: string;\n until?: string;\n}\n\nexport interface BannedWord {\n word: string;\n action: \"mask\" | \"reject\";\n created_by: string;\n created_at: string;\n}\n\nexport interface BannedWordInput {\n word: string;\n action?: \"mask\" | \"reject\";\n}\n\nexport interface Batch {\n source: string;\n preference_floor?: number;\n provision_scopes?: boolean;\n groups?: GroupRecord[];\n contents?: ContentRecord[];\n memberships?: MembershipSet[];\n offers?: OfferRecord[];\n aliases?: AliasRecord[];\n comments?: CommentRecord[];\n reactions?: ReactionRecord[];\n favorites?: FavoriteRecord[];\n polls?: PollRecord[];\n votes?: VoteRecord[];\n answers?: AnswerRecord[];\n signals?: SignalRecord[];\n deletes?: DeleteRecord[];\n}\n\nexport interface Card {\n id: string;\n kind: string;\n code: string;\n path: string;\n title: string;\n language: string;\n cover: PublicRendition[];\n version: CardVersion;\n groups: Record<string, Chip[]>;\n owner?: GroupCard;\n counts: Counts;\n access: CardAccess;\n viewer?: ViewerState;\n published_at?: string;\n attributes: Record<string, unknown>;\n}\n\nexport interface CardAccess {\n allowed: boolean;\n unknown?: boolean;\n sell?: string[];\n requires?: string;\n preview?: Preview;\n free_after?: string;\n offers?: Sale[];\n}\n\nexport interface CardVersion {\n id: string;\n code: string;\n languages: string[];\n traits: string[];\n label?: string;\n pages?: number;\n duration_ms?: number;\n}\n\nexport interface CatalogCover {\n image: string;\n url?: string;\n}\n\nexport interface CatalogText {\n title: string;\n slug?: string;\n description?: string;\n body?: string;\n sort_key?: string;\n aliases?: string[];\n}\n\nexport interface Chip {\n id: string;\n slug: string;\n name: string;\n path?: string;\n}\n\nexport interface Comment {\n id: string;\n reply_to_id?: string;\n user_id?: string;\n anon_name?: string;\n language?: string;\n body: string;\n deleted: boolean;\n likes: number;\n dislikes: number;\n mine: number;\n reply_count: number;\n author?: PublicUser;\n moderation?: ModerationState;\n moderation_reason?: string;\n created_at: string;\n updated_at: string;\n}\n\nexport interface CommentBan {\n user_id: string;\n user?: PublicUser;\n scope: string;\n reason?: string;\n until?: string;\n banned_by: string;\n banned_at: string;\n expired: boolean;\n}\n\nexport interface CommentEdit {\n body: string;\n}\n\nexport interface CommentInput {\n body: string;\n reply_to_id?: string;\n anon_name?: string;\n language?: string;\n}\n\nexport interface CommentRecord {\n external_id: string;\n id?: string;\n target: TargetRef;\n language?: string;\n reply_to?: Ref;\n user_id?: string;\n anon_name?: string;\n body: string;\n deleted_at?: string;\n created_at?: string;\n updated_at?: string;\n}\n\nexport interface CommentStanding {\n can_comment: boolean;\n comments: string;\n locked: boolean;\n ban?: BanNotice;\n anonymous: boolean;\n max_length: number;\n user_id?: string;\n moderate: boolean;\n ban_scopes: (\"global\" | \"owner\")[];\n}\n\nexport interface CommitBody {\n ref: RefBody;\n operation_id: string;\n ops: Op[];\n}\n\nexport interface CommitReply {\n files: FileInfo[];\n}\n\nexport interface CompleteReply {\n blob: string;\n type: string;\n size: number;\n}\n\nexport interface Config {\n anonymous: Anonymous;\n comment_max_length: number;\n}\n\nexport interface Content {\n id: string;\n kind: string;\n code: string;\n path: string;\n language: string;\n title: string;\n slug?: string;\n description?: string;\n body?: string;\n decision: Decision;\n images?: Record<string, string>;\n cover?: CatalogCover;\n state: ContentState;\n listing: Listing;\n comments: string;\n publish_at?: string;\n published_at?: string;\n live: boolean;\n access: Rule;\n restrictions: string[];\n attributes: Record<string, unknown>;\n owner_group_id?: string;\n owner?: GroupCard;\n creators: Creator[];\n groups: GroupSummary[];\n versions: Version[];\n views: number;\n viewer?: ViewerState;\n editor: boolean;\n text?: Record<string, CatalogText>;\n moderation?: ModerationState;\n moderation_reason?: string;\n ever_sold?: boolean;\n offer?: Offer;\n revision: number;\n created_at: string;\n updated_at: string;\n deleted_at?: string;\n}\n\nexport interface ContentFilter {\n group: GroupRef;\n default_visible: boolean;\n labels?: Record<string, string>;\n}\n\nexport interface ContentFilterState {\n group: GroupCard;\n label: string;\n default_visible: boolean;\n visible: boolean;\n overridden: boolean;\n}\n\nexport interface ContentFilters {\n filters: ContentFilterState[];\n include_deleted: boolean;\n}\n\nexport interface ContentFiltersUpdate {\n filters: FilterSetting[];\n include_deleted: boolean;\n}\n\nexport interface ContentGroups {\n kinds?: string[];\n memberships: Membership[];\n}\n\nexport interface ContentInput {\n kind: string;\n text?: Record<string, CatalogText>;\n listing?: Listing;\n comments?: string;\n access?: Rule;\n restrictions?: string[];\n attributes?: Record<string, unknown>;\n owner_group_id?: string;\n creators?: Creator[];\n versions?: VersionInput[];\n}\n\nexport interface ContentMemberships {\n content_id: string;\n memberships: Membership[];\n groups: GroupCard[];\n}\n\nexport interface ContentModel {\n languages: string[];\n tiers: string[];\n kinds: Record<string, Kind>;\n group_kinds: Record<string, GroupKind>;\n content_filters: ContentFilter[];\n signals: boolean;\n avatars?: PresetRule;\n}\n\nexport interface ContentPage {\n contents: Card[];\n next_cursor?: string;\n}\n\nexport interface ContentPatch {\n listing?: Listing;\n comments?: string;\n access?: Rule;\n restrictions?: string[];\n attributes?: Record<string, unknown>;\n creators?: Creator[];\n cover?: string;\n revision?: number;\n}\n\nexport interface ContentRecord {\n external_id: string;\n id?: string;\n kind: string;\n state: string;\n publish_at?: string;\n published_at?: string;\n deleted_at?: string;\n listing?: string;\n comments?: string;\n view_baseline?: number;\n access?: Rule;\n restrictions?: string[];\n attributes?: Record<string, unknown>;\n owner_group?: Ref;\n cover?: string;\n creators?: Creator[];\n text?: Record<string, CatalogText>;\n versions?: VersionRecord[];\n code?: string;\n created_at?: string;\n updated_at?: string;\n}\n\nexport interface ContentRef {\n tenant_id: string;\n content_kind: string;\n content_id: string;\n content_version_id?: string;\n}\n\nexport interface CopyBody {\n ref: RefBody;\n files: CopyRequest[];\n}\n\nexport interface CopyFrom {\n id: string;\n version?: string;\n path: string;\n}\n\nexport interface CopyReply {\n files: StagedCopy[];\n}\n\nexport interface CopyRequest {\n path: string;\n type: string;\n from: string;\n size?: number;\n}\n\nexport interface Count {\n content_kind: string;\n language: string;\n count: number;\n}\n\nexport interface Counts {\n likes: number;\n dislikes: number;\n favorites: number;\n views: number;\n}\n\nexport interface Creator {\n user_id: string;\n role?: string;\n position: number;\n}\n\nexport interface CreatorsInput {\n creators: Creator[];\n}\n\nexport interface Crop {\n x: number;\n y: number;\n w: number;\n h: number;\n}\n\nexport interface DateCount {\n date: string;\n count: number;\n free: number;\n}\n\nexport interface Decision {\n allowed: boolean;\n unknown?: boolean;\n sell?: string[];\n requires?: string;\n preview?: Preview;\n free_after?: string;\n offers?: Sale[];\n}\n\nexport interface DeleteRecord {\n entity: string;\n external_id: string;\n}\n\nexport interface Edge {\n from: string;\n relation: string;\n to: string;\n}\n\nexport interface EdgeInput {\n relation: string;\n to: string;\n}\n\nexport interface EdgeRecord {\n relation: string;\n to: Ref;\n}\n\nexport interface Edit {\n crop?: Crop;\n rotate?: number;\n}\n\nexport interface EncodeProgress {\n phase: EncodePhase;\n queue_position?: number;\n segments_done?: number;\n segments_total?: number;\n percent: number;\n speed?: number;\n eta?: number;\n at: number;\n stalled?: boolean;\n stage?: number;\n stages?: number;\n}\n\nexport interface ErrorDetails {\n width?: number;\n height?: number;\n min_width?: number;\n max_pixels?: number;\n type?: string;\n allowed?: string[];\n size?: number;\n max_bytes?: number;\n frames?: number;\n max_frames?: number;\n seconds?: number;\n max_seconds?: number;\n min_aspect?: number;\n max_aspect?: number;\n max?: number;\n}\n\nexport interface ErrorReply {\n error: string;\n code: ErrorCode;\n retry_after?: number;\n action?: \"comment\" | \"comment_reaction\" | \"favorite\" | \"poll_vote\" | \"reaction\";\n ban?: BanNotice;\n blobs?: string[];\n details?: ErrorDetails;\n}\n\nexport interface ExposureInput {\n render_id: string;\n stage: string;\n revision?: number;\n query_id?: string;\n surface?: string;\n ranker?: string;\n language?: string;\n occurred_at?: string;\n shown: Placement[];\n}\n\nexport interface Exposures {\n session?: string;\n exposures: ExposureInput[];\n}\n\nexport interface ExternalID {\n external_id: string;\n id: string;\n deleted: boolean;\n}\n\nexport interface ExternalIDList {\n entity: string;\n cursor?: string;\n limit?: number;\n}\n\nexport interface ExternalIDPage {\n entity: string;\n ids: ExternalID[];\n next_cursor?: string;\n}\n\nexport interface ExternalIDs {\n lists: ExternalIDPage[];\n}\n\nexport interface ExternalIDsQuery {\n source: string;\n lists: ExternalIDList[];\n}\n\nexport interface Failure {\n of: string;\n message: string;\n code?: ErrorCode;\n details?: ErrorDetails;\n}\n\nexport interface FavoriteRecord {\n target: TargetRef;\n user_id: string;\n created_at?: string;\n}\n\nexport interface FavoriteState {\n favorited: boolean;\n count: number;\n}\n\nexport interface FeedItem {\n id: string;\n reply_to_id?: string;\n user_id?: string;\n anon_name?: string;\n language?: string;\n body: string;\n deleted: boolean;\n likes: number;\n dislikes: number;\n mine: number;\n reply_count: number;\n author?: PublicUser;\n moderation?: ModerationState;\n moderation_reason?: string;\n created_at: string;\n updated_at: string;\n tenant_id: string;\n content_kind: string;\n content_id: string;\n content_version_id?: string;\n}\n\nexport interface FileInfo {\n path: string;\n type: string;\n size?: number;\n w?: number;\n h?: number;\n dur?: number;\n download?: string;\n url?: string;\n locked?: boolean;\n upload?: boolean;\n staged?: boolean;\n from?: string;\n edit?: Edit;\n frame?: Frame;\n meta?: Record<string, unknown>;\n unattached?: boolean;\n pending?: string[];\n failed?: Failure;\n editor_url?: string;\n progress?: EncodeProgress;\n}\n\nexport interface FilterSetting {\n group_id: string;\n visible: boolean;\n}\n\nexport interface ForegroundCue {\n file: string;\n page?: string;\n anchor?: string;\n}\n\nexport interface ForkBody {\n from: string;\n playback?: boolean;\n segments?: boolean;\n}\n\nexport interface Frame {\n t?: number;\n auto?: boolean;\n of?: string;\n}\n\nexport interface Group {\n id: string;\n kind: string;\n slug: string;\n scope_id?: string;\n parent_id?: string;\n state: \"active\" | \"deleted\" | \"merged\";\n merged_into?: string;\n attributes: Record<string, unknown>;\n cover?: GroupsCover;\n avatar?: Avatar;\n owner_user_id?: string;\n auth_scope?: string;\n created_at: string;\n updated_at: string;\n revision: number;\n}\n\nexport interface GroupAccess {\n key: string;\n offer?: Sale;\n member: boolean;\n editor?: boolean;\n unknown?: boolean;\n}\n\nexport interface GroupCard {\n id: string;\n kind: string;\n slug: string;\n scope_id?: string;\n parent_id?: string;\n name: string;\n name_language?: string;\n code?: string;\n path?: string;\n cover?: GroupsCover;\n avatar?: Avatar;\n count: number;\n attributes: Record<string, unknown>;\n access?: GroupAccess;\n created_at: string;\n}\n\nexport interface GroupDetail {\n id: string;\n kind: string;\n slug: string;\n scope_id?: string;\n parent_id?: string;\n state: \"active\" | \"deleted\" | \"merged\";\n merged_into?: string;\n attributes: Record<string, unknown>;\n cover?: GroupsCover;\n avatar?: Avatar;\n owner_user_id?: string;\n auth_scope?: string;\n created_at: string;\n updated_at: string;\n revision: number;\n name: string;\n name_language?: string;\n code?: string;\n path?: string;\n names: Name[];\n text: GroupsText[];\n parent?: GroupCard;\n scope?: GroupCard;\n children: GroupCard[];\n edges: Edge[];\n counts: Count[];\n access?: GroupAccess;\n offer?: Offer;\n}\n\nexport interface GroupInput {\n id?: string;\n kind: string;\n slug: string;\n scope_id?: string;\n parent_id?: string;\n attributes?: Record<string, unknown>;\n cover?: GroupsCover;\n avatar?: Avatar;\n owner_user_id?: string;\n auth_scope?: string;\n names?: Name[];\n text?: GroupsText[];\n}\n\nexport interface GroupJoin {\n group_id: string;\n key: string;\n member: boolean;\n}\n\nexport interface GroupKind {\n route?: string;\n ordered?: boolean;\n parent?: string;\n scoped_by?: string;\n owner?: string;\n max_per_owner?: number;\n uploads?: UploadLimits;\n sellable?: boolean;\n auth_scope?: boolean;\n windows?: boolean;\n version_level?: boolean;\n roles?: string[];\n attributes?: Record<string, Attribute>;\n}\n\nexport interface GroupPage {\n groups: GroupCard[];\n next_cursor?: string;\n}\n\nexport interface GroupRecord {\n external_id: string;\n id?: string;\n kind: string;\n slug: string;\n scope?: Ref;\n parent?: Ref;\n state?: string;\n names?: Name[];\n text?: GroupsText[];\n attributes?: Record<string, unknown>;\n cover_image?: string;\n cover_content?: Ref;\n avatar?: string;\n owner_user_id?: string;\n auth_scope?: string;\n edges?: EdgeRecord[];\n code?: string;\n created_at?: string;\n updated_at?: string;\n}\n\nexport interface GroupRef {\n kind: string;\n slug: string;\n}\n\nexport interface GroupSummary {\n id: string;\n kind: string;\n slug: string;\n name: string;\n code?: string;\n path?: string;\n}\n\nexport interface GroupUpdate {\n slug?: string;\n scope_id?: string;\n parent_id?: string;\n state?: \"active\" | \"deleted\" | \"merged\";\n attributes?: Record<string, unknown>;\n cover?: GroupsCover;\n avatar?: Avatar;\n owner_user_id?: string;\n auth_scope?: string;\n names?: Name[];\n text?: GroupsText[];\n}\n\nexport interface GroupsCover {\n image?: string;\n content_id?: string;\n auto?: boolean;\n renditions?: Rendition[];\n}\n\nexport interface GroupsText {\n language: string;\n description?: string;\n sort_key?: string;\n}\n\nexport interface HeldItem {\n kind: string;\n revision: number;\n id: string;\n ref: ContentRef;\n author_id?: string;\n author?: PublicUser;\n anon_name?: string;\n title?: string;\n body: string;\n reason: string;\n model?: string;\n prompt_version?: string;\n confidence?: number;\n error?: string;\n held_at: string;\n created_at: string;\n}\n\nexport interface HeldPage {\n items: HeldItem[];\n next?: string;\n}\n\nexport interface IPBan {\n network: string;\n reason?: string;\n until?: string;\n banned_by: string;\n banned_at: string;\n expired: boolean;\n}\n\nexport interface IPBanInput {\n network: string;\n reason?: string;\n until?: string;\n}\n\nexport interface ImageInput {\n image?: string;\n}\n\nexport interface InlineImage {\n ref: string;\n url: string | null;\n}\n\nexport interface Input {\n recipient: Recipient;\n origin: Origin;\n kind: string;\n data: unknown;\n created_at: string;\n status?: string;\n action_requested?: boolean;\n severity?: string;\n}\n\nexport interface Interaction {\n content?: Card;\n group?: GroupCard;\n value: number;\n at: string;\n}\n\nexport interface InteractionPage {\n items: Interaction[];\n next_cursor?: string;\n}\n\nexport interface Item {\n id: string;\n recipient: Recipient;\n origin: Origin;\n kind: string;\n data: unknown;\n created_at: string;\n status?: string;\n action_requested?: boolean;\n severity?: string;\n read_at: string | null;\n dismissed_at: string | null;\n resolved_at: string | null;\n updated_at: string;\n revision: number;\n}\n\nexport interface Kind {\n route: string;\n presentation: string;\n versions: VersionRules;\n segments?: boolean;\n groups?: string[];\n attributes?: Record<string, Attribute>;\n sort?: string;\n preview?: Preview;\n draft_ttl?: number;\n}\n\nexport interface LanguageTotal {\n public: number;\n tiers: Record<string, number>;\n}\n\nexport interface LibraryItem {\n content_id: string;\n kind: string;\n title: string;\n language?: string;\n deleted: boolean;\n whole: boolean;\n version_ids: string[];\n}\n\nexport interface LibraryPage {\n items: LibraryItem[];\n next_cursor?: string;\n complete: boolean;\n}\n\nexport interface MemberPage {\n members: Membership[];\n next_cursor?: string;\n}\n\nexport interface MemberRef {\n content_id: string;\n version_id?: string;\n}\n\nexport interface Membership {\n group_id?: string;\n content_id?: string;\n version_id?: string;\n role?: string;\n position?: number;\n active_from?: string;\n active_until?: string;\n state?: \"active\" | \"proposed\";\n}\n\nexport interface MembershipRecord {\n group: Ref;\n version?: Ref;\n role?: string;\n position?: number;\n active_from?: string;\n active_until?: string;\n state?: string;\n}\n\nexport interface MembershipSet {\n content: Ref;\n kinds: string[];\n memberships: MembershipRecord[];\n}\n\nexport interface MergeInput {\n into: string;\n}\n\nexport interface MergeReport {\n from: string;\n into: string;\n memberships_moved: number;\n names_moved: number;\n edges_moved: number;\n groups_moved: number;\n}\n\nexport interface MultipartReply {\n ticket: string;\n min_part_size: number;\n max_part_size: number;\n max_parts: number;\n}\n\nexport interface MyGroup {\n id: string;\n kind: string;\n slug: string;\n scope_id?: string;\n parent_id?: string;\n name: string;\n name_language?: string;\n code?: string;\n path?: string;\n cover?: GroupsCover;\n avatar?: Avatar;\n count: number;\n attributes: Record<string, unknown>;\n access?: GroupAccess;\n created_at: string;\n owner: boolean;\n editor: boolean;\n writer: boolean;\n moderator: boolean;\n member: boolean;\n}\n\nexport interface MyGroupPage {\n groups: MyGroup[];\n next_cursor?: string;\n}\n\nexport interface MyStats {\n completed: Record<string, number>;\n comments: number;\n comment_likes: number;\n comment_dislikes: number;\n}\n\nexport interface Name {\n language: string;\n kind: \"alias\" | \"name\";\n name: string;\n}\n\nexport interface Offer {\n id: string;\n target: Target;\n key: string;\n terms: Terms;\n state: \"active\" | \"withdrawn\";\n synced: boolean;\n product_id?: string;\n price_id?: string;\n last_error?: string;\n revision: number;\n updated_at: string;\n}\n\nexport interface OfferPage {\n offers: Offer[];\n next_cursor?: string;\n}\n\nexport interface OfferRecord {\n target: TargetRef;\n terms: Terms;\n}\n\nexport interface Op {\n op: OpName;\n path?: string;\n blob?: string;\n create_id?: string;\n index?: number;\n meta?: Record<string, unknown>;\n edit?: Edit;\n unattached?: boolean;\n to?: string;\n from?: CopyFrom;\n t?: number;\n auto?: boolean;\n preset?: string;\n force?: boolean;\n takedown?: boolean;\n}\n\nexport interface Order {\n members: MemberRef[];\n}\n\nexport interface OrderInput {\n ids: string[];\n}\n\nexport interface Origin {\n system: string;\n scope: string;\n id: string;\n subject?: string;\n}\n\nexport interface Page {\n items: Item[];\n next_cursor?: string;\n}\n\nexport interface PartBody {\n number: number;\n size: number;\n sha256: string;\n}\n\nexport interface PartReply {\n number: number;\n size?: number;\n sha256?: string;\n request?: RequestReply;\n}\n\nexport interface PartsBody {\n ticket: string;\n parts: PartBody[];\n}\n\nexport interface PartsReply {\n parts: PartReply[];\n}\n\nexport interface Pending {\n status: string;\n}\n\nexport interface Placement {\n content_id: string;\n position: number;\n}\n\nexport interface PlanPage {\n name: string;\n w: number;\n h: number;\n files: Record<string, string>;\n}\n\nexport interface PlanPlayback {\n item: RefBody;\n tracks: PlanTrack[];\n}\n\nexport interface PlanPreview {\n index: number;\n name: string;\n url: string;\n w: number;\n h: number;\n}\n\nexport interface PlanSegment {\n position?: number;\n title?: string;\n start: number;\n end: number;\n released: boolean;\n release_at?: string;\n locked: boolean;\n item?: RefBody;\n pages?: PlanPage[];\n}\n\nexport interface PlanTrack {\n name: string;\n path: string;\n duration?: number;\n}\n\nexport interface Poll {\n id: string;\n kind: PollKind;\n question: string;\n language: string;\n is_active: boolean;\n image_url?: string;\n live_at: string;\n closes_at?: string;\n closed: boolean;\n total_votes: number;\n options: PollOption[];\n voted: boolean;\n my_option?: string;\n answer_count?: number;\n groups?: AnswerGroup[];\n my_answer?: PollAnswer;\n}\n\nexport interface PollAnswer {\n id: string;\n text: string;\n classified: boolean;\n updated_at: string;\n}\n\nexport interface PollAnswerInput {\n text: string;\n}\n\nexport interface PollImage {\n image_url: string | null;\n}\n\nexport interface PollInput {\n kind?: PollKind;\n question: string;\n language: string;\n live_at?: string;\n closes_at?: string;\n options?: PollOptionInput[];\n}\n\nexport interface PollOption {\n id: string;\n label: string;\n image_url?: string;\n position: number;\n vote_count: number;\n}\n\nexport interface PollOptionInput {\n label: string;\n position: number;\n}\n\nexport interface PollOptionPatch {\n label?: string;\n position?: number;\n}\n\nexport interface PollOptionRecord {\n external_id: string;\n id?: string;\n label: string;\n position: number;\n}\n\nexport interface PollRecord {\n external_id: string;\n id?: string;\n kind?: string;\n question: string;\n language?: string;\n active?: boolean;\n live_at?: string;\n closes_at?: string;\n deleted_at?: string;\n created_at?: string;\n updated_at?: string;\n options?: PollOptionRecord[];\n}\n\nexport interface PollUpdate {\n question?: string;\n is_active?: boolean;\n live_at?: string;\n closes_at?: string | null;\n}\n\nexport interface PollVote {\n option_id: string;\n}\n\nexport interface PresetRule {\n kind: string;\n name: string;\n from: string;\n base: string;\n namespace: string;\n to: string;\n widths: number[];\n aspect?: string;\n min_width?: number;\n first?: number;\n default: boolean;\n stable?: boolean;\n}\n\nexport interface PresignBody {\n ref: RefBody;\n path: string;\n type: string;\n size: number;\n sha256: string;\n}\n\nexport interface PresignReply {\n path: string;\n blob: string;\n exists?: boolean;\n put?: RequestReply;\n multipart?: MultipartReply;\n process_on_upload?: boolean;\n}\n\nexport interface Preview {\n pages?: number;\n segments?: number;\n}\n\nexport interface PublicImage {\n from: string;\n preset: string;\n renditions: PublicRendition[];\n}\n\nexport interface PublicRendition {\n url: string;\n w: number;\n h: number;\n}\n\nexport interface PublicUser {\n id: string;\n name?: string;\n username: string;\n avatar?: string;\n avatar_srcset?: string;\n}\n\nexport interface Publication {\n notifications: Input[];\n}\n\nexport interface PublishInput {\n publish_at?: string;\n}\n\nexport interface Published {\n items: Item[];\n}\n\nexport interface RankedContent {\n id: string;\n title: string;\n path?: string;\n value: number;\n}\n\nexport interface ReactionCounts {\n likes: number;\n dislikes: number;\n mine: number;\n}\n\nexport interface ReactionRecord {\n target: TargetRef;\n user_id?: string;\n ip?: string;\n value: number;\n created_at?: string;\n updated_at?: string;\n}\n\nexport interface ReadResult {\n access: Access;\n expires: number;\n meta?: Record<string, unknown>;\n previews?: string[];\n public?: PublicImage[];\n total: number;\n offset: number;\n limit: number;\n hls?: string[];\n state?: ItemState;\n full?: boolean;\n uploads?: UploadRule[];\n files: FileInfo[];\n}\n\nexport interface ReaderDefaults {\n auto_advance_seconds?: number;\n}\n\nexport interface ReaderMeta {\n schema_version: number;\n defaults?: ReaderDefaults;\n anchors?: Anchor[];\n background?: BackgroundCue[];\n foreground?: ForegroundCue[];\n}\n\nexport interface ReaderPlan {\n content_id: string;\n version_id: string;\n publication_id: string;\n pages: number;\n segments: PlanSegment[];\n previews: PlanPreview[];\n playback?: PlanPlayback;\n reader?: ReaderMeta;\n}\n\nexport interface RecentSearch {\n query: string;\n results: number;\n searched_at: string;\n}\n\nexport interface RecentSearches {\n searches: RecentSearch[];\n}\n\nexport interface Recipient {\n audience: string;\n id: string;\n}\n\nexport interface RecordFailure {\n section: string;\n index: number;\n external_id?: string;\n code: string;\n error: string;\n}\n\nexport interface Ref {\n id?: string;\n external_id?: string;\n}\n\nexport interface RefBody {\n kind: string;\n id: string;\n version?: string;\n}\n\nexport interface ReleaseDates {\n dates: DateCount[];\n next_cursor?: string;\n}\n\nexport interface Rendition {\n url: string;\n w: number;\n h: number;\n}\n\nexport interface RequestReply {\n method: string;\n url: string;\n headers: Record<string, string>;\n expires: string;\n}\n\nexport interface ResolveInput {\n decision: \"approve\" | \"reject\" | \"review\";\n revision: number;\n reason?: string;\n}\n\nexport interface Resolved {\n tenant_id: string;\n content_kind: string;\n content_id: string;\n content_version_id?: string;\n code: string;\n slug: string;\n slugs?: Record<string, string>;\n path: string;\n version_id?: string;\n}\n\nexport interface Restored {\n restored: boolean;\n}\n\nexport interface Result {\n written: Record<string, number>;\n skipped?: Record<string, number>;\n failed: RecordFailure[];\n ids: Record<string, Record<string, string>>;\n}\n\nexport interface ReviewOutcome {\n decision: \"approve\" | \"reject\" | \"review\";\n}\n\nexport interface Rule {\n level: \"group\" | \"group_ppv\" | \"ppv\" | \"public\" | \"tier\";\n tiers?: string[];\n group_id?: string;\n preview?: Preview;\n free_after?: string;\n}\n\nexport interface Sale {\n key: string;\n target: Target;\n terms: Terms;\n price_id?: string;\n}\n\nexport interface Segment {\n position: number;\n title?: string;\n start: number;\n end: number;\n release_at?: string;\n released: boolean;\n}\n\nexport interface SegmentsInput {\n segments: Segment[];\n}\n\nexport interface SignalInput {\n content_id: string;\n version_id?: string;\n type: string;\n event_id: string;\n revision?: number;\n occurred_at: string;\n duration_s?: number;\n progress?: number;\n resume?: string;\n render_id?: string;\n surface?: string;\n position?: number;\n}\n\nexport interface SignalRecord {\n target: TargetRef;\n user_id?: string;\n anon_key?: string;\n type: string;\n event_id: string;\n revision?: number;\n occurred_at: string;\n duration_s?: number;\n progress?: number;\n resume?: string;\n}\n\nexport interface Signals {\n session?: string;\n signals: SignalInput[];\n}\n\nexport interface SitemapAlias {\n language: string;\n path: string;\n}\n\nexport interface SitemapEntry {\n id: string;\n code: string;\n path: string;\n lastmod: string;\n languages: SitemapAlias[];\n}\n\nexport interface SitemapPage {\n entries: SitemapEntry[];\n next_cursor?: string;\n}\n\nexport interface StagedCopy {\n path: string;\n blob: string;\n size: number;\n}\n\nexport interface Target {\n kind: \"content\" | \"group\" | \"version\";\n id: string;\n content_id?: string;\n}\n\nexport interface TargetRef {\n content?: Ref;\n version?: Ref;\n group?: Ref;\n comment?: Ref;\n}\n\nexport interface Terms {\n amount: string;\n currency: string;\n access_hours?: number;\n renew_hours?: number;\n}\n\nexport interface TicketBody {\n ticket: string;\n}\n\nexport interface TopContents {\n contents: RankedContent[];\n}\n\nexport interface Total {\n public: number;\n tiers: Record<string, number>;\n languages: Record<string, LanguageTotal>;\n}\n\nexport interface Totals {\n kinds: Record<string, Total>;\n}\n\nexport interface Typeahead {\n contents: Card[];\n groups: GroupCard[];\n}\n\nexport interface UploadLimits {\n files_per_hour?: number;\n bytes_per_day?: number;\n quota_bytes?: number;\n}\n\nexport interface UploadRule {\n path: string;\n types: string[];\n max_bytes: number;\n max?: number;\n named?: boolean;\n frames?: string;\n aspect?: string;\n min_width?: number;\n video?: VideoLimits;\n min_aspect?: number;\n max_aspect?: number;\n}\n\nexport interface Version {\n id: string;\n code: string;\n position: number;\n languages: string[];\n traits: string[];\n label?: string;\n title?: string;\n description?: string;\n state?: ContentState;\n publish_at?: string;\n published_at?: string;\n live: boolean;\n access?: Rule;\n attributes: Record<string, unknown>;\n pages?: number;\n duration_ms?: number;\n width?: number;\n height?: number;\n has_audio: boolean;\n ordinal?: number;\n segments: Segment[];\n decision: Decision;\n text?: Record<string, VersionText>;\n offer?: Offer;\n viewer?: ViewerState;\n}\n\nexport interface VersionFork {\n from: string;\n playback?: boolean;\n segments?: boolean;\n}\n\nexport interface VersionInput {\n languages?: string[];\n traits?: string[];\n label?: string;\n state?: VersionState;\n publish_at?: string;\n access?: Rule;\n clear_access?: boolean;\n attributes?: Record<string, unknown>;\n text?: Record<string, VersionText>;\n segments?: Segment[];\n ordinal?: number;\n fork?: VersionFork;\n}\n\nexport interface VersionRecord {\n external_id: string;\n id?: string;\n code?: string;\n languages: string[];\n traits?: string[];\n label?: string;\n state?: string;\n publish_at?: string;\n published_at?: string;\n deleted_at?: string;\n access?: Rule;\n attributes?: Record<string, unknown>;\n ordinal?: number;\n text?: Record<string, VersionText>;\n segments?: Segment[];\n created_at?: string;\n updated_at?: string;\n}\n\nexport interface VersionRules {\n languages: string;\n traits?: string[];\n hidden?: boolean;\n}\n\nexport interface VersionText {\n title?: string;\n description?: string;\n}\n\nexport interface VideoLimits {\n max_seconds?: number;\n max_fps?: number;\n max_pixels?: number;\n max_work?: number;\n}\n\nexport interface ViewerState {\n favorited: boolean;\n seen: boolean;\n completed: boolean;\n progress: number;\n progress_max: number;\n resume?: string;\n last_view_at?: string;\n}\n\nexport interface VoteRecord {\n poll: Ref;\n option: Ref;\n user_id?: string;\n ip?: string;\n created_at?: string;\n}\n\nexport interface Written {\n count: number;\n}\n"],"mappings":";AAOA,IAAa,IAAiB,UACjB,IAAgB,SAChB,IAAgB"}
@@ -9,11 +9,13 @@ export { FavoritesClient, type MineQuery } from './content/favorites.js';
9
9
  export { PollsClient, type PollAdminQuery, type PollQuery, type PollState } from './content/polls.js';
10
10
  export { BansClient } from './content/bans.js';
11
11
  export { ModerationClient, type HeldQuery } from './content/moderation.js';
12
- export { reactionVerb, sameTarget, type BanScope, type ContentChange, type Decision, type HeldKind, type Reaction, type Sort, type Target } from './content/types.js';
12
+ export { reactionVerb, sameTarget, type BanScope, type ContentChange, type Decision, type HeldKind, type Reaction, type RuleSet, type Sort, type Target } from './content/types.js';
13
13
  export { GroupsClient, type GroupImageSlot, type GroupQuery, type MemberQuery, type MyGroupQuery } from './groups.js';
14
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
15
  export { VersionsClient, isPending } from './versions.js';
16
+ export { emptyReaderMeta, matchAudio, readerMetaProblems, readerNames, type AudioMatch, type ReaderMetaProblem, type ReaderNames } from './reader-meta.js';
16
17
  export { OffersClient } from './offers.js';
18
+ export { StatsClient, type ActivityQuery, type TopQuery } from './stats.js';
17
19
  export { Beacons, eventId, readerSession, type Attribution, type BeaconOptions, type ViewSession } from './beacons.js';
18
20
  export { LETTERS, attributeInput, fold, fromLocalInput, humanize, letterOf, parseAttribute, parseAttributes, slugify, toLocalInput } from './group-fields.js';
19
21
  export { CodesClient } from './codes.js';
@@ -22,7 +24,7 @@ export { call, routeURL, type CallOptions, type Method, type PageQuery, type Pat
22
24
  export { MediaApi, type ReadOptions } from './media/api.js';
23
25
  export { MediaClient, backoff, samePath, stem, type CommitOptions, type CommitSource, type ContentFolders, type MediaOptions, type NamedOptions, type NamedUpload, type PlannedPart, type Progress, type PutOptions, type UploadOptions, type UploadState, type UploadedFile, type WaitOptions, } from './media/client.js';
24
26
  export { ItemUploadQueue, type ItemStatus, type QueueItem, type QueueOptions, type QueueSnapshot } from './media/queue.js';
25
- export { acceptOf, defaultImage, filesFor, isPattern, ratioLabel, ruleDir, ruleFor, screenFiles, uniqueName, uploadRules, withMediaType, type Screened, } from './media/rules.js';
27
+ export { acceptOf, defaultImage, stableImage, filesFor, isPattern, ratioLabel, ruleDir, ruleFor, screenFiles, uniqueName, uploadRules, withMediaType, type Screened, } from './media/rules.js';
26
28
  export { READ_CHUNK, chunkOffsets, isProcessing, mergeReads, processing } from './media/windows.js';
27
29
  export { defaultTransport, fetchTransport, xhrTransport, type Transport } from './media/transport.js';
28
30
  export { checkRef, isContentId, itemKey, sameItem } from './media/ref.js';
@@ -89,11 +89,13 @@ export declare class ItemUploadQueue {
89
89
  * Commits every uploaded item, in queue order, after the item's uploads:
90
90
  * unattached ones are attached (no reprocessing), the rest put. With head,
91
91
  * only the uploaded items before the first one still uploading or failed
92
- * (a draft committing files as they finish). Returns the item's attached
93
- * uploads in order.
92
+ * (a draft committing files as they finish). attach adds unattached
93
+ * uploads the queue does not hold (an earlier visit's) after them. Returns
94
+ * the item's attached uploads in order.
94
95
  */
95
96
  commit(signal?: AbortSignal, o?: {
96
97
  head?: boolean;
98
+ attach?: readonly string[];
97
99
  }): Promise<FileInfo[]>;
98
100
  /** Aborts running uploads and stops polling; call on unmount. */
99
101
  dispose(): void;
@@ -80,9 +80,9 @@ var a = 2 ** 31 - 1, o = class {
80
80
  let e = n.findIndex((e) => e.status !== "uploaded" && e.status !== "committed");
81
81
  e >= 0 && (n = n.slice(0, e));
82
82
  }
83
- let r = n.filter((e) => e.status === "uploaded");
84
- if (r.length === 0) return [];
85
- let i = r.flatMap((e) => {
83
+ let r = n.filter((e) => e.status === "uploaded"), i = t.attach ?? [];
84
+ if (r.length === 0 && i.length === 0) return [];
85
+ let o = r.flatMap((e) => {
86
86
  let t = {
87
87
  op: "attach",
88
88
  path: e.path,
@@ -99,20 +99,26 @@ var a = 2 ** 31 - 1, o = class {
99
99
  ...e.meta ? { meta: e.meta } : {}
100
100
  };
101
101
  return e.result.processOnUpload ? [n, t] : [n];
102
- }), o = Object.fromEntries(r.map((e) => [e.result.blob, {
102
+ });
103
+ for (let e of i) o.push({
104
+ op: "attach",
105
+ path: e,
106
+ index: a
107
+ });
108
+ let s = Object.fromEntries(r.map((e) => [e.result.blob, {
103
109
  file: e.file,
104
110
  type: e.result.type
105
- }])), s = JSON.stringify(i);
106
- this.batch?.input !== s && (this.batch = {
107
- input: s,
111
+ }])), c = JSON.stringify(o);
112
+ this.batch?.input !== c && (this.batch = {
113
+ input: c,
108
114
  operationID: crypto.randomUUID()
109
115
  });
110
- let c = this.batch, l = await this.client.commit(this.o.ref, i, {
116
+ let l = this.batch, u = await this.client.commit(this.o.ref, o, {
111
117
  signal: e,
112
- sources: o,
113
- operationID: c.operationID,
118
+ sources: s,
119
+ operationID: l.operationID,
114
120
  onState: (e) => {
115
- c.input = JSON.stringify(e.ops), c.operationID = e.operation_id;
121
+ l.input = JSON.stringify(e.ops), l.operationID = e.operation_id;
116
122
  let t = !1;
117
123
  for (let n of e.ops) {
118
124
  if (n.op !== "put" || !n.blob) continue;
@@ -131,7 +137,7 @@ var a = 2 ** 31 - 1, o = class {
131
137
  status: "committed",
132
138
  unattached: !1
133
139
  });
134
- return this.changed(), l.filter((e) => !e.unattached);
140
+ return this.changed(), u.filter((e) => !e.unattached);
135
141
  }
136
142
  dispose() {
137
143
  this.started = !1, this.disposed = !0;
@@ -1 +1 @@
1
- {"version":3,"file":"queue.js","names":[],"sources":["../../../src/client/media/queue.ts"],"sourcesContent":["import { ContentKitError, toContentKitError } from \"../errors.js\";\nimport type { FileInfo, Op, RefBody } from \"../generated/wire.js\";\nimport { fill } from \"../public.js\";\nimport { samePath, stem, type MediaClient, type Progress, type UploadedFile, type UploadState } from \"./client.js\";\n\nexport type ItemStatus = \"queued\" | \"uploading\" | \"uploaded\" | \"failed\" | \"committed\";\n\nexport interface QueueItem {\n readonly id: string;\n readonly file: File;\n /** The upload path the commit puts; the server's canonical one once uploaded. */\n path: string;\n meta?: Record<string, unknown>;\n status: ItemStatus;\n progress?: Progress;\n error?: ContentKitError;\n result?: UploadedFile;\n /** Resumable multipart state while uploading or after a failure. */\n state?: UploadState;\n /**\n * Committed unattached for processing on upload (the host's\n * ProcessOnUpload): commit() attaches it, remove() discards it.\n */\n unattached?: boolean;\n /** The server's view of an unattached upload while it processes (editor read): dims, pending, failed, progress. */\n processing?: FileInfo;\n /** Processing finished (placed and outputs written, or failed). */\n processed?: boolean;\n}\n\nexport interface QueueSnapshot {\n readonly items: readonly QueueItem[];\n /** A rate, quota or permission refusal: no new uploads start until start(). */\n readonly blocked?: ContentKitError;\n /** Every item is uploaded (or committed) and none is in flight. */\n readonly ready: boolean;\n}\n\nexport interface QueueOptions {\n ref: RefBody;\n /** Each file's upload path: a template whose {name} is the file's name, e.g. \"originals/{name}\". */\n path: string;\n /** Files uploading at once. Default 2. */\n concurrency?: number;\n /** Start uploads as files are added. Default true. */\n autoStart?: boolean;\n /** Called with every item state change, to persist resumable state. */\n onState?: (item: QueueItem, state: UploadState | null) => void;\n /** Milliseconds between processing polls of unattached uploads. Default 2000. */\n pollInterval?: number;\n}\n\n/** An op index past the end of the list: the upload is appended. */\nconst END = 2 ** 31 - 1;\n\n/**\n * An ordered file queue for one item: uploads in the background, keeps the\n * order the user arranges, and commits the uploads as puts appended in that\n * order. Framework-free; the React hooks wrap it.\n */\nexport class ItemUploadQueue {\n private items: QueueItem[] = [];\n private blocked?: ContentKitError;\n private running = new Map<string, AbortController>();\n private listeners = new Set<() => void>();\n private snap!: QueueSnapshot;\n private started: boolean;\n /** In-flight unattached commits, by item id. */\n private staging = new Map<string, Promise<FileInfo | undefined>>();\n private poll?: ReturnType<typeof setTimeout>;\n private disposed = false;\n private batch?: { input: string; operationID: string };\n\n constructor(\n private readonly client: MediaClient,\n private readonly o: QueueOptions,\n ) {\n this.started = o.autoStart ?? true;\n this.publish();\n }\n\n subscribe = (fn: () => void): (() => void) => {\n this.listeners.add(fn);\n return () => this.listeners.delete(fn);\n };\n\n getSnapshot = (): QueueSnapshot => this.snap;\n\n /** Queues files; state restores a multipart upload saved through onState before a reload. */\n add(\n files: Iterable<File>,\n opts: { path?: (f: File) => string; state?: (f: File) => UploadState | undefined } = {},\n ): QueueItem[] {\n const added = [...files].map(\n (file): QueueItem => ({\n id: crypto.randomUUID(),\n file,\n path: opts.path?.(file) ?? fill(this.o.path, { name: file.name }),\n status: \"queued\",\n state: opts.state?.(file),\n }),\n );\n this.items.push(...added);\n this.changed();\n return added;\n }\n\n /** Moves an item to index (before commit). */\n move(id: string, index: number): void {\n const from = this.items.findIndex((i) => i.id === id);\n if (from < 0) return;\n const [item] = this.items.splice(from, 1);\n this.items.splice(Math.max(0, Math.min(index, this.items.length)), 0, item!);\n this.changed();\n }\n\n update(id: string, patch: Pick<Partial<QueueItem>, \"path\" | \"meta\">): void {\n const item = this.find(id);\n if (item?.unattached && patch.path && patch.path !== item.path) {\n void this.client.commit(this.o.ref, [{ op: \"rename\", path: item.path, to: patch.path }]).then(\n (files) => {\n const f = files.find((x) => samePath(x.path, stem(patch.path!)));\n if (f) this.patch(id, { path: f.path });\n },\n () => {},\n );\n }\n this.patch(id, patch);\n }\n\n /**\n * Removes an item, discarding its multipart upload if one is open, or its\n * unattached upload (the commit removes it and its outputs).\n */\n remove(id: string): void {\n const item = this.find(id);\n if (!item) return;\n this.running.get(id)?.abort();\n if (item.state) void this.client.discard(item.state).catch(() => {});\n const staged = this.staging.get(id);\n if (item.unattached || staged) {\n void (staged ?? Promise.resolve())\n .then((file) => {\n if (file || item.unattached) {\n return this.client.commit(this.o.ref, [{ op: \"remove\", path: file?.path ?? item.path }]);\n }\n })\n .catch(() => {});\n }\n this.o.onState?.(item, null);\n this.items = this.items.filter((i) => i.id !== id);\n this.changed();\n }\n\n /** Re-queues a failed item; a multipart upload resumes from its state. */\n retry(id: string): void {\n this.patch(id, { status: \"queued\", error: undefined });\n }\n\n /** Starts uploading (autoStart false) or lifts a block after a refusal. */\n start(): void {\n this.started = true;\n this.blocked = undefined;\n this.changed();\n }\n\n /** Stops starting uploads and pauses running ones (they stay resumable). */\n pause(): void {\n this.started = false;\n for (const c of this.running.values()) c.abort();\n this.changed();\n }\n\n /**\n * Commits every uploaded item, in queue order, after the item's uploads:\n * unattached ones are attached (no reprocessing), the rest put. With head,\n * only the uploaded items before the first one still uploading or failed\n * (a draft committing files as they finish). Returns the item's attached\n * uploads in order.\n */\n async commit(signal?: AbortSignal, o: { head?: boolean } = {}): Promise<FileInfo[]> {\n await Promise.all(this.staging.values());\n let items = this.items;\n if (o.head) {\n // Only the uploaded head of the queue, so files land in the order added.\n const end = items.findIndex((i) => i.status !== \"uploaded\" && i.status !== \"committed\");\n if (end >= 0) items = items.slice(0, end);\n }\n const ready = items.filter((i) => i.status === \"uploaded\");\n if (ready.length === 0) return [];\n const ops = ready.flatMap((i): Op[] => {\n const attach: Op = { op: \"attach\", path: i.path, index: END, ...(i.meta ? { meta: i.meta } : {}) };\n if (i.unattached) return [attach];\n const put: Op = { op: \"put\", path: i.path, blob: i.result!.blob, create_id: i.id, index: END, ...(i.meta ? { meta: i.meta } : {}) };\n // A lost staging response may hide a successful unattached create.\n return i.result!.processOnUpload ? [put, attach] : [put];\n });\n const sources = Object.fromEntries(ready.map((i) => [i.result!.blob, { file: i.file, type: i.result!.type }]));\n const input = JSON.stringify(ops);\n if (this.batch?.input !== input) this.batch = { input, operationID: crypto.randomUUID() };\n const batch = this.batch;\n const files = await this.client.commit(this.o.ref, ops, {\n signal,\n sources,\n operationID: batch.operationID,\n onState: (body) => {\n batch.input = JSON.stringify(body.ops);\n batch.operationID = body.operation_id;\n let changed = false;\n for (const op of body.ops) {\n if (op.op !== \"put\" || !op.blob) continue;\n const original = ready.find((i) => i.id === op.create_id);\n if (!original?.result) continue;\n const current = this.find(original.id);\n if (current?.result && current.result.blob === original.result.blob && current.result.blob !== op.blob) {\n changed = this.set(current.id, { result: { ...current.result, blob: op.blob } }) || changed;\n }\n }\n if (changed) this.changed();\n },\n });\n for (const i of ready) this.set(i.id, { status: \"committed\", unattached: false });\n this.changed();\n return files.filter((f) => !f.unattached);\n }\n\n /** Aborts running uploads and stops polling; call on unmount. */\n dispose(): void {\n this.started = false;\n this.disposed = true;\n for (const c of this.running.values()) c.abort();\n clearTimeout(this.poll);\n this.listeners.clear();\n }\n\n /** Commits an uploaded file unattached, so the host processes it now. */\n private stage(id: string): void {\n const item = this.find(id);\n if (!item?.result) return;\n const { blob, type } = item.result;\n const op: Op = { op: \"put\", path: item.path, blob, create_id: item.id, unattached: true, ...(item.meta ? { meta: item.meta } : {}) };\n const p = this.client\n .commit(this.o.ref, [op], { sources: { [blob]: { file: item.file, type } } })\n .then(\n (files) => {\n const f = files.find((x) => x.unattached && samePath(x.path, stem(item.path)));\n if (f && this.find(id)) this.patch(id, { unattached: true, path: f.path });\n this.schedulePoll();\n return f;\n },\n (error: unknown) => {\n if (error instanceof ContentKitError && error.code === \"conflict\") {\n this.patch(id, { status: \"failed\", error });\n }\n // Other failures leave it uploaded; commit() retries the same create.\n return undefined;\n },\n )\n .finally(() => this.staging.delete(id));\n this.staging.set(id, p);\n }\n\n private schedulePoll(): void {\n if (this.poll || this.disposed) return;\n this.poll = setTimeout(() => {\n this.poll = undefined;\n void this.refresh().finally(() => {\n if (this.items.some((i) => i.unattached && !i.processed)) this.schedulePoll();\n });\n }, this.o.pollInterval ?? 2000);\n }\n\n /** Reads the processing state of unattached uploads still processing. */\n private async refresh(): Promise<void> {\n const pending = this.items.filter((i) => i.unattached && !i.processed);\n if (pending.length === 0) return;\n let files: FileInfo[];\n try {\n files = (await this.client.read(this.o.ref, { editor: true })).files;\n } catch {\n return;\n }\n const byPath = new Map(files.filter((f) => f.upload).map((f) => [f.path, f]));\n let changed = false;\n for (const i of pending) {\n const f = byPath.get(i.path);\n if (!f) continue;\n changed = this.set(i.id, { processing: f, processed: !!f.failed || (!f.pending?.length && !f.staged) }) || changed;\n }\n if (changed) this.changed();\n }\n\n private find(id: string): QueueItem | undefined {\n return this.items.find((i) => i.id === id);\n }\n\n private patch(id: string, patch: Partial<QueueItem>): void {\n if (this.set(id, patch)) this.changed();\n }\n\n private set(id: string, patch: Partial<QueueItem>): boolean {\n const i = this.items.findIndex((x) => x.id === id);\n if (i >= 0) this.items[i] = { ...this.items[i]!, ...patch };\n return i >= 0;\n }\n\n private changed(): void {\n this.pump();\n this.publish();\n for (const fn of this.listeners) fn();\n }\n\n private publish(): void {\n this.snap = {\n items: [...this.items],\n blocked: this.blocked,\n ready:\n this.running.size === 0 &&\n this.items.length > 0 &&\n this.items.every((i) => i.status === \"uploaded\" || i.status === \"committed\"),\n };\n }\n\n private pump(): void {\n if (!this.started || this.blocked) return;\n const limit = this.o.concurrency ?? 2;\n for (const item of this.items) {\n if (this.running.size >= limit) return;\n if (item.status === \"queued\" && !this.running.has(item.id)) this.run(item.id);\n }\n }\n\n private run(id: string): void {\n const item = this.find(id)!;\n const ctl = new AbortController();\n this.running.set(id, ctl);\n this.set(id, { status: \"uploading\", progress: undefined, error: undefined });\n this.client\n .upload(item.file, {\n ref: this.o.ref,\n path: item.path,\n signal: ctl.signal,\n resume: item.state,\n onProgress: (progress) => this.patch(id, { progress }),\n onState: (state) => {\n this.patch(id, { state: state ?? undefined });\n const cur = this.find(id);\n if (cur) this.o.onState?.(cur, state);\n },\n })\n .then(\n (result) => {\n this.running.delete(id);\n this.patch(id, { status: \"uploaded\", result, path: result.path, state: undefined });\n if (result.processOnUpload) this.stage(id);\n },\n (e: unknown) => {\n this.running.delete(id);\n const error = toContentKitError(e);\n if (error.code === \"aborted\") {\n this.patch(id, { status: \"queued\" });\n return;\n }\n if (error.blocksQueue) {\n // Refused for every file: keep it queued behind the block.\n this.blocked = error;\n this.patch(id, { status: \"queued\", error });\n return;\n }\n this.patch(id, { status: \"failed\", error });\n },\n );\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"queue.js","names":[],"sources":["../../../src/client/media/queue.ts"],"sourcesContent":["import { ContentKitError, toContentKitError } from \"../errors.js\";\nimport type { FileInfo, Op, RefBody } from \"../generated/wire.js\";\nimport { fill } from \"../public.js\";\nimport { samePath, stem, type MediaClient, type Progress, type UploadedFile, type UploadState } from \"./client.js\";\n\nexport type ItemStatus = \"queued\" | \"uploading\" | \"uploaded\" | \"failed\" | \"committed\";\n\nexport interface QueueItem {\n readonly id: string;\n readonly file: File;\n /** The upload path the commit puts; the server's canonical one once uploaded. */\n path: string;\n meta?: Record<string, unknown>;\n status: ItemStatus;\n progress?: Progress;\n error?: ContentKitError;\n result?: UploadedFile;\n /** Resumable multipart state while uploading or after a failure. */\n state?: UploadState;\n /**\n * Committed unattached for processing on upload (the host's\n * ProcessOnUpload): commit() attaches it, remove() discards it.\n */\n unattached?: boolean;\n /** The server's view of an unattached upload while it processes (editor read): dims, pending, failed, progress. */\n processing?: FileInfo;\n /** Processing finished (placed and outputs written, or failed). */\n processed?: boolean;\n}\n\nexport interface QueueSnapshot {\n readonly items: readonly QueueItem[];\n /** A rate, quota or permission refusal: no new uploads start until start(). */\n readonly blocked?: ContentKitError;\n /** Every item is uploaded (or committed) and none is in flight. */\n readonly ready: boolean;\n}\n\nexport interface QueueOptions {\n ref: RefBody;\n /** Each file's upload path: a template whose {name} is the file's name, e.g. \"originals/{name}\". */\n path: string;\n /** Files uploading at once. Default 2. */\n concurrency?: number;\n /** Start uploads as files are added. Default true. */\n autoStart?: boolean;\n /** Called with every item state change, to persist resumable state. */\n onState?: (item: QueueItem, state: UploadState | null) => void;\n /** Milliseconds between processing polls of unattached uploads. Default 2000. */\n pollInterval?: number;\n}\n\n/** An op index past the end of the list: the upload is appended. */\nconst END = 2 ** 31 - 1;\n\n/**\n * An ordered file queue for one item: uploads in the background, keeps the\n * order the user arranges, and commits the uploads as puts appended in that\n * order. Framework-free; the React hooks wrap it.\n */\nexport class ItemUploadQueue {\n private items: QueueItem[] = [];\n private blocked?: ContentKitError;\n private running = new Map<string, AbortController>();\n private listeners = new Set<() => void>();\n private snap!: QueueSnapshot;\n private started: boolean;\n /** In-flight unattached commits, by item id. */\n private staging = new Map<string, Promise<FileInfo | undefined>>();\n private poll?: ReturnType<typeof setTimeout>;\n private disposed = false;\n private batch?: { input: string; operationID: string };\n\n constructor(\n private readonly client: MediaClient,\n private readonly o: QueueOptions,\n ) {\n this.started = o.autoStart ?? true;\n this.publish();\n }\n\n subscribe = (fn: () => void): (() => void) => {\n this.listeners.add(fn);\n return () => this.listeners.delete(fn);\n };\n\n getSnapshot = (): QueueSnapshot => this.snap;\n\n /** Queues files; state restores a multipart upload saved through onState before a reload. */\n add(\n files: Iterable<File>,\n opts: { path?: (f: File) => string; state?: (f: File) => UploadState | undefined } = {},\n ): QueueItem[] {\n const added = [...files].map(\n (file): QueueItem => ({\n id: crypto.randomUUID(),\n file,\n path: opts.path?.(file) ?? fill(this.o.path, { name: file.name }),\n status: \"queued\",\n state: opts.state?.(file),\n }),\n );\n this.items.push(...added);\n this.changed();\n return added;\n }\n\n /** Moves an item to index (before commit). */\n move(id: string, index: number): void {\n const from = this.items.findIndex((i) => i.id === id);\n if (from < 0) return;\n const [item] = this.items.splice(from, 1);\n this.items.splice(Math.max(0, Math.min(index, this.items.length)), 0, item!);\n this.changed();\n }\n\n update(id: string, patch: Pick<Partial<QueueItem>, \"path\" | \"meta\">): void {\n const item = this.find(id);\n if (item?.unattached && patch.path && patch.path !== item.path) {\n void this.client.commit(this.o.ref, [{ op: \"rename\", path: item.path, to: patch.path }]).then(\n (files) => {\n const f = files.find((x) => samePath(x.path, stem(patch.path!)));\n if (f) this.patch(id, { path: f.path });\n },\n () => {},\n );\n }\n this.patch(id, patch);\n }\n\n /**\n * Removes an item, discarding its multipart upload if one is open, or its\n * unattached upload (the commit removes it and its outputs).\n */\n remove(id: string): void {\n const item = this.find(id);\n if (!item) return;\n this.running.get(id)?.abort();\n if (item.state) void this.client.discard(item.state).catch(() => {});\n const staged = this.staging.get(id);\n if (item.unattached || staged) {\n void (staged ?? Promise.resolve())\n .then((file) => {\n if (file || item.unattached) {\n return this.client.commit(this.o.ref, [{ op: \"remove\", path: file?.path ?? item.path }]);\n }\n })\n .catch(() => {});\n }\n this.o.onState?.(item, null);\n this.items = this.items.filter((i) => i.id !== id);\n this.changed();\n }\n\n /** Re-queues a failed item; a multipart upload resumes from its state. */\n retry(id: string): void {\n this.patch(id, { status: \"queued\", error: undefined });\n }\n\n /** Starts uploading (autoStart false) or lifts a block after a refusal. */\n start(): void {\n this.started = true;\n this.blocked = undefined;\n this.changed();\n }\n\n /** Stops starting uploads and pauses running ones (they stay resumable). */\n pause(): void {\n this.started = false;\n for (const c of this.running.values()) c.abort();\n this.changed();\n }\n\n /**\n * Commits every uploaded item, in queue order, after the item's uploads:\n * unattached ones are attached (no reprocessing), the rest put. With head,\n * only the uploaded items before the first one still uploading or failed\n * (a draft committing files as they finish). attach adds unattached\n * uploads the queue does not hold (an earlier visit's) after them. Returns\n * the item's attached uploads in order.\n */\n async commit(signal?: AbortSignal, o: { head?: boolean; attach?: readonly string[] } = {}): Promise<FileInfo[]> {\n await Promise.all(this.staging.values());\n let items = this.items;\n if (o.head) {\n // Only the uploaded head of the queue, so files land in the order added.\n const end = items.findIndex((i) => i.status !== \"uploaded\" && i.status !== \"committed\");\n if (end >= 0) items = items.slice(0, end);\n }\n const ready = items.filter((i) => i.status === \"uploaded\");\n const strays = o.attach ?? [];\n if (ready.length === 0 && strays.length === 0) return [];\n const ops = ready.flatMap((i): Op[] => {\n const attach: Op = { op: \"attach\", path: i.path, index: END, ...(i.meta ? { meta: i.meta } : {}) };\n if (i.unattached) return [attach];\n const put: Op = { op: \"put\", path: i.path, blob: i.result!.blob, create_id: i.id, index: END, ...(i.meta ? { meta: i.meta } : {}) };\n // A lost staging response may hide a successful unattached create.\n return i.result!.processOnUpload ? [put, attach] : [put];\n });\n for (const path of strays) ops.push({ op: \"attach\", path, index: END });\n const sources = Object.fromEntries(ready.map((i) => [i.result!.blob, { file: i.file, type: i.result!.type }]));\n const input = JSON.stringify(ops);\n if (this.batch?.input !== input) this.batch = { input, operationID: crypto.randomUUID() };\n const batch = this.batch;\n const files = await this.client.commit(this.o.ref, ops, {\n signal,\n sources,\n operationID: batch.operationID,\n onState: (body) => {\n batch.input = JSON.stringify(body.ops);\n batch.operationID = body.operation_id;\n let changed = false;\n for (const op of body.ops) {\n if (op.op !== \"put\" || !op.blob) continue;\n const original = ready.find((i) => i.id === op.create_id);\n if (!original?.result) continue;\n const current = this.find(original.id);\n if (current?.result && current.result.blob === original.result.blob && current.result.blob !== op.blob) {\n changed = this.set(current.id, { result: { ...current.result, blob: op.blob } }) || changed;\n }\n }\n if (changed) this.changed();\n },\n });\n for (const i of ready) this.set(i.id, { status: \"committed\", unattached: false });\n this.changed();\n return files.filter((f) => !f.unattached);\n }\n\n /** Aborts running uploads and stops polling; call on unmount. */\n dispose(): void {\n this.started = false;\n this.disposed = true;\n for (const c of this.running.values()) c.abort();\n clearTimeout(this.poll);\n this.listeners.clear();\n }\n\n /** Commits an uploaded file unattached, so the host processes it now. */\n private stage(id: string): void {\n const item = this.find(id);\n if (!item?.result) return;\n const { blob, type } = item.result;\n const op: Op = { op: \"put\", path: item.path, blob, create_id: item.id, unattached: true, ...(item.meta ? { meta: item.meta } : {}) };\n const p = this.client\n .commit(this.o.ref, [op], { sources: { [blob]: { file: item.file, type } } })\n .then(\n (files) => {\n const f = files.find((x) => x.unattached && samePath(x.path, stem(item.path)));\n if (f && this.find(id)) this.patch(id, { unattached: true, path: f.path });\n this.schedulePoll();\n return f;\n },\n (error: unknown) => {\n if (error instanceof ContentKitError && error.code === \"conflict\") {\n this.patch(id, { status: \"failed\", error });\n }\n // Other failures leave it uploaded; commit() retries the same create.\n return undefined;\n },\n )\n .finally(() => this.staging.delete(id));\n this.staging.set(id, p);\n }\n\n private schedulePoll(): void {\n if (this.poll || this.disposed) return;\n this.poll = setTimeout(() => {\n this.poll = undefined;\n void this.refresh().finally(() => {\n if (this.items.some((i) => i.unattached && !i.processed)) this.schedulePoll();\n });\n }, this.o.pollInterval ?? 2000);\n }\n\n /** Reads the processing state of unattached uploads still processing. */\n private async refresh(): Promise<void> {\n const pending = this.items.filter((i) => i.unattached && !i.processed);\n if (pending.length === 0) return;\n let files: FileInfo[];\n try {\n files = (await this.client.read(this.o.ref, { editor: true })).files;\n } catch {\n return;\n }\n const byPath = new Map(files.filter((f) => f.upload).map((f) => [f.path, f]));\n let changed = false;\n for (const i of pending) {\n const f = byPath.get(i.path);\n if (!f) continue;\n changed = this.set(i.id, { processing: f, processed: !!f.failed || (!f.pending?.length && !f.staged) }) || changed;\n }\n if (changed) this.changed();\n }\n\n private find(id: string): QueueItem | undefined {\n return this.items.find((i) => i.id === id);\n }\n\n private patch(id: string, patch: Partial<QueueItem>): void {\n if (this.set(id, patch)) this.changed();\n }\n\n private set(id: string, patch: Partial<QueueItem>): boolean {\n const i = this.items.findIndex((x) => x.id === id);\n if (i >= 0) this.items[i] = { ...this.items[i]!, ...patch };\n return i >= 0;\n }\n\n private changed(): void {\n this.pump();\n this.publish();\n for (const fn of this.listeners) fn();\n }\n\n private publish(): void {\n this.snap = {\n items: [...this.items],\n blocked: this.blocked,\n ready:\n this.running.size === 0 &&\n this.items.length > 0 &&\n this.items.every((i) => i.status === \"uploaded\" || i.status === \"committed\"),\n };\n }\n\n private pump(): void {\n if (!this.started || this.blocked) return;\n const limit = this.o.concurrency ?? 2;\n for (const item of this.items) {\n if (this.running.size >= limit) return;\n if (item.status === \"queued\" && !this.running.has(item.id)) this.run(item.id);\n }\n }\n\n private run(id: string): void {\n const item = this.find(id)!;\n const ctl = new AbortController();\n this.running.set(id, ctl);\n this.set(id, { status: \"uploading\", progress: undefined, error: undefined });\n this.client\n .upload(item.file, {\n ref: this.o.ref,\n path: item.path,\n signal: ctl.signal,\n resume: item.state,\n onProgress: (progress) => this.patch(id, { progress }),\n onState: (state) => {\n this.patch(id, { state: state ?? undefined });\n const cur = this.find(id);\n if (cur) this.o.onState?.(cur, state);\n },\n })\n .then(\n (result) => {\n this.running.delete(id);\n this.patch(id, { status: \"uploaded\", result, path: result.path, state: undefined });\n if (result.processOnUpload) this.stage(id);\n },\n (e: unknown) => {\n this.running.delete(id);\n const error = toContentKitError(e);\n if (error.code === \"aborted\") {\n this.patch(id, { status: \"queued\" });\n return;\n }\n if (error.blocksQueue) {\n // Refused for every file: keep it queued behind the block.\n this.blocked = error;\n this.patch(id, { status: \"queued\", error });\n return;\n }\n this.patch(id, { status: \"failed\", error });\n },\n );\n }\n}\n"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import { ContentKitError } from '../errors.js';
2
2
  import { PresetRule, ReadResult, UploadRule } from '../generated/wire.js';
3
3
  import { PublicPreset } from '../public.js';
4
- /** An editor read's upload rules (a viewer read has none). */
4
+ /** An editor read's upload rules (a viewer read has none): one empty list, so memos keyed on them hold. */
5
5
  export declare const uploadRules: (read: ReadResult | null | undefined) => UploadRule[];
6
6
  /** A pattern path ("originals/{name}") holds many files; a literal ("cover") one. */
7
7
  export declare const isPattern: (path: string) => boolean;
@@ -52,3 +52,9 @@ export declare function presetFor(presets: readonly PresetRule[], kind: string,
52
52
  * current file). null when the preset has no default (`rule.default`).
53
53
  */
54
54
  export declare function defaultImage(rule: PresetRule, id: string): PublicPreset | null;
55
+ /**
56
+ * A Stable preset's image of an item (`rule.stable`, e.g. the content
57
+ * model's `avatars`): URLs built from the id that show its current image,
58
+ * else the default. null for any other preset.
59
+ */
60
+ export declare function stableImage(rule: PresetRule, id: string): PublicPreset | null;