@openrails/contentkit-ui 0.93.0 → 0.94.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/README.md +34 -4
  2. package/dist/client/browser-reader-preferences.d.ts +38 -0
  3. package/dist/client/browser-reader-preferences.js +172 -0
  4. package/dist/client/browser-reader-preferences.js.map +1 -0
  5. package/dist/client/client.d.ts +3 -0
  6. package/dist/client/client.js +27 -25
  7. package/dist/client/client.js.map +1 -1
  8. package/dist/client/discovery.js +24 -23
  9. package/dist/client/discovery.js.map +1 -1
  10. package/dist/client/generated/routes.d.ts +15 -2
  11. package/dist/client/generated/routes.js +18 -2
  12. package/dist/client/generated/routes.js.map +1 -1
  13. package/dist/client/generated/wire.d.ts +5 -0
  14. package/dist/client/generated/wire.js.map +1 -1
  15. package/dist/client/http.d.ts +18 -1
  16. package/dist/client/http.js +67 -26
  17. package/dist/client/http.js.map +1 -1
  18. package/dist/client/index.d.ts +2 -0
  19. package/dist/client/reader-preferences.d.ts +46 -0
  20. package/dist/client/reader-preferences.js +147 -0
  21. package/dist/client/reader-preferences.js.map +1 -0
  22. package/dist/client.js +8 -6
  23. package/dist/components/content/filters.d.ts +1 -1
  24. package/dist/components/content/filters.js +1 -5
  25. package/dist/components/content/filters.js.map +1 -1
  26. package/dist/components/content/reader-settings.d.ts +1 -1
  27. package/dist/components/content/reader-settings.js +67 -44
  28. package/dist/components/content/reader-settings.js.map +1 -1
  29. package/dist/locales/discovery/de.js +2 -0
  30. package/dist/locales/discovery/de.js.map +1 -1
  31. package/dist/locales/discovery/en.d.ts +2 -0
  32. package/dist/locales/discovery/en.js +2 -0
  33. package/dist/locales/discovery/en.js.map +1 -1
  34. package/dist/locales/discovery/es.js +2 -0
  35. package/dist/locales/discovery/es.js.map +1 -1
  36. package/dist/locales/discovery/ja.js +2 -0
  37. package/dist/locales/discovery/ja.js.map +1 -1
  38. package/dist/locales/discovery/ko.js +2 -0
  39. package/dist/locales/discovery/ko.js.map +1 -1
  40. package/dist/locales/discovery/zh.js +2 -0
  41. package/dist/locales/discovery/zh.js.map +1 -1
  42. package/dist/locales/en.d.ts +2 -0
  43. package/dist/react/context.d.ts +1 -1
  44. package/dist/react/context.js.map +1 -1
  45. package/dist/react/discovery.d.ts +2 -2
  46. package/dist/react/discovery.js +187 -149
  47. package/dist/react/discovery.js.map +1 -1
  48. package/dist/react/index.d.ts +1 -0
  49. package/dist/react/provider.d.ts +4 -1
  50. package/dist/react/provider.js +38 -31
  51. package/dist/react/provider.js.map +1 -1
  52. package/dist/react/reader-preferences.d.ts +13 -0
  53. package/dist/react/reader-preferences.js +15 -0
  54. package/dist/react/reader-preferences.js.map +1 -0
  55. package/dist/react.js +3 -2
  56. package/package.json +1 -1
