@openrails/contentkit-ui 0.104.0-rc.1 → 0.104.0-rc.2

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 (210) hide show
  1. package/README.md +6 -6
  2. package/dist/client/beacons.js +4 -1
  3. package/dist/client/beacons.js.map +1 -1
  4. package/dist/client/contents.d.ts +7 -1
  5. package/dist/client/contents.js +8 -0
  6. package/dist/client/contents.js.map +1 -1
  7. package/dist/client/discovery.d.ts +6 -2
  8. package/dist/client/discovery.js +1 -1
  9. package/dist/client/discovery.js.map +1 -1
  10. package/dist/client/generated/routes.d.ts +41 -28
  11. package/dist/client/generated/routes.js +63 -61
  12. package/dist/client/generated/routes.js.map +1 -1
  13. package/dist/client/generated/wire.d.ts +36 -74
  14. package/dist/client/generated/wire.js.map +1 -1
  15. package/dist/client/index.d.ts +1 -1
  16. package/dist/client/media/windows.d.ts +2 -0
  17. package/dist/client/media/windows.js +4 -1
  18. package/dist/client/media/windows.js.map +1 -1
  19. package/dist/client/stats.d.ts +4 -20
  20. package/dist/client/stats.js +0 -11
  21. package/dist/client/stats.js.map +1 -1
  22. package/dist/client/versions.d.ts +8 -6
  23. package/dist/client/versions.js +15 -10
  24. package/dist/client/versions.js.map +1 -1
  25. package/dist/components/admin/bulk-drafts.d.ts +17 -0
  26. package/dist/components/admin/bulk-drafts.js +41 -0
  27. package/dist/components/admin/bulk-drafts.js.map +1 -0
  28. package/dist/components/admin/content-admin.d.ts +1 -1
  29. package/dist/components/admin/content-admin.js.map +1 -1
  30. package/dist/components/admin/content-manager.d.ts +11 -3
  31. package/dist/components/admin/content-manager.js +739 -380
  32. package/dist/components/admin/content-manager.js.map +1 -1
  33. package/dist/components/admin/group-manager.d.ts +8 -3
  34. package/dist/components/admin/group-manager.js +218 -146
  35. package/dist/components/admin/group-manager.js.map +1 -1
  36. package/dist/components/admin/page-notices.d.ts +16 -0
  37. package/dist/components/admin/page-notices.js +113 -0
  38. package/dist/components/admin/page-notices.js.map +1 -0
  39. package/dist/components/admin/page-picker.d.ts +38 -0
  40. package/dist/components/admin/page-picker.js +159 -0
  41. package/dist/components/admin/page-picker.js.map +1 -0
  42. package/dist/components/admin/reader-content-editor.js +537 -500
  43. package/dist/components/admin/reader-content-editor.js.map +1 -1
  44. package/dist/components/admin/reader-manifest.js +181 -179
  45. package/dist/components/admin/reader-manifest.js.map +1 -1
  46. package/dist/components/admin/shell.d.ts +10 -2
  47. package/dist/components/admin/shell.js +114 -42
  48. package/dist/components/admin/shell.js.map +1 -1
  49. package/dist/components/admin/stats-dashboard.d.ts +4 -9
  50. package/dist/components/admin/stats-dashboard.js +300 -202
  51. package/dist/components/admin/stats-dashboard.js.map +1 -1
  52. package/dist/components/admin/upload-queue.d.ts +14 -0
  53. package/dist/components/admin/upload-queue.js +115 -98
  54. package/dist/components/admin/upload-queue.js.map +1 -1
  55. package/dist/components/content/card.d.ts +31 -3
  56. package/dist/components/content/card.js +324 -158
  57. package/dist/components/content/card.js.map +1 -1
  58. package/dist/components/content/daily-releases.js +14 -12
  59. package/dist/components/content/daily-releases.js.map +1 -1
  60. package/dist/components/content/editor-save.js +27 -25
  61. package/dist/components/content/editor-save.js.map +1 -1
  62. package/dist/components/content/editors.js +274 -253
  63. package/dist/components/content/editors.js.map +1 -1
  64. package/dist/components/content/favorites.d.ts +1 -1
  65. package/dist/components/content/favorites.js +36 -22
  66. package/dist/components/content/favorites.js.map +1 -1
  67. package/dist/components/content/grid.d.ts +11 -4
  68. package/dist/components/content/grid.js +52 -43
  69. package/dist/components/content/grid.js.map +1 -1
  70. package/dist/components/content/history.js +83 -63
  71. package/dist/components/content/history.js.map +1 -1
  72. package/dist/components/content/index.d.ts +5 -3
  73. package/dist/components/content/listing.d.ts +14 -3
  74. package/dist/components/content/listing.js +65 -56
  75. package/dist/components/content/listing.js.map +1 -1
  76. package/dist/components/content/offer-fields.js +6 -6
  77. package/dist/components/content/paged-grid.d.ts +10 -0
  78. package/dist/components/content/paged-grid.js +116 -0
  79. package/dist/components/content/paged-grid.js.map +1 -0
  80. package/dist/components/content/pagination.d.ts +19 -0
  81. package/dist/components/content/pagination.js +97 -0
  82. package/dist/components/content/pagination.js.map +1 -0
  83. package/dist/components/content/paywall.d.ts +9 -0
  84. package/dist/components/content/paywall.js +27 -3
  85. package/dist/components/content/paywall.js.map +1 -1
  86. package/dist/components/content/post-list.d.ts +37 -0
  87. package/dist/components/content/post-list.js +203 -0
  88. package/dist/components/content/post-list.js.map +1 -0
  89. package/dist/components/content/reader-settings.d.ts +2 -0
  90. package/dist/components/content/reader-settings.js +1 -1
  91. package/dist/components/content/reader-settings.js.map +1 -1
  92. package/dist/components/groups/banner.d.ts +6 -2
  93. package/dist/components/groups/banner.js +79 -61
  94. package/dist/components/groups/banner.js.map +1 -1
  95. package/dist/components/groups/directory.d.ts +12 -2
  96. package/dist/components/groups/directory.js +168 -105
  97. package/dist/components/groups/directory.js.map +1 -1
  98. package/dist/components/groups/editor.js +10 -10
  99. package/dist/components/groups/editor.js.map +1 -1
  100. package/dist/components/groups/entity-chips.d.ts +61 -0
  101. package/dist/components/groups/entity-chips.js +382 -0
  102. package/dist/components/groups/entity-chips.js.map +1 -0
  103. package/dist/components/groups/group-members.d.ts +29 -0
  104. package/dist/components/groups/group-members.js +474 -0
  105. package/dist/components/groups/group-members.js.map +1 -0
  106. package/dist/components/groups/header.js +53 -45
  107. package/dist/components/groups/header.js.map +1 -1
  108. package/dist/components/groups/index.d.ts +3 -1
  109. package/dist/components/groups/memberships.d.ts +3 -36
  110. package/dist/components/groups/memberships.js +105 -480
  111. package/dist/components/groups/memberships.js.map +1 -1
  112. package/dist/components/groups/scoped.d.ts +23 -0
  113. package/dist/components/groups/scoped.js +92 -0
  114. package/dist/components/groups/scoped.js.map +1 -0
  115. package/dist/components/media-folder-editor.d.ts +5 -0
  116. package/dist/components/media-folder-editor.js +418 -230
  117. package/dist/components/media-folder-editor.js.map +1 -1
  118. package/dist/components/sortable-list.d.ts +5 -1
  119. package/dist/components/sortable-list.js +97 -81
  120. package/dist/components/sortable-list.js.map +1 -1
  121. package/dist/index.d.ts +1 -1
  122. package/dist/index.js +41 -37
  123. package/dist/locales/admin/de.js +78 -45
  124. package/dist/locales/admin/de.js.map +1 -1
  125. package/dist/locales/admin/en.d.ts +74 -41
  126. package/dist/locales/admin/en.js +78 -45
  127. package/dist/locales/admin/en.js.map +1 -1
  128. package/dist/locales/admin/es.js +78 -45
  129. package/dist/locales/admin/es.js.map +1 -1
  130. package/dist/locales/admin/ja.js +78 -45
  131. package/dist/locales/admin/ja.js.map +1 -1
  132. package/dist/locales/admin/ko.js +78 -45
  133. package/dist/locales/admin/ko.js.map +1 -1
  134. package/dist/locales/admin/zh.js +78 -45
  135. package/dist/locales/admin/zh.js.map +1 -1
  136. package/dist/locales/de.js +1 -0
  137. package/dist/locales/de.js.map +1 -1
  138. package/dist/locales/discovery/de.js +25 -1
  139. package/dist/locales/discovery/de.js.map +1 -1
  140. package/dist/locales/discovery/en.d.ts +24 -0
  141. package/dist/locales/discovery/en.js +25 -1
  142. package/dist/locales/discovery/en.js.map +1 -1
  143. package/dist/locales/discovery/es.js +25 -1
  144. package/dist/locales/discovery/es.js.map +1 -1
  145. package/dist/locales/discovery/ja.js +25 -1
  146. package/dist/locales/discovery/ja.js.map +1 -1
  147. package/dist/locales/discovery/ko.js +25 -1
  148. package/dist/locales/discovery/ko.js.map +1 -1
  149. package/dist/locales/discovery/zh.js +25 -1
  150. package/dist/locales/discovery/zh.js.map +1 -1
  151. package/dist/locales/en.d.ts +119 -43
  152. package/dist/locales/en.js +1 -0
  153. package/dist/locales/en.js.map +1 -1
  154. package/dist/locales/es.js +1 -0
  155. package/dist/locales/es.js.map +1 -1
  156. package/dist/locales/groups/de.js +22 -4
  157. package/dist/locales/groups/de.js.map +1 -1
  158. package/dist/locales/groups/en.d.ts +20 -2
  159. package/dist/locales/groups/en.js +22 -4
  160. package/dist/locales/groups/en.js.map +1 -1
  161. package/dist/locales/groups/es.js +22 -4
  162. package/dist/locales/groups/es.js.map +1 -1
  163. package/dist/locales/groups/ja.js +22 -4
  164. package/dist/locales/groups/ja.js.map +1 -1
  165. package/dist/locales/groups/ko.js +22 -4
  166. package/dist/locales/groups/ko.js.map +1 -1
  167. package/dist/locales/groups/zh.js +22 -4
  168. package/dist/locales/groups/zh.js.map +1 -1
  169. package/dist/locales/ja.js +1 -0
  170. package/dist/locales/ja.js.map +1 -1
  171. package/dist/locales/ko.js +1 -0
  172. package/dist/locales/ko.js.map +1 -1
  173. package/dist/locales/zh.js +1 -0
  174. package/dist/locales/zh.js.map +1 -1
  175. package/dist/react/discovery.d.ts +16 -0
  176. package/dist/react/discovery.js +69 -37
  177. package/dist/react/discovery.js.map +1 -1
  178. package/dist/react/groups.d.ts +19 -3
  179. package/dist/react/groups.js +49 -22
  180. package/dist/react/groups.js.map +1 -1
  181. package/dist/react/index.d.ts +6 -4
  182. package/dist/react/page-notices.d.ts +13 -0
  183. package/dist/react/page-notices.js +38 -0
  184. package/dist/react/page-notices.js.map +1 -0
  185. package/dist/react/post-reads.d.ts +23 -0
  186. package/dist/react/post-reads.js +90 -0
  187. package/dist/react/post-reads.js.map +1 -0
  188. package/dist/react/remembered-sort.d.ts +5 -0
  189. package/dist/react/remembered-sort.js +29 -0
  190. package/dist/react/remembered-sort.js.map +1 -0
  191. package/dist/react/resources.js +2 -2
  192. package/dist/react/resources.js.map +1 -1
  193. package/dist/react/stats.d.ts +3 -7
  194. package/dist/react/stats.js +2 -6
  195. package/dist/react/stats.js.map +1 -1
  196. package/dist/react/use-engagement.d.ts +2 -0
  197. package/dist/react/use-engagement.js +8 -3
  198. package/dist/react/use-engagement.js.map +1 -1
  199. package/dist/react.js +29 -27
  200. package/dist/styles.css +1 -1
  201. package/dist/urls/index.d.ts +1 -1
  202. package/dist/urls.js +2 -2
  203. package/dist/urls.js.map +1 -1
  204. package/package.json +1 -1
  205. package/dist/components/admin/page-review.d.ts +0 -13
  206. package/dist/components/admin/page-review.js +0 -225
  207. package/dist/components/admin/page-review.js.map +0 -1
  208. package/dist/react/page-review.d.ts +0 -14
  209. package/dist/react/page-review.js +0 -37
  210. package/dist/react/page-review.js.map +0 -1
