From 7cc1e3b666f48f3fdc6da95eb7a759479aa5bc21 Mon Sep 17 00:00:00 2001 From: Hunter Tunnicliff Date: Fri, 29 Nov 2024 12:32:23 -0800 Subject: [PATCH 1/4] Ensure optional fields use optional modifier and not null --- src/types/jmap-mail.ts | 4 ++-- src/types/jmap.ts | 44 +++++++++++++++++++++--------------------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/types/jmap-mail.ts b/src/types/jmap-mail.ts index 35644aa..846e34a 100644 --- a/src/types/jmap-mail.ts +++ b/src/types/jmap-mail.ts @@ -961,7 +961,7 @@ export type EmailSubmission = { /** * Information for use when sending via SMTP. */ - envelope: Envelope | null; + envelope?: Envelope; /** * The date the submission was/will be released for delivery. If the * client successfully used FUTURERELEASE [RFC4865] with the @@ -1044,7 +1044,7 @@ export type EmailSubmissionAddress = { * (see [RFC3461] and [RFC6533]) and JSON string encoding is * applied. */ - parameters: Record | null; + parameters?: Record; }; /** diff --git a/src/types/jmap.ts b/src/types/jmap.ts index 628cb89..bd7139c 100644 --- a/src/types/jmap.ts +++ b/src/types/jmap.ts @@ -487,7 +487,7 @@ export type GetArguments = { * type and the number of records does not exceed the * `maxObjectsInGet` limit. */ - ids?: ReadonlyArray | null; + ids?: ReadonlyArray; /** * If supplied, only the properties listed in the array are returned * for each `T` object. If null, all properties of the object are @@ -496,7 +496,7 @@ export type GetArguments = { * requested, the call MUST be rejected with an "invalidArguments" * error. */ - properties?: ReadonlyArray | null; + properties?: ReadonlyArray; }; export type GetResponse = Args extends GetArguments @@ -574,7 +574,7 @@ export type ChangesArguments = { * is given, the server MUST reject the call with an * `invalidArguments` error. */ - maxChanges: number | null; + maxChanges?: number; }; export type ChangesResponse = { @@ -642,7 +642,7 @@ export type SetArguments = { * returned. If null, any changes will be applied to the current * state. */ - ifInState: string | null; + ifInState?: string; /** * A map of a *creation id* (a temporary id set by the client) to `T` * objects, or null if no objects are to be created. @@ -653,13 +653,13 @@ export type SetArguments = { * The client MUST omit any properties that may only be set by the * server (for example, the `id` property on most object types). */ - create: Record | null; + create?: Record; /** * A map of an id to a PatchObject to apply to the current `T` * object with that id, or null if no objects are to be updated. */ - update: PatchObject | null; - destroy: ID[] | null; + update?: PatchObject; + destroy?: ID[]; }; /** @@ -863,7 +863,7 @@ export type CopyArguments = { * `stateMismatch` error returned. If null, the data will be read * from the current state. */ - ifFromInState: string | null; + ifFromInState?: string; /** * The id of the account to copy records to. This MUST be different * to the `fromAccountId`. @@ -876,7 +876,7 @@ export type CopyArguments = { * and a `stateMismatch` error returned. If null, any changes will * be applied to the current state. */ - ifInState: string | null; + ifInState?: string; /** * A map of the *creation id* to a `T` object. The `T` object MUST * contain an `id` property, which is the id (in the fromAccount) of @@ -884,7 +884,7 @@ export type CopyArguments = { * properties included are used instead of the current value for that * property on the original. */ - create: Record | null; + create: Record; /** * If true, an attempt will be made to destroy the original records * that were successfully copied: after emitting the "T/copy" @@ -899,7 +899,7 @@ export type CopyArguments = { * implicit "T/set" call, if made at the end of this request to * destroy the originals that were successfully copied. */ - destroyFromIfInState: string | null; + destroyFromIfInState?: string; }; export type CopyResponse = { @@ -985,7 +985,7 @@ export type QueryArguments = { * Determines the set of T objects returned in the results. If null, all * objects in the account of this type are included in the results. */ - filter?: FilterOperator | FilterCondition | null; + filter?: FilterOperator | FilterCondition; /** * Lists the names of properties to compare between two `T` records, * and how to compare them, to determine which comes first in the @@ -996,7 +996,7 @@ export type QueryArguments = { * order is server dependent, but it MUST be stable between calls to * "T/query". */ - sort?: ReadonlyArray> | null; + sort?: ReadonlyArray>; /** * The zero-based index of the first id in the full list of results * to return. @@ -1018,7 +1018,7 @@ export type QueryArguments = { * the `anchorOffset` argument to determine the index of the first * result to return */ - anchor?: string | null; + anchor?: string; /** * The index of the first result to return relative to the index of * the anchor, if an anchor is given. This MAY be negative. For @@ -1035,7 +1035,7 @@ export type QueryArguments = { * value is given, the call MUST be rejected with an * `invalidArguments` error. */ - limit?: number | null; + limit?: number; /** * Does the client wish to know the total number of results in the * query? This may be slow and expensive for servers to calculate, @@ -1141,11 +1141,11 @@ export type QueryChangesArguments = { /** * The filter argument that was used with "T/query". */ - filter: FilterOperator | FilterCondition | null; + filter?: FilterOperator | FilterCondition; /** * The sort argument that was used with "T/query". */ - sort: ReadonlyArray> | null; + sort?: ReadonlyArray>; /** * The current state of the query in the client. This is the string * that was returned as the `queryState` argument in the "T/query" @@ -1157,7 +1157,7 @@ export type QueryChangesArguments = { * The maximum number of changes to return in the response. See * error descriptions below for more details. */ - maxChanges: number | null; + maxChanges?: number; /** * The last (highest-index) id the client currently has cached from * the query results. When there are a large number of results, in a @@ -1168,7 +1168,7 @@ export type QueryChangesArguments = { * significantly increase efficiency. If they are not immutable, * this argument is ignored. */ - upToId: ID | null; + upToId?: ID; /** * Does the client wish to know the total number of results now in * the query? This may be slow and expensive for servers to @@ -1491,7 +1491,7 @@ export type PushSubscription = { * a push message, and the client updates the PushSubscription object * with the code; see Section 7.2.2 for details. */ - verificationCode?: string | null; + verificationCode?: string; /** * The time this push subscription expires. If specified, the JMAP * server MUST NOT make further requests to this resource after this @@ -1502,7 +1502,7 @@ export type PushSubscription = { * client or modify the expiry time given by the client to a shorter * duration. */ - expires?: UTCDate | null; + expires?: UTCDate; /** * A list of types the client is interested in (using the same names * as the keys in the TypeState object defined in the previous @@ -1511,7 +1511,7 @@ export type PushSubscription = { * the TypeState object. If null, changes will be pushed for all * types. */ - types?: string[] | null; + types?: string[]; }; /** From 21d13fd26f4c781c377dcef71a1780a680dc42d0 Mon Sep 17 00:00:00 2001 From: Hunter Tunnicliff Date: Fri, 29 Nov 2024 12:58:04 -0800 Subject: [PATCH 2/4] Omit server-set fields from set arguments --- src/types/contracts.ts | 21 +++++--- src/types/jmap-mail.ts | 116 +++++++++++++++++++++++++++++++++++++++++ src/types/jmap.ts | 22 ++++++-- 3 files changed, 149 insertions(+), 10 deletions(-) diff --git a/src/types/contracts.ts b/src/types/contracts.ts index e5aff81..1181a57 100644 --- a/src/types/contracts.ts +++ b/src/types/contracts.ts @@ -2,18 +2,23 @@ import type { Exact } from "type-fest"; import type { HasAllKeysOfRelated } from "../helpers.ts"; import type { Email, + EmailCreate, EmailFilterCondition, EmailImport, EmailSubmission, + EmailSubmissionCreate, EmailSubmissionFilterCondition, GetValueFromHeaderKey, HeaderFieldKey, Identity, + IdentityCreate, Mailbox, + MailboxCreate, MailboxFilterCondition, SearchSnippet, Thread, - VacationResponse + VacationResponse, + VacationResponseCreate } from "./jmap-mail.ts"; import type { BlobCopyArguments, @@ -30,6 +35,8 @@ import type { Request as JMAPRequest, Response as JMAPResponse, ProblemDetails, + PushSubscription, + PushSubscriptionCreate, QueryArguments, QueryChangesArguments, QueryChangesResponse, @@ -47,7 +54,7 @@ export type Requests = { // Push Subscription ---------------------- "PushSubscription/get": Omit, "accountId">; "PushSubscription/set": Omit< - SetArguments, + SetArguments, "accountId" | "ifInState" >; // Mailbox -------------------------------- @@ -61,7 +68,7 @@ export type Requests = { Mailbox, MailboxFilterCondition >; - "Mailbox/set": SetArguments & { + "Mailbox/set": SetArguments & { onDestroyRemoveEmails?: boolean; }; // Thread --------------------------------- @@ -76,7 +83,7 @@ export type Requests = { "Email/queryChanges": QueryChangesArguments & { collapseThreads?: boolean; }; - "Email/set": SetArguments>; + "Email/set": SetArguments; "Email/copy": CopyArguments< Pick >; @@ -107,7 +114,7 @@ export type Requests = { // Identity ------------------------------- "Identity/get": GetArguments; "Identity/changes": ChangesArguments; - "Identity/set": SetArguments; + "Identity/set": SetArguments; // Email Submission ----------------------- "EmailSubmission/get": GetArguments; "EmailSubmission/changes": ChangesArguments; @@ -119,13 +126,13 @@ export type Requests = { EmailSubmission, EmailSubmissionFilterCondition >; - "EmailSubmission/set": SetArguments & { + "EmailSubmission/set": SetArguments & { onSuccessUpdateEmail?: Record> | null; onSuccessDestroyEmail?: ID[] | null; }; // Vacation Response ---------------------- "VacationResponse/get": GetArguments; - "VacationResponse/set": SetArguments; + "VacationResponse/set": SetArguments; }; export type Methods = keyof Requests; diff --git a/src/types/jmap-mail.ts b/src/types/jmap-mail.ts index 846e34a..788162f 100644 --- a/src/types/jmap-mail.ts +++ b/src/types/jmap-mail.ts @@ -1,3 +1,4 @@ +import type { OmitDeep } from "type-fest"; import type { FilterCondition, ID, UTCDate } from "./jmap.ts"; /** @@ -32,6 +33,9 @@ export type Entity = keyof Entities; export type Mailbox = { /** * The id of the Mailbox + * + * @kind immutable + * @kind server-set */ id: ID; /** @@ -69,20 +73,28 @@ export type Mailbox = { sortOrder: number; /** * The number of Emails in this Mailbox. + * + * @kind server-set */ totalEmails: number; /** * The number of Emails in this Mailbox that have neither the "$seen" * keyword nor the "$draft" keyword. + * + * @kind server-set */ unreadEmails: number; /** * The number of Threads where at least one Email in the Thread is in * this Mailbox. + * + * @kind server-set */ totalThreads: number; /** * An indication of the number of "unread" Threads in the Mailbox. + * + * @kind server-set */ unreadThreads: number; /** @@ -94,6 +106,8 @@ export type Mailbox = { * The set of rights (Access Control Lists (ACLs)) the user has in * relation to this Mailbox. These are backwards compatible with * IMAP ACLs, as defined in [rfc4314](https://datatracker.ietf.org/doc/html/rfc4314). + * + * @kind server-set */ myRights: { /** @@ -157,6 +171,17 @@ export type Mailbox = { }; }; +export type MailboxCreate = Omit< + Mailbox, + // Fields set by server + | "id" + | "totalEmails" + | "unreadEmails" + | "totalThreads" + | "unreadThreads" + | "myRights" +>; + /** * [rfc8621 § 2.3](https://datatracker.ietf.org/doc/html/rfc8621#section-2.3) */ @@ -182,6 +207,9 @@ export type MailboxFilterCondition = { export type Thread = { /** * The id of the Thread. + * + * @kind immutable + * @kind server-set */ id: ID; /** @@ -189,6 +217,8 @@ export type Thread = { * date of the Email, oldest first. If two Emails have an identical * date, the sort is server dependent but MUST be stable (sorting by * id is recommended). + * + * @kind server-set */ emailIds: ID[]; }; @@ -208,6 +238,20 @@ export type Email = EmailMetadataFields & EmailHeaderFields & EmailBodyPartFields; +// TODO: Support exclusive patterns described in [rfc8621 § 4.6](https://datatracker.ietf.org/doc/html/rfc8621#section-4.6) +export type EmailCreate = Partial< + Omit< + Email, + | "id" + | "blobId" + | "threadId" + | "size" + | "hasAttachment" + | "preview" + | "headers" + > +>; + /** * [rfc8621 § 4.1.1](https://datatracker.ietf.org/doc/html/rfc8621#section-4.1.1) * @@ -218,16 +262,25 @@ type EmailMetadataFields = { /** * The id of the Email object. Note that this is the JMAP object id, * NOT the Message-ID header field value of the message [rfc5322](https://datatracker.ietf.org/doc/html/rfc5322). + * + * @kind immutable + * @kind server-set */ id: ID; /** * The id representing the raw octets of the message [rfc5322](https://datatracker.ietf.org/doc/html/rfc5322) for * this Email. This may be used to download the raw original message * or to attach it directly to another Email, etc. + * + * @kind immutable + * @kind server-set */ blobId: ID; /** * The id of the Thread to which this Email belongs. + * + * @kind immutable + * @kind server-set */ threadId: ID; /** @@ -269,11 +322,16 @@ type EmailMetadataFields = { * The size, in octets, of the raw data for the message [rfc5322](https://datatracker.ietf.org/doc/html/rfc5322) (as * referenced by the "blobId", i.e., the number of octets in the file * the user would download). + * + * @kind immutable + * @kind server-set */ size: number; /** * A UTC Date – The date the Email was received by the message store. This is the * "internal date" in IMAP [rfc3501](https://datatracker.ietf.org/doc/html/rfc3501). + * + * @kind immutable */ receivedAt: string; }; @@ -441,6 +499,8 @@ export type EmailHeader = { /** * [rfc8621 § 4.1.3](https://datatracker.ietf.org/doc/html/rfc8621#section-4.1.3) + * + * @kind immutable */ type EmailHeaderFields = { /** @@ -590,6 +650,8 @@ type EmailBodyPartFields = { * recursing into `message/rfc822` or `message/global` parts. Note * that EmailBodyParts may have subParts if they are of type * `multipart/*`. + * + * @kind immutable */ bodyStructure: EmailBodyPart; /** @@ -597,6 +659,8 @@ type EmailBodyPartFields = { * some, or all `text/*` parts. Which parts are included and whether * the value is truncated is determined by various arguments to * `Email/get` and `Email/parse`. + * + * @kind immutable */ bodyValues: Record; /** @@ -604,6 +668,8 @@ type EmailBodyPartFields = { * `video/*` parts to display (sequentially) as the message body, * with a preference for `text/plain` when alternative versions are * available. + * + * @kind immutable */ textBody: EmailBodyPart[]; /** @@ -611,6 +677,8 @@ type EmailBodyPartFields = { * `video/*` parts to display (sequentially) as the message body, * with a preference for `text/html` when alternative versions are * available. + * + * @kind immutable */ htmlBody: EmailBodyPart[]; /** @@ -627,6 +695,8 @@ type EmailBodyPartFields = { * Note that a `text/html` body part [HTML] may reference image parts * in attachments by using `cid:` links to reference the Content-Id, * as defined in [RFC2392], or by referencing the Content-Location. + * + * @kind immutable */ attachments: EmailBodyPart[]; /** @@ -637,6 +707,9 @@ type EmailBodyPartFields = { * server MAY ignore parts in this list that are processed * automatically in some way or are referenced as embedded images in * one of the `text/html` parts of the message. + * + * @kind immutable + * @kind server-set */ hasAttachment: boolean; /** @@ -655,6 +728,9 @@ type EmailBodyPartFields = { * the previous value is not considered incorrect, and the change * SHOULD NOT cause the Email object to be considered as changed by * the server. + * + * @kind immutable + * @kind server-set */ preview: string; }; @@ -883,6 +959,9 @@ export type SearchSnippet = { export type Identity = { /** * The id of the Identity. + * + * @kind immutable + * @kind server-set */ id: ID; /** @@ -896,6 +975,8 @@ export type Identity = { * (the section before the `@`) is the single character `*` (e.g., * `*@example.com`), the client may use any valid address ending in * that domain (e.g., `foo@example.com`). + * + * @kind immutable */ email: string; /** @@ -927,10 +1008,14 @@ export type Identity = { * set this to false for the user's username or other default * address. Attempts to destroy an Identity with "mayDelete: false" * will be rejected with a standard "forbidden" SetError. + * + * @kind server-set */ mayDelete: boolean; }; +export type IdentityCreate = Omit; + // ================================= // Email Submission // ================================= @@ -941,25 +1026,37 @@ export type Identity = { export type EmailSubmission = { /** * The id of the EmailSubmission. + * + * @kind immutable + * @kind server-set */ id: ID; /** * The id of the Identity to associate with this submission. + * + * @kind immutable */ identityId: ID; /** * The id of the Email to send. The Email being sent does not have * to be a draft, for example, when "redirecting" an existing Email * to a different address. + * + * @kind immutable */ emailId: ID; /** * The Thread id of the Email to send. This is set by the server to * the `threadId` property of the Email referenced by the `emailId`. + * + * @kind immutable + * @kind server-set */ threadId: ID; /** * Information for use when sending via SMTP. + * + * @kind immutable */ envelope?: Envelope; /** @@ -968,6 +1065,9 @@ export type EmailSubmission = { * submission, this MUST be the time when the server will release the * message; otherwise, it MUST be the time the EmailSubmission was * created. + * + * @kind immutable + * @kind server-set */ sendAt: UTCDate; /** @@ -985,6 +1085,8 @@ export type EmailSubmission = { * * This value is a map from the email address of each recipient to a * DeliveryStatus object. + * + * @kind server-set */ deliveryStatus: Record | null; /** @@ -992,6 +1094,8 @@ export type EmailSubmission = { * submission, in order of receipt, oldest first. The blob is the * whole MIME message (with a top-level content-type of "multipart/ * report"), as received. + * + * @kind server-set */ dsnBlobIds: ID[]; /** @@ -999,10 +1103,17 @@ export type EmailSubmission = { * submission, in order of receipt, oldest first. The blob is the * whole MIME message (with a top-level content-type of "multipart/ * report"), as received. + * + * @kind server-set */ mdnBlobIds: ID[]; }; +export type EmailSubmissionCreate = Omit< + EmailSubmission, + "id" | "threadId" | "sendAt" | "deliveryStatus" | "dsnBlobIds" | "mdnBlobIds" +>; + /** * Information for use when sending via SMTP. */ @@ -1198,6 +1309,9 @@ export type VacationResponse = { /** * The id of the object. There is only ever one VacationResponse * object, and its id is `singleton`. + * + * @kind immutable + * @kind server-set */ id: "singleton"; /** @@ -1243,3 +1357,5 @@ export type VacationResponse = { */ htmlBody: string | null; }; + +export type VacationResponseCreate = Omit; diff --git a/src/types/jmap.ts b/src/types/jmap.ts index bd7139c..b7e1030 100644 --- a/src/types/jmap.ts +++ b/src/types/jmap.ts @@ -25,6 +25,11 @@ import type { Obj } from "../helpers.ts"; */ export type ID = string; +/** + * An arbitrary string defined by the client, used to identify created records + */ +export type CreationID = string; + /** * [rfc8620 § 1.4](https://datatracker.ietf.org/doc/html/rfc8620#section-1.4) * @@ -629,7 +634,7 @@ export enum ChangesRequestErrorType { /** * [rfc8620 § 5.3](https://datatracker.ietf.org/doc/html/rfc8620#section-5.3) */ -export type SetArguments = { +export type SetArguments = { /** * The id of the account to use. */ @@ -653,7 +658,7 @@ export type SetArguments = { * The client MUST omit any properties that may only be set by the * server (for example, the `id` property on most object types). */ - create?: Record; + create?: Record; /** * A map of an id to a PatchObject to apply to the current `T` * object with that id, or null if no objects are to be updated. @@ -1439,6 +1444,9 @@ export type TypeState = Record; export type PushSubscription = { /** * The id of the push subscription. + * + * @kind immutable + * @kind server-set */ id: ID; /** @@ -1460,19 +1468,25 @@ export type PushSubscription = { * * To protect the privacy of the user, the `deviceClientId` id MUST NOT * contain an unobfuscated device id. + * + * @kind immutable */ deviceClientId: string; /** * An absolute URL where the JMAP server will POST the data for the * push message. This MUST begin with `https://`. + * + * @kind immutable */ url: string; /** * Client-generated encryption keys. If supplied, the server MUST * use them as specified in [RFC8291] to encrypt all data sent to the * push subscription. + * + * @kind immutable */ - keys?: null | { + keys?: { /** * The P-256 Elliptic Curve Diffie-Hellman (ECDH) public key as * described in [RFC8291], encoded in URL-safe base64 @@ -1514,6 +1528,8 @@ export type PushSubscription = { types?: string[]; }; +export type PushSubscriptionCreate = Omit; + /** * [rfc8620 § 7.2.2](https://datatracker.ietf.org/doc/html/rfc8620#section-7.2.2) */ From 5fe25912278ff83fc4b8d46bebbfdc2335409286 Mon Sep 17 00:00:00 2001 From: Hunter Tunnicliff Date: Sat, 30 Nov 2024 21:10:18 -0800 Subject: [PATCH 3/4] Adjust additional nullable fields --- src/types/jmap-mail.ts | 71 +++++++++++++++++++++--------------------- src/types/jmap.ts | 4 +-- 2 files changed, 36 insertions(+), 39 deletions(-) diff --git a/src/types/jmap-mail.ts b/src/types/jmap-mail.ts index 788162f..d509341 100644 --- a/src/types/jmap-mail.ts +++ b/src/types/jmap-mail.ts @@ -1,4 +1,3 @@ -import type { OmitDeep } from "type-fest"; import type { FilterCondition, ID, UTCDate } from "./jmap.ts"; /** @@ -367,7 +366,7 @@ export type ForbiddenKeywordCharacters = * [rfc8621 § 4.1.2.3](https://datatracker.ietf.org/doc/html/rfc8621#section-4.1.2.3) */ export type EmailAddress = { - name: string | null; + name?: string; email: string; }; @@ -375,7 +374,7 @@ export type EmailAddress = { * [rfc8621 § 4.1.2.4](https://datatracker.ietf.org/doc/html/rfc8621#section-4.1.2.4) */ export type EmailAddressGroup = { - name: string | null; + name?: string; addresses: EmailAddress[]; }; @@ -442,11 +441,11 @@ export type HeaderParsedForm = { * or folding white space (CFWS) and surrounding angle brackets ("<>") * are removed. If parsing fails, the value is null. */ - MessageIds: string[] | null; + MessageIds?: string[]; /** * [rfc8621 § 4.1.2.6](https://datatracker.ietf.org/doc/html/rfc8621#section-4.1.2.6) */ - Date: string | null; + Date?: string; /** * [rfc8621 § 4.1.2.7](https://datatracker.ietf.org/doc/html/rfc8721#section-4.1.2.6) * @@ -455,7 +454,7 @@ export type HeaderParsedForm = { * surrounding angle brackets or any comments in the header field with * the URLs. If parsing fails, the value is null. */ - URLs: string[] | null; + URLs?: string[]; }; /** @@ -512,47 +511,47 @@ type EmailHeaderFields = { * The value is identical to the value of `header:Message-ID:asMessageIds`. * For messages conforming to RFC 5322, this will be an array with a single entry. */ - messageId: string[] | null; + messageId?: string[]; /** * The value is identical to the value of `header:In-Reply-To:asMessageIds`. */ - inReplyTo: string[] | null; + inReplyTo?: string[]; /** * The value is identical to the value of `header:References:asMessageIds`. */ - references: string[] | null; + references?: string[]; /** * The value is identical to the value of `header:Sender:asAddresses`. */ - sender: EmailAddress[] | null; + sender?: EmailAddress[]; /** * The value is identical to the value of `header:From:asAddresses`. */ - from: EmailAddress[] | null; + from?: EmailAddress[]; /** * The value is identical to the value of `header:To:asAddresses`. */ - to: EmailAddress[] | null; + to?: EmailAddress[]; /** * The value is identical to the value of `header:Cc:asAddresses`. */ - cc: EmailAddress[] | null; + cc?: EmailAddress[]; /** * The value is identical to the value of `header:Bcc:asAddresses`. */ - bcc: EmailAddress[] | null; + bcc?: EmailAddress[]; /** * The value is identical to the value of `header:Reply-To:asAddresses`. */ - replyTo: EmailAddress[] | null; + replyTo?: EmailAddress[]; /** * The value is identical to the value of `header:Subject:asText`. */ - subject: string | null; + subject?: string; /** * The value is identical to the value of `header:Date:asDate`. */ - sentAt: string | null; + sentAt?: string; }; /** @@ -565,7 +564,7 @@ export type EmailBodyPart = { * representation. This is null if, and only if, the part is of type * `multipart/*`. */ - partId: ID | null; + partId?: ID; /** * The id representing the raw octets of the contents of the part, * after decoding any known Content-Transfer-Encoding (as defined in @@ -576,7 +575,7 @@ export type EmailBodyPart = { * the blob id. If the transfer encoding is unknown, it is treated * as though it had no transfer encoding. */ - blobId: ID | null; + blobId?: ID; /** * The size, in octets, of the raw data after content transfer * decoding (as referenced by the `blobId`, i.e., the number of @@ -594,7 +593,7 @@ export type EmailBodyPart = { * existing systems) if not present, then it's the decoded `name` * parameter of the Content-Type header field per [RFC2047]. */ - name: string | null; + name?: string; /** * The value of the Content-Type header field of the part, if * present; otherwise, the implicit type as per the MIME standard @@ -609,13 +608,13 @@ export type EmailBodyPart = { * exists and is of type `text/*` but has no charset parameter, this * is the implicit charset as per the MIME standard: `us-ascii`. */ - charset: string | null; + charset?: string; /** * The value of the Content-Disposition header field of the part, if * present; otherwise, it's null. CFWS is removed and any parameters * are stripped. */ - disposition: string | null; + disposition?: string; /** * The value of the Content-Id header field of the part, if present; * otherwise, it's null. CFWS and surrounding angle brackets ("<>") @@ -623,22 +622,22 @@ export type EmailBodyPart = { * within a "text/html" body part [HTML](https://datatracker.ietf.org/doc/html/rfc8621#ref-HTML) using the "cid:" protocol, * as defined in [RFC2392]. */ - cid: string | null; + cid?: string; /** * The list of language tags, as defined in [RFC3282], in the * Content-Language header field of the part, if present. */ - language: string[] | null; + language?: string[]; /** * The URI, as defined in [RFC2557], in the Content-Location header * field of the part, if present. */ - location: string | null; + location?: string; /** * If the type is "multipart/*", this contains the body parts of each * child. */ - subParts: EmailBodyPart[] | null; + subParts?: EmailBodyPart[]; }; /** @@ -934,7 +933,7 @@ export type SearchSnippet = { * If the subject does not match text from the filter, this property * is null. */ - subject: string | null; + subject?: string; /** * If text from the filter matches the plaintext or HTML body, this * is the relevant section of the body (converted to plaintext if @@ -943,7 +942,7 @@ export type SearchSnippet = { * body does not contain a match for the text from the filter, this * property is null. */ - preview: string | null; + preview?: string; }; // ================================= @@ -983,12 +982,12 @@ export type Identity = { * The Reply-To value the client SHOULD set when creating a new Email * from this Identity. */ - replyTo: EmailAddress[] | null; + replyTo?: EmailAddress[]; /** * The Bcc value the client SHOULD set when creating a new Email from * this Identity. */ - bcc: EmailAddress[] | null; + bcc?: EmailAddress[]; /** * A signature the client SHOULD insert into new plaintext messages * that will be sent from this Identity. Clients MAY ignore this @@ -1088,7 +1087,7 @@ export type EmailSubmission = { * * @kind server-set */ - deliveryStatus: Record | null; + deliveryStatus?: Record; /** * A list of blob ids for DSNs [RFC3464] received for this * submission, in order of receipt, oldest first. The blob is the @@ -1325,20 +1324,20 @@ export type VacationResponse = { * user's vacation response. If null, the vacation response is * effective immediately. */ - fromDate: UTCDate | null; + fromDate?: UTCDate; /** * If `isEnabled` is true, messages that arrive before this date-time * (but on or after the `fromDate` if defined) should receive the * user's vacation response. If null, the vacation response is * effective indefinitely. */ - toDate: UTCDate | null; + toDate?: UTCDate; /** * The subject that will be used by the message sent in response to * messages when the vacation response is enabled. If null, an * appropriate subject SHOULD be set by the server. */ - subject: string | null; + subject?: string; /** * The plaintext body to send in response to messages when the * vacation response is enabled. If this is null, the server SHOULD @@ -1347,7 +1346,7 @@ export type VacationResponse = { * only. If both "textBody" and "htmlBody" are null, an appropriate * default body SHOULD be generated for responses by the server. */ - textBody: string | null; + textBody?: string; /** * The HTML body to send in response to messages when the vacation * response is enabled. If this is null, the server MAY choose to @@ -1355,7 +1354,7 @@ export type VacationResponse = { * vacation responses or MAY choose to send the response as plaintext * only. */ - htmlBody: string | null; + htmlBody?: string; }; export type VacationResponseCreate = Omit; diff --git a/src/types/jmap.ts b/src/types/jmap.ts index b7e1030..7459fff 100644 --- a/src/types/jmap.ts +++ b/src/types/jmap.ts @@ -680,9 +680,7 @@ export type SetArguments = { * * TODO: Support more correct types for PatchObject */ -export type PatchObject = { - [key in ExtendedJSONPointer | keyof T]: Partial; -}; +export type PatchObject = Partial | { [K in ExtendedJSONPointer]: any }; export type SetResponse = { /** From 69de20b65c90ea90ee06726ad6ea09882e869699 Mon Sep 17 00:00:00 2001 From: Hunter Tunnicliff Date: Sat, 30 Nov 2024 21:12:17 -0800 Subject: [PATCH 4/4] Format --- tsconfig.json | 4 +--- vitest.config.ts | 6 +++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/tsconfig.json b/tsconfig.json index 1344c5a..5ec56e3 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -13,9 +13,7 @@ "outDir": "dist", "declarationMap": true, "noUnusedLocals": true, - "types": [ - "vitest/importMeta" - ] + "types": ["vitest/importMeta"] }, "include": ["src/**/*"] } diff --git a/vitest.config.ts b/vitest.config.ts index 354e0f4..c33af1a 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -3,8 +3,8 @@ import { defineConfig } from "vitest/config"; export default defineConfig({ test: { typecheck: { - enabled: true, + enabled: true }, - includeSource: ["src/**/*.ts"], - }, + includeSource: ["src/**/*.ts"] + } });