@omelhorsite/sdk 0.3.0 → 0.4.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 (70) hide show
  1. package/README.md +112 -130
  2. package/dist/index.js +1045 -1242
  3. package/dist/types/index.d.ts +1 -0
  4. package/dist/types/internal/attachments.d.ts +17 -0
  5. package/dist/types/internal/helpers.d.ts +9 -0
  6. package/dist/types/listing.d.ts +90 -0
  7. package/dist/types/resources/account.d.ts +5 -2
  8. package/dist/types/resources/admin/authorizedApplications.d.ts +133 -0
  9. package/dist/types/resources/admin/chests.d.ts +51 -0
  10. package/dist/types/resources/admin/eventAlerts.d.ts +60 -0
  11. package/dist/types/resources/admin/identities.d.ts +99 -0
  12. package/dist/types/resources/admin/index.d.ts +77 -0
  13. package/dist/types/resources/admin/jobs.d.ts +97 -0
  14. package/dist/types/resources/admin/myOauthApplications.d.ts +282 -0
  15. package/dist/types/resources/admin/notepads.d.ts +49 -0
  16. package/dist/types/resources/admin/oauthApplications.d.ts +309 -0
  17. package/dist/types/resources/admin/quotas.d.ts +114 -0
  18. package/dist/types/resources/admin/shortLinks.d.ts +201 -0
  19. package/dist/types/resources/admin/types.d.ts +222 -0
  20. package/dist/types/resources/admin/vocalSeparations.d.ts +98 -0
  21. package/dist/types/resources/admin.d.ts +17 -13
  22. package/dist/types/resources/auth/sessions.d.ts +6 -9
  23. package/dist/types/resources/content/analysis.d.ts +62 -0
  24. package/dist/types/resources/content/blogs.d.ts +471 -0
  25. package/dist/types/resources/content/feedbacks.d.ts +263 -0
  26. package/dist/types/resources/content/index.d.ts +52 -0
  27. package/dist/types/resources/content/intel/articles.d.ts +230 -0
  28. package/dist/types/resources/content/intel/config.d.ts +135 -0
  29. package/dist/types/resources/content/intel/index.d.ts +53 -0
  30. package/dist/types/resources/content/intel/items.d.ts +91 -0
  31. package/dist/types/resources/content/intel/reports.d.ts +108 -0
  32. package/dist/types/resources/content/intel/scripts.d.ts +152 -0
  33. package/dist/types/resources/content/intel/sources.d.ts +236 -0
  34. package/dist/types/resources/content/intel/stats.d.ts +105 -0
  35. package/dist/types/resources/content/intel/types.d.ts +86 -0
  36. package/dist/types/resources/content/jokes.d.ts +93 -0
  37. package/dist/types/resources/content/notifications.d.ts +147 -0
  38. package/dist/types/resources/content/serviceUsages.d.ts +73 -0
  39. package/dist/types/resources/content/servicesStatus.d.ts +194 -0
  40. package/dist/types/resources/content/siteConfig.d.ts +43 -0
  41. package/dist/types/resources/content/spaceInvaders.d.ts +154 -0
  42. package/dist/types/resources/content.d.ts +41 -236
  43. package/dist/types/resources/jobs.d.ts +7 -4
  44. package/dist/types/resources/library/annotations.d.ts +162 -0
  45. package/dist/types/resources/library/books.d.ts +534 -0
  46. package/dist/types/resources/library/chat.d.ts +259 -0
  47. package/dist/types/resources/library/index.d.ts +39 -0
  48. package/dist/types/resources/library/shelves.d.ts +237 -0
  49. package/dist/types/resources/library/types.d.ts +120 -0
  50. package/dist/types/resources/library.d.ts +20 -22
  51. package/dist/types/resources/movies/addons.d.ts +388 -0
  52. package/dist/types/resources/movies/collections.d.ts +352 -0
  53. package/dist/types/resources/movies/index.d.ts +29 -0
  54. package/dist/types/resources/movies/types.d.ts +50 -0
  55. package/dist/types/resources/movies/watchProgress.d.ts +277 -0
  56. package/dist/types/resources/movies.d.ts +27 -53
  57. package/dist/types/resources/music/artists.d.ts +3 -1
  58. package/dist/types/resources/music/imports.d.ts +9 -8
  59. package/dist/types/resources/music/playlists.d.ts +8 -7
  60. package/dist/types/resources/music/songs.d.ts +5 -21
  61. package/dist/types/resources/shortLinks.d.ts +5 -2
  62. package/dist/types/resources/social/groupChats.d.ts +458 -0
  63. package/dist/types/resources/social/index.d.ts +19 -0
  64. package/dist/types/resources/social/messages.d.ts +424 -0
  65. package/dist/types/resources/social/relationships.d.ts +322 -0
  66. package/dist/types/resources/social/types.d.ts +37 -0
  67. package/dist/types/resources/social.d.ts +21 -3
  68. package/dist/types/resources/storage.d.ts +24 -6
  69. package/dist/types/resources/tickets.d.ts +19 -10
  70. package/package.json +1 -1
