@openrails/contentkit-ui 0.96.0 → 0.97.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/README.md +9 -1
  2. package/dist/client/discovery.d.ts +2 -1
  3. package/dist/client/discovery.js.map +1 -1
  4. package/dist/client/entities.d.ts +6 -0
  5. package/dist/client/entities.js +13 -0
  6. package/dist/client/entities.js.map +1 -1
  7. package/dist/client/generated/routes.d.ts +26 -0
  8. package/dist/client/generated/routes.js +44 -0
  9. package/dist/client/generated/routes.js.map +1 -1
  10. package/dist/client/generated/wire.d.ts +7 -10
  11. package/dist/client/generated/wire.js.map +1 -1
  12. package/dist/client/http.d.ts +1 -0
  13. package/dist/client/http.js +5 -4
  14. package/dist/client/http.js.map +1 -1
  15. package/dist/client/index.d.ts +1 -1
  16. package/dist/client/reader-preferences.js +3 -7
  17. package/dist/client/reader-preferences.js.map +1 -1
  18. package/dist/components/admin/content-manager.d.ts +3 -1
  19. package/dist/components/admin/content-manager.js +241 -239
  20. package/dist/components/admin/content-manager.js.map +1 -1
  21. package/dist/components/admin/group-manager.js +133 -119
  22. package/dist/components/admin/group-manager.js.map +1 -1
  23. package/dist/components/content/editors.d.ts +1 -0
  24. package/dist/components/content/editors.js +1 -0
  25. package/dist/components/content/editors.js.map +1 -1
  26. package/dist/components/content/filters.d.ts +1 -1
  27. package/dist/components/content/filters.js +1 -1
  28. package/dist/components/content/filters.js.map +1 -1
  29. package/dist/components/groups/banner.d.ts +3 -1
  30. package/dist/components/groups/banner.js +29 -26
  31. package/dist/components/groups/banner.js.map +1 -1
  32. package/dist/components/groups/editor.d.ts +4 -1
  33. package/dist/components/groups/editor.js +369 -315
  34. package/dist/components/groups/editor.js.map +1 -1
  35. package/dist/components/groups/memberships.d.ts +2 -0
  36. package/dist/components/groups/memberships.js +1 -0
  37. package/dist/components/groups/memberships.js.map +1 -1
  38. package/dist/components/groups/parts.d.ts +4 -1
  39. package/dist/components/groups/parts.js +32 -30
  40. package/dist/components/groups/parts.js.map +1 -1
  41. package/dist/locales/en.d.ts +2 -0
  42. package/dist/locales/groups/de.js +2 -0
  43. package/dist/locales/groups/de.js.map +1 -1
  44. package/dist/locales/groups/en.d.ts +2 -0
  45. package/dist/locales/groups/en.js +2 -0
  46. package/dist/locales/groups/en.js.map +1 -1
  47. package/dist/locales/groups/es.js +2 -0
  48. package/dist/locales/groups/es.js.map +1 -1
  49. package/dist/locales/groups/ja.js +2 -0
  50. package/dist/locales/groups/ja.js.map +1 -1
  51. package/dist/locales/groups/ko.js +2 -0
  52. package/dist/locales/groups/ko.js.map +1 -1
  53. package/dist/locales/groups/zh.js +2 -0
  54. package/dist/locales/groups/zh.js.map +1 -1
  55. package/dist/react/discovery.d.ts +2 -1
  56. package/dist/react/discovery.js +54 -29
  57. package/dist/react/discovery.js.map +1 -1
  58. package/dist/react/groups.d.ts +3 -1
  59. package/dist/react/groups.js +7 -7
  60. package/dist/react/groups.js.map +1 -1
  61. package/package.json +1 -1
