@remit/api-openapi-spec 0.0.16 → 0.0.18

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 (2) hide show
  1. package/openapi.json +9 -7
  2. package/package.json +1 -1
package/openapi.json CHANGED
@@ -1552,7 +1552,7 @@
1552
1552
  "name": "category",
1553
1553
  "in": "query",
1554
1554
  "required": false,
1555
- "description": "Filter to messages with one of these header-derived categories. Off-row criterion, resolved by in-handler enrichment over the capped window.",
1555
+ "description": "Filter to messages with one of these header-derived categories. Applied as a SQL predicate over the whole mailbox, so a page comes back full of matches however rare the category is. `uncategorized` is one of them.",
1556
1556
  "schema": {
1557
1557
  "type": "array",
1558
1558
  "items": {
@@ -2521,7 +2521,7 @@
2521
2521
  "/threads": {
2522
2522
  "get": {
2523
2523
  "operationId": "UnifiedThreadOperations_listAllThreads",
2524
- "description": "List threads across all non-muted accounts of the caller, ordered by sentDate desc. Each row carries an attached `accountId` (derived from a mailboxId→accountId map at read time) plus the usual `senderTrust`/`category` enrichment. Served by the `byDate` LSI1 (pk = the caller's account config), so the caller can never see another account config's threads.\n\nINBOX scope: results are filtered to each account's top-level INBOX, matched by exact path `fullPath === \"INBOX\"` (RFC 6154 defines no Inbox special-use). By design this excludes namespaced sub-paths like `INBOX/Receipts` and non-English server primaries — the unified inbox is each account's primary INBOX, not its sub-folders. Soft-deleted rows are also excluded.\n\nMute exclusion: muted accounts and muted mailboxes are excluded. The inbox/mute filter set is rebuilt per page from live state, so muting/unmuting a mailbox mid-pagination takes effect on the next page (never retroactively).\n\nPagination: pass the returned `continuationToken` to fetch the next page; keep paging until it is absent. In the default (INBOX) mode the INBOX/deleted filter is applied after the index read with a bounded read window per request, so a page may return FEWER than `limit` rows while still carrying a `continuationToken` — treat the presence of the token (not a full page) as the \"more results\" signal. No rows are skipped or duplicated across pages.\n\nStarred mode (`starred=true`): served by the `byStarred` LSI4 instead. A star is a marker on the mail rather than on its placement, so the INBOX scope does not apply and a starred thread in Archive is still returned. Excluded are muted and soft-deleted rows as usual, plus the folders a star never surfaces from: Junk, Trash, and Gmail's All Mail (a second copy of everything, whose rows would otherwise double every starred message). All filtering happens in the query, so a page is short only when the results are exhausted. Rows are per mailbox: the same mail filed in two folders is two rows sharing a `threadId`, and a client rendering one row per conversation collapses by `threadId` across the pages it has accumulated.\n\nSearch mode (`query=<text>`): the listing widens past the INBOX scope to every non-muted mailbox of every non-muted account, and returns only rows whose subject or From address matches the text. This is the unscoped search of the daily brief: one query reaching every folder of every account. Whitespace splits the text into terms and every term must match (AND), each against subject OR From. Matching is case-insensitive substring, not tokenized full-text, so it neither stems nor ranks.\n\nSearch scope: every non-muted mailbox EXCEPT Trash. The scope is defined by what it excludes, so anything a server offers that is not on that list is searched — Spam, Drafts and Gmail's virtual folders (All Mail, Starred, Important) included. Spam is deliberately in scope: the point of an unscoped search is that it reaches the folders the user did not think to look in. Combining `query` with `starred=true` searches the starred scope instead.\n\nDuplicates: a virtual folder holds a second copy of mail that also lives in a real folder, so a backend keying a row by its mailbox returns the same message several times — a starred, Important Gmail inbox message four times. The extras are dropped from the response by message identity, keeping the copy in a real folder, so one message is one row. This is done after the read rather than by barring those folders from the scope, because a message whose only stored row sits in one of them would otherwise become unfindable. De-duplication is per page: a duplicate split across a page boundary survives, the same caveat that applies to collapsing by `threadId`. A page may therefore return fewer rows than `limit` while still carrying a `continuationToken`.\n\nSearch mode and pagination: `limit` is a page size over MATCHES, capped server-side at 500. A page may be shorter than `limit` for two reasons — the matches ran out, or duplicates were dropped from it — so treat the presence of a `continuationToken` as the \"more results\" signal rather than a full page. There is no total match count; page until the token is absent.\n\nWhat a row is in search mode: one per message, not one per mailbox. This differs from the starred mode above, where the same mail filed in two folders is two rows sharing a `threadId`. Collapsing by `threadId` is still a client's job if it renders one row per CONVERSATION, since distinct messages in one thread remain distinct rows here.\n\nCallers that omit `query` are unaffected: the INBOX (and starred) listings behave exactly as before.",
2524
+ "description": "List threads across all non-muted accounts of the caller, ordered by sentDate desc. Each row carries an attached `accountId` (derived from a mailboxId→accountId map at read time) plus the usual `senderTrust` enrichment; `category` is served from the ThreadMessage row. Served by the `byDate` LSI1 (pk = the caller's account config), so the caller can never see another account config's threads.\n\nINBOX scope: results are filtered to each account's top-level INBOX, matched by exact path `fullPath === \"INBOX\"` (RFC 6154 defines no Inbox special-use). By design this excludes namespaced sub-paths like `INBOX/Receipts` and non-English server primaries — the unified inbox is each account's primary INBOX, not its sub-folders. Soft-deleted rows are also excluded.\n\nMute exclusion: muted accounts and muted mailboxes are excluded. The inbox/mute filter set is rebuilt per page from live state, so muting/unmuting a mailbox mid-pagination takes effect on the next page (never retroactively).\n\nPagination: pass the returned `continuationToken` to fetch the next page; keep paging until it is absent. In the default (INBOX) mode the INBOX/deleted filter is applied after the index read with a bounded read window per request, so a page may return FEWER than `limit` rows while still carrying a `continuationToken` — treat the presence of the token (not a full page) as the \"more results\" signal. No rows are skipped or duplicated across pages.\n\nStarred mode (`starred=true`): served by the `byStarred` LSI4 instead. A star is a marker on the mail rather than on its placement, so the INBOX scope does not apply and a starred thread in Archive is still returned. Excluded are muted and soft-deleted rows as usual, plus the folders a star never surfaces from: Junk, Trash, and Gmail's All Mail (a second copy of everything, whose rows would otherwise double every starred message). All filtering happens in the query, so a page is short only when the results are exhausted. Rows are per mailbox: the same mail filed in two folders is two rows sharing a `threadId`, and a client rendering one row per conversation collapses by `threadId` across the pages it has accumulated.\n\nSearch mode (`query=<text>`): the listing widens past the INBOX scope to every non-muted mailbox of every non-muted account, and returns only rows whose subject or From address matches the text. This is the unscoped search of the daily brief: one query reaching every folder of every account. Whitespace splits the text into terms and every term must match (AND), each against subject OR From. Matching is case-insensitive substring, not tokenized full-text, so it neither stems nor ranks.\n\nSearch scope: every non-muted mailbox EXCEPT Trash. The scope is defined by what it excludes, so anything a server offers that is not on that list is searched — Spam, Drafts and Gmail's virtual folders (All Mail, Starred, Important) included. Spam is deliberately in scope: the point of an unscoped search is that it reaches the folders the user did not think to look in. Combining `query` with `starred=true` searches the starred scope instead.\n\nDuplicates: a virtual folder holds a second copy of mail that also lives in a real folder, so a backend keying a row by its mailbox returns the same message several times — a starred, Important Gmail inbox message four times. The extras are dropped from the response by message identity, keeping the copy in a real folder, so one message is one row. This is done after the read rather than by barring those folders from the scope, because a message whose only stored row sits in one of them would otherwise become unfindable. De-duplication is per page: a duplicate split across a page boundary survives, the same caveat that applies to collapsing by `threadId`. A page may therefore return fewer rows than `limit` while still carrying a `continuationToken`.\n\nSearch mode and pagination: `limit` is a page size over MATCHES, capped server-side at 500. A page may be shorter than `limit` for two reasons — the matches ran out, or duplicates were dropped from it — so treat the presence of a `continuationToken` as the \"more results\" signal rather than a full page. There is no total match count; page until the token is absent.\n\nWhat a row is in search mode: one per message, not one per mailbox. This differs from the starred mode above, where the same mail filed in two folders is two rows sharing a `threadId`. Collapsing by `threadId` is still a client's job if it renders one row per CONVERSATION, since distinct messages in one thread remain distinct rows here.\n\nCallers that omit `query` are unaffected: the INBOX (and starred) listings behave exactly as before.",
2525
2525
  "parameters": [
2526
2526
  {
2527
2527
  "name": "continuationToken",
@@ -3487,7 +3487,7 @@
3487
3487
  "$ref": "#/components/schemas/RemitImap.AddressFlagBase"
3488
3488
  }
3489
3489
  ],
3490
- "description": "Sender is blocked: never load images, even on explicit click"
3490
+ "description": "Sender is blocked: mail is placed in Junk, and images never load, even on explicit click"
3491
3491
  },
3492
3492
  "RemitImap.BlockedFlagUpdate": {
3493
3493
  "type": "object",
@@ -3501,7 +3501,7 @@
3501
3501
  "$ref": "#/components/schemas/RemitImap.AddressFlagBaseUpdate"
3502
3502
  }
3503
3503
  ],
3504
- "description": "Sender is blocked: never load images, even on explicit click"
3504
+ "description": "Sender is blocked: mail is placed in Junk, and images never load, even on explicit click"
3505
3505
  },
3506
3506
  "RemitImap.BodyPartResponse": {
3507
3507
  "type": "object",
@@ -4243,6 +4243,7 @@
4243
4243
  "cc",
4244
4244
  "bcc",
4245
4245
  "replyTo",
4246
+ "category",
4246
4247
  "senderTrust"
4247
4248
  ],
4248
4249
  "properties": {
@@ -4308,7 +4309,7 @@
4308
4309
  "$ref": "#/components/schemas/RemitImap.MessageCategory"
4309
4310
  }
4310
4311
  ],
