@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.
Files changed (75) hide show
  1. package/.smartconfig.json +50 -0
  2. package/changelog.md +10 -0
  3. package/cli.js +4 -0
  4. package/dist_ts/00_commitinfo_data.d.ts +8 -0
  5. package/dist_ts/00_commitinfo_data.js +9 -0
  6. package/dist_ts/classes.auth.d.ts +45 -0
  7. package/dist_ts/classes.auth.js +204 -0
  8. package/dist_ts/classes.config.d.ts +2 -0
  9. package/dist_ts/classes.config.js +72 -0
  10. package/dist_ts/classes.coremail.d.ts +41 -0
  11. package/dist_ts/classes.coremail.js +155 -0
  12. package/dist_ts/classes.desiredstate.d.ts +40 -0
  13. package/dist_ts/classes.desiredstate.js +382 -0
  14. package/dist_ts/classes.gateway.d.ts +62 -0
  15. package/dist_ts/classes.gateway.js +669 -0
  16. package/dist_ts/classes.inbound.d.ts +49 -0
  17. package/dist_ts/classes.inbound.js +702 -0
  18. package/dist_ts/classes.maintenance.d.ts +16 -0
  19. package/dist_ts/classes.maintenance.js +339 -0
  20. package/dist_ts/classes.models.d.ts +161 -0
  21. package/dist_ts/classes.models.js +595 -0
  22. package/dist_ts/classes.server.d.ts +40 -0
  23. package/dist_ts/classes.server.js +309 -0
  24. package/dist_ts/classes.storage.d.ts +32 -0
  25. package/dist_ts/classes.storage.js +317 -0
  26. package/dist_ts/classes.submissions.d.ts +33 -0
  27. package/dist_ts/classes.submissions.js +385 -0
  28. package/dist_ts/classes.transfer.d.ts +41 -0
  29. package/dist_ts/classes.transfer.js +289 -0
  30. package/dist_ts/coremail.cursor.d.ts +20 -0
  31. package/dist_ts/coremail.cursor.js +191 -0
  32. package/dist_ts/coremail.log.d.ts +3 -0
  33. package/dist_ts/coremail.log.js +11 -0
  34. package/dist_ts/coremail.mime.d.ts +8 -0
  35. package/dist_ts/coremail.mime.js +63 -0
  36. package/dist_ts/coremail.persistence.d.ts +148 -0
  37. package/dist_ts/coremail.persistence.js +695 -0
  38. package/dist_ts/coremail.quota.d.ts +29 -0
  39. package/dist_ts/coremail.quota.js +149 -0
  40. package/dist_ts/coremail.selectors.d.ts +3 -0
  41. package/dist_ts/coremail.selectors.js +21 -0
  42. package/dist_ts/coremail.validation.d.ts +9 -0
  43. package/dist_ts/coremail.validation.js +325 -0
  44. package/dist_ts/index.d.ts +4 -0
  45. package/dist_ts/index.js +39 -0
  46. package/dist_ts/interfaces.d.ts +92 -0
  47. package/dist_ts/interfaces.js +2 -0
  48. package/dist_ts/plugins.d.ts +11 -0
  49. package/dist_ts/plugins.js +12 -0
  50. package/license.md +19 -0
  51. package/package.json +48 -0
  52. package/readme.md +199 -0
  53. package/ts/00_commitinfo_data.ts +8 -0
  54. package/ts/classes.auth.ts +321 -0
  55. package/ts/classes.config.ts +104 -0
  56. package/ts/classes.coremail.ts +249 -0
  57. package/ts/classes.desiredstate.ts +508 -0
  58. package/ts/classes.gateway.ts +811 -0
  59. package/ts/classes.inbound.ts +898 -0
  60. package/ts/classes.maintenance.ts +335 -0
  61. package/ts/classes.models.ts +530 -0
  62. package/ts/classes.server.ts +444 -0
  63. package/ts/classes.storage.ts +448 -0
  64. package/ts/classes.submissions.ts +554 -0
  65. package/ts/classes.transfer.ts +379 -0
  66. package/ts/coremail.cursor.ts +328 -0
  67. package/ts/coremail.log.ts +21 -0
  68. package/ts/coremail.mime.ts +94 -0
  69. package/ts/coremail.persistence.ts +1156 -0
  70. package/ts/coremail.quota.ts +214 -0
  71. package/ts/coremail.selectors.ts +35 -0
  72. package/ts/coremail.validation.ts +446 -0
  73. package/ts/index.ts +38 -0
  74. package/ts/interfaces.ts +109 -0
  75. 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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29yZW1haWwuY3Vyc29yLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvY29yZW1haWwuY3Vyc29yLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sY0FBYyxDQUFDO0FBQ3hDLE9BQU8sRUFDTCx5QkFBeUIsRUFDekIsMEJBQTBCLEVBQzFCLG1CQUFtQixHQUNwQixNQUFNLHlCQUF5QixDQUFDO0FBOEJqQyxNQUFNLGtCQUFrQixHQUFHLEVBQUUsR0FBRyxFQUFFLEdBQUcsRUFBRSxHQUFHLEtBQUssQ0FBQztBQUNoRCxNQUFNLG1CQUFtQixHQUFHLEtBQUssQ0FBQztBQUNsQyxNQUFNLFdBQVcsR0FBRyxJQUFJLEdBQUcsQ0FBQztJQUMxQixTQUFTO0lBQ1QsVUFBVTtJQUNWLFdBQVc7SUFDWCxXQUFXO0lBQ1gsaUJBQWlCO0lBQ2pCLFlBQVk7SUFDWixZQUFZO0lBQ1osWUFBWTtDQUNiLENBQUMsQ0FBQztBQUNILE1BQU0sWUFBWSxHQUFHLElBQUksR0FBRyxDQUFDO0lBQzNCLFNBQVM7SUFDVCxPQUFPO0lBQ1AsWUFBWTtJQUNaLFNBQVM7SUFDVCxXQUFXO0NBQ1osQ0FBQyxDQUFDO0FBRUgsTUFBTSxnQkFBZ0IsR0FBRyxDQUN2QixVQUF5QyxFQUNqQyxFQUFFLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQztJQUMxQixPQUFPLEVBQUUsVUFBVSxDQUFDLE9BQU87SUFDM0IsUUFBUSxFQUFFLFVBQVUsQ0FBQyxRQUFRO0lBQzdCLFNBQVMsRUFBRSxVQUFVLENBQUMsU0FBUztJQUMvQixTQUFTLEVBQUUsVUFBVSxDQUFDLFNBQVM7SUFDL0IsZUFBZSxFQUFFLFVBQVUsQ0FBQyxlQUFlO0lBQzNDLFVBQVUsRUFBRSxVQUFVLENBQUMsVUFBVTtJQUNqQyxVQUFVLEVBQUUsVUFBVSxDQUFDLFVBQVU7SUFDakMsVUFBVSxFQUFFLFVBQVUsQ0FBQyxVQUFVO0NBQ2xDLENBQUMsQ0FBQztBQUVILE1BQU0saUJBQWlCLEdBQUcsQ0FDeEIsV0FBMkMsRUFDbkMsRUFBRSxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUM7SUFDMUIsT0FBTyxFQUFFLFdBQVcsQ0FBQyxPQUFPO0lBQzVCLEtBQUssRUFBRSxXQUFXLENBQUMsS0FBSztJQUN4QixVQUFVLEVBQUUsV0FBVyxDQUFDLFVBQVU7SUFDbEMsT0FBTyxFQUFFLFdBQVcsQ0FBQyxPQUFPO0lBQzVCLFNBQVMsRUFBRSxXQUFXLENBQUMsU0FBUztDQUNqQyxDQUFDLENBQUM7QUFFSCxNQUFNLGdCQUFnQixHQUFHLENBQ3ZCLFlBQTZDLEVBQ1osRUFBRSxDQUFDLENBQUM7SUFDckMsUUFBUSxFQUFFLHlCQUF5QixDQUNqQyxZQUFZLENBQUMsUUFBUSxFQUNyQixpQkFBaUIsQ0FDbEI7SUFDRCxTQUFTLEVBQUUseUJBQXlCLENBQ2xDLFlBQVksQ0FBQyxTQUFTLEVBQ3RCLGtCQUFrQixDQUNuQjtJQUNELFNBQVMsRUFBRSx5QkFBeUIsQ0FDbEMsWUFBWSxDQUFDLFNBQVMsRUFDdEIsa0JBQWtCLENBQ25CO0lBQ0QsZUFBZSxFQUFFLDBCQUEwQixDQUN6QyxZQUFZLENBQUMsZUFBZSxFQUM1Qix3QkFBd0IsRUFDeEIsQ0FBQyxDQUNGO0NBQ0YsQ0FBQyxDQUFDO0FBRUgsTUFBTSxvQkFBb0IsR0FBRyxDQUMzQixVQUFrQixFQUNsQixPQUFvQixFQUNLLEVBQUU7SUFDM0IsSUFBSSxPQUFlLENBQUM7SUFDcEIsSUFBSSxNQUFlLENBQUM7SUFDcEIsSUFBSSxDQUFDO1FBQ0gsT0FBTyxHQUFHLE1BQU0sQ0FBQyxJQUFJLENBQUMsVUFBVSxFQUFFLFdBQVcsQ0FBQyxDQUFDO1FBQy9DLElBQUksT0FBTyxDQUFDLFFBQVEsQ0FBQyxXQUFXLENBQUMsS0FBSyxVQUFVLEVBQUUsQ0FBQztZQUNqRCxNQUFNLElBQUksS0FBSyxDQUFDLHVCQUF1QixDQUFDLENBQUM7UUFDM0MsQ0FBQztRQUNELE1BQU0sR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksV0FBVyxDQUFDLE9BQU8sRUFBRSxFQUFFLEtBQUssRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDO0lBQ2pGLENBQUM7SUFBQyxNQUFNLENBQUM7UUFDUCxNQUFNLElBQUksS0FBSyxDQUFDLHFDQUFxQyxDQUFDLENBQUM7SUFDekQsQ0FBQztJQUNELElBQ0UsQ0FBQyxNQUFNO1dBQ0osT0FBTyxNQUFNLEtBQUssUUFBUTtXQUMxQixLQUFLLENBQUMsT0FBTyxDQUFDLE1BQU0sQ0FBQztXQUNyQixNQUFNLENBQUMsY0FBYyxDQUFDLE1BQU0sQ0FBQyxLQUFLLE1BQU0sQ0FBQyxTQUFTO1dBQ2xELE1BQU0sQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUMsTUFBTSxLQUFLLE9BQU8sQ0FBQyxJQUFJO1dBQzNDLE1BQU0sQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsTUFBTSxFQUFFLEVBQUUsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsTUFBTSxDQUFDLENBQUMsRUFDN0QsQ0FBQztRQUNELE1BQU0sSUFBSSxLQUFLLENBQUMscUNBQXFDLENBQUMsQ0FBQztJQUN6RCxDQUFDO0lBQ0QsT0FBTyxNQUFpQyxDQUFDO0FBQzNDLENBQUMsQ0FBQztBQUVGLE1BQU0sWUFBWSxHQUFHLENBQUMsVUFBa0IsRUFBaUMsRUFBRTtJQUN6RSxNQUFNLEtBQUssR0FBRyxvQkFBb0IsQ0FBQyxVQUFVLEVBQUUsV0FBVyxDQUFDLENBQUM7SUFDNUQsSUFBSSxLQUFLLENBQUMsT0FBTyxLQUFLLENBQUMsRUFBRSxDQUFDO1FBQ3hCLE1BQU0sSUFBSSxLQUFLLENBQUMsaURBQWlELENBQUMsQ0FBQztJQUNyRSxDQUFDO0lBQ0QsTUFBTSxPQUFPLEdBQWtDO1FBQzdDLE9BQU8sRUFBRSxDQUFDO1FBQ1YsUUFBUSxFQUFFLHlCQUF5QixDQUFDLEtBQUssQ0FBQyxRQUFRLEVBQUUsaUJBQWlCLENBQUM7UUFDdEUsU0FBUyxFQUFFLHlCQUF5QixDQUFDLEtBQUssQ0FBQyxTQUFTLEVBQUUsa0JBQWtCLENBQUM7UUFDekUsU0FBUyxFQUFFLHlCQUF5QixDQUFDLEtBQUssQ0FBQyxTQUFTLEVBQUUsa0JBQWtCLENBQUM7UUFDekUsZUFBZSxFQUFFLDBCQUEwQixDQUN6QyxLQUFLLENBQUMsZUFBZSxFQUNyQix3QkFBd0IsRUFDeEIsQ0FBQyxDQUNGO1FBQ0QsVUFBVSxFQUFFLDBCQUEwQixDQUNwQyxLQUFLLENBQUMsVUFBVSxFQUNoQixtQkFBbUIsQ0FDcEI7UUFDRCxVQUFVLEVBQUUsMEJBQTBCLENBQ3BDLEtBQUssQ0FBQyxVQUFVLEVBQ2hCLG1CQUFtQixDQUNwQjtRQUNELFVBQVUsRUFBRSxtQkFBbUIsQ0FBQyxLQUFLLENBQUMsVUFBVSxFQUFFLG1CQUFtQixDQUFDO0tBQ3ZFLENBQUM7SUFDRixJQUFJLGdCQUFnQixDQUFDLE9BQU8sQ0FBQyxLQUFLLE1BQU0sQ0FBQyxJQUFJLENBQUMsVUFBVSxFQUFFLFdBQVcsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxNQUFNLENBQUMsRUFBRSxDQUFDO1FBQ3hGLE1BQU0sSUFBSSxLQUFLLENBQUMsMkNBQTJDLENBQUMsQ0FBQztJQUMvRCxDQUFDO0lBQ0QsT0FBTyxPQUFPLENBQUM7QUFDakIsQ0FBQyxDQUFDO0FBRUYsTUFBTSxhQUFhLEdBQUcsQ0FBQyxTQUFpQixFQUFVLEVBQUU7SUFDbEQsSUFDRSxPQUFPLFNBQVMsS0FBSyxRQUFRO1dBQzFCLE1BQU0sQ0FBQyxVQUFVLENBQUMsU0FBUyxFQUFFLE1BQU0sQ0FBQyxHQUFHLEVBQUU7V0FDekMsTUFBTSxDQUFDLFVBQVUsQ0FBQyxTQUFTLEVBQUUsTUFBTSxDQUFDLEdBQUcsR0FBRztXQUMxQyxTQUFTLENBQUMsSUFBSSxFQUFFLEtBQUssU0FBUyxFQUNqQyxDQUFDO1FBQ0QsTUFBTSxJQUFJLEtBQUssQ0FBQyxvQ0FBb0MsQ0FBQyxDQUFDO0lBQ3hELENBQUM7SUFDRCxPQUFPLE9BQU8sQ0FBQyxVQUFVLENBQUMsVUFBVSxDQUFDLFFBQVEsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxTQUFTLEVBQUUsTUFBTSxDQUFDLENBQUMsTUFBTSxFQUFFLENBQUM7QUFDcEYsQ0FBQyxDQUFDO0FBRUYsTUFBTSxjQUFjLEdBQUcsQ0FDckIsUUFBZ0IsRUFDaEIsYUFBcUIsRUFDckIsVUFBa0IsRUFDVixFQUFFLENBQUMsR0FBRyxRQUFRLElBQUksYUFBYSxJQUFJLFVBQVUsRUFBRSxDQUFDO0FBRTFELE1BQU0sSUFBSSxHQUFHLENBQ1gsTUFBYyxFQUNkLFFBQWdCLEVBQ2hCLGFBQXFCLEVBQ3JCLFVBQWtCLEVBQ1YsRUFBRSxDQUFDLE9BQU8sQ0FBQyxVQUFVO0tBQzVCLFVBQVUsQ0FBQyxRQUFRLEVBQUUsTUFBTSxDQUFDO0tBQzVCLE1BQU0sQ0FBQyxjQUFjLENBQUMsUUFBUSxFQUFFLGFBQWEsRUFBRSxVQUFVLENBQUMsRUFBRSxPQUFPLENBQUM7S0FDcEUsTUFBTSxDQUFDLFdBQVcsQ0FBQyxDQUFDO0FBRXZCLE1BQU0sT0FBTywwQkFBMEI7SUFFbEI7SUFEbkIsWUFDbUIsb0JBQXNEO1FBQXRELHlCQUFvQixHQUFwQixvQkFBb0IsQ0FBa0M7SUFDdEUsQ0FBQztJQUVHLE1BQU0sQ0FDWCxZQUE2QyxFQUM3QyxXQUEyQyxFQUMzQyxPQUFxQjtRQUVyQixNQUFNLFVBQVUsR0FBRyxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUMsTUFBTSxFQUFFLEVBQUUsQ0FBQyxNQUFNLENBQUMsS0FBSyxLQUFLLFNBQVMsQ0FBQyxDQUFDO1FBQ3hFLElBQUksQ0FBQyxVQUFVLEVBQUUsQ0FBQztZQUNoQixNQUFNLElBQUksS0FBSyxDQUFDLDZDQUE2QyxDQUFDLENBQUM7UUFDakUsQ0FBQztRQUNELE1BQU0sU0FBUyxHQUFHLGdCQUFnQixDQUFDLFlBQVksQ0FBQyxDQUFDO1FBQ2pELE1BQU0sT0FBTyxHQUFrQztZQUM3QyxPQUFPLEVBQUUsQ0FBQztZQUNWLEdBQUcsU0FBUztZQUNaLFVBQVUsRUFBRSwwQkFBMEIsQ0FDcEMsV0FBVyxDQUFDLFVBQVUsRUFDdEIsbUJBQW1CLENBQ3BCO1lBQ0QsVUFBVSxFQUFFLDBCQUEwQixDQUNwQyxXQUFXLENBQUMsVUFBVSxFQUN0QixtQkFBbUIsQ0FDcEI7WUFDRCxVQUFVLEVBQUUsbUJBQW1CLENBQzdCLFdBQVcsQ0FBQyxVQUFVLEVBQ3RCLG1CQUFtQixDQUNwQjtTQUNGLENBQUM7UUFDRixJQUFJLE9BQU8sQ0FBQyxVQUFVLElBQUksT0FBTyxDQUFDLFVBQVUsRUFBRSxDQUFDO1lBQzdDLE1BQU0sSUFBSSxLQUFLLENBQUMsOENBQThDLENBQUMsQ0FBQztRQUNsRSxDQUFDO1FBQ0QsTUFBTSxjQUFjLEdBQUcsTUFBTSxDQUFDLElBQUksQ0FDaEMsZ0JBQWdCLENBQUMsT0FBTyxDQUFDLEVBQ3pCLE1BQU0sQ0FDUCxDQUFDLFFBQVEsQ0FBQyxXQUFXLENBQUMsQ0FBQztRQUN4QixNQUFNLFFBQVEsR0FBbUM7WUFDL0MsT0FBTyxFQUFFLENBQUM7WUFDVixLQUFLLEVBQUUsVUFBVSxDQUFDLEtBQUs7WUFDdkIsVUFBVSxFQUFFLFVBQVUsQ0FBQyxPQUFPO1lBQzlCLE9BQU8sRUFBRSxjQUFjO1lBQ3ZCLFNBQVMsRUFBRSxJQUFJLENBQ2IsYUFBYSxDQUFDLElBQUksQ0FBQyxvQkFBb0IsQ0FBQyxVQUFVLENBQUMsU0FBUyxDQUFDLENBQUMsRUFDOUQsVUFBVSxDQUFDLEtBQUssRUFDaEIsVUFBVSxDQUFDLE9BQU8sRUFDbEIsY0FBYyxDQUNmO1NBQ0YsQ0FBQztRQUNGLE9BQU8sT0FBTyxDQUFDLG1CQUFtQixDQUFDLElBQUksQ0FBQyw4QkFBOEIsQ0FDcEUsTUFBTSxDQUFDLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxRQUFRLENBQUMsRUFBRSxNQUFNLENBQUMsQ0FBQyxRQUFRLENBQUMsV0FBVyxDQUFDLENBQ3ZFLENBQUM7SUFDSixDQUFDO0lBRU0sTUFBTSxDQUNYLFNBQWlCLEVBQ2pCLFlBQTZDLEVBQzdDLE1BQWMsRUFDZCxPQUFxQjtRQUVyQixNQUFNLE1BQU0sR0FBRyxPQUFPLENBQUMsbUJBQW1CLENBQUMsSUFBSSxDQUFDLDhCQUE4QixDQUM1RSxTQUFTLENBQ1YsQ0FBQztRQUNGLE1BQU0sS0FBSyxHQUFHLG9CQUFvQixDQUFDLE1BQU0sRUFBRSxZQUFZLENBQUMsQ0FBQztRQUN6RCxJQUFJLEtBQUssQ0FBQyxPQUFPLEtBQUssQ0FBQyxFQUFFLENBQUM7WUFDeEIsTUFBTSxJQUFJLEtBQUssQ0FBQyxpREFBaUQsQ0FBQyxDQUFDO1FBQ3JFLENBQUM7UUFDRCxNQUFNLEdBQUcsR0FBRywwQkFBMEIsQ0FBQyxNQUFNLEVBQUUsWUFBWSxDQUFDLENBQUM7UUFDN0QsTUFBTSxLQUFLLEdBQUcseUJBQXlCLENBQUMsS0FBSyxDQUFDLEtBQUssRUFBRSxjQUFjLENBQUMsQ0FBQztRQUNyRSxNQUFNLFVBQVUsR0FBRywwQkFBMEIsQ0FDM0MsS0FBSyxDQUFDLFVBQVUsRUFDaEIsbUJBQW1CLEVBQ25CLENBQUMsQ0FDRixDQUFDO1FBQ0YsSUFBSSxPQUFPLEtBQUssQ0FBQyxPQUFPLEtBQUssUUFBUSxJQUFJLE9BQU8sS0FBSyxDQUFDLFNBQVMsS0FBSyxRQUFRLEVBQUUsQ0FBQztZQUM3RSxNQUFNLElBQUksS0FBSyxDQUFDLHFDQUFxQyxDQUFDLENBQUM7UUFDekQsQ0FBQztRQUNELE1BQU0sUUFBUSxHQUFtQztZQUMvQyxPQUFPLEVBQUUsQ0FBQztZQUNWLEtBQUs7WUFDTCxVQUFVO1lBQ1YsT0FBTyxFQUFFLEtBQUssQ0FBQyxPQUFPO1lBQ3RCLFNBQVMsRUFBRSxLQUFLLENBQUMsU0FBUztTQUMzQixDQUFDO1FBQ0YsSUFBSSxpQkFBaUIsQ0FBQyxRQUFRLENBQUMsS0FBSyxNQUFNLENBQUMsSUFBSSxDQUFDLE1BQU0sRUFBRSxXQUFXLENBQUMsQ0FBQyxRQUFRLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQztZQUN0RixNQUFNLElBQUksS0FBSyxDQUFDLDJDQUEyQyxDQUFDLENBQUM7UUFDL0QsQ0FBQztRQUNELE1BQU0sR0FBRyxHQUFHLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQyxNQUFNLEVBQUUsRUFBRSxDQUNsQyxNQUFNLENBQUMsS0FBSyxLQUFLLEtBQUs7ZUFDbkIsTUFBTSxDQUFDLE9BQU8sS0FBSyxVQUFVO2VBQzdCLENBQ0QsTUFBTSxDQUFDLEtBQUssS0FBSyxTQUFTO21CQUN2QixDQUNELE1BQU0sQ0FBQyxLQUFLLEtBQUssVUFBVTt1QkFDeEIsTUFBTSxDQUFDLFdBQVcsS0FBSyxTQUFTO3VCQUNoQyxNQUFNLENBQUMsV0FBVyxJQUFJLEdBQUcsQ0FDN0IsQ0FDRixDQUNGLENBQUM7UUFDRixJQUFJLENBQUMsR0FBRyxFQUFFLENBQUM7WUFDVCxNQUFNLElBQUksS0FBSyxDQUFDLDZDQUE2QyxDQUFDLENBQUM7UUFDakUsQ0FBQztRQUNELE1BQU0sZUFBZSxHQUFHLE1BQU0sQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLFNBQVMsRUFBRSxXQUFXLENBQUMsQ0FBQztRQUNyRSxNQUFNLGlCQUFpQixHQUFHLE1BQU0sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUN4QyxhQUFhLENBQUMsSUFBSSxDQUFDLG9CQUFvQixDQUFDLEdBQUcsQ0FBQyxTQUFTLENBQUMsQ0FBQyxFQUN2RCxHQUFHLENBQUMsS0FBSyxFQUNULEdBQUcsQ0FBQyxPQUFPLEVBQ1gsUUFBUSxDQUFDLE9BQU8sQ0FDakIsRUFBRSxXQUFXLENBQUMsQ0FBQztRQUNoQixJQUNFLGVBQWUsQ0FBQyxRQUFRLENBQUMsV0FBVyxDQUFDLEtBQUssUUFBUSxDQUFDLFNBQVM7ZUFDekQsZUFBZSxDQUFDLFVBQVUsS0FBSyxpQkFBaUIsQ0FBQyxVQUFVO2VBQzNELENBQUMsT0FBTyxDQUFDLFVBQVUsQ0FBQyxlQUFlLENBQUMsZUFBZSxFQUFFLGlCQUFpQixDQUFDLEVBQzFFLENBQUM7WUFDRCxNQUFNLElBQUksS0FBSyxDQUFDLHFDQUFxQyxDQUFDLENBQUM7UUFDekQsQ0FBQztRQUNELE1BQU0sT0FBTyxHQUFHLFlBQVksQ0FBQyxRQUFRLENBQUMsT0FBTyxDQUFDLENBQUM7UUFDL0MsTUFBTSxTQUFTLEdBQUcsZ0JBQWdCLENBQUMsWUFBWSxDQUFDLENBQUM7UUFDakQsSUFDRSxPQUFPLENBQUMsUUFBUSxLQUFLLFNBQVMsQ0FBQyxRQUFRO2VBQ3BDLE9BQU8sQ0FBQyxTQUFTLEtBQUssU0FBUyxDQUFDLFNBQVM7ZUFDekMsT0FBTyxDQUFDLFNBQVMsS0FBSyxTQUFTLENBQUMsU0FBUztlQUN6QyxPQUFPLENBQUMsZUFBZSxLQUFLLFNBQVMsQ0FBQyxlQUFlLEVBQ3hELENBQUM7WUFDRCxNQUFNLElBQUksS0FBSyxDQUFDLCtDQUErQyxDQUFDLENBQUM7UUFDbkUsQ0FBQztRQUNELElBQ0UsT0FBTyxDQUFDLFVBQVUsSUFBSSxPQUFPLENBQUMsVUFBVTtlQUNyQyxPQUFPLENBQUMsVUFBVSxHQUFHLEdBQUcsR0FBRyxtQkFBbUI7ZUFDOUMsR0FBRyxHQUFHLE9BQU8sQ0FBQyxVQUFVLEdBQUcsa0JBQWtCLEVBQ2hELENBQUM7WUFDRCxNQUFNLElBQUksS0FBSyxDQUFDLDRDQUE0QyxDQUFDLENBQUM7UUFDaEUsQ0FBQztRQUNELE9BQU87WUFDTCxVQUFVLEVBQUUsT0FBTyxDQUFDLFVBQVU7WUFDOUIsVUFBVSxFQUFFLE9BQU8sQ0FBQyxVQUFVO1lBQzlCLFVBQVUsRUFBRSxPQUFPLENBQUMsVUFBVTtTQUMvQixDQUFDO0lBQ0osQ0FBQztDQUNGIn0=
@@ -0,0 +1,3 @@
1
+ import * as plugins from './plugins.js';
2
+ export declare const coreMailLogger: plugins.smartlog.Smartlog;
3
+ export declare const logCoreMailFailure: (componentArg: "desired-state" | "gateway" | "retention" | "transfer" | "worker", codeArg: string) => Promise<void>;
@@ -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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