@volter/twin-postmark 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/README.md +144 -0
- package/client/postmark-mirror.css +79 -0
- package/client/postmark-mirror.tsx +221 -0
- package/dist/client/postmark-mirror.bundle.js +321 -0
- package/dist/client/postmark-mirror.css +79 -0
- package/dist/client/postmark-mirror.d.ts +18 -0
- package/dist/client/postmark-mirror.js +153 -0
- package/dist/client/postmark-mirror.tsx +221 -0
- package/dist/src/cli.d.ts +2 -0
- package/dist/src/cli.js +31 -0
- package/dist/src/index.d.ts +10 -0
- package/dist/src/index.js +54 -0
- package/dist/src/postmark-capabilities.d.ts +12 -0
- package/dist/src/postmark-capabilities.js +1502 -0
- package/dist/src/postmark-conformance.d.ts +33 -0
- package/dist/src/postmark-conformance.js +265 -0
- package/dist/src/postmark-connector.d.ts +167 -0
- package/dist/src/postmark-connector.js +251 -0
- package/dist/src/postmark-events.d.ts +85 -0
- package/dist/src/postmark-events.js +169 -0
- package/dist/src/postmark-mirror-ui.d.ts +58 -0
- package/dist/src/postmark-mirror-ui.js +207 -0
- package/dist/src/postmark-perform-harness.d.ts +9 -0
- package/dist/src/postmark-perform-harness.js +24 -0
- package/dist/src/postmark-server.d.ts +14 -0
- package/dist/src/postmark-server.js +29 -0
- package/dist/src/postmark-twin.d.ts +82 -0
- package/dist/src/postmark-twin.js +1575 -0
- package/dist/test-fixtures/postmark-swagger-operations.json +846 -0
- package/package.json +76 -0
- package/src/cli.ts +29 -0
- package/src/index.ts +89 -0
- package/src/postmark-capabilities.ts +1737 -0
- package/src/postmark-conformance.ts +282 -0
- package/src/postmark-connector.ts +312 -0
- package/src/postmark-events.ts +189 -0
- package/src/postmark-mirror-ui.ts +213 -0
- package/src/postmark-perform-harness.ts +21 -0
- package/src/postmark-server.ts +37 -0
- package/src/postmark-twin.ts +1520 -0
- package/test-fixtures/postmark-swagger-operations.json +846 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { specConformance } from '@volter/world-tooling';
|
|
2
|
+
type JsonSchema = specConformance.JsonSchema;
|
|
3
|
+
type KnownDeviation = specConformance.KnownDeviation;
|
|
4
|
+
type SpecViolation = specConformance.SpecViolation;
|
|
5
|
+
export type PostmarkViolation = SpecViolation & {
|
|
6
|
+
object: string;
|
|
7
|
+
id: string;
|
|
8
|
+
};
|
|
9
|
+
export type PostmarkConformanceReport = {
|
|
10
|
+
ok: boolean;
|
|
11
|
+
resourcesChecked: number;
|
|
12
|
+
fieldsChecked: number;
|
|
13
|
+
violations: PostmarkViolation[];
|
|
14
|
+
knownIgnored: number;
|
|
15
|
+
};
|
|
16
|
+
export declare function loadPostmarkSchemas(): Record<string, JsonSchema>;
|
|
17
|
+
/**
|
|
18
|
+
* Validate every Postmark resource the twin serves (in `root`) against its schema. A wrong
|
|
19
|
+
* type, an undeclared missing-required field, or an enum violation fails the report.
|
|
20
|
+
* Soft-deleted rows are skipped — a tombstone is not a served resource.
|
|
21
|
+
*/
|
|
22
|
+
export declare function checkPostmarkConformance(opts?: {
|
|
23
|
+
root?: string;
|
|
24
|
+
known?: KnownDeviation[];
|
|
25
|
+
}): PostmarkConformanceReport;
|
|
26
|
+
export type PostmarkCoverageReport = specConformance.SpecCoverageReport & {
|
|
27
|
+
object: string;
|
|
28
|
+
};
|
|
29
|
+
/** Per-object coverage: of the fields each Postmark object declares, which does the twin emit? */
|
|
30
|
+
export declare function postmarkCoverage(opts?: {
|
|
31
|
+
root?: string;
|
|
32
|
+
}): PostmarkCoverageReport[];
|
|
33
|
+
export {};
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
// Postmark twin conformance — spec-true validation of the resources the twin serves against
|
|
2
|
+
// per-object JSON Schemas (required fields, types, enums), offline (no key).
|
|
3
|
+
//
|
|
4
|
+
// The schemas below are transcribed from the OFFICIAL `postmark` SDK's own TypeScript models
|
|
5
|
+
// (node_modules/postmark/dist/client/models/**/*.d.ts) — a first-party artifact naming every
|
|
6
|
+
// field and its type. Postmark's API cannot be dual-run against a live sandbox in CI, so
|
|
7
|
+
// those published shapes carry the weight here.
|
|
8
|
+
//
|
|
9
|
+
// For every resource the twin emits, validate its body via the shared specConformance
|
|
10
|
+
// harness: declared TYPES, REQUIRED fields (omission), ENUMS. Twin-internal `_`-prefixed
|
|
11
|
+
// fields are exempt; polymorphic/nested blocks (Triggers, Metadata, MessageEvents, DNS
|
|
12
|
+
// detail) are left untyped to avoid false positives.
|
|
13
|
+
import { twinResources } from '@volter/world-core';
|
|
14
|
+
import { specConformance } from '@volter/world-tooling';
|
|
15
|
+
const SCHEMAS = {
|
|
16
|
+
// SDK: models/messages/OutboundMessage.d.ts
|
|
17
|
+
message: {
|
|
18
|
+
type: 'object',
|
|
19
|
+
required: ['MessageID', 'From', 'Recipients', 'ReceivedAt', 'Status', 'MessageStream'],
|
|
20
|
+
properties: {
|
|
21
|
+
MessageID: { type: 'string' },
|
|
22
|
+
From: { type: 'string' },
|
|
23
|
+
Subject: { type: 'string' },
|
|
24
|
+
To: { type: 'array' },
|
|
25
|
+
Cc: { type: 'array' },
|
|
26
|
+
Bcc: { type: 'array' },
|
|
27
|
+
Recipients: { type: 'array' },
|
|
28
|
+
Attachments: { type: 'array' },
|
|
29
|
+
ReceivedAt: { type: 'string' },
|
|
30
|
+
Tag: { type: 'string' },
|
|
31
|
+
Status: { type: 'string', enum: ['Sent', 'Queued', 'Processed'] },
|
|
32
|
+
TrackOpens: { type: 'boolean' },
|
|
33
|
+
TrackLinks: { type: 'string', enum: ['None', 'HtmlAndText', 'HtmlOnly', 'TextOnly'] },
|
|
34
|
+
MessageStream: { type: 'string' },
|
|
35
|
+
Sandboxed: { type: 'boolean' },
|
|
36
|
+
MessageEvents: { type: 'array' },
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
// SDK: models/bounces/Bounce.d.ts (+ BounceType / BounceTypeCode enums)
|
|
40
|
+
bounce: {
|
|
41
|
+
type: 'object',
|
|
42
|
+
required: ['RecordType', 'ID', 'Type', 'TypeCode', 'Name', 'Email', 'BouncedAt', 'MessageStream'],
|
|
43
|
+
properties: {
|
|
44
|
+
RecordType: { type: 'string', enum: ['Bounce'] },
|
|
45
|
+
ID: { type: 'number' },
|
|
46
|
+
Type: { type: 'string', enum: ['HardBounce', 'Transient', 'SoftBounce', 'SpamComplaint', 'SpamNotification', 'Unsubscribe', 'AutoResponder', 'DnsError', 'BadEmailAddress', 'Blocked', 'ManuallyDeactivated', 'Unknown'] },
|
|
47
|
+
TypeCode: { type: 'number' },
|
|
48
|
+
Name: { type: 'string' },
|
|
49
|
+
Tag: { type: 'string' },
|
|
50
|
+
MessageID: { type: 'string' },
|
|
51
|
+
ServerID: { type: 'number' },
|
|
52
|
+
Description: { type: 'string' },
|
|
53
|
+
Details: { type: 'string' },
|
|
54
|
+
Email: { type: 'string' },
|
|
55
|
+
From: { type: 'string' },
|
|
56
|
+
BouncedAt: { type: 'string' },
|
|
57
|
+
DumpAvailable: { type: 'boolean' },
|
|
58
|
+
Inactive: { type: 'boolean' },
|
|
59
|
+
CanActivate: { type: 'boolean' },
|
|
60
|
+
Subject: { type: 'string' },
|
|
61
|
+
MessageStream: { type: 'string' },
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
// SDK: models/templates/Template.d.ts (+ TemplateTypes enum)
|
|
65
|
+
template: {
|
|
66
|
+
type: 'object',
|
|
67
|
+
required: ['TemplateId', 'Name', 'TemplateType', 'Active'],
|
|
68
|
+
properties: {
|
|
69
|
+
TemplateId: { type: 'number' },
|
|
70
|
+
Name: { type: 'string' },
|
|
71
|
+
TemplateType: { type: 'string', enum: ['Standard', 'Layout'] },
|
|
72
|
+
Subject: { type: 'string' },
|
|
73
|
+
Active: { type: 'boolean' },
|
|
74
|
+
AssociatedServerId: { type: 'number' },
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
// SDK: models/streams/MessageStream.d.ts
|
|
78
|
+
message_stream: {
|
|
79
|
+
type: 'object',
|
|
80
|
+
required: ['ID', 'ServerID', 'Name', 'MessageStreamType', 'CreatedAt'],
|
|
81
|
+
properties: {
|
|
82
|
+
ID: { type: 'string' },
|
|
83
|
+
ServerID: { type: 'number' },
|
|
84
|
+
Name: { type: 'string' },
|
|
85
|
+
Description: { type: 'string' },
|
|
86
|
+
MessageStreamType: { type: 'string', enum: ['Transactional', 'Broadcast', 'Inbound'] },
|
|
87
|
+
CreatedAt: { type: 'string' },
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
// SDK: models/webhooks/Webhook.d.ts
|
|
91
|
+
webhook: {
|
|
92
|
+
type: 'object',
|
|
93
|
+
required: ['ID', 'Url', 'MessageStream'],
|
|
94
|
+
properties: {
|
|
95
|
+
ID: { type: 'number' },
|
|
96
|
+
Url: { type: 'string' },
|
|
97
|
+
MessageStream: { type: 'string' },
|
|
98
|
+
HttpHeaders: { type: 'array' },
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
// SDK: models/suppressions/Suppression.d.ts (+ SuppressionReason / SuppressionOrigin enums)
|
|
102
|
+
suppression: {
|
|
103
|
+
type: 'object',
|
|
104
|
+
required: ['EmailAddress', 'SuppressionReason', 'Origin', 'CreatedAt'],
|
|
105
|
+
properties: {
|
|
106
|
+
EmailAddress: { type: 'string' },
|
|
107
|
+
SuppressionReason: { type: 'string', enum: ['HardBounce', 'SpamComplaint', 'ManualSuppression'] },
|
|
108
|
+
Origin: { type: 'string', enum: ['Recipient', 'Customer', 'Admin'] },
|
|
109
|
+
CreatedAt: { type: 'string' },
|
|
110
|
+
MessageStream: { type: 'string' },
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
// SDK: models/server/Server.d.ts (+ ServerDeliveryTypes enum)
|
|
114
|
+
server: {
|
|
115
|
+
type: 'object',
|
|
116
|
+
required: ['ID', 'Name', 'DeliveryType'],
|
|
117
|
+
properties: {
|
|
118
|
+
ID: { type: 'number' },
|
|
119
|
+
Name: { type: 'string' },
|
|
120
|
+
ApiTokens: { type: 'array' },
|
|
121
|
+
ServerLink: { type: 'string' },
|
|
122
|
+
Color: { type: 'string' },
|
|
123
|
+
SmtpApiActivated: { type: 'boolean' },
|
|
124
|
+
RawEmailEnabled: { type: 'boolean' },
|
|
125
|
+
DeliveryType: { type: 'string', enum: ['Live', 'Sandbox'] },
|
|
126
|
+
InboundAddress: { type: 'string' },
|
|
127
|
+
InboundHash: { type: 'string' },
|
|
128
|
+
InboundSpamThreshold: { type: 'number' },
|
|
129
|
+
TrackOpens: { type: 'boolean' },
|
|
130
|
+
TrackLinks: { type: 'string', enum: ['None', 'HtmlAndText', 'HtmlOnly', 'TextOnly'] },
|
|
131
|
+
PostFirstOpenOnly: { type: 'boolean' },
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
// SDK: models/domains/Domain.d.ts (DomainDetails)
|
|
135
|
+
domain: {
|
|
136
|
+
type: 'object',
|
|
137
|
+
required: ['ID', 'Name', 'SPFVerified', 'DKIMVerified', 'ReturnPathDomainVerified'],
|
|
138
|
+
properties: {
|
|
139
|
+
ID: { type: 'number' },
|
|
140
|
+
Name: { type: 'string' },
|
|
141
|
+
SPFVerified: { type: 'boolean' },
|
|
142
|
+
DKIMVerified: { type: 'boolean' },
|
|
143
|
+
WeakDKIM: { type: 'boolean' },
|
|
144
|
+
ReturnPathDomainVerified: { type: 'boolean' },
|
|
145
|
+
SPFHost: { type: 'string' },
|
|
146
|
+
SPFTextValue: { type: 'string' },
|
|
147
|
+
DKIMHost: { type: 'string' },
|
|
148
|
+
DKIMTextValue: { type: 'string' },
|
|
149
|
+
DKIMUpdateStatus: { type: 'string' },
|
|
150
|
+
ReturnPathDomain: { type: 'string' },
|
|
151
|
+
ReturnPathDomainCNAMEValue: { type: 'string' },
|
|
152
|
+
},
|
|
153
|
+
},
|
|
154
|
+
// SDK: models/senders/Signature.d.ts (SignatureDetails extends DomainDetails)
|
|
155
|
+
sender_signature: {
|
|
156
|
+
type: 'object',
|
|
157
|
+
required: ['ID', 'Domain', 'EmailAddress', 'Name', 'Confirmed'],
|
|
158
|
+
properties: {
|
|
159
|
+
ID: { type: 'number' },
|
|
160
|
+
Domain: { type: 'string' },
|
|
161
|
+
EmailAddress: { type: 'string' },
|
|
162
|
+
ReplyToEmailAddress: { type: 'string' },
|
|
163
|
+
Name: { type: 'string' },
|
|
164
|
+
Confirmed: { type: 'boolean' },
|
|
165
|
+
ConfirmationPersonalNote: { type: 'string' },
|
|
166
|
+
},
|
|
167
|
+
},
|
|
168
|
+
// SDK: models/triggers/InboundRule.d.ts
|
|
169
|
+
inbound_rule: {
|
|
170
|
+
type: 'object',
|
|
171
|
+
required: ['ID', 'Rule'],
|
|
172
|
+
properties: { ID: { type: 'number' }, Rule: { type: 'string' } },
|
|
173
|
+
},
|
|
174
|
+
// SDK: models/messages/InboundMessage.d.ts
|
|
175
|
+
inbound_message: {
|
|
176
|
+
type: 'object',
|
|
177
|
+
required: ['MessageID', 'From', 'To', 'Date', 'Status', 'MessageStream'],
|
|
178
|
+
properties: {
|
|
179
|
+
MessageID: { type: 'string' },
|
|
180
|
+
From: { type: 'string' },
|
|
181
|
+
FromName: { type: 'string' },
|
|
182
|
+
To: { type: 'string' },
|
|
183
|
+
Cc: { type: 'string' },
|
|
184
|
+
Bcc: { type: 'string' },
|
|
185
|
+
Subject: { type: 'string' },
|
|
186
|
+
Date: { type: 'string' },
|
|
187
|
+
MailboxHash: { type: 'string' },
|
|
188
|
+
Tag: { type: 'string' },
|
|
189
|
+
Status: { type: 'string', enum: ['queued', 'sent', 'processed', 'blocked', 'failed', 'scheduled', 'Queued', 'Sent', 'Processed', 'Blocked', 'Failed', 'Scheduled'] },
|
|
190
|
+
Attachments: { type: 'array' },
|
|
191
|
+
MessageStream: { type: 'string' },
|
|
192
|
+
},
|
|
193
|
+
},
|
|
194
|
+
// SDK: models/data_removal/DataRemoval.d.ts
|
|
195
|
+
data_removal: {
|
|
196
|
+
type: 'object',
|
|
197
|
+
required: ['ID', 'Status'],
|
|
198
|
+
properties: {
|
|
199
|
+
ID: { type: 'number' },
|
|
200
|
+
Status: { type: 'string', enum: ['Pending', 'Done'] },
|
|
201
|
+
RequestedBy: { type: 'string' },
|
|
202
|
+
RequestedFor: { type: 'string' },
|
|
203
|
+
NotifyWhenCompleted: { type: 'boolean' },
|
|
204
|
+
},
|
|
205
|
+
},
|
|
206
|
+
};
|
|
207
|
+
// twin resource type → schema key (1:1 here).
|
|
208
|
+
const TYPE_TO_OBJECT = Object.fromEntries(Object.keys(SCHEMAS).map((k) => [k, k]));
|
|
209
|
+
const isTwinExtra = (k) => k.startsWith('_');
|
|
210
|
+
/** The emitted body: drop kernel meta + twin-internal fields. */
|
|
211
|
+
function emitted(r) {
|
|
212
|
+
const out = {};
|
|
213
|
+
for (const [k, v] of Object.entries(r)) {
|
|
214
|
+
if (k === 'type' || k === 'id' || k === 'updatedAt' || k.startsWith('_'))
|
|
215
|
+
continue;
|
|
216
|
+
out[k] = v;
|
|
217
|
+
}
|
|
218
|
+
return out;
|
|
219
|
+
}
|
|
220
|
+
export function loadPostmarkSchemas() {
|
|
221
|
+
return SCHEMAS;
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Validate every Postmark resource the twin serves (in `root`) against its schema. A wrong
|
|
225
|
+
* type, an undeclared missing-required field, or an enum violation fails the report.
|
|
226
|
+
* Soft-deleted rows are skipped — a tombstone is not a served resource.
|
|
227
|
+
*/
|
|
228
|
+
export function checkPostmarkConformance(opts = {}) {
|
|
229
|
+
const resources = twinResources('postmark', opts.root)
|
|
230
|
+
.filter((r) => !r.type.startsWith('_') && r.deleted !== true);
|
|
231
|
+
const violations = [];
|
|
232
|
+
let fieldsChecked = 0;
|
|
233
|
+
let knownIgnored = 0;
|
|
234
|
+
for (const r of resources) {
|
|
235
|
+
const objectName = TYPE_TO_OBJECT[r.type];
|
|
236
|
+
if (!objectName)
|
|
237
|
+
continue;
|
|
238
|
+
const schema = SCHEMAS[objectName];
|
|
239
|
+
if (!schema)
|
|
240
|
+
continue;
|
|
241
|
+
const rep = specConformance.checkSpecConformance(emitted(r), schema, { exemptKey: isTwinExtra, known: opts.known ?? [], flagExtra: false });
|
|
242
|
+
fieldsChecked += rep.fieldsChecked;
|
|
243
|
+
knownIgnored += rep.knownIgnored;
|
|
244
|
+
for (const v of rep.violations)
|
|
245
|
+
violations.push({ ...v, object: objectName, id: r.id });
|
|
246
|
+
}
|
|
247
|
+
return { ok: violations.length === 0, resourcesChecked: resources.length, fieldsChecked, violations, knownIgnored };
|
|
248
|
+
}
|
|
249
|
+
/** Per-object coverage: of the fields each Postmark object declares, which does the twin emit? */
|
|
250
|
+
export function postmarkCoverage(opts = {}) {
|
|
251
|
+
const byType = new Map();
|
|
252
|
+
for (const r of twinResources('postmark', opts.root)) {
|
|
253
|
+
if (!r.type.startsWith('_') && !byType.has(r.type) && r.deleted !== true)
|
|
254
|
+
byType.set(r.type, r);
|
|
255
|
+
}
|
|
256
|
+
const out = [];
|
|
257
|
+
for (const [type, r] of byType) {
|
|
258
|
+
const objectName = TYPE_TO_OBJECT[type];
|
|
259
|
+
const schema = objectName ? SCHEMAS[objectName] : undefined;
|
|
260
|
+
if (!objectName || !schema)
|
|
261
|
+
continue;
|
|
262
|
+
out.push({ object: objectName, ...specConformance.specCoverage(schema, emitted(r), { exemptKey: isTwinExtra }) });
|
|
263
|
+
}
|
|
264
|
+
return out;
|
|
265
|
+
}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import type { PerformContext, PushOutcome, RemoteExecute, SyncResource, TwinAction } from '@volter/world-core';
|
|
2
|
+
/**
|
|
3
|
+
* The injected real-Postmark boundary — the subset of `postmark`'s `ServerClient` this
|
|
4
|
+
* connector uses. A real `new ServerClient(token)` is structurally assignable; tests pass a
|
|
5
|
+
* fake. Only the methods the connector calls are declared, so the surface stays honest.
|
|
6
|
+
* Unlike Resend's `{data, error}` envelope, the Postmark SDK RESOLVES with the payload and
|
|
7
|
+
* REJECTS with a typed `PostmarkError` — this interface mirrors that.
|
|
8
|
+
*/
|
|
9
|
+
export interface PostmarkClient {
|
|
10
|
+
getOutboundMessages(filter?: Record<string, unknown>): Promise<{
|
|
11
|
+
TotalCount: number | string;
|
|
12
|
+
Messages: PostmarkOutboundMessage[];
|
|
13
|
+
}>;
|
|
14
|
+
getBounces(filter?: Record<string, unknown>): Promise<{
|
|
15
|
+
TotalCount: number;
|
|
16
|
+
Bounces: PostmarkBounce[];
|
|
17
|
+
}>;
|
|
18
|
+
getTemplates(filter?: Record<string, unknown>): Promise<{
|
|
19
|
+
TotalCount: number;
|
|
20
|
+
Templates: PostmarkTemplate[];
|
|
21
|
+
}>;
|
|
22
|
+
getMessageStreams(filter?: Record<string, unknown>): Promise<{
|
|
23
|
+
TotalCount: number;
|
|
24
|
+
MessageStreams: PostmarkMessageStream[];
|
|
25
|
+
}>;
|
|
26
|
+
getWebhooks(filter?: Record<string, unknown>): Promise<{
|
|
27
|
+
Webhooks: PostmarkWebhookConfig[];
|
|
28
|
+
}>;
|
|
29
|
+
getServer(): Promise<PostmarkServer>;
|
|
30
|
+
sendEmail(message: Record<string, unknown>): Promise<{
|
|
31
|
+
MessageID: string;
|
|
32
|
+
ErrorCode: number;
|
|
33
|
+
Message: string;
|
|
34
|
+
}>;
|
|
35
|
+
createTemplate(options: Record<string, unknown>): Promise<{
|
|
36
|
+
TemplateId: number;
|
|
37
|
+
}>;
|
|
38
|
+
createWebhook(options: Record<string, unknown>): Promise<{
|
|
39
|
+
ID: number;
|
|
40
|
+
}>;
|
|
41
|
+
createMessageStream(options: Record<string, unknown>): Promise<{
|
|
42
|
+
ID: string;
|
|
43
|
+
}>;
|
|
44
|
+
}
|
|
45
|
+
export type PostmarkRecipient = {
|
|
46
|
+
Email?: string;
|
|
47
|
+
Name?: string;
|
|
48
|
+
};
|
|
49
|
+
export type PostmarkOutboundMessage = {
|
|
50
|
+
MessageID: string;
|
|
51
|
+
Tag?: string;
|
|
52
|
+
To?: PostmarkRecipient[];
|
|
53
|
+
Cc?: PostmarkRecipient[];
|
|
54
|
+
Bcc?: PostmarkRecipient[];
|
|
55
|
+
Recipients?: string[];
|
|
56
|
+
ReceivedAt?: string;
|
|
57
|
+
From?: string;
|
|
58
|
+
Subject?: string;
|
|
59
|
+
Attachments?: string[];
|
|
60
|
+
Status?: string;
|
|
61
|
+
TrackOpens?: boolean;
|
|
62
|
+
TrackLinks?: string;
|
|
63
|
+
Metadata?: Record<string, string>;
|
|
64
|
+
MessageStream?: string;
|
|
65
|
+
Sandboxed?: boolean;
|
|
66
|
+
};
|
|
67
|
+
export type PostmarkBounce = {
|
|
68
|
+
ID: number;
|
|
69
|
+
Type?: string;
|
|
70
|
+
TypeCode?: number;
|
|
71
|
+
Name?: string;
|
|
72
|
+
Tag?: string;
|
|
73
|
+
MessageID?: string;
|
|
74
|
+
ServerID?: number;
|
|
75
|
+
Description?: string;
|
|
76
|
+
Details?: string;
|
|
77
|
+
Email?: string;
|
|
78
|
+
From?: string;
|
|
79
|
+
BouncedAt?: string;
|
|
80
|
+
DumpAvailable?: boolean;
|
|
81
|
+
Inactive?: boolean;
|
|
82
|
+
CanActivate?: boolean;
|
|
83
|
+
Subject?: string;
|
|
84
|
+
MessageStream?: string;
|
|
85
|
+
};
|
|
86
|
+
export type PostmarkTemplate = {
|
|
87
|
+
TemplateId: number;
|
|
88
|
+
Name?: string;
|
|
89
|
+
Alias?: string | null;
|
|
90
|
+
TemplateType?: string;
|
|
91
|
+
Active?: boolean;
|
|
92
|
+
LayoutTemplate?: string | null;
|
|
93
|
+
Subject?: string;
|
|
94
|
+
HtmlBody?: string | null;
|
|
95
|
+
TextBody?: string | null;
|
|
96
|
+
};
|
|
97
|
+
export type PostmarkMessageStream = {
|
|
98
|
+
ID: string;
|
|
99
|
+
ServerID?: number;
|
|
100
|
+
Name?: string;
|
|
101
|
+
Description?: string;
|
|
102
|
+
MessageStreamType?: string;
|
|
103
|
+
CreatedAt?: string;
|
|
104
|
+
UpdatedAt?: string | null;
|
|
105
|
+
ArchivedAt?: string | null;
|
|
106
|
+
SubscriptionManagementConfiguration?: Record<string, unknown>;
|
|
107
|
+
};
|
|
108
|
+
export type PostmarkWebhookConfig = {
|
|
109
|
+
ID: number;
|
|
110
|
+
Url?: string;
|
|
111
|
+
MessageStream?: string;
|
|
112
|
+
HttpAuth?: Record<string, unknown> | null;
|
|
113
|
+
HttpHeaders?: Array<Record<string, unknown>>;
|
|
114
|
+
Triggers?: Record<string, unknown>;
|
|
115
|
+
};
|
|
116
|
+
export type PostmarkServer = {
|
|
117
|
+
ID: number;
|
|
118
|
+
Name?: string;
|
|
119
|
+
Color?: string;
|
|
120
|
+
DeliveryType?: string;
|
|
121
|
+
TrackOpens?: boolean;
|
|
122
|
+
TrackLinks?: string;
|
|
123
|
+
InboundAddress?: string;
|
|
124
|
+
ServerLink?: string;
|
|
125
|
+
SmtpApiActivated?: boolean;
|
|
126
|
+
};
|
|
127
|
+
export declare function mapMessage(m: PostmarkOutboundMessage): SyncResource;
|
|
128
|
+
export declare function mapBounce(b: PostmarkBounce): SyncResource;
|
|
129
|
+
export declare function mapTemplate(t: PostmarkTemplate): SyncResource;
|
|
130
|
+
export declare function mapMessageStream(s: PostmarkMessageStream): SyncResource;
|
|
131
|
+
export declare function mapWebhook(w: PostmarkWebhookConfig): SyncResource;
|
|
132
|
+
export declare function mapServer(s: PostmarkServer): SyncResource;
|
|
133
|
+
export declare function pullPostmarkMessages(client: PostmarkClient): Promise<SyncResource[]>;
|
|
134
|
+
export declare function pullPostmarkBounces(client: PostmarkClient): Promise<SyncResource[]>;
|
|
135
|
+
export declare function pullPostmarkTemplates(client: PostmarkClient): Promise<SyncResource[]>;
|
|
136
|
+
export declare function pullPostmarkMessageStreams(client: PostmarkClient): Promise<SyncResource[]>;
|
|
137
|
+
export declare function pullPostmarkWebhooks(client: PostmarkClient): Promise<SyncResource[]>;
|
|
138
|
+
export declare function pullPostmarkServer(client: PostmarkClient): Promise<SyncResource[]>;
|
|
139
|
+
/**
|
|
140
|
+
* D7 entry point: pull from real Postmark and fold everything into the twin (mirror seeding).
|
|
141
|
+
* Callers never have to know the per-domain `pull*` mechanics.
|
|
142
|
+
*/
|
|
143
|
+
export declare function syncPostmarkFromReal(client: PostmarkClient, opts: {
|
|
144
|
+
root?: string;
|
|
145
|
+
occurredAt: string;
|
|
146
|
+
}): Promise<{
|
|
147
|
+
observed: number;
|
|
148
|
+
deltasAppended: number;
|
|
149
|
+
}>;
|
|
150
|
+
/**
|
|
151
|
+
* Push ONE pending action to REAL Postmark via the injected client. Returns the real external
|
|
152
|
+
* id (the Postmark object id from the response). WRITES TO THE REAL ACCOUNT (sends real email).
|
|
153
|
+
*/
|
|
154
|
+
export declare function pushPostmarkAction(client: PostmarkClient, action: Pick<TwinAction, 'operation' | 'subject' | 'fields'>): Promise<{
|
|
155
|
+
externalId: string;
|
|
156
|
+
}>;
|
|
157
|
+
/** A PostmarkClient over the kernel's executor. At a REAL boundary the kernel's executor sets the sealed
|
|
158
|
+
* credential over these headers (executor.ts); at the twin's own wire any credential is one. */
|
|
159
|
+
export declare function postmarkClientOver(execute: RemoteExecute): PostmarkClient;
|
|
160
|
+
/** The refresh adapter: pull the account's state through the executor and fold it into the root. */
|
|
161
|
+
export declare function syncPostmarkFromRemote(execute: RemoteExecute, opts?: {
|
|
162
|
+
root?: string;
|
|
163
|
+
origin?: string;
|
|
164
|
+
occurredAt?: string;
|
|
165
|
+
}): Promise<ReturnType<typeof syncPostmarkFromReal>>;
|
|
166
|
+
/** The perform adapter: a message send crosses; everything else is the twin's own record. */
|
|
167
|
+
export declare function performPostmarkAction(execute: RemoteExecute, action: TwinAction, _ctx: PerformContext): Promise<PushOutcome>;
|