jmap-kit 0.0.0 → 1.0.2
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 +21 -0
- package/README.md +138 -3
- package/dist/src/capabilities/blob/blob.d.ts +83 -0
- package/dist/src/capabilities/blob/blob.js +98 -0
- package/dist/src/capabilities/blob/blob.js.map +1 -0
- package/dist/src/capabilities/blob/types.d.ts +212 -0
- package/dist/src/capabilities/blob/types.js +16 -0
- package/dist/src/capabilities/blob/types.js.map +1 -0
- package/dist/src/capabilities/blob-capability.d.ts +195 -0
- package/dist/src/capabilities/blob-capability.js +277 -0
- package/dist/src/capabilities/blob-capability.js.map +1 -0
- package/dist/src/capabilities/core/core.d.ts +47 -0
- package/dist/src/capabilities/core/core.js +59 -0
- package/dist/src/capabilities/core/core.js.map +1 -0
- package/dist/src/capabilities/core/types.d.ts +13 -0
- package/dist/src/capabilities/core/types.js +2 -0
- package/dist/src/capabilities/core/types.js.map +1 -0
- package/dist/src/capabilities/core-capability.d.ts +307 -0
- package/dist/src/capabilities/core-capability.js +344 -0
- package/dist/src/capabilities/core-capability.js.map +1 -0
- package/dist/src/capabilities/email/email.d.ts +124 -0
- package/dist/src/capabilities/email/email.js +136 -0
- package/dist/src/capabilities/email/email.js.map +1 -0
- package/dist/src/capabilities/email/types.d.ts +776 -0
- package/dist/src/capabilities/email/types.js +2 -0
- package/dist/src/capabilities/email/types.js.map +1 -0
- package/dist/src/capabilities/email-capability.d.ts +266 -0
- package/dist/src/capabilities/email-capability.js +241 -0
- package/dist/src/capabilities/email-capability.js.map +1 -0
- package/dist/src/capabilities/emailsubmission/emailsubmission.d.ts +95 -0
- package/dist/src/capabilities/emailsubmission/emailsubmission.js +107 -0
- package/dist/src/capabilities/emailsubmission/emailsubmission.js.map +1 -0
- package/dist/src/capabilities/emailsubmission/types.d.ts +256 -0
- package/dist/src/capabilities/emailsubmission/types.js +2 -0
- package/dist/src/capabilities/emailsubmission/types.js.map +1 -0
- package/dist/src/capabilities/example/example.d.ts +80 -0
- package/dist/src/capabilities/example/example.js +91 -0
- package/dist/src/capabilities/example/example.js.map +1 -0
- package/dist/src/capabilities/example/types.d.ts +33 -0
- package/dist/src/capabilities/example/types.js +2 -0
- package/dist/src/capabilities/example/types.js.map +1 -0
- package/dist/src/capabilities/identity/identity.d.ts +71 -0
- package/dist/src/capabilities/identity/identity.js +83 -0
- package/dist/src/capabilities/identity/identity.js.map +1 -0
- package/dist/src/capabilities/identity/types.d.ts +110 -0
- package/dist/src/capabilities/identity/types.js +2 -0
- package/dist/src/capabilities/identity/types.js.map +1 -0
- package/dist/src/capabilities/mailbox/mailbox.d.ts +91 -0
- package/dist/src/capabilities/mailbox/mailbox.js +103 -0
- package/dist/src/capabilities/mailbox/mailbox.js.map +1 -0
- package/dist/src/capabilities/mailbox/types.d.ts +248 -0
- package/dist/src/capabilities/mailbox/types.js +2 -0
- package/dist/src/capabilities/mailbox/types.js.map +1 -0
- package/dist/src/capabilities/maskedemail/maskedemail.d.ts +60 -0
- package/dist/src/capabilities/maskedemail/maskedemail.js +72 -0
- package/dist/src/capabilities/maskedemail/maskedemail.js.map +1 -0
- package/dist/src/capabilities/maskedemail/types.d.ts +67 -0
- package/dist/src/capabilities/maskedemail/types.js +4 -0
- package/dist/src/capabilities/maskedemail/types.js.map +1 -0
- package/dist/src/capabilities/maskedemail-capability.d.ts +112 -0
- package/dist/src/capabilities/maskedemail-capability.js +166 -0
- package/dist/src/capabilities/maskedemail-capability.js.map +1 -0
- package/dist/src/capabilities/searchsnippet/searchsnippet.d.ts +51 -0
- package/dist/src/capabilities/searchsnippet/searchsnippet.js +63 -0
- package/dist/src/capabilities/searchsnippet/searchsnippet.js.map +1 -0
- package/dist/src/capabilities/searchsnippet/types.d.ts +88 -0
- package/dist/src/capabilities/searchsnippet/types.js +2 -0
- package/dist/src/capabilities/searchsnippet/types.js.map +1 -0
- package/dist/src/capabilities/submission-capability.d.ts +89 -0
- package/dist/src/capabilities/submission-capability.js +75 -0
- package/dist/src/capabilities/submission-capability.js.map +1 -0
- package/dist/src/capabilities/thread/thread.d.ts +58 -0
- package/dist/src/capabilities/thread/thread.js +70 -0
- package/dist/src/capabilities/thread/thread.js.map +1 -0
- package/dist/src/capabilities/thread/types.d.ts +43 -0
- package/dist/src/capabilities/thread/types.js +2 -0
- package/dist/src/capabilities/thread/types.js.map +1 -0
- package/dist/src/capabilities/utils/assert-invocation-datatype.d.ts +7 -0
- package/dist/src/capabilities/utils/assert-invocation-datatype.js +13 -0
- package/dist/src/capabilities/utils/assert-invocation-datatype.js.map +1 -0
- package/dist/src/capabilities/utils/assert-invocation-method.d.ts +7 -0
- package/dist/src/capabilities/utils/assert-invocation-method.js +13 -0
- package/dist/src/capabilities/utils/assert-invocation-method.js.map +1 -0
- package/dist/src/capabilities/utils/assert-invocation.d.ts +7 -0
- package/dist/src/capabilities/utils/assert-invocation.js +22 -0
- package/dist/src/capabilities/utils/assert-invocation.js.map +1 -0
- package/dist/src/capabilities/utils/assert-non-nullish.d.ts +1 -0
- package/dist/src/capabilities/utils/assert-non-nullish.js +6 -0
- package/dist/src/capabilities/utils/assert-non-nullish.js.map +1 -0
- package/dist/src/capabilities/utils/create-readonly-account-validator.d.ts +49 -0
- package/dist/src/capabilities/utils/create-readonly-account-validator.js +80 -0
- package/dist/src/capabilities/utils/create-readonly-account-validator.js.map +1 -0
- package/dist/src/capabilities/vacationresponse/types.d.ts +100 -0
- package/dist/src/capabilities/vacationresponse/types.js +2 -0
- package/dist/src/capabilities/vacationresponse/types.js.map +1 -0
- package/dist/src/capabilities/vacationresponse/vacationresponse.d.ts +61 -0
- package/dist/src/capabilities/vacationresponse/vacationresponse.js +73 -0
- package/dist/src/capabilities/vacationresponse/vacationresponse.js.map +1 -0
- package/dist/src/capabilities/vacationresponse-capability.d.ts +65 -0
- package/dist/src/capabilities/vacationresponse-capability.js +68 -0
- package/dist/src/capabilities/vacationresponse-capability.js.map +1 -0
- package/dist/src/capability-registry/capability-registry.d.ts +148 -0
- package/dist/src/capability-registry/capability-registry.js +360 -0
- package/dist/src/capability-registry/capability-registry.js.map +1 -0
- package/dist/src/capability-registry/types.d.ts +385 -0
- package/dist/src/capability-registry/types.js +2 -0
- package/dist/src/capability-registry/types.js.map +1 -0
- package/dist/src/capability-registry/utils.d.ts +71 -0
- package/dist/src/capability-registry/utils.js +163 -0
- package/dist/src/capability-registry/utils.js.map +1 -0
- package/dist/src/common/registry.d.ts +366 -0
- package/dist/src/common/registry.js +321 -0
- package/dist/src/common/registry.js.map +1 -0
- package/dist/src/common/types.d.ts +338 -0
- package/dist/src/common/types.js +21 -0
- package/dist/src/common/types.js.map +1 -0
- package/dist/src/common/utils.d.ts +20 -0
- package/dist/src/common/utils.js +26 -0
- package/dist/src/common/utils.js.map +1 -0
- package/dist/src/index.d.ts +40 -0
- package/dist/src/index.js +33 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/invocation/arguments-proxy.d.ts +14 -0
- package/dist/src/invocation/arguments-proxy.js +37 -0
- package/dist/src/invocation/arguments-proxy.js.map +1 -0
- package/dist/src/invocation/error-invocation.d.ts +27 -0
- package/dist/src/invocation/error-invocation.js +39 -0
- package/dist/src/invocation/error-invocation.js.map +1 -0
- package/dist/src/invocation/invocation.d.ts +111 -0
- package/dist/src/invocation/invocation.js +158 -0
- package/dist/src/invocation/invocation.js.map +1 -0
- package/dist/src/invocation/result-reference.d.ts +86 -0
- package/dist/src/invocation/result-reference.js +118 -0
- package/dist/src/invocation/result-reference.js.map +1 -0
- package/dist/src/invocation/types.d.ts +637 -0
- package/dist/src/invocation/types.js +2 -0
- package/dist/src/invocation/types.js.map +1 -0
- package/dist/src/invocation/utils.d.ts +21 -0
- package/dist/src/invocation/utils.js +30 -0
- package/dist/src/invocation/utils.js.map +1 -0
- package/dist/src/invocation-factory/invocation-factory-manager.d.ts +20 -0
- package/dist/src/invocation-factory/invocation-factory-manager.js +50 -0
- package/dist/src/invocation-factory/invocation-factory-manager.js.map +1 -0
- package/dist/src/invocation-factory/invocation-list.d.ts +32 -0
- package/dist/src/invocation-factory/invocation-list.js +77 -0
- package/dist/src/invocation-factory/invocation-list.js.map +1 -0
- package/dist/src/invocation-factory/types.d.ts +11 -0
- package/dist/src/invocation-factory/types.js +2 -0
- package/dist/src/invocation-factory/types.js.map +1 -0
- package/dist/src/jmap-client/jmap-client.d.ts +252 -0
- package/dist/src/jmap-client/jmap-client.js +777 -0
- package/dist/src/jmap-client/jmap-client.js.map +1 -0
- package/dist/src/jmap-client/types.d.ts +427 -0
- package/dist/src/jmap-client/types.js +21 -0
- package/dist/src/jmap-client/types.js.map +1 -0
- package/dist/src/jmap-client/utils/abort-controller.d.ts +8 -0
- package/dist/src/jmap-client/utils/abort-controller.js +24 -0
- package/dist/src/jmap-client/utils/abort-controller.js.map +1 -0
- package/dist/src/jmap-client/utils/assert-connected.d.ts +7 -0
- package/dist/src/jmap-client/utils/assert-connected.js +11 -0
- package/dist/src/jmap-client/utils/assert-connected.js.map +1 -0
- package/dist/src/jmap-client/utils/deep-freeze.d.ts +7 -0
- package/dist/src/jmap-client/utils/deep-freeze.js +17 -0
- package/dist/src/jmap-client/utils/deep-freeze.js.map +1 -0
- package/dist/src/jmap-client/utils/emitter.d.ts +9 -0
- package/dist/src/jmap-client/utils/emitter.js +18 -0
- package/dist/src/jmap-client/utils/emitter.js.map +1 -0
- package/dist/src/jmap-client/utils/filter-session-capabilities.d.ts +22 -0
- package/dist/src/jmap-client/utils/filter-session-capabilities.js +40 -0
- package/dist/src/jmap-client/utils/filter-session-capabilities.js.map +1 -0
- package/dist/src/jmap-client/utils/jmap-request-error.d.ts +28 -0
- package/dist/src/jmap-client/utils/jmap-request-error.js +48 -0
- package/dist/src/jmap-client/utils/jmap-request-error.js.map +1 -0
- package/dist/src/jmap-client/utils/logger.d.ts +6 -0
- package/dist/src/jmap-client/utils/logger.js +22 -0
- package/dist/src/jmap-client/utils/logger.js.map +1 -0
- package/dist/src/jmap-client/utils/merge-headers.d.ts +11 -0
- package/dist/src/jmap-client/utils/merge-headers.js +40 -0
- package/dist/src/jmap-client/utils/merge-headers.js.map +1 -0
- package/dist/src/jmap-client/utils/template-utils.d.ts +27 -0
- package/dist/src/jmap-client/utils/template-utils.js +61 -0
- package/dist/src/jmap-client/utils/template-utils.js.map +1 -0
- package/dist/src/jmap-client/utils/track-utils.d.ts +19 -0
- package/dist/src/jmap-client/utils/track-utils.js +35 -0
- package/dist/src/jmap-client/utils/track-utils.js.map +1 -0
- package/dist/src/jmap-client/utils/transport.d.ts +12 -0
- package/dist/src/jmap-client/utils/transport.js +38 -0
- package/dist/src/jmap-client/utils/transport.js.map +1 -0
- package/dist/src/jmap-client/utils/validate-session.d.ts +19 -0
- package/dist/src/jmap-client/utils/validate-session.js +29 -0
- package/dist/src/jmap-client/utils/validate-session.js.map +1 -0
- package/dist/src/request-builder/request-builder.d.ts +95 -0
- package/dist/src/request-builder/request-builder.js +343 -0
- package/dist/src/request-builder/request-builder.js.map +1 -0
- package/dist/src/request-builder/types.d.ts +32 -0
- package/dist/src/request-builder/types.js +2 -0
- package/dist/src/request-builder/types.js.map +1 -0
- package/package.json +69 -3
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
import { z } from "zod/v4";
|
|
2
|
+
import type { ValidationPlugin } from "../capability-registry/types.js";
|
|
3
|
+
import { CORE_CAPABILITY_URI } from "../common/registry.js";
|
|
4
|
+
import type { BaseFilterCondition, BaseGetRequestInvocationArgs, BaseInvocationArgs, BaseObject, BaseQueryRequestInvocationArgs, BaseSetRequestInvocationArgs } from "../invocation/types.js";
|
|
5
|
+
import type { BlobCopyRequestInvocationArgs } from "./blob/types.js";
|
|
6
|
+
/**
|
|
7
|
+
* Validates that `/get` method calls do not exceed the server's `maxObjectsInGet` limit.
|
|
8
|
+
*
|
|
9
|
+
* **Object Retrieval Limits (RFC 8620 Section 5.1):**
|
|
10
|
+
* - Enforces the `maxObjectsInGet` limit defined in the Core capability object
|
|
11
|
+
* - This limit applies to the `ids` array in any `/get` method call (`Mailbox/get`, `Email/get`, etc.)
|
|
12
|
+
* - Prevents requests that would be rejected by the server with a `requestTooLarge` error
|
|
13
|
+
* - The server defines this limit based on resource constraints and performance characteristics
|
|
14
|
+
*
|
|
15
|
+
* This validation catches oversized requests client-side before transmission, providing
|
|
16
|
+
* immediate feedback and avoiding unnecessary network traffic.
|
|
17
|
+
*
|
|
18
|
+
* **Example:**
|
|
19
|
+
* If a server sets `maxObjectsInGet: 500`, attempting to fetch 1000 email IDs in a single
|
|
20
|
+
* `Email/get` call would be caught and reported by this validator.
|
|
21
|
+
*
|
|
22
|
+
* @see {@link https://www.rfc-editor.org/rfc/rfc8620.html#section-5.1 | RFC 8620 Section 5.1: /get}
|
|
23
|
+
* @see {@link https://www.rfc-editor.org/rfc/rfc8620.html#section-2 | RFC 8620 Section 2: The JMAP Session Resource}
|
|
24
|
+
|
|
25
|
+
*/
|
|
26
|
+
export declare const maxObjectsInGetPlugin: ValidationPlugin<"invocation", BaseGetRequestInvocationArgs<BaseInvocationArgs>>;
|
|
27
|
+
/**
|
|
28
|
+
* Validates that `/set` method calls do not exceed the server's `maxObjectsInSet` limit.
|
|
29
|
+
*
|
|
30
|
+
* **Object Modification Limits (RFC 8620 Section 5.3):**
|
|
31
|
+
* - Enforces the `maxObjectsInSet` limit defined in the Core capability object
|
|
32
|
+
* - Counts the total number of operations across `create`, `update`, and `destroy` arguments
|
|
33
|
+
* - Applies to all `/set` method calls (`Mailbox/set`, `Email/set`, etc.)
|
|
34
|
+
* - Prevents requests that would be rejected with a `requestTooLarge` error
|
|
35
|
+
* - The server defines this limit to manage transaction size and resource usage
|
|
36
|
+
*
|
|
37
|
+
* This validation catches oversized batch operations client-side before transmission,
|
|
38
|
+
* allowing clients to split large operations into multiple requests if needed.
|
|
39
|
+
*
|
|
40
|
+
* **Example:**
|
|
41
|
+
* If a server sets `maxObjectsInSet: 100`, attempting to create 50 mailboxes, update 30,
|
|
42
|
+
* and delete 25 (total: 105 operations) in a single `Mailbox/set` call would be rejected.
|
|
43
|
+
*
|
|
44
|
+
* @see {@link https://www.rfc-editor.org/rfc/rfc8620.html#section-5.3 | RFC 8620 Section 5.3: /set}
|
|
45
|
+
* @see {@link https://www.rfc-editor.org/rfc/rfc8620.html#section-2 | RFC 8620 Section 2: The JMAP Session Resource}
|
|
46
|
+
|
|
47
|
+
*/
|
|
48
|
+
export declare const maxObjectsInSetPlugin: ValidationPlugin<"invocation", BaseSetRequestInvocationArgs<BaseInvocationArgs>>;
|
|
49
|
+
/**
|
|
50
|
+
* Validates that `/query` method calls only use server-supported collation algorithms.
|
|
51
|
+
*
|
|
52
|
+
* **Collation Algorithm Support (RFC 8620 Section 5.5):**
|
|
53
|
+
* - Validates that any `collation` property in sort criteria is included in the server's
|
|
54
|
+
* `collationAlgorithms` capability list
|
|
55
|
+
* - Collation algorithms control how strings are compared and sorted (case sensitivity,
|
|
56
|
+
* locale-specific ordering, accent handling, etc.)
|
|
57
|
+
* - Common algorithms include "i;ascii-casemap" (case-insensitive ASCII) and "i;unicode-casemap"
|
|
58
|
+
* - Server support varies based on implementation and internationalisation capabilities
|
|
59
|
+
*
|
|
60
|
+
* This validation prevents query errors by catching unsupported collation algorithms before
|
|
61
|
+
* sending the request. Without this check, the server would reject the query with an
|
|
62
|
+
* `unsupportedSort` error.
|
|
63
|
+
*
|
|
64
|
+
* **Example:**
|
|
65
|
+
* If a server only supports "i;ascii-casemap" collation, attempting to sort with
|
|
66
|
+
* "i;unicode-casemap" would be caught and reported by this validator.
|
|
67
|
+
*
|
|
68
|
+
* @see {@link https://www.rfc-editor.org/rfc/rfc8620.html#section-5.5 | RFC 8620 Section 5.5: /query}
|
|
69
|
+
* @see {@link https://www.rfc-editor.org/rfc/rfc8620.html#section-2 | RFC 8620 Section 2: The JMAP Session Resource}
|
|
70
|
+
|
|
71
|
+
*/
|
|
72
|
+
export declare const collationAlgorithmsPlugin: ValidationPlugin<"invocation", BaseQueryRequestInvocationArgs<BaseInvocationArgs, BaseFilterCondition>>;
|
|
73
|
+
/**
|
|
74
|
+
* Prevents `/set` method calls on read-only accounts.
|
|
75
|
+
*
|
|
76
|
+
* **Read-Only Account Protection (RFC 8620 Section 1.6.2, Section 5.3):**
|
|
77
|
+
* - Validates that the target account's `isReadOnly` property is `false`
|
|
78
|
+
* - Read-only accounts cannot accept any data modification operations
|
|
79
|
+
* - All `/set` methods (create, update, destroy) require write access
|
|
80
|
+
* - Attempting modifications on read-only accounts would fail with an `accountReadOnly` error
|
|
81
|
+
*
|
|
82
|
+
* This validator catches the error client-side before making a server request, providing
|
|
83
|
+
* immediate feedback when attempting invalid operations on read-only accounts.
|
|
84
|
+
*
|
|
85
|
+
* **Common read-only scenarios:**
|
|
86
|
+
* - Shared resources with read-only permissions
|
|
87
|
+
* - Archive or backup accounts
|
|
88
|
+
* - Accounts in maintenance mode
|
|
89
|
+
* - Delegated access with restricted permissions
|
|
90
|
+
*
|
|
91
|
+
* @see {@link https://www.rfc-editor.org/rfc/rfc8620.html#section-5.3 | RFC 8620 Section 5.3: /set}
|
|
92
|
+
* @see {@link https://www.rfc-editor.org/rfc/rfc8620.html#section-2 | RFC 8620 Section 2: The JMAP Session Resource}
|
|
93
|
+
|
|
94
|
+
*/
|
|
95
|
+
export declare const preventSetOnReadOnlyAccountPlugin: ValidationPlugin<"invocation", BaseSetRequestInvocationArgs<BaseObject>>;
|
|
96
|
+
/**
|
|
97
|
+
* Validates that API requests do not exceed the server's `maxCallsInRequest` limit.
|
|
98
|
+
*
|
|
99
|
+
* **Request Batching Limits (RFC 8620 Section 3.2):**
|
|
100
|
+
* - Enforces the `maxCallsInRequest` limit defined in the Core capability object
|
|
101
|
+
* - This limit applies to the total number of method calls in the `methodCalls` array
|
|
102
|
+
* - JMAP allows batching multiple method calls in a single request for efficiency
|
|
103
|
+
* - Prevents requests that would be rejected with a `requestTooLarge` error
|
|
104
|
+
* - The server defines this limit to manage request processing complexity and resources
|
|
105
|
+
*
|
|
106
|
+
* This validation runs during the `pre-build` lifecycle hook, checking the request structure
|
|
107
|
+
* before serialisation. It catches oversized batches early, allowing clients to split large
|
|
108
|
+
* batches into multiple API requests.
|
|
109
|
+
*
|
|
110
|
+
* **Example:**
|
|
111
|
+
* If a server sets `maxCallsInRequest: 50`, attempting to batch 75 method calls
|
|
112
|
+
* (e.g., Email/get + 50 Email/set + 24 Mailbox/get) would be caught by this validator.
|
|
113
|
+
*
|
|
114
|
+
* @see {@link https://www.rfc-editor.org/rfc/rfc8620.html#section-3.2 | RFC 8620 Section 3.2: Making an API Request}
|
|
115
|
+
* @see {@link https://www.rfc-editor.org/rfc/rfc8620.html#section-2 | RFC 8620 Section 2: The JMAP Session Resource}
|
|
116
|
+
|
|
117
|
+
*/
|
|
118
|
+
export declare const maxCallsInRequestPlugin: ValidationPlugin<"pre-build">;
|
|
119
|
+
/**
|
|
120
|
+
* Validates that serialised API requests do not exceed the server's `maxSizeRequest` limit.
|
|
121
|
+
*
|
|
122
|
+
* **Request Size Limits (RFC 8620 Section 3.2):**
|
|
123
|
+
* - Enforces the `maxSizeRequest` limit defined in the Core capability object
|
|
124
|
+
* - Measures the total size of the serialised request body in bytes
|
|
125
|
+
* - This limit applies after the request has been serialised (JSON stringified and encoded)
|
|
126
|
+
* - Prevents requests that would be rejected with a `requestTooLarge` error
|
|
127
|
+
* - The server defines this limit based on available memory, bandwidth, and processing capacity
|
|
128
|
+
*
|
|
129
|
+
* This validation runs during the `post-serialization` lifecycle hook, checking the actual
|
|
130
|
+
* wire format size. It handles various body formats (string, Blob, ArrayBuffer) and provides
|
|
131
|
+
* human-readable error messages in megabytes.
|
|
132
|
+
*
|
|
133
|
+
* **Example:**
|
|
134
|
+
* If a server sets `maxSizeRequest: 10000000` (10 MB), attempting to send a request with
|
|
135
|
+
* a large Email/import containing 15 MB of email data would be caught and reported as
|
|
136
|
+
* "Request size (15.00 MB) exceeds server limit of 10.00 MB".
|
|
137
|
+
*
|
|
138
|
+
* @see {@link https://www.rfc-editor.org/rfc/rfc8620.html#section-3.2 | RFC 8620 Section 3.2: Making an API Request}
|
|
139
|
+
* @see {@link https://www.rfc-editor.org/rfc/rfc8620.html#section-2 | RFC 8620 Section 2: The JMAP Session Resource}
|
|
140
|
+
|
|
141
|
+
*/
|
|
142
|
+
export declare const maxSizeRequestPlugin: ValidationPlugin<"post-serialization">;
|
|
143
|
+
/**
|
|
144
|
+
* Prevents Blob/copy operations on read-only target accounts.
|
|
145
|
+
*
|
|
146
|
+
* **Read-Only Account Protection for Blob/copy:**
|
|
147
|
+
* - Validates that the target account's (accountId) `isReadOnly` property is `false`
|
|
148
|
+
* - Blob/copy writes blobs to the target account, requiring write access
|
|
149
|
+
* - The source account (fromAccountId) only needs read access
|
|
150
|
+
* - Attempting to copy to a read-only account would fail with an `accountReadOnly` error
|
|
151
|
+
*
|
|
152
|
+
* This validator catches the error client-side before making a server request, providing
|
|
153
|
+
* immediate feedback when attempting to copy blobs to read-only accounts.
|
|
154
|
+
*
|
|
155
|
+
* **Common read-only scenarios:**
|
|
156
|
+
* - Shared accounts with read-only permissions
|
|
157
|
+
* - Archive or backup accounts
|
|
158
|
+
* - Accounts in maintenance mode
|
|
159
|
+
*
|
|
160
|
+
* @see {@link https://www.rfc-editor.org/rfc/rfc8620.html#section-6.3 | RFC 8620 Section 6.3: Blob/copy}
|
|
161
|
+
|
|
162
|
+
*/
|
|
163
|
+
export declare const preventBlobCopyOnReadOnlyAccountPlugin: ValidationPlugin<"invocation", BlobCopyRequestInvocationArgs>;
|
|
164
|
+
/**
|
|
165
|
+
* Defines the Core capability, including Core/echo and Blob/copy invocations
|
|
166
|
+
* and validation plugins.
|
|
167
|
+
*/
|
|
168
|
+
export declare const CoreCapability: {
|
|
169
|
+
uri: "urn:ietf:params:jmap:core";
|
|
170
|
+
invocations: {
|
|
171
|
+
Core: {
|
|
172
|
+
request: {
|
|
173
|
+
echo: import("../invocation/types.js").InvocationFactory<BaseInvocationArgs, import("./core/core.js").CoreInvocation<BaseInvocationArgs>>;
|
|
174
|
+
};
|
|
175
|
+
response: {
|
|
176
|
+
echo: import("../invocation/types.js").InvocationFactory<BaseInvocationArgs, import("./core/core.js").CoreInvocation<BaseInvocationArgs>>;
|
|
177
|
+
};
|
|
178
|
+
};
|
|
179
|
+
Blob: {
|
|
180
|
+
request: {
|
|
181
|
+
copy: import("../invocation/types.js").InvocationFactory<BlobCopyRequestInvocationArgs, import("./blob/blob.js").BlobInvocation<BlobCopyRequestInvocationArgs>>;
|
|
182
|
+
};
|
|
183
|
+
response: {
|
|
184
|
+
copy: import("../invocation/types.js").InvocationFactory<import("./blob/types.js").BlobCopyResponseInvocationArgs, import("./blob/blob.js").BlobInvocation<import("./blob/types.js").BlobCopyResponseInvocationArgs>>;
|
|
185
|
+
};
|
|
186
|
+
};
|
|
187
|
+
};
|
|
188
|
+
validators: ({
|
|
189
|
+
name: string;
|
|
190
|
+
hook: "pre-build";
|
|
191
|
+
trigger: import("../capability-registry/types.js").PluginTrigger;
|
|
192
|
+
validate(this: void, context: import("../capability-registry/types.js").PluginContext<"pre-build">): import("../index.js").MaybePromise<import("../capability-registry/types.js").ValidationResult>;
|
|
193
|
+
} | {
|
|
194
|
+
name: string;
|
|
195
|
+
hook: "post-serialization";
|
|
196
|
+
trigger: import("../capability-registry/types.js").PluginTrigger;
|
|
197
|
+
validate(this: void, context: import("../capability-registry/types.js").PluginContext<"post-serialization">): import("../index.js").MaybePromise<import("../capability-registry/types.js").ValidationResult>;
|
|
198
|
+
} | {
|
|
199
|
+
name: string;
|
|
200
|
+
hook: "invocation";
|
|
201
|
+
trigger: {
|
|
202
|
+
capabilityUri?: import("../index.js").JMAPCapability;
|
|
203
|
+
dataType?: import("../index.js").JMAPDataType;
|
|
204
|
+
method?: import("../index.js").JMAPMethodName;
|
|
205
|
+
};
|
|
206
|
+
validate(this: void, context: import("../capability-registry/types.js").BasePluginContext & {
|
|
207
|
+
invocation: import("../index.js").Invocation<BaseGetRequestInvocationArgs<BaseInvocationArgs>>;
|
|
208
|
+
}): import("../index.js").MaybePromise<import("../capability-registry/types.js").ValidationResult>;
|
|
209
|
+
} | {
|
|
210
|
+
name: string;
|
|
211
|
+
hook: "invocation";
|
|
212
|
+
trigger: {
|
|
213
|
+
capabilityUri?: import("../index.js").JMAPCapability;
|
|
214
|
+
dataType?: import("../index.js").JMAPDataType;
|
|
215
|
+
method?: import("../index.js").JMAPMethodName;
|
|
216
|
+
};
|
|
217
|
+
validate(this: void, context: import("../capability-registry/types.js").BasePluginContext & {
|
|
218
|
+
invocation: import("../index.js").Invocation<BaseSetRequestInvocationArgs<BaseInvocationArgs>>;
|
|
219
|
+
}): import("../index.js").MaybePromise<import("../capability-registry/types.js").ValidationResult>;
|
|
220
|
+
} | {
|
|
221
|
+
name: string;
|
|
222
|
+
hook: "invocation";
|
|
223
|
+
trigger: {
|
|
224
|
+
capabilityUri?: import("../index.js").JMAPCapability;
|
|
225
|
+
dataType?: import("../index.js").JMAPDataType;
|
|
226
|
+
method?: import("../index.js").JMAPMethodName;
|
|
227
|
+
};
|
|
228
|
+
validate(this: void, context: import("../capability-registry/types.js").BasePluginContext & {
|
|
229
|
+
invocation: import("../index.js").Invocation<BaseQueryRequestInvocationArgs<BaseInvocationArgs, BaseFilterCondition>>;
|
|
230
|
+
}): import("../index.js").MaybePromise<import("../capability-registry/types.js").ValidationResult>;
|
|
231
|
+
} | {
|
|
232
|
+
name: string;
|
|
233
|
+
hook: "invocation";
|
|
234
|
+
trigger: {
|
|
235
|
+
capabilityUri?: import("../index.js").JMAPCapability;
|
|
236
|
+
dataType?: import("../index.js").JMAPDataType;
|
|
237
|
+
method?: import("../index.js").JMAPMethodName;
|
|
238
|
+
};
|
|
239
|
+
validate(this: void, context: import("../capability-registry/types.js").BasePluginContext & {
|
|
240
|
+
invocation: import("../index.js").Invocation<BlobCopyRequestInvocationArgs>;
|
|
241
|
+
}): import("../index.js").MaybePromise<import("../capability-registry/types.js").ValidationResult>;
|
|
242
|
+
})[];
|
|
243
|
+
schema: {
|
|
244
|
+
serverCapability: z.ZodObject<{
|
|
245
|
+
maxSizeUpload: z.ZodNumber;
|
|
246
|
+
maxConcurrentUpload: z.ZodNumber;
|
|
247
|
+
maxSizeRequest: z.ZodNumber;
|
|
248
|
+
maxConcurrentRequests: z.ZodNumber;
|
|
249
|
+
maxCallsInRequest: z.ZodNumber;
|
|
250
|
+
maxObjectsInGet: z.ZodNumber;
|
|
251
|
+
maxObjectsInSet: z.ZodNumber;
|
|
252
|
+
collationAlgorithms: z.ZodArray<z.ZodString>;
|
|
253
|
+
}, z.core.$loose>;
|
|
254
|
+
};
|
|
255
|
+
};
|
|
256
|
+
declare module "../common/types.js" {
|
|
257
|
+
interface ServerCapabilityRegistry {
|
|
258
|
+
[CORE_CAPABILITY_URI]: {
|
|
259
|
+
/**
|
|
260
|
+
* The maximum file size, in octets, that the server will accept for a single file upload
|
|
261
|
+
* (for any purpose). Suggested minimum: 50,000,000.
|
|
262
|
+
*/
|
|
263
|
+
maxSizeUpload: UnsignedInt;
|
|
264
|
+
/**
|
|
265
|
+
* The maximum number of concurrent requests the server will accept to the upload endpoint.
|
|
266
|
+
* Suggested minimum: 4.
|
|
267
|
+
*/
|
|
268
|
+
maxConcurrentUpload: UnsignedInt;
|
|
269
|
+
/**
|
|
270
|
+
* The maximum size, in octets, that the server will accept for a single request to the API
|
|
271
|
+
* endpoint. Suggested minimum: 10,000,000.
|
|
272
|
+
*/
|
|
273
|
+
maxSizeRequest: UnsignedInt;
|
|
274
|
+
/**
|
|
275
|
+
* The maximum number of concurrent requests the server will accept to the API endpoint.
|
|
276
|
+
* Suggested minimum: 4.
|
|
277
|
+
*/
|
|
278
|
+
maxConcurrentRequests: UnsignedInt;
|
|
279
|
+
/**
|
|
280
|
+
* The maximum number of method calls the server will accept in a single request to the API
|
|
281
|
+
* endpoint. Suggested minimum: 16.
|
|
282
|
+
*/
|
|
283
|
+
maxCallsInRequest: UnsignedInt;
|
|
284
|
+
/**
|
|
285
|
+
* The maximum number of objects that the client may request in a single /get type method
|
|
286
|
+
* call. Suggested minimum: 500.
|
|
287
|
+
*/
|
|
288
|
+
maxObjectsInGet: UnsignedInt;
|
|
289
|
+
/**
|
|
290
|
+
* The maximum number of objects the client may send to create, update, or destroy in a
|
|
291
|
+
* single /set type method call. This is the combined total, e.g., if the maximum is 10, you
|
|
292
|
+
* could not create 7 objects and destroy 6, as this would be 13 actions, which exceeds the
|
|
293
|
+
* limit. Suggested minimum: 500.
|
|
294
|
+
*/
|
|
295
|
+
maxObjectsInSet: UnsignedInt;
|
|
296
|
+
/**
|
|
297
|
+
* A list of identifiers for algorithms registered in the collation registry, as defined in
|
|
298
|
+
* {@link https://www.rfc-editor.org/rfc/rfc4790.html RFC 4790}, that the server supports for
|
|
299
|
+
* sorting when querying records.
|
|
300
|
+
*/
|
|
301
|
+
collationAlgorithms: string[];
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
interface AccountCapabilityRegistry {
|
|
305
|
+
"urn:ietf:params:jmap:core": EmptyObject;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
import { z } from "zod/v4";
|
|
2
|
+
import { CORE_CAPABILITY_URI } from "../common/registry.js";
|
|
3
|
+
import { Blob } from "./blob/blob.js";
|
|
4
|
+
import { Core } from "./core/core.js";
|
|
5
|
+
import { assertInvocationMethod } from "./utils/assert-invocation-method.js";
|
|
6
|
+
import { createReadOnlyAccountValidator } from "./utils/create-readonly-account-validator.js";
|
|
7
|
+
const MEGABYTE = 1_000_000; // 1 MB in bytes
|
|
8
|
+
/**
|
|
9
|
+
* Validates that `/get` method calls do not exceed the server's `maxObjectsInGet` limit.
|
|
10
|
+
*
|
|
11
|
+
* **Object Retrieval Limits (RFC 8620 Section 5.1):**
|
|
12
|
+
* - Enforces the `maxObjectsInGet` limit defined in the Core capability object
|
|
13
|
+
* - This limit applies to the `ids` array in any `/get` method call (`Mailbox/get`, `Email/get`, etc.)
|
|
14
|
+
* - Prevents requests that would be rejected by the server with a `requestTooLarge` error
|
|
15
|
+
* - The server defines this limit based on resource constraints and performance characteristics
|
|
16
|
+
*
|
|
17
|
+
* This validation catches oversized requests client-side before transmission, providing
|
|
18
|
+
* immediate feedback and avoiding unnecessary network traffic.
|
|
19
|
+
*
|
|
20
|
+
* **Example:**
|
|
21
|
+
* If a server sets `maxObjectsInGet: 500`, attempting to fetch 1000 email IDs in a single
|
|
22
|
+
* `Email/get` call would be caught and reported by this validator.
|
|
23
|
+
*
|
|
24
|
+
* @see {@link https://www.rfc-editor.org/rfc/rfc8620.html#section-5.1 | RFC 8620 Section 5.1: /get}
|
|
25
|
+
* @see {@link https://www.rfc-editor.org/rfc/rfc8620.html#section-2 | RFC 8620 Section 2: The JMAP Session Resource}
|
|
26
|
+
|
|
27
|
+
*/
|
|
28
|
+
export const maxObjectsInGetPlugin = {
|
|
29
|
+
name: "core-max-objects-in-get",
|
|
30
|
+
hook: "invocation",
|
|
31
|
+
trigger: {
|
|
32
|
+
method: "get",
|
|
33
|
+
},
|
|
34
|
+
validate(context) {
|
|
35
|
+
const { serverCapabilities, invocation } = context;
|
|
36
|
+
assertInvocationMethod(invocation, "get");
|
|
37
|
+
const coreCapability = serverCapabilities[CORE_CAPABILITY_URI];
|
|
38
|
+
const ids = invocation.getArgument("ids");
|
|
39
|
+
if (ids && ids.length > coreCapability.maxObjectsInGet) {
|
|
40
|
+
return {
|
|
41
|
+
valid: false,
|
|
42
|
+
errors: [
|
|
43
|
+
new Error(`Request contains ${ids.length} objects, but server limit is ${coreCapability.maxObjectsInGet}`),
|
|
44
|
+
],
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
return { valid: true };
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* Validates that `/set` method calls do not exceed the server's `maxObjectsInSet` limit.
|
|
52
|
+
*
|
|
53
|
+
* **Object Modification Limits (RFC 8620 Section 5.3):**
|
|
54
|
+
* - Enforces the `maxObjectsInSet` limit defined in the Core capability object
|
|
55
|
+
* - Counts the total number of operations across `create`, `update`, and `destroy` arguments
|
|
56
|
+
* - Applies to all `/set` method calls (`Mailbox/set`, `Email/set`, etc.)
|
|
57
|
+
* - Prevents requests that would be rejected with a `requestTooLarge` error
|
|
58
|
+
* - The server defines this limit to manage transaction size and resource usage
|
|
59
|
+
*
|
|
60
|
+
* This validation catches oversized batch operations client-side before transmission,
|
|
61
|
+
* allowing clients to split large operations into multiple requests if needed.
|
|
62
|
+
*
|
|
63
|
+
* **Example:**
|
|
64
|
+
* If a server sets `maxObjectsInSet: 100`, attempting to create 50 mailboxes, update 30,
|
|
65
|
+
* and delete 25 (total: 105 operations) in a single `Mailbox/set` call would be rejected.
|
|
66
|
+
*
|
|
67
|
+
* @see {@link https://www.rfc-editor.org/rfc/rfc8620.html#section-5.3 | RFC 8620 Section 5.3: /set}
|
|
68
|
+
* @see {@link https://www.rfc-editor.org/rfc/rfc8620.html#section-2 | RFC 8620 Section 2: The JMAP Session Resource}
|
|
69
|
+
|
|
70
|
+
*/
|
|
71
|
+
export const maxObjectsInSetPlugin = {
|
|
72
|
+
name: "core-max-objects-in-set",
|
|
73
|
+
hook: "invocation",
|
|
74
|
+
trigger: {
|
|
75
|
+
method: "set",
|
|
76
|
+
},
|
|
77
|
+
validate(context) {
|
|
78
|
+
const { serverCapabilities, invocation } = context;
|
|
79
|
+
assertInvocationMethod(invocation, "set");
|
|
80
|
+
const coreCapability = serverCapabilities[CORE_CAPABILITY_URI];
|
|
81
|
+
const create = invocation.getArgument("create");
|
|
82
|
+
const update = invocation.getArgument("update");
|
|
83
|
+
const destroy = invocation.getArgument("destroy");
|
|
84
|
+
const totalObjects = (create ? Object.keys(create).length : 0) +
|
|
85
|
+
(update ? Object.keys(update).length : 0) +
|
|
86
|
+
(destroy ? destroy.length : 0);
|
|
87
|
+
if (totalObjects > coreCapability.maxObjectsInSet) {
|
|
88
|
+
return {
|
|
89
|
+
valid: false,
|
|
90
|
+
errors: [
|
|
91
|
+
new Error(`Request contains ${totalObjects} operations, but server limit is ${coreCapability.maxObjectsInSet}`),
|
|
92
|
+
],
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
return { valid: true };
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
/**
|
|
99
|
+
* Validates that `/query` method calls only use server-supported collation algorithms.
|
|
100
|
+
*
|
|
101
|
+
* **Collation Algorithm Support (RFC 8620 Section 5.5):**
|
|
102
|
+
* - Validates that any `collation` property in sort criteria is included in the server's
|
|
103
|
+
* `collationAlgorithms` capability list
|
|
104
|
+
* - Collation algorithms control how strings are compared and sorted (case sensitivity,
|
|
105
|
+
* locale-specific ordering, accent handling, etc.)
|
|
106
|
+
* - Common algorithms include "i;ascii-casemap" (case-insensitive ASCII) and "i;unicode-casemap"
|
|
107
|
+
* - Server support varies based on implementation and internationalisation capabilities
|
|
108
|
+
*
|
|
109
|
+
* This validation prevents query errors by catching unsupported collation algorithms before
|
|
110
|
+
* sending the request. Without this check, the server would reject the query with an
|
|
111
|
+
* `unsupportedSort` error.
|
|
112
|
+
*
|
|
113
|
+
* **Example:**
|
|
114
|
+
* If a server only supports "i;ascii-casemap" collation, attempting to sort with
|
|
115
|
+
* "i;unicode-casemap" would be caught and reported by this validator.
|
|
116
|
+
*
|
|
117
|
+
* @see {@link https://www.rfc-editor.org/rfc/rfc8620.html#section-5.5 | RFC 8620 Section 5.5: /query}
|
|
118
|
+
* @see {@link https://www.rfc-editor.org/rfc/rfc8620.html#section-2 | RFC 8620 Section 2: The JMAP Session Resource}
|
|
119
|
+
|
|
120
|
+
*/
|
|
121
|
+
export const collationAlgorithmsPlugin = {
|
|
122
|
+
name: "core-collation-algorithms",
|
|
123
|
+
hook: "invocation",
|
|
124
|
+
trigger: {
|
|
125
|
+
method: "query",
|
|
126
|
+
},
|
|
127
|
+
validate(context) {
|
|
128
|
+
const { serverCapabilities, invocation } = context;
|
|
129
|
+
assertInvocationMethod(invocation, "query");
|
|
130
|
+
const coreCapability = serverCapabilities[CORE_CAPABILITY_URI];
|
|
131
|
+
const sort = invocation.getArgument("sort");
|
|
132
|
+
if (sort) {
|
|
133
|
+
const errors = [];
|
|
134
|
+
for (const sortCriterion of sort) {
|
|
135
|
+
if (sortCriterion.collation && !coreCapability.collationAlgorithms.includes(sortCriterion.collation)) {
|
|
136
|
+
errors.push(new Error(`Unsupported collation algorithm '${sortCriterion.collation}'. Supported algorithms: ${coreCapability.collationAlgorithms.join(", ")}`));
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
if (errors.length > 0) {
|
|
140
|
+
return {
|
|
141
|
+
valid: false,
|
|
142
|
+
errors,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return { valid: true };
|
|
147
|
+
},
|
|
148
|
+
};
|
|
149
|
+
/**
|
|
150
|
+
* Prevents `/set` method calls on read-only accounts.
|
|
151
|
+
*
|
|
152
|
+
* **Read-Only Account Protection (RFC 8620 Section 1.6.2, Section 5.3):**
|
|
153
|
+
* - Validates that the target account's `isReadOnly` property is `false`
|
|
154
|
+
* - Read-only accounts cannot accept any data modification operations
|
|
155
|
+
* - All `/set` methods (create, update, destroy) require write access
|
|
156
|
+
* - Attempting modifications on read-only accounts would fail with an `accountReadOnly` error
|
|
157
|
+
*
|
|
158
|
+
* This validator catches the error client-side before making a server request, providing
|
|
159
|
+
* immediate feedback when attempting invalid operations on read-only accounts.
|
|
160
|
+
*
|
|
161
|
+
* **Common read-only scenarios:**
|
|
162
|
+
* - Shared resources with read-only permissions
|
|
163
|
+
* - Archive or backup accounts
|
|
164
|
+
* - Accounts in maintenance mode
|
|
165
|
+
* - Delegated access with restricted permissions
|
|
166
|
+
*
|
|
167
|
+
* @see {@link https://www.rfc-editor.org/rfc/rfc8620.html#section-5.3 | RFC 8620 Section 5.3: /set}
|
|
168
|
+
* @see {@link https://www.rfc-editor.org/rfc/rfc8620.html#section-2 | RFC 8620 Section 2: The JMAP Session Resource}
|
|
169
|
+
|
|
170
|
+
*/
|
|
171
|
+
export const preventSetOnReadOnlyAccountPlugin = createReadOnlyAccountValidator({
|
|
172
|
+
name: "core-prevent-set-on-readonly-account",
|
|
173
|
+
trigger: {
|
|
174
|
+
method: "set",
|
|
175
|
+
},
|
|
176
|
+
});
|
|
177
|
+
/**
|
|
178
|
+
* Validates that API requests do not exceed the server's `maxCallsInRequest` limit.
|
|
179
|
+
*
|
|
180
|
+
* **Request Batching Limits (RFC 8620 Section 3.2):**
|
|
181
|
+
* - Enforces the `maxCallsInRequest` limit defined in the Core capability object
|
|
182
|
+
* - This limit applies to the total number of method calls in the `methodCalls` array
|
|
183
|
+
* - JMAP allows batching multiple method calls in a single request for efficiency
|
|
184
|
+
* - Prevents requests that would be rejected with a `requestTooLarge` error
|
|
185
|
+
* - The server defines this limit to manage request processing complexity and resources
|
|
186
|
+
*
|
|
187
|
+
* This validation runs during the `pre-build` lifecycle hook, checking the request structure
|
|
188
|
+
* before serialisation. It catches oversized batches early, allowing clients to split large
|
|
189
|
+
* batches into multiple API requests.
|
|
190
|
+
*
|
|
191
|
+
* **Example:**
|
|
192
|
+
* If a server sets `maxCallsInRequest: 50`, attempting to batch 75 method calls
|
|
193
|
+
* (e.g., Email/get + 50 Email/set + 24 Mailbox/get) would be caught by this validator.
|
|
194
|
+
*
|
|
195
|
+
* @see {@link https://www.rfc-editor.org/rfc/rfc8620.html#section-3.2 | RFC 8620 Section 3.2: Making an API Request}
|
|
196
|
+
* @see {@link https://www.rfc-editor.org/rfc/rfc8620.html#section-2 | RFC 8620 Section 2: The JMAP Session Resource}
|
|
197
|
+
|
|
198
|
+
*/
|
|
199
|
+
export const maxCallsInRequestPlugin = {
|
|
200
|
+
name: "core-max-calls-in-request",
|
|
201
|
+
hook: "pre-build",
|
|
202
|
+
trigger: {},
|
|
203
|
+
validate(context) {
|
|
204
|
+
const { serverCapabilities, data: { methodCalls }, } = context;
|
|
205
|
+
const { maxCallsInRequest } = serverCapabilities[CORE_CAPABILITY_URI];
|
|
206
|
+
if (methodCalls.length > maxCallsInRequest) {
|
|
207
|
+
return {
|
|
208
|
+
valid: false,
|
|
209
|
+
errors: [
|
|
210
|
+
new Error(`Request contains ${methodCalls.length} methods, but server limit is ${maxCallsInRequest}`),
|
|
211
|
+
],
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
return { valid: true };
|
|
215
|
+
},
|
|
216
|
+
};
|
|
217
|
+
/**
|
|
218
|
+
* Validates that serialised API requests do not exceed the server's `maxSizeRequest` limit.
|
|
219
|
+
*
|
|
220
|
+
* **Request Size Limits (RFC 8620 Section 3.2):**
|
|
221
|
+
* - Enforces the `maxSizeRequest` limit defined in the Core capability object
|
|
222
|
+
* - Measures the total size of the serialised request body in bytes
|
|
223
|
+
* - This limit applies after the request has been serialised (JSON stringified and encoded)
|
|
224
|
+
* - Prevents requests that would be rejected with a `requestTooLarge` error
|
|
225
|
+
* - The server defines this limit based on available memory, bandwidth, and processing capacity
|
|
226
|
+
*
|
|
227
|
+
* This validation runs during the `post-serialization` lifecycle hook, checking the actual
|
|
228
|
+
* wire format size. It handles various body formats (string, Blob, ArrayBuffer) and provides
|
|
229
|
+
* human-readable error messages in megabytes.
|
|
230
|
+
*
|
|
231
|
+
* **Example:**
|
|
232
|
+
* If a server sets `maxSizeRequest: 10000000` (10 MB), attempting to send a request with
|
|
233
|
+
* a large Email/import containing 15 MB of email data would be caught and reported as
|
|
234
|
+
* "Request size (15.00 MB) exceeds server limit of 10.00 MB".
|
|
235
|
+
*
|
|
236
|
+
* @see {@link https://www.rfc-editor.org/rfc/rfc8620.html#section-3.2 | RFC 8620 Section 3.2: Making an API Request}
|
|
237
|
+
* @see {@link https://www.rfc-editor.org/rfc/rfc8620.html#section-2 | RFC 8620 Section 2: The JMAP Session Resource}
|
|
238
|
+
|
|
239
|
+
*/
|
|
240
|
+
export const maxSizeRequestPlugin = {
|
|
241
|
+
name: "core-max-size-request",
|
|
242
|
+
hook: "post-serialization",
|
|
243
|
+
trigger: {},
|
|
244
|
+
async validate(context) {
|
|
245
|
+
const { serverCapabilities, data: { body }, } = context;
|
|
246
|
+
const { maxSizeRequest } = serverCapabilities[CORE_CAPABILITY_URI];
|
|
247
|
+
// Forward-compatibility: serialize() always produces a string body; Blob and ArrayBuffer
|
|
248
|
+
// paths exist for custom post-serialization transformers.
|
|
249
|
+
let inputData;
|
|
250
|
+
if (typeof body === "string") {
|
|
251
|
+
const encoder = new TextEncoder();
|
|
252
|
+
inputData = encoder.encode(body);
|
|
253
|
+
/* v8 ignore start */
|
|
254
|
+
/* istanbul ignore else */
|
|
255
|
+
}
|
|
256
|
+
else if (body instanceof globalThis.Blob) {
|
|
257
|
+
/* istanbul ignore next */
|
|
258
|
+
inputData = new Uint8Array(await body.arrayBuffer());
|
|
259
|
+
/* istanbul ignore else */
|
|
260
|
+
}
|
|
261
|
+
else {
|
|
262
|
+
inputData = new Uint8Array(body);
|
|
263
|
+
}
|
|
264
|
+
/* v8 ignore stop */
|
|
265
|
+
const requestSize = inputData.length;
|
|
266
|
+
if (requestSize > maxSizeRequest) {
|
|
267
|
+
const sizeInMB = (requestSize / MEGABYTE).toFixed(2);
|
|
268
|
+
const maxSizeInMB = (maxSizeRequest / MEGABYTE).toFixed(2);
|
|
269
|
+
return {
|
|
270
|
+
valid: false,
|
|
271
|
+
errors: [new Error(`Request size (${sizeInMB} MB) exceeds server limit of ${maxSizeInMB} MB`)],
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
return {
|
|
275
|
+
valid: true,
|
|
276
|
+
};
|
|
277
|
+
},
|
|
278
|
+
};
|
|
279
|
+
/**
|
|
280
|
+
* Prevents Blob/copy operations on read-only target accounts.
|
|
281
|
+
*
|
|
282
|
+
* **Read-Only Account Protection for Blob/copy:**
|
|
283
|
+
* - Validates that the target account's (accountId) `isReadOnly` property is `false`
|
|
284
|
+
* - Blob/copy writes blobs to the target account, requiring write access
|
|
285
|
+
* - The source account (fromAccountId) only needs read access
|
|
286
|
+
* - Attempting to copy to a read-only account would fail with an `accountReadOnly` error
|
|
287
|
+
*
|
|
288
|
+
* This validator catches the error client-side before making a server request, providing
|
|
289
|
+
* immediate feedback when attempting to copy blobs to read-only accounts.
|
|
290
|
+
*
|
|
291
|
+
* **Common read-only scenarios:**
|
|
292
|
+
* - Shared accounts with read-only permissions
|
|
293
|
+
* - Archive or backup accounts
|
|
294
|
+
* - Accounts in maintenance mode
|
|
295
|
+
*
|
|
296
|
+
* @see {@link https://www.rfc-editor.org/rfc/rfc8620.html#section-6.3 | RFC 8620 Section 6.3: Blob/copy}
|
|
297
|
+
|
|
298
|
+
*/
|
|
299
|
+
export const preventBlobCopyOnReadOnlyAccountPlugin = createReadOnlyAccountValidator({
|
|
300
|
+
name: "core-prevent-blob-copy-on-readonly-account",
|
|
301
|
+
trigger: {
|
|
302
|
+
dataType: "Blob",
|
|
303
|
+
method: "copy",
|
|
304
|
+
},
|
|
305
|
+
});
|
|
306
|
+
const coreServerCapabilitySchema = z.looseObject({
|
|
307
|
+
maxSizeUpload: z.number().int().min(0),
|
|
308
|
+
maxConcurrentUpload: z.number().int().min(1),
|
|
309
|
+
maxSizeRequest: z.number().int().min(0),
|
|
310
|
+
maxConcurrentRequests: z.number().int().min(0),
|
|
311
|
+
maxCallsInRequest: z.number().int().min(0),
|
|
312
|
+
maxObjectsInGet: z.number().int().min(0),
|
|
313
|
+
maxObjectsInSet: z.number().int().min(0),
|
|
314
|
+
collationAlgorithms: z.array(z.string()),
|
|
315
|
+
});
|
|
316
|
+
/**
|
|
317
|
+
* Defines the Core capability, including Core/echo and Blob/copy invocations
|
|
318
|
+
* and validation plugins.
|
|
319
|
+
*/
|
|
320
|
+
export const CoreCapability = {
|
|
321
|
+
uri: CORE_CAPABILITY_URI,
|
|
322
|
+
invocations: {
|
|
323
|
+
Core,
|
|
324
|
+
Blob: {
|
|
325
|
+
request: {
|
|
326
|
+
copy: Blob.request.copy,
|
|
327
|
+
},
|
|
328
|
+
response: {
|
|
329
|
+
copy: Blob.response.copy,
|
|
330
|
+
},
|
|
331
|
+
},
|
|
332
|
+
},
|
|
333
|
+
validators: [
|
|
334
|
+
maxObjectsInGetPlugin,
|
|
335
|
+
maxObjectsInSetPlugin,
|
|
336
|
+
collationAlgorithmsPlugin,
|
|
337
|
+
preventSetOnReadOnlyAccountPlugin,
|
|
338
|
+
preventBlobCopyOnReadOnlyAccountPlugin,
|
|
339
|
+
maxCallsInRequestPlugin,
|
|
340
|
+
maxSizeRequestPlugin,
|
|
341
|
+
],
|
|
342
|
+
schema: { serverCapability: coreServerCapabilitySchema },
|
|
343
|
+
};
|
|
344
|
+
//# sourceMappingURL=core-capability.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"core-capability.js","sourceRoot":"","sources":["../../../src/capabilities/core-capability.ts"],"names":[],"mappings":"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"}
|