package/README.md CHANGED
@@ -755,12 +755,12 @@ const nav = useMemo(() => ({ pathname: location.pathname, search: location.searc
755
755
  Management authorization remains enforced by the server. Set
756
756
  `presentation="header"` for the shared centered-header dropdown.
757
757
 
758
- - **`PageReview`**: retained page sources, original filenames, automatic cleanup
759
- reasons, previews, inclusion overrides and ordering. `ContentManager` includes
760
- it for reader versions; use `content` and `version` to embed it separately.
761
- `usePageReview()` exposes the same revision-checked API. Explicitly segmented
762
- versions keep their selection/order and show read-only review controls so
763
- cleanup cannot move pages across release/access boundaries.
758
+ - **`PageNotices`**: quiet, collapsed notes about automatic page changes:
759
+ pages removed as blank, duplicate or covered by a spread (one-click Restore)
760
+ and archives put in name order (Dismiss). It renders nothing without notices
761
+ and never blocks saving or publishing. `ContentManager` includes it for reader
762
+ versions; `usePageNotices()` exposes the same API. Page tiles are named by
763
+ their place and reordered by dragging the whole tile.
764
764
 
765
765
  - **`ReaderManifestEditor`**: a reader version's manifest over its published
766
766
  pages and audio: scroll anchors, audio cues (matched to pages or anchors by
@@ -158,7 +158,10 @@ var p = class {
158
158
  async contexts(e, n, r) {
159
159
  if (this.paused() || !await this.recording()) return n === this.generation && this.pause(), null;
160
160
  if (n !== this.generation) return null;
161
- let i = [...new Map(e.map((e) => [m(e), e])).values()], a = [], o = this.session();
161
+ let i = [...new Map(e.map((e) => [m(e), {
162
+ content_id: e.content_id,
163
+ version_id: e.version_id
164
+ }])).values()], a = [], o = this.session();
162
165
  for (let e = 0; e < i.length; e += t) a.push(this.client.discovery.activityContexts(i.slice(e, e + t), o, r));
163
166
  let s = await Promise.all(a);
164
167
  if (n !== this.generation) return null;
@@ -1 +1 @@
1
- {"version":3,"file":"beacons.js","names":[],"sources":["../../src/client/beacons.ts"],"sourcesContent":["import type { ContentKitClient } from \"./client.js\";\nimport { toContentKitError } from \"./errors.js\";\nimport type { ActivityContext, ActivityTarget, ExposureInput, SignalInput } from \"./generated/wire.js\";\n\nexport interface BeaconOptions {\n /** A signed-out visitor identity, persistent across this browser's tabs. */\n session?: () => string | undefined;\n /** How long events gather before they are sent, ms. Default 2000. */\n delay?: number;\n /** How long beacons stay off after the site records no signals, ms, before the content model is read again. Default 60000. */\n recheck?: number;\n}\n\n/** Where a click came from: the list render it was shown in. */\nexport interface Attribution {\n /** Language of the displayed content; defaults to the client's request language. */\n language?: string;\n render_id?: string;\n surface?: string;\n /** 1-based position in that render. */\n position?: number;\n}\n\n\nconst MAX = 100;\n// Fetch's keepalive limit includes all in-flight bodies in the fetch group.\n// Leave headroom and serialize this module's keepalive requests across clients.\nconst KEEPALIVE_BYTES = 60 * 1024;\nconst FLUSH_BYTES = 48 * 1024;\nlet keepaliveTail: Promise<void> = Promise.resolve();\nconst encoder = new TextEncoder();\nconst bytes = (field: \"signals\" | \"exposures\", rows: readonly unknown[], session?: string) => encoder.encode(JSON.stringify({ session, [field]: rows })).length;\n\nfunction packets<T>(items: readonly T[], field: \"signals\" | \"exposures\", session?: string): { items: T[]; bytes: number }[] {\n const out: { items: T[]; bytes: number }[] = [];\n let current: T[] = [];\n for (const item of items) {\n if (current.length && (current.length === MAX || bytes(field, [...current, item], session) > KEEPALIVE_BYTES)) {\n out.push({ items: current, bytes: bytes(field, current, session) });\n current = [];\n }\n current.push(item);\n }\n if (current.length) out.push({ items: current, bytes: bytes(field, current, session) });\n return out;\n}\n\nfunction serializeKeepalive(send: () => Promise<void>): Promise<void> {\n const pending = keepaliveTail.then(send, send);\n keepaliveTail = pending.then(() => {}, () => {});\n return pending;\n}\nconst SESSION_KEY = \"contentkit.visitor.v1\";\nexport const eventId = () => (typeof crypto !== \"undefined\" && \"randomUUID\" in crypto ? crypto.randomUUID() : `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`);\n\nlet unstored: string | undefined;\n\n/** A random signed-out browser identity shared across tabs/reloads, never an account credential. */\nexport function readerSession(): string {\n try {\n const kept = localStorage.getItem(SESSION_KEY);\n if (kept) return kept;\n const made = eventId();\n localStorage.setItem(SESSION_KEY, made);\n return made;\n } catch {\n return (unstored ??= eventId());\n }\n}\n\n/**\n * Batched, best-effort signals and exposures: gathered for `delay`, sent in\n * one request each, flushed with keepalive when the page hides. While the\n * content model says the site records no signals (or a send is refused as\n * not_configured or unavailable) events are dropped; after `recheck` the\n * next event reads the model again, so signals resume when the site's\n * signal plane is back.\n */\nexport class Beacons {\n private signals: SignalInput[] = [];\n private exposures: ExposureInput[] = [];\n private timer: ReturnType<typeof setTimeout> | undefined;\n private offUntil = 0;\n private recorded: Promise<boolean> | undefined;\n private generation = 0;\n private hidden = false;\n private readonly pending = new Set<Promise<void>>();\n private readonly exposureContexts = new Map<string, { active: boolean; releaseRevision: number; occurredAt?: string; contexts: Map<string, Promise<Receipt | null>> }>();\n private readonly session: () => string | undefined;\n private readonly delay: number;\n private readonly recheck: number;\n\n constructor(\n private readonly client: Pick<ContentKitClient, \"discovery\" | \"kinds\"> & Partial<Pick<ContentKitClient, \"preferences\">>,\n o: BeaconOptions = {},\n ) {\n this.session = o.session ?? readerSession;\n this.delay = o.delay ?? 2000;\n this.recheck = o.recheck ?? 60_000;\n client.preferences?.subscribeIdentity(() => {\n // The old credentials can no longer be assumed available for a flush.\n this.generation++;\n clearTimeout(this.timer);\n this.timer = undefined;\n this.signals = [];\n this.exposures = [];\n this.pending.clear();\n for (const state of this.exposureContexts.values()) state.active = false;\n this.exposureContexts.clear();\n this.recorded = undefined;\n });\n if (typeof addEventListener === \"function\") {\n addEventListener(\"pagehide\", () => { this.hidden = true; void this.flush(true); });\n addEventListener(\"pageshow\", () => { this.hidden = false; });\n addEventListener(\"visibilitychange\", () => {\n this.hidden = typeof document !== \"undefined\" && document.visibilityState === \"hidden\";\n if (this.hidden) void this.flush(true);\n });\n }\n }\n\n /** A click on a listed card. Acquire its access snapshot at observation time. */\n click(content: string, a: Attribution & { version_id?: string } = {}): void {\n if (this.paused()) return;\n const generation = this.generation;\n const event = eventId(), at = new Date().toISOString();\n const { language, ...attribution } = a;\n const target = { content_id: content, version_id: a.version_id };\n this.track(this.contexts([target], generation, language).then((receipts) => {\n const receipt = receipts?.get(targetKey(target));\n if (!receipt || generation !== this.generation) return;\n this.signal({ content_id: content, type: \"click\", event_id: event, ...attribution,\n occurred_at: receipt.context?.occurred_at ?? at, activity_token: receipt.context?.token });\n }), generation);\n }\n\n /** A cumulative result-list stage; each newly observed target gets one receipt. */\n exposure(e: ExposureInput): void {\n if (this.paused() || !e.shown.length) return;\n const generation = this.generation;\n const key = `${e.render_id}\\u0000${e.stage}`;\n let state = this.exposureContexts.get(key);\n if (!state) {\n state = { active: true, releaseRevision: 0, contexts: new Map() };\n this.exposureContexts.set(key, state);\n }\n state.releaseRevision++;\n const captured = state;\n const unseen = e.shown.filter((p) => !captured.contexts.has(targetKey(p)));\n if (unseen.length) {\n const contexts = this.contexts(unseen, generation, e.language);\n for (const target of unseen) {\n const key = targetKey(target);\n const pending: Promise<Receipt | null> = contexts.then((values) => {\n const receipt = values?.get(key) ?? null;\n if (!receipt && captured.contexts.get(key) === pending) captured.contexts.delete(key);\n return receipt;\n }, (error: unknown) => {\n if (captured.contexts.get(key) === pending) captured.contexts.delete(key);\n throw error;\n });\n captured.contexts.set(key, pending);\n }\n }\n this.track(Promise.all(e.shown.map((p) => captured.contexts.get(targetKey(p))!)).then((receipts) => {\n if (generation !== this.generation || !captured.active || receipts.some((r) => !r)) return;\n const valid = receipts as Receipt[];\n if (new Set(valid.map((r) => r.enabled)).size !== 1) return;\n captured.occurredAt ??= valid.find((r) => r.context)?.context?.occurred_at ?? e.occurred_at ?? new Date().toISOString();\n const enriched = { ...e, occurred_at: captured.occurredAt,\n shown: e.shown.map((p, i) => ({ ...p, activity_token: valid[i]?.context?.token })) };\n // A later cumulative revision supersedes a queued earlier snapshot.\n const previous = this.exposures.find((x) => x.render_id === e.render_id && x.stage === e.stage);\n if (previous && (previous.revision ?? 0) > (e.revision ?? 0)) return;\n this.exposures = this.exposures.filter((x) => x.render_id !== e.render_id || x.stage !== e.stage);\n this.exposures.push(enriched);\n this.schedule();\n }), generation);\n }\n\n /** Keep receipt state when a mounted list resumes (including StrictMode replay). */\n beginExposure(render: string): void {\n for (const stage of [\"served\", \"rendered\", \"visible\"]) {\n const state = this.exposureContexts.get(`${render}\\u0000${stage}`);\n if (state) state.releaseRevision++;\n }\n }\n\n /** Retire receipt state after a list unmounts without cancelling observations. */\n endExposure(render: string): void {\n for (const stage of [\"served\", \"rendered\", \"visible\"]) {\n const key = `${render}\\u0000${stage}`, state = this.exposureContexts.get(key);\n if (!state) continue;\n const release = ++state.releaseRevision;\n queueMicrotask(() => {\n if (this.exposureContexts.get(key) === state && state.releaseRevision === release) this.exposureContexts.delete(key);\n });\n }\n }\n\n /** Sends ready observations; receipts are never acquired for the first time here. */\n async flush(keepalive = false): Promise<void> {\n clearTimeout(this.timer);\n this.timer = undefined;\n const generation = this.generation;\n await Promise.allSettled([...this.pending]);\n if (generation !== this.generation) return;\n clearTimeout(this.timer);\n this.timer = undefined;\n const signals = this.signals.splice(0), exposures = this.exposures.splice(0);\n if (this.paused() || (!signals.length && !exposures.length)) return;\n const session = this.session();\n const jobs = [\n ...packets(signals, \"signals\", session).map((packet) => ({ ...packet, send: (persist: boolean) => this.client.discovery.signals(packet.items, session, { keepalive: persist }) })),\n ...packets(exposures, \"exposures\", session).map((packet) => ({ ...packet, send: (persist: boolean) => this.client.discovery.exposures(packet.items, session, { keepalive: persist }) })),\n ];\n const done = await Promise.allSettled(jobs.map((job) => {\n const persist = keepalive && job.bytes <= KEEPALIVE_BYTES;\n const send = () => generation === this.generation ? job.send(persist) : Promise.resolve();\n // A single oversized cumulative exposure cannot be split without losing\n // history. Normal fetch is the best effort available during pagehide.\n return persist ? serializeKeepalive(send) : send();\n }));\n if (generation === this.generation && done.some((r) => r.status === \"rejected\" && unavailable(r.reason))) this.pause();\n }\n\n private async contexts(targets: readonly ActivityTarget[], generation: number, language?: string): Promise<Map<string, Receipt> | null> {\n if (this.paused() || !(await this.recording())) {\n if (generation === this.generation) this.pause();\n return null;\n }\n if (generation !== this.generation) return null;\n const unique = [...new Map(targets.map((target) => [targetKey(target), target])).values()];\n const batches = [];\n const session = this.session();\n for (let i = 0; i < unique.length; i += MAX) batches.push(this.client.discovery.activityContexts(unique.slice(i, i + MAX), session, language));\n const responses = await Promise.all(batches);\n if (generation !== this.generation) return null;\n const out = new Map<string, Receipt>();\n for (let i = 0; i < responses.length; i++) {\n const response = responses[i]!;\n if (typeof response?.enabled !== \"boolean\" || (response.enabled && !Array.isArray(response.contexts))) throw new Error(\"contentkit: invalid activity context response\");\n const contexts = new Map((response.contexts ?? []).map((context) => [targetKey(context), context]));\n for (const target of unique.slice(i * MAX, (i + 1) * MAX)) {\n const context = contexts.get(targetKey(target));\n if (response.enabled && (!context?.token || !Number.isFinite(Date.parse(context.occurred_at)))) throw new Error(\"contentkit: invalid activity context\");\n out.set(targetKey(target), { enabled: response.enabled, context: response.enabled ? context : undefined });\n }\n }\n return out;\n }\n\n private track(task: Promise<void>, generation: number): void {\n const pending = task.catch((error) => { if (generation === this.generation && unavailable(error)) this.pause(); }).finally(() => { this.pending.delete(pending); });\n this.pending.add(pending);\n }\n\n private signal(s: SignalInput): void {\n if (this.paused()) return;\n this.signals.push(s);\n this.schedule();\n }\n private paused(): boolean {\n if (this.offUntil > Date.now()) return true;\n this.offUntil = 0;\n return false;\n }\n private pause(): void { this.offUntil = Date.now() + this.recheck; this.recorded = undefined; }\n private recording(): Promise<boolean> {\n return (this.recorded ??= this.client.kinds().then((m) => m.signals, () => true));\n }\n private schedule(): void {\n const queued = bytes(\"signals\", this.signals, this.session()) + bytes(\"exposures\", this.exposures, this.session());\n if (this.signals.length + this.exposures.length >= MAX || queued >= FLUSH_BYTES) return void this.flush(this.hidden);\n this.timer ??= setTimeout(() => void this.flush(), this.delay);\n }\n}\n\ntype Receipt = { enabled: boolean; context?: ActivityContext };\nconst targetKey = (target: ActivityTarget) => `${target.content_id}\\u0000${target.version_id ?? \"\"}`;\nconst unavailable = (error: unknown) => [\"not_configured\", \"unavailable\", \"server_unavailable\"].includes(toContentKitError(error).code);\n"],"mappings":"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"}
1
+ {"version":3,"file":"beacons.js","names":[],"sources":["../../src/client/beacons.ts"],"sourcesContent":["import type { ContentKitClient } from \"./client.js\";\nimport { toContentKitError } from \"./errors.js\";\nimport type { ActivityContext, ActivityTarget, ExposureInput, SignalInput } from \"./generated/wire.js\";\n\nexport interface BeaconOptions {\n /** A signed-out visitor identity, persistent across this browser's tabs. */\n session?: () => string | undefined;\n /** How long events gather before they are sent, ms. Default 2000. */\n delay?: number;\n /** How long beacons stay off after the site records no signals, ms, before the content model is read again. Default 60000. */\n recheck?: number;\n}\n\n/** Where a click came from: the list render it was shown in. */\nexport interface Attribution {\n /** Language of the displayed content; defaults to the client's request language. */\n language?: string;\n render_id?: string;\n surface?: string;\n /** 1-based position in that render. */\n position?: number;\n}\n\n\nconst MAX = 100;\n// Fetch's keepalive limit includes all in-flight bodies in the fetch group.\n// Leave headroom and serialize this module's keepalive requests across clients.\nconst KEEPALIVE_BYTES = 60 * 1024;\nconst FLUSH_BYTES = 48 * 1024;\nlet keepaliveTail: Promise<void> = Promise.resolve();\nconst encoder = new TextEncoder();\nconst bytes = (field: \"signals\" | \"exposures\", rows: readonly unknown[], session?: string) => encoder.encode(JSON.stringify({ session, [field]: rows })).length;\n\nfunction packets<T>(items: readonly T[], field: \"signals\" | \"exposures\", session?: string): { items: T[]; bytes: number }[] {\n const out: { items: T[]; bytes: number }[] = [];\n let current: T[] = [];\n for (const item of items) {\n if (current.length && (current.length === MAX || bytes(field, [...current, item], session) > KEEPALIVE_BYTES)) {\n out.push({ items: current, bytes: bytes(field, current, session) });\n current = [];\n }\n current.push(item);\n }\n if (current.length) out.push({ items: current, bytes: bytes(field, current, session) });\n return out;\n}\n\nfunction serializeKeepalive(send: () => Promise<void>): Promise<void> {\n const pending = keepaliveTail.then(send, send);\n keepaliveTail = pending.then(() => {}, () => {});\n return pending;\n}\nconst SESSION_KEY = \"contentkit.visitor.v1\";\nexport const eventId = () => (typeof crypto !== \"undefined\" && \"randomUUID\" in crypto ? crypto.randomUUID() : `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`);\n\nlet unstored: string | undefined;\n\n/** A random signed-out browser identity shared across tabs/reloads, never an account credential. */\nexport function readerSession(): string {\n try {\n const kept = localStorage.getItem(SESSION_KEY);\n if (kept) return kept;\n const made = eventId();\n localStorage.setItem(SESSION_KEY, made);\n return made;\n } catch {\n return (unstored ??= eventId());\n }\n}\n\n/**\n * Batched, best-effort signals and exposures: gathered for `delay`, sent in\n * one request each, flushed with keepalive when the page hides. While the\n * content model says the site records no signals (or a send is refused as\n * not_configured or unavailable) events are dropped; after `recheck` the\n * next event reads the model again, so signals resume when the site's\n * signal plane is back.\n */\nexport class Beacons {\n private signals: SignalInput[] = [];\n private exposures: ExposureInput[] = [];\n private timer: ReturnType<typeof setTimeout> | undefined;\n private offUntil = 0;\n private recorded: Promise<boolean> | undefined;\n private generation = 0;\n private hidden = false;\n private readonly pending = new Set<Promise<void>>();\n private readonly exposureContexts = new Map<string, { active: boolean; releaseRevision: number; occurredAt?: string; contexts: Map<string, Promise<Receipt | null>> }>();\n private readonly session: () => string | undefined;\n private readonly delay: number;\n private readonly recheck: number;\n\n constructor(\n private readonly client: Pick<ContentKitClient, \"discovery\" | \"kinds\"> & Partial<Pick<ContentKitClient, \"preferences\">>,\n o: BeaconOptions = {},\n ) {\n this.session = o.session ?? readerSession;\n this.delay = o.delay ?? 2000;\n this.recheck = o.recheck ?? 60_000;\n client.preferences?.subscribeIdentity(() => {\n // The old credentials can no longer be assumed available for a flush.\n this.generation++;\n clearTimeout(this.timer);\n this.timer = undefined;\n this.signals = [];\n this.exposures = [];\n this.pending.clear();\n for (const state of this.exposureContexts.values()) state.active = false;\n this.exposureContexts.clear();\n this.recorded = undefined;\n });\n if (typeof addEventListener === \"function\") {\n addEventListener(\"pagehide\", () => { this.hidden = true; void this.flush(true); });\n addEventListener(\"pageshow\", () => { this.hidden = false; });\n addEventListener(\"visibilitychange\", () => {\n this.hidden = typeof document !== \"undefined\" && document.visibilityState === \"hidden\";\n if (this.hidden) void this.flush(true);\n });\n }\n }\n\n /** A click on a listed card. Acquire its access snapshot at observation time. */\n click(content: string, a: Attribution & { version_id?: string } = {}): void {\n if (this.paused()) return;\n const generation = this.generation;\n const event = eventId(), at = new Date().toISOString();\n const { language, ...attribution } = a;\n const target = { content_id: content, version_id: a.version_id };\n this.track(this.contexts([target], generation, language).then((receipts) => {\n const receipt = receipts?.get(targetKey(target));\n if (!receipt || generation !== this.generation) return;\n this.signal({ content_id: content, type: \"click\", event_id: event, ...attribution,\n occurred_at: receipt.context?.occurred_at ?? at, activity_token: receipt.context?.token });\n }), generation);\n }\n\n /** A cumulative result-list stage; each newly observed target gets one receipt. */\n exposure(e: ExposureInput): void {\n if (this.paused() || !e.shown.length) return;\n const generation = this.generation;\n const key = `${e.render_id}\\u0000${e.stage}`;\n let state = this.exposureContexts.get(key);\n if (!state) {\n state = { active: true, releaseRevision: 0, contexts: new Map() };\n this.exposureContexts.set(key, state);\n }\n state.releaseRevision++;\n const captured = state;\n const unseen = e.shown.filter((p) => !captured.contexts.has(targetKey(p)));\n if (unseen.length) {\n const contexts = this.contexts(unseen, generation, e.language);\n for (const target of unseen) {\n const key = targetKey(target);\n const pending: Promise<Receipt | null> = contexts.then((values) => {\n const receipt = values?.get(key) ?? null;\n if (!receipt && captured.contexts.get(key) === pending) captured.contexts.delete(key);\n return receipt;\n }, (error: unknown) => {\n if (captured.contexts.get(key) === pending) captured.contexts.delete(key);\n throw error;\n });\n captured.contexts.set(key, pending);\n }\n }\n this.track(Promise.all(e.shown.map((p) => captured.contexts.get(targetKey(p))!)).then((receipts) => {\n if (generation !== this.generation || !captured.active || receipts.some((r) => !r)) return;\n const valid = receipts as Receipt[];\n if (new Set(valid.map((r) => r.enabled)).size !== 1) return;\n captured.occurredAt ??= valid.find((r) => r.context)?.context?.occurred_at ?? e.occurred_at ?? new Date().toISOString();\n const enriched = { ...e, occurred_at: captured.occurredAt,\n shown: e.shown.map((p, i) => ({ ...p, activity_token: valid[i]?.context?.token })) };\n // A later cumulative revision supersedes a queued earlier snapshot.\n const previous = this.exposures.find((x) => x.render_id === e.render_id && x.stage === e.stage);\n if (previous && (previous.revision ?? 0) > (e.revision ?? 0)) return;\n this.exposures = this.exposures.filter((x) => x.render_id !== e.render_id || x.stage !== e.stage);\n this.exposures.push(enriched);\n this.schedule();\n }), generation);\n }\n\n /** Keep receipt state when a mounted list resumes (including StrictMode replay). */\n beginExposure(render: string): void {\n for (const stage of [\"served\", \"rendered\", \"visible\"]) {\n const state = this.exposureContexts.get(`${render}\\u0000${stage}`);\n if (state) state.releaseRevision++;\n }\n }\n\n /** Retire receipt state after a list unmounts without cancelling observations. */\n endExposure(render: string): void {\n for (const stage of [\"served\", \"rendered\", \"visible\"]) {\n const key = `${render}\\u0000${stage}`, state = this.exposureContexts.get(key);\n if (!state) continue;\n const release = ++state.releaseRevision;\n queueMicrotask(() => {\n if (this.exposureContexts.get(key) === state && state.releaseRevision === release) this.exposureContexts.delete(key);\n });\n }\n }\n\n /** Sends ready observations; receipts are never acquired for the first time here. */\n async flush(keepalive = false): Promise<void> {\n clearTimeout(this.timer);\n this.timer = undefined;\n const generation = this.generation;\n await Promise.allSettled([...this.pending]);\n if (generation !== this.generation) return;\n clearTimeout(this.timer);\n this.timer = undefined;\n const signals = this.signals.splice(0), exposures = this.exposures.splice(0);\n if (this.paused() || (!signals.length && !exposures.length)) return;\n const session = this.session();\n const jobs = [\n ...packets(signals, \"signals\", session).map((packet) => ({ ...packet, send: (persist: boolean) => this.client.discovery.signals(packet.items, session, { keepalive: persist }) })),\n ...packets(exposures, \"exposures\", session).map((packet) => ({ ...packet, send: (persist: boolean) => this.client.discovery.exposures(packet.items, session, { keepalive: persist }) })),\n ];\n const done = await Promise.allSettled(jobs.map((job) => {\n const persist = keepalive && job.bytes <= KEEPALIVE_BYTES;\n const send = () => generation === this.generation ? job.send(persist) : Promise.resolve();\n // A single oversized cumulative exposure cannot be split without losing\n // history. Normal fetch is the best effort available during pagehide.\n return persist ? serializeKeepalive(send) : send();\n }));\n if (generation === this.generation && done.some((r) => r.status === \"rejected\" && unavailable(r.reason))) this.pause();\n }\n\n private async contexts(targets: readonly ActivityTarget[], generation: number, language?: string): Promise<Map<string, Receipt> | null> {\n if (this.paused() || !(await this.recording())) {\n if (generation === this.generation) this.pause();\n return null;\n }\n if (generation !== this.generation) return null;\n // Exposure rows carry list fields (position); the strict endpoint takes only the target.\n const unique = [...new Map(targets.map((t) => [targetKey(t), { content_id: t.content_id, version_id: t.version_id }])).values()];\n const batches = [];\n const session = this.session();\n for (let i = 0; i < unique.length; i += MAX) batches.push(this.client.discovery.activityContexts(unique.slice(i, i + MAX), session, language));\n const responses = await Promise.all(batches);\n if (generation !== this.generation) return null;\n const out = new Map<string, Receipt>();\n for (let i = 0; i < responses.length; i++) {\n const response = responses[i]!;\n if (typeof response?.enabled !== \"boolean\" || (response.enabled && !Array.isArray(response.contexts))) throw new Error(\"contentkit: invalid activity context response\");\n const contexts = new Map((response.contexts ?? []).map((context) => [targetKey(context), context]));\n for (const target of unique.slice(i * MAX, (i + 1) * MAX)) {\n const context = contexts.get(targetKey(target));\n if (response.enabled && (!context?.token || !Number.isFinite(Date.parse(context.occurred_at)))) throw new Error(\"contentkit: invalid activity context\");\n out.set(targetKey(target), { enabled: response.enabled, context: response.enabled ? context : undefined });\n }\n }\n return out;\n }\n\n private track(task: Promise<void>, generation: number): void {\n const pending = task.catch((error) => { if (generation === this.generation && unavailable(error)) this.pause(); }).finally(() => { this.pending.delete(pending); });\n this.pending.add(pending);\n }\n\n private signal(s: SignalInput): void {\n if (this.paused()) return;\n this.signals.push(s);\n this.schedule();\n }\n private paused(): boolean {\n if (this.offUntil > Date.now()) return true;\n this.offUntil = 0;\n return false;\n }\n private pause(): void { this.offUntil = Date.now() + this.recheck; this.recorded = undefined; }\n private recording(): Promise<boolean> {\n return (this.recorded ??= this.client.kinds().then((m) => m.signals, () => true));\n }\n private schedule(): void {\n const queued = bytes(\"signals\", this.signals, this.session()) + bytes(\"exposures\", this.exposures, this.session());\n if (this.signals.length + this.exposures.length >= MAX || queued >= FLUSH_BYTES) return void this.flush(this.hidden);\n this.timer ??= setTimeout(() => void this.flush(), this.delay);\n }\n}\n\ntype Receipt = { enabled: boolean; context?: ActivityContext };\nconst targetKey = (target: ActivityTarget) => `${target.content_id}\\u0000${target.version_id ?? \"\"}`;\nconst unavailable = (error: unknown) => [\"not_configured\", \"unavailable\", \"server_unavailable\"].includes(toContentKitError(error).code);\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { CatalogText, Content, ContentInput, ContentPatch, Creator, InlineImage, Segment, Version, VersionInput } from './generated/wire.js';
1
+ import { CatalogText, Content, ContentInput, ContentPatch, Creator, InlineImage, ManageCount, Segment, Version, VersionInput } from './generated/wire.js';
2
2
  import { Http } from './http.js';
3
3
  import { RouteQuery } from './route.js';
4
4
  /** The URL scheme of an image reference in a content body. */
@@ -15,6 +15,10 @@ export declare const imageRefs: () => RegExp;
15
15
  export type ManageQuery = RouteQuery<"GET", "/manage"> & {
16
16
  kind?: string;
17
17
  };
18
+ /** The /manage/{kind} filters a count takes. */
19
+ export type ManageCountQuery = RouteQuery<"GET", "/manage/{kind}/count"> & {
20
+ kind: string;
21
+ };
18
22
  /**
19
23
  * Content items: a viewer's reads, and the writes of their creators, their
20
24
  * owner group's editors and root:content:write holders.
@@ -27,6 +31,8 @@ export declare class ContentsClient {
27
31
  version(id: string, vid: string, language?: string, signal?: AbortSignal): Promise<Version>;
28
32
  /** Items the caller manages, newest first: all for root:content:write holders, else theirs or an owner group's. */
29
33
  manage(q?: ManageQuery, signal?: AbortSignal): Promise<Content[]>;
34
+ /** How many items of a kind the caller manages under these filters: exact up to 10,000, then truncated. */
35
+ manageCount(q: ManageCountQuery, signal?: AbortSignal): Promise<ManageCount>;
30
36
  /** Creates a draft. */
31
37
  create(input: ContentInput): Promise<Content>;
32
38
  update(id: string, patch: ContentPatch): Promise<Content>;
@@ -33,6 +33,14 @@ var t = "contentkit:", n = (e) => t + e, r = () => /contentkit:(i-[0-9a-f]{8}-[0
33
33
  signal: n
34
34
  });
35
35
  }
36
+ manageCount(t, n) {
37
+ let { kind: r, ...i } = t;
38
+ return e(this.http, "GET", "/manage/{kind}/count", {
39
+ params: { kind: r },
40
+ query: i,
41
+ signal: n
42
+ });
43
+ }
36
44
  create(t) {
37
45
  return this.write(e(this.http, "POST", "/contents", { body: t }));
38
46
  }
@@ -1 +1 @@
1
- {"version":3,"file":"contents.js","names":[],"sources":["../../src/client/contents.ts"],"sourcesContent":["import type {\n CatalogText,\n Content,\n ContentInput,\n ContentPatch,\n Creator,\n InlineImage,\n Segment,\n Version,\n VersionInput,\n} from \"./generated/wire.js\";\nimport type { Http } from \"./http.js\";\nimport { call, type RouteQuery } from \"./route.js\";\n\n/** The URL scheme of an image reference in a content body. */\nexport const IMAGE_SCHEME = \"contentkit:\";\n\n/**\n * The reference a body stores for one of its images, by name\n * (\"contentkit:i-{uuid}\"), where the image's URL would go: reads resolve it to\n * the image's current public file (`Content.images`).\n */\nexport const imageRef = (name: string) => IMAGE_SCHEME + name;\n\n/** Matches every image reference; the name is group 1. */\nexport const imageRefs = () => /contentkit:(i-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/g;\n\n/** Management filters; kind selects /manage/{kind}, otherwise /manage. */\nexport type ManageQuery = RouteQuery<\"GET\", \"/manage\"> & { kind?: string };\n\n/**\n * Content items: a viewer's reads, and the writes of their creators, their\n * owner group's editors and root:content:write holders.\n */\nexport class ContentsClient {\n constructor(private readonly http: Http) {}\n\n /** The item as the caller sees it; text in language, else the site's first, else any. */\n get(id: string, language?: string, signal?: AbortSignal): Promise<Content> {\n return call(this.http, \"GET\", \"/contents/{id}\", { params: { id }, query: { language }, signal });\n }\n version(id: string, vid: string, language?: string, signal?: AbortSignal): Promise<Version> {\n return call(this.http, \"GET\", \"/contents/{id}/versions/{vid}\", { params: { id, vid }, query: { language }, signal });\n }\n /** Items the caller manages, newest first: all for root:content:write holders, else theirs or an owner group's. */\n manage(q: ManageQuery = {}, signal?: AbortSignal): Promise<Content[]> {\n const { kind, ...query } = q;\n return kind\n ? call(this.http, \"GET\", \"/manage/{kind}\", { params: { kind }, query, signal })\n : call(this.http, \"GET\", \"/manage\", { query, signal });\n }\n /** Creates a draft. */\n create(input: ContentInput): Promise<Content> {\n return this.write(call(this.http, \"POST\", \"/contents\", { body: input }));\n }\n update(id: string, patch: ContentPatch): Promise<Content> {\n return this.write(call(this.http, \"PATCH\", \"/contents/{id}\", { params: { id }, body: patch }));\n }\n /** Sets the text in one language; moderation \"held\" when the moderator holds the edit. */\n putText(id: string, language: string, text: CatalogText): Promise<Content> {\n return this.write(call(this.http, \"PUT\", \"/contents/{id}/text/{language}\", { params: { id, language }, body: text }));\n }\n deleteText(id: string, language: string): Promise<Content> {\n return this.write(call(this.http, \"DELETE\", \"/contents/{id}/text/{language}\", { params: { id, language } }));\n }\n setCreators(id: string, creators: Creator[]): Promise<Content> {\n return this.write(call(this.http, \"PUT\", \"/contents/{id}/creators\", { params: { id }, body: { creators } }));\n }\n /** Publishes now, or schedules for at; moderation \"held\" when the moderator holds it. */\n publish(id: string, at?: string): Promise<Content> {\n return this.write(call(this.http, \"POST\", \"/contents/{id}/publish\", { params: { id }, body: { publish_at: at } }));\n }\n unpublish(id: string): Promise<Content> {\n return this.write(call(this.http, \"POST\", \"/contents/{id}/unpublish\", { params: { id } }));\n }\n async remove(id: string): Promise<void> {\n const emit = this.http.captureChanges();\n await call(this.http, \"DELETE\", \"/contents/{id}\", { params: { id } });\n emit({ type: \"content.deleted\", id });\n }\n restore(id: string): Promise<Content> {\n return this.write(call(this.http, \"POST\", \"/contents/{id}/restore\", { params: { id } }));\n }\n /** Selects a version's cover by the name (\"i-{uuid}\") of one of its cover uploads; null returns to its first. */\n setCover(id: string, vid: string, image: string | null): Promise<Version> {\n return this.updateVersion(id, vid, { cover: image ?? \"\" });\n }\n /**\n * A body image uploaded to version vid (its private files), copied into\n * every other version: the reference to store in the body and a URL that shows\n * it to the editor now (null while none exists).\n */\n inlineImage(id: string, vid: string, image: string, signal?: AbortSignal): Promise<InlineImage> {\n return call(this.http, \"POST\", \"/contents/{id}/images\", { params: { id }, body: { image, version: vid }, signal });\n }\n addVersion(id: string, input: VersionInput): Promise<Version> {\n return this.writeVersion(id, call(this.http, \"POST\", \"/contents/{id}/versions\", { params: { id }, body: input }));\n }\n updateVersion(id: string, vid: string, input: VersionInput): Promise<Version> {\n return this.writeVersion(id, call(this.http, \"PATCH\", \"/contents/{id}/versions/{vid}\", { params: { id, vid }, body: input }));\n }\n async removeVersion(id: string, vid: string): Promise<void> {\n await this.writeVersion(id, call(this.http, \"DELETE\", \"/contents/{id}/versions/{vid}\", { params: { id, vid } }));\n }\n /** Orders the versions: ids lists each version that is not deleted, first first. */\n orderVersions(id: string, ids: string[]): Promise<Content> {\n return this.write(call(this.http, \"PUT\", \"/contents/{id}/versions/order\", { params: { id }, body: { ids } }));\n }\n /** Replaces a version's segments (chapters). */\n setSegments(id: string, vid: string, segments: Segment[]): Promise<Version> {\n return this.writeVersion(id, call(this.http, \"PUT\", \"/contents/{id}/versions/{vid}/segments\", { params: { id, vid }, body: { segments } }));\n }\n\n private async write(p: Promise<Content>): Promise<Content> {\n const emit = this.http.captureChanges();\n const content = await p;\n emit({ type: \"content.saved\", content });\n return content;\n }\n\n private async writeVersion<T>(id: string, p: Promise<T>): Promise<T> {\n const emit = this.http.captureChanges();\n const out = await p;\n emit({ type: \"content.changed\", id });\n return out;\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"contents.js","names":[],"sources":["../../src/client/contents.ts"],"sourcesContent":["import type {\n CatalogText,\n Content,\n ContentInput,\n ContentPatch,\n Creator,\n InlineImage,\n ManageCount,\n Segment,\n Version,\n VersionInput,\n} from \"./generated/wire.js\";\nimport type { Http } from \"./http.js\";\nimport { call, type RouteQuery } from \"./route.js\";\n\n/** The URL scheme of an image reference in a content body. */\nexport const IMAGE_SCHEME = \"contentkit:\";\n\n/**\n * The reference a body stores for one of its images, by name\n * (\"contentkit:i-{uuid}\"), where the image's URL would go: reads resolve it to\n * the image's current public file (`Content.images`).\n */\nexport const imageRef = (name: string) => IMAGE_SCHEME + name;\n\n/** Matches every image reference; the name is group 1. */\nexport const imageRefs = () => /contentkit:(i-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/g;\n\n/** Management filters; kind selects /manage/{kind}, otherwise /manage. */\nexport type ManageQuery = RouteQuery<\"GET\", \"/manage\"> & { kind?: string };\n\n/** The /manage/{kind} filters a count takes. */\nexport type ManageCountQuery = RouteQuery<\"GET\", \"/manage/{kind}/count\"> & { kind: string };\n\n/**\n * Content items: a viewer's reads, and the writes of their creators, their\n * owner group's editors and root:content:write holders.\n */\nexport class ContentsClient {\n constructor(private readonly http: Http) {}\n\n /** The item as the caller sees it; text in language, else the site's first, else any. */\n get(id: string, language?: string, signal?: AbortSignal): Promise<Content> {\n return call(this.http, \"GET\", \"/contents/{id}\", { params: { id }, query: { language }, signal });\n }\n version(id: string, vid: string, language?: string, signal?: AbortSignal): Promise<Version> {\n return call(this.http, \"GET\", \"/contents/{id}/versions/{vid}\", { params: { id, vid }, query: { language }, signal });\n }\n /** Items the caller manages, newest first: all for root:content:write holders, else theirs or an owner group's. */\n manage(q: ManageQuery = {}, signal?: AbortSignal): Promise<Content[]> {\n const { kind, ...query } = q;\n return kind\n ? call(this.http, \"GET\", \"/manage/{kind}\", { params: { kind }, query, signal })\n : call(this.http, \"GET\", \"/manage\", { query, signal });\n }\n /** How many items of a kind the caller manages under these filters: exact up to 10,000, then truncated. */\n manageCount(q: ManageCountQuery, signal?: AbortSignal): Promise<ManageCount> {\n const { kind, ...query } = q;\n return call(this.http, \"GET\", \"/manage/{kind}/count\", { params: { kind }, query, signal });\n }\n /** Creates a draft. */\n create(input: ContentInput): Promise<Content> {\n return this.write(call(this.http, \"POST\", \"/contents\", { body: input }));\n }\n update(id: string, patch: ContentPatch): Promise<Content> {\n return this.write(call(this.http, \"PATCH\", \"/contents/{id}\", { params: { id }, body: patch }));\n }\n /** Sets the text in one language; moderation \"held\" when the moderator holds the edit. */\n putText(id: string, language: string, text: CatalogText): Promise<Content> {\n return this.write(call(this.http, \"PUT\", \"/contents/{id}/text/{language}\", { params: { id, language }, body: text }));\n }\n deleteText(id: string, language: string): Promise<Content> {\n return this.write(call(this.http, \"DELETE\", \"/contents/{id}/text/{language}\", { params: { id, language } }));\n }\n setCreators(id: string, creators: Creator[]): Promise<Content> {\n return this.write(call(this.http, \"PUT\", \"/contents/{id}/creators\", { params: { id }, body: { creators } }));\n }\n /** Publishes now, or schedules for at; moderation \"held\" when the moderator holds it. */\n publish(id: string, at?: string): Promise<Content> {\n return this.write(call(this.http, \"POST\", \"/contents/{id}/publish\", { params: { id }, body: { publish_at: at } }));\n }\n unpublish(id: string): Promise<Content> {\n return this.write(call(this.http, \"POST\", \"/contents/{id}/unpublish\", { params: { id } }));\n }\n async remove(id: string): Promise<void> {\n const emit = this.http.captureChanges();\n await call(this.http, \"DELETE\", \"/contents/{id}\", { params: { id } });\n emit({ type: \"content.deleted\", id });\n }\n restore(id: string): Promise<Content> {\n return this.write(call(this.http, \"POST\", \"/contents/{id}/restore\", { params: { id } }));\n }\n /** Selects a version's cover by the name (\"i-{uuid}\") of one of its cover uploads; null returns to its first. */\n setCover(id: string, vid: string, image: string | null): Promise<Version> {\n return this.updateVersion(id, vid, { cover: image ?? \"\" });\n }\n /**\n * A body image uploaded to version vid (its private files), copied into\n * every other version: the reference to store in the body and a URL that shows\n * it to the editor now (null while none exists).\n */\n inlineImage(id: string, vid: string, image: string, signal?: AbortSignal): Promise<InlineImage> {\n return call(this.http, \"POST\", \"/contents/{id}/images\", { params: { id }, body: { image, version: vid }, signal });\n }\n addVersion(id: string, input: VersionInput): Promise<Version> {\n return this.writeVersion(id, call(this.http, \"POST\", \"/contents/{id}/versions\", { params: { id }, body: input }));\n }\n updateVersion(id: string, vid: string, input: VersionInput): Promise<Version> {\n return this.writeVersion(id, call(this.http, \"PATCH\", \"/contents/{id}/versions/{vid}\", { params: { id, vid }, body: input }));\n }\n async removeVersion(id: string, vid: string): Promise<void> {\n await this.writeVersion(id, call(this.http, \"DELETE\", \"/contents/{id}/versions/{vid}\", { params: { id, vid } }));\n }\n /** Orders the versions: ids lists each version that is not deleted, first first. */\n orderVersions(id: string, ids: string[]): Promise<Content> {\n return this.write(call(this.http, \"PUT\", \"/contents/{id}/versions/order\", { params: { id }, body: { ids } }));\n }\n /** Replaces a version's segments (chapters). */\n setSegments(id: string, vid: string, segments: Segment[]): Promise<Version> {\n return this.writeVersion(id, call(this.http, \"PUT\", \"/contents/{id}/versions/{vid}/segments\", { params: { id, vid }, body: { segments } }));\n }\n\n private async write(p: Promise<Content>): Promise<Content> {\n const emit = this.http.captureChanges();\n const content = await p;\n emit({ type: \"content.saved\", content });\n return content;\n }\n\n private async writeVersion<T>(id: string, p: Promise<T>): Promise<T> {\n const emit = this.http.captureChanges();\n const out = await p;\n emit({ type: \"content.changed\", id });\n return out;\n }\n}\n"],"mappings":"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"}
@@ -53,7 +53,11 @@ export interface PageQuery {
53
53
  cursor?: string;
54
54
  limit?: number;
55
55
  }
56
- export type ContentQuery = ContentFilterQuery & ReaderQuery & CardQuery & PageQuery & {
56
+ /** A numbered page from 1 instead of cursor; its summary then comes with every page. */
57
+ export interface NumberedPageQuery {
58
+ page?: number;
59
+ }
60
+ export type ContentQuery = ContentFilterQuery & ReaderQuery & CardQuery & PageQuery & NumberedPageQuery & {
57
61
  /** Selection-wide metadata; requested only on the first page. */
58
62
  summary?: boolean;
59
63
  sort?: ListSort;
@@ -75,7 +79,7 @@ export declare function popularQuery(period?: PopularPeriod | PopularDateRange,
75
79
  export declare function popularBetween(from: string, until: string, filters: PopularQuery & {
76
80
  kind: string;
77
81
  }): ContentQuery;
78
- export type SearchQuery = ContentFilterQuery & ReaderQuery & CardQuery & PageQuery & {
82
+ export type SearchQuery = ContentFilterQuery & ReaderQuery & CardQuery & PageQuery & NumberedPageQuery & {
79
83
  q: string;
80
84
  sort?: "relevance" | ListSort;
81
85
  personalize?: boolean;
@@ -48,7 +48,7 @@ var s = class {
48
48
  });
49
49
  }
50
50
  async search(t, n) {
51
- let r = t.cursor ? null : this.http.captureChanges(), i = await e(this.http, "GET", "/search", {
51
+ let r = t.cursor || (t.page ?? 1) > 1 ? null : this.http.captureChanges(), i = await e(this.http, "GET", "/search", {
52
52
  query: o(t),
53
53
  signal: n
54
54
  });
@@ -1 +1 @@
1
- {"version":3,"file":"discovery.js","names":[],"sources":["../../src/client/discovery.ts"],"sourcesContent":["import type {\n ActivityContexts,\n ActivityContextsInput,\n ActivityTarget,\n ContentFilters,\n ContentFiltersUpdate,\n ContentPage,\n DailyReleases,\n EngagementBatch,\n EngagementSnapshot,\n ExposureInput,\n Exposures,\n RecentSearches,\n ReleaseDates,\n SignalInput,\n Signals,\n Typeahead,\n} from \"./generated/wire.js\";\nimport type { Http } from \"./http.js\";\nimport { call, routeURL } from \"./route.js\";\nimport { browserSearchHistory, type BrowserSearchHistory } from \"./browser-search-history.js\";\n\n/** A popularity period. */\nexport type Period = \"7d\" | \"30d\" | \"90d\" | \"365d\" | \"all\";\n/** A list's order; position orders by the first of `groups` (an ordered group). */\nexport type ListSort = \"newest\" | \"oldest\" | \"title\" | \"popular\" | `popular:${Period}` | \"top_rated\" | `top_rated:${Period}` | \"most_favorited\" | \"position\" | \"random\";\n/** How locked contents list: marked (lock, the default) or left out (hide). */\nexport type AccessMode = \"lock\" | \"hide\";\n\n/** Which contents a list holds. */\nexport interface ContentFilterQuery {\n kind?: string;\n /** Public card hydration under current domain/access/preferences; at most 100. */\n ids?: readonly string[];\n /** Group ids every listed version is in. */\n groups?: readonly string[];\n /** Group ids no listed version is in. */\n exclude_groups?: readonly string[];\n /** Only contents this group owns: a channel's feed. */\n owner_group?: string;\n /** User ids: only contents one of them is credited on. */\n creators?: readonly string[];\n /** public (anyone opens it now) or the level it stays locked at. */\n access?: readonly (\"public\" | \"tier\" | \"group\" | \"ppv\" | \"group_ppv\")[];\n /** Only contents locked behind one of these tiers. */\n tiers?: readonly string[];\n /** Version traits every listed version has. */\n traits?: readonly string[];\n /** \"name:value\" of the kind's filterable attributes. */\n attribute?: readonly string[];\n /** Published on this day (YYYY-MM-DD) or month (YYYY-MM). */\n released_on?: string;\n /** Only contents published within this age window; independent of the popularity score window. */\n published_within?: Period;\n /** IANA time zone of released_on and release dates; UTC by default. */\n tz?: string;\n /** The signed-in reader's unseen, in-progress or completed contents. */\n view_status?: \"unseen\" | \"in_progress\" | \"completed\";\n}\n\n/** How a list reads to its reader. */\nexport interface ReaderQuery {\n /** Only content with a live version in this language. */\n language?: string;\n /** Titles, names and paths in this language, and each card's preferred version's; language by default. */\n display_language?: string;\n mode?: AccessMode;\n}\n\n/** The group kinds cards name; every kind with a page by default. */\nexport interface CardQuery {\n card_entities?: readonly string[];\n}\n\nexport interface PageQuery {\n cursor?: string;\n limit?: number;\n}\n\nexport type ContentQuery = ContentFilterQuery & ReaderQuery & CardQuery & PageQuery & {\n /** Selection-wide metadata; requested only on the first page. */\n summary?: boolean;\n sort?: ListSort;\n /** Inclusive UTC date (YYYY-MM-DD); requires activity_until, kind and sort=popular. */\n activity_from?: string;\n /** Exclusive UTC date (YYYY-MM-DD); requires activity_from, kind and sort=popular. */\n activity_until?: string;\n};\nexport type PopularPeriod = \"7d\" | \"90d\" | \"365d\" | \"all\";\n/** Whole UTC dates: from inclusive, until exclusive. */\nexport interface PopularDateRange {\n from: string;\n until: string;\n}\nexport type PopularQuery = Omit<ContentQuery, \"sort\" | \"activity_from\" | \"activity_until\">;\n\n/** Rank activity in the selected window; publication age is an optional separate filter. */\nexport function popularQuery(period: PopularPeriod | PopularDateRange = \"7d\", filters: PopularQuery = {}): ContentQuery {\n return typeof period === \"string\"\n ? { ...filters, sort: `popular:${period}` }\n : { ...filters, sort: \"popular\", activity_from: period.from, activity_until: period.until };\n}\n\n/** Rank activity between two UTC dates: from inclusive, until exclusive. */\nexport function popularBetween(from: string, until: string, filters: PopularQuery & { kind: string }): ContentQuery {\n return popularQuery({ from, until }, filters);\n}\n\nexport type SearchQuery = ContentFilterQuery &\n ReaderQuery &\n CardQuery &\n PageQuery & {\n q: string;\n sort?: \"relevance\" | ListSort;\n personalize?: boolean;\n summary?: boolean;\n /** Explicit legacy server-side anonymous history. Omit for public search without server history. */\n session?: string;\n };\nexport interface RecentSearchesQuery {\n /** Explicit legacy server-side anonymous history; no anonymous session is created by default. */\n session?: string;\n /** 10 by default, at most 100. */\n limit?: number;\n}\nexport type RailQuery = ContentFilterQuery & ReaderQuery & CardQuery & { limit?: number; summary?: boolean };\nexport type ReleaseDatesQuery = ContentFilterQuery & ReaderQuery & PageQuery & { by?: \"month\" | \"day\" };\n/** Complete daily metadata for one calendar window, without pagination. */\nexport type DailyReleasesQuery = Omit<ContentFilterQuery, \"released_on\" | \"tz\"> & ReaderQuery & CardQuery & {\n days?: number;\n /** Inclusive YYYY-MM-DD in time_zone; today by default. Future dates are rejected. */\n end_date?: string;\n time_zone?: string;\n};\nexport type HistoryQuery = ReaderQuery & CardQuery & PageQuery & { kind?: string; status?: \"seen\" | \"in_progress\" | \"completed\"; summary?: boolean };\nexport interface TypeaheadQuery extends ReaderQuery {\n q: string;\n /** Only contents of this kind. */\n kind?: string;\n /** The group kinds suggested; every kind with a page by default. */\n entity_kinds?: readonly string[];\n /** Per section: 8 by default, at most 20. */\n limit?: number;\n}\n\n/** Private editor navigation. Each requested section must be chosen explicitly. */\nexport interface AdminSuggestionsQuery {\n q: string;\n display_language?: string;\n content_kinds?: readonly string[];\n entity_kinds?: readonly string[];\n include_contents: boolean;\n include_entities: boolean;\n /** Per section: 8 by default, at most 20. */\n limit?: number;\n}\n\n// Comma-joined list parameters; `attribute` repeats instead.\nconst LISTS = new Set([\"ids\", \"groups\", \"exclude_groups\", \"creators\", \"access\", \"tiers\", \"traits\", \"card_entities\", \"entity_kinds\", \"content_kinds\"]);\n\n/** A query with its list parameters comma-joined, as discovery routes read them. */\nexport function query(q: object): Record<string, string | number | boolean | readonly string[] | undefined> {\n const out: Record<string, string | number | boolean | readonly string[] | undefined> = {};\n for (const [k, v] of Object.entries(q)) out[k] = LISTS.has(k) && Array.isArray(v) ? (v.length ? v.join(\",\") : undefined) : v;\n return out;\n}\n\n/**\n * Discovery: lists, search, typeahead, similar and recommended contents,\n * release dates, the reader's content filters and history, signals and exposures.\n */\nexport class DiscoveryClient {\n /** Local anonymous history, isolated from authenticated server history. */\n readonly browserSearches: BrowserSearchHistory;\n\n constructor(private readonly http: Http) {\n this.browserSearches = browserSearchHistory(http.mount(\"discovery\"));\n }\n\n /** Contents as cards, one per content item with the version that fits the reader. */\n list(q: ContentQuery = {}, signal?: AbortSignal): Promise<ContentPage> {\n return call(this.http, \"GET\", \"/contents\", { query: query(q) as never, signal });\n }\n /** A first page is kept among the reader's recent searches. */\n async search(q: SearchQuery, signal?: AbortSignal): Promise<ContentPage> {\n const emit = q.cursor ? null : this.http.captureChanges();\n const out: ContentPage = await call(this.http, \"GET\", \"/search\", { query: query(q) as never, signal });\n emit?.({ type: \"searches.changed\" });\n return out;\n }\n /** The reader's recent searches, newest first, since they last cleared them. */\n recentSearches(q: RecentSearchesQuery = {}, signal?: AbortSignal): Promise<RecentSearches> {\n return call(this.http, \"GET\", \"/me/searches\", { query: q, signal });\n }\n /** Clears one recent search, or all of them. */\n async clearSearches(o: { query?: string; session?: string } = {}): Promise<void> {\n const emit = this.http.captureChanges();\n await call(this.http, \"DELETE\", \"/me/searches\", { query: o });\n emit({ type: \"searches.changed\" });\n }\n /** Contents and groups while the reader types. */\n typeahead(q: TypeaheadQuery, signal?: AbortSignal): Promise<Typeahead> {\n return call(this.http, \"GET\", \"/typeahead\", { query: query(q) as never, signal });\n }\n /** Current names of authorized draft/hidden content and entities; no public search fallback. */\n adminSuggestions(q: AdminSuggestionsQuery, signal?: AbortSignal): Promise<Typeahead> {\n return call(this.http, \"GET\", \"/manage/search-suggestions\", { query: query(q) as never, signal });\n }\n /** Contents like one, of its kind unless q.kind names another. */\n similar(id: string, q: RailQuery = {}, signal?: AbortSignal): Promise<ContentPage> {\n return call(this.http, \"GET\", \"/contents/{id}/similar\", { params: { id }, query: query(q) as never, signal });\n }\n /** For the signed-in reader. */\n recommendations(q: RailQuery = {}, signal?: AbortSignal): Promise<ContentPage> {\n return call(this.http, \"GET\", \"/recommendations\", { query: query(q) as never, signal });\n }\n /** Per month or day, newest first: how many contents the reader's listing shows released then, and how many are free. */\n releaseDates(q: ReleaseDatesQuery = {}, signal?: AbortSignal): Promise<ReleaseDates> {\n return call(this.http, \"GET\", \"/release-dates\", { query: query(q) as never, signal });\n }\n /** Every visible card in each day; opening or expanding a day needs no further request. */\n releases(q: DailyReleasesQuery = {}, signal?: AbortSignal): Promise<DailyReleases> {\n return call(this.http, \"GET\", \"/releases\", { query: query(q) as never, signal });\n }\n /** Only the current domain's reader-toggleable entities, named in the requested language. */\n async contentFilters(language?: string, signal?: AbortSignal): Promise<ContentFilters> {\n const result = await this.http.request<ContentFilters>(routeURL(this.http, \"GET\", \"/content-filters\", { query: { language } }), { signal, guestPreferences: false });\n this.http.acceptGuestFilters(result.filters);\n return result;\n }\n /** Atomically changes submitted shared choices; omitted settings remain intact. */\n async setContentFilters(update: ContentFiltersUpdate, language?: string): Promise<ContentFilters> {\n const emit = this.http.captureChanges();\n const out: ContentFilters = await call(this.http, \"PATCH\", \"/me/content-filters\", { query: { language }, body: update });\n emit({ type: \"contentFilters.changed\" });\n return out;\n }\n /** What the reader engaged with, most recent first. */\n history(q: HistoryQuery = {}, signal?: AbortSignal): Promise<ContentPage> {\n return call(this.http, \"GET\", \"/me/history\", { query: query(q) as never, signal });\n }\n /** Erases the reader's history: of one content item, one kind or everything. */\n async forgetHistory(o: { kind?: string; content_id?: string } = {}): Promise<void> {\n const emit = this.http.captureChanges();\n await call(this.http, \"DELETE\", \"/me/history\", { query: o });\n emit({ type: \"history.changed\" });\n }\n /** Capture server-owned access and eligibility at an observation's start. */\n activityContexts(targets: readonly ActivityTarget[], session?: string, language = this.http.language()): Promise<ActivityContexts> {\n const body: ActivityContextsInput = { targets: [...targets], session, language };\n return call(this.http, \"POST\", \"/activity-contexts\", { body });\n }\n /** Cumulative measured attention. Successful checkpoints refresh history/progress caches. */\n async engagement(snapshots: EngagementSnapshot[], o: { session?: string; keepalive?: boolean } = {}): Promise<void> {\n const emit = this.http.captureChanges();\n const body: EngagementBatch = { session: o.session, snapshots };\n await call(this.http, \"POST\", \"/engagement\", { body, keepalive: o.keepalive });\n emit({ type: \"engagement.changed\", ids: [...new Set(snapshots.map(s => s.content_id))] });\n }\n /** Clicks and explicit feedback; signed out, of the visitor named. */\n signals(signals: SignalInput[], session?: string, o: { keepalive?: boolean } = {}): Promise<void> {\n const body: Signals = { session, signals };\n return call(this.http, \"POST\", \"/signals\", { body, keepalive: o.keepalive });\n }\n /** Result lists served, rendered or shown, for click attribution. */\n exposures(exposures: ExposureInput[], session?: string, o: { keepalive?: boolean } = {}): Promise<void> {\n const body: Exposures = { session, exposures };\n return call(this.http, \"POST\", \"/exposures\", { body, keepalive: o.keepalive });\n }\n}\n"],"mappings":";;;AAiGA,SAAgB,EAAa,IAA2C,MAAM,IAAwB,CAAC,GAAiB;CACtH,OAAO,OAAO,KAAW,WACrB;EAAE,GAAG;EAAS,MAAM,WAAW;CAAS,IACxC;EAAE,GAAG;EAAS,MAAM;EAAW,eAAe,EAAO;EAAM,gBAAgB,EAAO;CAAM;AAC9F;AAGA,SAAgB,EAAe,GAAc,GAAe,GAAwD;CAClH,OAAO,EAAa;EAAE;EAAM;CAAM,GAAG,CAAO;AAC9C;AAoDA,IAAM,oBAAQ,IAAI,IAAI;CAAC;CAAO;CAAU;CAAkB;CAAY;CAAU;CAAS;CAAU;CAAiB;CAAgB;AAAe,CAAC;AAGpJ,SAAgB,EAAM,GAAsF;CAC1G,IAAM,IAAiF,CAAC;CACxF,KAAK,IAAM,CAAC,GAAG,MAAM,OAAO,QAAQ,CAAC,GAAG,EAAI,KAAK,EAAM,IAAI,CAAC,KAAK,MAAM,QAAQ,CAAC,IAAK,EAAE,SAAS,EAAE,KAAK,GAAG,IAAI,KAAA,IAAa;CAC3H,OAAO;AACT;AAMA,IAAa,IAAb,MAA6B;CAIE;CAF7B;CAEA,YAAY,GAA6B;EACvC,AAD2B,KAAA,OAAA,GAC3B,KAAK,kBAAkB,EAAqB,EAAK,MAAM,WAAW,CAAC;CACrE;CAGA,KAAK,IAAkB,CAAC,GAAG,GAA4C;EACrE,OAAO,EAAK,KAAK,MAAM,OAAO,aAAa;GAAE,OAAO,EAAM,CAAC;GAAY;EAAO,CAAC;CACjF;CAEA,MAAM,OAAO,GAAgB,GAA4C;EACvE,IAAM,IAAO,EAAE,SAAS,OAAO,KAAK,KAAK,eAAe,GAClD,IAAmB,MAAM,EAAK,KAAK,MAAM,OAAO,WAAW;GAAE,OAAO,EAAM,CAAC;GAAY;EAAO,CAAC;EAErG,OADA,IAAO,EAAE,MAAM,mBAAmB,CAAC,GAC5B;CACT;CAEA,eAAe,IAAyB,CAAC,GAAG,GAA+C;EACzF,OAAO,EAAK,KAAK,MAAM,OAAO,gBAAgB;GAAE,OAAO;GAAG;EAAO,CAAC;CACpE;CAEA,MAAM,cAAc,IAA0C,CAAC,GAAkB;EAC/E,IAAM,IAAO,KAAK,KAAK,eAAe;EAEtC,AADA,MAAM,EAAK,KAAK,MAAM,UAAU,gBAAgB,EAAE,OAAO,EAAE,CAAC,GAC5D,EAAK,EAAE,MAAM,mBAAmB,CAAC;CACnC;CAEA,UAAU,GAAmB,GAA0C;EACrE,OAAO,EAAK,KAAK,MAAM,OAAO,cAAc;GAAE,OAAO,EAAM,CAAC;GAAY;EAAO,CAAC;CAClF;CAEA,iBAAiB,GAA0B,GAA0C;EACnF,OAAO,EAAK,KAAK,MAAM,OAAO,8BAA8B;GAAE,OAAO,EAAM,CAAC;GAAY;EAAO,CAAC;CAClG;CAEA,QAAQ,GAAY,IAAe,CAAC,GAAG,GAA4C;EACjF,OAAO,EAAK,KAAK,MAAM,OAAO,0BAA0B;GAAE,QAAQ,EAAE,MAAG;GAAG,OAAO,EAAM,CAAC;GAAY;EAAO,CAAC;CAC9G;CAEA,gBAAgB,IAAe,CAAC,GAAG,GAA4C;EAC7E,OAAO,EAAK,KAAK,MAAM,OAAO,oBAAoB;GAAE,OAAO,EAAM,CAAC;GAAY;EAAO,CAAC;CACxF;CAEA,aAAa,IAAuB,CAAC,GAAG,GAA6C;EACnF,OAAO,EAAK,KAAK,MAAM,OAAO,kBAAkB;GAAE,OAAO,EAAM,CAAC;GAAY;EAAO,CAAC;CACtF;CAEA,SAAS,IAAwB,CAAC,GAAG,GAA8C;EACjF,OAAO,EAAK,KAAK,MAAM,OAAO,aAAa;GAAE,OAAO,EAAM,CAAC;GAAY;EAAO,CAAC;CACjF;CAEA,MAAM,eAAe,GAAmB,GAA+C;EACrF,IAAM,IAAS,MAAM,KAAK,KAAK,QAAwB,EAAS,KAAK,MAAM,OAAO,oBAAoB,EAAE,OAAO,EAAE,YAAS,EAAE,CAAC,GAAG;GAAE;GAAQ,kBAAkB;EAAM,CAAC;EAEnK,OADA,KAAK,KAAK,mBAAmB,EAAO,OAAO,GACpC;CACT;CAEA,MAAM,kBAAkB,GAA8B,GAA4C;EAChG,IAAM,IAAO,KAAK,KAAK,eAAe,GAChC,IAAsB,MAAM,EAAK,KAAK,MAAM,SAAS,uBAAuB;GAAE,OAAO,EAAE,YAAS;GAAG,MAAM;EAAO,CAAC;EAEvH,OADA,EAAK,EAAE,MAAM,yBAAyB,CAAC,GAChC;CACT;CAEA,QAAQ,IAAkB,CAAC,GAAG,GAA4C;EACxE,OAAO,EAAK,KAAK,MAAM,OAAO,eAAe;GAAE,OAAO,EAAM,CAAC;GAAY;EAAO,CAAC;CACnF;CAEA,MAAM,cAAc,IAA4C,CAAC,GAAkB;EACjF,IAAM,IAAO,KAAK,KAAK,eAAe;EAEtC,AADA,MAAM,EAAK,KAAK,MAAM,UAAU,eAAe,EAAE,OAAO,EAAE,CAAC,GAC3D,EAAK,EAAE,MAAM,kBAAkB,CAAC;CAClC;CAEA,iBAAiB,GAAoC,GAAkB,IAAW,KAAK,KAAK,SAAS,GAA8B;EACjI,IAAM,IAA8B;GAAE,SAAS,CAAC,GAAG,CAAO;GAAG;GAAS;EAAS;EAC/E,OAAO,EAAK,KAAK,MAAM,QAAQ,sBAAsB,EAAE,QAAK,CAAC;CAC/D;CAEA,MAAM,WAAW,GAAiC,IAA+C,CAAC,GAAkB;EAClH,IAAM,IAAO,KAAK,KAAK,eAAe,GAChC,IAAwB;GAAE,SAAS,EAAE;GAAS;EAAU;EAE9D,AADA,MAAM,EAAK,KAAK,MAAM,QAAQ,eAAe;GAAE;GAAM,WAAW,EAAE;EAAU,CAAC,GAC7E,EAAK;GAAE,MAAM;GAAsB,KAAK,CAAC,GAAG,IAAI,IAAI,EAAU,KAAI,MAAK,EAAE,UAAU,CAAC,CAAC;EAAE,CAAC;CAC1F;CAEA,QAAQ,GAAwB,GAAkB,IAA6B,CAAC,GAAkB;EAChG,IAAM,IAAgB;GAAE;GAAS;EAAQ;EACzC,OAAO,EAAK,KAAK,MAAM,QAAQ,YAAY;GAAE;GAAM,WAAW,EAAE;EAAU,CAAC;CAC7E;CAEA,UAAU,GAA4B,GAAkB,IAA6B,CAAC,GAAkB;EACtG,IAAM,IAAkB;GAAE;GAAS;EAAU;EAC7C,OAAO,EAAK,KAAK,MAAM,QAAQ,cAAc;GAAE;GAAM,WAAW,EAAE;EAAU,CAAC;CAC/E;AACF"}
1
+ {"version":3,"file":"discovery.js","names":[],"sources":["../../src/client/discovery.ts"],"sourcesContent":["import type {\n ActivityContexts,\n ActivityContextsInput,\n ActivityTarget,\n ContentFilters,\n ContentFiltersUpdate,\n ContentPage,\n DailyReleases,\n EngagementBatch,\n EngagementSnapshot,\n ExposureInput,\n Exposures,\n RecentSearches,\n ReleaseDates,\n SignalInput,\n Signals,\n Typeahead,\n} from \"./generated/wire.js\";\nimport type { Http } from \"./http.js\";\nimport { call, routeURL } from \"./route.js\";\nimport { browserSearchHistory, type BrowserSearchHistory } from \"./browser-search-history.js\";\n\n/** A popularity period. */\nexport type Period = \"7d\" | \"30d\" | \"90d\" | \"365d\" | \"all\";\n/** A list's order; position orders by the first of `groups` (an ordered group). */\nexport type ListSort = \"newest\" | \"oldest\" | \"title\" | \"popular\" | `popular:${Period}` | \"top_rated\" | `top_rated:${Period}` | \"most_favorited\" | \"position\" | \"random\";\n/** How locked contents list: marked (lock, the default) or left out (hide). */\nexport type AccessMode = \"lock\" | \"hide\";\n\n/** Which contents a list holds. */\nexport interface ContentFilterQuery {\n kind?: string;\n /** Public card hydration under current domain/access/preferences; at most 100. */\n ids?: readonly string[];\n /** Group ids every listed version is in. */\n groups?: readonly string[];\n /** Group ids no listed version is in. */\n exclude_groups?: readonly string[];\n /** Only contents this group owns: a channel's feed. */\n owner_group?: string;\n /** User ids: only contents one of them is credited on. */\n creators?: readonly string[];\n /** public (anyone opens it now) or the level it stays locked at. */\n access?: readonly (\"public\" | \"tier\" | \"group\" | \"ppv\" | \"group_ppv\")[];\n /** Only contents locked behind one of these tiers. */\n tiers?: readonly string[];\n /** Version traits every listed version has. */\n traits?: readonly string[];\n /** \"name:value\" of the kind's filterable attributes. */\n attribute?: readonly string[];\n /** Published on this day (YYYY-MM-DD) or month (YYYY-MM). */\n released_on?: string;\n /** Only contents published within this age window; independent of the popularity score window. */\n published_within?: Period;\n /** IANA time zone of released_on and release dates; UTC by default. */\n tz?: string;\n /** The signed-in reader's unseen, in-progress or completed contents. */\n view_status?: \"unseen\" | \"in_progress\" | \"completed\";\n}\n\n/** How a list reads to its reader. */\nexport interface ReaderQuery {\n /** Only content with a live version in this language. */\n language?: string;\n /** Titles, names and paths in this language, and each card's preferred version's; language by default. */\n display_language?: string;\n mode?: AccessMode;\n}\n\n/** The group kinds cards name; every kind with a page by default. */\nexport interface CardQuery {\n card_entities?: readonly string[];\n}\n\nexport interface PageQuery {\n cursor?: string;\n limit?: number;\n}\n\n/** A numbered page from 1 instead of cursor; its summary then comes with every page. */\nexport interface NumberedPageQuery {\n page?: number;\n}\n\nexport type ContentQuery = ContentFilterQuery & ReaderQuery & CardQuery & PageQuery & NumberedPageQuery & {\n /** Selection-wide metadata; requested only on the first page. */\n summary?: boolean;\n sort?: ListSort;\n /** Inclusive UTC date (YYYY-MM-DD); requires activity_until, kind and sort=popular. */\n activity_from?: string;\n /** Exclusive UTC date (YYYY-MM-DD); requires activity_from, kind and sort=popular. */\n activity_until?: string;\n};\nexport type PopularPeriod = \"7d\" | \"90d\" | \"365d\" | \"all\";\n/** Whole UTC dates: from inclusive, until exclusive. */\nexport interface PopularDateRange {\n from: string;\n until: string;\n}\nexport type PopularQuery = Omit<ContentQuery, \"sort\" | \"activity_from\" | \"activity_until\">;\n\n/** Rank activity in the selected window; publication age is an optional separate filter. */\nexport function popularQuery(period: PopularPeriod | PopularDateRange = \"7d\", filters: PopularQuery = {}): ContentQuery {\n return typeof period === \"string\"\n ? { ...filters, sort: `popular:${period}` }\n : { ...filters, sort: \"popular\", activity_from: period.from, activity_until: period.until };\n}\n\n/** Rank activity between two UTC dates: from inclusive, until exclusive. */\nexport function popularBetween(from: string, until: string, filters: PopularQuery & { kind: string }): ContentQuery {\n return popularQuery({ from, until }, filters);\n}\n\nexport type SearchQuery = ContentFilterQuery &\n ReaderQuery &\n CardQuery &\n PageQuery &\n NumberedPageQuery & {\n q: string;\n sort?: \"relevance\" | ListSort;\n personalize?: boolean;\n summary?: boolean;\n /** Explicit legacy server-side anonymous history. Omit for public search without server history. */\n session?: string;\n };\nexport interface RecentSearchesQuery {\n /** Explicit legacy server-side anonymous history; no anonymous session is created by default. */\n session?: string;\n /** 10 by default, at most 100. */\n limit?: number;\n}\nexport type RailQuery = ContentFilterQuery & ReaderQuery & CardQuery & { limit?: number; summary?: boolean };\nexport type ReleaseDatesQuery = ContentFilterQuery & ReaderQuery & PageQuery & { by?: \"month\" | \"day\" };\n/** Complete daily metadata for one calendar window, without pagination. */\nexport type DailyReleasesQuery = Omit<ContentFilterQuery, \"released_on\" | \"tz\"> & ReaderQuery & CardQuery & {\n days?: number;\n /** Inclusive YYYY-MM-DD in time_zone; today by default. Future dates are rejected. */\n end_date?: string;\n time_zone?: string;\n};\nexport type HistoryQuery = ReaderQuery & CardQuery & PageQuery & { kind?: string; status?: \"seen\" | \"in_progress\" | \"completed\"; summary?: boolean };\nexport interface TypeaheadQuery extends ReaderQuery {\n q: string;\n /** Only contents of this kind. */\n kind?: string;\n /** The group kinds suggested; every kind with a page by default. */\n entity_kinds?: readonly string[];\n /** Per section: 8 by default, at most 20. */\n limit?: number;\n}\n\n/** Private editor navigation. Each requested section must be chosen explicitly. */\nexport interface AdminSuggestionsQuery {\n q: string;\n display_language?: string;\n content_kinds?: readonly string[];\n entity_kinds?: readonly string[];\n include_contents: boolean;\n include_entities: boolean;\n /** Per section: 8 by default, at most 20. */\n limit?: number;\n}\n\n// Comma-joined list parameters; `attribute` repeats instead.\nconst LISTS = new Set([\"ids\", \"groups\", \"exclude_groups\", \"creators\", \"access\", \"tiers\", \"traits\", \"card_entities\", \"entity_kinds\", \"content_kinds\"]);\n\n/** A query with its list parameters comma-joined, as discovery routes read them. */\nexport function query(q: object): Record<string, string | number | boolean | readonly string[] | undefined> {\n const out: Record<string, string | number | boolean | readonly string[] | undefined> = {};\n for (const [k, v] of Object.entries(q)) out[k] = LISTS.has(k) && Array.isArray(v) ? (v.length ? v.join(\",\") : undefined) : v;\n return out;\n}\n\n/**\n * Discovery: lists, search, typeahead, similar and recommended contents,\n * release dates, the reader's content filters and history, signals and exposures.\n */\nexport class DiscoveryClient {\n /** Local anonymous history, isolated from authenticated server history. */\n readonly browserSearches: BrowserSearchHistory;\n\n constructor(private readonly http: Http) {\n this.browserSearches = browserSearchHistory(http.mount(\"discovery\"));\n }\n\n /** Contents as cards, one per content item with the version that fits the reader. */\n list(q: ContentQuery = {}, signal?: AbortSignal): Promise<ContentPage> {\n return call(this.http, \"GET\", \"/contents\", { query: query(q) as never, signal });\n }\n /** A first page is kept among the reader's recent searches. */\n async search(q: SearchQuery, signal?: AbortSignal): Promise<ContentPage> {\n const emit = q.cursor || (q.page ?? 1) > 1 ? null : this.http.captureChanges();\n const out: ContentPage = await call(this.http, \"GET\", \"/search\", { query: query(q) as never, signal });\n emit?.({ type: \"searches.changed\" });\n return out;\n }\n /** The reader's recent searches, newest first, since they last cleared them. */\n recentSearches(q: RecentSearchesQuery = {}, signal?: AbortSignal): Promise<RecentSearches> {\n return call(this.http, \"GET\", \"/me/searches\", { query: q, signal });\n }\n /** Clears one recent search, or all of them. */\n async clearSearches(o: { query?: string; session?: string } = {}): Promise<void> {\n const emit = this.http.captureChanges();\n await call(this.http, \"DELETE\", \"/me/searches\", { query: o });\n emit({ type: \"searches.changed\" });\n }\n /** Contents and groups while the reader types. */\n typeahead(q: TypeaheadQuery, signal?: AbortSignal): Promise<Typeahead> {\n return call(this.http, \"GET\", \"/typeahead\", { query: query(q) as never, signal });\n }\n /** Current names of authorized draft/hidden content and entities; no public search fallback. */\n adminSuggestions(q: AdminSuggestionsQuery, signal?: AbortSignal): Promise<Typeahead> {\n return call(this.http, \"GET\", \"/manage/search-suggestions\", { query: query(q) as never, signal });\n }\n /** Contents like one, of its kind unless q.kind names another. */\n similar(id: string, q: RailQuery = {}, signal?: AbortSignal): Promise<ContentPage> {\n return call(this.http, \"GET\", \"/contents/{id}/similar\", { params: { id }, query: query(q) as never, signal });\n }\n /** For the signed-in reader. */\n recommendations(q: RailQuery = {}, signal?: AbortSignal): Promise<ContentPage> {\n return call(this.http, \"GET\", \"/recommendations\", { query: query(q) as never, signal });\n }\n /** Per month or day, newest first: how many contents the reader's listing shows released then, and how many are free. */\n releaseDates(q: ReleaseDatesQuery = {}, signal?: AbortSignal): Promise<ReleaseDates> {\n return call(this.http, \"GET\", \"/release-dates\", { query: query(q) as never, signal });\n }\n /** Every visible card in each day; opening or expanding a day needs no further request. */\n releases(q: DailyReleasesQuery = {}, signal?: AbortSignal): Promise<DailyReleases> {\n return call(this.http, \"GET\", \"/releases\", { query: query(q) as never, signal });\n }\n /** Only the current domain's reader-toggleable entities, named in the requested language. */\n async contentFilters(language?: string, signal?: AbortSignal): Promise<ContentFilters> {\n const result = await this.http.request<ContentFilters>(routeURL(this.http, \"GET\", \"/content-filters\", { query: { language } }), { signal, guestPreferences: false });\n this.http.acceptGuestFilters(result.filters);\n return result;\n }\n /** Atomically changes submitted shared choices; omitted settings remain intact. */\n async setContentFilters(update: ContentFiltersUpdate, language?: string): Promise<ContentFilters> {\n const emit = this.http.captureChanges();\n const out: ContentFilters = await call(this.http, \"PATCH\", \"/me/content-filters\", { query: { language }, body: update });\n emit({ type: \"contentFilters.changed\" });\n return out;\n }\n /** What the reader engaged with, most recent first. */\n history(q: HistoryQuery = {}, signal?: AbortSignal): Promise<ContentPage> {\n return call(this.http, \"GET\", \"/me/history\", { query: query(q) as never, signal });\n }\n /** Erases the reader's history: of one content item, one kind or everything. */\n async forgetHistory(o: { kind?: string; content_id?: string } = {}): Promise<void> {\n const emit = this.http.captureChanges();\n await call(this.http, \"DELETE\", \"/me/history\", { query: o });\n emit({ type: \"history.changed\" });\n }\n /** Capture server-owned access and eligibility at an observation's start. */\n activityContexts(targets: readonly ActivityTarget[], session?: string, language = this.http.language()): Promise<ActivityContexts> {\n const body: ActivityContextsInput = { targets: [...targets], session, language };\n return call(this.http, \"POST\", \"/activity-contexts\", { body });\n }\n /** Cumulative measured attention. Successful checkpoints refresh history/progress caches. */\n async engagement(snapshots: EngagementSnapshot[], o: { session?: string; keepalive?: boolean } = {}): Promise<void> {\n const emit = this.http.captureChanges();\n const body: EngagementBatch = { session: o.session, snapshots };\n await call(this.http, \"POST\", \"/engagement\", { body, keepalive: o.keepalive });\n emit({ type: \"engagement.changed\", ids: [...new Set(snapshots.map(s => s.content_id))] });\n }\n /** Clicks and explicit feedback; signed out, of the visitor named. */\n signals(signals: SignalInput[], session?: string, o: { keepalive?: boolean } = {}): Promise<void> {\n const body: Signals = { session, signals };\n return call(this.http, \"POST\", \"/signals\", { body, keepalive: o.keepalive });\n }\n /** Result lists served, rendered or shown, for click attribution. */\n exposures(exposures: ExposureInput[], session?: string, o: { keepalive?: boolean } = {}): Promise<void> {\n const body: Exposures = { session, exposures };\n return call(this.http, \"POST\", \"/exposures\", { body, keepalive: o.keepalive });\n }\n}\n"],"mappings":"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"}
@@ -905,7 +905,7 @@ export declare const CONTENTKIT_ROUTES: readonly [{
905
905
  readonly path: "/manage";
906
906
  readonly auth: "public";
907
907
  readonly permission: null;
908
- readonly query: readonly ["state", "owner_group", "q", "language", "limit", "offset"];
908
+ readonly query: readonly ["state", "owner_group", "access", "tier", "q", "code", "ids", "language", "limit", "offset"];
909
909
  readonly request: null;
910
910
  readonly responses: {
911
911
  readonly 200: "Content[]";
@@ -918,13 +918,26 @@ export declare const CONTENTKIT_ROUTES: readonly [{
918
918
  readonly path: "/manage/{kind}";
919
919
  readonly auth: "public";
920
920
  readonly permission: null;
921
- readonly query: readonly ["state", "owner_group", "q", "language", "limit", "offset"];
921
+ readonly query: readonly ["state", "owner_group", "access", "tier", "q", "code", "ids", "language", "limit", "offset"];
922
922
  readonly request: null;
923
923
  readonly responses: {
924
924
  readonly 200: "Content[]";
925
925
  };
926
926
  readonly errors: readonly ["not_found"];
927
927
  readonly errorSets: readonly ["module:contents", "auth", "request"];
928
+ }, {
929
+ readonly method: "GET";
930
+ readonly module: "contents";
931
+ readonly path: "/manage/{kind}/count";
932
+ readonly auth: "public";
933
+ readonly permission: null;
934
+ readonly query: readonly ["state", "owner_group", "access", "tier", "q"];
935
+ readonly request: null;
936
+ readonly responses: {
937
+ readonly 200: "ManageCount";
938
+ };
939
+ readonly errors: readonly ["not_found"];
940
+ readonly errorSets: readonly ["module:contents", "auth", "request"];
928
941
  }, {
929
942
  readonly method: "POST";
930
943
  readonly module: "contents";
@@ -1295,7 +1308,7 @@ export declare const CONTENTKIT_ROUTES: readonly [{
1295
1308
  readonly method: "GET";
1296
1309
  readonly module: "codes";
1297
1310
  readonly path: "/{code}";
1298
- readonly auth: "open";
1311
+ readonly auth: "public";
1299
1312
  readonly permission: null;
1300
1313
  readonly query: readonly ["lang", "v"];
1301
1314
  readonly request: null;
@@ -1303,7 +1316,7 @@ export declare const CONTENTKIT_ROUTES: readonly [{
1303
1316
  readonly 200: "Resolved";
1304
1317
  };
1305
1318
  readonly errors: readonly ["gone", "not_found"];
1306
- readonly errorSets: readonly ["module:codes", "request"];
1319
+ readonly errorSets: readonly ["module:codes", "auth", "request"];
1307
1320
  }, {
1308
1321
  readonly method: "POST";
1309
1322
  readonly module: "codes";
@@ -1323,7 +1336,7 @@ export declare const CONTENTKIT_ROUTES: readonly [{
1323
1336
  readonly path: "/entities";
1324
1337
  readonly auth: "public";
1325
1338
  readonly permission: null;
1326
- readonly query: readonly ["kind", "parent", "scope", "slug", "letter", "language", "content_kind", "sort", "cursor", "limit"];
1339
+ readonly query: readonly ["kind", "parent", "scope", "slug", "letter", "language", "content_kind", "sort", "cursor", "page", "limit"];
1327
1340
  readonly request: null;
1328
1341
  readonly responses: {
1329
1342
  readonly 200: "GroupPage";
@@ -1349,7 +1362,7 @@ export declare const CONTENTKIT_ROUTES: readonly [{
1349
1362
  readonly path: "/manage/entities";
1350
1363
  readonly auth: "staff";
1351
1364
  readonly permission: "root:entities:write";
1352
- readonly query: readonly ["kind", "parent", "scope", "slug", "q", "letter", "language", "content_kind", "sort", "cursor", "limit"];
1365
+ readonly query: readonly ["state", "kind", "parent", "scope", "slug", "q", "letter", "language", "content_kind", "sort", "cursor", "limit"];
1353
1366
  readonly request: null;
1354
1367
  readonly responses: {
1355
1368
  readonly 200: "GroupPage";
@@ -1658,28 +1671,41 @@ export declare const CONTENTKIT_ROUTES: readonly [{
1658
1671
  }, {
1659
1672
  readonly method: "GET";
1660
1673
  readonly module: "versions";
1661
- readonly path: "/contents/{id}/versions/{version}/pages";
1674
+ readonly path: "/contents/{id}/versions/{version}/page-notices";
1662
1675
  readonly auth: "editor";
1663
1676
  readonly permission: "root:content:write";
1664
1677
  readonly query: readonly [];
1665
1678
  readonly request: null;
1666
1679
  readonly responses: {
1667
- readonly 200: "PageReview";
1680
+ readonly 200: "PageNotices";
1668
1681
  };
1669
1682
  readonly errors: readonly ["not_found", "forbidden"];
1670
1683
  readonly errorSets: readonly ["module:versions", "auth", "request"];
1671
1684
  }, {
1672
- readonly method: "PATCH";
1685
+ readonly method: "POST";
1686
+ readonly module: "versions";
1687
+ readonly path: "/contents/{id}/versions/{version}/page-notices/{notice}/dismiss";
1688
+ readonly auth: "editor";
1689
+ readonly permission: "root:content:write";
1690
+ readonly query: readonly [];
1691
+ readonly request: null;
1692
+ readonly responses: {
1693
+ readonly 204: null;
1694
+ };
1695
+ readonly errors: readonly ["not_found", "forbidden"];
1696
+ readonly errorSets: readonly ["module:versions", "auth", "request"];
1697
+ }, {
1698
+ readonly method: "POST";
1673
1699
  readonly module: "versions";
1674
- readonly path: "/contents/{id}/versions/{version}/pages";
1700
+ readonly path: "/contents/{id}/versions/{version}/page-notices/{notice}/restore";
1675
1701
  readonly auth: "editor";
1676
1702
  readonly permission: "root:content:write";
1677
1703
  readonly query: readonly [];
1678
- readonly request: "PageReviewPatch";
1704
+ readonly request: null;
1679
1705
  readonly responses: {
1680
- readonly 200: "PageReview";
1706
+ readonly 200: "PageNotices";
1681
1707
  };
1682
- readonly errors: readonly ["conflict", "invalid_request", "not_found", "forbidden"];
1708
+ readonly errors: readonly ["invalid_request", "not_found", "forbidden"];
1683
1709
  readonly errorSets: readonly ["module:versions", "auth", "request"];
1684
1710
  }, {
1685
1711
  readonly method: "GET";
@@ -1923,7 +1949,7 @@ export declare const CONTENTKIT_ROUTES: readonly [{
1923
1949
  readonly path: "/contents";
1924
1950
  readonly auth: "public";
1925
1951
  readonly permission: null;
1926
- readonly query: readonly ["kind", "ids", "groups", "exclude_groups", "owner_group", "creators", "access", "tiers", "traits", "attribute", "released_on", "published_within", "tz", "view_status", "language", "display_language", "mode", "sort", "card_entities", "summary", "activity_from", "activity_until", "cursor", "limit"];
1952
+ readonly query: readonly ["kind", "ids", "groups", "exclude_groups", "owner_group", "creators", "access", "tiers", "traits", "attribute", "released_on", "published_within", "tz", "view_status", "language", "display_language", "mode", "sort", "card_entities", "summary", "activity_from", "activity_until", "cursor", "limit", "page"];
1927
1953
  readonly request: null;
1928
1954
  readonly responses: {
1929
1955
  readonly 200: "ContentPage";
@@ -1936,7 +1962,7 @@ export declare const CONTENTKIT_ROUTES: readonly [{
1936
1962
  readonly path: "/search";
1937
1963
  readonly auth: "public";
1938
1964
  readonly permission: null;
1939
- readonly query: readonly ["q", "kind", "ids", "groups", "exclude_groups", "owner_group", "creators", "access", "tiers", "traits", "attribute", "released_on", "published_within", "tz", "view_status", "language", "display_language", "mode", "sort", "card_entities", "summary", "personalize", "session", "cursor", "limit"];
1965
+ readonly query: readonly ["q", "kind", "ids", "groups", "exclude_groups", "owner_group", "creators", "access", "tiers", "traits", "attribute", "released_on", "published_within", "tz", "view_status", "language", "display_language", "mode", "sort", "card_entities", "summary", "personalize", "session", "cursor", "limit", "page"];
1940
1966
  readonly request: null;
1941
1967
  readonly responses: {
1942
1968
  readonly 200: "ContentPage";
@@ -2177,19 +2203,6 @@ export declare const CONTENTKIT_ROUTES: readonly [{
2177
2203
  };
2178
2204
  readonly errors: readonly ["not_configured"];
2179
2205
  readonly errorSets: readonly ["module:discovery", "auth", "request"];
2180
- }, {
2181
- readonly method: "GET";
2182
- readonly module: "discovery";
2183
- readonly path: "/stats/top";
2184
- readonly auth: "staff";
2185
- readonly permission: "root:content:view-restricted";
2186
- readonly query: readonly ["kind", "metric", "period", "access", "tiers", "language", "limit"];
2187
- readonly request: null;
2188
- readonly responses: {
2189
- readonly 200: "TopContents";
2190
- };
2191
- readonly errors: readonly ["not_configured"];
2192
- readonly errorSets: readonly ["module:discovery", "auth", "request"];
2193
2206
  }, {
2194
2207
  readonly method: "GET";
2195
2208
  readonly module: "discovery";