@@ -0,0 +1,105 @@
1
+ /** Intel stats: the dashboard counters. */
2
+ import { Resource } from "../../../http";
3
+ import type { RequestOptions } from "../../../types";
4
+ /** One category bucket of {@link IntelStats}. */
5
+ export interface IntelCategoryCount {
6
+ readonly category: string;
7
+ /** Named `c`, not `count`. */
8
+ readonly c: number;
9
+ }
10
+ /** One day of the {@link IntelStats} histogram. */
11
+ export interface IntelDayCount {
12
+ /** `YYYY-MM-DD`, from Postgres `DATE(last_seen_at)`. Not a full timestamp. */
13
+ readonly day: string;
14
+ readonly c: number;
15
+ }
16
+ /**
17
+ * The importance histogram, with the backend's own Portuguese bucket names.
18
+ *
19
+ * The boundaries are hard-coded server-side and are not
20
+ * configurable: `critico` >=9, `alta` 7-8, `media` 5-6, `baixa` 3-4, `ruido`
21
+ * <3. Note they are NOT the same thresholds as
22
+ * {@link IntelConfig.report_min_importance} or
23
+ * {@link IntelConfig.enrich_min_importance} - those are yours, these are the
24
+ * dashboard's.
25
+ */
26
+ export interface IntelImportanceBuckets {
27
+ readonly critico: number;
28
+ readonly alta: number;
29
+ readonly media: number;
30
+ readonly baixa: number;
31
+ readonly ruido: number;
32
+ }
33
+ /** Row counts on the {@link IntelStats} answer. */
34
+ export interface IntelStatsTotals {
35
+ /** Stories you own. */
36
+ readonly articles: number;
37
+ /**
38
+ * **Not the number of feeds you have configured.** This counts the
39
+ * story-to-item POINTER rows, so this is "how many citations exist across
40
+ * all my stories" and it grows without bound as
41
+ * stories accumulate. If you want the number of configured sources, read the
42
+ * length of {@link IntelSourcesNamespace.list}. The name is the backend's and
43
+ * the SDK does not rename it, but do not put it under a "Sources" label.
44
+ */
45
+ readonly sources: number;
46
+ readonly reports: number;
47
+ /** Raw items you own, processed or not. */
48
+ readonly items: number;
49
+ /**
50
+ * Items the analysis pipeline has not consumed yet (`processed_at IS NULL`).
51
+ *
52
+ * The one number worth watching: a figure that climbs and never falls means
53
+ * the pipeline is not running - most often because `Intel::LlmClient` is
54
+ * disabled for want of an API key, in which case `AnalysisDispatcherJob`
55
+ * returns immediately and silently.
56
+ */
57
+ readonly pending_items: number;
58
+ }
59
+ /**
60
+ * `GET /intel_stats` - counters for the intel dashboard.
61
+ *
62
+ * Not a record: it has no `id`, no timestamps and no `:extended` view.
63
+ */
64
+ export interface IntelStats {
65
+ readonly totals: IntelStatsTotals;
66
+ /**
67
+ * Categories by story count, descending. Stories with a `null` category are
68
+ * EXCLUDED, so these do not sum to `totals.articles`.
69
+ */
70
+ readonly by_category: IntelCategoryCount[];
71
+ /**
72
+ * The last 30 days by `last_seen_at`, ascending.
73
+ *
74
+ * Sparse: a day with no activity is simply ABSENT, not present with zero.
75
+ * Fill the gaps before plotting or the line will lie about its own x-axis.
76
+ */
77
+ readonly by_day: IntelDayCount[];
78
+ readonly by_importance: IntelImportanceBuckets;
79
+ /** Stories touched in the last 24 hours. */
80
+ readonly last24h: number;
81
+ }
82
+ /** `/intel_stats` - the dashboard counters. One route, one verb. */
83
+ export declare class IntelStatsNamespace extends Resource {
84
+ /**
85
+ * `GET /intel_stats` - every counter the intel dashboard shows, in one call.
86
+ *
87
+ * Also a Rails singular resource, so the path is `/intel_stats` with no id
88
+ * despite the plural spelling.
89
+ *
90
+ * **Cost, and the reason not to poll this.** The controller does
91
+ * `articles.pluck(:importance)` - it loads the importance of EVERY story you
92
+ * own into Ruby memory to build {@link IntelStats.by_importance} - and then
93
+ * runs five more aggregate queries beside it. There is no cache, no `ETag`
94
+ * (the hand-written action never calls `stale?`, unlike every list in this
95
+ * file) and therefore no `304`. Cost grows linearly with your story count for
96
+ * ever. Fetch it on a dashboard open, not on a timer.
97
+ *
98
+ * Read {@link IntelStatsTotals.sources} before you label it: it does not
99
+ * count your feeds.
100
+ *
101
+ * @throws {OmsApiError} 403 `"Intel access is restricted."` outside the
102
+ * allowlist.
103
+ */
104
+ get(options?: RequestOptions): Promise<IntelStats>;
105
+ }
@@ -0,0 +1,86 @@
1
+ /** Vocabulary shared across the intel families. */
2
+ /**
3
+ * ## Access: this is effectively a one-user feature
4
+ *
5
+ * Every intel route is gated by a fixed allowlist of one handle plus admins.
6
+ * So: anonymous is `401`, any other signed-in account is
7
+ * `403 "Intel access is restricted."`, and no amount of correct request
8
+ * shaping changes that. Do not build a shared feature on it, and do not
9
+ * treat a 403 here as a bug in the caller.
10
+ *
11
+ * An OAuth access token is `403 {"error":"insufficient_scope"}` on every
12
+ * route below. Session credential only.
13
+ *
14
+ * ## Ids are STRINGS here
15
+ *
16
+ * Articles, reports, sources, scripts, items and the config row all carry
17
+ * opaque string ids, unlike blogs, notifications, jokes and Space Invaders
18
+ * games, whose ids are integers. Nothing in intel is ever a number you can
19
+ * compare or sort by.
20
+ *
21
+ * ## Ceilings
22
+ *
23
+ * None of their own. Every route rides the general bucket: 600 requests per
24
+ * minute for an authenticated caller. Two of them are still expensive and are
25
+ * documented as such - {@link IntelStatsNamespace.get} and
26
+ * {@link IntelSourcesNamespace.run}.
27
+ */
28
+ /** Categories a story can carry. `null` when the classifier declined to pick one. */
29
+ export declare const INTEL_ARTICLE_CATEGORIES: readonly ["incidente", "politica", "comunidade", "sociedade", "internacional", "economia", "outro"];
30
+ /**
31
+ * A story's category.
32
+ *
33
+ * Widened with `string & {}` deliberately: the list is a Ruby constant that a
34
+ * migration can extend without the SDK noticing, and a `switch` that fails to
35
+ * compile on a new category is worse than one that falls through to a default.
36
+ * The backend DOES validate inclusion, so a value outside the list can only
37
+ * mean the constant moved.
38
+ */
39
+ export type IntelArticleCategory = (typeof INTEL_ARTICLE_CATEGORIES)[number] | (string & {});
40
+ /** Report windows a report can cover. */
41
+ export declare const INTEL_REPORT_KINDS: readonly ["6h", "day", "week", "month"];
42
+ /** Which window a report covers. */
43
+ export type IntelReportKind = (typeof INTEL_REPORT_KINDS)[number] | (string & {});
44
+ /**
45
+ * Base of the third-party image proxy {@link intelArticleImageUrl} builds on.
46
+ *
47
+ * `wsrv.nl` is a free public image CDN. It is NOT this API and NOT our
48
+ * infrastructure.
49
+ */
50
+ export declare const INTEL_IMAGE_PROXY_BASE_URL = "https://wsrv.nl/";
51
+ /** Knobs for {@link intelArticleImageUrl}. */
52
+ export interface IntelImageOptions {
53
+ /** Target width in pixels. Default 480. Height follows the aspect ratio. */
54
+ readonly width?: number;
55
+ /** Quality, 1-100. Default 45 - low on purpose; these are thumbnails. */
56
+ readonly quality?: number;
57
+ }
58
+ /**
59
+ * Builds a resized, re-compressed URL for {@link IntelArticle.image_url}.
60
+ *
61
+ * Pure string building, no request, isolate-safe. Returns `""` for a story with
62
+ * no image so it can be dropped straight into an `<img src>` without a
63
+ * conditional - though a real client should test the field and render nothing.
64
+ *
65
+ * ## What this actually does, and why you might not want it
66
+ *
67
+ * `image_url` is the raw `og:image` of a news site: full size, arbitrary
68
+ * format, arbitrary weight, and served from that site's own host. Nothing in
69
+ * this API resizes it. This helper routes it through `wsrv.nl`, a free public
70
+ * image CDN, which fetches the origin image and hands back a width-limited
71
+ * WebP.
72
+ *
73
+ * The trade is explicit and it is not the SDK's to make silently:
74
+ *
75
+ * - the ORIGIN URL is sent to a third party in a query string, so wsrv.nl
76
+ * learns which article your user is looking at, and so does anyone reading
77
+ * the request. There is no credential involved - the images are public - but
78
+ * it is still a referrer-shaped leak;
79
+ * - availability is theirs, not ours. A wsrv.nl outage is a page of broken
80
+ * images, and there is no fallback in the URL;
81
+ * - `&we` asks it not to enlarge images smaller than `width`.
82
+ *
83
+ * If neither trade suits you, use {@link IntelArticle.image_url} directly and
84
+ * size it in CSS.
85
+ */
86
+ export declare function intelArticleImageUrl(imageUrl: string | null | undefined, options?: IntelImageOptions): string;
@@ -0,0 +1,93 @@
1
+ /** The joke table behind the loading screens. */
2
+ import { Resource } from "../../http";
3
+ import type { ListParams } from "../../listing";
4
+ import type { Paginated, RequestOptions, Timestamp } from "../../types";
5
+ /** `GET /jokes` filters on {@link BASE_FILTER_COLUMNS} only. */
6
+ export declare const JOKE_FILTER_COLUMNS: readonly [];
7
+ /** Filters for {@link JokesNamespace.list}. */
8
+ export type ListJokesParams = ListParams<(typeof JOKE_FILTER_COLUMNS)[number]>;
9
+ /** Primary key of a joke. An INTEGER. */
10
+ export type JokeId = number;
11
+ /**
12
+ * A joke.
13
+ *
14
+ * Unlike the blog records this one really does carry all three base fields.
15
+ */
16
+ export interface Joke {
17
+ readonly id: JokeId;
18
+ /**
19
+ * Language tag, as whoever typed it wrote it. Free text with a presence
20
+ * validation and NOTHING else - no inclusion list, no normalisation - so the
21
+ * table can and does hold `"pt"` next to `"PT"` next to `"pt-PT"`. Compare
22
+ * case-insensitively, and see {@link JokesNamespace.list} for why you cannot
23
+ * make the server do the filtering.
24
+ */
25
+ readonly lang: string;
26
+ /**
27
+ * The joke. A `varchar` with no database limit and no model validation; the
28
+ * web composer caps input at 255 characters as a house rule, which nothing
29
+ * server-side enforces.
30
+ */
31
+ readonly content: string;
32
+ readonly created_at: Timestamp;
33
+ readonly updated_at: Timestamp;
34
+ }
35
+ /** Arguments for {@link JokesNamespace.create}. Both fields are required by the model. */
36
+ export interface JokeInput {
37
+ readonly lang: string;
38
+ readonly content: string;
39
+ }
40
+ /**
41
+ * The `jokes` namespace: the joke table behind the site's loading screens.
42
+ *
43
+ * Reading is fully public; writing is admin-only. `Joke.viewable_by` is `all`,
44
+ * so every joke is visible to every caller including anonymous ones, and
45
+ * `creatable_by?`/`updatable_by?`/`destroyable_by?` all reduce to
46
+ * `user.admin?`.
47
+ */
48
+ export declare class JokesNamespace extends Resource {
49
+ /**
50
+ * `GET /jokes` - the joke table, paged. Anonymous callers welcome.
51
+ *
52
+ * **You cannot filter by language.** The allowlist is only the three
53
+ * defaults - `id`, `created_at`, `updated_at` - and `lang` is not on it.
54
+ * `search: { lang: "pt" }`
55
+ * is `400 "Unknown search filter: lang"`, not a wider result: the DSL fails
56
+ * closed. Pull a page and filter client-side, which is what every caller
57
+ * ends up doing.
58
+ *
59
+ * For "give me a joke", `random: true` with `pageSize: 1` is the whole
60
+ * recipe: the server applies `ORDER BY RANDOM()` and the pagination is
61
+ * applied after it. Note that a random listing carries no `ETag` and can
62
+ * never answer `304`, which is exactly what you want here and exactly what
63
+ * you do not want on a normal page.
64
+ *
65
+ * `modifiers[order]` also accepts a third segment for an explicit value
66
+ * ordering (`"lang:asc:pt,en"` puts those languages first), which the rest
67
+ * of the SDK does not advertise because almost nothing needs it.
68
+ */
69
+ list(params?: ListJokesParams, options?: RequestOptions): Promise<Paginated<Joke>>;
70
+ /**
71
+ * `POST /jokes` - adds a joke. `201`. **Admin only.**
72
+ *
73
+ * A signed-in non-admin gets `401 "You are not authorized to create this
74
+ * resource"`; an anonymous caller gets `401 "Session required to access
75
+ * this resource."` from the authentication filter first.
76
+ */
77
+ create(input: JokeInput, options?: RequestOptions): Promise<Joke>;
78
+ /**
79
+ * `PATCH /jokes/:id` - edits a joke. **Admin only.**
80
+ *
81
+ * Both fields are permitted and both are optional; the model requires each
82
+ * to be present, so sending `content: ""` is `400`, not a clear.
83
+ */
84
+ update(id: JokeId, input: Partial<JokeInput>, options?: RequestOptions): Promise<Joke>;
85
+ /**
86
+ * `DELETE /jokes/:id` - removes a joke. `204`. **Admin only.**
87
+ *
88
+ * There is no `GET /jokes/:id`: the resource is declared
89
+ * `only: [:create, :index, :update, :destroy]`, so a single fetch by id is a
90
+ * routing 404.
91
+ */
92
+ destroy(id: JokeId, options?: RequestOptions): Promise<void>;
93
+ }
@@ -0,0 +1,147 @@
1
+ /** The per-user notification inbox. */
2
+ import { Resource } from "../../http";
3
+ import type { ListParams } from "../../listing";
4
+ import type { Id, Paginated, RequestOptions, Timestamp } from "../../types";
5
+ /** Filter columns of `GET /notifications`, on top of {@link BASE_FILTER_COLUMNS}. `read` and `kind` are not filterable. */
6
+ export declare const NOTIFICATION_FILTER_COLUMNS: readonly ["user_id"];
7
+ /** Filters for {@link NotificationsNamespace.list}. */
8
+ export type ListNotificationsParams = ListParams<(typeof NOTIFICATION_FILTER_COLUMNS)[number]>;
9
+ /** Primary key of a notification. An INTEGER. */
10
+ export type NotificationId = number;
11
+ /**
12
+ * The `kind` strings the backend emits today.
13
+ *
14
+ * NOT a closed set and not validated anywhere - `Notification` only requires
15
+ * `kind` to be present, so a new feature can add one without a migration. The
16
+ * union is here so the kinds you handle autocomplete; keep a default branch
17
+ * for the ones you do not, and never let an unknown kind break the inbox.
18
+ *
19
+ * Each kind implies a different {@link Notification.context} shape, which is
20
+ * why `context` is typed as an open record rather than a discriminated union:
21
+ * the backend guarantees a JSON object and nothing about its keys.
22
+ */
23
+ export type NotificationKind = "friendship_request" | "friendship_accepted" | "user_followed" | "message_received" | "fs_grant_received" | "jam_invite" | "vocal_separation_done" | "vocal_separation_failed" | (string & {});
24
+ /**
25
+ * One notification in a user's inbox.
26
+ *
27
+ * Unlike most of this file it IS a full record, so it carries `id`,
28
+ * `created_at` and `updated_at`. The `:extended` view adds
29
+ * nothing, so a notification arriving over the cable and one arriving over
30
+ * HTTP have the same fields.
31
+ */
32
+ export interface Notification {
33
+ /** An integer, and a JSON number, not a string. */
34
+ readonly id: NotificationId;
35
+ /** What happened. See {@link NotificationKind}. */
36
+ readonly kind: NotificationKind;
37
+ /**
38
+ * Free-form JSON payload, whose keys depend entirely on `kind` - these are
39
+ * the i18n interpolation values the client renders the sentence with.
40
+ *
41
+ * Never `null` (the column is `NOT NULL DEFAULT '{}'`), and never large: the
42
+ * emitter runs user-supplied text through a 120-character preview before
43
+ * storing it, so a message-received notification carries a truncated
44
+ * snippet, not the message.
45
+ *
46
+ * One shape is documented outside the code and worth having here:
47
+ * `jam_invite` carries `{ jam_id, host_id, host_handle, inviter_id,
48
+ * inviter_handle }`. The rest you learn by reading a row.
49
+ */
50
+ readonly context: Record<string, unknown>;
51
+ /** Whether it has been marked read. See {@link NotificationsNamespace.markAllRead}. */
52
+ readonly read: boolean;
53
+ /** Owner. Always the caller: the scope is `user.notifications`. A STRING. */
54
+ readonly user_id: Id;
55
+ readonly created_at: Timestamp;
56
+ readonly updated_at: Timestamp;
57
+ }
58
+ /**
59
+ * The `notifications` namespace: the per-user inbox.
60
+ *
61
+ * ## This covers the HTTP half only
62
+ *
63
+ * Notifications are pushed in real time over ActionCable, on the per-user
64
+ * `NotificationsChannel` stream, and that is where a live inbox gets its
65
+ * updates from: the channel transmits `{type: "unread_count", unread_count}`
66
+ * on subscribe, `{type: "created", notification, unread_count}` when one
67
+ * arrives, and `{type: "unread_count", unread_count}` again whenever the read
68
+ * state or the row count changes. The SDK does not open that socket and does
69
+ * not wrap it - it has no cable client - so these methods are the polling
70
+ * fallback and the write path, not the way to keep a badge live. A host with a
71
+ * socket should subscribe and use {@link unreadCount} only for the first
72
+ * paint.
73
+ *
74
+ * ## You cannot mark ONE notification read
75
+ *
76
+ * `PATCH /notifications/:id` is routed, and it cannot succeed for anybody: it
77
+ * answers `401 "You are not authorized to update this resource"` on every
78
+ * call - for the owner, for an admin, for everyone. The SDK therefore exposes
79
+ * no `markRead(id)`: there is nothing honest to put behind it. Mark the whole
80
+ * inbox with {@link markAllRead}, or remove the row with {@link dismiss}.
81
+ *
82
+ * There is also no `GET /notifications/:id`: the resource is declared
83
+ * `only: [:index, :update, :destroy]`, so a single fetch by id is a routing
84
+ * 404. Read one out of {@link list}.
85
+ *
86
+ * Everything here needs a session and rides the general 600/min ceiling.
87
+ */
88
+ export declare class NotificationsNamespace extends Resource {
89
+ /**
90
+ * `GET /notifications` - the caller's inbox, one page at a time.
91
+ *
92
+ * Scoped to the caller by `viewable_by` (`user.notifications`), so there is
93
+ * no way to read anybody else's and the `user_id` filter below is redundant.
94
+ *
95
+ * **The filterable columns are almost none.** The controller declares
96
+ * `search_params :user_id`, which the DSL merges with the three defaults, so
97
+ * the complete allowlist is `id`, `created_at`, `updated_at` and `user_id`.
98
+ * `read` and `kind` are NOT on it, and filters fail closed: asking for
99
+ * `exact_search: { read: false }` - the obvious way to fetch the unread ones -
100
+ * is `400 "Unknown exact_search filter: read"`, not an unfiltered list.
101
+ * Fetch a page and filter client-side, or read {@link unreadCount} for the
102
+ * badge.
103
+ *
104
+ * No default ordering is declared, so rows come back in whatever order
105
+ * Postgres chooses. Pass `order: "created_at:desc"` for an inbox; there is
106
+ * an index on `(user_id, created_at)` behind it.
107
+ *
108
+ * Sends an `ETag`, so an unchanged page answers `304` and costs nothing -
109
+ * except with `random: true`, which disables the check.
110
+ */
111
+ list(params?: ListNotificationsParams, options?: RequestOptions): Promise<Paginated<Notification>>;
112
+ /**
113
+ * `GET /notifications/unread_count` - how many unread notifications the
114
+ * caller has. Unwraps the `{"count": n}` the server sends.
115
+ *
116
+ * One indexed `COUNT` behind a partial index (`WHERE read = false`), so it is
117
+ * cheap - but it is still a request per call, and the cable already pushes
118
+ * this number on subscribe and on every change. Poll it only where there is
119
+ * no socket.
120
+ */
121
+ unreadCount(options?: RequestOptions): Promise<number>;
122
+ /**
123
+ * `POST /notifications/read_all` - marks every unread notification read.
124
+ *
125
+ * Returns how many rows changed, which is the unread count from an instant
126
+ * ago; calling it twice returns `0` the second time. `200`, not `201` - it
127
+ * creates nothing.
128
+ *
129
+ * Runs as a single `update_all`, so no model callback fires and the per-row
130
+ * broadcast is skipped; the controller pushes the new count over the cable
131
+ * by hand afterwards, which is why every device still updates.
132
+ *
133
+ * Idempotent, so a retry is harmless. It is not enabled by default (the
134
+ * transport does not replay a `POST`); pass `retry: {}` if you want one.
135
+ */
136
+ markAllRead(options?: RequestOptions): Promise<number>;
137
+ /**
138
+ * `DELETE /notifications/:id` - removes one notification. `204`, no body.
139
+ *
140
+ * This is the closest thing to "mark as read" the API has: the row is gone,
141
+ * so the unread count drops and the new count is pushed over the cable.
142
+ *
143
+ * Owner only - `viewable_by` scopes the lookup to the caller, so somebody
144
+ * else's id is `404 "Resource not found"` rather than a 401.
145
+ */
146
+ dismiss(id: NotificationId, options?: RequestOptions): Promise<void>;
147
+ }
@@ -0,0 +1,73 @@
1
+ /** Per-user service visit counters. */
2
+ import { Resource } from "../../http";
3
+ import type { RequestOptions } from "../../types";
4
+ /**
5
+ * The twelve service ids the counter accepts. A closed set: anything else is
6
+ * `400 "Unknown service_id"`.
7
+ */
8
+ export declare const SERVICE_USAGE_IDS: readonly ["storage", "tools", "games", "music", "movies", "ai", "account", "tickets", "messages", "blogs", "status", "administration"];
9
+ /** One of {@link SERVICE_USAGE_IDS}. */
10
+ export type ServiceUsageId = (typeof SERVICE_USAGE_IDS)[number];
11
+ /**
12
+ * A per-user visit counter.
13
+ *
14
+ * Note what is NOT here: no `id`, no `user_id`, no timestamps. Both routes
15
+ * answer a bare `{ service_id, count }`, so this is one of the very few
16
+ * payloads in the API that is not a record.
17
+ */
18
+ export interface ServiceUsage {
19
+ readonly service_id: ServiceUsageId;
20
+ /** Lifetime visit count for this user and service. Monotonic, never reset. */
21
+ readonly count: number;
22
+ }
23
+ /**
24
+ * The `service_usages` namespace: which parts of the site a user opens, so the
25
+ * home screen can put their favourites first.
26
+ *
27
+ * Both routes need a session - there is no `allow_unauthenticated_access` on
28
+ * this controller - and both are pure bookkeeping. The music app calls
29
+ * {@link record} with `"music"` on launch, fire and forget.
30
+ */
31
+ export declare class ServiceUsagesNamespace extends Resource {
32
+ /**
33
+ * `POST /service_usages` - increments the caller's counter for one service.
34
+ *
35
+ * Answers **`200`, not `201`**, even on the very first call that creates the
36
+ * row: the controller uses `ok!` rather than `created!`, so this is one of
37
+ * the handful of creates in the API that breaks the 201 convention. The body
38
+ * is the updated `{ service_id, count }`.
39
+ *
40
+ * ## Not idempotent, and it can page somebody
41
+ *
42
+ * Every call does `count += 1` and stamps `last_visited_at`, so a retry
43
+ * inflates the number. That is why the transport's default of never
44
+ * replaying a `POST` is the right default here: do not pass `retry: {}`.
45
+ *
46
+ * It can also fire a Discord `service_opened` alert - on the first ever
47
+ * visit, and again whenever more than an hour has passed since the last one.
48
+ * A client that calls this on every route change inside an app is fine (the
49
+ * hour gap suppresses the alert), but a client that calls it from a
50
+ * background poller is a pager, not telemetry.
51
+ *
52
+ * Fire and forget: nothing in a UI should wait on this, and nothing should
53
+ * fail because it failed.
54
+ *
55
+ * @throws {OmsApiError} 400 `"Unknown service_id"` for anything outside
56
+ * {@link SERVICE_USAGE_IDS}; 401 without a session.
57
+ */
58
+ record(serviceId: ServiceUsageId, options?: RequestOptions): Promise<ServiceUsage>;
59
+ /**
60
+ * `GET /service_usages/top` - the caller's most-used services, busiest
61
+ * first, tie-broken by most recently visited.
62
+ *
63
+ * Not the list DSL: `limit` is the only parameter, it is clamped to
64
+ * `1..10` (silently - asking for 50 returns 10), and it defaults to 3. There
65
+ * is no paging and no way to read the full set.
66
+ *
67
+ * Only services the caller has actually opened appear, so a fresh account
68
+ * gets an empty array rather than every id with a zero.
69
+ */
70
+ top(input?: {
71
+ readonly limit?: number;
72
+ }, options?: RequestOptions): Promise<ServiceUsage[]>;
73
+ }