@serve.zone/coremail 1.0.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/.smartconfig.json +50 -0
- package/changelog.md +10 -0
- package/cli.js +4 -0
- package/dist_ts/00_commitinfo_data.d.ts +8 -0
- package/dist_ts/00_commitinfo_data.js +9 -0
- package/dist_ts/classes.auth.d.ts +45 -0
- package/dist_ts/classes.auth.js +204 -0
- package/dist_ts/classes.config.d.ts +2 -0
- package/dist_ts/classes.config.js +72 -0
- package/dist_ts/classes.coremail.d.ts +41 -0
- package/dist_ts/classes.coremail.js +155 -0
- package/dist_ts/classes.desiredstate.d.ts +40 -0
- package/dist_ts/classes.desiredstate.js +382 -0
- package/dist_ts/classes.gateway.d.ts +62 -0
- package/dist_ts/classes.gateway.js +669 -0
- package/dist_ts/classes.inbound.d.ts +49 -0
- package/dist_ts/classes.inbound.js +702 -0
- package/dist_ts/classes.maintenance.d.ts +16 -0
- package/dist_ts/classes.maintenance.js +339 -0
- package/dist_ts/classes.models.d.ts +161 -0
- package/dist_ts/classes.models.js +595 -0
- package/dist_ts/classes.server.d.ts +40 -0
- package/dist_ts/classes.server.js +309 -0
- package/dist_ts/classes.storage.d.ts +32 -0
- package/dist_ts/classes.storage.js +317 -0
- package/dist_ts/classes.submissions.d.ts +33 -0
- package/dist_ts/classes.submissions.js +385 -0
- package/dist_ts/classes.transfer.d.ts +41 -0
- package/dist_ts/classes.transfer.js +289 -0
- package/dist_ts/coremail.cursor.d.ts +20 -0
- package/dist_ts/coremail.cursor.js +191 -0
- package/dist_ts/coremail.log.d.ts +3 -0
- package/dist_ts/coremail.log.js +11 -0
- package/dist_ts/coremail.mime.d.ts +8 -0
- package/dist_ts/coremail.mime.js +63 -0
- package/dist_ts/coremail.persistence.d.ts +148 -0
- package/dist_ts/coremail.persistence.js +695 -0
- package/dist_ts/coremail.quota.d.ts +29 -0
- package/dist_ts/coremail.quota.js +149 -0
- package/dist_ts/coremail.selectors.d.ts +3 -0
- package/dist_ts/coremail.selectors.js +21 -0
- package/dist_ts/coremail.validation.d.ts +9 -0
- package/dist_ts/coremail.validation.js +325 -0
- package/dist_ts/index.d.ts +4 -0
- package/dist_ts/index.js +39 -0
- package/dist_ts/interfaces.d.ts +92 -0
- package/dist_ts/interfaces.js +2 -0
- package/dist_ts/plugins.d.ts +11 -0
- package/dist_ts/plugins.js +12 -0
- package/license.md +19 -0
- package/package.json +48 -0
- package/readme.md +199 -0
- package/ts/00_commitinfo_data.ts +8 -0
- package/ts/classes.auth.ts +321 -0
- package/ts/classes.config.ts +104 -0
- package/ts/classes.coremail.ts +249 -0
- package/ts/classes.desiredstate.ts +508 -0
- package/ts/classes.gateway.ts +811 -0
- package/ts/classes.inbound.ts +898 -0
- package/ts/classes.maintenance.ts +335 -0
- package/ts/classes.models.ts +530 -0
- package/ts/classes.server.ts +444 -0
- package/ts/classes.storage.ts +448 -0
- package/ts/classes.submissions.ts +554 -0
- package/ts/classes.transfer.ts +379 -0
- package/ts/coremail.cursor.ts +328 -0
- package/ts/coremail.log.ts +21 -0
- package/ts/coremail.mime.ts +94 -0
- package/ts/coremail.persistence.ts +1156 -0
- package/ts/coremail.quota.ts +214 -0
- package/ts/coremail.selectors.ts +35 -0
- package/ts/coremail.validation.ts +446 -0
- package/ts/index.ts +38 -0
- package/ts/interfaces.ts +109 -0
- package/ts/plugins.ts +11 -0
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
import {} from './classes.models.js';
|
|
3
|
+
import { CoreMailStorage, createOpaqueToken, createSha256, } from './classes.storage.js';
|
|
4
|
+
import { logCoreMailFailure } from './coremail.log.js';
|
|
5
|
+
import { requireCoreMailIdentifier, requireCoreMailUuid, } from './coremail.selectors.js';
|
|
6
|
+
const transferPathRegex = /^\/transfers\/([a-f0-9-]{36})$/;
|
|
7
|
+
const maximumBytesForPurpose = (purposeArg) => {
|
|
8
|
+
const limits = plugins.serveZoneInterfaces.data.coreMailLimits;
|
|
9
|
+
switch (purposeArg) {
|
|
10
|
+
case 'desired-state-upload':
|
|
11
|
+
return limits.desiredStateBytes;
|
|
12
|
+
case 'outbound-part-upload':
|
|
13
|
+
return limits.attachmentBytes;
|
|
14
|
+
case 'outbound-mime-download':
|
|
15
|
+
case 'inbound-mime-upload':
|
|
16
|
+
case 'inbound-mime-download':
|
|
17
|
+
return limits.serializedMimeBytes;
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
const secureDigestEqual = (leftArg, rightArg) => {
|
|
21
|
+
const left = Buffer.from(leftArg.slice('sha256:'.length), 'hex');
|
|
22
|
+
const right = Buffer.from(rightArg.slice('sha256:'.length), 'hex');
|
|
23
|
+
return left.byteLength === right.byteLength
|
|
24
|
+
&& plugins.nodeCrypto.timingSafeEqual(left, right);
|
|
25
|
+
};
|
|
26
|
+
const authorityFilter = (grantIdArg, authorityArg) => ({
|
|
27
|
+
grantId: requireCoreMailUuid(grantIdArg, 'grantId'),
|
|
28
|
+
authorityKind: authorityArg.kind,
|
|
29
|
+
tenantId: authorityArg.tenantId === '-'
|
|
30
|
+
? '-'
|
|
31
|
+
: requireCoreMailIdentifier(authorityArg.tenantId, 'authority.tenantId'),
|
|
32
|
+
serviceId: requireCoreMailIdentifier(authorityArg.serviceId, 'authority.serviceId'),
|
|
33
|
+
bindingId: authorityArg.bindingId === '-'
|
|
34
|
+
? '-'
|
|
35
|
+
: requireCoreMailIdentifier(authorityArg.bindingId, 'authority.bindingId'),
|
|
36
|
+
});
|
|
37
|
+
export class CoreMailTransferService {
|
|
38
|
+
models;
|
|
39
|
+
storage;
|
|
40
|
+
now;
|
|
41
|
+
completionTasks = new Set();
|
|
42
|
+
operationControllers = new Set();
|
|
43
|
+
stopController = new AbortController();
|
|
44
|
+
stopping = false;
|
|
45
|
+
constructor(models, storage, now = Date.now) {
|
|
46
|
+
this.models = models;
|
|
47
|
+
this.storage = storage;
|
|
48
|
+
this.now = now;
|
|
49
|
+
}
|
|
50
|
+
async stop() {
|
|
51
|
+
this.stopping = true;
|
|
52
|
+
this.stopController.abort(new Error('CoreMail transfer service is stopping.'));
|
|
53
|
+
for (const controller of this.operationControllers) {
|
|
54
|
+
controller.abort(new Error('CoreMail transfer service is stopping.'));
|
|
55
|
+
}
|
|
56
|
+
await Promise.allSettled([...this.completionTasks]);
|
|
57
|
+
}
|
|
58
|
+
track(taskArg, controllerArg) {
|
|
59
|
+
this.completionTasks.add(taskArg);
|
|
60
|
+
this.operationControllers.add(controllerArg);
|
|
61
|
+
void taskArg.then(() => {
|
|
62
|
+
this.completionTasks.delete(taskArg);
|
|
63
|
+
this.operationControllers.delete(controllerArg);
|
|
64
|
+
}, () => {
|
|
65
|
+
this.completionTasks.delete(taskArg);
|
|
66
|
+
this.operationControllers.delete(controllerArg);
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
async issueGrant(optionsArg) {
|
|
70
|
+
if (this.stopping) {
|
|
71
|
+
throw new Error('CoreMail transfer service is stopping.');
|
|
72
|
+
}
|
|
73
|
+
const maximumBytes = maximumBytesForPurpose(optionsArg.purpose);
|
|
74
|
+
if (!Number.isSafeInteger(optionsArg.lengthBytes)
|
|
75
|
+
|| optionsArg.lengthBytes < 0
|
|
76
|
+
|| optionsArg.lengthBytes > maximumBytes) {
|
|
77
|
+
throw new Error('CoreMail transfer length exceeds its purpose budget.');
|
|
78
|
+
}
|
|
79
|
+
const grantId = plugins.nodeCrypto.randomUUID();
|
|
80
|
+
const bearerToken = createOpaqueToken();
|
|
81
|
+
const path = `/transfers/${grantId}`;
|
|
82
|
+
const createdAt = this.now();
|
|
83
|
+
const expiresAt = createdAt + plugins.serveZoneInterfaces.data.coreMailLimits.transferGrantTtlMs;
|
|
84
|
+
const inserted = await this.models.TransferGrant.exact.insert({
|
|
85
|
+
grantId,
|
|
86
|
+
authorityKind: optionsArg.authority.kind,
|
|
87
|
+
tenantId: optionsArg.authority.tenantId,
|
|
88
|
+
serviceId: optionsArg.authority.serviceId,
|
|
89
|
+
bindingId: optionsArg.authority.bindingId,
|
|
90
|
+
purpose: optionsArg.purpose,
|
|
91
|
+
method: optionsArg.method,
|
|
92
|
+
path,
|
|
93
|
+
bearerSha256: createSha256(bearerToken),
|
|
94
|
+
expectedSha256: optionsArg.expectedSha256,
|
|
95
|
+
lengthBytes: optionsArg.lengthBytes,
|
|
96
|
+
contentType: optionsArg.contentType,
|
|
97
|
+
objectKey: optionsArg.objectKey,
|
|
98
|
+
state: 'issued',
|
|
99
|
+
expiresAt,
|
|
100
|
+
createdAt,
|
|
101
|
+
});
|
|
102
|
+
if (inserted.status === 'conflict') {
|
|
103
|
+
throw new Error('CoreMail transfer grant identity conflict.');
|
|
104
|
+
}
|
|
105
|
+
return {
|
|
106
|
+
grantId,
|
|
107
|
+
method: optionsArg.method,
|
|
108
|
+
path,
|
|
109
|
+
bearerToken,
|
|
110
|
+
sha256: optionsArg.expectedSha256,
|
|
111
|
+
lengthBytes: optionsArg.lengthBytes,
|
|
112
|
+
contentType: optionsArg.contentType,
|
|
113
|
+
issuedAt: createdAt,
|
|
114
|
+
expiresAt,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
async assertConsumed(optionsArg) {
|
|
118
|
+
const grant = await this.models.TransferGrant.exact.findStoredOne({
|
|
119
|
+
...authorityFilter(optionsArg.grantId, optionsArg.authority),
|
|
120
|
+
state: 'consumed',
|
|
121
|
+
purpose: optionsArg.purpose,
|
|
122
|
+
expectedSha256: optionsArg.expectedSha256,
|
|
123
|
+
lengthBytes: optionsArg.lengthBytes,
|
|
124
|
+
...(optionsArg.objectKey === undefined
|
|
125
|
+
? {}
|
|
126
|
+
: { objectKey: optionsArg.objectKey }),
|
|
127
|
+
});
|
|
128
|
+
if (!grant) {
|
|
129
|
+
throw new Error('CoreMail transfer grant is not consumed by this authority.');
|
|
130
|
+
}
|
|
131
|
+
await this.storage.verifyObject(grant.objectKey, grant.lengthBytes, grant.expectedSha256, maximumBytesForPurpose(grant.purpose));
|
|
132
|
+
return grant;
|
|
133
|
+
}
|
|
134
|
+
async handleHttp(contextArg) {
|
|
135
|
+
const match = transferPathRegex.exec(contextArg.path);
|
|
136
|
+
if (!match) {
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
if (contextArg.method !== 'GET' && contextArg.method !== 'PUT') {
|
|
140
|
+
return new Response('Method Not Allowed', {
|
|
141
|
+
status: 405,
|
|
142
|
+
headers: { Allow: 'GET, PUT' },
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
const authorization = contextArg.headers.get('authorization');
|
|
146
|
+
if (!authorization?.startsWith('Bearer ')) {
|
|
147
|
+
return new Response('Unauthorized', { status: 401 });
|
|
148
|
+
}
|
|
149
|
+
const bearer = authorization.slice('Bearer '.length);
|
|
150
|
+
if (bearer.length < 32 || bearer.length > 512) {
|
|
151
|
+
return new Response('Unauthorized', { status: 401 });
|
|
152
|
+
}
|
|
153
|
+
const grantId = match[1];
|
|
154
|
+
const grant = await this.models.TransferGrant.exact.findStoredOne({
|
|
155
|
+
grantId,
|
|
156
|
+
method: contextArg.method,
|
|
157
|
+
state: 'issued',
|
|
158
|
+
});
|
|
159
|
+
if (!grant
|
|
160
|
+
|| grant.expiresAt < this.now()
|
|
161
|
+
|| !secureDigestEqual(grant.bearerSha256, createSha256(bearer))) {
|
|
162
|
+
return new Response('Unauthorized', { status: 401 });
|
|
163
|
+
}
|
|
164
|
+
const claim = await this.models.TransferGrant.exact.transition({
|
|
165
|
+
current: grant,
|
|
166
|
+
change: (modelArg) => {
|
|
167
|
+
modelArg.state = 'transferring';
|
|
168
|
+
},
|
|
169
|
+
});
|
|
170
|
+
if (claim.status !== 'transitioned') {
|
|
171
|
+
return new Response('Conflict', { status: 409 });
|
|
172
|
+
}
|
|
173
|
+
if (contextArg.method === 'PUT') {
|
|
174
|
+
return await this.handleUpload(contextArg, claim.document);
|
|
175
|
+
}
|
|
176
|
+
return await this.handleDownload(contextArg, claim.document);
|
|
177
|
+
}
|
|
178
|
+
async handleUpload(contextArg, grantArg) {
|
|
179
|
+
const contentLength = Number(contextArg.headers.get('content-length'));
|
|
180
|
+
if (!contextArg.request.body
|
|
181
|
+
|| !Number.isSafeInteger(contentLength)
|
|
182
|
+
|| contentLength !== grantArg.lengthBytes
|
|
183
|
+
|| contextArg.headers.get('content-type') !== grantArg.contentType) {
|
|
184
|
+
await this.failGrant(grantArg.grantId);
|
|
185
|
+
return new Response('Invalid transfer metadata', { status: 400 });
|
|
186
|
+
}
|
|
187
|
+
const operationController = new AbortController();
|
|
188
|
+
const signal = AbortSignal.any([
|
|
189
|
+
contextArg.request.signal,
|
|
190
|
+
this.stopController.signal,
|
|
191
|
+
operationController.signal,
|
|
192
|
+
AbortSignal.timeout(plugins.serveZoneInterfaces.data.coreMailLimits.transferOverallTimeoutMs),
|
|
193
|
+
]);
|
|
194
|
+
const task = (async () => {
|
|
195
|
+
try {
|
|
196
|
+
await this.storage.putExact(grantArg.objectKey, contextArg.request.body, grantArg.lengthBytes, grantArg.expectedSha256, grantArg.contentType, maximumBytesForPurpose(grantArg.purpose), signal);
|
|
197
|
+
const consumed = await this.models.TransferGrant.exact.transition({
|
|
198
|
+
current: grantArg,
|
|
199
|
+
change: (modelArg) => {
|
|
200
|
+
modelArg.state = 'consumed';
|
|
201
|
+
modelArg.consumedAt = this.now();
|
|
202
|
+
},
|
|
203
|
+
});
|
|
204
|
+
if (consumed.status !== 'transitioned') {
|
|
205
|
+
throw new Error('CoreMail transfer completion fence changed.');
|
|
206
|
+
}
|
|
207
|
+
return new Response(null, { status: 204 });
|
|
208
|
+
}
|
|
209
|
+
catch {
|
|
210
|
+
operationController.abort();
|
|
211
|
+
await this.failGrant(grantArg.grantId);
|
|
212
|
+
return new Response('Transfer failed', { status: 409 });
|
|
213
|
+
}
|
|
214
|
+
})();
|
|
215
|
+
this.track(task, operationController);
|
|
216
|
+
return await task;
|
|
217
|
+
}
|
|
218
|
+
async handleDownload(contextArg, grantArg) {
|
|
219
|
+
const operationController = new AbortController();
|
|
220
|
+
const signal = AbortSignal.any([
|
|
221
|
+
contextArg.request.signal,
|
|
222
|
+
this.stopController.signal,
|
|
223
|
+
operationController.signal,
|
|
224
|
+
AbortSignal.timeout(plugins.serveZoneInterfaces.data.coreMailLimits.transferOverallTimeoutMs),
|
|
225
|
+
]);
|
|
226
|
+
try {
|
|
227
|
+
const verified = await this.storage.getVerifiedStream(grantArg.objectKey, grantArg.lengthBytes, grantArg.expectedSha256, maximumBytesForPurpose(grantArg.purpose), signal);
|
|
228
|
+
const completion = verified.completion.then(async () => {
|
|
229
|
+
const consumed = await this.models.TransferGrant.exact.transition({
|
|
230
|
+
current: grantArg,
|
|
231
|
+
change: (modelArg) => {
|
|
232
|
+
modelArg.state = 'consumed';
|
|
233
|
+
modelArg.consumedAt = this.now();
|
|
234
|
+
},
|
|
235
|
+
});
|
|
236
|
+
if (consumed.status !== 'transitioned') {
|
|
237
|
+
throw new Error('CoreMail transfer completion fence changed.');
|
|
238
|
+
}
|
|
239
|
+
}).catch(async () => {
|
|
240
|
+
await this.failGrant(grantArg.grantId);
|
|
241
|
+
});
|
|
242
|
+
this.track(completion, operationController);
|
|
243
|
+
return new Response(verified.stream, {
|
|
244
|
+
status: 200,
|
|
245
|
+
headers: {
|
|
246
|
+
'Content-Type': grantArg.contentType,
|
|
247
|
+
'Content-Length': String(grantArg.lengthBytes),
|
|
248
|
+
'Cache-Control': 'no-store',
|
|
249
|
+
'X-Content-Type-Options': 'nosniff',
|
|
250
|
+
},
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
catch {
|
|
254
|
+
operationController.abort();
|
|
255
|
+
await this.failGrant(grantArg.grantId);
|
|
256
|
+
return new Response('Transfer failed', { status: 409 });
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
async failGrant(grantIdArg) {
|
|
260
|
+
try {
|
|
261
|
+
const grant = await this.models.TransferGrant.exact.findStoredOne({
|
|
262
|
+
grantId: grantIdArg,
|
|
263
|
+
state: { $in: ['issued', 'transferring'] },
|
|
264
|
+
});
|
|
265
|
+
if (!grant) {
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
const failed = await this.models.TransferGrant.exact.transition({
|
|
269
|
+
current: grant,
|
|
270
|
+
change: (modelArg) => {
|
|
271
|
+
modelArg.state = 'failed';
|
|
272
|
+
},
|
|
273
|
+
});
|
|
274
|
+
if (failed.status === 'concurrent_change') {
|
|
275
|
+
const current = await this.models.TransferGrant.exact.findStoredOne({
|
|
276
|
+
grantId: grantIdArg,
|
|
277
|
+
});
|
|
278
|
+
if (current?.state === 'issued' || current?.state === 'transferring') {
|
|
279
|
+
throw new Error('CoreMail transfer failure fence changed.');
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
catch {
|
|
284
|
+
await logCoreMailFailure('transfer', 'GRANT_FAILURE_PERSISTENCE_FAILED')
|
|
285
|
+
.catch(() => undefined);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy50cmFuc2Zlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL2NsYXNzZXMudHJhbnNmZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSxjQUFjLENBQUM7QUFDeEMsT0FBTyxFQUVOLE1BQU0scUJBQXFCLENBQUM7QUFDN0IsT0FBTyxFQUNMLGVBQWUsRUFDZixpQkFBaUIsRUFDakIsWUFBWSxHQUNiLE1BQU0sc0JBQXNCLENBQUM7QUFLOUIsT0FBTyxFQUFFLGtCQUFrQixFQUFFLE1BQU0sbUJBQW1CLENBQUM7QUFFdkQsT0FBTyxFQUNMLHlCQUF5QixFQUN6QixtQkFBbUIsR0FDcEIsTUFBTSx5QkFBeUIsQ0FBQztBQU1qQyxNQUFNLGlCQUFpQixHQUFHLGdDQUFnQyxDQUFDO0FBRTNELE1BQU0sc0JBQXNCLEdBQUcsQ0FBQyxVQUFpQyxFQUFVLEVBQUU7SUFDM0UsTUFBTSxNQUFNLEdBQUcsT0FBTyxDQUFDLG1CQUFtQixDQUFDLElBQUksQ0FBQyxjQUFjLENBQUM7SUFDL0QsUUFBUSxVQUFVLEVBQUUsQ0FBQztRQUNuQixLQUFLLHNCQUFzQjtZQUN6QixPQUFPLE1BQU0sQ0FBQyxpQkFBaUIsQ0FBQztRQUNsQyxLQUFLLHNCQUFzQjtZQUN6QixPQUFPLE1BQU0sQ0FBQyxlQUFlLENBQUM7UUFDaEMsS0FBSyx3QkFBd0IsQ0FBQztRQUM5QixLQUFLLHFCQUFxQixDQUFDO1FBQzNCLEtBQUssdUJBQXVCO1lBQzFCLE9BQU8sTUFBTSxDQUFDLG1CQUFtQixDQUFDO0lBQ3RDLENBQUM7QUFDSCxDQUFDLENBQUM7QUFFRixNQUFNLGlCQUFpQixHQUFHLENBQ3hCLE9BQXdCLEVBQ3hCLFFBQXlCLEVBQ2hCLEVBQUU7SUFDWCxNQUFNLElBQUksR0FBRyxNQUFNLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUMsU0FBUyxDQUFDLE1BQU0sQ0FBQyxFQUFFLEtBQUssQ0FB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
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
export interface ICoreMailInboundCursorAuthority {
|
|
3
|
+
tenantId: string;
|
|
4
|
+
serviceId: string;
|
|
5
|
+
bindingId: string;
|
|
6
|
+
bindingRevision: number;
|
|
7
|
+
}
|
|
8
|
+
export interface ICoreMailInboundCursorPosition {
|
|
9
|
+
snapshotAt: number;
|
|
10
|
+
receivedAt: number;
|
|
11
|
+
deliveryId: string;
|
|
12
|
+
}
|
|
13
|
+
type TCursorKey = plugins.serveZoneInterfaces.data.ICoreMailRuntimeKeyReference;
|
|
14
|
+
export declare class CoreMailInboundCursorCodec {
|
|
15
|
+
private readonly resolveRuntimeSecret;
|
|
16
|
+
constructor(resolveRuntimeSecret: (secretKeyArg: string) => string);
|
|
17
|
+
encode(authorityArg: ICoreMailInboundCursorAuthority, positionArg: ICoreMailInboundCursorPosition, keysArg: TCursorKey[]): string;
|
|
18
|
+
decode(cursorArg: string, authorityArg: ICoreMailInboundCursorAuthority, nowArg: number, keysArg: TCursorKey[]): ICoreMailInboundCursorPosition;
|
|
19
|
+
}
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
import { requireCoreMailIdentifier, requireCoreMailSafeInteger, requireCoreMailUuid, } from './coremail.selectors.js';
|
|
3
|
+
const maximumCursorAgeMs = 24 * 60 * 60 * 1_000;
|
|
4
|
+
const maximumFutureSkewMs = 1_000;
|
|
5
|
+
const payloadKeys = new Set([
|
|
6
|
+
'version',
|
|
7
|
+
'tenantId',
|
|
8
|
+
'serviceId',
|
|
9
|
+
'bindingId',
|
|
10
|
+
'bindingRevision',
|
|
11
|
+
'snapshotAt',
|
|
12
|
+
'receivedAt',
|
|
13
|
+
'deliveryId',
|
|
14
|
+
]);
|
|
15
|
+
const envelopeKeys = new Set([
|
|
16
|
+
'version',
|
|
17
|
+
'keyId',
|
|
18
|
+
'keyVersion',
|
|
19
|
+
'payload',
|
|
20
|
+
'signature',
|
|
21
|
+
]);
|
|
22
|
+
const canonicalPayload = (payloadArg) => JSON.stringify({
|
|
23
|
+
version: payloadArg.version,
|
|
24
|
+
tenantId: payloadArg.tenantId,
|
|
25
|
+
serviceId: payloadArg.serviceId,
|
|
26
|
+
bindingId: payloadArg.bindingId,
|
|
27
|
+
bindingRevision: payloadArg.bindingRevision,
|
|
28
|
+
snapshotAt: payloadArg.snapshotAt,
|
|
29
|
+
receivedAt: payloadArg.receivedAt,
|
|
30
|
+
deliveryId: payloadArg.deliveryId,
|
|
31
|
+
});
|
|
32
|
+
const canonicalEnvelope = (envelopeArg) => JSON.stringify({
|
|
33
|
+
version: envelopeArg.version,
|
|
34
|
+
keyId: envelopeArg.keyId,
|
|
35
|
+
keyVersion: envelopeArg.keyVersion,
|
|
36
|
+
payload: envelopeArg.payload,
|
|
37
|
+
signature: envelopeArg.signature,
|
|
38
|
+
});
|
|
39
|
+
const requireAuthority = (authorityArg) => ({
|
|
40
|
+
tenantId: requireCoreMailIdentifier(authorityArg.tenantId, 'cursor.tenantId'),
|
|
41
|
+
serviceId: requireCoreMailIdentifier(authorityArg.serviceId, 'cursor.serviceId'),
|
|
42
|
+
bindingId: requireCoreMailIdentifier(authorityArg.bindingId, 'cursor.bindingId'),
|
|
43
|
+
bindingRevision: requireCoreMailSafeInteger(authorityArg.bindingRevision, 'cursor.bindingRevision', 1),
|
|
44
|
+
});
|
|
45
|
+
const parseCanonicalRecord = (encodedArg, keysArg) => {
|
|
46
|
+
let decoded;
|
|
47
|
+
let parsed;
|
|
48
|
+
try {
|
|
49
|
+
decoded = Buffer.from(encodedArg, 'base64url');
|
|
50
|
+
if (decoded.toString('base64url') !== encodedArg) {
|
|
51
|
+
throw new Error('noncanonical encoding');
|
|
52
|
+
}
|
|
53
|
+
parsed = JSON.parse(new TextDecoder('utf-8', { fatal: true }).decode(decoded));
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
throw new Error('CoreMail inbound cursor is invalid.');
|
|
57
|
+
}
|
|
58
|
+
if (!parsed
|
|
59
|
+
|| typeof parsed !== 'object'
|
|
60
|
+
|| Array.isArray(parsed)
|
|
61
|
+
|| Object.getPrototypeOf(parsed) !== Object.prototype
|
|
62
|
+
|| Object.keys(parsed).length !== keysArg.size
|
|
63
|
+
|| Object.keys(parsed).some((keyArg) => !keysArg.has(keyArg))) {
|
|
64
|
+
throw new Error('CoreMail inbound cursor is invalid.');
|
|
65
|
+
}
|
|
66
|
+
return parsed;
|
|
67
|
+
};
|
|
68
|
+
const parsePayload = (encodedArg) => {
|
|
69
|
+
const value = parseCanonicalRecord(encodedArg, payloadKeys);
|
|
70
|
+
if (value.version !== 1) {
|
|
71
|
+
throw new Error('CoreMail inbound cursor version is unsupported.');
|
|
72
|
+
}
|
|
73
|
+
const payload = {
|
|
74
|
+
version: 1,
|
|
75
|
+
tenantId: requireCoreMailIdentifier(value.tenantId, 'cursor.tenantId'),
|
|
76
|
+
serviceId: requireCoreMailIdentifier(value.serviceId, 'cursor.serviceId'),
|
|
77
|
+
bindingId: requireCoreMailIdentifier(value.bindingId, 'cursor.bindingId'),
|
|
78
|
+
bindingRevision: requireCoreMailSafeInteger(value.bindingRevision, 'cursor.bindingRevision', 1),
|
|
79
|
+
snapshotAt: requireCoreMailSafeInteger(value.snapshotAt, 'cursor.snapshotAt'),
|
|
80
|
+
receivedAt: requireCoreMailSafeInteger(value.receivedAt, 'cursor.receivedAt'),
|
|
81
|
+
deliveryId: requireCoreMailUuid(value.deliveryId, 'cursor.deliveryId'),
|
|
82
|
+
};
|
|
83
|
+
if (canonicalPayload(payload) !== Buffer.from(encodedArg, 'base64url').toString('utf8')) {
|
|
84
|
+
throw new Error('CoreMail inbound cursor is not canonical.');
|
|
85
|
+
}
|
|
86
|
+
return payload;
|
|
87
|
+
};
|
|
88
|
+
const deriveHmacKey = (secretArg) => {
|
|
89
|
+
if (typeof secretArg !== 'string'
|
|
90
|
+
|| Buffer.byteLength(secretArg, 'utf8') < 32
|
|
91
|
+
|| Buffer.byteLength(secretArg, 'utf8') > 512
|
|
92
|
+
|| secretArg.trim() !== secretArg) {
|
|
93
|
+
throw new Error('CoreMail cursor secret is invalid.');
|
|
94
|
+
}
|
|
95
|
+
return plugins.nodeCrypto.createHash('sha256').update(secretArg, 'utf8').digest();
|
|
96
|
+
};
|
|
97
|
+
const signatureInput = (keyIdArg, keyVersionArg, payloadArg) => `${keyIdArg}:${keyVersionArg}:${payloadArg}`;
|
|
98
|
+
const sign = (keyArg, keyIdArg, keyVersionArg, payloadArg) => plugins.nodeCrypto
|
|
99
|
+
.createHmac('sha256', keyArg)
|
|
100
|
+
.update(signatureInput(keyIdArg, keyVersionArg, payloadArg), 'ascii')
|
|
101
|
+
.digest('base64url');
|
|
102
|
+
export class CoreMailInboundCursorCodec {
|
|
103
|
+
resolveRuntimeSecret;
|
|
104
|
+
constructor(resolveRuntimeSecret) {
|
|
105
|
+
this.resolveRuntimeSecret = resolveRuntimeSecret;
|
|
106
|
+
}
|
|
107
|
+
encode(authorityArg, positionArg, keysArg) {
|
|
108
|
+
const currentKey = keysArg.find((keyArg) => keyArg.state === 'current');
|
|
109
|
+
if (!currentKey) {
|
|
110
|
+
throw new Error('CoreMail current cursor key is unavailable.');
|
|
111
|
+
}
|
|
112
|
+
const authority = requireAuthority(authorityArg);
|
|
113
|
+
const payload = {
|
|
114
|
+
version: 1,
|
|
115
|
+
...authority,
|
|
116
|
+
snapshotAt: requireCoreMailSafeInteger(positionArg.snapshotAt, 'cursor.snapshotAt'),
|
|
117
|
+
receivedAt: requireCoreMailSafeInteger(positionArg.receivedAt, 'cursor.receivedAt'),
|
|
118
|
+
deliveryId: requireCoreMailUuid(positionArg.deliveryId, 'cursor.deliveryId'),
|
|
119
|
+
};
|
|
120
|
+
if (payload.receivedAt >= payload.snapshotAt) {
|
|
121
|
+
throw new Error('CoreMail inbound cursor position is invalid.');
|
|
122
|
+
}
|
|
123
|
+
const encodedPayload = Buffer.from(canonicalPayload(payload), 'utf8').toString('base64url');
|
|
124
|
+
const envelope = {
|
|
125
|
+
version: 1,
|
|
126
|
+
keyId: currentKey.keyId,
|
|
127
|
+
keyVersion: currentKey.version,
|
|
128
|
+
payload: encodedPayload,
|
|
129
|
+
signature: sign(deriveHmacKey(this.resolveRuntimeSecret(currentKey.secretKey)), currentKey.keyId, currentKey.version, encodedPayload),
|
|
130
|
+
};
|
|
131
|
+
return plugins.serveZoneInterfaces.data.normalizeCoreMailInboundCursor(Buffer.from(canonicalEnvelope(envelope), 'utf8').toString('base64url'));
|
|
132
|
+
}
|
|
133
|
+
decode(cursorArg, authorityArg, nowArg, keysArg) {
|
|
134
|
+
const cursor = plugins.serveZoneInterfaces.data.normalizeCoreMailInboundCursor(cursorArg);
|
|
135
|
+
const value = parseCanonicalRecord(cursor, envelopeKeys);
|
|
136
|
+
if (value.version !== 1) {
|
|
137
|
+
throw new Error('CoreMail inbound cursor version is unsupported.');
|
|
138
|
+
}
|
|
139
|
+
const now = requireCoreMailSafeInteger(nowArg, 'cursor.now');
|
|
140
|
+
const keyId = requireCoreMailIdentifier(value.keyId, 'cursor.keyId');
|
|
141
|
+
const keyVersion = requireCoreMailSafeInteger(value.keyVersion, 'cursor.keyVersion', 1);
|
|
142
|
+
if (typeof value.payload !== 'string' || typeof value.signature !== 'string') {
|
|
143
|
+
throw new Error('CoreMail inbound cursor is invalid.');
|
|
144
|
+
}
|
|
145
|
+
const envelope = {
|
|
146
|
+
version: 1,
|
|
147
|
+
keyId,
|
|
148
|
+
keyVersion,
|
|
149
|
+
payload: value.payload,
|
|
150
|
+
signature: value.signature,
|
|
151
|
+
};
|
|
152
|
+
if (canonicalEnvelope(envelope) !== Buffer.from(cursor, 'base64url').toString('utf8')) {
|
|
153
|
+
throw new Error('CoreMail inbound cursor is not canonical.');
|
|
154
|
+
}
|
|
155
|
+
const key = keysArg.find((keyArg) => keyArg.keyId === keyId
|
|
156
|
+
&& keyArg.version === keyVersion
|
|
157
|
+
&& (keyArg.state === 'current'
|
|
158
|
+
|| (keyArg.state === 'retiring'
|
|
159
|
+
&& keyArg.acceptUntil !== undefined
|
|
160
|
+
&& keyArg.acceptUntil >= now)));
|
|
161
|
+
if (!key) {
|
|
162
|
+
throw new Error('CoreMail inbound cursor key is unavailable.');
|
|
163
|
+
}
|
|
164
|
+
const actualSignature = Buffer.from(envelope.signature, 'base64url');
|
|
165
|
+
const expectedSignature = Buffer.from(sign(deriveHmacKey(this.resolveRuntimeSecret(key.secretKey)), key.keyId, key.version, envelope.payload), 'base64url');
|
|
166
|
+
if (actualSignature.toString('base64url') !== envelope.signature
|
|
167
|
+
|| actualSignature.byteLength !== expectedSignature.byteLength
|
|
168
|
+
|| !plugins.nodeCrypto.timingSafeEqual(actualSignature, expectedSignature)) {
|
|
169
|
+
throw new Error('CoreMail inbound cursor is invalid.');
|
|
170
|
+
}
|
|
171
|
+
const payload = parsePayload(envelope.payload);
|
|
172
|
+
const authority = requireAuthority(authorityArg);
|
|
173
|
+
if (payload.tenantId !== authority.tenantId
|
|
174
|
+
|| payload.serviceId !== authority.serviceId
|
|
175
|
+
|| payload.bindingId !== authority.bindingId
|
|
176
|
+
|| payload.bindingRevision !== authority.bindingRevision) {
|
|
177
|
+
throw new Error('CoreMail inbound cursor authority is invalid.');
|
|
178
|
+
}
|
|
179
|
+
if (payload.receivedAt >= payload.snapshotAt
|
|
180
|
+
|| payload.snapshotAt > now + maximumFutureSkewMs
|
|
181
|
+
|| now - payload.snapshotAt > maximumCursorAgeMs) {
|
|
182
|
+
throw new Error('CoreMail inbound cursor window is invalid.');
|
|
183
|
+
}
|
|
184
|
+
return {
|
|
185
|
+
snapshotAt: payload.snapshotAt,
|
|
186
|
+
receivedAt: payload.receivedAt,
|
|
187
|
+
deliveryId: payload.deliveryId,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29yZW1haWwuY3Vyc29yLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvY29yZW1haWwuY3Vyc29yLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sY0FBYyxDQUFDO0FBQ3hDLE9BQU8sRUFDTCx5QkFBeUIsRUFDekIsMEJBQTBCLEVBQzFCLG1CQUFtQixHQUNwQixNQUFNLHlCQUF5QixDQUFDO0FBOEJqQyxNQUFNLGtCQUFrQixHQUFHLEVBQUUsR0FBRyxFQUFFLEdBQUcsRUFBRSxHQUFHLEtBQUssQ0FBQztBQUNoRCxNQUFNLG1CQUFtQixHQUFHLEtBQUssQ0FBQztBQUNsQyxNQUFNLFdBQVcsR0FBRyxJQUFJLEdBQUcsQ0FBQztJQUMxQixTQUFTO0lBQ1QsVUFBVTtJQUNWLFdBQVc7SUFDWCxXQUFXO0lBQ1gsaUJBQWlCO0lBQ2pCLFlBQVk7SUFDWixZQUFZO0lBQ1osWUFBWTtDQUNiLENBQUMsQ0FBQztBQUNILE1BQU0sWUFBWSxHQUFHLElBQUksR0FBRyxDQUFDO0lBQzNCLFNBQVM7SUFDVCxPQUFPO0lBQ1AsWUFBWTtJQUNaLFNBQVM7SUFDVCxXQUFXO0NBQ1osQ0FBQyxDQUFDO0FBRUgsTUFBTSxnQkFBZ0IsR0FBRyxDQUN2QixVQUF5QyxFQUNqQyxFQUFFLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQztJQUMxQixPQUFPLEVBQUUsVUFBVSxDQUFDLE9BQU87SUFDM0IsUUFBUSxFQUFFLFVBQVUsQ0FBQyxRQUFRO0lBQzdCLFNBQVMsRUFBRSxVQUFVLENBQUMsU0FBUztJQUMvQixTQUFTLEVBQUUsVUFBVSxDQUFDLFNBQVM7SUFDL0IsZUFBZSxFQUFFLFVBQVUsQ0FBQyxlQUFlO0lBQzNDLFVBQVUsRUFBRSxVQUFVLENBQUMsVUFBVTtJQUNqQyxVQUFVLEVBQUUsVUFBVSxDQUFDLFVBQVU7SUFDakMsVUFBVSxFQUFFLFVBQVUsQ0FBQyxVQUFVO0NBQ2xDLENBQUMsQ0FBQztBQUVILE1B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
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
export const coreMailLogger = plugins.smartlog.Smartlog.createForCommitinfo({
|
|
3
|
+
name: '@serve.zone/coremail',
|
|
4
|
+
version: '1.0.0',
|
|
5
|
+
description: 'Authenticated mail persistence and delivery orchestration for serve.zone workloads.',
|
|
6
|
+
});
|
|
7
|
+
coreMailLogger.enableConsole();
|
|
8
|
+
export const logCoreMailFailure = async (componentArg, codeArg) => {
|
|
9
|
+
await coreMailLogger.log('warn', 'CoreMail background operation failed.', { component: componentArg, code: codeArg });
|
|
10
|
+
};
|
|
11
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29yZW1haWwubG9nLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvY29yZW1haWwubG9nLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sY0FBYyxDQUFDO0FBRXhDLE1BQU0sQ0FBQyxNQUFNLGNBQWMsR0FBRyxPQUFPLENBQUMsUUFBUSxDQUFDLFFBQVEsQ0FBQyxtQkFBbUIsQ0FBQztJQUMxRSxJQUFJLEVBQUUsc0JBQXNCO0lBQzVCLE9BQU8sRUFBRSxPQUFPO0lBQ2hCLFdBQVcsRUFDVCxxRkFBcUY7Q0FDeEYsQ0FBQyxDQUFDO0FBRUgsY0FBYyxDQUFDLGFBQWEsRUFBRSxDQUFDO0FBRS9CLE1BQU0sQ0FBQyxNQUFNLGtCQUFrQixHQUFHLEtBQUssRUFDckMsWUFBK0UsRUFDL0UsT0FBZSxFQUNBLEVBQUU7SUFDakIsTUFBTSxjQUFjLENBQUMsR0FBRyxDQUN0QixNQUFNLEVBQ04sdUNBQXVDLEVBQ3ZDLEVBQUUsU0FBUyxFQUFFLFlBQVksRUFBRSxJQUFJLEVBQUUsT0FBTyxFQUFFLENBQzNDLENBQUM7QUFDSixDQUFDLENBQUMifQ==
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
import type { ISubmissionPartRecord } from './interfaces.js';
|
|
3
|
+
type TMessage = plugins.serveZoneInterfaces.data.ICoreMailOutboundMessageDescriptor;
|
|
4
|
+
export declare const createCoreMailMime: (submissionIdArg: string, submissionDigestArg: plugins.serveZoneInterfaces.data.TCoreMailSha256, createdAtArg: number, messageArg: TMessage, partsArg: Array<{
|
|
5
|
+
descriptor: ISubmissionPartRecord;
|
|
6
|
+
bytes: Uint8Array;
|
|
7
|
+
}>) => Uint8Array;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
const encodeHeaderText = (valueArg) => /^[\x20-\x7e]*$/.test(valueArg)
|
|
3
|
+
? valueArg
|
|
4
|
+
: `=?UTF-8?B?${Buffer.from(valueArg, 'utf8').toString('base64')}?=`;
|
|
5
|
+
const formatMailbox = (mailboxArg) => {
|
|
6
|
+
if (!mailboxArg.displayName) {
|
|
7
|
+
return `<${mailboxArg.address}>`;
|
|
8
|
+
}
|
|
9
|
+
const displayName = encodeHeaderText(mailboxArg.displayName);
|
|
10
|
+
const rendered = /^[A-Za-z0-9 ._-]+$/.test(displayName)
|
|
11
|
+
? displayName
|
|
12
|
+
: `"${displayName.replaceAll('\\', '\\\\').replaceAll('"', '\\"')}"`;
|
|
13
|
+
return `${rendered} <${mailboxArg.address}>`;
|
|
14
|
+
};
|
|
15
|
+
const foldBase64 = (bytesArg) => Buffer.from(bytesArg)
|
|
16
|
+
.toString('base64')
|
|
17
|
+
.match(/.{1,76}/g)
|
|
18
|
+
?.join('\r\n') ?? '';
|
|
19
|
+
const contentDisposition = (partArg) => {
|
|
20
|
+
if (partArg.kind !== 'attachment') {
|
|
21
|
+
return 'inline';
|
|
22
|
+
}
|
|
23
|
+
if (!partArg.filename) {
|
|
24
|
+
return 'attachment';
|
|
25
|
+
}
|
|
26
|
+
const fallback = partArg.filename
|
|
27
|
+
.replace(/[^\x20-\x7e]/g, '_')
|
|
28
|
+
.replaceAll('\\', '\\\\')
|
|
29
|
+
.replaceAll('"', '\\"');
|
|
30
|
+
return `attachment; filename="${fallback}"; filename*=UTF-8''${encodeURIComponent(partArg.filename)}`;
|
|
31
|
+
};
|
|
32
|
+
export const createCoreMailMime = (submissionIdArg, submissionDigestArg, createdAtArg, messageArg, partsArg) => {
|
|
33
|
+
const boundary = `coremail_${submissionDigestArg.slice('sha256:'.length, 48)}`;
|
|
34
|
+
const to = messageArg.recipients.to.map(formatMailbox).join(', ');
|
|
35
|
+
const cc = messageArg.recipients.cc?.map(formatMailbox).join(', ');
|
|
36
|
+
const headers = [
|
|
37
|
+
`Date: ${new Date(createdAtArg).toUTCString()}`,
|
|
38
|
+
`Message-ID: <${submissionIdArg}@coremail.serve.zone>`,
|
|
39
|
+
`From: ${formatMailbox(messageArg.sender)}`,
|
|
40
|
+
`To: ${to}`,
|
|
41
|
+
...(cc ? [`Cc: ${cc}`] : []),
|
|
42
|
+
...(messageArg.replyTo ? [`Reply-To: ${formatMailbox(messageArg.replyTo)}`] : []),
|
|
43
|
+
`Subject: ${encodeHeaderText(messageArg.subject)}`,
|
|
44
|
+
...(messageArg.headers ?? []).map((headerArg) => `${headerArg.name}: ${encodeHeaderText(headerArg.value)}`),
|
|
45
|
+
'MIME-Version: 1.0',
|
|
46
|
+
`Content-Type: multipart/mixed; boundary="${boundary}"`,
|
|
47
|
+
'',
|
|
48
|
+
'This is a multipart message in MIME format.',
|
|
49
|
+
];
|
|
50
|
+
for (const part of partsArg) {
|
|
51
|
+
headers.push(`--${boundary}`, `Content-Type: ${part.descriptor.contentType}`, 'Content-Transfer-Encoding: base64', `Content-Disposition: ${contentDisposition(part.descriptor)}`, ...(part.descriptor.contentId
|
|
52
|
+
? [`Content-ID: <${part.descriptor.contentId}>`]
|
|
53
|
+
: []), '', foldBase64(part.bytes));
|
|
54
|
+
}
|
|
55
|
+
headers.push(`--${boundary}--`, '');
|
|
56
|
+
const encoded = new TextEncoder().encode(headers.join('\r\n'));
|
|
57
|
+
if (encoded.byteLength >
|
|
58
|
+
plugins.serveZoneInterfaces.data.coreMailLimits.serializedMimeBytes) {
|
|
59
|
+
throw new Error('CoreMail serialized MIME exceeds its byte budget.');
|
|
60
|
+
}
|
|
61
|
+
return encoded;
|
|
62
|
+
};
|
|
63
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29yZW1haWwubWltZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL2NvcmVtYWlsLm1pbWUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSxjQUFjLENBQUM7QUFLeEMsTUFBTSxnQkFBZ0IsR0FBRyxDQUFDLFFBQWdCLEVBQVUsRUFBRSxDQUNwRCxnQkFBZ0IsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDO0lBQzdCLENBQUMsQ0FBQyxRQUFRO0lBQ1YsQ0FBQyxDQUFDLGFBQWEsTUFBTSxDQUFDLElBQUksQ0FBQyxRQUFRLEVBQUUsTUFBTSxDQUFDLENBQUMsUUFBUSxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUM7QUFFeEUsTUFBTSxhQUFhLEdBQUcsQ0FDcEIsVUFBNkQsRUFDckQsRUFBRTtJQUNWLElBQUksQ0FBQyxVQUFVLENBQUMsV0FBVyxFQUFFLENBQUM7UUFDNUIsT0FBTyxJQUFJLFVBQVUsQ0FBQyxPQUFPLEdBQUcsQ0FBQztJQUNuQyxDQUFDO0lBQ0QsTUFBTSxXQUFXLEdBQUcsZ0JBQWdCLENBQUMsVUFBVSxDQUFDLFdBQVcsQ0FBQyxDQUFDO0lBQzdELE1BQU0sUUFBUSxHQUFHLG9CQUFvQixDQUFDLElBQUksQ0FBQyxXQUFXLENBQUM7UUFDckQsQ0FBQyxDQUFDLFdBQVc7UUFDYixDQUFDLENBQUMsSUFBSSxXQUFXLENBQUMsVUFBVSxDQUFDLElBQUksRUFBRSxNQUFNLENBQUMsQ0FBQyxVQUFVLENBQUMsR0FBRyxFQUFFLEtBQUssQ0FBQyxHQUFHLENBQUM7SUFDdkUsT0FBTyxHQUFHLFFBQVEsS0FBSyxVQUFVLENBQUMsT0FBTyxHQUFHLENBQUM7QUFDL0MsQ0FBQyxDQUFDO0FBRUYsTUFBTSxVQUFVLEdBQUcsQ0FBQyxRQUFvQixFQUFVLEVBQUUsQ0FDbEQsTUFBTSxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUM7S0FDbEIsUUFBUSxDQUFDLFFBQVEsQ0FBQztLQUNsQixLQUFL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
|