package/README.md CHANGED
@@ -689,6 +689,10 @@ const nav = useMemo(() => ({ pathname: location.pathname, search: location.searc
689
689
  at `basePath/{kind}/{id}` with `GroupEditor` and `GroupMembers`. Props:
690
690
  `groupKinds` (default the model's site-owned ones), `kinds`, `labels`,
691
691
  `contentKind`, `renderContent`, `pageSize`. `useGroupManagerAccess()`.
692
+ Its staff listing includes internal entities, with an “Internal use only”
693
+ setting in the editor. Public directories and search never include those
694
+ entities. Use `management` on a standalone `GroupEditor` or `GroupPicker`
695
+ to opt into the same staff API; ordinary user-owned editors stay public.
692
696
 
693
697
  ```tsx
694
698
  <GroupManager client={contentkit} basePath="/admin/groups" navigation={nav} labels={{ artist: "Artists" }} />
@@ -821,7 +825,11 @@ the host owns search-page navigation.
821
825
  Render these under `ContentKitProvider` with `viewer={null}` for guests. Guest
822
826
  language choices and filter overrides are stored per app in localStorage and
823
827
  shared across tabs. Account filter choices come from ContentKit's database;
824
- account languages use the provider's `languagePreference` adapter:
828
+ account languages use the provider's `languagePreference` adapter. Each domain
829
+ only offers its configured, user-toggleable entities; internal classifications
830
+ and required or locked rules never appear in reader settings. Labels use the
831
+ entities' localized names. Saving this domain's settings preserves choices for
832
+ entities offered by other domains.
825
833
 
826
834
  ```tsx
827
835
  <ContentKitProvider
@@ -127,8 +127,9 @@ export declare class DiscoveryClient {
127
127
  releaseDates(q?: ReleaseDatesQuery, signal?: AbortSignal): Promise<ReleaseDates>;
128
128
  /** Every visible card in each day; opening or expanding a day needs no further request. */
129
129
  releases(q?: DailyReleasesQuery, signal?: AbortSignal): Promise<DailyReleases>;
130
+ /** Only the current domain's reader-toggleable entities, named in the requested language. */
130
131
  contentFilters(language?: string, signal?: AbortSignal): Promise<ContentFilters>;
131
- /** Replaces the reader's filter settings; every read applies them. */
132
+ /** Replaces this domain's reader settings; other domains' choices remain intact. */
132
133
  setContentFilters(update: ContentFiltersUpdate, language?: string): Promise<ContentFilters>;
133
134
  /** What the reader engaged with, most recent first. */
134
135
  history(q?: HistoryQuery, signal?: AbortSignal): Promise<ContentPage>;
@@ -1 +1 @@
1
- {"version":3,"file":"discovery.js","names":[],"sources":["../../src/client/discovery.ts"],"sourcesContent":["import type {\n ContentFilters,\n ContentFiltersUpdate,\n ContentPage,\n DailyReleases,\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\" | \"most_viewed\" | \"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 /** 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 & { sort?: ListSort };\nexport type PopularPeriod = \"7d\" | \"90d\" | \"365d\" | \"all\";\nexport type PopularQuery = Omit<ContentQuery, \"sort\" | \"published_within\">;\n\n/** Match the popularity score window and publication age window. */\nexport function popularQuery(period: PopularPeriod = \"7d\", filters: PopularQuery = {}): ContentQuery {\n return { ...filters, sort: `popular:${period}`, published_within: period };\n}\n\nexport type SearchQuery = ContentFilterQuery &\n ReaderQuery &\n CardQuery &\n PageQuery & {\n q: string;\n sort?: \"relevance\" | ListSort;\n personalize?: 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 };\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 & { days?: number; time_zone?: string };\nexport type HistoryQuery = ReaderQuery & CardQuery & PageQuery & { kind?: string; status?: \"seen\" | \"in_progress\" | \"completed\" };\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// Comma-joined list parameters; `attribute` repeats instead.\nconst LISTS = new Set([\"groups\", \"exclude_groups\", \"creators\", \"access\", \"tiers\", \"traits\", \"card_entities\", \"entity_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 /** 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 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 /** Replaces the reader's filter settings; every read applies them. */\n async setContentFilters(update: ContentFiltersUpdate, language?: string): Promise<ContentFilters> {\n const emit = this.http.captureChanges();\n const out: ContentFilters = await call(this.http, \"PUT\", \"/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 /** Views and clicks; signed out, of the session 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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"}
1
+ {"version":3,"file":"discovery.js","names":[],"sources":["../../src/client/discovery.ts"],"sourcesContent":["import type {\n ContentFilters,\n ContentFiltersUpdate,\n ContentPage,\n DailyReleases,\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\" | \"most_viewed\" | \"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 /** 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 & { sort?: ListSort };\nexport type PopularPeriod = \"7d\" | \"90d\" | \"365d\" | \"all\";\nexport type PopularQuery = Omit<ContentQuery, \"sort\" | \"published_within\">;\n\n/** Match the popularity score window and publication age window. */\nexport function popularQuery(period: PopularPeriod = \"7d\", filters: PopularQuery = {}): ContentQuery {\n return { ...filters, sort: `popular:${period}`, published_within: period };\n}\n\nexport type SearchQuery = ContentFilterQuery &\n ReaderQuery &\n CardQuery &\n PageQuery & {\n q: string;\n sort?: \"relevance\" | ListSort;\n personalize?: 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 };\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 & { days?: number; time_zone?: string };\nexport type HistoryQuery = ReaderQuery & CardQuery & PageQuery & { kind?: string; status?: \"seen\" | \"in_progress\" | \"completed\" };\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// Comma-joined list parameters; `attribute` repeats instead.\nconst LISTS = new Set([\"groups\", \"exclude_groups\", \"creators\", \"access\", \"tiers\", \"traits\", \"card_entities\", \"entity_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 /** 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 /** Replaces this domain's reader settings; other domains' choices 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, \"PUT\", \"/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 /** Views and clicks; signed out, of the session 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":";;;AA6EA,SAAgB,EAAa,IAAwB,MAAM,IAAwB,CAAC,GAAiB;CACnG,OAAO;EAAE,GAAG;EAAS,MAAM,WAAW;EAAU,kBAAkB;CAAO;AAC3E;AAkCA,IAAM,oBAAQ,IAAI,IAAI;CAAC;CAAU;CAAkB;CAAY;CAAU;CAAS;CAAU;CAAiB;AAAc,CAAC;AAG5H,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,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,OAAO,uBAAuB;GAAE,OAAO,EAAE,YAAS;GAAG,MAAM;EAAO,CAAC;EAErH,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,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"}
@@ -4,6 +4,8 @@ import { MediaClient, NamedOptions } from './media/client.js';
4
4
  import { RouteQuery } from './route.js';
5
5
  /** GET /entities filters, as the route names them. */
6
6
  export type EntityQuery = RouteQuery<"GET", "/entities">;
7
+ /** Staff entity listing, including internal entities. */
8
+ export type ManageEntityQuery = RouteQuery<"GET", "/manage/entities">;
7
9
  /** GET /me/entities filters. */
8
10
  export type MyEntityQuery = RouteQuery<"GET", "/me/entities">;
9
11
  /** An entity's image slots: every kind's cover, a user-owned kind's avatar. */
@@ -16,6 +18,10 @@ export declare class EntitiesClient {
16
18
  private readonly media;
17
19
  constructor(http: Http, media: MediaClient);
18
20
  list(q?: EntityQuery, signal?: AbortSignal): Promise<GroupPage>;
21
+ /** Staff listing, including internal entities hidden from public discovery. */
22
+ manage(q?: ManageEntityQuery, signal?: AbortSignal): Promise<GroupPage>;
23
+ /** Staff detail, including internal entities. */
24
+ getForManagement(id: string, language?: string, signal?: AbortSignal): Promise<GroupDetail>;
19
25
  /** A merged group answers with its survivor. */
20
26
  get(id: string, language?: string, signal?: AbortSignal): Promise<GroupDetail>;
21
27
  create(groups: GroupInput[]): Promise<Group[]>;
@@ -12,6 +12,19 @@ var t = class {
12
12
  signal: n
13
13
  });
14
14
  }
15
+ manage(t = {}, n) {
16
+ return e(this.http, "GET", "/manage/entities", {
17
+ query: t,
18
+ signal: n
19
+ });
20
+ }
21
+ getForManagement(t, n, r) {
22
+ return e(this.http, "GET", "/manage/entities/{id}", {
23
+ params: { id: t },
24
+ query: { language: n },
25
+ signal: r
26
+ });
27
+ }
15
28
  get(t, n, r) {
16
29
  return e(this.http, "GET", "/entities/{id}", {
17
30
  params: { id: t },
@@ -1 +1 @@
1
- {"version":3,"file":"entities.js","names":[],"sources":["../../src/client/entities.ts"],"sourcesContent":["import type {\n ContentGroups,\n ContentMemberships,\n Edge,\n EdgeInput,\n Group,\n GroupDetail,\n GroupInput,\n GroupPage,\n GroupUpdate,\n MemberPage,\n MemberRef,\n Membership,\n MergeReport,\n MyGroupPage,\n Name,\n RefBody,\n Written,\n} from \"./generated/wire.js\";\nimport type { Http } from \"./http.js\";\nimport type { MediaClient, NamedOptions } from \"./media/client.js\";\nimport { call, type RouteQuery } from \"./route.js\";\n\n/** GET /entities filters, as the route names them. */\nexport type EntityQuery = RouteQuery<\"GET\", \"/entities\">;\n/** GET /me/entities filters. */\nexport type MyEntityQuery = RouteQuery<\"GET\", \"/me/entities\">;\n/** An entity's image slots: every kind's cover, a user-owned kind's avatar. */\nexport type EntityImageSlot = \"cover\" | \"avatar\";\n/** GET /entities/{id}/members paging. */\nexport type MemberQuery = RouteQuery<\"GET\", \"/entities/{id}/members\">;\n\n/** Entities: public listings and pages; writes for staff (root:groups:write). */\nexport class EntitiesClient {\n constructor(\n private readonly http: Http,\n private readonly media: MediaClient,\n ) {}\n\n list(q: EntityQuery = {}, signal?: AbortSignal): Promise<GroupPage> {\n return call(this.http, \"GET\", \"/entities\", { query: q, signal });\n }\n /** A merged group answers with its survivor. */\n get(id: string, language?: string, signal?: AbortSignal): Promise<GroupDetail> {\n return call(this.http, \"GET\", \"/entities/{id}\", { params: { id }, query: { language }, signal });\n }\n create(groups: GroupInput[]): Promise<Group[]> {\n return this.write(call(this.http, \"POST\", \"/entities\", { body: groups }));\n }\n update(id: string, patch: GroupUpdate): Promise<Group> {\n return this.write(call(this.http, \"PATCH\", \"/entities/{id}\", { params: { id }, body: patch }));\n }\n /** Marks the group deleted. */\n remove(id: string): Promise<Group> {\n return this.write(call(this.http, \"DELETE\", \"/entities/{id}\", { params: { id } }));\n }\n /** Replaces, adds or removes names and aliases; resolves with the group's names. */\n names(id: string, op: \"set\" | \"add\" | \"remove\", names: Name[]): Promise<Name[]> {\n const method = op === \"set\" ? \"PUT\" : op === \"add\" ? \"POST\" : \"DELETE\";\n return this.write(call(this.http, method, \"/entities/{id}/names\", { params: { id }, body: names }));\n }\n /** Adds or removes edges from the group; resolves with every edge touching it. */\n edges(id: string, op: \"add\" | \"remove\", edges: EdgeInput[]): Promise<Edge[]> {\n return this.write(call(this.http, op === \"add\" ? \"POST\" : \"DELETE\", \"/entities/{id}/edges\", { params: { id }, body: edges }));\n }\n /** The group's media folder: the media kind named after its group kind. */\n folder(group: { kind: string; id: string }): RefBody {\n return { kind: group.kind, id: group.id };\n }\n /**\n * Uploads an image to the folder's server-named `{slot}/{name}` and\n * selects it as the group's cover or avatar (a user-owned kind's).\n */\n async uploadImage(group: { kind: string; id: string }, slot: EntityImageSlot, file: Blob & { name?: string }, o: Omit<NamedOptions, \"path\"> = {}): Promise<Group> {\n const up = await this.media.uploadNamed(this.folder(group), file, { ...o, path: `${slot}/${file.name || slot}` });\n return this.update(group.id, { [slot]: { image: up.name } });\n }\n /** Entities the signed-in person owns, edits or belongs to. */\n mine(q: MyEntityQuery = {}, signal?: AbortSignal): Promise<MyGroupPage> {\n return call(this.http, \"GET\", \"/me/entities\", { query: q, signal });\n }\n merge(id: string, into: string): Promise<MergeReport> {\n return this.write(call(this.http, \"POST\", \"/entities/{id}/merge\", { params: { id }, body: { into } }));\n }\n /** The group's memberships in member order, a cursor page at a time. */\n members(id: string, q: MemberQuery = {}, signal?: AbortSignal): Promise<MemberPage> {\n return call(this.http, \"GET\", \"/entities/{id}/members\", { params: { id }, query: q, signal });\n }\n addMembers(id: string, members: Membership[]): Promise<Written> {\n return this.write(call(this.http, \"POST\", \"/entities/{id}/members\", { params: { id }, body: members }));\n }\n removeMembers(id: string, members: Membership[]): Promise<Written> {\n return this.write(call(this.http, \"DELETE\", \"/entities/{id}/members\", { params: { id }, body: members }));\n }\n /** Orders an ordered group: the listed members first, in list order. */\n reorder(id: string, members: MemberRef[]): Promise<Written> {\n return this.write(call(this.http, \"PUT\", \"/entities/{id}/members/order\", { params: { id }, body: { members } }));\n }\n /** A content's memberships, its versions' included, with their groups. */\n ofContent(contentId: string, language?: string, signal?: AbortSignal): Promise<ContentMemberships> {\n return call(this.http, \"GET\", \"/contents/{id}/entities\", { params: { id: contentId }, query: { language }, signal });\n }\n /** Replaces a content's memberships in the kinds named (every kind its kind joins by default). */\n setOfContent(contentId: string, set: ContentGroups, language?: string): Promise<ContentMemberships> {\n return this.write(call(this.http, \"PUT\", \"/contents/{id}/entities\", { params: { id: contentId }, query: { language }, body: set }));\n }\n\n private async write<T>(p: Promise<T>): Promise<T> {\n const emit = this.http.captureChanges();\n const out = await p;\n emit({ type: \"entities.changed\" });\n return out;\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"entities.js","names":[],"sources":["../../src/client/entities.ts"],"sourcesContent":["import type {\n ContentGroups,\n ContentMemberships,\n Edge,\n EdgeInput,\n Group,\n GroupDetail,\n GroupInput,\n GroupPage,\n GroupUpdate,\n MemberPage,\n MemberRef,\n Membership,\n MergeReport,\n MyGroupPage,\n Name,\n RefBody,\n Written,\n} from \"./generated/wire.js\";\nimport type { Http } from \"./http.js\";\nimport type { MediaClient, NamedOptions } from \"./media/client.js\";\nimport { call, type RouteQuery } from \"./route.js\";\n\n/** GET /entities filters, as the route names them. */\nexport type EntityQuery = RouteQuery<\"GET\", \"/entities\">;\n/** Staff entity listing, including internal entities. */\nexport type ManageEntityQuery = RouteQuery<\"GET\", \"/manage/entities\">;\n/** GET /me/entities filters. */\nexport type MyEntityQuery = RouteQuery<\"GET\", \"/me/entities\">;\n/** An entity's image slots: every kind's cover, a user-owned kind's avatar. */\nexport type EntityImageSlot = \"cover\" | \"avatar\";\n/** GET /entities/{id}/members paging. */\nexport type MemberQuery = RouteQuery<\"GET\", \"/entities/{id}/members\">;\n\n/** Entities: public listings and pages; writes for staff (root:groups:write). */\nexport class EntitiesClient {\n constructor(\n private readonly http: Http,\n private readonly media: MediaClient,\n ) {}\n\n list(q: EntityQuery = {}, signal?: AbortSignal): Promise<GroupPage> {\n return call(this.http, \"GET\", \"/entities\", { query: q, signal });\n }\n /** Staff listing, including internal entities hidden from public discovery. */\n manage(q: ManageEntityQuery = {}, signal?: AbortSignal): Promise<GroupPage> {\n return call(this.http, \"GET\", \"/manage/entities\", { query: q, signal });\n }\n /** Staff detail, including internal entities. */\n getForManagement(id: string, language?: string, signal?: AbortSignal): Promise<GroupDetail> {\n return call(this.http, \"GET\", \"/manage/entities/{id}\", { params: { id }, query: { language }, signal });\n }\n /** A merged group answers with its survivor. */\n get(id: string, language?: string, signal?: AbortSignal): Promise<GroupDetail> {\n return call(this.http, \"GET\", \"/entities/{id}\", { params: { id }, query: { language }, signal });\n }\n create(groups: GroupInput[]): Promise<Group[]> {\n return this.write(call(this.http, \"POST\", \"/entities\", { body: groups }));\n }\n update(id: string, patch: GroupUpdate): Promise<Group> {\n return this.write(call(this.http, \"PATCH\", \"/entities/{id}\", { params: { id }, body: patch }));\n }\n /** Marks the group deleted. */\n remove(id: string): Promise<Group> {\n return this.write(call(this.http, \"DELETE\", \"/entities/{id}\", { params: { id } }));\n }\n /** Replaces, adds or removes names and aliases; resolves with the group's names. */\n names(id: string, op: \"set\" | \"add\" | \"remove\", names: Name[]): Promise<Name[]> {\n const method = op === \"set\" ? \"PUT\" : op === \"add\" ? \"POST\" : \"DELETE\";\n return this.write(call(this.http, method, \"/entities/{id}/names\", { params: { id }, body: names }));\n }\n /** Adds or removes edges from the group; resolves with every edge touching it. */\n edges(id: string, op: \"add\" | \"remove\", edges: EdgeInput[]): Promise<Edge[]> {\n return this.write(call(this.http, op === \"add\" ? \"POST\" : \"DELETE\", \"/entities/{id}/edges\", { params: { id }, body: edges }));\n }\n /** The group's media folder: the media kind named after its group kind. */\n folder(group: { kind: string; id: string }): RefBody {\n return { kind: group.kind, id: group.id };\n }\n /**\n * Uploads an image to the folder's server-named `{slot}/{name}` and\n * selects it as the group's cover or avatar (a user-owned kind's).\n */\n async uploadImage(group: { kind: string; id: string }, slot: EntityImageSlot, file: Blob & { name?: string }, o: Omit<NamedOptions, \"path\"> = {}): Promise<Group> {\n const up = await this.media.uploadNamed(this.folder(group), file, { ...o, path: `${slot}/${file.name || slot}` });\n return this.update(group.id, { [slot]: { image: up.name } });\n }\n /** Entities the signed-in person owns, edits or belongs to. */\n mine(q: MyEntityQuery = {}, signal?: AbortSignal): Promise<MyGroupPage> {\n return call(this.http, \"GET\", \"/me/entities\", { query: q, signal });\n }\n merge(id: string, into: string): Promise<MergeReport> {\n return this.write(call(this.http, \"POST\", \"/entities/{id}/merge\", { params: { id }, body: { into } }));\n }\n /** The group's memberships in member order, a cursor page at a time. */\n members(id: string, q: MemberQuery = {}, signal?: AbortSignal): Promise<MemberPage> {\n return call(this.http, \"GET\", \"/entities/{id}/members\", { params: { id }, query: q, signal });\n }\n addMembers(id: string, members: Membership[]): Promise<Written> {\n return this.write(call(this.http, \"POST\", \"/entities/{id}/members\", { params: { id }, body: members }));\n }\n removeMembers(id: string, members: Membership[]): Promise<Written> {\n return this.write(call(this.http, \"DELETE\", \"/entities/{id}/members\", { params: { id }, body: members }));\n }\n /** Orders an ordered group: the listed members first, in list order. */\n reorder(id: string, members: MemberRef[]): Promise<Written> {\n return this.write(call(this.http, \"PUT\", \"/entities/{id}/members/order\", { params: { id }, body: { members } }));\n }\n /** A content's memberships, its versions' included, with their groups. */\n ofContent(contentId: string, language?: string, signal?: AbortSignal): Promise<ContentMemberships> {\n return call(this.http, \"GET\", \"/contents/{id}/entities\", { params: { id: contentId }, query: { language }, signal });\n }\n /** Replaces a content's memberships in the kinds named (every kind its kind joins by default). */\n setOfContent(contentId: string, set: ContentGroups, language?: string): Promise<ContentMemberships> {\n return this.write(call(this.http, \"PUT\", \"/contents/{id}/entities\", { params: { id: contentId }, query: { language }, body: set }));\n }\n\n private async write<T>(p: Promise<T>): Promise<T> {\n const emit = this.http.captureChanges();\n const out = await p;\n emit({ type: \"entities.changed\" });\n return out;\n }\n}\n"],"mappings":"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"}
@@ -1343,6 +1343,32 @@ export declare const CONTENTKIT_ROUTES: readonly [{
1343
1343
  };
1344
1344
  readonly errors: readonly ["not_found"];
1345
1345
  readonly errorSets: readonly ["module:entities", "auth", "request"];
1346
+ }, {
1347
+ readonly method: "GET";
1348
+ readonly module: "entities";
1349
+ readonly path: "/manage/entities";
1350
+ readonly auth: "staff";
1351
+ readonly permission: "root:entities:write";
1352
+ readonly query: readonly ["kind", "parent", "scope", "slug", "q", "letter", "language", "content_kind", "sort", "cursor", "limit"];
1353
+ readonly request: null;
1354
+ readonly responses: {
1355
+ readonly 200: "GroupPage";
1356
+ };
1357
+ readonly errors: readonly [];
1358
+ readonly errorSets: readonly ["module:entities", "auth", "request"];
1359
+ }, {
1360
+ readonly method: "GET";
1361
+ readonly module: "entities";
1362
+ readonly path: "/manage/entities/{id}";
1363
+ readonly auth: "staff";
1364
+ readonly permission: "root:entities:write";
1365
+ readonly query: readonly ["language"];
1366
+ readonly request: null;
1367
+ readonly responses: {
1368
+ readonly 200: "GroupDetail";
1369
+ };
1370
+ readonly errors: readonly ["not_found"];
1371
+ readonly errorSets: readonly ["module:entities", "auth", "request"];
1346
1372
  }, {
1347
1373
  readonly method: "GET";
1348
1374
  readonly module: "entities";
@@ -1893,6 +1893,50 @@ var e = {
1893
1893
  "request"
1894
1894
  ]
1895
1895
  },
1896
+ {
1897
+ method: "GET",
1898
+ module: "entities",
1899
+ path: "/manage/entities",
1900
+ auth: "staff",
1901
+ permission: "root:entities:write",
1902
+ query: [
1903
+ "kind",
1904
+ "parent",
1905
+ "scope",
1906
+ "slug",
1907
+ "q",
1908
+ "letter",
1909
+ "language",
1910
+ "content_kind",
1911
+ "sort",
1912
+ "cursor",
1913
+ "limit"
1914
+ ],
1915
+ request: null,
1916
+ responses: { 200: "GroupPage" },
1917
+ errors: [],
1918
+ errorSets: [
1919
+ "module:entities",
1920
+ "auth",
1921
+ "request"
1922
+ ]
1923
+ },
1924
+ {
1925
+ method: "GET",
1926
+ module: "entities",
1927
+ path: "/manage/entities/{id}",
1928
+ auth: "staff",
1929
+ permission: "root:entities:write",
1930
+ query: ["language"],
1931
+ request: null,
1932
+ responses: { 200: "GroupDetail" },
1933
+ errors: ["not_found"],
1934
+ errorSets: [
1935
+ "module:entities",
1936
+ "auth",
1937
+ "request"
1938
+ ]
1939
+ },
1896
1940
  {
1897
1941
  method: "GET",
1898
1942
  module: "entities",