@openrails/contentkit-ui 0.85.1 → 0.87.0-rc.1

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 (211) hide show
  1. package/README.md +28 -7
  2. package/dist/client/beacons.d.ts +13 -4
  3. package/dist/client/beacons.js +14 -7
  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/types.d.ts +4 -2
  12. package/dist/client/content/types.js.map +1 -1
  13. package/dist/client/discovery.js +3 -0
  14. package/dist/client/discovery.js.map +1 -1
  15. package/dist/client/errors.d.ts +6 -0
  16. package/dist/client/errors.js +6 -2
  17. package/dist/client/errors.js.map +1 -1
  18. package/dist/client/gallery.d.ts +3 -5
  19. package/dist/client/gallery.js +16 -20
  20. package/dist/client/gallery.js.map +1 -1
  21. package/dist/client/generated/error-codes.d.ts +1 -1
  22. package/dist/client/generated/error-codes.js +9 -1
  23. package/dist/client/generated/error-codes.js.map +1 -1
  24. package/dist/client/generated/routes.d.ts +29 -3
  25. package/dist/client/generated/routes.js +44 -3
  26. package/dist/client/generated/routes.js.map +1 -1
  27. package/dist/client/generated/wire.d.ts +60 -10
  28. package/dist/client/generated/wire.js.map +1 -1
  29. package/dist/client/index.d.ts +2 -1
  30. package/dist/client/media/queue.d.ts +4 -2
  31. package/dist/client/media/queue.js +18 -12
  32. package/dist/client/media/queue.js.map +1 -1
  33. package/dist/client/media/rules.d.ts +7 -1
  34. package/dist/client/media/rules.js +27 -21
  35. package/dist/client/media/rules.js.map +1 -1
  36. package/dist/client/media/windows.js +1 -1
  37. package/dist/client/media/windows.js.map +1 -1
  38. package/dist/client/stats.d.ts +32 -0
  39. package/dist/client/stats.js +35 -0
  40. package/dist/client/stats.js.map +1 -0
  41. package/dist/client/versions.d.ts +6 -4
  42. package/dist/client/versions.js +24 -26
  43. package/dist/client/versions.js.map +1 -1
  44. package/dist/client.js +30 -29
  45. package/dist/components/admin/content-manager.js +206 -206
  46. package/dist/components/admin/content-manager.js.map +1 -1
  47. package/dist/components/admin/group-manager.js +71 -70
  48. package/dist/components/admin/group-manager.js.map +1 -1
  49. package/dist/components/admin/media-item-panel.d.ts +24 -7
  50. package/dist/components/admin/media-item-panel.js +209 -197
  51. package/dist/components/admin/media-item-panel.js.map +1 -1
  52. package/dist/components/admin/poll-manager.js +25 -25
  53. package/dist/components/admin/stats-dashboard.d.ts +22 -0
  54. package/dist/components/admin/stats-dashboard.js +257 -0
  55. package/dist/components/admin/stats-dashboard.js.map +1 -0
  56. package/dist/components/content/card.d.ts +5 -3
  57. package/dist/components/content/card.js +155 -133
  58. package/dist/components/content/card.js.map +1 -1
  59. package/dist/components/content/download-button.d.ts +26 -0
  60. package/dist/components/content/download-button.js +51 -0
  61. package/dist/components/content/download-button.js.map +1 -0
  62. package/dist/components/content/editors.js +455 -427
  63. package/dist/components/content/editors.js.map +1 -1
  64. package/dist/components/content/grid.d.ts +8 -1
  65. package/dist/components/content/grid.js +153 -104
  66. package/dist/components/content/grid.js.map +1 -1
  67. package/dist/components/content/index.d.ts +4 -2
  68. package/dist/components/content/listing.d.ts +3 -1
  69. package/dist/components/content/listing.js +34 -34
  70. package/dist/components/content/listing.js.map +1 -1
  71. package/dist/components/content/paywall.d.ts +17 -5
  72. package/dist/components/content/paywall.js +115 -100
  73. package/dist/components/content/paywall.js.map +1 -1
  74. package/dist/components/content/reader-stats.d.ts +12 -0
  75. package/dist/components/content/reader-stats.js +47 -0
  76. package/dist/components/content/reader-stats.js.map +1 -0
  77. package/dist/components/groups/banner.js +33 -33
  78. package/dist/components/groups/directory.js +11 -11
  79. package/dist/components/groups/editor.js +293 -293
  80. package/dist/components/groups/editor.js.map +1 -1
  81. package/dist/components/groups/header.js +27 -27
  82. package/dist/components/groups/header.js.map +1 -1
  83. package/dist/components/groups/memberships.js +180 -180
  84. package/dist/components/groups/parts.d.ts +0 -2
  85. package/dist/components/groups/parts.js +9 -27
  86. package/dist/components/groups/parts.js.map +1 -1
  87. package/dist/components/media-folder-editor.js +239 -189
  88. package/dist/components/media-folder-editor.js.map +1 -1
  89. package/dist/components/media-gallery.d.ts +1 -4
  90. package/dist/components/media-gallery.js +19 -34
  91. package/dist/components/media-gallery.js.map +1 -1
  92. package/dist/components/notifications.js +17 -17
  93. package/dist/components/slot-upload.d.ts +2 -2
  94. package/dist/components/slot-upload.js +84 -79
  95. package/dist/components/slot-upload.js.map +1 -1
  96. package/dist/components/social/bans.js +34 -34
  97. package/dist/components/social/comments.js +1 -1
  98. package/dist/components/social/comments.js.map +1 -1
  99. package/dist/components/social/engagement.d.ts +2 -1
  100. package/dist/components/social/engagement.js +5 -3
  101. package/dist/components/social/engagement.js.map +1 -1
  102. package/dist/components/social/moderation.js +52 -48
  103. package/dist/components/social/moderation.js.map +1 -1
  104. package/dist/components/video-player.d.ts +2 -0
  105. package/dist/components/video-player.js +318 -314
  106. package/dist/components/video-player.js.map +1 -1
  107. package/dist/i18n/context.d.ts +2 -0
  108. package/dist/i18n/context.js +23 -5
  109. package/dist/i18n/context.js.map +1 -1
  110. package/dist/index.d.ts +2 -1
  111. package/dist/index.js +13 -10
  112. package/dist/locales/admin/de.js +27 -0
  113. package/dist/locales/admin/de.js.map +1 -1
  114. package/dist/locales/admin/en.d.ts +27 -0
  115. package/dist/locales/admin/en.js +27 -0
  116. package/dist/locales/admin/en.js.map +1 -1
  117. package/dist/locales/admin/es.js +27 -0
  118. package/dist/locales/admin/es.js.map +1 -1
  119. package/dist/locales/admin/ja.js +27 -0
  120. package/dist/locales/admin/ja.js.map +1 -1
  121. package/dist/locales/admin/ko.js +27 -0
  122. package/dist/locales/admin/ko.js.map +1 -1
  123. package/dist/locales/admin/zh.js +27 -0
  124. package/dist/locales/admin/zh.js.map +1 -1
  125. package/dist/locales/content/de.js +6 -0
  126. package/dist/locales/content/de.js.map +1 -1
  127. package/dist/locales/content/en.d.ts +6 -0
  128. package/dist/locales/content/en.js +6 -0
  129. package/dist/locales/content/en.js.map +1 -1
  130. package/dist/locales/content/es.js +6 -0
  131. package/dist/locales/content/es.js.map +1 -1
  132. package/dist/locales/content/ja.js +6 -0
  133. package/dist/locales/content/ja.js.map +1 -1
  134. package/dist/locales/content/ko.js +6 -0
  135. package/dist/locales/content/ko.js.map +1 -1
  136. package/dist/locales/content/zh.js +6 -0
  137. package/dist/locales/content/zh.js.map +1 -1
  138. package/dist/locales/de.js +14 -2
  139. package/dist/locales/de.js.map +1 -1
  140. package/dist/locales/discovery/de.js +3 -0
  141. package/dist/locales/discovery/de.js.map +1 -1
  142. package/dist/locales/discovery/en.d.ts +3 -0
  143. package/dist/locales/discovery/en.js +3 -0
  144. package/dist/locales/discovery/en.js.map +1 -1
  145. package/dist/locales/discovery/es.js +3 -0
  146. package/dist/locales/discovery/es.js.map +1 -1
  147. package/dist/locales/discovery/ja.js +3 -0
  148. package/dist/locales/discovery/ja.js.map +1 -1
  149. package/dist/locales/discovery/ko.js +3 -0
  150. package/dist/locales/discovery/ko.js.map +1 -1
  151. package/dist/locales/discovery/zh.js +3 -0
  152. package/dist/locales/discovery/zh.js.map +1 -1
  153. package/dist/locales/en.d.ts +50 -2
  154. package/dist/locales/en.js +14 -2
  155. package/dist/locales/en.js.map +1 -1
  156. package/dist/locales/es.js +14 -2
  157. package/dist/locales/es.js.map +1 -1
  158. package/dist/locales/ja.js +14 -2
  159. package/dist/locales/ja.js.map +1 -1
  160. package/dist/locales/ko.js +14 -2
  161. package/dist/locales/ko.js.map +1 -1
  162. package/dist/locales/zh.js +14 -2
  163. package/dist/locales/zh.js.map +1 -1
  164. package/dist/react/admin.js +22 -23
  165. package/dist/react/admin.js.map +1 -1
  166. package/dist/react/canonical.d.ts +4 -3
  167. package/dist/react/canonical.js +34 -33
  168. package/dist/react/canonical.js.map +1 -1
  169. package/dist/react/comments.d.ts +1 -1
  170. package/dist/react/comments.js +3 -2
  171. package/dist/react/comments.js.map +1 -1
  172. package/dist/react/contents.js +144 -144
  173. package/dist/react/contents.js.map +1 -1
  174. package/dist/react/context.d.ts +1 -1
  175. package/dist/react/context.js.map +1 -1
  176. package/dist/react/detail.d.ts +12 -6
  177. package/dist/react/detail.js +31 -24
  178. package/dist/react/detail.js.map +1 -1
  179. package/dist/react/discovery.d.ts +11 -0
  180. package/dist/react/discovery.js +49 -30
  181. package/dist/react/discovery.js.map +1 -1
  182. package/dist/react/engagement.d.ts +6 -2
  183. package/dist/react/engagement.js +2 -6
  184. package/dist/react/engagement.js.map +1 -1
  185. package/dist/react/folder.d.ts +7 -2
  186. package/dist/react/folder.js +165 -147
  187. package/dist/react/folder.js.map +1 -1
  188. package/dist/react/groups.d.ts +0 -1
  189. package/dist/react/groups.js +13 -12
  190. package/dist/react/groups.js.map +1 -1
  191. package/dist/react/index.d.ts +6 -4
  192. package/dist/react/location.d.ts +11 -0
  193. package/dist/react/location.js +18 -0
  194. package/dist/react/location.js.map +1 -0
  195. package/dist/react/public.d.ts +8 -0
  196. package/dist/react/public.js +32 -27
  197. package/dist/react/public.js.map +1 -1
  198. package/dist/react/stats.d.ts +24 -0
  199. package/dist/react/stats.js +28 -0
  200. package/dist/react/stats.js.map +1 -0
  201. package/dist/react/use-resource.js +8 -7
  202. package/dist/react/use-resource.js.map +1 -1
  203. package/dist/react/versions.d.ts +25 -5
  204. package/dist/react/versions.js +61 -40
  205. package/dist/react/versions.js.map +1 -1
  206. package/dist/react.js +26 -24
  207. package/dist/styles.css +1 -1
  208. package/dist/urls/index.d.ts +35 -10
  209. package/dist/urls.js +100 -70
  210. package/dist/urls.js.map +1 -1
  211. package/package.json +1 -1
