@dxos/plugin-jmap 0.10.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 (180) hide show
  1. package/LICENSE +105 -0
  2. package/dist/lib/JmapPlugin.mjs +16 -0
  3. package/dist/lib/JmapPlugin.mjs.map +1 -0
  4. package/dist/lib/apis.mjs +2 -0
  5. package/dist/lib/capabilities.mjs +17 -0
  6. package/dist/lib/capabilities.mjs.map +1 -0
  7. package/dist/lib/chunk-apis.mjs +756 -0
  8. package/dist/lib/chunk-apis.mjs.map +1 -0
  9. package/dist/lib/chunk-connector.mjs +111 -0
  10. package/dist/lib/chunk-connector.mjs.map +1 -0
  11. package/dist/lib/chunk-constants.mjs +23 -0
  12. package/dist/lib/chunk-constants.mjs.map +1 -0
  13. package/dist/lib/chunk-errors.mjs +42 -0
  14. package/dist/lib/chunk-errors.mjs.map +1 -0
  15. package/dist/lib/chunk-handler.mjs +27 -0
  16. package/dist/lib/chunk-handler.mjs.map +1 -0
  17. package/dist/lib/chunk-jmap-fixtures.mjs +102 -0
  18. package/dist/lib/chunk-jmap-fixtures.mjs.map +1 -0
  19. package/dist/lib/chunk-mail-send.mjs +17 -0
  20. package/dist/lib/chunk-mail-send.mjs.map +1 -0
  21. package/dist/lib/chunk-meta.mjs +37 -0
  22. package/dist/lib/chunk-meta.mjs.map +1 -0
  23. package/dist/lib/chunk-operation-handler.mjs +12 -0
  24. package/dist/lib/chunk-operation-handler.mjs.map +1 -0
  25. package/dist/lib/chunk-send.mjs +98 -0
  26. package/dist/lib/chunk-send.mjs.map +1 -0
  27. package/dist/lib/chunk-sync-provider.mjs +511 -0
  28. package/dist/lib/chunk-sync-provider.mjs.map +1 -0
  29. package/dist/lib/chunk-sync.mjs +37 -0
  30. package/dist/lib/chunk-sync.mjs.map +1 -0
  31. package/dist/lib/index.mjs +4 -0
  32. package/dist/lib/meta.mjs +2 -0
  33. package/dist/lib/operations.mjs +13 -0
  34. package/dist/lib/operations.mjs.map +1 -0
  35. package/dist/lib/plugin.mjs +9 -0
  36. package/dist/lib/plugin.mjs.map +1 -0
  37. package/dist/lib/services.mjs +139 -0
  38. package/dist/lib/services.mjs.map +1 -0
  39. package/dist/lib/testing/node.mjs +2 -0
  40. package/dist/lib/testing.mjs +19 -0
  41. package/dist/lib/testing.mjs.map +1 -0
  42. package/dist/lib/translations.mjs +7 -0
  43. package/dist/lib/translations.mjs.map +1 -0
  44. package/dist/types/dx.config.d.ts +28 -0
  45. package/dist/types/dx.config.d.ts.map +1 -0
  46. package/dist/types/src/JmapPlugin.d.ts +9 -0
  47. package/dist/types/src/JmapPlugin.d.ts.map +1 -0
  48. package/dist/types/src/apis/Jmap/api.d.ts +42 -0
  49. package/dist/types/src/apis/Jmap/api.d.ts.map +1 -0
  50. package/dist/types/src/apis/Jmap/index.d.ts +3 -0
  51. package/dist/types/src/apis/Jmap/index.d.ts.map +1 -0
  52. package/dist/types/src/apis/Jmap/types.d.ts +65 -0
  53. package/dist/types/src/apis/Jmap/types.d.ts.map +1 -0
  54. package/dist/types/src/apis/JmapMail/api.d.ts +223 -0
  55. package/dist/types/src/apis/JmapMail/api.d.ts.map +1 -0
  56. package/dist/types/src/apis/JmapMail/index.d.ts +4 -0
  57. package/dist/types/src/apis/JmapMail/index.d.ts.map +1 -0
  58. package/dist/types/src/apis/JmapMail/query.d.ts +26 -0
  59. package/dist/types/src/apis/JmapMail/query.d.ts.map +1 -0
  60. package/dist/types/src/apis/JmapMail/query.test.d.ts +2 -0
  61. package/dist/types/src/apis/JmapMail/query.test.d.ts.map +1 -0
  62. package/dist/types/src/apis/JmapMail/types.d.ts +290 -0
  63. package/dist/types/src/apis/JmapMail/types.d.ts.map +1 -0
  64. package/dist/types/src/apis/index.d.ts +3 -0
  65. package/dist/types/src/apis/index.d.ts.map +1 -0
  66. package/dist/types/src/apis/jmap-api.test.d.ts +2 -0
  67. package/dist/types/src/apis/jmap-api.test.d.ts.map +1 -0
  68. package/dist/types/src/capabilities/connector.d.ts +5 -0
  69. package/dist/types/src/capabilities/connector.d.ts.map +1 -0
  70. package/dist/types/src/capabilities/credential-form.d.ts +37 -0
  71. package/dist/types/src/capabilities/credential-form.d.ts.map +1 -0
  72. package/dist/types/src/capabilities/credential-form.test.d.ts +2 -0
  73. package/dist/types/src/capabilities/credential-form.test.d.ts.map +1 -0
  74. package/dist/types/src/capabilities/index.d.ts +5 -0
  75. package/dist/types/src/capabilities/index.d.ts.map +1 -0
  76. package/dist/types/src/capabilities/mail-send.d.ts +6 -0
  77. package/dist/types/src/capabilities/mail-send.d.ts.map +1 -0
  78. package/dist/types/src/capabilities/operation-handler.d.ts +5 -0
  79. package/dist/types/src/capabilities/operation-handler.d.ts.map +1 -0
  80. package/dist/types/src/constants.d.ts +19 -0
  81. package/dist/types/src/constants.d.ts.map +1 -0
  82. package/dist/types/src/errors.d.ts +103 -0
  83. package/dist/types/src/errors.d.ts.map +1 -0
  84. package/dist/types/src/index.d.ts +4 -0
  85. package/dist/types/src/index.d.ts.map +1 -0
  86. package/dist/types/src/meta.d.ts +33 -0
  87. package/dist/types/src/meta.d.ts.map +1 -0
  88. package/dist/types/src/operations/index.d.ts +3 -0
  89. package/dist/types/src/operations/index.d.ts.map +1 -0
  90. package/dist/types/src/operations/mail/mapper.d.ts +49 -0
  91. package/dist/types/src/operations/mail/mapper.d.ts.map +1 -0
  92. package/dist/types/src/operations/mail/materialize/handler.d.ts +11 -0
  93. package/dist/types/src/operations/mail/materialize/handler.d.ts.map +1 -0
  94. package/dist/types/src/operations/mail/send/handler.d.ts +4 -0
  95. package/dist/types/src/operations/mail/send/handler.d.ts.map +1 -0
  96. package/dist/types/src/operations/mail/send/index.d.ts +2 -0
  97. package/dist/types/src/operations/mail/send/index.d.ts.map +1 -0
  98. package/dist/types/src/operations/mail/sync/handler.d.ts +4 -0
  99. package/dist/types/src/operations/mail/sync/handler.d.ts.map +1 -0
  100. package/dist/types/src/operations/mail/sync/index.d.ts +3 -0
  101. package/dist/types/src/operations/mail/sync/index.d.ts.map +1 -0
  102. package/dist/types/src/operations/mail/sync/mapper.test.d.ts +2 -0
  103. package/dist/types/src/operations/mail/sync/mapper.test.d.ts.map +1 -0
  104. package/dist/types/src/operations/mail/sync/sync-e2e.test.d.ts +2 -0
  105. package/dist/types/src/operations/mail/sync/sync-e2e.test.d.ts.map +1 -0
  106. package/dist/types/src/operations/mail/sync/sync-provider.d.ts +11 -0
  107. package/dist/types/src/operations/mail/sync/sync-provider.d.ts.map +1 -0
  108. package/dist/types/src/operations/mail/sync/sync.test.d.ts +2 -0
  109. package/dist/types/src/operations/mail/sync/sync.test.d.ts.map +1 -0
  110. package/dist/types/src/operations/mail/sync/system-tags.d.ts +14 -0
  111. package/dist/types/src/operations/mail/sync/system-tags.d.ts.map +1 -0
  112. package/dist/types/src/operations/mail/tags.d.ts +12 -0
  113. package/dist/types/src/operations/mail/tags.d.ts.map +1 -0
  114. package/dist/types/src/plugin.d.ts +4 -0
  115. package/dist/types/src/plugin.d.ts.map +1 -0
  116. package/dist/types/src/services/index.d.ts +3 -0
  117. package/dist/types/src/services/index.d.ts.map +1 -0
  118. package/dist/types/src/services/jmap-credentials.d.ts +39 -0
  119. package/dist/types/src/services/jmap-credentials.d.ts.map +1 -0
  120. package/dist/types/src/services/jmap-mail-api.d.ts +109 -0
  121. package/dist/types/src/services/jmap-mail-api.d.ts.map +1 -0
  122. package/dist/types/src/testing/index.d.ts +5 -0
  123. package/dist/types/src/testing/index.d.ts.map +1 -0
  124. package/dist/types/src/testing/jmap-fixtures.d.ts +26 -0
  125. package/dist/types/src/testing/jmap-fixtures.d.ts.map +1 -0
  126. package/dist/types/src/testing/jmap-fixtures.test.d.ts +2 -0
  127. package/dist/types/src/testing/jmap-fixtures.test.d.ts.map +1 -0
  128. package/dist/types/src/testing/node.d.ts +4 -0
  129. package/dist/types/src/testing/node.d.ts.map +1 -0
  130. package/dist/types/src/testing/sync-fixture.d.ts +22 -0
  131. package/dist/types/src/testing/sync-fixture.d.ts.map +1 -0
  132. package/dist/types/src/translations.d.ts +8 -0
  133. package/dist/types/src/translations.d.ts.map +1 -0
  134. package/dist/types/tsconfig.tsbuildinfo +1 -0
  135. package/dx.config.ts +32 -0
  136. package/package.json +121 -0
  137. package/src/JmapPlugin.ts +25 -0
  138. package/src/apis/Jmap/api.ts +138 -0
  139. package/src/apis/Jmap/index.ts +6 -0
  140. package/src/apis/Jmap/types.ts +86 -0
  141. package/src/apis/JmapMail/api.ts +324 -0
  142. package/src/apis/JmapMail/index.ts +7 -0
  143. package/src/apis/JmapMail/query.test.ts +181 -0
  144. package/src/apis/JmapMail/query.ts +231 -0
  145. package/src/apis/JmapMail/types.ts +196 -0
  146. package/src/apis/index.ts +8 -0
  147. package/src/apis/jmap-api.test.ts +261 -0
  148. package/src/capabilities/connector.ts +44 -0
  149. package/src/capabilities/credential-form.test.ts +69 -0
  150. package/src/capabilities/credential-form.ts +113 -0
  151. package/src/capabilities/index.ts +24 -0
  152. package/src/capabilities/mail-send.ts +21 -0
  153. package/src/capabilities/operation-handler.ts +16 -0
  154. package/src/constants.ts +24 -0
  155. package/src/errors.ts +38 -0
  156. package/src/index.ts +7 -0
  157. package/src/meta.ts +9 -0
  158. package/src/operations/index.ts +13 -0
  159. package/src/operations/mail/mapper.ts +191 -0
  160. package/src/operations/mail/materialize/handler.ts +41 -0
  161. package/src/operations/mail/send/handler.ts +116 -0
  162. package/src/operations/mail/send/index.ts +5 -0
  163. package/src/operations/mail/sync/handler.ts +52 -0
  164. package/src/operations/mail/sync/index.ts +7 -0
  165. package/src/operations/mail/sync/mapper.test.ts +150 -0
  166. package/src/operations/mail/sync/sync-e2e.test.ts +63 -0
  167. package/src/operations/mail/sync/sync-provider.ts +481 -0
  168. package/src/operations/mail/sync/sync.test.ts +766 -0
  169. package/src/operations/mail/sync/system-tags.ts +24 -0
  170. package/src/operations/mail/tags.ts +29 -0
  171. package/src/plugin.ts +11 -0
  172. package/src/services/index.ts +6 -0
  173. package/src/services/jmap-credentials.ts +61 -0
  174. package/src/services/jmap-mail-api.ts +294 -0
  175. package/src/testing/index.ts +8 -0
  176. package/src/testing/jmap-fixtures.test.ts +107 -0
  177. package/src/testing/jmap-fixtures.ts +105 -0
  178. package/src/testing/node.ts +12 -0
  179. package/src/testing/sync-fixture.ts +39 -0
  180. package/src/translations.ts +15 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chunk-sync-provider.mjs","names":[],"sources":["../../src/operations/mail/mapper.ts","../../src/operations/mail/tags.ts","../../src/operations/mail/sync/system-tags.ts","../../src/operations/mail/sync/sync-provider.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\nimport * as Effect from 'effect/Effect';\n\nimport { Obj, Ref } from '@dxos/echo';\nimport { type Resolver, resolve } from '@dxos/extractor';\nimport { log } from '@dxos/log';\nimport { ContentBlock, Message, Person } from '@dxos/types';\n\nimport { JmapMail } from '../../apis';\nimport { JMAP_DOMAIN } from '../../constants';\n\n/**\n * Result of mapping a JMAP email. `mailboxIds` (the folders the email is in) and `keywords` (the set\n * flags, e.g. `$flagged`) are propagated separately so the caller can apply the corresponding tags to\n * the immutable feed Message — mirrors the Gmail mapper's `labelIds`. Both axes are needed because JMAP\n * splits folder membership (`mailboxIds`) from flags (`keywords`), unlike Gmail's single label list.\n */\nexport type MappedEmail = {\n message: Message.Message;\n mailboxIds: readonly string[];\n keywords: readonly string[];\n};\n\n/** A JMAP email with its raw HTML and/or plaintext body (JMAP delivers decoded values; no markdown). */\nexport type DecodedEmail = {\n raw: JmapMail.Email;\n html?: string;\n plain?: string;\n attachments: readonly AttachmentMetadata[];\n};\n\n/** Metadata for an attachment part (`Email.attachments`, distinct from the `textBody`/`htmlBody` parts). */\nexport type AttachmentMetadata = {\n readonly blobId: string;\n readonly name?: string;\n readonly mimeType?: string;\n readonly size?: number;\n /** The part's `Content-ID` (angle brackets stripped, if present), if any — matches a `cid:` reference in an HTML body. */\n readonly contentId?: string;\n};\n\n/** Attachment parts carry a `blobId` to fetch bytes for; parts without one can't be downloaded. */\nconst collectAttachments = (attachments: readonly JmapMail.EmailBodyPart[] | undefined): AttachmentMetadata[] =>\n (attachments ?? []).flatMap((part) =>\n part.blobId\n ? [\n {\n blobId: part.blobId,\n name: part.name ?? undefined,\n mimeType: part.type,\n size: part.size,\n // Servers vary on whether `cid` includes the enclosing `<...>`; strip it defensively so it\n // matches the bare id an HTML body's `cid:` reference uses.\n contentId: part.cid ? part.cid.replace(/^<|>$/g, '') : undefined,\n },\n ]\n : [],\n );\n\n/**\n * Extracts the raw HTML and plaintext bodies from a JMAP email (values fetched via\n * `fetchHTMLBodyValues`/`fetchTextBodyValues`). Returns `null` when the email has neither. No\n * HTML→markdown conversion — the markdown/plain views derive from these raw blocks in the component.\n */\nexport const decodeBody = (email: JmapMail.Email): DecodedEmail | null => {\n const html = getBodyValue(email, email.htmlBody);\n const plain = getBodyValue(email, email.textBody);\n if (html === undefined && plain === undefined) {\n log('jmap decodeBody: dropping email with no extractable body', {\n id: email.id,\n htmlBodyParts: email.htmlBody?.length ?? 0,\n textBodyParts: email.textBody?.length ?? 0,\n bodyValues: email.bodyValues ? Object.keys(email.bodyValues).length : 0,\n });\n return null;\n }\n return { raw: email, html, plain, attachments: collectAttachments(email.attachments) };\n};\n\n/**\n * Builds an ECHO Message from a decoded (already body-normalized) JMAP email and an optional resolved\n * sender contact. Pure — contact resolution is done by the caller. Returns `null` when the email has\n * no sender.\n */\nexport const mapToMessage = (decoded: DecodedEmail, contact: Person.Person | undefined): MappedEmail | null => {\n const { raw: email, html, plain } = decoded;\n const fromAddress = email.from?.[0];\n if (!fromAddress) {\n log('jmap mapToMessage: dropping email with no sender', { id: email.id });\n return null;\n }\n\n // Omit `contact` entirely when unresolved: an explicit `undefined` round-trips to `null` and\n // fails Message schema validation on queue load (see mapper.test.ts).\n const sender = {\n email: fromAddress.email,\n ...(fromAddress.name ? { name: fromAddress.name } : {}),\n ...(contact ? { contact: Ref.make(contact) } : {}),\n };\n\n // Store the raw HTML and plaintext bodies as separately-typed blocks; the markdown view derives\n // from the HTML in-component.\n const blocks: ContentBlock.Text[] = [];\n if (html !== undefined) {\n blocks.push({ _tag: 'text', text: html, mimeType: 'text/html' });\n }\n if (plain !== undefined) {\n blocks.push({ _tag: 'text', text: plain, mimeType: 'text/plain' });\n }\n\n const echoMessage = Obj.make(Message.Message, {\n [Obj.Meta]: {\n keys: [{ id: email.id, source: JMAP_DOMAIN }],\n },\n\n created: email.receivedAt,\n sender,\n threadId: email.threadId,\n\n properties: {\n threadId: email.threadId,\n // `snippet` is the field inbox UI renders for previews; JMAP's `preview` is its analogue.\n snippet: email.preview ?? undefined,\n subject: email.subject ?? undefined,\n messageId: email.messageId?.[0],\n inReplyTo: email.inReplyTo?.[0],\n references: email.references?.join(' '),\n to: formatAddresses(email.to),\n cc: formatAddresses(email.cc),\n },\n\n blocks,\n });\n\n // `keywords` is a `{ [keyword]: true }` set; keep only the keys currently set.\n const keywords = email.keywords\n ? Object.entries(email.keywords)\n .filter(([, set]) => set)\n .map(([keyword]) => keyword)\n : [];\n\n return {\n message: echoMessage,\n mailboxIds: email.mailboxIds ? Object.keys(email.mailboxIds) : [],\n keywords,\n };\n};\n\n/**\n * Maps a JMAP email to an ECHO Message, resolving the sender against existing contacts. Composes\n * {@link decodeBody} → {@link mapToMessage}. Returns `null` when the email has no body or no sender.\n */\nexport const mapEmail: (email: JmapMail.Email) => Effect.Effect<MappedEmail | null, never, Resolver> =\n Effect.fnUntraced(function* (email) {\n const decoded = decodeBody(email);\n if (!decoded) {\n return null;\n }\n const fromAddress = email.from?.[0];\n const contact = fromAddress ? yield* resolve(Person.Person, { email: fromAddress.email }) : undefined;\n return mapToMessage(decoded, contact ?? undefined);\n });\n\n/** Formats a JMAP address as `\"Name <email>\"`, or just the address when unnamed. */\nconst formatAddress = (address: JmapMail.EmailAddress): string =>\n address.name ? `${address.name} <${address.email}>` : address.email;\n\n/** Joins an address list into a header-style string, or `undefined` when empty. */\nconst formatAddresses = (addresses: readonly JmapMail.EmailAddress[] | null | undefined): string | undefined =>\n addresses && addresses.length > 0 ? addresses.map(formatAddress).join(', ') : undefined;\n\n/**\n * Returns the first non-empty decoded value among the given body parts (e.g. `email.htmlBody` or\n * `email.textBody`). `Email/get` is requested with `fetchHTMLBodyValues`/`fetchTextBodyValues`, so the\n * referenced `bodyValues` are already decoded.\n */\nconst getBodyValue = (\n email: JmapMail.Email,\n parts: readonly JmapMail.EmailBodyPart[] | undefined,\n): string | undefined => {\n for (const part of parts ?? []) {\n const value = part.partId ? email.bodyValues?.[part.partId]?.value : undefined;\n if (value && value.trim().length > 0) {\n return value;\n }\n }\n return undefined;\n};\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport { type Database, Tag } from '@dxos/echo';\n\nimport { JMAP_DOMAIN } from '../../constants';\n\n/**\n * The pre-rename source, matched as a fallback so folders synced before the rename keep their tag\n * identity instead of being duplicated. Remove one release after landing.\n */\nconst LEGACY_JMAP_TAG_SOURCE = 'org.ietf.jmap.mailbox';\n\n/**\n * Finds an existing JMAP provider {@link Tag} object by its JMAP mailbox-id foreign key, or creates one\n * carrying that key ({@link JMAP_DOMAIN}, which also marks it read-only via `Tag.isProviderTag`). Keeps\n * the folder label in sync with the server on re-sync. Used by `sync/` for custom user folders;\n * well-known roles map onto canonical system tags instead. Mirrors Gmail's `findOrCreateGmailTag`.\n */\nexport const findOrCreateJmapTag = (\n db: Database.Database,\n { id, name }: { id: string; name: string },\n): Promise<Tag.Tag> =>\n Tag.findOrCreate(db, {\n key: { source: JMAP_DOMAIN, id },\n legacyKeys: [{ source: LEGACY_JMAP_TAG_SOURCE, id }],\n label: name,\n });\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport type * as SystemTags from '@dxos/plugin-inbox/SystemTags';\n\n/**\n * JMAP mailbox role → canonical system tag ({@link SystemTags.SystemTag}). Roles absent here are\n * intentionally dropped: `archive` is derived as \"not in inbox\" (Gmail's model), and\n * `drafts`/`trash`/`junk` are never synced.\n */\nexport const JMAP_ROLE_TAGS: Partial<Record<string, SystemTags.SystemTagId>> = {\n inbox: 'inbox',\n sent: 'sent',\n};\n\n/**\n * JMAP keyword → canonical system tag ({@link SystemTags.SystemTag}). Only `$flagged` (starred) is\n * projected; read-state (`$seen`) and the rest (`$answered`, `$draft`, `$forwarded`, …) are intentionally\n * dropped as high-churn and noisier than useful.\n */\nexport const JMAP_KEYWORD_TAGS: Partial<Record<string, SystemTags.SystemTagId>> = {\n $flagged: 'starred',\n};\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport * as Chunk from 'effect/Chunk';\nimport * as Effect from 'effect/Effect';\nimport * as Layer from 'effect/Layer';\nimport * as Option from 'effect/Option';\nimport * as Predicate from 'effect/Predicate';\nimport * as Stream from 'effect/Stream';\n\nimport { type Resolver, resolve } from '@dxos/extractor';\nimport { Cursor } from '@dxos/link';\nimport { log } from '@dxos/log';\nimport { Stage } from '@dxos/pipeline';\nimport { EmailStage } from '@dxos/pipeline-email';\nimport * as Mailbox from '@dxos/plugin-inbox/Mailbox';\nimport { MailSyncError, type MailSyncItem, MailSyncProvider, type MailSyncSource } from '@dxos/plugin-inbox/sync';\nimport type * as SyncStreamConfig from '@dxos/plugin-inbox/SyncStreamConfig';\nimport * as SystemTags from '@dxos/plugin-inbox/SystemTags';\nimport { TagIndex } from '@dxos/schema';\nimport { Person } from '@dxos/types';\n\nimport { Jmap, JmapMail } from '../../../apis';\nimport { JMAP_DOMAIN } from '../../../constants';\nimport { type JmapApiError } from '../../../errors';\nimport { JmapMailApi } from '../../../services';\nimport { type AttachmentMetadata, decodeBody, mapToMessage } from '../mapper';\nimport { findOrCreateJmapTag } from '../tags';\nimport { JMAP_KEYWORD_TAGS, JMAP_ROLE_TAGS } from './system-tags';\n\n/** The resolved delta for one run — either a fresh capture (no delta) or a fetched `Email/changes` chunk. */\ntype DeltaPlan = {\n readonly token: string | undefined;\n readonly createdIds: readonly string[] | undefined;\n readonly updatedIds: readonly string[];\n readonly hasMoreDelta: boolean;\n};\n\nconst MAIL_ACCOUNT_CAPABILITY = 'urn:ietf:params:jmap:mail';\n\n/** JMAP mail's streaming-pipeline tuning; see {@link SyncStreamConfig.SyncStreamConfig}. */\nconst JMAP_SYNC_CONFIG = {\n listPageSize: 50,\n fetchConcurrency: 5,\n commitPageSize: 10,\n maxItemsPerRun: 500,\n} as const satisfies SyncStreamConfig.SyncStreamConfig;\n\n/**\n * JMAP's {@link MailSyncProvider}: session/account discovery, the email source, the folder→tag map, and\n * the fused decode+map. Captures {@link JmapMailApi} + {@link Resolver} so the harness never names them.\n * Mirror of the Gmail provider (`googleMailSyncProvider`).\n */\nexport const jmapMailSyncProvider = (): Layer.Layer<MailSyncProvider, never, JmapMailApi | Resolver> =>\n Layer.effect(\n MailSyncProvider,\n Effect.gen(function* () {\n // The API is provided into the source stream (leaving `Cursor.Service` for the harness); the full\n // context into each `process` (whose only needs are API + resolver).\n const context = yield* Effect.context<JmapMailApi | Resolver>();\n const providerApi = yield* JmapMailApi;\n return {\n name: 'jmap',\n config: JMAP_SYNC_CONFIG,\n foreignKeySource: JMAP_DOMAIN,\n prepare: ({ db, binding, now, token, maxMessages }) =>\n Effect.gen(function* () {\n const api = yield* JmapMailApi;\n const session = yield* api.getSession;\n const accountId = session.primaryAccounts[MAIL_ACCOUNT_CAPABILITY];\n if (!accountId) {\n log.warn('jmap sync: session has no mail account', { username: session.username });\n return undefined;\n }\n const target: JmapMail.Target = { apiUrl: session.apiUrl, accountId, downloadUrl: session.downloadUrl };\n log('jmap sync: session resolved', { apiUrl: session.apiUrl, accountId });\n\n // TODO(wittjosiah): Migrate this folder→Tag sync onto a pipeline (source: folders; sink: find-or-create Tag).\n // Build a folder-id → tag-uri map — mirrors Gmail's `syncLabels`. A well-known role\n // (`inbox`/`sent`) maps onto the shared canonical system tag; a custom folder gets a\n // JMAP-scoped provider tag; a dropped role (`archive` — derived as \"not in inbox\";\n // `drafts`/`trash`/`junk` — not synced) produces no tag.\n const { list: folders } = yield* api.mailboxGet(target);\n const folderTagMap = new Map<string, string>();\n for (const folder of folders) {\n const canonical = folder.role ? JMAP_ROLE_TAGS[folder.role] : undefined;\n if (canonical) {\n const tag = yield* Effect.promise(() => SystemTags.findOrCreateSystemTag(db, canonical));\n folderTagMap.set(folder.id, Mailbox.tagUri(tag));\n } else if (folder.role) {\n continue;\n } else {\n const tag = yield* Effect.promise(() => findOrCreateJmapTag(db, { id: folder.id, name: folder.name }));\n folderTagMap.set(folder.id, Mailbox.tagUri(tag));\n }\n }\n\n // Keyword → canonical system tag uri (only `$flagged`/starred today). Built once so the\n // initial map and the reconcile diff resolve the same tags.\n const keywordTagMap = new Map<string, string>();\n for (const [keyword, canonical] of Object.entries(JMAP_KEYWORD_TAGS)) {\n if (!canonical) {\n continue;\n }\n const tag = yield* Effect.promise(() => SystemTags.findOrCreateSystemTag(db, canonical));\n keywordTagMap.set(keyword, Mailbox.tagUri(tag));\n }\n\n // Fused decode + map; `undefined` drops the item (no body, or unmappable). Constructs the\n // `Change` (an `insert`) directly, so no separate wrapping stage is needed downstream.\n const toMapped = (\n email: JmapMail.Email,\n ): Effect.Effect<EmailStage.Change | undefined, never, JmapMailApi | Resolver> =>\n Effect.gen(function* () {\n const decoded = decodeBody(email);\n if (!decoded) {\n return undefined;\n }\n const fromAddress = decoded.raw.from?.[0];\n const contact = fromAddress ? yield* resolve(Person.Person, { email: fromAddress.email }) : undefined;\n const mapped = mapToMessage(decoded, contact ?? undefined);\n if (!mapped) {\n return undefined;\n }\n const folderUris = mapped.mailboxIds.flatMap((folderId) => {\n const uri = folderTagMap.get(folderId);\n return uri ? [uri] : [];\n });\n const keywordUris = mapped.keywords.flatMap((keyword) => {\n const uri = keywordTagMap.get(keyword);\n return uri ? [uri] : [];\n });\n const tagUris = [...folderUris, ...keywordUris];\n const attachments = yield* fetchAttachments(target, decoded.attachments);\n return {\n _tag: 'insert',\n message: mapped.message,\n foreignId: decoded.raw.id,\n key: new Date(decoded.raw.receivedAt).getTime(),\n tagUris,\n attachments,\n } satisfies EmailStage.Change;\n });\n\n const toItem = (email: JmapMail.Email): MailSyncItem => ({\n foreignId: email.id,\n key: new Date(email.receivedAt).getTime(),\n process: toMapped(email).pipe(Effect.provide(context)),\n });\n\n // The first-tick baseline (and stale-token fallback): the current `Email/get` state with no\n // delta applied (so mail arriving during backfill is caught by the next incremental, not\n // missed). Defined once so both call sites share the same capture.\n const captureFreshDelta = Effect.map(\n api.emailGet(target, []),\n (result): DeltaPlan => ({\n token: result.state,\n createdIds: undefined,\n updatedIds: [],\n hasMoreDelta: false,\n }),\n );\n\n // Resolve the delta plan. An incremental run fetches one bounded `Email/changes` chunk since\n // the token (`maxChanges` = the per-run budget); `hasMoreChanges` drives `runAgain`, and\n // `newState` is the chunk boundary the token advances to — so a large delta drains across\n // runs. A stale token (`cannotCalculateChanges`) falls back to `captureFreshDelta`;\n // `Effect.catchIf` recovers only that case.\n const resolveDelta: Effect.Effect<DeltaPlan, JmapApiError, never> =\n token === undefined\n ? captureFreshDelta\n : api.emailChanges(target, token, maxMessages).pipe(\n Effect.map((result): DeltaPlan => {\n // `updated` are ids whose keywords/mailboxIds may have changed — re-fetched + diffed\n // against local tags in the reconcile branch. Exclude ids that are also newly\n // created (their full fetch already carries current tags).\n const created = new Set(result.created);\n const updatedIds = result.updated.filter((id) => !created.has(id));\n log('jmap sync: incremental delta', {\n created: result.created.length,\n updated: result.updated.length,\n destroyed: result.destroyed.length,\n hasMoreDelta: result.hasMoreChanges,\n });\n return {\n token: result.newState,\n createdIds: result.created,\n updatedIds,\n hasMoreDelta: result.hasMoreChanges,\n };\n }),\n Effect.catchIf(\n (error) => error.type === 'cannotCalculateChanges',\n () => {\n log('jmap sync: state token stale, falling back to window scan');\n Cursor.clearToken(binding);\n return captureFreshDelta;\n },\n ),\n );\n const { token: capturedToken, createdIds, updatedIds, hasMoreDelta } = yield* resolveDelta;\n\n const source: MailSyncSource = {\n buildSource: ({ windows, filter, tagIndex, onEnumerated, onRetrieved }) => {\n // Incremental replaces the forward window with the delta's created ids but keeps the\n // backward backfill window, so each tick still makes backfill progress. When a user filter\n // is set, the delta's account-wide created ids would bypass it — so fall back to the\n // filtered forward window scan for additions (the delta still drives reconcile).\n const forwardIds = filter ? undefined : createdIds;\n if (forwardIds) {\n onEnumerated(forwardIds.length);\n }\n return {\n additions: jmapEmails(target, folders, {\n windows,\n filter,\n now,\n onEnumerated,\n onRetrieved,\n forwardIds,\n }).pipe(\n Stream.map(toItem),\n Stream.provideService(JmapMailApi, providerApi),\n Stream.mapError(MailSyncError.wrap()),\n ),\n // Empty on non-incremental runs; `jmapReconcile` re-fetches + diffs each `updated` id\n // and resolves it to a `Change` itself (it needs the entityId to read local tags).\n reconciles: jmapReconcile(updatedIds, target, folderTagMap, keywordTagMap, tagIndex).pipe(\n Stream.provideService(JmapMailApi, providerApi),\n Stream.mapError(MailSyncError.wrap()),\n ),\n };\n },\n nextToken: () => capturedToken,\n reconcileForeignIds: updatedIds,\n hasMoreDelta: () => hasMoreDelta,\n };\n return source;\n }).pipe(Effect.provide(context), Effect.mapError(MailSyncError.wrap())),\n };\n }),\n );\n\n/**\n * Reconcile branch for JMAP: for each `updated` email id, re-fetch its current `mailboxIds` + `keywords`,\n * map them to Tags, and diff against the message's current *local* tags — a remote-wins, snapshot-free\n * reconciliation that is idempotent (a crash re-run produces an empty diff). Emits a retag\n * {@link EmailStage.Change} keyed by the resolved EntityId; ids not in the feed or with no net change are\n * dropped.\n *\n * The two axes differ: **folder** tags are fully remote-wins (added *and* removed, since folder\n * membership is server-authoritative), while **keyword** tags (starred) are **add-only** — the remote can\n * add a star but we never auto-remove one, which would clobber a locally-toggled star until we write\n * local flags back to the provider.\n */\nconst jmapReconcile = (\n updatedIds: readonly string[],\n target: JmapMail.Target,\n folderTagMap: ReadonlyMap<string, string>,\n keywordTagMap: ReadonlyMap<string, string>,\n tagIndex: TagIndex.TagIndex,\n): Stream.Stream<EmailStage.Change, JmapApiError, JmapMailApi | Cursor.Service> => {\n const folderProviderUris = new Set(folderTagMap.values());\n return Stream.fromIterable(updatedIds).pipe(\n Stage.map('jmap-reconcile', (id: string) =>\n Effect.gen(function* () {\n const { foreignIndex } = yield* Cursor.Service;\n const entityId = foreignIndex?.get(id);\n if (!entityId) {\n return undefined;\n }\n const api = yield* JmapMailApi;\n const { list } = yield* api.emailGet(target, [id]);\n const email = list[0];\n if (!email) {\n return undefined;\n }\n const remoteFolderUris = (email.mailboxIds ? Object.keys(email.mailboxIds) : []).flatMap((folderId) => {\n const uri = folderTagMap.get(folderId);\n return uri ? [uri] : [];\n });\n const remoteKeywordUris = (\n email.keywords\n ? Object.entries(email.keywords)\n .filter(([, set]) => set)\n .map(([keyword]) => keyword)\n : []\n ).flatMap((keyword) => {\n const uri = keywordTagMap.get(keyword);\n return uri ? [uri] : [];\n });\n const localTags = TagIndex.bind(tagIndex).tags(entityId);\n const remoteFolders = new Set(remoteFolderUris);\n // Add any remote folder/keyword tag the message lacks.\n const addTagIds = [...remoteFolderUris, ...remoteKeywordUris].filter((tagUri) => !localTags.includes(tagUri));\n // Remove only folder tags the remote no longer has; user tags and starred are never auto-removed.\n const removeTagIds = localTags.filter((tagUri) => folderProviderUris.has(tagUri) && !remoteFolders.has(tagUri));\n if (addTagIds.length === 0 && removeTagIds.length === 0) {\n return undefined;\n }\n return { _tag: 'retag', foreignId: id, entityId, addTagIds, removeTagIds } satisfies EmailStage.Change;\n }),\n ),\n );\n};\n\n/**\n * Downloads each attachment's bytes via `JmapMailApi.downloadBlob`. One failed download (including a\n * session with no `downloadUrl`) is logged and dropped rather than failing the whole message.\n */\nconst fetchAttachments = (\n target: JmapMail.Target,\n attachments: readonly AttachmentMetadata[],\n): Effect.Effect<readonly EmailStage.Attachment[], never, JmapMailApi> =>\n Effect.gen(function* () {\n const api = yield* JmapMailApi;\n const fetched = yield* Effect.forEach(\n attachments,\n (attachment) =>\n api.downloadBlob(target, attachment.blobId, { name: attachment.name, type: attachment.mimeType }).pipe(\n Effect.map(\n (bytes): EmailStage.Attachment => ({\n name: attachment.name,\n mimeType: attachment.mimeType,\n size: attachment.size ?? bytes.byteLength,\n bytes,\n contentId: attachment.contentId,\n }),\n ),\n Effect.catchAll((error) => {\n log.catch(error, { blobId: attachment.blobId, name: attachment.name });\n return Effect.succeed(undefined);\n }),\n ),\n { concurrency: JMAP_SYNC_CONFIG.fetchConcurrency },\n );\n return fetched.filter((attachment): attachment is EmailStage.Attachment => attachment !== undefined);\n });\n\n/**\n * Streams JMAP email ids over a {@link Cursor.Window}: build the query filter (folder scope + date\n * bounds + optional user DSL), then paginate. Forward pages oldest-first from `max` so a capped run\n * advances `max` gap-free instead of jumping to the newest key and stranding the middle; backward pages\n * newest-first from `min` to the horizon. Backward's upper bound is queried 1ms past `min` so a message\n * sharing that exact millisecond is re-queried (and deduped) rather than skipped once `min` passes it.\n * Split from the full-email fetch so a bidirectional run can cap the combined id stream before fetching.\n */\nconst jmapIds = (\n target: JmapMail.Target,\n folders: readonly JmapMail.Mailbox[],\n window: Cursor.Window,\n options: {\n filter?: string;\n /** Reference \"now\" for the user filter's relative dates (pinned by tests). */\n now: Date;\n /** Called with each query page's enumerated id count, to accumulate the retrieval total. */\n onEnumerated?: (count: number) => void;\n },\n): Stream.Stream<string, JmapApiError, JmapMailApi> =>\n Stream.unwrap(\n Effect.gen(function* () {\n const api = yield* JmapMailApi;\n\n const userFilter = options.filter\n ? JmapMail.parseMailQuery(options.filter, {\n now: options.now,\n resolveMailbox: (nameOrRole) => JmapMail.resolveMailboxByNameOrRole(folders, nameOrRole),\n })\n : Option.none<Jmap.Filter>();\n // When the user filter already scopes a mailbox, skip the default folder restriction.\n const scopesMailbox = Option.match(userFilter, { onNone: () => false, onSome: JmapMail.filterScopesMailbox });\n // Default to all mail (every folder incl. Sent) so full conversations sync, excluding Trash/Junk/Drafts.\n const excludedFolderIds = folders\n .filter((folder) => folder.role === 'trash' || folder.role === 'junk' || folder.role === 'drafts')\n .map((folder) => folder.id);\n\n const conditions: Jmap.Filter[] = [];\n if (!scopesMailbox && excludedFolderIds.length > 0) {\n conditions.push({ inMailboxOtherThan: excludedFolderIds });\n }\n // Bound the query to the window. Backward's `end` is `min` — extend 1ms to include the boundary\n // millisecond (see doc above).\n const upperBound = window.direction === 'backward' ? new Date(window.end.getTime() + 1) : window.end;\n conditions.push({ after: window.start.toISOString() });\n conditions.push({ before: upperBound.toISOString() });\n if (Option.isSome(userFilter)) {\n conditions.push(userFilter.value);\n }\n const filter: Jmap.Filter = conditions.length === 1 ? conditions[0] : { operator: 'AND', conditions };\n log('starting jmap sync', {\n direction: window.direction,\n start: window.start.toISOString(),\n end: window.end.toISOString(),\n conditions: conditions.length,\n });\n\n return Stream.paginateChunkEffect(0, (position: number) =>\n Effect.gen(function* () {\n const { ids } = yield* api.emailQuery(target, {\n filter,\n sort: [{ property: 'receivedAt', isAscending: window.direction === 'forward' }],\n position,\n limit: JMAP_SYNC_CONFIG.listPageSize,\n });\n log('jmap sync: queried page', { position, total: ids.length });\n // Report each page's count as it arrives so the meter's retrieval total leads the fetch.\n options.onEnumerated?.(ids.length);\n const next =\n ids.length < JMAP_SYNC_CONFIG.listPageSize ? Option.none<number>() : Option.some(position + ids.length);\n return [Chunk.fromIterable(ids), next];\n }),\n );\n }),\n );\n\n/**\n * Fetches the full JMAP email for each id. Takes a plain id stream (not a window) so a bidirectional\n * run can cap the combined stream once, then fetch full emails for exactly the capped set.\n */\nconst jmapEmailsForIds = (\n target: JmapMail.Target,\n ids: Stream.Stream<string, JmapApiError, JmapMailApi | Cursor.Service>,\n options: {\n /** Called once per email retrieved (full fetch), to advance progress. */\n onRetrieved?: () => void;\n } = {},\n): Stream.Stream<JmapMail.Email, JmapApiError, JmapMailApi | Cursor.Service> =>\n ids.pipe(\n Stream.flatMap(\n (id) =>\n // Drop an id deleted between query and `emailGet` (returns nothing) by filtering out the null.\n // Do NOT recover the error channel: a real `JmapApiError` must propagate and fail the run so the\n // durable retry re-fetches, rather than stranding the message once `max` advances.\n Stream.fromEffect(\n Effect.gen(function* () {\n const api = yield* JmapMailApi;\n const { list } = yield* api.emailGet(target, [id]);\n options.onRetrieved?.();\n return list[0];\n }),\n ).pipe(Stream.filter(Predicate.isNotNullable)),\n { concurrency: JMAP_SYNC_CONFIG.fetchConcurrency, bufferSize: 10 },\n ),\n );\n\n/**\n * Streams full JMAP emails for one run: the forward source's ids then the backward window's,\n * concatenated and fetched in full. Intentionally UNBOUNDED — the harness caps after dedup (see\n * `runMailSync`). The forward source is either the `max`-anchored forward window (first tick / stale\n * fallback) or, when `forwardIds` is set, the incremental delta's created ids — in which case the\n * backward window still runs, so an incremental tick keeps making backfill progress.\n *\n * `Cursor.skipCommitted` drops ids already in the dedup set before `emailGet`, so re-queried boundary\n * ids aren't downloaded; the harness's post-fetch `Cursor.dedupStage` stays the authority.\n */\nconst jmapEmails = (\n target: JmapMail.Target,\n folders: readonly JmapMail.Mailbox[],\n config: {\n windows: Cursor.Windows;\n filter?: string;\n now: Date;\n onEnumerated?: (count: number) => void;\n onRetrieved?: () => void;\n /** Incremental delta's created ids; when set, replaces the forward window (backward still runs). */\n forwardIds?: readonly string[];\n },\n): Stream.Stream<JmapMail.Email, JmapApiError, JmapMailApi | Cursor.Service> => {\n const idsFor = (window: Cursor.Window | undefined) =>\n window\n ? jmapIds(target, folders, window, { filter: config.filter, now: config.now, onEnumerated: config.onEnumerated })\n : Stream.empty;\n const forward = config.forwardIds ? Stream.fromIterable(config.forwardIds) : idsFor(config.windows.forward);\n\n return jmapEmailsForIds(\n target,\n Stream.concat(forward, idsFor(config.windows.backward)).pipe(Cursor.skipCommitted('skip-committed', (id) => id)),\n { onRetrieved: config.onRetrieved },\n );\n};\n"],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ import { h as JmapCredentials } from "./chunk-apis.mjs";
2
+ import { JmapMailApi } from "./services.mjs";
3
+ import { t as jmapMailSyncProvider } from "./chunk-sync-provider.mjs";
4
+ import * as Effect from "effect/Effect";
5
+ import * as Layer from "effect/Layer";
6
+ import { Obj } from "@dxos/echo";
7
+ import { log } from "@dxos/log";
8
+ import * as Operation from "@dxos/compute/Operation";
9
+ import * as InboxOperation from "@dxos/plugin-inbox/InboxOperation";
10
+ import { runMailSync } from "@dxos/plugin-inbox/sync";
11
+ import { Cursor } from "@dxos/link";
12
+ import * as FetchHttpClient from "@effect/platform/FetchHttpClient";
13
+ import * as InboxResolver from "@dxos/extractor-lib";
14
+ //#region src/operations/mail/sync/handler.ts
15
+ var __dxlog_file = "/Users/burdon/Code/dxos/dxos/.claude/worktrees/confident-jones-b0b4bf/packages/plugins/plugin-jmap/src/operations/mail/sync/handler.ts";
16
+ var handler = InboxOperation.JmapSync.pipe(Operation.withHandler(({ binding: bindingRef }) => Effect.gen(function* () {
17
+ const bindingObj = bindingRef.target;
18
+ const db = bindingObj ? Obj.getDatabase(bindingObj) : void 0;
19
+ if (!bindingObj || !db || !Cursor.isExternal(bindingObj)) {
20
+ log.warn("jmap sync skipped: missing binding target or database", {
21
+ hasBinding: Boolean(bindingObj),
22
+ hasDatabase: Boolean(db)
23
+ }, {
24
+ "~LogMeta": "~LogMeta",
25
+ F: __dxlog_file,
26
+ L: 26,
27
+ S: this
28
+ });
29
+ return { newMessages: 0 };
30
+ }
31
+ const accessTokenRef = bindingObj.spec.source;
32
+ return yield* runMailSync({ binding: bindingRef }).pipe(Effect.provide(jmapMailSyncProvider().pipe(Layer.provide(InboxResolver.Live), Layer.provide(JmapMailApi.Live), Layer.provide(FetchHttpClient.layer), Layer.provide(JmapCredentials.fromAccessToken(accessTokenRef)))), Effect.withSpan("jmap-sync"));
33
+ })), Operation.opaqueHandler);
34
+ //#endregion
35
+ export { handler as default, jmapMailSyncProvider };
36
+
37
+ //# sourceMappingURL=chunk-sync.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chunk-sync.mjs","names":[],"sources":["../../src/operations/mail/sync/handler.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\nimport * as FetchHttpClient from '@effect/platform/FetchHttpClient';\nimport * as Effect from 'effect/Effect';\nimport * as Layer from 'effect/Layer';\n\nimport * as Operation from '@dxos/compute/Operation';\nimport { Obj } from '@dxos/echo';\nimport * as InboxResolver from '@dxos/extractor-lib';\nimport { Cursor } from '@dxos/link';\nimport { log } from '@dxos/log';\nimport * as InboxOperation from '@dxos/plugin-inbox/InboxOperation';\nimport { runMailSync } from '@dxos/plugin-inbox/sync';\n\nimport { JmapCredentials, JmapMailApi } from '../../../services';\nimport { jmapMailSyncProvider } from './sync-provider';\n\nconst handler = InboxOperation.JmapSync.pipe(\n Operation.withHandler(({ binding: bindingRef }) =>\n Effect.gen(function* () {\n const bindingObj = bindingRef.target;\n const db = bindingObj ? Obj.getDatabase(bindingObj) : undefined;\n if (!bindingObj || !db || !Cursor.isExternal(bindingObj)) {\n log.warn('jmap sync skipped: missing binding target or database', {\n hasBinding: Boolean(bindingObj),\n hasDatabase: Boolean(db),\n });\n return { newMessages: 0 };\n }\n\n const accessTokenRef = bindingObj.spec.source;\n // Layer stack, top-down: the provider needs JmapMailApi + Resolver; JmapMailApi.Live needs the\n // HTTP client + credentials. Chained `Layer.provide` reads as that dependency stack.\n return yield* runMailSync({ binding: bindingRef }).pipe(\n Effect.provide(\n jmapMailSyncProvider().pipe(\n Layer.provide(InboxResolver.Live),\n Layer.provide(JmapMailApi.Live),\n Layer.provide(FetchHttpClient.layer),\n Layer.provide(JmapCredentials.fromAccessToken(accessTokenRef)),\n ),\n ),\n Effect.withSpan('jmap-sync'),\n );\n }),\n ),\n Operation.opaqueHandler,\n);\n\nexport default handler;\n"],"mappings":";;;;;;;;;;;;;;;AAmBA,IAAM,UAAU,eAAe,SAAS,KACtC,UAAU,aAAa,EAAE,SAAS,iBAChC,OAAO,IAAI,aAAa;CACtB,MAAM,aAAa,WAAW;CAC9B,MAAM,KAAK,aAAa,IAAI,YAAY,UAAU,IAAI,KAAA;CACtD,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,OAAO,WAAW,UAAU,GAAG;EACxD,IAAI,KAAK,yDAAyD;GAChE,YAAY,QAAQ,UAAU;GAC9B,aAAa,QAAQ,EAAE;EACzB,GAAA;GAAA,YAAA;GAAA,GAAA;GAAA,GAAA;GAAA,GAAA;EAAA,CAAC;EACD,OAAO,EAAE,aAAa,EAAE;CAC1B;CAEA,MAAM,iBAAiB,WAAW,KAAK;CAGvC,OAAO,OAAO,YAAY,EAAE,SAAS,WAAW,CAAC,CAAC,CAAC,KACjD,OAAO,QACL,qBAAqB,CAAC,CAAC,KACrB,MAAM,QAAQ,cAAc,IAAI,GAChC,MAAM,QAAQ,YAAY,IAAI,GAC9B,MAAM,QAAQ,gBAAgB,KAAK,GACnC,MAAM,QAAQ,gBAAgB,gBAAgB,cAAc,CAAC,CAC/D,CACF,GACA,OAAO,SAAS,WAAW,CAC7B;AACF,CAAC,CACH,GACA,UAAU,aACZ"}
@@ -0,0 +1,4 @@
1
+ import { n as JMAP_DOMAIN, r as JMAP_MAIL_CONNECTOR_ID, t as JMAP_DEFAULT_HOST } from "./chunk-constants.mjs";
2
+ import { n as JmapSendIdentityNotFoundError, r as JmapSendMessageInvalidError, t as JmapApiError } from "./chunk-errors.mjs";
3
+ import { t as meta } from "./chunk-meta.mjs";
4
+ export { JMAP_DEFAULT_HOST, JMAP_DOMAIN, JMAP_MAIL_CONNECTOR_ID, JmapApiError, JmapSendIdentityNotFoundError, JmapSendMessageInvalidError, meta };
@@ -0,0 +1,2 @@
1
+ import { t as meta } from "./chunk-meta.mjs";
2
+ export { meta };
@@ -0,0 +1,13 @@
1
+ import * as Operation from "@dxos/compute/Operation";
2
+ import * as OperationHandlerSet from "@dxos/compute/OperationHandlerSet";
3
+ import * as InboxOperation from "@dxos/plugin-inbox/InboxOperation";
4
+ //#region src/operations/index.ts
5
+ var JmapOperationHandlerSet = OperationHandlerSet.lazy([
6
+ InboxOperation.MaterializeJmapTarget.pipe(Operation.lazyHandler(() => import("./chunk-handler.mjs"))),
7
+ InboxOperation.JmapSend.pipe(Operation.lazyHandler(() => import("./chunk-send.mjs"))),
8
+ InboxOperation.JmapSync.pipe(Operation.lazyHandler(() => import("./chunk-sync.mjs")))
9
+ ]);
10
+ //#endregion
11
+ export { JmapOperationHandlerSet };
12
+
13
+ //# sourceMappingURL=operations.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"operations.mjs","names":[],"sources":["../../src/operations/index.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\nimport * as Operation from '@dxos/compute/Operation';\nimport * as OperationHandlerSet from '@dxos/compute/OperationHandlerSet';\nimport * as InboxOperation from '@dxos/plugin-inbox/InboxOperation';\n\nexport const JmapOperationHandlerSet = OperationHandlerSet.lazy([\n InboxOperation.MaterializeJmapTarget.pipe(Operation.lazyHandler(() => import('./mail/materialize/handler'))),\n InboxOperation.JmapSend.pipe(Operation.lazyHandler(() => import('./mail/send'))),\n InboxOperation.JmapSync.pipe(Operation.lazyHandler(() => import('./mail/sync'))),\n]);\n"],"mappings":";;;;AAQA,IAAa,0BAA0B,oBAAoB,KAAK;CAC9D,eAAe,sBAAsB,KAAK,UAAU,kBAAkB,OAAO,sBAA6B,CAAC;CAC3G,eAAe,SAAS,KAAK,UAAU,kBAAkB,OAAO,mBAAc,CAAC;CAC/E,eAAe,SAAS,KAAK,UAAU,kBAAkB,OAAO,mBAAc,CAAC;AACjF,CAAC"}
@@ -0,0 +1,9 @@
1
+ import { t as meta } from "./chunk-meta.mjs";
2
+ import { JmapOperationHandlerSet } from "./operations.mjs";
3
+ import * as Plugin from "@dxos/app-framework/Plugin";
4
+ //#region src/plugin.ts
5
+ var JmapPlugin = Plugin.lazy(meta, () => import("#plugin"));
6
+ //#endregion
7
+ export { JmapOperationHandlerSet, JmapPlugin };
8
+
9
+ //# sourceMappingURL=plugin.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.mjs","names":[],"sources":["../../src/plugin.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\nimport * as Plugin from '@dxos/app-framework/Plugin';\n\nimport { meta } from './meta';\n\nexport const JmapPlugin = Plugin.lazy(meta, () => import('#plugin'));\n\nexport { JmapOperationHandlerSet } from './operations';\n"],"mappings":";;;;AAQA,IAAa,aAAa,OAAO,KAAK,YAAY,OAAO,UAAU"}
@@ -0,0 +1,139 @@
1
+ import { a as downloadBlob, c as emailQuery, d as mailboxGet, f as submitEmail, h as JmapCredentials, l as emailSetUpdate, m as getSession, o as emailChanges, s as emailGet, u as identityGet } from "./chunk-apis.mjs";
2
+ import { t as JmapApiError } from "./chunk-errors.mjs";
3
+ import * as Effect from "effect/Effect";
4
+ import * as Context from "effect/Context";
5
+ import * as Layer from "effect/Layer";
6
+ import * as Predicate from "effect/Predicate";
7
+ //#region src/services/jmap-mail-api.ts
8
+ /** Narrows an `unknown` filter value to a plain object so the mock can walk its conditions. */
9
+ var isRecord = (value) => typeof value === "object" && value !== null;
10
+ /**
11
+ * Evaluates an `Email/query` filter against an email — enough of RFC 8621 §4.4.1 for the sync's
12
+ * generated filter: the `after`/`before` window, `inMailbox`/`inMailboxOtherThan` folder scope, and
13
+ * the AND/OR/NOT operator combinators. Unknown conditions are treated as matching.
14
+ */
15
+ var matchesFilter = (email, filter) => {
16
+ if (!isRecord(filter)) return true;
17
+ if ("operator" in filter && Array.isArray(filter.conditions)) {
18
+ const results = filter.conditions.map((condition) => matchesFilter(email, condition));
19
+ if (filter.operator === "OR") return results.some(Boolean);
20
+ if (filter.operator === "NOT") return !results.some(Boolean);
21
+ return results.every(Boolean);
22
+ }
23
+ const receivedAt = new Date(email.receivedAt).getTime();
24
+ if (typeof filter.after === "string" && receivedAt < new Date(filter.after).getTime()) return false;
25
+ if (typeof filter.before === "string" && receivedAt >= new Date(filter.before).getTime()) return false;
26
+ const mailboxIds = email.mailboxIds ? Object.keys(email.mailboxIds) : [];
27
+ if (typeof filter.inMailbox === "string" && !mailboxIds.includes(filter.inMailbox)) return false;
28
+ if (Array.isArray(filter.inMailboxOtherThan)) {
29
+ const excluded = filter.inMailboxOtherThan;
30
+ if (!mailboxIds.some((id) => !excluded.includes(id))) return false;
31
+ }
32
+ return true;
33
+ };
34
+ /** Sorts emails by the query's primary sort (only `receivedAt` is used by the sync). */
35
+ var sortEmails = (emails, sort) => {
36
+ const primary = sort?.[0];
37
+ if (!primary || primary.property !== "receivedAt") return emails;
38
+ const direction = primary.isAscending ? 1 : -1;
39
+ return [...emails].sort((left, right) => direction * (new Date(left.receivedAt).getTime() - new Date(right.receivedAt).getTime()));
40
+ };
41
+ var JmapMailApi = class JmapMailApi extends Context.Tag("@dxos/plugin-inbox/JmapMailApi")() {
42
+ /**
43
+ * Live layer backed by the real JMAP HTTP client. Captures the auth/HTTP context once and provides
44
+ * it to each request, so the resulting service methods carry no requirements. Requires an
45
+ * `HttpClient` and a `JmapCredentials` (e.g. {@link JmapCredentials.fromConnection}) to be available
46
+ * where it is provided.
47
+ */
48
+ static Live = Layer.effect(JmapMailApi, Effect.gen(function* () {
49
+ const context = yield* Effect.context();
50
+ return JmapMailApi.of({
51
+ getSession: Effect.provide(getSession, context),
52
+ mailboxGet: (target) => Effect.provide(mailboxGet(target), context),
53
+ emailQuery: (target, options) => Effect.provide(emailQuery(target, options), context),
54
+ emailGet: (target, ids, properties) => Effect.provide(emailGet(target, ids, properties), context),
55
+ emailChanges: (target, sinceState, maxChanges) => Effect.provide(emailChanges(target, sinceState, maxChanges), context),
56
+ downloadBlob: (target, blobId, options) => Effect.provide(downloadBlob(target, blobId, options), context),
57
+ identityGet: (target) => Effect.provide(identityGet(target), context),
58
+ emailSetUpdate: (target, emailId, patch) => Effect.provide(emailSetUpdate(target, emailId, patch), context),
59
+ submitEmail: (target, args) => Effect.provide(submitEmail(target, args), context)
60
+ });
61
+ }));
62
+ /**
63
+ * Zero-dependency mock backed by an in-memory {@link JmapDataset} — for unit tests that drive the
64
+ * real sync pipeline with no live account. `emailQuery` honours the query filter (`after`/`before`
65
+ * window + folder scope), the `receivedAt` sort, and `position`/`limit` pagination, so the sync's
66
+ * window walk and pagination exercise realistically. Write methods are unsupported (die).
67
+ */
68
+ static mock = (dataset) => {
69
+ const byId = new Map(dataset.emails.map((email) => [email.id, email]));
70
+ return Layer.succeed(JmapMailApi, JmapMailApi.of({
71
+ getSession: Effect.succeed(dataset.session),
72
+ mailboxGet: () => Effect.succeed({ list: dataset.folders }),
73
+ emailQuery: (_target, options = {}) => Effect.sync(() => {
74
+ const matching = sortEmails(dataset.emails.filter((email) => matchesFilter(email, options.filter)), options.sort);
75
+ const position = options.position ?? 0;
76
+ const limit = options.limit ?? matching.length;
77
+ const page = matching.slice(position, position + limit);
78
+ return {
79
+ position,
80
+ total: matching.length,
81
+ ids: page.map((email) => email.id)
82
+ };
83
+ }),
84
+ emailGet: (_target, ids) => Effect.sync(() => ({
85
+ list: ids.map((id) => byId.get(id)).filter(Predicate.isNotNullable),
86
+ state: dataset.state
87
+ })),
88
+ emailChanges: (_target, sinceState, maxChanges) => Effect.gen(function* () {
89
+ const latest = dataset.state ?? sinceState;
90
+ if (sinceState === latest) return {
91
+ oldState: sinceState,
92
+ newState: latest,
93
+ hasMoreChanges: false,
94
+ created: [],
95
+ updated: [],
96
+ destroyed: []
97
+ };
98
+ const created = [];
99
+ const updated = [];
100
+ const destroyed = [];
101
+ let chain = sinceState;
102
+ let matched = false;
103
+ let hasMoreChanges = false;
104
+ for (const step of dataset.changeLog ?? []) if (step.sinceState === chain) {
105
+ const stepSize = (step.created?.length ?? 0) + (step.updated?.length ?? 0) + (step.destroyed?.length ?? 0);
106
+ if (maxChanges !== void 0 && chain !== sinceState && created.length + updated.length + destroyed.length + stepSize > maxChanges) {
107
+ hasMoreChanges = true;
108
+ break;
109
+ }
110
+ matched = true;
111
+ created.push(...step.created ?? []);
112
+ updated.push(...step.updated ?? []);
113
+ destroyed.push(...step.destroyed ?? []);
114
+ chain = step.newState;
115
+ }
116
+ if (!matched) return yield* Effect.fail(new JmapApiError(void 0, "Cannot calculate changes", "cannotCalculateChanges"));
117
+ return {
118
+ oldState: sinceState,
119
+ newState: chain,
120
+ hasMoreChanges,
121
+ created,
122
+ updated,
123
+ destroyed
124
+ };
125
+ }),
126
+ downloadBlob: (_target, blobId) => {
127
+ const bytes = dataset.blobs?.[blobId];
128
+ return bytes ? Effect.succeed(bytes) : Effect.die(/* @__PURE__ */ new Error(`mock JmapMailApi: blob not in dataset: ${blobId}`));
129
+ },
130
+ identityGet: () => Effect.die(/* @__PURE__ */ new Error("mock JmapMailApi: identityGet not supported")),
131
+ emailSetUpdate: () => Effect.die(/* @__PURE__ */ new Error("mock JmapMailApi: emailSetUpdate not supported")),
132
+ submitEmail: () => Effect.die(/* @__PURE__ */ new Error("mock JmapMailApi: submitEmail not supported"))
133
+ }));
134
+ };
135
+ };
136
+ //#endregion
137
+ export { JmapCredentials, JmapMailApi };
138
+
139
+ //# sourceMappingURL=services.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"services.mjs","names":[],"sources":["../../src/services/jmap-mail-api.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\nimport type * as HttpClient from '@effect/platform/HttpClient';\nimport * as Context from 'effect/Context';\nimport * as Effect from 'effect/Effect';\nimport * as Layer from 'effect/Layer';\nimport * as Predicate from 'effect/Predicate';\n\nimport { Jmap, JmapMail } from '../apis';\nimport { JmapApiError } from '../errors';\nimport { type JmapCredentials } from './jmap-credentials';\n\n/**\n * The requirements the underlying {@link Jmap}/{@link JmapMail} request functions carry (HTTP client\n * + JMAP credentials). {@link JmapMailApi.Live} bakes these in so the service methods themselves\n * require nothing — which is what lets a test satisfy {@link JmapMailApi} with a zero-dependency mock\n * (no HTTP, no credentials, no live server).\n */\ntype Requirements = HttpClient.HttpClient | JmapCredentials;\n\n/**\n * Swappable JMAP API surface. `Live` delegates to the real {@link Jmap}/{@link JmapMail} request\n * functions; tests provide a data-backed mock. Making the JMAP dependency a service (rather than the\n * sync operation calling `Jmap.*`/`JmapMail.*` and hardcoding `FetchHttpClient.layer` internally) is\n * what lets the sync run against generated data with no live account — mirrors `GoogleMailApi`.\n * Every request fails only with {@link JmapApiError}, and methods carry no requirements: `Live`\n * bakes them in (see {@link Requirements}) so a mock can satisfy the surface with no HTTP or creds.\n */\nexport interface JmapMailApiService {\n readonly getSession: Effect.Effect<Jmap.Session, JmapApiError>;\n readonly mailboxGet: (target: JmapMail.Target) => Effect.Effect<JmapMail.MailboxGetResult, JmapApiError>;\n readonly emailQuery: (\n target: JmapMail.Target,\n options?: {\n filter?: unknown;\n sort?: readonly { property: string; isAscending?: boolean }[];\n position?: number;\n limit?: number;\n calculateTotal?: boolean;\n },\n ) => Effect.Effect<JmapMail.EmailQueryResult, JmapApiError>;\n readonly emailGet: (\n target: JmapMail.Target,\n ids: readonly string[],\n properties?: readonly string[],\n ) => Effect.Effect<JmapMail.EmailGetResult, JmapApiError>;\n readonly emailChanges: (\n target: JmapMail.Target,\n sinceState: string,\n maxChanges?: number,\n ) => Effect.Effect<JmapMail.EmailChangesResult, JmapApiError>;\n readonly downloadBlob: (\n target: JmapMail.Target,\n blobId: string,\n options?: { name?: string; type?: string },\n ) => Effect.Effect<Uint8Array, JmapApiError>;\n readonly identityGet: (target: JmapMail.Target) => Effect.Effect<JmapMail.IdentityGetResult, JmapApiError>;\n readonly emailSetUpdate: (\n target: JmapMail.Target,\n emailId: string,\n patch: Record<string, unknown>,\n ) => Effect.Effect<JmapMail.EmailSetResult, JmapApiError>;\n readonly submitEmail: (\n target: JmapMail.Target,\n args: {\n identityId: string;\n draftsMailboxId: string;\n sentMailboxId: string;\n draft: {\n from: readonly JmapMail.EmailAddress[];\n to: readonly JmapMail.EmailAddress[];\n cc?: readonly JmapMail.EmailAddress[];\n bcc?: readonly JmapMail.EmailAddress[];\n subject?: string;\n inReplyTo?: readonly string[];\n references?: readonly string[];\n text: string;\n };\n },\n ) => Effect.Effect<{ id: string; threadId: string | undefined }, JmapApiError>;\n}\n\n/**\n * An in-memory JMAP dataset a {@link JmapMailApi.mock} serves — the session (account discovery), the\n * folders (mailboxes), and the full emails the sync would fetch. Build one with `generateJmapDataset`\n * from this plugin's `./testing`.\n */\nexport interface JmapDataset {\n readonly session: Jmap.Session;\n readonly folders: readonly JmapMail.Mailbox[];\n /** Full emails, ordered ascending by `receivedAt` (the mock sorts/paginates a window over them). */\n readonly emails: readonly JmapMail.Email[];\n /** Attachment bytes keyed by `blobId`, served by `downloadBlob` for emails that carry one. */\n readonly blobs?: Readonly<Record<string, Uint8Array>>;\n /**\n * Current `Email/get` state token — `emailGet` returns it and it's the newest `Email/changes` state.\n * Set to exercise the incremental path; absent means \"no delta support\" (token capture returns undefined).\n */\n readonly state?: string;\n /** Ordered `Email/changes` steps chaining `sinceState` → `newState`, each carrying the delta. */\n readonly changeLog?: readonly JmapChangeStep[];\n}\n\n/** One `Email/changes` step in a {@link JmapDataset}'s change log. */\nexport interface JmapChangeStep {\n readonly sinceState: string;\n readonly newState: string;\n readonly created?: readonly string[];\n readonly updated?: readonly string[];\n readonly destroyed?: readonly string[];\n}\n\n/** Narrows an `unknown` filter value to a plain object so the mock can walk its conditions. */\nconst isRecord = (value: unknown): value is Record<string, unknown> => typeof value === 'object' && value !== null;\n\n/**\n * Evaluates an `Email/query` filter against an email — enough of RFC 8621 §4.4.1 for the sync's\n * generated filter: the `after`/`before` window, `inMailbox`/`inMailboxOtherThan` folder scope, and\n * the AND/OR/NOT operator combinators. Unknown conditions are treated as matching.\n */\nconst matchesFilter = (email: JmapMail.Email, filter: unknown): boolean => {\n if (!isRecord(filter)) {\n return true;\n }\n if ('operator' in filter && Array.isArray(filter.conditions)) {\n const results = filter.conditions.map((condition) => matchesFilter(email, condition));\n if (filter.operator === 'OR') {\n return results.some(Boolean);\n }\n if (filter.operator === 'NOT') {\n return !results.some(Boolean);\n }\n return results.every(Boolean); // AND (the default the sync builds).\n }\n\n const receivedAt = new Date(email.receivedAt).getTime();\n // `after` is inclusive (>=), `before` is exclusive (<) — matches the sync's window semantics.\n if (typeof filter.after === 'string' && receivedAt < new Date(filter.after).getTime()) {\n return false;\n }\n if (typeof filter.before === 'string' && receivedAt >= new Date(filter.before).getTime()) {\n return false;\n }\n const mailboxIds = email.mailboxIds ? Object.keys(email.mailboxIds) : [];\n if (typeof filter.inMailbox === 'string' && !mailboxIds.includes(filter.inMailbox)) {\n return false;\n }\n if (Array.isArray(filter.inMailboxOtherThan)) {\n const excluded = filter.inMailboxOtherThan;\n if (!mailboxIds.some((id) => !excluded.includes(id))) {\n return false;\n }\n }\n return true;\n};\n\n/** Sorts emails by the query's primary sort (only `receivedAt` is used by the sync). */\nconst sortEmails = (\n emails: readonly JmapMail.Email[],\n sort: readonly { property: string; isAscending?: boolean }[] | undefined,\n): readonly JmapMail.Email[] => {\n const primary = sort?.[0];\n if (!primary || primary.property !== 'receivedAt') {\n return emails;\n }\n const direction = primary.isAscending ? 1 : -1;\n return [...emails].sort(\n (left, right) => direction * (new Date(left.receivedAt).getTime() - new Date(right.receivedAt).getTime()),\n );\n};\n\nexport class JmapMailApi extends Context.Tag('@dxos/plugin-inbox/JmapMailApi')<JmapMailApi, JmapMailApiService>() {\n /**\n * Live layer backed by the real JMAP HTTP client. Captures the auth/HTTP context once and provides\n * it to each request, so the resulting service methods carry no requirements. Requires an\n * `HttpClient` and a `JmapCredentials` (e.g. {@link JmapCredentials.fromConnection}) to be available\n * where it is provided.\n */\n static readonly Live: Layer.Layer<JmapMailApi, never, Requirements> = Layer.effect(\n JmapMailApi,\n Effect.gen(function* () {\n const context = yield* Effect.context<Requirements>();\n return JmapMailApi.of({\n getSession: Effect.provide(Jmap.getSession, context),\n mailboxGet: (target) => Effect.provide(JmapMail.mailboxGet(target), context),\n emailQuery: (target, options) => Effect.provide(JmapMail.emailQuery(target, options), context),\n emailGet: (target, ids, properties) => Effect.provide(JmapMail.emailGet(target, ids, properties), context),\n emailChanges: (target, sinceState, maxChanges) =>\n Effect.provide(JmapMail.emailChanges(target, sinceState, maxChanges), context),\n downloadBlob: (target, blobId, options) =>\n Effect.provide(JmapMail.downloadBlob(target, blobId, options), context),\n identityGet: (target) => Effect.provide(JmapMail.identityGet(target), context),\n emailSetUpdate: (target, emailId, patch) =>\n Effect.provide(JmapMail.emailSetUpdate(target, emailId, patch), context),\n submitEmail: (target, args) => Effect.provide(JmapMail.submitEmail(target, args), context),\n });\n }),\n );\n\n /**\n * Zero-dependency mock backed by an in-memory {@link JmapDataset} — for unit tests that drive the\n * real sync pipeline with no live account. `emailQuery` honours the query filter (`after`/`before`\n * window + folder scope), the `receivedAt` sort, and `position`/`limit` pagination, so the sync's\n * window walk and pagination exercise realistically. Write methods are unsupported (die).\n */\n static readonly mock = (dataset: JmapDataset): Layer.Layer<JmapMailApi> => {\n const byId = new Map(dataset.emails.map((email) => [email.id, email]));\n return Layer.succeed(\n JmapMailApi,\n JmapMailApi.of({\n getSession: Effect.succeed(dataset.session),\n mailboxGet: () => Effect.succeed({ list: dataset.folders }),\n emailQuery: (_target, options = {}) =>\n Effect.sync(() => {\n const matching = sortEmails(\n dataset.emails.filter((email) => matchesFilter(email, options.filter)),\n options.sort,\n );\n const position = options.position ?? 0;\n const limit = options.limit ?? matching.length;\n const page = matching.slice(position, position + limit);\n return { position, total: matching.length, ids: page.map((email) => email.id) };\n }),\n emailGet: (_target, ids) =>\n Effect.sync(() => ({\n list: ids.map((id) => byId.get(id)).filter(Predicate.isNotNullable),\n state: dataset.state,\n })),\n // Chains the change-log steps from `sinceState`, honoring `maxChanges`: it stops before a step\n // that would exceed the budget (always including at least one, so a single oversized step still\n // makes progress), sets `hasMoreChanges`, and returns the intermediate `newState` as the chunk\n // boundary to resume from. An unknown/evicted state (no chain match, not already the latest) fails\n // with `cannotCalculateChanges`, matching a server past its retention window.\n emailChanges: (_target, sinceState, maxChanges) =>\n Effect.gen(function* () {\n const latest = dataset.state ?? sinceState;\n if (sinceState === latest) {\n return {\n oldState: sinceState,\n newState: latest,\n hasMoreChanges: false,\n created: [],\n updated: [],\n destroyed: [],\n };\n }\n const created: string[] = [];\n const updated: string[] = [];\n const destroyed: string[] = [];\n let chain = sinceState;\n let matched = false;\n let hasMoreChanges = false;\n for (const step of dataset.changeLog ?? []) {\n if (step.sinceState === chain) {\n const stepSize =\n (step.created?.length ?? 0) + (step.updated?.length ?? 0) + (step.destroyed?.length ?? 0);\n // Stop before overflowing the budget, but never before the first step (guarantees progress).\n if (\n maxChanges !== undefined &&\n chain !== sinceState &&\n created.length + updated.length + destroyed.length + stepSize > maxChanges\n ) {\n hasMoreChanges = true;\n break;\n }\n matched = true;\n created.push(...(step.created ?? []));\n updated.push(...(step.updated ?? []));\n destroyed.push(...(step.destroyed ?? []));\n chain = step.newState;\n }\n }\n if (!matched) {\n return yield* Effect.fail(\n new JmapApiError(undefined, 'Cannot calculate changes', 'cannotCalculateChanges'),\n );\n }\n return { oldState: sinceState, newState: chain, hasMoreChanges, created, updated, destroyed };\n }),\n downloadBlob: (_target, blobId) => {\n const bytes = dataset.blobs?.[blobId];\n return bytes\n ? Effect.succeed(bytes)\n : Effect.die(new Error(`mock JmapMailApi: blob not in dataset: ${blobId}`));\n },\n identityGet: () => Effect.die(new Error('mock JmapMailApi: identityGet not supported')),\n emailSetUpdate: () => Effect.die(new Error('mock JmapMailApi: emailSetUpdate not supported')),\n submitEmail: () => Effect.die(new Error('mock JmapMailApi: submitEmail not supported')),\n }),\n );\n };\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import { t as generateJmapDataset } from "../chunk-jmap-fixtures.mjs";
2
+ export { generateJmapDataset };
@@ -0,0 +1,19 @@
1
+ import { JmapMailApi } from "./services.mjs";
2
+ import { t as generateJmapDataset } from "./chunk-jmap-fixtures.mjs";
3
+ import { t as jmapMailSyncProvider } from "./chunk-sync-provider.mjs";
4
+ import * as Effect from "effect/Effect";
5
+ import * as Layer from "effect/Layer";
6
+ import { runMailSync } from "@dxos/plugin-inbox/sync";
7
+ import { ambientSyncServices } from "@dxos/plugin-inbox/testing/sync";
8
+ //#region src/testing/sync-fixture.ts
9
+ /**
10
+ * Test entry point for the JMAP sync — `runMailSync` with this provider's layer, leaving the API for the
11
+ * test to supply (mock or Live). Production inlines the same wiring in the sync handler.
12
+ */
13
+ var runJmapSync = (options) => runMailSync(options).pipe(Effect.provide(jmapMailSyncProvider()), Effect.withSpan("jmap-sync"));
14
+ /** The ambient services {@link runJmapSync} requires, backed by a mock JMAP API + a real db. */
15
+ var jmapSyncTestServices = (db, dataset, options) => Layer.mergeAll(JmapMailApi.mock(dataset), ambientSyncServices(db, options));
16
+ //#endregion
17
+ export { generateJmapDataset, jmapSyncTestServices, runJmapSync };
18
+
19
+ //# sourceMappingURL=testing.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testing.mjs","names":[],"sources":["../../src/testing/sync-fixture.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\nimport * as Effect from 'effect/Effect';\nimport * as Layer from 'effect/Layer';\n\nimport type * as Capability from '@dxos/app-framework/Capability';\nimport type * as Operation from '@dxos/compute/Operation';\nimport type * as Trace from '@dxos/compute/Trace';\nimport { type Database } from '@dxos/echo';\nimport { type EntityNotFoundError } from '@dxos/echo/Err';\nimport { type Resolver } from '@dxos/extractor';\nimport { MailSyncError, type RunMailSyncOptions, runMailSync } from '@dxos/plugin-inbox/sync';\nimport { ambientSyncServices } from '@dxos/plugin-inbox/testing/sync';\n\nimport { jmapMailSyncProvider } from '../operations/mail/sync/sync-provider';\nimport { type JmapDataset, JmapMailApi } from '../services';\n\n/**\n * Test entry point for the JMAP sync — `runMailSync` with this provider's layer, leaving the API for the\n * test to supply (mock or Live). Production inlines the same wiring in the sync handler.\n */\nexport const runJmapSync = (\n options: RunMailSyncOptions,\n): Effect.Effect<\n { newMessages: number },\n MailSyncError | EntityNotFoundError,\n Database.Service | Capability.Service | Operation.Service | Trace.TraceService | JmapMailApi | Resolver\n> => runMailSync(options).pipe(Effect.provide(jmapMailSyncProvider()), Effect.withSpan('jmap-sync'));\n\n/** The ambient services {@link runJmapSync} requires, backed by a mock JMAP API + a real db. */\nexport const jmapSyncTestServices = (\n db: Database.Database,\n dataset: JmapDataset,\n options?: { traceLayer?: Layer.Layer<Trace.TraceService> },\n): Layer.Layer<\n JmapMailApi | Database.Service | Resolver | Capability.Service | Trace.TraceService | Operation.Service\n> => Layer.mergeAll(JmapMailApi.mock(dataset), ambientSyncServices(db, options));\n"],"mappings":";;;;;;;;;;;;AAuBA,IAAa,eACX,YAKG,YAAY,OAAO,CAAC,CAAC,KAAK,OAAO,QAAQ,qBAAqB,CAAC,GAAG,OAAO,SAAS,WAAW,CAAC;;AAGnG,IAAa,wBACX,IACA,SACA,YAGG,MAAM,SAAS,YAAY,KAAK,OAAO,GAAG,oBAAoB,IAAI,OAAO,CAAC"}
@@ -0,0 +1,7 @@
1
+ import { meta } from "#meta";
2
+ //#region src/translations.ts
3
+ var translations = [{ "en-US": { [meta.profile.key]: { "plugin.name": "JMAP" } } }];
4
+ //#endregion
5
+ export { translations };
6
+
7
+ //# sourceMappingURL=translations.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"translations.mjs","names":[],"sources":["../../src/translations.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\nimport { meta } from '#meta';\n\nexport const translations = [\n {\n 'en-US': {\n [meta.profile.key]: {\n 'plugin.name': 'JMAP',\n },\n },\n },\n];\n"],"mappings":";;AAMA,IAAa,eAAe,CAC1B,EACE,SAAS,GACN,KAAK,QAAQ,MAAM,EAClB,eAAe,OACjB,EACF,EACF,CACF"}
@@ -0,0 +1,28 @@
1
+ declare const _default: {
2
+ readonly plugin: {
3
+ readonly key: string;
4
+ readonly name: string;
5
+ readonly description?: string | undefined;
6
+ readonly author?: string | undefined;
7
+ readonly homePage?: string | undefined;
8
+ readonly source?: string | undefined;
9
+ readonly screenshots?: readonly {
10
+ readonly light?: string | undefined;
11
+ readonly dark?: string | undefined;
12
+ }[] | undefined;
13
+ readonly tags?: readonly string[] | undefined;
14
+ readonly icon?: {
15
+ readonly key: string;
16
+ readonly hue?: string | undefined;
17
+ } | undefined;
18
+ readonly spec?: string | undefined;
19
+ readonly dependsOn?: readonly string[] | undefined;
20
+ };
21
+ readonly publish?: {
22
+ readonly buildCommand?: string | undefined;
23
+ readonly outputDirectory?: string | undefined;
24
+ readonly assetBaseUrl?: string | undefined;
25
+ } | undefined;
26
+ };
27
+ export default _default;
28
+ //# sourceMappingURL=dx.config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dx.config.d.ts","sourceRoot":"","sources":["../../dx.config.ts"],"names":[],"mappings":""}
@@ -0,0 +1,9 @@
1
+ import * as Plugin from '@dxos/app-framework/Plugin';
2
+ /**
3
+ * Headless mail provider: contributes the JMAP connector, its send-routing entry, and the sync/send/
4
+ * materialize handlers. Every UI surface, the `Mailbox` type, and the sync harness these handlers run
5
+ * against belong to `@dxos/plugin-inbox`.
6
+ */
7
+ export declare const JmapPlugin: Plugin.PluginFactory<void>;
8
+ export default JmapPlugin;
9
+ //# sourceMappingURL=JmapPlugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"JmapPlugin.d.ts","sourceRoot":"","sources":["../../../src/JmapPlugin.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,MAAM,MAAM,4BAA4B,CAAC;AAOrD;;;;GAIG;AACH,eAAO,MAAM,UAAU,4BAMtB,CAAC;eAEa,UAAU"}
@@ -0,0 +1,42 @@
1
+ import * as HttpClient from '@effect/platform/HttpClient';
2
+ import * as Effect from 'effect/Effect';
3
+ import * as Schedule from 'effect/Schedule';
4
+ import * as Schema from 'effect/Schema';
5
+ import { JmapApiError } from '../../errors';
6
+ import { JmapCredentials } from '../../services/jmap-credentials';
7
+ import { Response, Session } from './types';
8
+ /** Shared per-attempt timeout/retry policy for JMAP HTTP requests (session, batched API, blob download). */
9
+ export declare const REQUEST_TIMEOUT = "10 seconds";
10
+ export declare const REQUEST_RETRY: Schedule.Schedule<number, unknown, never>;
11
+ export declare const shouldRetry: (error: unknown) => boolean;
12
+ /** Capability set for read/write mail requests. */
13
+ export declare const MAIL_CAPABILITIES: readonly ['urn:ietf:params:jmap:core', 'urn:ietf:params:jmap:mail'];
14
+ /** Capability set including submission, required to send mail. */
15
+ export declare const SUBMISSION_CAPABILITIES: readonly ["urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail", "urn:ietf:params:jmap:submission"];
16
+ /** A single `[methodName, args, callId]` invocation in a batched request. */
17
+ export type MethodCall = readonly [string, unknown, string];
18
+ /** A batched JMAP API request body (RFC 8620 §3.3). */
19
+ export type RequestBody = {
20
+ readonly using: readonly string[];
21
+ readonly methodCalls: readonly MethodCall[];
22
+ };
23
+ /**
24
+ * Discovers the JMAP session: `GET https://${host}/.well-known/jmap` (the well-known URL
25
+ * 302-redirects to the session resource, which `fetch` follows). Yields `apiUrl` +
26
+ * `primaryAccounts`. Tracer disabled (Effect + CORS: https://github.com/Effect-TS/effect/issues/4568).
27
+ */
28
+ export declare const getSession: Effect.Effect<Session, JmapApiError, HttpClient.HttpClient | JmapCredentials>;
29
+ /**
30
+ * Issues a batched JMAP API request (single POST to the session `apiUrl`) and decodes the response
31
+ * envelope. Read requests retry on transient failure; pass `{ retry: false }` for non-idempotent
32
+ * writes (draft create + submission) so a retry can't double-send.
33
+ */
34
+ export declare const jmapRequest: (apiUrl: string, body: RequestBody, options?: {
35
+ retry?: boolean;
36
+ }) => Effect.Effect<Response, JmapApiError, HttpClient.HttpClient | JmapCredentials>;
37
+ /**
38
+ * Extracts and decodes the result for a given `callId` from a response envelope. A method-level
39
+ * `["error", …]` response (or a decode failure) becomes a typed {@link JmapApiError}.
40
+ */
41
+ export declare const getMethodResponse: <A, I, R>(response: Response, callId: string, schema: Schema.Schema<A, I, R>) => Effect.Effect<A, JmapApiError, R>;
42
+ //# sourceMappingURL=api.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../../../../src/apis/Jmap/api.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,UAAU,MAAM,6BAA6B,CAAC;AAG1D,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,QAAQ,MAAM,iBAAiB,CAAC;AAC5C,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAIxC,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAClE,OAAO,EAAe,QAAQ,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAEzD,4GAA4G;AAC5G,eAAO,MAAM,eAAe,eAAe,CAAC;AAC5C,eAAO,MAAM,aAAa,2CAAyE,CAAC;AAIpG,eAAO,MAAM,WAAW,UAAW,OAAO,KAAG,OACgE,CAAC;AAE9G,mDAAmD;AACnD,eAAO,MAAM,iBAAiB,YAAI,2BAA2B,EAAE,2BAA2B,CAAU,CAAC;AACrG,kEAAkE;AAClE,eAAO,MAAM,uBAAuB,wGAAqE,CAAC;AAE1G,6EAA6E;AAC7E,MAAM,MAAM,UAAU,GAAG,SAAS,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;AAE5D,uDAAuD;AACvD,MAAM,MAAM,WAAW,GAAG;IACxB,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,QAAQ,CAAC,WAAW,EAAE,SAAS,UAAU,EAAE,CAAC;CAC7C,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,UAAU,EAAE,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,YAAY,EAAE,UAAU,CAAC,UAAU,GAAG,eAAe,CAiBpG,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,WAAW,WACd,MAAM,QACR,WAAW,YACR;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,KAC3B,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,YAAY,EAAE,UAAU,CAAC,UAAU,GAAG,eAAe,CAiB5E,CAAC;AAEL;;;GAGG;AACH,eAAO,MAAM,iBAAiB,GAAI,CAAC,EAAE,CAAC,EAAE,CAAC,YAC7B,QAAQ,UACV,MAAM,UACN,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,KAC7B,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,CAiBlC,CAAC"}
@@ -0,0 +1,3 @@
1
+ export * from './api';
2
+ export * from './types';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/apis/Jmap/index.ts"],"names":[],"mappings":"AAIA,cAAc,OAAO,CAAC;AACtB,cAAc,SAAS,CAAC"}
@@ -0,0 +1,65 @@
1
+ import * as Schema from 'effect/Schema';
2
+ /** Session object returned from `https://${host}/.well-known/jmap` (RFC 8620 §2). */
3
+ export declare const Session: Schema.Struct<{
4
+ /** Endpoint for all subsequent API requests. */
5
+ apiUrl: typeof Schema.String;
6
+ /**
7
+ * URI Template (RFC 6570) for blob downloads, with `accountId`/`blobId`/`type`/`name` variables
8
+ * (RFC 8620 §6.2). Required, matching the spec — every real JMAP session advertises one.
9
+ */
10
+ downloadUrl: typeof Schema.String;
11
+ /** Authenticated username (used to seed the connection account when the form leaves it blank). */
12
+ username: Schema.optional<typeof Schema.String>;
13
+ /** Capability urn → primary account id (e.g. `urn:ietf:params:jmap:mail`). */
14
+ primaryAccounts: Schema.Record$<typeof Schema.String, typeof Schema.String>;
15
+ accounts: Schema.optional<Schema.Record$<typeof Schema.String, typeof Schema.Unknown>>;
16
+ capabilities: Schema.optional<Schema.Record$<typeof Schema.String, typeof Schema.Unknown>>;
17
+ state: Schema.optional<typeof Schema.String>;
18
+ }>;
19
+ export type Session = Schema.Schema.Type<typeof Session>;
20
+ /** A method-level error response: `["error", { type, description }, callId]` (RFC 8620 §3.6.1). */
21
+ export declare const MethodError: Schema.Struct<{
22
+ type: typeof Schema.String;
23
+ description: Schema.optional<Schema.NullOr<typeof Schema.String>>;
24
+ }>;
25
+ export type MethodError = Schema.Schema.Type<typeof MethodError>;
26
+ /** A single `[name, args, callId]` invocation result; `args` is decoded per-method by the caller. */
27
+ export declare const MethodResponse: Schema.Tuple<[typeof Schema.String, typeof Schema.Unknown, typeof Schema.String]>;
28
+ export type MethodResponse = Schema.Schema.Type<typeof MethodResponse>;
29
+ /** The top-level response envelope of a batched API request (RFC 8620 §3.3). */
30
+ export declare const Response: Schema.Struct<{
31
+ methodResponses: Schema.Array$<Schema.Tuple<[typeof Schema.String, typeof Schema.Unknown, typeof Schema.String]>>;
32
+ sessionState: Schema.optional<typeof Schema.String>;
33
+ }>;
34
+ export type Response = Schema.Schema.Type<typeof Response>;
35
+ /** A single filter condition; all present properties must match. */
36
+ export type FilterCondition = {
37
+ readonly inMailbox?: string;
38
+ readonly inMailboxOtherThan?: readonly string[];
39
+ readonly before?: string;
40
+ readonly after?: string;
41
+ readonly minSize?: number;
42
+ readonly maxSize?: number;
43
+ readonly hasKeyword?: string;
44
+ readonly notKeyword?: string;
45
+ readonly allInThreadHaveKeyword?: string;
46
+ readonly someInThreadHaveKeyword?: string;
47
+ readonly noneInThreadHaveKeyword?: string;
48
+ readonly text?: string;
49
+ readonly from?: string;
50
+ readonly to?: string;
51
+ readonly cc?: string;
52
+ readonly bcc?: string;
53
+ readonly subject?: string;
54
+ readonly body?: string;
55
+ readonly header?: readonly string[];
56
+ readonly hasAttachment?: boolean;
57
+ };
58
+ /** Combines sub-filters (RFC 8620 §5.5). */
59
+ export type FilterOperator = {
60
+ readonly operator: 'AND' | 'OR' | 'NOT';
61
+ readonly conditions: readonly Filter[];
62
+ };
63
+ /** An `Email/query` filter: a single condition or an AND/OR/NOT combination. */
64
+ export type Filter = FilterCondition | FilterOperator;
65
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../src/apis/Jmap/types.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAOxC,qFAAqF;AACrF,eAAO,MAAM,OAAO;IAClB,gDAAgD;;IAEhD;;;OAGG;;IAEH,kGAAkG;;IAElG,8EAA8E;;;;;EAK9E,CAAC;AACH,MAAM,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,OAAO,CAAC,CAAC;AAEzD,mGAAmG;AACnG,eAAO,MAAM,WAAW;;;EAGtB,CAAC;AACH,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,WAAW,CAAC,CAAC;AAEjE,qGAAqG;AACrG,eAAO,MAAM,cAAc,mFAA6D,CAAC;AACzF,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,cAAc,CAAC,CAAC;AAEvE,gFAAgF;AAChF,eAAO,MAAM,QAAQ;;;EAGnB,CAAC;AACH,MAAM,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,QAAQ,CAAC,CAAC;AAQ3D,oEAAoE;AACpE,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,kBAAkB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAChD,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,sBAAsB,CAAC,EAAE,MAAM,CAAC;IACzC,QAAQ,CAAC,uBAAuB,CAAC,EAAE,MAAM,CAAC;IAC1C,QAAQ,CAAC,uBAAuB,CAAC,EAAE,MAAM,CAAC;IAC1C,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,QAAQ,CAAC,aAAa,CAAC,EAAE,OAAO,CAAC;CAClC,CAAC;AAEF,4CAA4C;AAC5C,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,CAAC,QAAQ,EAAE,KAAK,GAAG,IAAI,GAAG,KAAK,CAAC;IACxC,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;CACxC,CAAC;AAEF,gFAAgF;AAChF,MAAM,MAAM,MAAM,GAAG,eAAe,GAAG,cAAc,CAAC"}