4311
- "description": "Header-derived category for the message. Absent for messages synced before classification rolled out."
4312
+ "description": "Header-derived category for the message. Always present: `Message.category` is NOT NULL with an `uncategorized` default, and `uncategorized` names the not-yet-classified state rather than standing for absence (issue #45)."
4312
4313
  },
4313
4314
  "senderTrust": {
4314
4315
  "allOf": [
@@ -6247,6 +6248,7 @@
6247
6248
  "isDeleted",
6248
6249
  "createdAt",
6249
6250
  "updatedAt",
6251
+ "category",
6250
6252
  "senderTrust"
6251
6253
  ],
6252
6254
  "properties": {
@@ -6371,7 +6373,7 @@
6371
6373
  "$ref": "#/components/schemas/RemitImap.MessageCategory"
6372
6374
  }
6373
6375
  ],
6374
- "description": "Header-derived category for the message (from the underlying Message). Absent for messages synced before classification rolled out clients should treat this as `personal`."
6376
+ "description": "Header-derived category for the message, served from the ThreadMessage row. Always present: the column is NOT NULL with an `uncategorized` default, and `uncategorized` is a named value for the not-yet-classified state, never a stand-in for `personal` (issue #45)."
6375
6377
  },
6376
6378
  "senderTrust": {
6377
6379
  "allOf": [
@@ -6413,7 +6415,7 @@
6413
6415
  "description": "Labels applied to this message — filter-, organize-, and manually-applied alike (issue #26). Absent/empty means no label is applied."
6414
6416
  }
6415
6417
  },
6416
- "description": "Thread message item returned by list/search operations. `category` and `senderTrust` are attached at read time via batch-fetch so the inbox row can render badges without a per-row round trip."
6418
+ "description": "Thread message item returned by list/search operations. `senderTrust` is attached at read time via batch-fetch so the inbox row can render its badge without a per-row round trip. `category` is served from the ThreadMessage row itself, which is also the row the category filter matches, so the badge and the filter cannot disagree."
6417
6419
  },
6418
6420
  "RemitImap.ThreadSearchResponse": {
6419
6421
  "type": "object",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remit/api-openapi-spec",
3
- "version": "0.0.16",
3
+ "version": "0.0.18",
4
4
  "description": "Remit API OpenAPI 3 document, generated from TypeSpec.",
5
5
  "exports": {
6
6
  ".": "./openapi.json",