@@ -168,6 +168,8 @@ export interface Card {
168
168
  }
169
169
  export interface CardAccess {
170
170
  allowed: boolean;
171
+ level: "group" | "group_ppv" | "ppv" | "public" | "tier";
172
+ tiers?: string[];
171
173
  unknown?: boolean;
172
174
  sell?: string[];
173
175
  requires?: string;
@@ -177,11 +179,13 @@ export interface CardAccess {
177
179
  }
178
180
  export interface CardVersion {
179
181
  id: string;
182
+ code: string;
180
183
  languages: string[];
181
184
  traits: string[];
182
185
  label?: string;
183
186
  pages?: number;
184
187
  duration_ms?: number;
188
+ has_audio: boolean;
185
189
  }
186
190
  export interface CatalogCover {
187
191
  image: string;
@@ -306,7 +310,7 @@ export interface Content {
306
310
  creators: Creator[];
307
311
  groups: GroupSummary[];
308
312
  versions: Version[];
309
- views: number;
313
+ views?: number;
310
314
  viewer?: ViewerState;
311
315
  editor: boolean;
312
316
  text?: Record<string, CatalogText>;
@@ -367,6 +371,7 @@ export interface ContentModel {
367
371
  group_kinds: Record<string, GroupKind>;
368
372
  content_filters: ContentFilter[];
369
373
  signals: boolean;
374
+ avatars?: PresetRule;
370
375
  }
371
376
  export interface ContentPage {
372
377
  contents: Card[];
@@ -435,10 +440,10 @@ export interface Count {
435
440
  count: number;
436
441
  }
437
442
  export interface Counts {
438
- likes: number;
439
- dislikes: number;
440
- favorites: number;
441
- views: number;
443
+ likes?: number;
444
+ dislikes?: number;
445
+ favorites?: number;
446
+ views?: number;
442
447
  }
443
448
  export interface Creator {
444
449
  user_id: string;
@@ -472,6 +477,10 @@ export interface DeleteRecord {
472
477
  entity: string;
473
478
  external_id: string;
474
479
  }
480
+ export interface Download {
481
+ off?: boolean;
482
+ serve?: string;
483
+ }
475
484
  export interface Edge {
476
485
  from: string;
477
486
  relation: string;
@@ -527,6 +536,8 @@ export interface ErrorReply {
527
536
  ban?: BanNotice;
528
537
  blobs?: string[];
529
538
  details?: ErrorDetails;
539
+ reason?: string;
540
+ available_at?: string;
530
541
  }
531
542
  export interface ExposureInput {
532
543
  render_id: string;
@@ -543,6 +554,28 @@ export interface Exposures {
543
554
  session?: string;
544
555
  exposures: ExposureInput[];
545
556
  }
557
+ export interface ExternalID {
558
+ external_id: string;
559
+ id: string;
560
+ deleted: boolean;
561
+ }
562
+ export interface ExternalIDList {
563
+ entity: string;
564
+ cursor?: string;
565
+ limit?: number;
566
+ }
567
+ export interface ExternalIDPage {
568
+ entity: string;
569
+ ids: ExternalID[];
570
+ next_cursor?: string;
571
+ }
572
+ export interface ExternalIDs {
573
+ lists: ExternalIDPage[];
574
+ }
575
+ export interface ExternalIDsQuery {
576
+ source: string;
577
+ lists: ExternalIDList[];
578
+ }
546
579
  export interface Failure {
547
580
  of: string;
548
581
  message: string;
@@ -556,7 +589,7 @@ export interface FavoriteRecord {
556
589
  }
557
590
  export interface FavoriteState {
558
591
  favorited: boolean;
559
- count: number;
592
+ count?: number;
560
593
  }
561
594
  export interface FeedItem {
562
595
  id: string;
@@ -580,6 +613,13 @@ export interface FeedItem {
580
613
  content_id: string;
581
614
  content_version_id?: string;
582
615
  }
616
+ export interface FileDownload {
617
+ url: string;
618
+ name: string;
619
+ type: string;
620
+ size?: number;
621
+ expires_at: string;
622
+ }
583
623
  export interface FileInfo {
584
624
  path: string;
585
625
  type: string;
@@ -589,7 +629,6 @@ export interface FileInfo {
589
629
  dur?: number;
590
630
  download?: string;
591
631
  url?: string;
592
- locked?: boolean;
593
632
  upload?: boolean;
594
633
  staged?: boolean;
595
634
  from?: string;
@@ -873,7 +912,9 @@ export interface Kind {
873
912
  groups?: string[];
874
913
  attributes?: Record<string, Attribute>;
875
914
  sort?: string;
915
+ locked?: Locked;
876
916
  draft_ttl?: number;
917
+ download?: Download;
877
918
  }
878
919
  export interface LanguageTotal {
879
920
  public: number;
@@ -893,6 +934,10 @@ export interface LibraryPage {
893
934
  next_cursor?: string;
894
935
  complete: boolean;
895
936
  }
937
+ export interface Locked {
938
+ show: string[];
939
+ preview?: Preview;
940
+ }
896
941
  export interface MemberPage {
897
942
  members: Membership[];
898
943
  next_cursor?: string;
@@ -1185,6 +1230,7 @@ export interface PresetRule {
1185
1230
  min_width?: number;
1186
1231
  first?: number;
1187
1232
  default: boolean;
1233
+ stable?: boolean;
1188
1234
  }
1189
1235
  export interface PresignBody {
1190
1236
  ref: RefBody;
@@ -1238,8 +1284,8 @@ export interface RankedContent {
1238
1284
  value: number;
1239
1285
  }
1240
1286
  export interface ReactionCounts {
1241
- likes: number;
1242
- dislikes: number;
1287
+ likes?: number;
1288
+ dislikes?: number;
1243
1289
  mine: number;
1244
1290
  }
1245
1291
  export interface ReactionRecord {
@@ -1256,6 +1302,7 @@ export interface ReadResult {
1256
1302
  meta?: Record<string, unknown>;
1257
1303
  previews?: string[];
1258
1304
  public?: PublicImage[];
1305
+ locked?: boolean;
1259
1306
  total: number;
1260
1307
  offset: number;
1261
1308
  limit: number;
@@ -1279,7 +1326,7 @@ export interface ReaderPlan {
1279
1326
  content_id: string;
1280
1327
  version_id: string;
1281
1328
  publication_id: string;
1282
- pages: number;
1329
+ pages?: number;
1283
1330
  segments: PlanSegment[];
1284
1331
  previews: PlanPreview[];
1285
1332
  playback?: PlanPlayback;
@@ -1342,6 +1389,7 @@ export interface Resolved {
1342
1389
  slug: string;
1343
1390
  slugs?: Record<string, string>;
1344
1391
  path: string;
1392
+ version_id?: string;
1345
1393
  }
1346
1394
  export interface Restored {
1347
1395
  restored: boolean;
@@ -1484,6 +1532,7 @@ export interface UploadRule {
1484
1532
  }
1485
1533
  export interface Version {
1486
1534
  id: string;
1535
+ code: string;
1487
1536
  position: number;
1488
1537
  languages: string[];
1489
1538
  traits: string[];
@@ -1530,6 +1579,7 @@ export interface VersionInput {
1530
1579
  export interface VersionRecord {
1531
1580
  external_id: string;
1532
1581
  id?: string;
1582
+ code?: string;
1533
1583
  languages: string[];
1534
1584
  traits?: string[];
1535
1585
  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 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 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}\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 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 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}\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}\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 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 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 level: \"group\" | \"group_ppv\" | \"ppv\" | \"public\" | \"tier\";\n tiers?: string[];\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 has_audio: boolean;\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 Download {\n off?: boolean;\n serve?: 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 reason?: string;\n available_at?: string;\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 FileDownload {\n url: string;\n name: string;\n type: string;\n size?: number;\n expires_at: 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 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 locked?: Locked;\n draft_ttl?: number;\n download?: Download;\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 Locked {\n show: string[];\n preview?: Preview;\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 locked?: boolean;\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"}
@@ -15,6 +15,7 @@ export { DiscoveryClient, type AccessMode, type CardQuery, type ContentFilterQue
15
15
  export { VersionsClient, isPending } from './versions.js';
16
16
  export { emptyReaderMeta, matchAudio, readerMetaProblems, readerNames, type AudioMatch, type ReaderMetaProblem, type ReaderNames } from './reader-meta.js';
17
17
  export { OffersClient } from './offers.js';
18
+ export { StatsClient, type ActivityQuery, type TopQuery } from './stats.js';
18
19
  export { Beacons, eventId, readerSession, type Attribution, type BeaconOptions, type ViewSession } from './beacons.js';
19
20
  export { LETTERS, attributeInput, fold, fromLocalInput, humanize, letterOf, parseAttribute, parseAttributes, slugify, toLocalInput } from './group-fields.js';
20
21
  export { CodesClient } from './codes.js';
@@ -23,7 +24,7 @@ export { call, routeURL, type CallOptions, type Method, type PageQuery, type Pat
23
24
  export { MediaApi, type ReadOptions } from './media/api.js';
24
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';
25
26
  export { ItemUploadQueue, type ItemStatus, type QueueItem, type QueueOptions, type QueueSnapshot } from './media/queue.js';
26
- 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';
27
28
  export { READ_CHUNK, chunkOffsets, isProcessing, mergeReads, processing } from './media/windows.js';
28
29
  export { defaultTransport, fetchTransport, xhrTransport, type Transport } from './media/transport.js';
29
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;