@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.
- package/LICENSE +105 -0
- package/dist/lib/JmapPlugin.mjs +16 -0
- package/dist/lib/JmapPlugin.mjs.map +1 -0
- package/dist/lib/apis.mjs +2 -0
- package/dist/lib/capabilities.mjs +17 -0
- package/dist/lib/capabilities.mjs.map +1 -0
- package/dist/lib/chunk-apis.mjs +756 -0
- package/dist/lib/chunk-apis.mjs.map +1 -0
- package/dist/lib/chunk-connector.mjs +111 -0
- package/dist/lib/chunk-connector.mjs.map +1 -0
- package/dist/lib/chunk-constants.mjs +23 -0
- package/dist/lib/chunk-constants.mjs.map +1 -0
- package/dist/lib/chunk-errors.mjs +42 -0
- package/dist/lib/chunk-errors.mjs.map +1 -0
- package/dist/lib/chunk-handler.mjs +27 -0
- package/dist/lib/chunk-handler.mjs.map +1 -0
- package/dist/lib/chunk-jmap-fixtures.mjs +102 -0
- package/dist/lib/chunk-jmap-fixtures.mjs.map +1 -0
- package/dist/lib/chunk-mail-send.mjs +17 -0
- package/dist/lib/chunk-mail-send.mjs.map +1 -0
- package/dist/lib/chunk-meta.mjs +37 -0
- package/dist/lib/chunk-meta.mjs.map +1 -0
- package/dist/lib/chunk-operation-handler.mjs +12 -0
- package/dist/lib/chunk-operation-handler.mjs.map +1 -0
- package/dist/lib/chunk-send.mjs +98 -0
- package/dist/lib/chunk-send.mjs.map +1 -0
- package/dist/lib/chunk-sync-provider.mjs +511 -0
- package/dist/lib/chunk-sync-provider.mjs.map +1 -0
- package/dist/lib/chunk-sync.mjs +37 -0
- package/dist/lib/chunk-sync.mjs.map +1 -0
- package/dist/lib/index.mjs +4 -0
- package/dist/lib/meta.mjs +2 -0
- package/dist/lib/operations.mjs +13 -0
- package/dist/lib/operations.mjs.map +1 -0
- package/dist/lib/plugin.mjs +9 -0
- package/dist/lib/plugin.mjs.map +1 -0
- package/dist/lib/services.mjs +139 -0
- package/dist/lib/services.mjs.map +1 -0
- package/dist/lib/testing/node.mjs +2 -0
- package/dist/lib/testing.mjs +19 -0
- package/dist/lib/testing.mjs.map +1 -0
- package/dist/lib/translations.mjs +7 -0
- package/dist/lib/translations.mjs.map +1 -0
- package/dist/types/dx.config.d.ts +28 -0
- package/dist/types/dx.config.d.ts.map +1 -0
- package/dist/types/src/JmapPlugin.d.ts +9 -0
- package/dist/types/src/JmapPlugin.d.ts.map +1 -0
- package/dist/types/src/apis/Jmap/api.d.ts +42 -0
- package/dist/types/src/apis/Jmap/api.d.ts.map +1 -0
- package/dist/types/src/apis/Jmap/index.d.ts +3 -0
- package/dist/types/src/apis/Jmap/index.d.ts.map +1 -0
- package/dist/types/src/apis/Jmap/types.d.ts +65 -0
- package/dist/types/src/apis/Jmap/types.d.ts.map +1 -0
- package/dist/types/src/apis/JmapMail/api.d.ts +223 -0
- package/dist/types/src/apis/JmapMail/api.d.ts.map +1 -0
- package/dist/types/src/apis/JmapMail/index.d.ts +4 -0
- package/dist/types/src/apis/JmapMail/index.d.ts.map +1 -0
- package/dist/types/src/apis/JmapMail/query.d.ts +26 -0
- package/dist/types/src/apis/JmapMail/query.d.ts.map +1 -0
- package/dist/types/src/apis/JmapMail/query.test.d.ts +2 -0
- package/dist/types/src/apis/JmapMail/query.test.d.ts.map +1 -0
- package/dist/types/src/apis/JmapMail/types.d.ts +290 -0
- package/dist/types/src/apis/JmapMail/types.d.ts.map +1 -0
- package/dist/types/src/apis/index.d.ts +3 -0
- package/dist/types/src/apis/index.d.ts.map +1 -0
- package/dist/types/src/apis/jmap-api.test.d.ts +2 -0
- package/dist/types/src/apis/jmap-api.test.d.ts.map +1 -0
- package/dist/types/src/capabilities/connector.d.ts +5 -0
- package/dist/types/src/capabilities/connector.d.ts.map +1 -0
- package/dist/types/src/capabilities/credential-form.d.ts +37 -0
- package/dist/types/src/capabilities/credential-form.d.ts.map +1 -0
- package/dist/types/src/capabilities/credential-form.test.d.ts +2 -0
- package/dist/types/src/capabilities/credential-form.test.d.ts.map +1 -0
- package/dist/types/src/capabilities/index.d.ts +5 -0
- package/dist/types/src/capabilities/index.d.ts.map +1 -0
- package/dist/types/src/capabilities/mail-send.d.ts +6 -0
- package/dist/types/src/capabilities/mail-send.d.ts.map +1 -0
- package/dist/types/src/capabilities/operation-handler.d.ts +5 -0
- package/dist/types/src/capabilities/operation-handler.d.ts.map +1 -0
- package/dist/types/src/constants.d.ts +19 -0
- package/dist/types/src/constants.d.ts.map +1 -0
- package/dist/types/src/errors.d.ts +103 -0
- package/dist/types/src/errors.d.ts.map +1 -0
- package/dist/types/src/index.d.ts +4 -0
- package/dist/types/src/index.d.ts.map +1 -0
- package/dist/types/src/meta.d.ts +33 -0
- package/dist/types/src/meta.d.ts.map +1 -0
- package/dist/types/src/operations/index.d.ts +3 -0
- package/dist/types/src/operations/index.d.ts.map +1 -0
- package/dist/types/src/operations/mail/mapper.d.ts +49 -0
- package/dist/types/src/operations/mail/mapper.d.ts.map +1 -0
- package/dist/types/src/operations/mail/materialize/handler.d.ts +11 -0
- package/dist/types/src/operations/mail/materialize/handler.d.ts.map +1 -0
- package/dist/types/src/operations/mail/send/handler.d.ts +4 -0
- package/dist/types/src/operations/mail/send/handler.d.ts.map +1 -0
- package/dist/types/src/operations/mail/send/index.d.ts +2 -0
- package/dist/types/src/operations/mail/send/index.d.ts.map +1 -0
- package/dist/types/src/operations/mail/sync/handler.d.ts +4 -0
- package/dist/types/src/operations/mail/sync/handler.d.ts.map +1 -0
- package/dist/types/src/operations/mail/sync/index.d.ts +3 -0
- package/dist/types/src/operations/mail/sync/index.d.ts.map +1 -0
- package/dist/types/src/operations/mail/sync/mapper.test.d.ts +2 -0
- package/dist/types/src/operations/mail/sync/mapper.test.d.ts.map +1 -0
- package/dist/types/src/operations/mail/sync/sync-e2e.test.d.ts +2 -0
- package/dist/types/src/operations/mail/sync/sync-e2e.test.d.ts.map +1 -0
- package/dist/types/src/operations/mail/sync/sync-provider.d.ts +11 -0
- package/dist/types/src/operations/mail/sync/sync-provider.d.ts.map +1 -0
- package/dist/types/src/operations/mail/sync/sync.test.d.ts +2 -0
- package/dist/types/src/operations/mail/sync/sync.test.d.ts.map +1 -0
- package/dist/types/src/operations/mail/sync/system-tags.d.ts +14 -0
- package/dist/types/src/operations/mail/sync/system-tags.d.ts.map +1 -0
- package/dist/types/src/operations/mail/tags.d.ts +12 -0
- package/dist/types/src/operations/mail/tags.d.ts.map +1 -0
- package/dist/types/src/plugin.d.ts +4 -0
- package/dist/types/src/plugin.d.ts.map +1 -0
- package/dist/types/src/services/index.d.ts +3 -0
- package/dist/types/src/services/index.d.ts.map +1 -0
- package/dist/types/src/services/jmap-credentials.d.ts +39 -0
- package/dist/types/src/services/jmap-credentials.d.ts.map +1 -0
- package/dist/types/src/services/jmap-mail-api.d.ts +109 -0
- package/dist/types/src/services/jmap-mail-api.d.ts.map +1 -0
- package/dist/types/src/testing/index.d.ts +5 -0
- package/dist/types/src/testing/index.d.ts.map +1 -0
- package/dist/types/src/testing/jmap-fixtures.d.ts +26 -0
- package/dist/types/src/testing/jmap-fixtures.d.ts.map +1 -0
- package/dist/types/src/testing/jmap-fixtures.test.d.ts +2 -0
- package/dist/types/src/testing/jmap-fixtures.test.d.ts.map +1 -0
- package/dist/types/src/testing/node.d.ts +4 -0
- package/dist/types/src/testing/node.d.ts.map +1 -0
- package/dist/types/src/testing/sync-fixture.d.ts +22 -0
- package/dist/types/src/testing/sync-fixture.d.ts.map +1 -0
- package/dist/types/src/translations.d.ts +8 -0
- package/dist/types/src/translations.d.ts.map +1 -0
- package/dist/types/tsconfig.tsbuildinfo +1 -0
- package/dx.config.ts +32 -0
- package/package.json +121 -0
- package/src/JmapPlugin.ts +25 -0
- package/src/apis/Jmap/api.ts +138 -0
- package/src/apis/Jmap/index.ts +6 -0
- package/src/apis/Jmap/types.ts +86 -0
- package/src/apis/JmapMail/api.ts +324 -0
- package/src/apis/JmapMail/index.ts +7 -0
- package/src/apis/JmapMail/query.test.ts +181 -0
- package/src/apis/JmapMail/query.ts +231 -0
- package/src/apis/JmapMail/types.ts +196 -0
- package/src/apis/index.ts +8 -0
- package/src/apis/jmap-api.test.ts +261 -0
- package/src/capabilities/connector.ts +44 -0
- package/src/capabilities/credential-form.test.ts +69 -0
- package/src/capabilities/credential-form.ts +113 -0
- package/src/capabilities/index.ts +24 -0
- package/src/capabilities/mail-send.ts +21 -0
- package/src/capabilities/operation-handler.ts +16 -0
- package/src/constants.ts +24 -0
- package/src/errors.ts +38 -0
- package/src/index.ts +7 -0
- package/src/meta.ts +9 -0
- package/src/operations/index.ts +13 -0
- package/src/operations/mail/mapper.ts +191 -0
- package/src/operations/mail/materialize/handler.ts +41 -0
- package/src/operations/mail/send/handler.ts +116 -0
- package/src/operations/mail/send/index.ts +5 -0
- package/src/operations/mail/sync/handler.ts +52 -0
- package/src/operations/mail/sync/index.ts +7 -0
- package/src/operations/mail/sync/mapper.test.ts +150 -0
- package/src/operations/mail/sync/sync-e2e.test.ts +63 -0
- package/src/operations/mail/sync/sync-provider.ts +481 -0
- package/src/operations/mail/sync/sync.test.ts +766 -0
- package/src/operations/mail/sync/system-tags.ts +24 -0
- package/src/operations/mail/tags.ts +29 -0
- package/src/plugin.ts +11 -0
- package/src/services/index.ts +6 -0
- package/src/services/jmap-credentials.ts +61 -0
- package/src/services/jmap-mail-api.ts +294 -0
- package/src/testing/index.ts +8 -0
- package/src/testing/jmap-fixtures.test.ts +107 -0
- package/src/testing/jmap-fixtures.ts +105 -0
- package/src/testing/node.ts +12 -0
- package/src/testing/sync-fixture.ts +39 -0
- package/src/translations.ts +15 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chunk-apis.mjs","names":[],"sources":["../../src/services/jmap-credentials.ts","../../src/apis/Jmap/types.ts","../../src/apis/Jmap/api.ts","../../src/apis/Jmap/index.ts","../../src/apis/JmapMail/types.ts","../../src/apis/JmapMail/api.ts","../../src/apis/JmapMail/query.ts","../../src/apis/JmapMail/index.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\nimport * as Context from 'effect/Context';\nimport * as Effect from 'effect/Effect';\nimport * as Layer from 'effect/Layer';\n\nimport { Database, type Ref } from '@dxos/echo';\nimport { type AccessToken } from '@dxos/link';\nimport { log } from '@dxos/log';\nimport * as Connection from '@dxos/plugin-connector/Connection';\n\n/**\n * Credentials needed to talk to a JMAP server: the server `host` (used to discover the session at\n * `https://${host}/.well-known/jmap`), the optional `account` (email/username for display and\n * identity matching), and the Bearer `token`.\n */\nexport type Credentials = {\n readonly host: string;\n readonly account: string | undefined;\n readonly token: string;\n};\n\n/**\n * Service for accessing JMAP credentials.\n *\n * Mirrors `GoogleCredentials`: an operation invoked with a `Connection` composes\n * `fromConnection(ref)`; one invoked with an external-sync cursor composes\n * `fromAccessToken(cursor.spec.source)` directly (the cursor no longer relates to `Connection`).\n * Unlike Google, there is no database-credential fallback — a JMAP connection always carries host +\n * token in its token record.\n */\nexport class JmapCredentials extends Context.Tag('JmapCredentials')<JmapCredentials, Credentials>() {\n /** Creates a credentials layer from an AccessToken ref. Loads its `source` (host), `account`, `token`. */\n static fromAccessToken = (accessTokenRef: Ref.Ref<AccessToken.AccessToken>) =>\n Layer.effect(\n JmapCredentials,\n Effect.gen(function* () {\n const accessToken = yield* Database.load(accessTokenRef);\n log('using access token', { source: accessToken.source });\n return { host: accessToken.source, account: accessToken.account, token: accessToken.token };\n }),\n );\n\n /** Creates a credentials layer from a Connection ref. Loads its `accessToken`'s host/account/token. */\n static fromConnection = (connectionRef: Ref.Ref<Connection.Connection>) =>\n Layer.effect(\n JmapCredentials,\n Effect.gen(function* () {\n const connection = yield* Database.load(connectionRef);\n const accessToken = yield* Database.load(connection.accessToken);\n log('using connection access token', { source: accessToken.source });\n return { host: accessToken.source, account: accessToken.account, token: accessToken.token };\n }),\n );\n\n /** Creates a credentials layer from explicit values (credential-form validation and tests). */\n static fromValues = (values: { host: string; account?: string; token: string }) =>\n Layer.succeed(JmapCredentials, { host: values.host, account: values.account, token: values.token });\n}\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport * as Schema from 'effect/Schema';\n\n//\n// Shared JMAP core schemas (RFC 8620). These are capability-agnostic — they apply to mail, calendars,\n// and contacts alike.\n//\n\n/** Session object returned from `https://${host}/.well-known/jmap` (RFC 8620 §2). */\nexport const Session = Schema.Struct({\n /** Endpoint for all subsequent API requests. */\n apiUrl: Schema.String,\n /**\n * URI Template (RFC 6570) for blob downloads, with `accountId`/`blobId`/`type`/`name` variables\n * (RFC 8620 §6.2). Required, matching the spec — every real JMAP session advertises one.\n */\n downloadUrl: Schema.String,\n /** Authenticated username (used to seed the connection account when the form leaves it blank). */\n username: Schema.optional(Schema.String),\n /** Capability urn → primary account id (e.g. `urn:ietf:params:jmap:mail`). */\n primaryAccounts: Schema.Record({ key: Schema.String, value: Schema.String }),\n accounts: Schema.optional(Schema.Record({ key: Schema.String, value: Schema.Unknown })),\n capabilities: Schema.optional(Schema.Record({ key: Schema.String, value: Schema.Unknown })),\n state: Schema.optional(Schema.String),\n});\nexport type Session = Schema.Schema.Type<typeof Session>;\n\n/** A method-level error response: `[\"error\", { type, description }, callId]` (RFC 8620 §3.6.1). */\nexport const MethodError = Schema.Struct({\n type: Schema.String,\n description: Schema.optional(Schema.NullOr(Schema.String)),\n});\nexport type MethodError = Schema.Schema.Type<typeof MethodError>;\n\n/** A single `[name, args, callId]` invocation result; `args` is decoded per-method by the caller. */\nexport const MethodResponse = Schema.Tuple(Schema.String, Schema.Unknown, Schema.String);\nexport type MethodResponse = Schema.Schema.Type<typeof MethodResponse>;\n\n/** The top-level response envelope of a batched API request (RFC 8620 §3.3). */\nexport const Response = Schema.Struct({\n methodResponses: Schema.Array(MethodResponse),\n sessionState: Schema.optional(Schema.String),\n});\nexport type Response = Schema.Schema.Type<typeof Response>;\n\n//\n// `Email/query` filter (RFC 8621 §4.4.1). Constructed client-side (never decoded), so these are plain\n// types rather than Schemas. Shared here because future calendar/contacts filters share the same\n// AND/OR/NOT operator structure (RFC 8620 §5.5).\n//\n\n/** A single filter condition; all present properties must match. */\nexport type FilterCondition = {\n readonly inMailbox?: string;\n readonly inMailboxOtherThan?: readonly string[];\n readonly before?: string;\n readonly after?: string;\n readonly minSize?: number;\n readonly maxSize?: number;\n readonly hasKeyword?: string;\n readonly notKeyword?: string;\n readonly allInThreadHaveKeyword?: string;\n readonly someInThreadHaveKeyword?: string;\n readonly noneInThreadHaveKeyword?: string;\n readonly text?: string;\n readonly from?: string;\n readonly to?: string;\n readonly cc?: string;\n readonly bcc?: string;\n readonly subject?: string;\n readonly body?: string;\n readonly header?: readonly string[];\n readonly hasAttachment?: boolean;\n};\n\n/** Combines sub-filters (RFC 8620 §5.5). */\nexport type FilterOperator = {\n readonly operator: 'AND' | 'OR' | 'NOT';\n readonly conditions: readonly Filter[];\n};\n\n/** An `Email/query` filter: a single condition or an AND/OR/NOT combination. */\nexport type Filter = FilterCondition | FilterOperator;\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport * as HttpClient from '@effect/platform/HttpClient';\nimport * as HttpClientRequest from '@effect/platform/HttpClientRequest';\nimport * as HttpClientResponse from '@effect/platform/HttpClientResponse';\nimport * as Effect from 'effect/Effect';\nimport * as Schedule from 'effect/Schedule';\nimport * as Schema from 'effect/Schema';\n\nimport { withAuthorization } from '@dxos/compute-runtime';\n\nimport { JmapApiError } from '../../errors';\nimport { JmapCredentials } from '../../services/jmap-credentials';\nimport { MethodError, Response, Session } from './types';\n\n/** Shared per-attempt timeout/retry policy for JMAP HTTP requests (session, batched API, blob download). */\nexport const REQUEST_TIMEOUT = '10 seconds';\nexport const REQUEST_RETRY = Schedule.exponential(1_000).pipe(Schedule.compose(Schedule.recurs(3)));\n\n// Retry transient failures (network, 5xx, timeout) but not 4xx — a bad host/token or malformed\n// request will fail identically on retry, so retrying only adds latency.\nexport const shouldRetry = (error: unknown): boolean =>\n !(error instanceof JmapApiError && error.status !== undefined && error.status >= 400 && error.status < 500);\n\n/** Capability set for read/write mail requests. */\nexport const MAIL_CAPABILITIES = ['urn:ietf:params:jmap:core', 'urn:ietf:params:jmap:mail'] as const;\n/** Capability set including submission, required to send mail. */\nexport const SUBMISSION_CAPABILITIES = [...MAIL_CAPABILITIES, 'urn:ietf:params:jmap:submission'] as const;\n\n/** A single `[methodName, args, callId]` invocation in a batched request. */\nexport type MethodCall = readonly [string, unknown, string];\n\n/** A batched JMAP API request body (RFC 8620 §3.3). */\nexport type RequestBody = {\n readonly using: readonly string[];\n readonly methodCalls: readonly MethodCall[];\n};\n\n/**\n * Discovers the JMAP session: `GET https://${host}/.well-known/jmap` (the well-known URL\n * 302-redirects to the session resource, which `fetch` follows). Yields `apiUrl` +\n * `primaryAccounts`. Tracer disabled (Effect + CORS: https://github.com/Effect-TS/effect/issues/4568).\n */\nexport const getSession: Effect.Effect<Session, JmapApiError, HttpClient.HttpClient | JmapCredentials> = Effect.gen(\n function* () {\n const { host, token } = yield* JmapCredentials;\n const httpClient = yield* HttpClient.HttpClient.pipe(Effect.map(withAuthorization(token, 'Bearer')));\n const httpClientWithTracerDisabled = httpClient.pipe(HttpClient.withTracerDisabledWhen(() => true));\n\n return yield* HttpClientRequest.get(`https://${host}/.well-known/jmap`).pipe(\n HttpClientRequest.setHeader('accept', 'application/json'),\n httpClientWithTracerDisabled.execute,\n Effect.flatMap(decodeJsonOrFail(Session)),\n Effect.timeout(REQUEST_TIMEOUT),\n Effect.retry({ schedule: REQUEST_RETRY, while: shouldRetry }),\n Effect.scoped,\n Effect.withSpan('JmapGetSession'),\n Effect.mapError(asJmapApiError),\n );\n },\n);\n\n/**\n * Issues a batched JMAP API request (single POST to the session `apiUrl`) and decodes the response\n * envelope. Read requests retry on transient failure; pass `{ retry: false }` for non-idempotent\n * writes (draft create + submission) so a retry can't double-send.\n */\nexport const jmapRequest = (\n apiUrl: string,\n body: RequestBody,\n options: { retry?: boolean } = {},\n): Effect.Effect<Response, JmapApiError, HttpClient.HttpClient | JmapCredentials> =>\n Effect.gen(function* () {\n const { token } = yield* JmapCredentials;\n const httpClient = yield* HttpClient.HttpClient.pipe(Effect.map(withAuthorization(token, 'Bearer')));\n const httpClientWithTracerDisabled = httpClient.pipe(HttpClient.withTracerDisabledWhen(() => true));\n\n const executed = HttpClientRequest.post(apiUrl).pipe(\n HttpClientRequest.setHeader('accept', 'application/json'),\n HttpClientRequest.bodyUnsafeJson(body),\n httpClientWithTracerDisabled.execute,\n Effect.flatMap(decodeJsonOrFail(Response)),\n Effect.timeout(REQUEST_TIMEOUT),\n );\n\n return yield* (\n options.retry === false ? executed : executed.pipe(Effect.retry({ schedule: REQUEST_RETRY, while: shouldRetry }))\n ).pipe(Effect.scoped, Effect.withSpan('JmapApiRequest'), Effect.mapError(asJmapApiError));\n });\n\n/**\n * Extracts and decodes the result for a given `callId` from a response envelope. A method-level\n * `[\"error\", …]` response (or a decode failure) becomes a typed {@link JmapApiError}.\n */\nexport const getMethodResponse = <A, I, R>(\n response: Response,\n callId: string,\n schema: Schema.Schema<A, I, R>,\n): Effect.Effect<A, JmapApiError, R> => {\n const entry = response.methodResponses.find((methodResponse) => methodResponse[2] === callId);\n if (!entry) {\n return Effect.fail(new JmapApiError(undefined, `No method response for call ${callId}.`));\n }\n\n const [name, args] = entry;\n if (name === 'error') {\n return Schema.decodeUnknown(MethodError)(args).pipe(\n Effect.matchEffect({\n onFailure: () => Effect.fail(new JmapApiError(undefined, `JMAP method error (${callId}).`)),\n onSuccess: (error) => Effect.fail(new JmapApiError(undefined, error.description ?? error.type, error.type)),\n }),\n );\n }\n\n return Schema.decodeUnknown(schema)(args).pipe(Effect.mapError(asJmapApiError));\n};\n\n/** Decodes a 2xx JSON body against `schema`; a >=400 status fails with the (truncated) body text. */\nconst decodeJsonOrFail =\n <A, I, R>(schema: Schema.Schema<A, I, R>) =>\n (response: HttpClientResponse.HttpClientResponse): Effect.Effect<A, JmapApiError, R> =>\n Effect.gen(function* () {\n if (response.status >= 400) {\n const body = yield* response.text;\n return yield* Effect.fail(new JmapApiError(response.status, truncate(body)));\n }\n return yield* HttpClientResponse.schemaBodyJson(schema)(response);\n }).pipe(Effect.mapError(asJmapApiError));\n\n/** Collapses transport/decode failures (HttpClientError, ParseError, timeout) into a typed JmapApiError. */\nconst asJmapApiError = (error: unknown): JmapApiError =>\n error instanceof JmapApiError\n ? error\n : new JmapApiError(undefined, error instanceof Error ? error.message : String(error));\n\nconst truncate = (text: string, max = 500): string => (text.length > max ? `${text.slice(0, max)}…` : text);\n","//\n// Copyright 2026 DXOS.org\n//\n\nexport * from './api';\nexport * from './types';\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport * as Schema from 'effect/Schema';\n\n/** Structured email address — JMAP gives `{ name, email }` directly (no header parsing). */\nexport const EmailAddress = Schema.Struct({\n name: Schema.optional(Schema.NullOr(Schema.String)),\n email: Schema.String,\n});\nexport type EmailAddress = Schema.Schema.Type<typeof EmailAddress>;\n\n/** A folder. `role` is a well-known role (`inbox`, `drafts`, `sent`, `trash`, …) or null for custom folders. */\nexport const Mailbox = Schema.Struct({\n id: Schema.String,\n name: Schema.String,\n role: Schema.optional(Schema.NullOr(Schema.String)),\n parentId: Schema.optional(Schema.NullOr(Schema.String)),\n sortOrder: Schema.optional(Schema.Number),\n totalEmails: Schema.optional(Schema.Number),\n unreadEmails: Schema.optional(Schema.Number),\n});\nexport type Mailbox = Schema.Schema.Type<typeof Mailbox>;\n\n/** A decoded body part value (when fetched with `fetchTextBodyValues`). */\nexport const BodyValue = Schema.Struct({\n value: Schema.String,\n isEncodingProblem: Schema.optional(Schema.Boolean),\n isTruncated: Schema.optional(Schema.Boolean),\n});\nexport type BodyValue = Schema.Schema.Type<typeof BodyValue>;\n\n/** A body part reference (the `partId` keys into `Email.bodyValues`; `blobId` fetches raw bytes). */\nexport const EmailBodyPart = Schema.Struct({\n partId: Schema.optional(Schema.NullOr(Schema.String)),\n blobId: Schema.optional(Schema.NullOr(Schema.String)),\n size: Schema.optional(Schema.Number),\n type: Schema.optional(Schema.String),\n charset: Schema.optional(Schema.NullOr(Schema.String)),\n /** Attachment filename, present on parts listed in `Email.attachments`. */\n name: Schema.optional(Schema.NullOr(Schema.String)),\n /** MIME `Content-Disposition` (`attachment` or `inline`), present on parts listed in `Email.attachments`. */\n disposition: Schema.optional(Schema.NullOr(Schema.String)),\n /** The part's `Content-ID` header (RFC 8621 §4.1.4), if any — matches a `cid:` reference in an HTML body. */\n cid: Schema.optional(Schema.NullOr(Schema.String)),\n});\nexport type EmailBodyPart = Schema.Schema.Type<typeof EmailBodyPart>;\n\n/** An email (RFC 8621 §4). Only the properties requested in `Email/get` are present. */\nexport const Email = Schema.Struct({\n id: Schema.String,\n blobId: Schema.optional(Schema.String),\n // Server-set and always present on a fetched Email (RFC 8621 §4.1.1); `EMAIL_PROPERTIES` requests it.\n threadId: Schema.String,\n mailboxIds: Schema.optional(Schema.Record({ key: Schema.String, value: Schema.Boolean })),\n keywords: Schema.optional(Schema.Record({ key: Schema.String, value: Schema.Boolean })),\n from: Schema.optional(Schema.NullOr(Schema.Array(EmailAddress))),\n to: Schema.optional(Schema.NullOr(Schema.Array(EmailAddress))),\n cc: Schema.optional(Schema.NullOr(Schema.Array(EmailAddress))),\n bcc: Schema.optional(Schema.NullOr(Schema.Array(EmailAddress))),\n subject: Schema.optional(Schema.NullOr(Schema.String)),\n receivedAt: Schema.String,\n sentAt: Schema.optional(Schema.NullOr(Schema.String)),\n preview: Schema.optional(Schema.NullOr(Schema.String)),\n messageId: Schema.optional(Schema.NullOr(Schema.Array(Schema.String))),\n inReplyTo: Schema.optional(Schema.NullOr(Schema.Array(Schema.String))),\n references: Schema.optional(Schema.NullOr(Schema.Array(Schema.String))),\n bodyValues: Schema.optional(Schema.Record({ key: Schema.String, value: BodyValue })),\n textBody: Schema.optional(Schema.Array(EmailBodyPart)),\n htmlBody: Schema.optional(Schema.Array(EmailBodyPart)),\n /** Non-body parts (RFC 8621 §4.1.4) — the attachments proper, distinct from `textBody`/`htmlBody`. */\n attachments: Schema.optional(Schema.Array(EmailBodyPart)),\n});\nexport type Email = Schema.Schema.Type<typeof Email>;\n\n/** A sending identity (RFC 8621 §6); `email` is an address the server permits as `from`. */\nexport const Identity = Schema.Struct({\n id: Schema.String,\n name: Schema.optional(Schema.NullOr(Schema.String)),\n email: Schema.String,\n});\nexport type Identity = Schema.Schema.Type<typeof Identity>;\n\n//\n// Method-result schemas.\n//\n\nexport const MailboxGetResult = Schema.Struct({\n accountId: Schema.optional(Schema.String),\n state: Schema.optional(Schema.String),\n list: Schema.Array(Mailbox),\n notFound: Schema.optional(Schema.NullOr(Schema.Array(Schema.String))),\n});\nexport type MailboxGetResult = Schema.Schema.Type<typeof MailboxGetResult>;\n\nexport const EmailQueryResult = Schema.Struct({\n accountId: Schema.optional(Schema.String),\n queryState: Schema.optional(Schema.String),\n position: Schema.optional(Schema.Number),\n total: Schema.optional(Schema.Number),\n limit: Schema.optional(Schema.Number),\n ids: Schema.Array(Schema.String),\n});\nexport type EmailQueryResult = Schema.Schema.Type<typeof EmailQueryResult>;\n\nexport const EmailGetResult = Schema.Struct({\n accountId: Schema.optional(Schema.String),\n state: Schema.optional(Schema.String),\n list: Schema.Array(Email),\n notFound: Schema.optional(Schema.NullOr(Schema.Array(Schema.String))),\n});\nexport type EmailGetResult = Schema.Schema.Type<typeof EmailGetResult>;\n\n/** `Email/changes` delta since an opaque `sinceState` token (RFC 8621 §4.3 / RFC 8620 §5.2). */\nexport const EmailChangesResult = Schema.Struct({\n accountId: Schema.optional(Schema.String),\n oldState: Schema.String,\n newState: Schema.String,\n hasMoreChanges: Schema.Boolean,\n created: Schema.Array(Schema.String),\n updated: Schema.Array(Schema.String),\n destroyed: Schema.Array(Schema.String),\n});\nexport type EmailChangesResult = Schema.Schema.Type<typeof EmailChangesResult>;\n\nexport const IdentityGetResult = Schema.Struct({\n accountId: Schema.optional(Schema.String),\n state: Schema.optional(Schema.String),\n list: Schema.Array(Identity),\n notFound: Schema.optional(Schema.NullOr(Schema.Array(Schema.String))),\n});\nexport type IdentityGetResult = Schema.Schema.Type<typeof IdentityGetResult>;\n\nexport const EmailSetResult = Schema.Struct({\n accountId: Schema.optional(Schema.String),\n oldState: Schema.optional(Schema.NullOr(Schema.String)),\n newState: Schema.optional(Schema.String),\n created: Schema.optional(\n Schema.NullOr(\n Schema.Record({\n key: Schema.String,\n value: Schema.Struct({\n id: Schema.String,\n blobId: Schema.optional(Schema.String),\n threadId: Schema.optional(Schema.String),\n size: Schema.optional(Schema.Number),\n }),\n }),\n ),\n ),\n updated: Schema.optional(Schema.NullOr(Schema.Record({ key: Schema.String, value: Schema.Unknown }))),\n destroyed: Schema.optional(Schema.NullOr(Schema.Array(Schema.String))),\n notCreated: Schema.optional(\n Schema.NullOr(\n Schema.Record({\n key: Schema.String,\n value: Schema.Struct({ type: Schema.String, description: Schema.optional(Schema.NullOr(Schema.String)) }),\n }),\n ),\n ),\n notUpdated: Schema.optional(\n Schema.NullOr(\n Schema.Record({\n key: Schema.String,\n value: Schema.Struct({ type: Schema.String, description: Schema.optional(Schema.NullOr(Schema.String)) }),\n }),\n ),\n ),\n notDestroyed: Schema.optional(\n Schema.NullOr(\n Schema.Record({\n key: Schema.String,\n value: Schema.Struct({ type: Schema.String, description: Schema.optional(Schema.NullOr(Schema.String)) }),\n }),\n ),\n ),\n});\nexport type EmailSetResult = Schema.Schema.Type<typeof EmailSetResult>;\n\nexport const EmailSubmissionSetResult = Schema.Struct({\n accountId: Schema.optional(Schema.String),\n newState: Schema.optional(Schema.String),\n created: Schema.optional(\n Schema.NullOr(Schema.Record({ key: Schema.String, value: Schema.Struct({ id: Schema.String }) })),\n ),\n notCreated: Schema.optional(\n Schema.NullOr(\n Schema.Record({\n key: Schema.String,\n value: Schema.Struct({ type: Schema.String, description: Schema.optional(Schema.NullOr(Schema.String)) }),\n }),\n ),\n ),\n});\nexport type EmailSubmissionSetResult = Schema.Schema.Type<typeof EmailSubmissionSetResult>;\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport * as HttpClient from '@effect/platform/HttpClient';\nimport * as HttpClientRequest from '@effect/platform/HttpClientRequest';\nimport * as Effect from 'effect/Effect';\n\nimport { withAuthorization } from '@dxos/compute-runtime';\n\nimport { JmapApiError } from '../../errors';\nimport { JmapCredentials } from '../../services/jmap-credentials';\nimport {\n MAIL_CAPABILITIES,\n REQUEST_RETRY,\n REQUEST_TIMEOUT,\n SUBMISSION_CAPABILITIES,\n getMethodResponse,\n jmapRequest,\n shouldRetry,\n} from '../Jmap/api';\nimport {\n type EmailAddress,\n EmailChangesResult,\n EmailGetResult,\n EmailQueryResult,\n EmailSetResult,\n EmailSubmissionSetResult,\n IdentityGetResult,\n MailboxGetResult,\n} from './types';\n\n/**\n * Resolved per-request context: the session `apiUrl`, the mail account id, and (optionally) the\n * session's blob `downloadUrl` template (RFC 8620 §6.2). Most operations (mailboxGet, emailQuery,\n * send, delete, …) don't need it and construct a `Target` without it; only the attachment-fetching\n * path populates it from `Jmap.Session.downloadUrl`, so {@link downloadBlob} is the sole consumer\n * that requires it — absent here just means \"this call site never populated it\", not \"the server\n * didn't advertise one\" (every real session does).\n */\nexport type Target = {\n readonly apiUrl: string;\n readonly accountId: string;\n readonly downloadUrl?: string;\n};\n\n/** Expands a JMAP `downloadUrl` URI Template (RFC 6570 level 1) with the given variables. */\nconst expandDownloadUrl = (\n template: string,\n variables: { accountId: string; blobId: string; type: string; name: string },\n): string =>\n template.replace(/\\{(\\w+)\\}/g, (_, key: string) =>\n key in variables ? encodeURIComponent(variables[key as keyof typeof variables]) : '',\n );\n\n/**\n * Downloads a blob's raw bytes via the session's `downloadUrl` template. `name`/`type` are advisory\n * (they only affect the response's `Content-Disposition`/`Accept`), not required for correctness.\n */\nexport const downloadBlob = Effect.fn('downloadBlob')(function* (\n target: Target,\n blobId: string,\n options: { name?: string; type?: string } = {},\n) {\n if (!target.downloadUrl) {\n return yield* Effect.fail(new JmapApiError(undefined, 'Session has no downloadUrl.'));\n }\n const url = expandDownloadUrl(target.downloadUrl, {\n accountId: target.accountId,\n blobId,\n name: options.name ?? blobId,\n type: options.type ?? 'application/octet-stream',\n });\n\n const { token } = yield* JmapCredentials;\n const httpClient = yield* HttpClient.HttpClient.pipe(Effect.map(withAuthorization(token, 'Bearer')));\n\n // Mapped to `JmapApiError` before `Effect.timeout`/`Effect.retry` so `shouldRetry` can actually\n // distinguish a permanent 4xx from a transient failure — mirrors `jmapRequest`.\n const buffer = yield* HttpClientRequest.get(url).pipe(\n httpClient.execute,\n Effect.flatMap((response) => response.arrayBuffer),\n Effect.mapError(asJmapDownloadError),\n Effect.timeout(REQUEST_TIMEOUT),\n Effect.retry({ schedule: REQUEST_RETRY, while: shouldRetry }),\n Effect.scoped,\n Effect.mapError(asJmapDownloadError),\n );\n return new Uint8Array(buffer);\n});\n\n/** Collapses transport/decode failures into a typed {@link JmapApiError} (mirrors `Jmap/api.ts`). */\nconst asJmapDownloadError = (error: unknown): JmapApiError =>\n error instanceof JmapApiError\n ? error\n : new JmapApiError(undefined, error instanceof Error ? error.message : String(error));\n\n/** Email properties fetched by {@link emailGet} — enough for the mapper to build a Message. */\nexport const EMAIL_PROPERTIES = [\n 'id',\n 'blobId',\n 'threadId',\n 'mailboxIds',\n 'keywords',\n 'from',\n 'to',\n 'cc',\n 'subject',\n 'receivedAt',\n 'sentAt',\n 'preview',\n 'messageId',\n 'inReplyTo',\n 'references',\n 'bodyValues',\n 'textBody',\n 'htmlBody',\n 'attachments',\n] as const;\n\n/** Lists all folders (mailboxes) in the account (RFC 8621 §2.3). */\nexport const mailboxGet = Effect.fn('mailboxGet')(function* (target: Target) {\n const response = yield* jmapRequest(target.apiUrl, {\n using: MAIL_CAPABILITIES,\n methodCalls: [['Mailbox/get', { accountId: target.accountId, ids: null }, '0']],\n });\n return yield* getMethodResponse(response, '0', MailboxGetResult);\n});\n\n/** Queries email ids matching a filter (RFC 8621 §4.4). */\nexport const emailQuery = Effect.fn('emailQuery')(function* (\n target: Target,\n options: {\n filter?: unknown;\n sort?: readonly { property: string; isAscending?: boolean }[];\n position?: number;\n limit?: number;\n calculateTotal?: boolean;\n } = {},\n) {\n const args: Record<string, unknown> = { accountId: target.accountId };\n if (options.filter !== undefined) {\n args.filter = options.filter;\n }\n if (options.sort !== undefined) {\n args.sort = options.sort;\n }\n if (options.position !== undefined) {\n args.position = options.position;\n }\n if (options.limit !== undefined) {\n args.limit = options.limit;\n }\n if (options.calculateTotal !== undefined) {\n args.calculateTotal = options.calculateTotal;\n }\n\n const response = yield* jmapRequest(target.apiUrl, {\n using: MAIL_CAPABILITIES,\n methodCalls: [['Email/query', args, '0']],\n });\n return yield* getMethodResponse(response, '0', EmailQueryResult);\n});\n\n/** Fetches emails by id with decoded HTML and text body values (RFC 8621 §4.2). */\nexport const emailGet = Effect.fn('emailGet')(function* (\n target: Target,\n ids: readonly string[],\n properties: readonly string[] = EMAIL_PROPERTIES,\n) {\n const response = yield* jmapRequest(target.apiUrl, {\n using: MAIL_CAPABILITIES,\n methodCalls: [\n [\n 'Email/get',\n { accountId: target.accountId, ids, properties, fetchTextBodyValues: true, fetchHTMLBodyValues: true },\n '0',\n ],\n ],\n });\n return yield* getMethodResponse(response, '0', EmailGetResult);\n});\n\n/**\n * Fetches the delta of email changes since an opaque `sinceState` token (RFC 8621 §4.3). A server that\n * can't compute the delta (state too old / evicted) returns a `cannotCalculateChanges` method error,\n * surfaced as a {@link JmapApiError} with that `type` — the caller's cue to fall back to a full scan.\n * The token to pass comes from a prior `Email/get` `state` (NOT `Email/query` `queryState`).\n */\nexport const emailChanges = Effect.fn('emailChanges')(function* (\n target: Target,\n sinceState: string,\n maxChanges?: number,\n) {\n const args: Record<string, unknown> = { accountId: target.accountId, sinceState };\n if (maxChanges !== undefined) {\n args.maxChanges = maxChanges;\n }\n const response = yield* jmapRequest(target.apiUrl, {\n using: MAIL_CAPABILITIES,\n methodCalls: [['Email/changes', args, '0']],\n });\n return yield* getMethodResponse(response, '0', EmailChangesResult);\n});\n\n/** Lists the account's sending identities (RFC 8621 §6.2). */\nexport const identityGet = Effect.fn('identityGet')(function* (target: Target) {\n const response = yield* jmapRequest(target.apiUrl, {\n using: SUBMISSION_CAPABILITIES,\n methodCalls: [['Identity/get', { accountId: target.accountId, ids: null }, '0']],\n });\n return yield* getMethodResponse(response, '0', IdentityGetResult);\n});\n\n/** Patches a single email (e.g. replace `mailboxIds` to move to Trash). Idempotent, so retryable. */\nexport const emailSetUpdate = Effect.fn('emailSetUpdate')(function* (\n target: Target,\n emailId: string,\n patch: Record<string, unknown>,\n) {\n const response = yield* jmapRequest(target.apiUrl, {\n using: MAIL_CAPABILITIES,\n methodCalls: [['Email/set', { accountId: target.accountId, update: { [emailId]: patch } }, '0']],\n });\n const result = yield* getMethodResponse(response, '0', EmailSetResult);\n const error = result.notUpdated?.[emailId];\n if (error) {\n return yield* Effect.fail(new JmapApiError(undefined, error.description ?? error.type, error.type));\n }\n return result;\n});\n\n/**\n * Creates a draft and submits it for delivery in a single batched request. Not retried (a retry\n * could double-send). Returns the created email's id and thread id.\n */\nexport const submitEmail = Effect.fn('submitEmail')(function* (\n target: Target,\n args: {\n identityId: string;\n draftsMailboxId: string;\n sentMailboxId: string;\n draft: {\n from: readonly EmailAddress[];\n to: readonly EmailAddress[];\n cc?: readonly EmailAddress[];\n bcc?: readonly EmailAddress[];\n subject?: string;\n inReplyTo?: readonly string[];\n references?: readonly string[];\n text: string;\n };\n },\n) {\n const { identityId, draftsMailboxId, sentMailboxId, draft } = args;\n const draftCreate: Record<string, unknown> = {\n mailboxIds: { [draftsMailboxId]: true },\n keywords: { $draft: true, $seen: true },\n from: draft.from,\n to: draft.to,\n bodyValues: { body: { value: draft.text } },\n textBody: [{ partId: 'body', type: 'text/plain' }],\n };\n if (draft.cc && draft.cc.length > 0) {\n draftCreate.cc = draft.cc;\n }\n if (draft.bcc && draft.bcc.length > 0) {\n draftCreate.bcc = draft.bcc;\n }\n if (draft.subject !== undefined) {\n draftCreate.subject = draft.subject;\n }\n if (draft.inReplyTo && draft.inReplyTo.length > 0) {\n draftCreate.inReplyTo = draft.inReplyTo;\n }\n if (draft.references && draft.references.length > 0) {\n draftCreate.references = draft.references;\n }\n\n const response = yield* jmapRequest(\n target.apiUrl,\n {\n using: SUBMISSION_CAPABILITIES,\n methodCalls: [\n ['Email/set', { accountId: target.accountId, create: { draft: draftCreate } }, '0'],\n [\n 'EmailSubmission/set',\n {\n accountId: target.accountId,\n create: { sub: { emailId: '#draft', identityId } },\n onSuccessUpdateEmail: {\n '#sub': {\n [`mailboxIds/${draftsMailboxId}`]: null,\n [`mailboxIds/${sentMailboxId}`]: true,\n 'keywords/$draft': null,\n },\n },\n },\n '1',\n ],\n ],\n },\n { retry: false },\n );\n\n const setResult = yield* getMethodResponse(response, '0', EmailSetResult);\n const created = setResult.created?.draft;\n if (!created) {\n const error = setResult.notCreated?.draft;\n return yield* Effect.fail(\n new JmapApiError(undefined, error?.description ?? error?.type ?? 'Draft was not created.', error?.type),\n );\n }\n\n const submissionResult = yield* getMethodResponse(response, '1', EmailSubmissionSetResult);\n if (!submissionResult.created?.sub) {\n const error = submissionResult.notCreated?.sub;\n return yield* Effect.fail(\n new JmapApiError(undefined, error?.description ?? error?.type ?? 'Email submission failed.', error?.type),\n );\n }\n\n return { id: created.id, threadId: created.threadId };\n});\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport { subDays, subMonths, subWeeks, subYears } from 'date-fns';\nimport * as Match from 'effect/Match';\nimport * as Option from 'effect/Option';\n\nimport { type Filter, type FilterCondition, type FilterOperator } from '../Jmap';\nimport { type Mailbox } from './types';\n\n/** Context for {@link parseMailQuery}: a clock (for relative dates) and a label/folder resolver. */\nexport type ParseContext = {\n readonly now: Date;\n /** Resolves a `label:`/`in:` name or system role to a JMAP mailbox id. */\n readonly resolveMailbox: (nameOrRole: string) => Option.Option<string>;\n};\n\ntype Token =\n | { readonly _tag: 'or' }\n | { readonly _tag: 'term'; readonly negate: boolean; readonly op: Option.Option<string>; readonly value: string };\n\n/**\n * Translates a Gmail-like query string into a JMAP `Email/query` filter (RFC 8621 §4.4.1).\n *\n * Supported operators: `from:`/`to:`/`cc:`/`bcc:`/`subject:`/`body:`, `label:`/`in:` (resolved to a\n * mailbox id), `is:unread|read|starred|flagged|unstarred|draft|answered`, `has:attachment`,\n * `after:`/`before:` (`YYYY/MM/DD` or `YYYY-MM-DD`), `newer_than:`/`older_than:` (e.g. `7d`, `2w`,\n * `3m`, `1y`), `larger:`/`smaller:` (e.g. `10M`, `500k`), `list:`/`deliveredto:` (header match),\n * quoted phrases, `-` negation, `OR` groups, and bare words (full-text). Space-separated terms are\n * AND-ed. Returns `None` for an empty query, or one whose terms all fail to resolve (e.g. an unknown\n * label).\n */\nexport const parseMailQuery = (query: string, ctx: ParseContext): Option.Option<Filter> =>\n buildFilter(tokenize(query), ctx);\n\n/** Resolves a label/folder name (case-insensitive) or a system role/alias to a mailbox id. */\nexport const resolveMailboxByNameOrRole = (folders: readonly Mailbox[], nameOrRole: string): Option.Option<string> => {\n const lowered = nameOrRole.toLowerCase();\n const role = ROLE_ALIASES[lowered] ?? lowered;\n const match =\n folders.find((folder) => folder.role === role) ?? folders.find((folder) => folder.name.toLowerCase() === lowered);\n return Option.fromNullable(match?.id);\n};\n\n//\n// Tokenizer.\n//\n\n// A token is an optional `-`, optional `op:` prefix, then a `\"quoted\"` value or a non-space run.\nconst TOKEN_RE = /-?(?:[A-Za-z_]+:)?\"[^\"]*\"|\\S+/g;\n\nconst tokenize = (query: string): readonly Token[] => (query.match(TOKEN_RE) ?? []).map(parseToken);\n\nconst parseToken = (raw: string): Token => {\n if (raw === 'OR') {\n return { _tag: 'or' };\n }\n const negate = raw.startsWith('-');\n const rest = negate ? raw.slice(1) : raw;\n const colon = rest.indexOf(':');\n if (colon > 0 && /^[A-Za-z_]+$/.test(rest.slice(0, colon))) {\n return {\n _tag: 'term',\n negate,\n op: Option.some(rest.slice(0, colon).toLowerCase()),\n value: stripQuotes(rest.slice(colon + 1)),\n };\n }\n return { _tag: 'term', negate, op: Option.none(), value: stripQuotes(rest) };\n};\n\nconst stripQuotes = (value: string): string =>\n value.length >= 2 && value.startsWith('\"') && value.endsWith('\"') ? value.slice(1, -1) : value;\n\n//\n// Folding tokens into a filter tree. Space-separated terms AND together; an `OR` token merges the\n// next term with the previous operand. Terms that resolve to `None` (unknown label, empty value) are\n// dropped.\n//\n\nconst buildFilter = (tokens: readonly Token[], ctx: ParseContext): Option.Option<Filter> => {\n const { operands } = tokens.reduce<{ operands: readonly Filter[]; pendingOr: boolean }>(\n (state, token) => {\n if (token._tag === 'or') {\n return { ...state, pendingOr: state.operands.length > 0 };\n }\n const leaf = buildTerm(token, ctx);\n if (Option.isNone(leaf)) {\n // A dropped term (unknown label, empty value) consumes any pending OR: the next valid term\n // must not merge with the previous operand unless its own `or` token immediately precedes it.\n return { ...state, pendingOr: false };\n }\n const previous = state.pendingOr ? state.operands[state.operands.length - 1] : undefined;\n return previous !== undefined\n ? { operands: [...state.operands.slice(0, -1), mergeOr(previous, leaf.value)], pendingOr: false }\n : { operands: [...state.operands, leaf.value], pendingOr: false };\n },\n { operands: [], pendingOr: false },\n );\n\n return Match.value(operands.length).pipe(\n Match.when(0, () => Option.none<Filter>()),\n Match.when(1, () => Option.fromNullable(operands[0])),\n Match.orElse(() => Option.some<Filter>({ operator: 'AND', conditions: operands })),\n );\n};\n\nconst mergeOr = (previous: Filter, next: Filter): FilterOperator =>\n isOperator(previous) && previous.operator === 'OR'\n ? { operator: 'OR', conditions: [...previous.conditions, next] }\n : { operator: 'OR', conditions: [previous, next] };\n\nconst isOperator = (filter: Filter): filter is FilterOperator => 'operator' in filter;\n\n/** Whether a filter restricts the mailbox scope (`inMailbox`/`inMailboxOtherThan`), recursively. */\nexport const filterScopesMailbox = (filter: Filter): boolean =>\n isOperator(filter)\n ? filter.conditions.some(filterScopesMailbox)\n : filter.inMailbox !== undefined || filter.inMailboxOtherThan !== undefined;\n\nconst buildTerm = (token: Extract<Token, { _tag: 'term' }>, ctx: ParseContext): Option.Option<Filter> =>\n buildLeaf(token.op, token.value, ctx).pipe(Option.map((leaf) => (token.negate ? negate(leaf) : leaf)));\n\nconst negate = (filter: Filter): FilterOperator => ({ operator: 'NOT', conditions: [filter] });\n\n//\n// Leaf construction (operator → JMAP FilterCondition).\n//\n\nconst buildLeaf = (op: Option.Option<string>, value: string, ctx: ParseContext): Option.Option<Filter> =>\n op.pipe(\n Option.match({\n onNone: () => textLeaf(value),\n onSome: (operator) => buildOperatorLeaf(operator, value, ctx),\n }),\n );\n\nconst buildOperatorLeaf = (operator: string, value: string, ctx: ParseContext): Option.Option<Filter> =>\n Match.value(operator).pipe(\n Match.whenOr('from', 'to', 'cc', 'bcc', 'subject', 'body', (field) =>\n condLeaf(value, STRING_FIELD_LEAVES[field](value)),\n ),\n Match.whenOr('label', 'in', () => ctx.resolveMailbox(value).pipe(Option.map((id) => ({ inMailbox: id })))),\n Match.when('is', () => Option.fromNullable(IS_LEAVES[value.toLowerCase()])),\n Match.when('has', () =>\n value.toLowerCase() === 'attachment' ? Option.some<Filter>({ hasAttachment: true }) : Option.none<Filter>(),\n ),\n Match.whenOr('after', 'newer', () => dateLeaf(value, 'after')),\n Match.whenOr('before', 'older', () => dateLeaf(value, 'before')),\n Match.when('newer_than', () => relativeDateLeaf(value, ctx.now, 'after')),\n Match.when('older_than', () => relativeDateLeaf(value, ctx.now, 'before')),\n Match.when('larger', () => sizeLeaf(value, 'minSize')),\n Match.when('smaller', () => sizeLeaf(value, 'maxSize')),\n Match.when('list', () => headerLeaf('List-Id', value)),\n Match.when('deliveredto', () => headerLeaf('Delivered-To', value)),\n // Unknown operator: keep the literal `op:value` as a full-text term (mirrors Gmail's fallback).\n Match.orElse(() => textLeaf(`${operator}:${value}`)),\n );\n\ntype StringField = 'from' | 'to' | 'cc' | 'bcc' | 'subject' | 'body';\n\nconst STRING_FIELD_LEAVES: Record<StringField, (value: string) => FilterCondition> = {\n from: (value) => ({ from: value }),\n to: (value) => ({ to: value }),\n cc: (value) => ({ cc: value }),\n bcc: (value) => ({ bcc: value }),\n subject: (value) => ({ subject: value }),\n body: (value) => ({ body: value }),\n};\n\nconst IS_LEAVES: Record<string, Filter> = {\n unread: { notKeyword: '$seen' },\n read: { hasKeyword: '$seen' },\n starred: { hasKeyword: '$flagged' },\n flagged: { hasKeyword: '$flagged' },\n unstarred: { notKeyword: '$flagged' },\n unflagged: { notKeyword: '$flagged' },\n draft: { hasKeyword: '$draft' },\n answered: { hasKeyword: '$answered' },\n replied: { hasKeyword: '$answered' },\n};\n\nconst ROLE_ALIASES: Record<string, string> = { spam: 'junk', bin: 'trash', deleted: 'trash' };\n\nconst SIZE_UNITS: Record<string, number> = { '': 1, 'k': 1024, 'm': 1024 * 1024, 'g': 1024 * 1024 * 1024 };\nconst DATE_RE = /^(\\d{4})[/-](\\d{1,2})[/-](\\d{1,2})$/;\nconst RELATIVE_RE = /^(\\d+)([dwmy])$/;\nconst SIZE_RE = /^(\\d+)([kmg]?)b?$/i;\n\n/** Wraps a built condition in `Some`, or `None` when the value is blank. */\nconst condLeaf = (value: string, condition: Filter): Option.Option<Filter> =>\n value.trim().length === 0 ? Option.none() : Option.some(condition);\n\nconst textLeaf = (value: string): Option.Option<Filter> => condLeaf(value, { text: value });\n\nconst headerLeaf = (name: string, value: string): Option.Option<Filter> => condLeaf(value, { header: [name, value] });\n\nconst dateLeaf = (value: string, bound: 'after' | 'before'): Option.Option<Filter> => {\n const match = DATE_RE.exec(value);\n if (!match) {\n return Option.none();\n }\n const iso = new Date(Date.UTC(Number(match[1]), Number(match[2]) - 1, Number(match[3]))).toISOString();\n return Option.some(bound === 'after' ? { after: iso } : { before: iso });\n};\n\nconst relativeDateLeaf = (value: string, now: Date, bound: 'after' | 'before'): Option.Option<Filter> => {\n const match = RELATIVE_RE.exec(value.toLowerCase());\n if (!match) {\n return Option.none();\n }\n const amount = Number(match[1]);\n const date = Match.value(match[2]).pipe(\n Match.when('w', () => subWeeks(now, amount)),\n Match.when('m', () => subMonths(now, amount)),\n Match.when('y', () => subYears(now, amount)),\n Match.orElse(() => subDays(now, amount)),\n );\n const iso = date.toISOString();\n return Option.some(bound === 'after' ? { after: iso } : { before: iso });\n};\n\nconst sizeLeaf = (value: string, bound: 'minSize' | 'maxSize'): Option.Option<Filter> => {\n const match = SIZE_RE.exec(value.toLowerCase());\n if (!match) {\n return Option.none();\n }\n const bytes = Number(match[1]) * (SIZE_UNITS[match[2] ?? ''] ?? 1);\n return Option.some(bound === 'minSize' ? { minSize: bytes } : { maxSize: bytes });\n};\n","//\n// Copyright 2026 DXOS.org\n//\n\nexport * from './api';\nexport * from './types';\nexport * from './query';\n"],"mappings":"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"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { h as JmapCredentials, m as getSession } from "./chunk-apis.mjs";
|
|
2
|
+
import { r as JMAP_MAIL_CONNECTOR_ID, t as JMAP_DEFAULT_HOST } from "./chunk-constants.mjs";
|
|
3
|
+
import { t as JmapApiError } from "./chunk-errors.mjs";
|
|
4
|
+
import "./services.mjs";
|
|
5
|
+
import * as Effect from "effect/Effect";
|
|
6
|
+
import * as Schema from "effect/Schema";
|
|
7
|
+
import * as Layer from "effect/Layer";
|
|
8
|
+
import { Format, Obj, Ref, Type } from "@dxos/echo";
|
|
9
|
+
import * as Capability from "@dxos/app-framework/Capability";
|
|
10
|
+
import * as ConnectorSpec from "@dxos/plugin-connector/ConnectorSpec";
|
|
11
|
+
import * as InboxOperation from "@dxos/plugin-inbox/InboxOperation";
|
|
12
|
+
import { MAIL_AUTO_SYNC, MAIL_REMOTE_SYNC, MAIL_SYNC_CRON } from "@dxos/plugin-inbox/sync";
|
|
13
|
+
import { AccessToken } from "@dxos/link";
|
|
14
|
+
import * as Mailbox from "@dxos/plugin-inbox/Mailbox";
|
|
15
|
+
import * as Trigger from "@dxos/compute/Trigger";
|
|
16
|
+
import * as SyncOptions from "@dxos/plugin-inbox/SyncOptions";
|
|
17
|
+
import * as FetchHttpClient from "@effect/platform/FetchHttpClient";
|
|
18
|
+
import * as Connection from "@dxos/plugin-connector/Connection";
|
|
19
|
+
var jmapCredentialForm = {
|
|
20
|
+
schema: Schema.Struct({
|
|
21
|
+
host: Schema.String.annotations({
|
|
22
|
+
title: "Server",
|
|
23
|
+
description: "JMAP server host. The session is discovered at https://<host>/.well-known/jmap."
|
|
24
|
+
}),
|
|
25
|
+
email: Schema.String.annotations({
|
|
26
|
+
title: "Email",
|
|
27
|
+
description: "Your email address / username on the JMAP server."
|
|
28
|
+
}),
|
|
29
|
+
token: Schema.String.pipe(Format.FormatAnnotation.set(Format.TypeFormat.Password)).annotations({
|
|
30
|
+
title: "API token",
|
|
31
|
+
description: "A JMAP API token, sent as a Bearer credential."
|
|
32
|
+
})
|
|
33
|
+
}),
|
|
34
|
+
defaultValues: {
|
|
35
|
+
host: JMAP_DEFAULT_HOST,
|
|
36
|
+
email: "",
|
|
37
|
+
token: ""
|
|
38
|
+
},
|
|
39
|
+
onValidate: ({ values }) => Effect.gen(function* () {
|
|
40
|
+
const host = values.host.trim();
|
|
41
|
+
const token = values.token.trim();
|
|
42
|
+
if (host.length === 0) return yield* Effect.fail(/* @__PURE__ */ new Error("Server host is required."));
|
|
43
|
+
if (token.length === 0) return yield* Effect.fail(/* @__PURE__ */ new Error("API token is required."));
|
|
44
|
+
yield* fetchSession(host, token);
|
|
45
|
+
}),
|
|
46
|
+
onSubmit: ({ values, connector }) => Effect.gen(function* () {
|
|
47
|
+
const host = values.host.trim();
|
|
48
|
+
const email = values.email.trim();
|
|
49
|
+
const token = values.token.trim();
|
|
50
|
+
const { accessToken, connection } = buildJmapCredential({
|
|
51
|
+
host,
|
|
52
|
+
email,
|
|
53
|
+
token,
|
|
54
|
+
session: yield* fetchSession(host, token),
|
|
55
|
+
connector
|
|
56
|
+
});
|
|
57
|
+
return {
|
|
58
|
+
kind: "complete",
|
|
59
|
+
accessToken,
|
|
60
|
+
connection
|
|
61
|
+
};
|
|
62
|
+
})
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* Builds the `AccessToken` (source = host, account = form email or session username) and `Connection`
|
|
66
|
+
* for a validated JMAP credential. Pure (no DB), so the construction is unit-testable.
|
|
67
|
+
*/
|
|
68
|
+
var buildJmapCredential = ({ host, email, token, session, connector }) => {
|
|
69
|
+
const account = email.length > 0 ? email : session.username;
|
|
70
|
+
const accessToken = Obj.make(AccessToken.AccessToken, {
|
|
71
|
+
source: host,
|
|
72
|
+
...account ? { account } : {},
|
|
73
|
+
token
|
|
74
|
+
});
|
|
75
|
+
return {
|
|
76
|
+
accessToken,
|
|
77
|
+
connection: Connection.make({
|
|
78
|
+
name: connector.label ?? "JMAP",
|
|
79
|
+
connectorId: connector.id,
|
|
80
|
+
accessToken: Ref.make(accessToken)
|
|
81
|
+
})
|
|
82
|
+
};
|
|
83
|
+
};
|
|
84
|
+
/** Discovers the JMAP session for the given host/token, surfacing a friendly message on 401. */
|
|
85
|
+
var fetchSession = (host, token) => getSession.pipe(Effect.provide(Layer.mergeAll(FetchHttpClient.layer, JmapCredentials.fromValues({
|
|
86
|
+
host,
|
|
87
|
+
token
|
|
88
|
+
}))), Effect.mapError((error) => error instanceof JmapApiError && error.status === 401 ? /* @__PURE__ */ new Error("The JMAP server rejected the token (401). Check the host and API token and try again.") : error instanceof Error ? error : new Error(String(error))));
|
|
89
|
+
//#endregion
|
|
90
|
+
//#region src/capabilities/connector.ts
|
|
91
|
+
var connector_default = Capability.makeModule(Effect.fnUntraced(function* () {
|
|
92
|
+
return Capability.contribute(ConnectorSpec.Connector, [{
|
|
93
|
+
id: JMAP_MAIL_CONNECTOR_ID,
|
|
94
|
+
source: JMAP_DEFAULT_HOST,
|
|
95
|
+
label: "JMAP Mail",
|
|
96
|
+
credentialForm: jmapCredentialForm,
|
|
97
|
+
sync: {
|
|
98
|
+
operation: InboxOperation.JmapSync,
|
|
99
|
+
targetTypename: Type.getTypename(Mailbox.Mailbox),
|
|
100
|
+
materializeTarget: InboxOperation.MaterializeJmapTarget,
|
|
101
|
+
optionsSchema: SyncOptions.SyncOptions,
|
|
102
|
+
auto: MAIL_AUTO_SYNC,
|
|
103
|
+
trigger: Trigger.specTimer(MAIL_SYNC_CRON),
|
|
104
|
+
remote: MAIL_REMOTE_SYNC
|
|
105
|
+
}
|
|
106
|
+
}]);
|
|
107
|
+
}));
|
|
108
|
+
//#endregion
|
|
109
|
+
export { connector_default as default };
|
|
110
|
+
|
|
111
|
+
//# sourceMappingURL=chunk-connector.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chunk-connector.mjs","names":[],"sources":["../../src/capabilities/credential-form.ts","../../src/capabilities/connector.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';\nimport * as Schema from 'effect/Schema';\n\nimport { Format, Obj, Ref } from '@dxos/echo';\nimport { AccessToken } from '@dxos/link';\nimport * as Connection from '@dxos/plugin-connector/Connection';\nimport * as ConnectorSpec from '@dxos/plugin-connector/ConnectorSpec';\n\nimport { Jmap } from '../apis';\nimport { JMAP_DEFAULT_HOST } from '../constants';\nimport { JmapApiError } from '../errors';\nimport { JmapCredentials } from '../services';\n\n/**\n * Manual-credential form for the JMAP connector. JMAP auth is a server-issued Bearer API token\n * (Fastmail: Settings → Privacy & Security → Integrations), outside any OAuth flow, so the host,\n * email, and token are collected directly.\n */\nconst JmapCredentialFormSchema = Schema.Struct({\n host: Schema.String.annotations({\n title: 'Server',\n description: 'JMAP server host. The session is discovered at https://<host>/.well-known/jmap.',\n }),\n email: Schema.String.annotations({\n title: 'Email',\n description: 'Your email address / username on the JMAP server.',\n }),\n token: Schema.String.pipe(Format.FormatAnnotation.set(Format.TypeFormat.Password)).annotations({\n title: 'API token',\n description: 'A JMAP API token, sent as a Bearer credential.',\n }),\n});\n\ntype JmapCredentialFormValues = Schema.Schema.Type<typeof JmapCredentialFormSchema>;\n\nexport const jmapCredentialForm: ConnectorSpec.CredentialForm<JmapCredentialFormValues> = {\n schema: JmapCredentialFormSchema,\n defaultValues: { host: JMAP_DEFAULT_HOST, email: '', token: '' },\n // Validate against the live session before the dialog closes so a wrong host/token fails inline\n // (with a clear 401 message) instead of creating a Connection that errors on every later sync.\n onValidate: ({ values }) =>\n Effect.gen(function* () {\n const host = values.host.trim();\n const token = values.token.trim();\n if (host.length === 0) {\n return yield* Effect.fail(new Error('Server host is required.'));\n }\n if (token.length === 0) {\n return yield* Effect.fail(new Error('API token is required.'));\n }\n yield* fetchSession(host, token);\n }),\n onSubmit: ({ values, connector }) =>\n Effect.gen(function* () {\n // Trim defensively: onValidate is optional and callers bypass it in tests.\n const host = values.host.trim();\n const email = values.email.trim();\n const token = values.token.trim();\n const session = yield* fetchSession(host, token);\n const { accessToken, connection } = buildJmapCredential({ host, email, token, session, connector });\n return { kind: 'complete' as const, accessToken, connection };\n }),\n};\n\n/**\n * Builds the `AccessToken` (source = host, account = form email or session username) and `Connection`\n * for a validated JMAP credential. Pure (no DB), so the construction is unit-testable.\n */\nexport const buildJmapCredential = ({\n host,\n email,\n token,\n session,\n connector,\n}: {\n host: string;\n email: string;\n token: string;\n session: Jmap.Session;\n connector: { id?: string; label?: string };\n}) => {\n const account = email.length > 0 ? email : session.username;\n const accessToken = Obj.make(AccessToken.AccessToken, {\n source: host,\n ...(account ? { account } : {}),\n token,\n });\n const connection = Connection.make({\n name: connector.label ?? 'JMAP',\n connectorId: connector.id,\n accessToken: Ref.make(accessToken),\n });\n return { accessToken, connection };\n};\n\n/** Discovers the JMAP session for the given host/token, surfacing a friendly message on 401. */\nconst fetchSession = (host: string, token: string) =>\n Jmap.getSession.pipe(\n Effect.provide(Layer.mergeAll(FetchHttpClient.layer, JmapCredentials.fromValues({ host, token }))),\n Effect.mapError((error) =>\n error instanceof JmapApiError && error.status === 401\n ? new Error('The JMAP server rejected the token (401). Check the host and API token and try again.')\n : error instanceof Error\n ? error\n : new Error(String(error)),\n ),\n );\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport * as Effect from 'effect/Effect';\n\nimport * as Capability from '@dxos/app-framework/Capability';\nimport * as Trigger from '@dxos/compute/Trigger';\nimport { Type } from '@dxos/echo';\nimport * as ConnectorSpec from '@dxos/plugin-connector/ConnectorSpec';\nimport * as InboxOperation from '@dxos/plugin-inbox/InboxOperation';\nimport * as Mailbox from '@dxos/plugin-inbox/Mailbox';\nimport { MAIL_AUTO_SYNC, MAIL_REMOTE_SYNC, MAIL_SYNC_CRON } from '@dxos/plugin-inbox/sync';\nimport * as SyncOptions from '@dxos/plugin-inbox/SyncOptions';\n\nimport { JMAP_DEFAULT_HOST, JMAP_MAIL_CONNECTOR_ID } from '../constants';\nimport { jmapCredentialForm } from './credential-form';\n\nexport default Capability.makeModule(\n Effect.fnUntraced(function* () {\n return Capability.contribute(ConnectorSpec.Connector, [\n {\n id: JMAP_MAIL_CONNECTOR_ID,\n // Nominal default; the real `AccessToken.source` (host) is captured by the credential form.\n source: JMAP_DEFAULT_HOST,\n label: 'JMAP Mail',\n // Non-OAuth: host + email + Bearer API token, validated against the live session on submit.\n credentialForm: jmapCredentialForm,\n sync: {\n operation: InboxOperation.JmapSync,\n // What this connector binds — how `Mailbox` discovers it without naming JMAP.\n targetTypename: Type.getTypename(Mailbox.Mailbox),\n // Single-target connector (the account inbox): no `getTargets`. The coordinator calls\n // `materializeTarget` (no remoteTarget) to create the Mailbox, then binds.\n materializeTarget: InboxOperation.MaterializeJmapTarget,\n optionsSchema: SyncOptions.SyncOptions,\n auto: MAIL_AUTO_SYNC,\n trigger: Trigger.specTimer(MAIL_SYNC_CRON),\n remote: MAIL_REMOTE_SYNC,\n },\n },\n ]);\n }),\n);\n"],"mappings":";;;;;;;;;;;;;;;;;;AAyCA,IAAa,qBAA6E;CACxF,QAlB+B,OAAO,OAAO;EAC7C,MAAM,OAAO,OAAO,YAAY;GAC9B,OAAO;GACP,aAAa;EACf,CAAC;EACD,OAAO,OAAO,OAAO,YAAY;GAC/B,OAAO;GACP,aAAa;EACf,CAAC;EACD,OAAO,OAAO,OAAO,KAAK,OAAO,iBAAiB,IAAI,OAAO,WAAW,QAAQ,CAAC,CAAC,CAAC,YAAY;GAC7F,OAAO;GACP,aAAa;EACf,CAAC;CACH,CAKU;CACR,eAAe;EAAE,MAAM;EAAmB,OAAO;EAAI,OAAO;CAAG;CAG/D,aAAa,EAAE,aACb,OAAO,IAAI,aAAa;EACtB,MAAM,OAAO,OAAO,KAAK,KAAK;EAC9B,MAAM,QAAQ,OAAO,MAAM,KAAK;EAChC,IAAI,KAAK,WAAW,GAClB,OAAO,OAAO,OAAO,qBAAK,IAAI,MAAM,0BAA0B,CAAC;EAEjE,IAAI,MAAM,WAAW,GACnB,OAAO,OAAO,OAAO,qBAAK,IAAI,MAAM,wBAAwB,CAAC;EAE/D,OAAO,aAAa,MAAM,KAAK;CACjC,CAAC;CACH,WAAW,EAAE,QAAQ,gBACnB,OAAO,IAAI,aAAa;EAEtB,MAAM,OAAO,OAAO,KAAK,KAAK;EAC9B,MAAM,QAAQ,OAAO,MAAM,KAAK;EAChC,MAAM,QAAQ,OAAO,MAAM,KAAK;EAEhC,MAAM,EAAE,aAAa,eAAe,oBAAoB;GAAE;GAAM;GAAO;GAAO,gBADvD,aAAa,MAAM,KAAK;GACwC;EAAU,CAAC;EAClG,OAAO;GAAE,MAAM;GAAqB;GAAa;EAAW;CAC9D,CAAC;AACL;;;;;AAMA,IAAa,uBAAuB,EAClC,MACA,OACA,OACA,SACA,gBAOI;CACJ,MAAM,UAAU,MAAM,SAAS,IAAI,QAAQ,QAAQ;CACnD,MAAM,cAAc,IAAI,KAAK,YAAY,aAAa;EACpD,QAAQ;EACR,GAAI,UAAU,EAAE,QAAQ,IAAI,CAAC;EAC7B;CACF,CAAC;CAMD,OAAO;EAAE;EAAa,YALH,WAAW,KAAK;GACjC,MAAM,UAAU,SAAS;GACzB,aAAa,UAAU;GACvB,aAAa,IAAI,KAAK,WAAW;EACnC,CACsB;CAAW;AACnC;;AAGA,IAAM,gBAAgB,MAAc,UAAA,WAClB,KACd,OAAO,QAAQ,MAAM,SAAS,gBAAgB,OAAO,gBAAgB,WAAW;CAAE;CAAM;AAAM,CAAC,CAAC,CAAC,GACjG,OAAO,UAAU,UACf,iBAAiB,gBAAgB,MAAM,WAAW,sBAC9C,IAAI,MAAM,uFAAuF,IACjG,iBAAiB,QACf,QACA,IAAI,MAAM,OAAO,KAAK,CAAC,CAC/B,CACF;;;AC9FF,IAAA,oBAAe,WAAW,WACxB,OAAO,WAAW,aAAa;CAC7B,OAAO,WAAW,WAAW,cAAc,WAAW,CACpD;EACE,IAAI;EAEJ,QAAQ;EACR,OAAO;EAEP,gBAAgB;EAChB,MAAM;GACJ,WAAW,eAAe;GAE1B,gBAAgB,KAAK,YAAY,QAAQ,OAAO;GAGhD,mBAAmB,eAAe;GAClC,eAAe,YAAY;GAC3B,MAAM;GACN,SAAS,QAAQ,UAAU,cAAc;GACzC,QAAQ;EACV;CACF,CACF,CAAC;AACH,CAAC,CACH"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
//#region src/constants.ts
|
|
2
|
+
/** `Connector.id` for the JMAP mail connector (RFC 8620/8621); stored as `Connection.connectorId`. */
|
|
3
|
+
var JMAP_MAIL_CONNECTOR_ID = "jmap-mail";
|
|
4
|
+
/**
|
|
5
|
+
* Default JMAP server host pre-filled in the credential form. Fastmail is the canonical JMAP
|
|
6
|
+
* provider; the session is discovered at `https://${host}/.well-known/jmap`.
|
|
7
|
+
*/
|
|
8
|
+
var JMAP_DEFAULT_HOST = "api.fastmail.com";
|
|
9
|
+
/**
|
|
10
|
+
* The provider's origin domain, used as the foreign-key `Meta.keys[].source` on everything this plugin
|
|
11
|
+
* syncs — messages (the dedup key; see the mapper) and folder {@link Tag}s alike. One string for both
|
|
12
|
+
* is deliberate: a foreign key is scoped by the object it sits on, so the type already distinguishes a
|
|
13
|
+
* message key from a tag key, and `Tag.getOrigin` reads it to mark synced tags read-only.
|
|
14
|
+
*
|
|
15
|
+
* (Gmail is asymmetric here — its message source is the older `com.google.mail` while its tags carry
|
|
16
|
+
* `com.google.gmail`. That is left alone deliberately: renaming a persisted message key would orphan
|
|
17
|
+
* mail. See `Tag.md` §"Origin domain == key source".)
|
|
18
|
+
*/
|
|
19
|
+
var JMAP_DOMAIN = "org.ietf.jmap";
|
|
20
|
+
//#endregion
|
|
21
|
+
export { JMAP_DOMAIN as n, JMAP_MAIL_CONNECTOR_ID as r, JMAP_DEFAULT_HOST as t };
|
|
22
|
+
|
|
23
|
+
//# sourceMappingURL=chunk-constants.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chunk-constants.mjs","names":[],"sources":["../../src/constants.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\n/** `Connector.id` for the JMAP mail connector (RFC 8620/8621); stored as `Connection.connectorId`. */\nexport const JMAP_MAIL_CONNECTOR_ID = 'jmap-mail';\n\n/**\n * Default JMAP server host pre-filled in the credential form. Fastmail is the canonical JMAP\n * provider; the session is discovered at `https://${host}/.well-known/jmap`.\n */\nexport const JMAP_DEFAULT_HOST = 'api.fastmail.com';\n\n/**\n * The provider's origin domain, used as the foreign-key `Meta.keys[].source` on everything this plugin\n * syncs — messages (the dedup key; see the mapper) and folder {@link Tag}s alike. One string for both\n * is deliberate: a foreign key is scoped by the object it sits on, so the type already distinguishes a\n * message key from a tag key, and `Tag.getOrigin` reads it to mark synced tags read-only.\n *\n * (Gmail is asymmetric here — its message source is the older `com.google.mail` while its tags carry\n * `com.google.gmail`. That is left alone deliberately: renaming a persisted message key would orphan\n * mail. See `Tag.md` §\"Origin domain == key source\".)\n */\nexport const JMAP_DOMAIN = 'org.ietf.jmap';\n"],"mappings":";;AAKA,IAAa,yBAAyB;;;;;AAMtC,IAAa,oBAAoB;;;;;;;;;;;AAYjC,IAAa,cAAc"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { BaseError } from "@dxos/errors";
|
|
2
|
+
//#region src/errors.ts
|
|
3
|
+
/**
|
|
4
|
+
* JMAP request failed: a non-2xx HTTP status, a JMAP problem-details body, or a method-level
|
|
5
|
+
* `["error", { type, description }, id]` response. `status` carries the HTTP status (when the
|
|
6
|
+
* failure is transport-level) and `type` the JMAP error type (when method-level) so callers can
|
|
7
|
+
* recover with `Effect.catchTag` and distinguish auth (401) from other failures.
|
|
8
|
+
*/
|
|
9
|
+
var JmapApiError = class extends BaseError.extend("JmapApiError", "JMAP API request failed.") {
|
|
10
|
+
status;
|
|
11
|
+
detail;
|
|
12
|
+
type;
|
|
13
|
+
constructor(status, detail, type, options) {
|
|
14
|
+
super({
|
|
15
|
+
...options,
|
|
16
|
+
context: {
|
|
17
|
+
...options?.context ?? {},
|
|
18
|
+
status,
|
|
19
|
+
detail,
|
|
20
|
+
type
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
this.status = status;
|
|
24
|
+
this.detail = detail;
|
|
25
|
+
this.type = type;
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
/** JMAP send payload missing required fields (`to` or body text). */
|
|
29
|
+
var JmapSendMessageInvalidError = class extends BaseError.extend("JmapSendMessageInvalidError", "Missing \"to\" or content in message.") {};
|
|
30
|
+
/** No JMAP identity permits the connection account as a `from` address, so the email cannot be sent. */
|
|
31
|
+
var JmapSendIdentityNotFoundError = class extends BaseError.extend("JmapSendIdentityNotFoundError", "No JMAP identity matches the connection account.") {
|
|
32
|
+
constructor(account, options) {
|
|
33
|
+
super({
|
|
34
|
+
context: { account },
|
|
35
|
+
...options
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
//#endregion
|
|
40
|
+
export { JmapSendIdentityNotFoundError as n, JmapSendMessageInvalidError as r, JmapApiError as t };
|
|
41
|
+
|
|
42
|
+
//# sourceMappingURL=chunk-errors.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chunk-errors.mjs","names":[],"sources":["../../src/errors.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\nimport { BaseError, type BaseErrorOptions } from '@dxos/errors';\n\n/**\n * JMAP request failed: a non-2xx HTTP status, a JMAP problem-details body, or a method-level\n * `[\"error\", { type, description }, id]` response. `status` carries the HTTP status (when the\n * failure is transport-level) and `type` the JMAP error type (when method-level) so callers can\n * recover with `Effect.catchTag` and distinguish auth (401) from other failures.\n */\nexport class JmapApiError extends BaseError.extend('JmapApiError', 'JMAP API request failed.') {\n constructor(\n public readonly status: number | undefined,\n public readonly detail: string,\n public readonly type?: string,\n options?: BaseErrorOptions,\n ) {\n super({ ...options, context: { ...(options?.context ?? {}), status, detail, type } });\n }\n}\n\n/** JMAP send payload missing required fields (`to` or body text). */\nexport class JmapSendMessageInvalidError extends BaseError.extend(\n 'JmapSendMessageInvalidError',\n 'Missing \"to\" or content in message.',\n) {}\n\n/** No JMAP identity permits the connection account as a `from` address, so the email cannot be sent. */\nexport class JmapSendIdentityNotFoundError extends BaseError.extend(\n 'JmapSendIdentityNotFoundError',\n 'No JMAP identity matches the connection account.',\n) {\n constructor(account: string | undefined, options?: Omit<BaseErrorOptions, 'context'>) {\n super({ context: { account }, ...options });\n }\n}\n"],"mappings":";;;;;;;;AAYA,IAAa,eAAb,cAAkC,UAAU,OAAO,gBAAgB,0BAA0B,CAAC,CAAC;CAE3E;CACA;CACA;CAHlB,YACE,QACA,QACA,MACA,SACA;EACA,MAAM;GAAE,GAAG;GAAS,SAAS;IAAE,GAAI,SAAS,WAAW,CAAC;IAAI;IAAQ;IAAQ;GAAK;EAAE,CAAC;EALpE,KAAA,SAAA;EACA,KAAA,SAAA;EACA,KAAA,OAAA;CAIlB;AACF;;AAGA,IAAa,8BAAb,cAAiD,UAAU,OACzD,+BACA,uCACF,CAAC,CAAC,CAAC;;AAGH,IAAa,gCAAb,cAAmD,UAAU,OAC3D,iCACA,kDACF,CAAC,CAAC;CACA,YAAY,SAA6B,SAA6C;EACpF,MAAM;GAAE,SAAS,EAAE,QAAQ;GAAG,GAAG;EAAQ,CAAC;CAC5C;AACF"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect";
|
|
2
|
+
import { Database, Obj, Ref } from "@dxos/echo";
|
|
3
|
+
import * as Operation from "@dxos/compute/Operation";
|
|
4
|
+
import * as InboxOperation from "@dxos/plugin-inbox/InboxOperation";
|
|
5
|
+
import * as Mailbox from "@dxos/plugin-inbox/Mailbox";
|
|
6
|
+
import { SyncDatabaseMissingError } from "@dxos/app-toolkit";
|
|
7
|
+
//#region src/operations/mail/materialize/handler.ts
|
|
8
|
+
/**
|
|
9
|
+
* Eagerly materializes a local Mailbox so the sync cursor's target exists before the cursor is
|
|
10
|
+
* created. JMAP is a single-target connector (the account inbox), so a fresh Mailbox is always
|
|
11
|
+
* created; the connection's `accessToken.account` seeds the default name. Mirrors the Gmail
|
|
12
|
+
* materialize-target.
|
|
13
|
+
*/
|
|
14
|
+
var handler = InboxOperation.MaterializeJmapTarget.pipe(Operation.withHandler(Effect.fnUntraced(function* ({ connection }) {
|
|
15
|
+
const connectionObj = connection.target;
|
|
16
|
+
const db = connectionObj ? Obj.getDatabase(connectionObj) : void 0;
|
|
17
|
+
if (!connectionObj || !db) return yield* Effect.fail(new SyncDatabaseMissingError());
|
|
18
|
+
return yield* Effect.gen(function* () {
|
|
19
|
+
const name = (yield* Database.load(connectionObj.accessToken)).account ?? "Inbox";
|
|
20
|
+
const created = yield* Database.add(Mailbox.make({ name }));
|
|
21
|
+
return { target: Ref.make(created) };
|
|
22
|
+
}).pipe(Effect.provide(Database.layer(db)));
|
|
23
|
+
})));
|
|
24
|
+
//#endregion
|
|
25
|
+
export { handler as default };
|
|
26
|
+
|
|
27
|
+
//# sourceMappingURL=chunk-handler.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chunk-handler.mjs","names":[],"sources":["../../src/operations/mail/materialize/handler.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\nimport * as Effect from 'effect/Effect';\n\nimport { SyncDatabaseMissingError } from '@dxos/app-toolkit';\nimport * as Operation from '@dxos/compute/Operation';\nimport { Database, Obj, Ref } from '@dxos/echo';\nimport * as InboxOperation from '@dxos/plugin-inbox/InboxOperation';\nimport * as Mailbox from '@dxos/plugin-inbox/Mailbox';\n\n/**\n * Eagerly materializes a local Mailbox so the sync cursor's target exists before the cursor is\n * created. JMAP is a single-target connector (the account inbox), so a fresh Mailbox is always\n * created; the connection's `accessToken.account` seeds the default name. Mirrors the Gmail\n * materialize-target.\n */\nconst handler: Operation.WithHandler<typeof InboxOperation.MaterializeJmapTarget> =\n InboxOperation.MaterializeJmapTarget.pipe(\n Operation.withHandler(\n Effect.fnUntraced(function* ({ connection }) {\n // The operation derives the db from the connection ref's target and provides\n // `Database.layer(db)` itself (composer's invoker is wired without a `databaseResolver`).\n const connectionObj = connection.target;\n const db = connectionObj ? Obj.getDatabase(connectionObj) : undefined;\n if (!connectionObj || !db) {\n return yield* Effect.fail(new SyncDatabaseMissingError());\n }\n\n return yield* Effect.gen(function* () {\n const accessToken = yield* Database.load(connectionObj.accessToken);\n const name = accessToken.account ?? 'Inbox';\n const created = yield* Database.add(Mailbox.make({ name }));\n return { target: Ref.make(created) };\n }).pipe(Effect.provide(Database.layer(db)));\n }),\n ),\n );\n\nexport default handler;\n"],"mappings":";;;;;;;;;;;;;AAkBA,IAAM,UACJ,eAAe,sBAAsB,KACnC,UAAU,YACR,OAAO,WAAW,WAAW,EAAE,cAAc;CAG3C,MAAM,gBAAgB,WAAW;CACjC,MAAM,KAAK,gBAAgB,IAAI,YAAY,aAAa,IAAI,KAAA;CAC5D,IAAI,CAAC,iBAAiB,CAAC,IACrB,OAAO,OAAO,OAAO,KAAK,IAAI,yBAAyB,CAAC;CAG1D,OAAO,OAAO,OAAO,IAAI,aAAa;EAEpC,MAAM,QAAO,OADc,SAAS,KAAK,cAAc,WAAW,EAAA,CACzC,WAAW;EACpC,MAAM,UAAU,OAAO,SAAS,IAAI,QAAQ,KAAK,EAAE,KAAK,CAAC,CAAC;EAC1D,OAAO,EAAE,QAAQ,IAAI,KAAK,OAAO,EAAE;CACrC,CAAC,CAAC,CAAC,KAAK,OAAO,QAAQ,SAAS,MAAM,EAAE,CAAC,CAAC;AAC5C,CAAC,CACH,CACF"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { subDays } from "date-fns";
|
|
2
|
+
import { random } from "@dxos/random";
|
|
3
|
+
//#region src/testing/jmap-fixtures.ts
|
|
4
|
+
var MAIL_ACCOUNT_CAPABILITY = "urn:ietf:params:jmap:mail";
|
|
5
|
+
var ACCOUNT_ID = "test-account";
|
|
6
|
+
var INBOX_ID = "mb-inbox";
|
|
7
|
+
/** System folders every JMAP account has; the sync excludes trash/junk/drafts from the default scope. */
|
|
8
|
+
var SYSTEM_FOLDERS = [
|
|
9
|
+
{
|
|
10
|
+
id: INBOX_ID,
|
|
11
|
+
name: "Inbox",
|
|
12
|
+
role: "inbox"
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
id: "mb-sent",
|
|
16
|
+
name: "Sent",
|
|
17
|
+
role: "sent"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
id: "mb-drafts",
|
|
21
|
+
name: "Drafts",
|
|
22
|
+
role: "drafts"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
id: "mb-trash",
|
|
26
|
+
name: "Trash",
|
|
27
|
+
role: "trash"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
id: "mb-junk",
|
|
31
|
+
name: "Junk",
|
|
32
|
+
role: "junk"
|
|
33
|
+
}
|
|
34
|
+
];
|
|
35
|
+
/** The session a {@link JmapMailApi.mock} serves — a single mail account discovered at `apiUrl`. */
|
|
36
|
+
var SESSION = {
|
|
37
|
+
apiUrl: "https://jmap.test/api/",
|
|
38
|
+
username: "test@jmap.test",
|
|
39
|
+
primaryAccounts: { [MAIL_ACCOUNT_CAPABILITY]: ACCOUNT_ID },
|
|
40
|
+
downloadUrl: "https://jmap.test/download/{accountId}/{blobId}/{name}?type={type}"
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Deterministically generates a {@link JmapDataset} for driving {@link JmapMailApi.mock} in unit
|
|
44
|
+
* tests — no live account. Emails are ordered ascending by `receivedAt` and spread across the
|
|
45
|
+
* [start, end] window (all in the Inbox) so the sync's window walk + pagination exercise realistically.
|
|
46
|
+
* Mirrors `generateGmailDataset`.
|
|
47
|
+
*/
|
|
48
|
+
var generateJmapDataset = (options = {}) => {
|
|
49
|
+
const count = options.count ?? 100;
|
|
50
|
+
const threadCount = Math.max(1, options.threads ?? Math.ceil(count / 3));
|
|
51
|
+
const senderCount = Math.max(1, options.senders ?? Math.ceil(count / 5));
|
|
52
|
+
const end = options.end ?? /* @__PURE__ */ new Date();
|
|
53
|
+
const start = options.start ?? subDays(end, 90);
|
|
54
|
+
const idPrefix = options.idPrefix ?? "eml";
|
|
55
|
+
random.seed(options.seed ?? 42);
|
|
56
|
+
const customFolders = (options.folders ?? ["Work", "Personal"]).map((name, index) => ({
|
|
57
|
+
id: `mb-custom-${index + 1}`,
|
|
58
|
+
name,
|
|
59
|
+
role: null
|
|
60
|
+
}));
|
|
61
|
+
const folders = [...SYSTEM_FOLDERS, ...customFolders];
|
|
62
|
+
const senders = Array.from({ length: senderCount }, () => ({
|
|
63
|
+
name: random.person.fullName(),
|
|
64
|
+
email: random.internet.email()
|
|
65
|
+
}));
|
|
66
|
+
const threadIds = Array.from({ length: threadCount }, (_, index) => `${idPrefix}-thread-${index}`);
|
|
67
|
+
const startMs = start.getTime();
|
|
68
|
+
const endMs = end.getTime();
|
|
69
|
+
return {
|
|
70
|
+
session: SESSION,
|
|
71
|
+
folders,
|
|
72
|
+
emails: Array.from({ length: count }, (_, index) => {
|
|
73
|
+
const sender = random.helpers.arrayElement(senders);
|
|
74
|
+
const threadId = random.helpers.arrayElement(threadIds);
|
|
75
|
+
const receivedAtMs = Math.round(startMs + (endMs - startMs) * index / Math.max(1, count));
|
|
76
|
+
const subject = random.lorem.sentence();
|
|
77
|
+
const body = random.lorem.paragraph();
|
|
78
|
+
return {
|
|
79
|
+
id: `${idPrefix}-${index}`,
|
|
80
|
+
threadId,
|
|
81
|
+
mailboxIds: { [INBOX_ID]: true },
|
|
82
|
+
from: [{
|
|
83
|
+
name: sender.name,
|
|
84
|
+
email: sender.email
|
|
85
|
+
}],
|
|
86
|
+
to: [{ email: "me@jmap.test" }],
|
|
87
|
+
subject,
|
|
88
|
+
receivedAt: new Date(receivedAtMs).toISOString(),
|
|
89
|
+
preview: body.slice(0, 100),
|
|
90
|
+
bodyValues: { body: { value: body } },
|
|
91
|
+
textBody: [{
|
|
92
|
+
partId: "body",
|
|
93
|
+
type: "text/plain"
|
|
94
|
+
}]
|
|
95
|
+
};
|
|
96
|
+
})
|
|
97
|
+
};
|
|
98
|
+
};
|
|
99
|
+
//#endregion
|
|
100
|
+
export { generateJmapDataset as t };
|
|
101
|
+
|
|
102
|
+
//# sourceMappingURL=chunk-jmap-fixtures.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chunk-jmap-fixtures.mjs","names":[],"sources":["../../src/testing/jmap-fixtures.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\nimport { subDays } from 'date-fns';\n\nimport { random } from '@dxos/random';\n\nimport { JmapMail } from '../apis';\nimport { type JmapDataset } from '../services';\n\nexport interface GenerateJmapDatasetOptions {\n /** Number of emails to generate. */\n count?: number;\n /** Distinct conversation threads to spread emails across. Defaults to ~count/3. */\n threads?: number;\n /** Distinct senders. Defaults to ~count/5. */\n senders?: number;\n /** Custom (user) folder names, in addition to the always-present system folders. */\n folders?: readonly string[];\n /** Seed for deterministic output (same seed → same dataset). */\n seed?: number;\n /** `receivedAt` window. Defaults to [now − 90 days, now]. */\n start?: Date;\n end?: Date;\n /** Prefix for email + thread ids, so disjoint datasets (e.g. date bands) don't collide. Defaults to `eml`. */\n idPrefix?: string;\n}\n\nconst MAIL_ACCOUNT_CAPABILITY = 'urn:ietf:params:jmap:mail';\nconst ACCOUNT_ID = 'test-account';\nconst INBOX_ID = 'mb-inbox';\n\n/** System folders every JMAP account has; the sync excludes trash/junk/drafts from the default scope. */\nconst SYSTEM_FOLDERS: readonly JmapMail.Mailbox[] = [\n { id: INBOX_ID, name: 'Inbox', role: 'inbox' },\n { id: 'mb-sent', name: 'Sent', role: 'sent' },\n { id: 'mb-drafts', name: 'Drafts', role: 'drafts' },\n { id: 'mb-trash', name: 'Trash', role: 'trash' },\n { id: 'mb-junk', name: 'Junk', role: 'junk' },\n];\n\n/** The session a {@link JmapMailApi.mock} serves — a single mail account discovered at `apiUrl`. */\nconst SESSION = {\n apiUrl: 'https://jmap.test/api/',\n username: 'test@jmap.test',\n primaryAccounts: { [MAIL_ACCOUNT_CAPABILITY]: ACCOUNT_ID },\n downloadUrl: 'https://jmap.test/download/{accountId}/{blobId}/{name}?type={type}',\n};\n\n/**\n * Deterministically generates a {@link JmapDataset} for driving {@link JmapMailApi.mock} in unit\n * tests — no live account. Emails are ordered ascending by `receivedAt` and spread across the\n * [start, end] window (all in the Inbox) so the sync's window walk + pagination exercise realistically.\n * Mirrors `generateGmailDataset`.\n */\nexport const generateJmapDataset = (options: GenerateJmapDatasetOptions = {}): JmapDataset => {\n const count = options.count ?? 100;\n const threadCount = Math.max(1, options.threads ?? Math.ceil(count / 3));\n const senderCount = Math.max(1, options.senders ?? Math.ceil(count / 5));\n const end = options.end ?? new Date();\n const start = options.start ?? subDays(end, 90);\n const idPrefix = options.idPrefix ?? 'eml';\n random.seed(options.seed ?? 42);\n\n const customFolders: JmapMail.Mailbox[] = (options.folders ?? ['Work', 'Personal']).map((name, index) => ({\n id: `mb-custom-${index + 1}`,\n name,\n role: null,\n }));\n const folders = [...SYSTEM_FOLDERS, ...customFolders];\n\n const senders = Array.from({ length: senderCount }, () => ({\n name: random.person.fullName(),\n email: random.internet.email(),\n }));\n const threadIds = Array.from({ length: threadCount }, (_, index) => `${idPrefix}-thread-${index}`);\n\n const startMs = start.getTime();\n const endMs = end.getTime();\n\n const emails: JmapMail.Email[] = Array.from({ length: count }, (_, index) => {\n const sender = random.helpers.arrayElement(senders);\n const threadId = random.helpers.arrayElement(threadIds);\n // Monotonic ascending across the window (the mock relies on ascending receivedAt).\n const receivedAtMs = Math.round(startMs + ((endMs - startMs) * index) / Math.max(1, count));\n const subject = random.lorem.sentence();\n const body = random.lorem.paragraph();\n\n return {\n id: `${idPrefix}-${index}`,\n threadId,\n mailboxIds: { [INBOX_ID]: true },\n from: [{ name: sender.name, email: sender.email }],\n to: [{ email: 'me@jmap.test' }],\n subject,\n receivedAt: new Date(receivedAtMs).toISOString(),\n preview: body.slice(0, 100),\n bodyValues: { body: { value: body } },\n textBody: [{ partId: 'body', type: 'text/plain' }],\n };\n });\n\n return { session: SESSION, folders, emails };\n};\n"],"mappings":";;;AA6BA,IAAM,0BAA0B;AAChC,IAAM,aAAa;AACnB,IAAM,WAAW;;AAGjB,IAAM,iBAA8C;CAClD;EAAE,IAAI;EAAU,MAAM;EAAS,MAAM;CAAQ;CAC7C;EAAE,IAAI;EAAW,MAAM;EAAQ,MAAM;CAAO;CAC5C;EAAE,IAAI;EAAa,MAAM;EAAU,MAAM;CAAS;CAClD;EAAE,IAAI;EAAY,MAAM;EAAS,MAAM;CAAQ;CAC/C;EAAE,IAAI;EAAW,MAAM;EAAQ,MAAM;CAAO;AAC9C;;AAGA,IAAM,UAAU;CACd,QAAQ;CACR,UAAU;CACV,iBAAiB,GAAG,0BAA0B,WAAW;CACzD,aAAa;AACf;;;;;;;AAQA,IAAa,uBAAuB,UAAsC,CAAC,MAAmB;CAC5F,MAAM,QAAQ,QAAQ,SAAS;CAC/B,MAAM,cAAc,KAAK,IAAI,GAAG,QAAQ,WAAW,KAAK,KAAK,QAAQ,CAAC,CAAC;CACvE,MAAM,cAAc,KAAK,IAAI,GAAG,QAAQ,WAAW,KAAK,KAAK,QAAQ,CAAC,CAAC;CACvE,MAAM,MAAM,QAAQ,uBAAO,IAAI,KAAK;CACpC,MAAM,QAAQ,QAAQ,SAAS,QAAQ,KAAK,EAAE;CAC9C,MAAM,WAAW,QAAQ,YAAY;CACrC,OAAO,KAAK,QAAQ,QAAQ,EAAE;CAE9B,MAAM,iBAAqC,QAAQ,WAAW,CAAC,QAAQ,UAAU,EAAA,CAAG,KAAK,MAAM,WAAW;EACxG,IAAI,aAAa,QAAQ;EACzB;EACA,MAAM;CACR,EAAE;CACF,MAAM,UAAU,CAAC,GAAG,gBAAgB,GAAG,aAAa;CAEpD,MAAM,UAAU,MAAM,KAAK,EAAE,QAAQ,YAAY,UAAU;EACzD,MAAM,OAAO,OAAO,SAAS;EAC7B,OAAO,OAAO,SAAS,MAAM;CAC/B,EAAE;CACF,MAAM,YAAY,MAAM,KAAK,EAAE,QAAQ,YAAY,IAAI,GAAG,UAAU,GAAG,SAAS,UAAU,OAAO;CAEjG,MAAM,UAAU,MAAM,QAAQ;CAC9B,MAAM,QAAQ,IAAI,QAAQ;CAwB1B,OAAO;EAAE,SAAS;EAAS;EAAS,QAtBH,MAAM,KAAK,EAAE,QAAQ,MAAM,IAAI,GAAG,UAAU;GAC3E,MAAM,SAAS,OAAO,QAAQ,aAAa,OAAO;GAClD,MAAM,WAAW,OAAO,QAAQ,aAAa,SAAS;GAEtD,MAAM,eAAe,KAAK,MAAM,WAAY,QAAQ,WAAW,QAAS,KAAK,IAAI,GAAG,KAAK,CAAC;GAC1F,MAAM,UAAU,OAAO,MAAM,SAAS;GACtC,MAAM,OAAO,OAAO,MAAM,UAAU;GAEpC,OAAO;IACL,IAAI,GAAG,SAAS,GAAG;IACnB;IACA,YAAY,GAAG,WAAW,KAAK;IAC/B,MAAM,CAAC;KAAE,MAAM,OAAO;KAAM,OAAO,OAAO;IAAM,CAAC;IACjD,IAAI,CAAC,EAAE,OAAO,eAAe,CAAC;IAC9B;IACA,YAAY,IAAI,KAAK,YAAY,CAAC,CAAC,YAAY;IAC/C,SAAS,KAAK,MAAM,GAAG,GAAG;IAC1B,YAAY,EAAE,MAAM,EAAE,OAAO,KAAK,EAAE;IACpC,UAAU,CAAC;KAAE,QAAQ;KAAQ,MAAM;IAAa,CAAC;GACnD;EACF,CAEoC;CAAO;AAC7C"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { r as JMAP_MAIL_CONNECTOR_ID } from "./chunk-constants.mjs";
|
|
2
|
+
import * as Effect from "effect/Effect";
|
|
3
|
+
import * as Capability from "@dxos/app-framework/Capability";
|
|
4
|
+
import * as InboxCapabilities from "@dxos/plugin-inbox/InboxCapabilities";
|
|
5
|
+
import * as InboxOperation from "@dxos/plugin-inbox/InboxOperation";
|
|
6
|
+
//#region src/capabilities/mail-send.ts
|
|
7
|
+
/** Routes a draft whose mailbox is bound to a JMAP connection to this provider's send operation. */
|
|
8
|
+
var mail_send_default = Capability.makeModule(Effect.fnUntraced(function* () {
|
|
9
|
+
return Capability.contribute(InboxCapabilities.MailSendOperation, {
|
|
10
|
+
connectorId: JMAP_MAIL_CONNECTOR_ID,
|
|
11
|
+
getOperation: () => InboxOperation.JmapSend
|
|
12
|
+
});
|
|
13
|
+
}));
|
|
14
|
+
//#endregion
|
|
15
|
+
export { mail_send_default as default };
|
|
16
|
+
|
|
17
|
+
//# sourceMappingURL=chunk-mail-send.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chunk-mail-send.mjs","names":[],"sources":["../../src/capabilities/mail-send.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\nimport * as Effect from 'effect/Effect';\n\nimport * as Capability from '@dxos/app-framework/Capability';\nimport * as InboxCapabilities from '@dxos/plugin-inbox/InboxCapabilities';\nimport * as InboxOperation from '@dxos/plugin-inbox/InboxOperation';\n\nimport { JMAP_MAIL_CONNECTOR_ID } from '../constants';\n\n/** Routes a draft whose mailbox is bound to a JMAP connection to this provider's send operation. */\nexport default Capability.makeModule(\n Effect.fnUntraced(function* () {\n return Capability.contribute(InboxCapabilities.MailSendOperation, {\n connectorId: JMAP_MAIL_CONNECTOR_ID,\n getOperation: () => InboxOperation.JmapSend,\n });\n }),\n);\n"],"mappings":";;;;;;;AAaA,IAAA,oBAAe,WAAW,WACxB,OAAO,WAAW,aAAa;CAC7B,OAAO,WAAW,WAAW,kBAAkB,mBAAmB;EAChE,aAAa;EACb,oBAAoB,eAAe;CACrC,CAAC;AACH,CAAC,CACH"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import * as Plugin from "@dxos/app-framework/Plugin";
|
|
2
|
+
import { Config2 } from "@dxos/app-framework/config";
|
|
3
|
+
import { trim } from "@dxos/util";
|
|
4
|
+
//#region dx.config.ts
|
|
5
|
+
var dx_config_default = Config2.make({ plugin: {
|
|
6
|
+
key: "org.dxos.plugin.jmap",
|
|
7
|
+
name: "JMAP",
|
|
8
|
+
author: "DXOS",
|
|
9
|
+
source: "https://github.com/dxos/dxos/tree/main/packages/plugins/plugin-jmap",
|
|
10
|
+
description: trim`
|
|
11
|
+
Syncs mail from any JMAP server (RFC 8620/8621) into a local-first Mailbox. Fastmail is the
|
|
12
|
+
canonical provider; the session is discovered at the well-known JMAP endpoint on the host you
|
|
13
|
+
supply.
|
|
14
|
+
|
|
15
|
+
Headless: this plugin contributes the JMAP connector, its credential form, and the sync, send
|
|
16
|
+
and materialize operations. The mailbox itself, its UI, and the provider-agnostic sync harness
|
|
17
|
+
belong to the Inbox plugin, which this one depends on.
|
|
18
|
+
|
|
19
|
+
Credentials are a host, an account email, and a Bearer API token, validated against the live
|
|
20
|
+
session when the form is submitted — no OAuth. Sync is incremental: after the first run the
|
|
21
|
+
server's state string drives a bounded delta per run, and folder and keyword changes reconcile
|
|
22
|
+
against messages already in the feed.
|
|
23
|
+
`,
|
|
24
|
+
dependsOn: ["org.dxos.plugin.inbox"],
|
|
25
|
+
icon: {
|
|
26
|
+
key: "ph--envelope-simple-open--regular",
|
|
27
|
+
hue: "green"
|
|
28
|
+
},
|
|
29
|
+
tags: ["alpha", "connector"]
|
|
30
|
+
} });
|
|
31
|
+
//#endregion
|
|
32
|
+
//#region src/meta.ts
|
|
33
|
+
var meta = Plugin.getMetaFromConfig(dx_config_default);
|
|
34
|
+
//#endregion
|
|
35
|
+
export { meta as t };
|
|
36
|
+
|
|
37
|
+
//# sourceMappingURL=chunk-meta.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chunk-meta.mjs","names":[],"sources":["../../dx.config.ts","../../src/meta.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\nimport { Config2 } from '@dxos/app-framework/config';\nimport { trim } from '@dxos/util';\n\nexport default Config2.make({\n plugin: {\n key: 'org.dxos.plugin.jmap',\n name: 'JMAP',\n author: 'DXOS',\n source: 'https://github.com/dxos/dxos/tree/main/packages/plugins/plugin-jmap',\n description: trim`\n Syncs mail from any JMAP server (RFC 8620/8621) into a local-first Mailbox. Fastmail is the\n canonical provider; the session is discovered at the well-known JMAP endpoint on the host you\n supply.\n\n Headless: this plugin contributes the JMAP connector, its credential form, and the sync, send\n and materialize operations. The mailbox itself, its UI, and the provider-agnostic sync harness\n belong to the Inbox plugin, which this one depends on.\n\n Credentials are a host, an account email, and a Bearer API token, validated against the live\n session when the form is submitted — no OAuth. Sync is incremental: after the first run the\n server's state string drives a bounded delta per run, and folder and keyword changes reconcile\n against messages already in the feed.\n `,\n dependsOn: ['org.dxos.plugin.inbox'],\n icon: { key: 'ph--envelope-simple-open--regular', hue: 'green' },\n tags: ['alpha', 'connector'],\n },\n});\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport * as Plugin from '@dxos/app-framework/Plugin';\n\nimport config from '../dx.config';\n\nexport const meta = Plugin.getMetaFromConfig(config);\n"],"mappings":";;;;AAOA,IAAA,oBAAe,QAAQ,KAAK,EAC1B,QAAQ;CACN,KAAK;CACL,MAAM;CACN,QAAQ;CACR,QAAQ;CACR,aAAa,IAAI;;;;;;;;;;;;;;CAcjB,WAAW,CAAC,uBAAuB;CACnC,MAAM;EAAE,KAAK;EAAqC,KAAK;CAAQ;CAC/D,MAAM,CAAC,SAAS,WAAW;AAC7B,EACF,CAAC;;;ACvBD,IAAa,OAAO,OAAO,kBAAkB,iBAAM"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect";
|
|
2
|
+
import * as Capability from "@dxos/app-framework/Capability";
|
|
3
|
+
import * as Capabilities from "@dxos/app-framework/Capabilities";
|
|
4
|
+
import { JmapOperationHandlerSet } from "#operations";
|
|
5
|
+
//#region src/capabilities/operation-handler.ts
|
|
6
|
+
var operation_handler_default = Capability.makeModule(Effect.fnUntraced(function* () {
|
|
7
|
+
return Capability.contribute(Capabilities.OperationHandler, JmapOperationHandlerSet);
|
|
8
|
+
}));
|
|
9
|
+
//#endregion
|
|
10
|
+
export { operation_handler_default as default };
|
|
11
|
+
|
|
12
|
+
//# sourceMappingURL=chunk-operation-handler.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chunk-operation-handler.mjs","names":[],"sources":["../../src/capabilities/operation-handler.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\nimport * as Effect from 'effect/Effect';\n\nimport * as Capabilities from '@dxos/app-framework/Capabilities';\nimport * as Capability from '@dxos/app-framework/Capability';\n\nimport { JmapOperationHandlerSet } from '#operations';\n\nexport default Capability.makeModule(\n Effect.fnUntraced(function* () {\n return Capability.contribute(Capabilities.OperationHandler, JmapOperationHandlerSet);\n }),\n);\n"],"mappings":";;;;;AAWA,IAAA,4BAAe,WAAW,WACxB,OAAO,WAAW,aAAa;CAC7B,OAAO,WAAW,WAAW,aAAa,kBAAkB,uBAAuB;AACrF,CAAC,CACH"}
|