@@ -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 Archive {\n name: string;\n size: number;\n sha256: 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 entities?: 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 BodyImageInput {\n image?: string;\n version: string;\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 entities: 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 hls?: string;\n}\n\nexport interface CatalogCover {\n image?: string;\n url?: string;\n renditions?: PublicRendition[];\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 type CompleteRequest = Record<string, never>;\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 entities: 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 entity: GroupRef;\n default_visible: boolean;\n labels?: Record<string, string>;\n}\n\nexport interface ContentFilterState {\n entity: 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 entities: GroupCard[];\n}\n\nexport interface ContentModel {\n languages: string[];\n tiers: string[];\n kinds: Record<string, Kind>;\n entity_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 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 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 EntityReference {\n id?: string;\n kind?: string;\n name?: string;\n language?: string;\n scope?: EntityReference;\n}\n\nexport interface EntityResolution {\n id: string;\n kind: string;\n name: string;\n created: boolean;\n scope?: EntityResolution;\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 entities: 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 visibility?: string;\n app_id?: string;\n status?: string;\n action_requested?: boolean;\n severity?: string;\n}\n\nexport interface Interaction {\n content?: Card;\n entity?: 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 visibility?: string;\n app_id?: 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 entities?: 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 entities: MyGroup[];\n next_cursor?: string;\n}\n\nexport interface MyStats {\n completed: Record<string, number>;\n access_seconds?: 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 Operation {\n id: string;\n state: \"awaiting_upload\" | \"blocked\" | \"failed\" | \"processing\" | \"published\" | \"publishing\" | \"ready\" | \"review_required\" | \"scheduled\";\n content_id: string;\n version_id: string;\n publish_at?: string;\n entities: EntityResolution[];\n progress: Progress;\n result?: OperationResult;\n error?: OperationError;\n upload?: PresignReply;\n created_at: string;\n updated_at: string;\n}\n\nexport interface OperationError {\n code: string;\n message: string;\n retryable: boolean;\n field?: string;\n}\n\nexport interface OperationResult {\n path: string;\n pages: number;\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 root?: 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 Progress {\n stage: string;\n completed: number;\n total: 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 Request {\n content: ContentInput;\n entities?: EntityReference[];\n archive: Archive;\n publish?: \"draft\" | \"when_ready\";\n publish_at?: string;\n create_missing_entities?: boolean;\n source?: Source;\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 image?: 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 Source {\n provider: string;\n id: string;\n url?: 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 entities: 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 body?: 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 cover?: CatalogCover;\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 cover?: string;\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 cover?: string;\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 Archive {\n name: string;\n size: number;\n sha256: 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 entities?: 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 BodyImageInput {\n image?: string;\n version: string;\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 entities: 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 hls?: string;\n}\n\nexport interface CatalogCover {\n image?: string;\n url?: string;\n renditions?: PublicRendition[];\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 type CompleteRequest = Record<string, never>;\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 entities: 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 entity: GroupRef;\n default_visible: boolean;\n labels?: Record<string, string>;\n}\n\nexport interface ContentFilterState {\n entity: 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 ContentFiltersInitialization {\n account_id: string;\n filters: FilterSetting[];\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 entities: GroupCard[];\n}\n\nexport interface ContentModel {\n languages: string[];\n tiers: string[];\n kinds: Record<string, Kind>;\n entity_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 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 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 EntityReference {\n id?: string;\n kind?: string;\n name?: string;\n language?: string;\n scope?: EntityReference;\n}\n\nexport interface EntityResolution {\n id: string;\n kind: string;\n name: string;\n created: boolean;\n scope?: EntityResolution;\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 entities: 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 visibility?: string;\n app_id?: string;\n status?: string;\n action_requested?: boolean;\n severity?: string;\n}\n\nexport interface Interaction {\n content?: Card;\n entity?: 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 visibility?: string;\n app_id?: 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 entities?: 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 entities: MyGroup[];\n next_cursor?: string;\n}\n\nexport interface MyStats {\n completed: Record<string, number>;\n access_seconds?: 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 Operation {\n id: string;\n state: \"awaiting_upload\" | \"blocked\" | \"failed\" | \"processing\" | \"published\" | \"publishing\" | \"ready\" | \"review_required\" | \"scheduled\";\n content_id: string;\n version_id: string;\n publish_at?: string;\n entities: EntityResolution[];\n progress: Progress;\n result?: OperationResult;\n error?: OperationError;\n upload?: PresignReply;\n created_at: string;\n updated_at: string;\n}\n\nexport interface OperationError {\n code: string;\n message: string;\n retryable: boolean;\n field?: string;\n}\n\nexport interface OperationResult {\n path: string;\n pages: number;\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 root?: 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 Progress {\n stage: string;\n completed: number;\n total: 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 Request {\n content: ContentInput;\n entities?: EntityReference[];\n archive: Archive;\n publish?: \"draft\" | \"when_ready\";\n publish_at?: string;\n create_missing_entities?: boolean;\n source?: Source;\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 image?: 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 Source {\n provider: string;\n id: string;\n url?: 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 entities: 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 body?: 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 cover?: CatalogCover;\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 cover?: string;\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 cover?: string;\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,5 +1,6 @@
1
1
  import { ContentChange } from './content/types.js';
2
2
  import { FileInfo, Item, RefBody } from './generated/wire.js';
3
+ import { BrowserReaderPreferences } from './browser-reader-preferences.js';
3
4
  /** A module's base URL, or one per item (a host that splits uploads by kind). */
4
5
  export type Mount = string | ((ref: RefBody) => string);
5
6
  /**
@@ -78,6 +79,10 @@ export interface RequestOptions {
78
79
  /** Read the body as a Blob instead of JSON. */
79
80
  blob?: boolean;
80
81
  keepalive?: boolean;
82
+ /** Internal preference inventory reads never send unvalidated overrides. */
83
+ guestPreferences?: boolean;
84
+ /** Internal: avoid repeating recovery if a refreshed override is rejected. */
85
+ retryGuestPreferences?: boolean;
81
86
  }
82
87
  /** How a player loads HLS: the media client's `hls`. */
83
88
  export interface HlsLoader {
@@ -92,10 +97,22 @@ export declare class Http {
92
97
  private readonly listeners;
93
98
  private readScope;
94
99
  private readonly base;
100
+ readonly browserPreferences: BrowserReaderPreferences;
101
+ private viewer?;
102
+ private viewerRevision;
103
+ private filterIDs?;
104
+ private filterInventory?;
95
105
  constructor(o: HttpOptions);
106
+ setViewer(viewer: string | null | undefined): void;
107
+ acceptGuestFilters(filters: readonly {
108
+ entity: {
109
+ id: string;
110
+ };
111
+ }[]): void;
112
+ private validateGuestFilters;
96
113
  /** The module's base URL, without a trailing slash. */
97
114
  mount(module: ContentKitModule, ref?: RefBody): string;
98
- request<T>(url: string, { method, body, signal, blob, keepalive }?: RequestOptions): Promise<T>;
115
+ request<T>(url: string, { method, body, signal, blob, keepalive, guestPreferences, retryGuestPreferences }?: RequestOptions): Promise<T>;
99
116
  /**
100
117
  * How players load this API's HLS. Playlists and sprite VTTs go through
101
118
  * the client's fetch, so they carry what its other calls carry (a DPoP
@@ -1,45 +1,86 @@
1
- import { readContentKitError as e, requestFailure as t } from "./errors.js";
2
- import { CONTENTKIT_MODULES as n } from "./generated/routes.js";
1
+ import { ContentKitError as e, readContentKitError as t, requestFailure as n } from "./errors.js";
2
+ import { CONTENTKIT_MODULES as r } from "./generated/routes.js";
3
+ import { browserReaderPreferences as i } from "./browser-reader-preferences.js";
3
4
  //#region src/client/http.ts
4
- var r = (e) => e.replace(/\/+$/, ""), i = class {
5
+ var a = (e) => e.replace(/\/+$/, ""), o = class {
5
6
  o;
6
7
  listeners = /* @__PURE__ */ new Set();
7
8
  readScope = { key: "{}" };
8
9
  base;
10
+ browserPreferences;
11
+ viewer;
12
+ viewerRevision = 0;
13
+ filterIDs;
14
+ filterInventory;
9
15
  constructor(e) {
10
16
  if (this.o = e, typeof e.baseUrl != "string") throw Error("contentkit: createContentKitClient needs baseUrl");
11
- this.base = r(e.baseUrl);
17
+ this.base = a(e.baseUrl), this.browserPreferences = i(this.mount("discovery"));
18
+ }
19
+ setViewer(e) {
20
+ e !== this.viewer && this.viewerRevision++, this.viewer = e;
21
+ }
22
+ acceptGuestFilters(e) {
23
+ this.filterIDs = new Set(e.map((e) => e.entity.id));
24
+ let t = this.browserPreferences.snapshot().filters, n = t.filter((e) => this.filterIDs.has(e.group_id));
25
+ n.length !== t.length && this.browserPreferences.setFilters(n);
26
+ }
27
+ async validateGuestFilters() {
28
+ !this.filterIDs && this.browserPreferences.snapshot().filters.length && (this.filterInventory ??= this.request(`${this.mount("discovery")}/content-filters`, { guestPreferences: !1 }).then((e) => this.acceptGuestFilters(e.filters)).finally(() => {
29
+ this.filterInventory = void 0;
30
+ }), await this.filterInventory);
12
31
  }
13
32
  mount(e, t) {
14
- let i = this.o.mounts?.[e];
15
- if (typeof i == "function") {
33
+ let n = this.o.mounts?.[e];
34
+ if (typeof n == "function") {
16
35
  if (!t) throw Error(`contentkit: the ${e} mount is per item; pass a ref`);
17
- return r(i(t));
36
+ return a(n(t));
18
37
  }
19
- return i === void 0 ? this.base + n[e] : r(i);
38
+ return n === void 0 ? this.base + r[e] : a(n);
20
39
  }
21
- async request(n, { method: r, body: i, signal: a, blob: o, keepalive: s } = {}) {
22
- let c = this.o.fetch ?? fetch, l = new Headers(await this.o.headers?.());
23
- i !== void 0 && l.set("Content-Type", "application/json");
24
- let u = await this.o.token?.();
25
- u && !l.has("Authorization") && l.set("Authorization", `Bearer ${u}`);
26
- let d = this.o.language?.();
27
- d && l.set("Accept-Language", d);
28
- let f;
40
+ async request(r, { method: i, body: a, signal: o, blob: s, keepalive: c, guestPreferences: l = !0, retryGuestPreferences: u = !0 } = {}) {
41
+ let d = this.viewerRevision;
42
+ if (this.viewer === null && l && await this.validateGuestFilters(), d !== this.viewerRevision) throw new e("aborted", "The account changed.");
43
+ let f = this.o.fetch ?? fetch, p = new Headers(await this.o.headers?.());
44
+ p.delete("ContentKit-Reader-Preferences"), a !== void 0 && p.set("Content-Type", "application/json");
45
+ let m = await this.o.token?.();
46
+ m && !p.has("Authorization") && p.set("Authorization", `Bearer ${m}`);
47
+ let h = this.o.language?.();
48
+ if (h && p.set("Accept-Language", h), this.viewer === null && l) {
49
+ let e = this.browserPreferences.snapshot().filters;
50
+ e.length && p.set("ContentKit-Reader-Preferences", JSON.stringify({ filters: e }));
51
+ }
52
+ if (d !== this.viewerRevision) throw new e("aborted", "The account changed.");
53
+ let g;
29
54
  try {
30
- f = await c(n, {
31
- method: r ?? (i === void 0 ? "GET" : "POST"),
32
- headers: l,
33
- body: i === void 0 ? null : JSON.stringify(i),
55
+ g = await f(r, {
56
+ method: i ?? (a === void 0 ? "GET" : "POST"),
57
+ headers: p,
58
+ body: a === void 0 ? null : JSON.stringify(a),
34
59
  credentials: this.o.credentials ?? "same-origin",
35
- signal: a ?? null,
36
- ...s ? { keepalive: s } : {}
60
+ signal: o ?? null,
61
+ ...c ? { keepalive: c } : {}
37
62
  });
38
63
  } catch (e) {
39
- throw t(e, a, `ContentKit API ${new URL(n, "http://x").pathname}`);
64
+ throw n(e, o, `ContentKit API ${new URL(r, "http://x").pathname}`);
65
+ }
66
+ if (!g.ok) {
67
+ let n = await t(g), f = p.get("ContentKit-Reader-Preferences");
68
+ if ((i ?? (a === void 0 ? "GET" : "POST")) === "GET" && n.code === "invalid_request" && f && this.viewer === null && u) {
69
+ if (this.filterIDs = void 0, await this.validateGuestFilters(), d !== this.viewerRevision) throw new e("aborted", "The account changed.");
70
+ let t = this.browserPreferences.snapshot().filters;
71
+ if ((t.length ? JSON.stringify({ filters: t }) : null) !== f) return this.request(r, {
72
+ method: i,
73
+ body: a,
74
+ signal: o,
75
+ blob: s,
76
+ keepalive: c,
77
+ guestPreferences: l,
78
+ retryGuestPreferences: !1
79
+ });
80
+ }
81
+ throw n;
40
82
  }
41
- if (!f.ok) throw await e(f);
42
- return o ? await f.blob() : f.status === 204 ? void 0 : await f.json();
83
+ return s ? await g.blob() : g.status === 204 ? void 0 : await g.json();
43
84
  }
44
85
  hls = {
45
86
  playlist: async (e, t) => {
@@ -85,6 +126,6 @@ var r = (e) => e.replace(/\/+$/, ""), i = class {
85
126
  }
86
127
  };
87
128
  //#endregion
88
- export { i as Http };
129
+ export { o as Http };
89
130
 
90
131
  //# sourceMappingURL=http.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"http.js","names":[],"sources":["../../src/client/http.ts"],"sourcesContent":["import { readContentKitError, requestFailure } from \"./errors.js\";\nimport type { ContentChange } from \"./content/types.js\";\nimport { CONTENTKIT_MODULES } from \"./generated/routes.js\";\nimport type { FileInfo, Item, RefBody } from \"./generated/wire.js\";\n\n/** A module's base URL, or one per item (a host that splits uploads by kind). */\nexport type Mount = string | ((ref: RefBody) => string);\n\n/**\n * Where each module lives when the host does not serve them all under\n * baseUrl's fixed sub-paths (CONTENTKIT_MODULES: the API version, then the\n * module's path). A mount is the module's full base URL.\n */\nexport interface ContentKitMounts {\n /** Comments, reactions, favorites, polls, bans, moderation. Default `{baseUrl}/v1`. */\n content?: string;\n /** Content items and catalog totals. Default `{baseUrl}/v1`. */\n contents?: string;\n /** Media reads and HLS playlists. Default `{baseUrl}/v1/media`. */\n media?: Mount;\n /** Uploads and commits. Default `{baseUrl}/v1/media/upload`. */\n upload?: Mount;\n /** Content-code lookup. Default `{baseUrl}/v1/codes`. */\n codes?: string;\n /** Entities. Default `{baseUrl}/v1`. */\n entities?: string;\n /** The site's content model. Default `{baseUrl}/v1`. */\n kinds?: string;\n /** User inbox. Default `{baseUrl}/v1/notifications`. */\n notifications?: string;\n /** Content versions: reader plans, reader metadata, forks, downloads. Default `{baseUrl}/v1`. */\n versions?: string;\n /** Offers, group joins and the library. Default `{baseUrl}/v1`. */\n offers?: string;\n /** Discovery: lists, search, typeahead, similar, recommendations, history, signals. Default `{baseUrl}/v1`. */\n discovery?: string;\n /** Bulk import (a service's). Default `{baseUrl}/v1`. */\n import?: string;\n /** Durable archive publishing operations. Default `{baseUrl}/v1`. */\n publishing?: string;\n}\n\nexport type ContentKitModule = keyof ContentKitMounts;\n\n/** A mutation made through the client: the host's cache-invalidation hook gets each one. */\nexport type ContentKitChange =\n /** A commit applied; files are the item's uploads as an editor reads them, without URLs. */\n | { type: \"media.committed\"; ref: RefBody; files: FileInfo[] }\n /** An upload finished processing, as an editor read returns it. */\n | { type: \"media.processed\"; ref: RefBody; file: FileInfo }\n | { type: \"notifications.changed\" }\n | { type: \"notifications.updated\"; item: Item }\n | ContentChange;\n\nexport type TokenSource = () => string | null | undefined | Promise<string | null | undefined>;\n\nexport interface HttpOptions {\n /** Where the host mounts ContentKit's routes (contentkit.Routes.Prefix), e.g. \"/api/contentkit\". */\n baseUrl: string;\n mounts?: ContentKitMounts;\n /** Transport for API calls; pass an authenticating fetch (auth-ui's authFetch). */\n fetch?: typeof fetch;\n /** Bearer for API calls and HLS playlists. An authenticating fetch overrides it. */\n token?: TokenSource;\n /** Sent as Accept-Language. */\n language?: () => string | null | undefined;\n /** Extra headers per call (CSRF). */\n headers?: () => HeadersInit | Promise<HeadersInit>;\n /** Default \"same-origin\"; \"include\" also sends cookies with HLS segment requests. */\n credentials?: RequestCredentials;\n}\n\nexport interface RequestOptions {\n method?: string;\n body?: unknown;\n signal?: AbortSignal;\n /** Read the body as a Blob instead of JSON. */\n blob?: boolean;\n keepalive?: boolean;\n}\n\nconst trim = (u: string) => u.replace(/\\/+$/, \"\");\n\n/** How a player loads HLS: the media client's `hls`. */\nexport interface HlsLoader {\n /** Fetches a playlist or sprite VTT with the app's credentials. */\n playlist(url: string, signal?: AbortSignal): Promise<Response>;\n /** hls.js `xhrSetup` for segments: cookies, never the app's credentials. */\n segment(xhr: XMLHttpRequest, url: string): void;\n}\n\n/** The transport every module shares: mounts, auth, language, errors and change events. */\nexport class Http {\n private readonly listeners = new Set<(change: ContentKitChange, scope: string | null) => void>();\n private readScope = { key: \"{}\" };\n private readonly base: string;\n\n constructor(private readonly o: HttpOptions) {\n if (typeof o.baseUrl !== \"string\") throw new Error(\"contentkit: createContentKitClient needs baseUrl\");\n this.base = trim(o.baseUrl);\n }\n\n /** The module's base URL, without a trailing slash. */\n mount(module: ContentKitModule, ref?: RefBody): string {\n const m = this.o.mounts?.[module];\n if (typeof m === \"function\") {\n if (!ref) throw new Error(`contentkit: the ${module} mount is per item; pass a ref`);\n return trim(m(ref));\n }\n if (m !== undefined) return trim(m);\n return this.base + CONTENTKIT_MODULES[module];\n }\n\n async request<T>(url: string, { method, body, signal, blob, keepalive }: RequestOptions = {}): Promise<T> {\n const f = this.o.fetch ?? fetch;\n const headers = new Headers(await this.o.headers?.());\n if (body !== undefined) headers.set(\"Content-Type\", \"application/json\");\n const token = await this.o.token?.();\n if (token && !headers.has(\"Authorization\")) headers.set(\"Authorization\", `Bearer ${token}`);\n const lang = this.o.language?.();\n if (lang) headers.set(\"Accept-Language\", lang);\n let res: Response;\n try {\n res = await f(url, {\n method: method ?? (body === undefined ? \"GET\" : \"POST\"),\n headers,\n body: body === undefined ? null : JSON.stringify(body),\n credentials: this.o.credentials ?? \"same-origin\",\n signal: signal ?? null,\n ...(keepalive ? { keepalive } : {}),\n });\n } catch (err) {\n throw requestFailure(err, signal, `ContentKit API ${new URL(url, \"http://x\").pathname}`);\n }\n if (!res.ok) throw await readContentKitError(res);\n if (blob) return (await res.blob()) as T;\n return (res.status === 204 ? undefined : await res.json()) as T;\n }\n\n /**\n * How players load this API's HLS. Playlists and sprite VTTs go through\n * the client's fetch, so they carry what its other calls carry (a DPoP\n * proof when that fetch adds one); segments only carry cookies.\n */\n readonly hls: HlsLoader = {\n playlist: async (url, signal) => {\n const init: RequestInit = { credentials: this.o.credentials ?? \"same-origin\", signal: signal ?? null };\n if (!this.onApiOrigin(url)) return fetch(url, init);\n const token = await this.o.token?.();\n return (this.o.fetch ?? fetch)(url, token ? { ...init, headers: { Authorization: `Bearer ${token}` } } : init);\n },\n segment: (xhr, url) => {\n if (!xhr.readyState) xhr.open(\"GET\", url, true);\n if (this.o.credentials === \"include\") xhr.withCredentials = true;\n },\n };\n\n setReadScope(key: string): void {\n if (key !== this.readScope.key) this.readScope = { key };\n }\n\n /** Capture before awaiting a write; null tells caches to refetch after a scope change. */\n captureChanges(): (change: ContentKitChange) => void {\n const scope = this.readScope;\n return (change) => this.emit(change, scope === this.readScope ? scope.key : null);\n }\n\n subscribe(listener: (change: ContentKitChange, scope: string | null) => void): () => void {\n this.listeners.add(listener);\n return () => void this.listeners.delete(listener);\n }\n\n private emit(change: ContentKitChange, scope: string | null): void {\n for (const l of this.listeners) {\n try {\n l(change, scope);\n } catch (e) {\n // A host listener's fault must not fail the mutation that succeeded.\n console.error(\"contentkit: change listener failed\", e);\n }\n }\n }\n\n private onApiOrigin(url: string): boolean {\n const here = typeof location === \"undefined\" ? \"http://localhost\" : location.href;\n try {\n return new URL(url, here).origin === new URL(this.base || \"/\", here).origin;\n } catch {\n return false;\n }\n }\n}\n"],"mappings":";;;AAiFA,IAAM,KAAQ,MAAc,EAAE,QAAQ,QAAQ,EAAE,GAWnC,IAAb,MAAkB;CAKa;CAJ7B,4BAA6B,IAAI,IAA8D;CAC/F,YAAoB,EAAE,KAAK,KAAK;CAChC;CAEA,YAAY,GAAiC;EAC3C,IAD2B,KAAA,IAAA,GACvB,OAAO,EAAE,WAAY,UAAU,MAAU,MAAM,kDAAkD;EACrG,KAAK,OAAO,EAAK,EAAE,OAAO;CAC5B;CAGA,MAAM,GAA0B,GAAuB;EACrD,IAAM,IAAI,KAAK,EAAE,SAAS;EAC1B,IAAI,OAAO,KAAM,YAAY;GAC3B,IAAI,CAAC,GAAK,MAAU,MAAM,mBAAmB,EAAO,+BAA+B;GACnF,OAAO,EAAK,EAAE,CAAG,CAAC;EACpB;EAEA,OADI,MAAM,KAAA,IACH,KAAK,OAAO,EAAmB,KADV,EAAK,CAAC;CAEpC;CAEA,MAAM,QAAW,GAAa,EAAE,WAAQ,SAAM,WAAQ,SAAM,iBAA8B,CAAC,GAAe;EACxG,IAAM,IAAI,KAAK,EAAE,SAAS,OACpB,IAAU,IAAI,QAAQ,MAAM,KAAK,EAAE,UAAU,CAAC;EACpD,AAAI,MAAS,KAAA,KAAW,EAAQ,IAAI,gBAAgB,kBAAkB;EACtE,IAAM,IAAQ,MAAM,KAAK,EAAE,QAAQ;EACnC,AAAI,KAAS,CAAC,EAAQ,IAAI,eAAe,KAAG,EAAQ,IAAI,iBAAiB,UAAU,GAAO;EAC1F,IAAM,IAAO,KAAK,EAAE,WAAW;EAC/B,AAAI,KAAM,EAAQ,IAAI,mBAAmB,CAAI;EAC7C,IAAI;EACJ,IAAI;GACF,IAAM,MAAM,EAAE,GAAK;IACjB,QAAQ,MAAW,MAAS,KAAA,IAAY,QAAQ;IAChD;IACA,MAAM,MAAS,KAAA,IAAY,OAAO,KAAK,UAAU,CAAI;IACrD,aAAa,KAAK,EAAE,eAAe;IACnC,QAAQ,KAAU;IAClB,GAAI,IAAY,EAAE,aAAU,IAAI,CAAC;GACnC,CAAC;EACH,SAAS,GAAK;GACZ,MAAM,EAAe,GAAK,GAAQ,kBAAkB,IAAI,IAAI,GAAK,UAAU,CAAC,CAAC,UAAU;EACzF;EACA,IAAI,CAAC,EAAI,IAAI,MAAM,MAAM,EAAoB,CAAG;EAEhD,OADI,IAAc,MAAM,EAAI,KAAK,IACzB,EAAI,WAAW,MAAM,KAAA,IAAY,MAAM,EAAI,KAAK;CAC1D;CAOA,MAA0B;EACxB,UAAU,OAAO,GAAK,MAAW;GAC/B,IAAM,IAAoB;IAAE,aAAa,KAAK,EAAE,eAAe;IAAe,QAAQ,KAAU;GAAK;GACrG,IAAI,CAAC,KAAK,YAAY,CAAG,GAAG,OAAO,MAAM,GAAK,CAAI;GAClD,IAAM,IAAQ,MAAM,KAAK,EAAE,QAAQ;GACnC,QAAQ,KAAK,EAAE,SAAS,MAAA,CAAO,GAAK,IAAQ;IAAE,GAAG;IAAM,SAAS,EAAE,eAAe,UAAU,IAAQ;GAAE,IAAI,CAAI;EAC/G;EACA,UAAU,GAAK,MAAQ;GAErB,AADK,EAAI,cAAY,EAAI,KAAK,OAAO,GAAK,EAAI,GAC1C,KAAK,EAAE,gBAAgB,cAAW,EAAI,kBAAkB;EAC9D;CACF;CAEA,aAAa,GAAmB;EAC9B,AAAI,MAAQ,KAAK,UAAU,QAAK,KAAK,YAAY,EAAE,OAAI;CACzD;CAGA,iBAAqD;EACnD,IAAM,IAAQ,KAAK;EACnB,QAAQ,MAAW,KAAK,KAAK,GAAQ,MAAU,KAAK,YAAY,EAAM,MAAM,IAAI;CAClF;CAEA,UAAU,GAAgF;EAExF,OADA,KAAK,UAAU,IAAI,CAAQ,SACd,KAAK,KAAK,UAAU,OAAO,CAAQ;CAClD;CAEA,KAAa,GAA0B,GAA4B;EACjE,KAAK,IAAM,KAAK,KAAK,WACnB,IAAI;GACF,EAAE,GAAQ,CAAK;EACjB,SAAS,GAAG;GAEV,QAAQ,MAAM,sCAAsC,CAAC;EACvD;CAEJ;CAEA,YAAoB,GAAsB;EACxC,IAAM,IAAO,OAAO,WAAa,MAAc,qBAAqB,SAAS;EAC7E,IAAI;GACF,OAAO,IAAI,IAAI,GAAK,CAAI,CAAC,CAAC,WAAW,IAAI,IAAI,KAAK,QAAQ,KAAK,CAAI,CAAC,CAAC;EACvE,QAAQ;GACN,OAAO;EACT;CACF;AACF"}
1
+ {"version":3,"file":"http.js","names":[],"sources":["../../src/client/http.ts"],"sourcesContent":["import { ContentKitError, readContentKitError, requestFailure } from \"./errors.js\";\nimport type { ContentChange } from \"./content/types.js\";\nimport { CONTENTKIT_MODULES } from \"./generated/routes.js\";\nimport type { FileInfo, Item, RefBody } from \"./generated/wire.js\";\nimport { browserReaderPreferences, type BrowserReaderPreferences } from \"./browser-reader-preferences.js\";\n\n/** A module's base URL, or one per item (a host that splits uploads by kind). */\nexport type Mount = string | ((ref: RefBody) => string);\n\n/**\n * Where each module lives when the host does not serve them all under\n * baseUrl's fixed sub-paths (CONTENTKIT_MODULES: the API version, then the\n * module's path). A mount is the module's full base URL.\n */\nexport interface ContentKitMounts {\n /** Comments, reactions, favorites, polls, bans, moderation. Default `{baseUrl}/v1`. */\n content?: string;\n /** Content items and catalog totals. Default `{baseUrl}/v1`. */\n contents?: string;\n /** Media reads and HLS playlists. Default `{baseUrl}/v1/media`. */\n media?: Mount;\n /** Uploads and commits. Default `{baseUrl}/v1/media/upload`. */\n upload?: Mount;\n /** Content-code lookup. Default `{baseUrl}/v1/codes`. */\n codes?: string;\n /** Entities. Default `{baseUrl}/v1`. */\n entities?: string;\n /** The site's content model. Default `{baseUrl}/v1`. */\n kinds?: string;\n /** User inbox. Default `{baseUrl}/v1/notifications`. */\n notifications?: string;\n /** Content versions: reader plans, reader metadata, forks, downloads. Default `{baseUrl}/v1`. */\n versions?: string;\n /** Offers, group joins and the library. Default `{baseUrl}/v1`. */\n offers?: string;\n /** Discovery: lists, search, typeahead, similar, recommendations, history, signals. Default `{baseUrl}/v1`. */\n discovery?: string;\n /** Bulk import (a service's). Default `{baseUrl}/v1`. */\n import?: string;\n /** Durable archive publishing operations. Default `{baseUrl}/v1`. */\n publishing?: string;\n}\n\nexport type ContentKitModule = keyof ContentKitMounts;\n\n/** A mutation made through the client: the host's cache-invalidation hook gets each one. */\nexport type ContentKitChange =\n /** A commit applied; files are the item's uploads as an editor reads them, without URLs. */\n | { type: \"media.committed\"; ref: RefBody; files: FileInfo[] }\n /** An upload finished processing, as an editor read returns it. */\n | { type: \"media.processed\"; ref: RefBody; file: FileInfo }\n | { type: \"notifications.changed\" }\n | { type: \"notifications.updated\"; item: Item }\n | ContentChange;\n\nexport type TokenSource = () => string | null | undefined | Promise<string | null | undefined>;\n\nexport interface HttpOptions {\n /** Where the host mounts ContentKit's routes (contentkit.Routes.Prefix), e.g. \"/api/contentkit\". */\n baseUrl: string;\n mounts?: ContentKitMounts;\n /** Transport for API calls; pass an authenticating fetch (auth-ui's authFetch). */\n fetch?: typeof fetch;\n /** Bearer for API calls and HLS playlists. An authenticating fetch overrides it. */\n token?: TokenSource;\n /** Sent as Accept-Language. */\n language?: () => string | null | undefined;\n /** Extra headers per call (CSRF). */\n headers?: () => HeadersInit | Promise<HeadersInit>;\n /** Default \"same-origin\"; \"include\" also sends cookies with HLS segment requests. */\n credentials?: RequestCredentials;\n}\n\nexport interface RequestOptions {\n method?: string;\n body?: unknown;\n signal?: AbortSignal;\n /** Read the body as a Blob instead of JSON. */\n blob?: boolean;\n keepalive?: boolean;\n /** Internal preference inventory reads never send unvalidated overrides. */\n guestPreferences?: boolean;\n /** Internal: avoid repeating recovery if a refreshed override is rejected. */\n retryGuestPreferences?: boolean;\n}\n\nconst trim = (u: string) => u.replace(/\\/+$/, \"\");\n\n/** How a player loads HLS: the media client's `hls`. */\nexport interface HlsLoader {\n /** Fetches a playlist or sprite VTT with the app's credentials. */\n playlist(url: string, signal?: AbortSignal): Promise<Response>;\n /** hls.js `xhrSetup` for segments: cookies, never the app's credentials. */\n segment(xhr: XMLHttpRequest, url: string): void;\n}\n\n/** The transport every module shares: mounts, auth, language, errors and change events. */\nexport class Http {\n private readonly listeners = new Set<(change: ContentKitChange, scope: string | null) => void>();\n private readScope = { key: \"{}\" };\n private readonly base: string;\n readonly browserPreferences: BrowserReaderPreferences;\n private viewer?: string | null;\n private viewerRevision = 0;\n private filterIDs?: Set<string>;\n private filterInventory?: Promise<void>;\n\n constructor(private readonly o: HttpOptions) {\n if (typeof o.baseUrl !== \"string\") throw new Error(\"contentkit: createContentKitClient needs baseUrl\");\n this.base = trim(o.baseUrl);\n this.browserPreferences = browserReaderPreferences(this.mount(\"discovery\"));\n }\n\n setViewer(viewer: string | null | undefined): void {\n if (viewer !== this.viewer) this.viewerRevision++;\n this.viewer = viewer;\n }\n\n acceptGuestFilters(filters: readonly { entity: { id: string } }[]): void {\n this.filterIDs = new Set(filters.map((f) => f.entity.id));\n const saved = this.browserPreferences.snapshot().filters;\n const valid = saved.filter((f) => this.filterIDs!.has(f.group_id));\n if (valid.length !== saved.length) this.browserPreferences.setFilters(valid);\n }\n\n private async validateGuestFilters(): Promise<void> {\n if (this.filterIDs || !this.browserPreferences.snapshot().filters.length) return;\n this.filterInventory ??= this.request<{ filters: { entity: { id: string } }[] }>(`${this.mount(\"discovery\")}/content-filters`, { guestPreferences: false })\n .then((result) => this.acceptGuestFilters(result.filters))\n .finally(() => { this.filterInventory = undefined; });\n await this.filterInventory;\n }\n\n /** The module's base URL, without a trailing slash. */\n mount(module: ContentKitModule, ref?: RefBody): string {\n const m = this.o.mounts?.[module];\n if (typeof m === \"function\") {\n if (!ref) throw new Error(`contentkit: the ${module} mount is per item; pass a ref`);\n return trim(m(ref));\n }\n if (m !== undefined) return trim(m);\n return this.base + CONTENTKIT_MODULES[module];\n }\n\n async request<T>(url: string, { method, body, signal, blob, keepalive, guestPreferences = true, retryGuestPreferences = true }: RequestOptions = {}): Promise<T> {\n const viewerRevision = this.viewerRevision;\n if (this.viewer === null && guestPreferences) await this.validateGuestFilters();\n if (viewerRevision !== this.viewerRevision) throw new ContentKitError(\"aborted\", \"The account changed.\");\n const f = this.o.fetch ?? fetch;\n const headers = new Headers(await this.o.headers?.());\n headers.delete(\"ContentKit-Reader-Preferences\");\n if (body !== undefined) headers.set(\"Content-Type\", \"application/json\");\n const token = await this.o.token?.();\n if (token && !headers.has(\"Authorization\")) headers.set(\"Authorization\", `Bearer ${token}`);\n const lang = this.o.language?.();\n if (lang) headers.set(\"Accept-Language\", lang);\n if (this.viewer === null && guestPreferences) {\n const filters = this.browserPreferences.snapshot().filters;\n if (filters.length) headers.set(\"ContentKit-Reader-Preferences\", JSON.stringify({ filters }));\n }\n if (viewerRevision !== this.viewerRevision) throw new ContentKitError(\"aborted\", \"The account changed.\");\n let res: Response;\n try {\n res = await f(url, {\n method: method ?? (body === undefined ? \"GET\" : \"POST\"),\n headers,\n body: body === undefined ? null : JSON.stringify(body),\n credentials: this.o.credentials ?? \"same-origin\",\n signal: signal ?? null,\n ...(keepalive ? { keepalive } : {}),\n });\n } catch (err) {\n throw requestFailure(err, signal, `ContentKit API ${new URL(url, \"http://x\").pathname}`);\n }\n if (!res.ok) {\n const error = await readContentKitError(res);\n const old = headers.get(\"ContentKit-Reader-Preferences\");\n if ((method ?? (body === undefined ? \"GET\" : \"POST\")) === \"GET\" && error.code === \"invalid_request\" && old && this.viewer === null && retryGuestPreferences) {\n this.filterIDs = undefined;\n await this.validateGuestFilters();\n if (viewerRevision !== this.viewerRevision) throw new ContentKitError(\"aborted\", \"The account changed.\");\n const filters = this.browserPreferences.snapshot().filters;\n const current = filters.length ? JSON.stringify({ filters }) : null;\n if (current !== old) return this.request(url, { method, body, signal, blob, keepalive, guestPreferences, retryGuestPreferences: false });\n }\n throw error;\n }\n if (blob) return (await res.blob()) as T;\n return (res.status === 204 ? undefined : await res.json()) as T;\n }\n\n /**\n * How players load this API's HLS. Playlists and sprite VTTs go through\n * the client's fetch, so they carry what its other calls carry (a DPoP\n * proof when that fetch adds one); segments only carry cookies.\n */\n readonly hls: HlsLoader = {\n playlist: async (url, signal) => {\n const init: RequestInit = { credentials: this.o.credentials ?? \"same-origin\", signal: signal ?? null };\n if (!this.onApiOrigin(url)) return fetch(url, init);\n const token = await this.o.token?.();\n return (this.o.fetch ?? fetch)(url, token ? { ...init, headers: { Authorization: `Bearer ${token}` } } : init);\n },\n segment: (xhr, url) => {\n if (!xhr.readyState) xhr.open(\"GET\", url, true);\n if (this.o.credentials === \"include\") xhr.withCredentials = true;\n },\n };\n\n setReadScope(key: string): void {\n if (key !== this.readScope.key) this.readScope = { key };\n }\n\n /** Capture before awaiting a write; null tells caches to refetch after a scope change. */\n captureChanges(): (change: ContentKitChange) => void {\n const scope = this.readScope;\n return (change) => this.emit(change, scope === this.readScope ? scope.key : null);\n }\n\n subscribe(listener: (change: ContentKitChange, scope: string | null) => void): () => void {\n this.listeners.add(listener);\n return () => void this.listeners.delete(listener);\n }\n\n private emit(change: ContentKitChange, scope: string | null): void {\n for (const l of this.listeners) {\n try {\n l(change, scope);\n } catch (e) {\n // A host listener's fault must not fail the mutation that succeeded.\n console.error(\"contentkit: change listener failed\", e);\n }\n }\n }\n\n private onApiOrigin(url: string): boolean {\n const here = typeof location === \"undefined\" ? \"http://localhost\" : location.href;\n try {\n return new URL(url, here).origin === new URL(this.base || \"/\", here).origin;\n } catch {\n return false;\n }\n }\n}\n"],"mappings":"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"}
@@ -20,6 +20,8 @@ export { Beacons, eventId, readerSession, type Attribution, type BeaconOptions,
20
20
  export { LETTERS, attributeInput, fold, fromLocalInput, humanize, letterOf, parseAttribute, parseAttributes, slugify, toLocalInput } from './group-fields.js';
21
21
  export { CodesClient } from './codes.js';
22
22
  export { NotificationsClient, type NotificationItem } from './notifications.js';
23
+ export { ReaderPreferencesClient, type LanguagePreference, type ReaderPreferenceState } from './reader-preferences.js';
24
+ export { BrowserReaderPreferences, type GuestReaderPreferences } from './browser-reader-preferences.js';
23
25
  export { call, routeURL, type CallOptions, type Method, type PageQuery, type PathParams, type RouteQuery, type RoutePath } from './route.js';
24
26
  export { MediaApi, type ReadOptions } from './media/api.js';
25
27
  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';
@@ -0,0 +1,46 @@
1
+ import { ContentKitError } from './errors.js';
2
+ import { Http } from './http.js';
3
+ /** The host's account-owned preferred language, without a route fallback. */
4
+ export interface LanguagePreference {
5
+ load(accountId: string): Promise<string | null | undefined>;
6
+ save(language: string, accountId: string): Promise<void>;
7
+ }
8
+ export interface ReaderPreferenceState {
9
+ language?: string;
10
+ loading: boolean;
11
+ saving: boolean;
12
+ pending: boolean;
13
+ error?: ContentKitError;
14
+ }
15
+ /** Guest storage and explicit, retryable registration inheritance. */
16
+ export declare class ReaderPreferencesClient {
17
+ private readonly http;
18
+ readonly browser: import('./browser-reader-preferences.js').BrowserReaderPreferences;
19
+ private viewer?;
20
+ private adapter?;
21
+ private accountLanguage?;
22
+ private loading;
23
+ private saving;
24
+ private failure?;
25
+ private generation;
26
+ private syncing?;
27
+ private syncingGeneration;
28
+ private state;
29
+ private readonly listeners;
30
+ private stopBrowser?;
31
+ private guestValue;
32
+ constructor(http: Http);
33
+ readonly snapshot: () => ReaderPreferenceState;
34
+ readonly subscribe: (listener: () => void) => (() => void);
35
+ configure(viewer: string | null | undefined, adapter?: LanguagePreference): void;
36
+ setLanguage(language: string): Promise<void>;
37
+ /** Call only after a confirmed registration event, never on ordinary login. */
38
+ inheritAfterRegistration(account: string): Promise<void>;
39
+ retry(): Promise<void>;
40
+ retryInheritance(): Promise<void>;
41
+ private activate;
42
+ private inherit;
43
+ private readonly retryOnline;
44
+ private readonly browserChanged;
45
+ private refresh;
46
+ }
@@ -0,0 +1,147 @@
1
+ import { ContentKitError as e, toContentKitError as t } from "./errors.js";
2
+ import { call as n } from "./route.js";
3
+ //#region src/client/reader-preferences.ts
4
+ var r = class {
5
+ http;
6
+ browser;
7
+ viewer;
8
+ adapter;
9
+ accountLanguage;
10
+ loading = !1;
11
+ saving = !1;
12
+ failure;
13
+ generation = 0;
14
+ syncing;
15
+ syncingGeneration = -1;
16
+ state = Object.freeze({
17
+ loading: !1,
18
+ saving: !1,
19
+ pending: !1
20
+ });
21
+ listeners = /* @__PURE__ */ new Set();
22
+ stopBrowser;
23
+ guestValue;
24
+ constructor(e) {
25
+ this.http = e, this.browser = e.browserPreferences, this.guestValue = JSON.stringify(this.browser.snapshot());
26
+ }
27
+ snapshot = () => this.state;
28
+ subscribe = (e) => (this.listeners.size || (this.stopBrowser = this.browser.subscribe(this.browserChanged), typeof window < "u" && window.addEventListener("online", this.retryOnline)), this.listeners.add(e), this.refresh(), () => {
29
+ this.listeners.delete(e), this.listeners.size || (this.stopBrowser?.(), typeof window < "u" && window.removeEventListener("online", this.retryOnline));
30
+ });
31
+ configure(e, t) {
32
+ if (this.http.setViewer(e), e === this.viewer && t === this.adapter) return;
33
+ this.viewer = e, this.adapter = t;
34
+ let n = ++this.generation;
35
+ this.accountLanguage = void 0, this.failure = void 0, this.saving = !1, this.loading = !!e && !!t, this.refresh(), e && this.activate(n).catch(() => {});
36
+ }
37
+ async setLanguage(n) {
38
+ try {
39
+ n = Intl.getCanonicalLocales(n)[0];
40
+ } catch {
41
+ throw new e("invalid_request", "Invalid preferred language.");
42
+ }
43
+ if (!n) throw new e("invalid_request", "Invalid preferred language.");
44
+ let r = this.viewer;
45
+ if (r == null) {
46
+ this.browser.setLanguage(n), this.refresh();
47
+ return;
48
+ }
49
+ if (!this.adapter) throw Error("contentkit: account language preference adapter is required");
50
+ let i = this.generation;
51
+ this.saving = !0, this.failure = void 0, this.refresh();
52
+ try {
53
+ if (await this.adapter.save(n, r), i !== this.generation) throw new e("aborted", "The account changed.");
54
+ this.accountLanguage = n, this.browser.complete(r, "language");
55
+ } catch (e) {
56
+ throw i === this.generation && (this.failure = t(e)), e;
57
+ } finally {
58
+ i === this.generation && (this.saving = !1, this.refresh());
59
+ }
60
+ }
61
+ async inheritAfterRegistration(e) {
62
+ return this.browser.register(e), this.refresh(), this.viewer === e ? this.retryInheritance() : Promise.resolve();
63
+ }
64
+ async retry() {
65
+ if (!this.viewer) return;
66
+ let e = this.generation;
67
+ if (this.failure = void 0, this.loading = !0, this.refresh(), await this.activate(e), e === this.generation && this.failure) throw this.failure;
68
+ }
69
+ retryInheritance() {
70
+ if (!this.viewer || !this.browser.pending(this.viewer)) return Promise.resolve();
71
+ if (this.syncing && this.syncingGeneration === this.generation) return this.syncing;
72
+ let e = this.viewer, t = this.generation, n = this.inherit(e, t).finally(() => {
73
+ this.syncing === n && (this.syncing = void 0), this.refresh();
74
+ });
75
+ return this.syncing = n, this.syncingGeneration = t, this.refresh(), this.syncing;
76
+ }
77
+ async activate(e) {
78
+ try {
79
+ if (await this.retryInheritance(), e !== this.generation) return;
80
+ let t = this.viewer ? await this.adapter?.load(this.viewer) : void 0;
81
+ e === this.generation && (this.accountLanguage = t ?? void 0);
82
+ } catch (n) {
83
+ e === this.generation && (this.failure = t(n));
84
+ } finally {
85
+ e === this.generation && (this.loading = !1, this.refresh());
86
+ }
87
+ }
88
+ async inherit(e, r) {
89
+ this.failure = void 0;
90
+ try {
91
+ let t = this.browser.pending(e);
92
+ if (!t.languageDone) {
93
+ if (!this.adapter) throw Error("contentkit: account language preference adapter is required");
94
+ let n = await this.adapter.load(e) ?? null;
95
+ if (r !== this.generation) return;
96
+ let i = this.browser.pending(e);
97
+ if (i && !i.languageDone) {
98
+ i.languageObserved || this.browser.observeLanguage(e, n), t = this.browser.pending(e);
99
+ let a = n !== t.initialLanguage && n !== t.language;
100
+ if (!a && n !== t.language && t.language && await this.adapter.save(t.language, e), r !== this.generation) return;
101
+ this.accountLanguage = a ? n ?? void 0 : t.language, this.browser.complete(e, "language");
102
+ }
103
+ }
104
+ if (t = this.browser.pending(e), t && !t.filtersDone) {
105
+ if (r !== this.generation) return;
106
+ let i = await this.http.request(`${this.http.mount("discovery")}/content-filters`, { guestPreferences: !1 });
107
+ if (r !== this.generation) return;
108
+ let a = new Set(i.filters.map((e) => e.entity.id));
109
+ if (await n(this.http, "POST", "/me/content-filters/initialize", { body: {
110
+ account_id: e,
111
+ filters: t.filters.filter((e) => a.has(e.group_id)),
112
+ include_deleted: !1
113
+ } }), r !== this.generation) return;
114
+ this.browser.complete(e, "filters");
115
+ }
116
+ r === this.generation && this.http.captureChanges()({ type: "contentFilters.changed" });
117
+ } catch (e) {
118
+ throw r === this.generation && (this.failure = t(e)), e;
119
+ }
120
+ }
121
+ retryOnline = () => {
122
+ (this.state.pending || this.state.error) && this.retry().catch(() => {});
123
+ };
124
+ browserChanged = () => {
125
+ let e = JSON.stringify(this.browser.snapshot());
126
+ this.viewer === null && e !== this.guestValue && this.http.captureChanges()({ type: "contentFilters.changed" }), this.guestValue = e, this.refresh();
127
+ };
128
+ refresh() {
129
+ let e = {
130
+ language: this.viewer == null ? this.browser.snapshot().language : this.accountLanguage,
131
+ loading: this.loading,
132
+ saving: this.saving,
133
+ pending: !!this.viewer && !!this.browser.pending(this.viewer),
134
+ error: this.failure
135
+ };
136
+ if (e.language !== this.state.language || e.loading !== this.state.loading || e.saving !== this.state.saving || e.pending !== this.state.pending || e.error !== this.state.error) {
137
+ this.state = Object.freeze(e);
138
+ for (let e of this.listeners) try {
139
+ e();
140
+ } catch {}
141
+ }
142
+ }
143
+ };
144
+ //#endregion
145
+ export { r as ReaderPreferencesClient };
146
+
147
+ //# sourceMappingURL=reader-preferences.js.map