@voyant-travel/notifications 0.111.7
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 +201 -0
- package/README.md +179 -0
- package/dist/index.d.ts +61 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +196 -0
- package/dist/liquid.d.ts +5 -0
- package/dist/liquid.d.ts.map +1 -0
- package/dist/liquid.js +156 -0
- package/dist/providers/local.d.ts +22 -0
- package/dist/providers/local.d.ts.map +1 -0
- package/dist/providers/local.js +23 -0
- package/dist/providers/voyant-cloud-email.d.ts +27 -0
- package/dist/providers/voyant-cloud-email.d.ts.map +1 -0
- package/dist/providers/voyant-cloud-email.js +73 -0
- package/dist/providers/voyant-cloud-sms.d.ts +26 -0
- package/dist/providers/voyant-cloud-sms.d.ts.map +1 -0
- package/dist/providers/voyant-cloud-sms.js +24 -0
- package/dist/routes.d.ts +1546 -0
- package/dist/routes.d.ts.map +1 -0
- package/dist/routes.js +337 -0
- package/dist/schema.d.ts +2119 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +356 -0
- package/dist/service-booking-document-lifecycle.d.ts +99 -0
- package/dist/service-booking-document-lifecycle.d.ts.map +1 -0
- package/dist/service-booking-document-lifecycle.js +259 -0
- package/dist/service-booking-documents.d.ts +256 -0
- package/dist/service-booking-documents.d.ts.map +1 -0
- package/dist/service-booking-documents.js +323 -0
- package/dist/service-deliveries.d.ts +183 -0
- package/dist/service-deliveries.d.ts.map +1 -0
- package/dist/service-deliveries.js +413 -0
- package/dist/service-delivery-metadata.d.ts +42 -0
- package/dist/service-delivery-metadata.d.ts.map +1 -0
- package/dist/service-delivery-metadata.js +114 -0
- package/dist/service-reminder-authoring.d.ts +33 -0
- package/dist/service-reminder-authoring.d.ts.map +1 -0
- package/dist/service-reminder-authoring.js +247 -0
- package/dist/service-reminder-booking-context.d.ts +94 -0
- package/dist/service-reminder-booking-context.d.ts.map +1 -0
- package/dist/service-reminder-booking-context.js +164 -0
- package/dist/service-reminder-events.d.ts +33 -0
- package/dist/service-reminder-events.d.ts.map +1 -0
- package/dist/service-reminder-events.js +178 -0
- package/dist/service-reminder-run-state.d.ts +114 -0
- package/dist/service-reminder-run-state.d.ts.map +1 -0
- package/dist/service-reminder-run-state.js +100 -0
- package/dist/service-reminder-stage-runs.d.ts +6 -0
- package/dist/service-reminder-stage-runs.d.ts.map +1 -0
- package/dist/service-reminder-stage-runs.js +310 -0
- package/dist/service-reminders.d.ts +30 -0
- package/dist/service-reminders.d.ts.map +1 -0
- package/dist/service-reminders.js +189 -0
- package/dist/service-sequence-targets.d.ts +50 -0
- package/dist/service-sequence-targets.d.ts.map +1 -0
- package/dist/service-sequence-targets.js +136 -0
- package/dist/service-sequence.d.ts +68 -0
- package/dist/service-sequence.d.ts.map +1 -0
- package/dist/service-sequence.js +316 -0
- package/dist/service-shared.d.ts +107 -0
- package/dist/service-shared.d.ts.map +1 -0
- package/dist/service-shared.js +159 -0
- package/dist/service-stages.d.ts +23 -0
- package/dist/service-stages.d.ts.map +1 -0
- package/dist/service-stages.js +203 -0
- package/dist/service-template-data.d.ts +19 -0
- package/dist/service-template-data.d.ts.map +1 -0
- package/dist/service-template-data.js +278 -0
- package/dist/service-templates.d.ts +260 -0
- package/dist/service-templates.d.ts.map +1 -0
- package/dist/service-templates.js +293 -0
- package/dist/service.d.ts +273 -0
- package/dist/service.d.ts.map +1 -0
- package/dist/service.js +51 -0
- package/dist/task-runtime.d.ts +19 -0
- package/dist/task-runtime.d.ts.map +1 -0
- package/dist/task-runtime.js +11 -0
- package/dist/tasks/deliver-reminder.d.ts +9 -0
- package/dist/tasks/deliver-reminder.d.ts.map +1 -0
- package/dist/tasks/deliver-reminder.js +12 -0
- package/dist/tasks/index.d.ts +3 -0
- package/dist/tasks/index.d.ts.map +1 -0
- package/dist/tasks/index.js +2 -0
- package/dist/tasks/send-due-reminders.d.ts +7 -0
- package/dist/tasks/send-due-reminders.d.ts.map +1 -0
- package/dist/tasks/send-due-reminders.js +31 -0
- package/dist/template-authoring.d.ts +23 -0
- package/dist/template-authoring.d.ts.map +1 -0
- package/dist/template-authoring.js +386 -0
- package/dist/types.d.ts +82 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1 -0
- package/dist/validation.d.ts +1093 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +451 -0
- package/package.json +102 -0
package/dist/liquid.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { Liquid } from "liquidjs";
|
|
2
|
+
declare const engine: Liquid;
|
|
3
|
+
export declare function renderLiquidTemplate(template: string | null | undefined, data: Record<string, unknown>): string | null;
|
|
4
|
+
export { engine as notificationLiquidEngine };
|
|
5
|
+
//# sourceMappingURL=liquid.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"liquid.d.ts","sourceRoot":"","sources":["../src/liquid.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AAEjC,QAAA,MAAM,MAAM,QAOV,CAAA;AAqIF,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EACnC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,MAAM,GAAG,IAAI,CAiBf;AAED,OAAO,EAAE,MAAM,IAAI,wBAAwB,EAAE,CAAA"}
|
package/dist/liquid.js
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { Liquid } from "liquidjs";
|
|
2
|
+
const engine = new Liquid({
|
|
3
|
+
strictFilters: false,
|
|
4
|
+
strictVariables: false,
|
|
5
|
+
trimTagLeft: false,
|
|
6
|
+
trimTagRight: false,
|
|
7
|
+
greedy: false,
|
|
8
|
+
jsTruthy: true,
|
|
9
|
+
});
|
|
10
|
+
engine.registerFilter("json", (value) => JSON.stringify(value ?? null));
|
|
11
|
+
engine.registerFilter("currency", (value, currency = "EUR", locale = "en-US") => {
|
|
12
|
+
const num = typeof value === "number" ? value : Number.parseFloat(String(value));
|
|
13
|
+
if (Number.isNaN(num))
|
|
14
|
+
return String(value ?? "");
|
|
15
|
+
return new Intl.NumberFormat(String(locale), {
|
|
16
|
+
style: "currency",
|
|
17
|
+
currency: String(currency || "EUR"),
|
|
18
|
+
}).format(num);
|
|
19
|
+
});
|
|
20
|
+
engine.registerFilter("date_format", (value, _format, locale = "en-US") => {
|
|
21
|
+
if (!value)
|
|
22
|
+
return "";
|
|
23
|
+
const date = value instanceof Date ? value : new Date(String(value));
|
|
24
|
+
if (Number.isNaN(date.getTime()))
|
|
25
|
+
return String(value);
|
|
26
|
+
return date.toLocaleDateString(String(locale), {
|
|
27
|
+
year: "numeric",
|
|
28
|
+
month: "long",
|
|
29
|
+
day: "numeric",
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
engine.registerFilter("phone", (value) => {
|
|
33
|
+
if (!value)
|
|
34
|
+
return "";
|
|
35
|
+
const phone = String(value).replace(/\D/g, "");
|
|
36
|
+
if (phone.length === 10) {
|
|
37
|
+
return `(${phone.slice(0, 3)}) ${phone.slice(3, 6)}-${phone.slice(6)}`;
|
|
38
|
+
}
|
|
39
|
+
if (phone.length === 11 && phone.startsWith("1")) {
|
|
40
|
+
return `+1 (${phone.slice(1, 4)}) ${phone.slice(4, 7)}-${phone.slice(7)}`;
|
|
41
|
+
}
|
|
42
|
+
if (phone.length > 10) {
|
|
43
|
+
return `+${phone.slice(0, -9)} ${phone.slice(-9, -6)} ${phone.slice(-6, -3)} ${phone.slice(-3)}`;
|
|
44
|
+
}
|
|
45
|
+
return String(value);
|
|
46
|
+
});
|
|
47
|
+
engine.registerFilter("default", (value, defaultValue) => {
|
|
48
|
+
if (value === null || value === undefined || value === "")
|
|
49
|
+
return defaultValue;
|
|
50
|
+
return value;
|
|
51
|
+
});
|
|
52
|
+
engine.registerFilter("pluralize", (count, singular, plural) => {
|
|
53
|
+
const num = typeof count === "number" ? count : Number.parseInt(String(count), 10);
|
|
54
|
+
return num === 1 ? singular : plural;
|
|
55
|
+
});
|
|
56
|
+
function toSnakeCase(str) {
|
|
57
|
+
return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);
|
|
58
|
+
}
|
|
59
|
+
function normalizeKeys(obj) {
|
|
60
|
+
const result = {};
|
|
61
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
62
|
+
result[key] =
|
|
63
|
+
value && typeof value === "object" && !Array.isArray(value)
|
|
64
|
+
? normalizeKeys(value)
|
|
65
|
+
: value;
|
|
66
|
+
const snakeKey = toSnakeCase(key);
|
|
67
|
+
if (snakeKey !== key) {
|
|
68
|
+
result[snakeKey] = result[key];
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return result;
|
|
72
|
+
}
|
|
73
|
+
function decodeHtmlEntities(str) {
|
|
74
|
+
return str
|
|
75
|
+
.replace(/"/g, '"')
|
|
76
|
+
.replace(/'/g, "'")
|
|
77
|
+
.replace(/&/g, "&")
|
|
78
|
+
.replace(/</g, "<")
|
|
79
|
+
.replace(/>/g, ">");
|
|
80
|
+
}
|
|
81
|
+
function healBrokenTags(html) {
|
|
82
|
+
html = html.replace(/\{\{([\s\S]{1,300}?)\}\}/g, (match) => {
|
|
83
|
+
let healed = match;
|
|
84
|
+
if (/</.test(healed))
|
|
85
|
+
healed = healed.replace(/<[^>]+>/g, "").replace(/\s+/g, " ");
|
|
86
|
+
if (/&\w+;/.test(healed))
|
|
87
|
+
healed = decodeHtmlEntities(healed);
|
|
88
|
+
return healed;
|
|
89
|
+
});
|
|
90
|
+
html = html.replace(/\{%([\s\S]{1,300}?)%\}/g, (match) => {
|
|
91
|
+
let healed = match;
|
|
92
|
+
if (/</.test(healed))
|
|
93
|
+
healed = healed.replace(/<[^>]+>/g, "").replace(/\s+/g, " ");
|
|
94
|
+
if (/&\w+;/.test(healed))
|
|
95
|
+
healed = decodeHtmlEntities(healed);
|
|
96
|
+
return healed;
|
|
97
|
+
});
|
|
98
|
+
return html;
|
|
99
|
+
}
|
|
100
|
+
function healSyntax(template) {
|
|
101
|
+
let result = healBrokenTags(template);
|
|
102
|
+
result = result.replace(/\{%[-\s]*for\s+(\w+)\s*[-]?%\}/gi, (match, collection) => {
|
|
103
|
+
if (/\bfor\s+\w+\s+in\s+/i.test(match))
|
|
104
|
+
return match;
|
|
105
|
+
const singular = collection.endsWith("ies")
|
|
106
|
+
? `${collection.slice(0, -3)}y`
|
|
107
|
+
: collection.endsWith("s")
|
|
108
|
+
? collection.slice(0, -1)
|
|
109
|
+
: `${collection}_item`;
|
|
110
|
+
return `{% for ${singular} in ${collection} %}`;
|
|
111
|
+
});
|
|
112
|
+
result = result.replace(/\{%[-\s]*(endfor|endif|else|endunless|endcase|endcapture|endraw)\s*[-]?%\}/gi, (_match, tag) => `{% ${tag.toLowerCase()} %}`);
|
|
113
|
+
return result;
|
|
114
|
+
}
|
|
115
|
+
function regexFallback(template, data) {
|
|
116
|
+
function resolve(obj, path) {
|
|
117
|
+
const parts = path.split(".");
|
|
118
|
+
let cur = obj;
|
|
119
|
+
for (const p of parts) {
|
|
120
|
+
if (cur == null || typeof cur !== "object")
|
|
121
|
+
return undefined;
|
|
122
|
+
cur = cur[p];
|
|
123
|
+
}
|
|
124
|
+
return cur;
|
|
125
|
+
}
|
|
126
|
+
let result = template;
|
|
127
|
+
result = result.replace(/\{\{\s*([^}|]+?)(?:\s*\|[^}]*)?\s*\}\}/g, (_match, rawPath) => {
|
|
128
|
+
const path = rawPath.trim();
|
|
129
|
+
const value = resolve(data, path);
|
|
130
|
+
if (value !== undefined && value !== null)
|
|
131
|
+
return String(value);
|
|
132
|
+
return _match;
|
|
133
|
+
});
|
|
134
|
+
result = result.replace(/\{%[\s\S]*?%\}/g, "");
|
|
135
|
+
return result;
|
|
136
|
+
}
|
|
137
|
+
export function renderLiquidTemplate(template, data) {
|
|
138
|
+
if (!template)
|
|
139
|
+
return null;
|
|
140
|
+
const normalized = normalizeKeys(data);
|
|
141
|
+
const healed = healSyntax(template);
|
|
142
|
+
try {
|
|
143
|
+
return engine.parseAndRenderSync(healed, normalized);
|
|
144
|
+
}
|
|
145
|
+
catch (error) {
|
|
146
|
+
console.error("[notifications:liquid] template parsing failed, falling back to regex", error);
|
|
147
|
+
}
|
|
148
|
+
try {
|
|
149
|
+
return regexFallback(healed, normalized);
|
|
150
|
+
}
|
|
151
|
+
catch (fallbackError) {
|
|
152
|
+
console.error("[notifications:liquid] regex fallback failed", fallbackError);
|
|
153
|
+
return template;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
export { engine as notificationLiquidEngine };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { NotificationChannel, NotificationPayload, NotificationProvider } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Options for {@link createLocalProvider}.
|
|
4
|
+
*/
|
|
5
|
+
export interface LocalProviderOptions {
|
|
6
|
+
/** Channels this provider advertises. Defaults to `["email", "sms"]`. */
|
|
7
|
+
channels?: ReadonlyArray<NotificationChannel>;
|
|
8
|
+
/**
|
|
9
|
+
* Optional sink for captured payloads. Defaults to `console.log`.
|
|
10
|
+
* Tests can pass a vi.fn() to capture notifications without console noise.
|
|
11
|
+
*/
|
|
12
|
+
sink?: (payload: NotificationPayload) => void;
|
|
13
|
+
/** Provider name (defaults to `"local"`). Useful for stacking multiple locals. */
|
|
14
|
+
name?: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Create a development/test notification provider that captures sends to a
|
|
18
|
+
* sink (console by default) and returns synthetic message ids. Never makes
|
|
19
|
+
* network calls.
|
|
20
|
+
*/
|
|
21
|
+
export declare function createLocalProvider(options?: LocalProviderOptions): NotificationProvider;
|
|
22
|
+
//# sourceMappingURL=local.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"local.d.ts","sourceRoot":"","sources":["../../src/providers/local.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,mBAAmB,EACnB,mBAAmB,EACnB,oBAAoB,EAErB,MAAM,aAAa,CAAA;AAEpB;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,yEAAyE;IACzE,QAAQ,CAAC,EAAE,aAAa,CAAC,mBAAmB,CAAC,CAAA;IAC7C;;;OAGG;IACH,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,mBAAmB,KAAK,IAAI,CAAA;IAC7C,kFAAkF;IAClF,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,GAAE,oBAAyB,GAAG,oBAAoB,CAmB5F"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Create a development/test notification provider that captures sends to a
|
|
3
|
+
* sink (console by default) and returns synthetic message ids. Never makes
|
|
4
|
+
* network calls.
|
|
5
|
+
*/
|
|
6
|
+
export function createLocalProvider(options = {}) {
|
|
7
|
+
const name = options.name ?? "local";
|
|
8
|
+
const channels = options.channels ?? ["email", "sms"];
|
|
9
|
+
const sink = options.sink ??
|
|
10
|
+
((payload) => {
|
|
11
|
+
console.log(`[notifications:${name}]`, payload);
|
|
12
|
+
});
|
|
13
|
+
let counter = 0;
|
|
14
|
+
return {
|
|
15
|
+
name,
|
|
16
|
+
channels,
|
|
17
|
+
async send(payload) {
|
|
18
|
+
counter += 1;
|
|
19
|
+
sink(payload);
|
|
20
|
+
return { id: `${name}_${counter}`, provider: name };
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { VoyantCloudClient } from "@voyant-travel/cloud-sdk";
|
|
2
|
+
import type { NotificationProvider } from "../types.js";
|
|
3
|
+
export interface VoyantCloudEmailRendered {
|
|
4
|
+
subject: string;
|
|
5
|
+
html?: string;
|
|
6
|
+
text?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface VoyantCloudEmailProviderOptions {
|
|
9
|
+
/** Cloud SDK client. Construct via `getVoyantCloudClient(env)`. */
|
|
10
|
+
client: VoyantCloudClient;
|
|
11
|
+
/** Default sender address. Payload `from` overrides. */
|
|
12
|
+
from: string;
|
|
13
|
+
/** Optional default reply-to addresses. Payload may override per-send. */
|
|
14
|
+
replyTo?: ReadonlyArray<string>;
|
|
15
|
+
/**
|
|
16
|
+
* Render a template id + data tuple into an email body. When omitted,
|
|
17
|
+
* the payload's `template` is used as the subject and `data` is
|
|
18
|
+
* JSON-stringified into the text body.
|
|
19
|
+
*/
|
|
20
|
+
renderTemplate?: (template: string, data: unknown) => Promise<VoyantCloudEmailRendered> | VoyantCloudEmailRendered;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Notification provider that delivers email through the Voyant Cloud
|
|
24
|
+
* `/email/v1/messages` endpoint.
|
|
25
|
+
*/
|
|
26
|
+
export declare function createVoyantCloudEmailProvider(options: VoyantCloudEmailProviderOptions): NotificationProvider;
|
|
27
|
+
//# sourceMappingURL=voyant-cloud-email.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"voyant-cloud-email.d.ts","sourceRoot":"","sources":["../../src/providers/voyant-cloud-email.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAA;AAEjE,OAAO,KAAK,EAA0B,oBAAoB,EAAsB,MAAM,aAAa,CAAA;AAEnG,MAAM,WAAW,wBAAwB;IACvC,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,+BAA+B;IAC9C,mEAAmE;IACnE,MAAM,EAAE,iBAAiB,CAAA;IACzB,wDAAwD;IACxD,IAAI,EAAE,MAAM,CAAA;IACZ,0EAA0E;IAC1E,OAAO,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;IAC/B;;;;OAIG;IACH,cAAc,CAAC,EAAE,CACf,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,OAAO,KACV,OAAO,CAAC,wBAAwB,CAAC,GAAG,wBAAwB,CAAA;CAClE;AAsBD;;;GAGG;AACH,wBAAgB,8BAA8B,CAC5C,OAAO,EAAE,+BAA+B,GACvC,oBAAoB,CAqDtB"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
function mapAttachments(attachments) {
|
|
2
|
+
if (!attachments || attachments.length === 0)
|
|
3
|
+
return undefined;
|
|
4
|
+
return attachments.map((attachment) => ({
|
|
5
|
+
filename: attachment.filename,
|
|
6
|
+
...(attachment.contentBase64 ? { content: attachment.contentBase64 } : {}),
|
|
7
|
+
...(attachment.path ? { path: attachment.path } : {}),
|
|
8
|
+
...(attachment.contentType ? { contentType: attachment.contentType } : {}),
|
|
9
|
+
...(attachment.contentId ? { contentId: attachment.contentId } : {}),
|
|
10
|
+
}));
|
|
11
|
+
}
|
|
12
|
+
function attachEmailRequestContext(error, request) {
|
|
13
|
+
if (!error || typeof error !== "object")
|
|
14
|
+
return;
|
|
15
|
+
Object.defineProperty(error, "notificationRequest", {
|
|
16
|
+
configurable: true,
|
|
17
|
+
enumerable: true,
|
|
18
|
+
value: request,
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Notification provider that delivers email through the Voyant Cloud
|
|
23
|
+
* `/email/v1/messages` endpoint.
|
|
24
|
+
*/
|
|
25
|
+
export function createVoyantCloudEmailProvider(options) {
|
|
26
|
+
return {
|
|
27
|
+
name: "voyant-cloud-email",
|
|
28
|
+
channels: ["email"],
|
|
29
|
+
async send(payload) {
|
|
30
|
+
if (payload.channel !== "email") {
|
|
31
|
+
throw new Error(`Voyant Cloud email provider only supports the "email" channel, got "${payload.channel}"`);
|
|
32
|
+
}
|
|
33
|
+
const rendered = options.renderTemplate
|
|
34
|
+
? await options.renderTemplate(payload.template, payload.data)
|
|
35
|
+
: {
|
|
36
|
+
subject: payload.subject ?? payload.template,
|
|
37
|
+
text: JSON.stringify(payload.data ?? {}),
|
|
38
|
+
};
|
|
39
|
+
const attachments = mapAttachments(payload.attachments);
|
|
40
|
+
const request = {
|
|
41
|
+
from: payload.from ?? options.from,
|
|
42
|
+
to: [payload.to],
|
|
43
|
+
subject: payload.subject ?? rendered.subject,
|
|
44
|
+
html: payload.html ?? rendered.html ?? null,
|
|
45
|
+
text: payload.text ?? rendered.text ?? null,
|
|
46
|
+
...(attachments ? { attachments } : {}),
|
|
47
|
+
...(options.replyTo ? { replyTo: [...options.replyTo] } : {}),
|
|
48
|
+
};
|
|
49
|
+
let message;
|
|
50
|
+
try {
|
|
51
|
+
message = await options.client.email.sendMessage(request);
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
attachEmailRequestContext(error, {
|
|
55
|
+
from: request.from,
|
|
56
|
+
to: request.to,
|
|
57
|
+
replyTo: "replyTo" in request ? request.replyTo : null,
|
|
58
|
+
subject: request.subject,
|
|
59
|
+
attachmentCount: attachments?.length ?? 0,
|
|
60
|
+
attachments: attachments?.map((attachment) => ({
|
|
61
|
+
filename: attachment.filename,
|
|
62
|
+
path: attachment.path ?? null,
|
|
63
|
+
contentType: attachment.contentType ?? null,
|
|
64
|
+
contentId: attachment.contentId ?? null,
|
|
65
|
+
hasContent: Boolean(attachment.content),
|
|
66
|
+
})) ?? [],
|
|
67
|
+
});
|
|
68
|
+
throw error;
|
|
69
|
+
}
|
|
70
|
+
return { id: message.id, provider: "voyant-cloud-email" };
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { VoyantCloudClient } from "@voyant-travel/cloud-sdk";
|
|
2
|
+
import type { NotificationProvider } from "../types.js";
|
|
3
|
+
export interface VoyantCloudSmsRendered {
|
|
4
|
+
text: string;
|
|
5
|
+
}
|
|
6
|
+
export interface VoyantCloudSmsProviderOptions {
|
|
7
|
+
/** Cloud SDK client. Construct via `getVoyantCloudClient(env)`. */
|
|
8
|
+
client: VoyantCloudClient;
|
|
9
|
+
/**
|
|
10
|
+
* Default sender phone number. When omitted, the cloud account's default
|
|
11
|
+
* shared number is used (server-side selection).
|
|
12
|
+
*/
|
|
13
|
+
from?: string;
|
|
14
|
+
/**
|
|
15
|
+
* Render a template id + data tuple into an SMS body. When omitted, the
|
|
16
|
+
* payload's `text` field is used directly, falling back to a JSON dump of
|
|
17
|
+
* `data`.
|
|
18
|
+
*/
|
|
19
|
+
renderTemplate?: (template: string, data: unknown) => Promise<VoyantCloudSmsRendered> | VoyantCloudSmsRendered;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Notification provider that delivers SMS through the Voyant Cloud
|
|
23
|
+
* `/sms/v1/messages` endpoint.
|
|
24
|
+
*/
|
|
25
|
+
export declare function createVoyantCloudSmsProvider(options: VoyantCloudSmsProviderOptions): NotificationProvider;
|
|
26
|
+
//# sourceMappingURL=voyant-cloud-sms.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"voyant-cloud-sms.d.ts","sourceRoot":"","sources":["../../src/providers/voyant-cloud-sms.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAA;AAEjE,OAAO,KAAK,EAAE,oBAAoB,EAAsB,MAAM,aAAa,CAAA;AAE3E,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,MAAM,CAAA;CACb;AAED,MAAM,WAAW,6BAA6B;IAC5C,mEAAmE;IACnE,MAAM,EAAE,iBAAiB,CAAA;IACzB;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAA;IACb;;;;OAIG;IACH,cAAc,CAAC,EAAE,CACf,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,OAAO,KACV,OAAO,CAAC,sBAAsB,CAAC,GAAG,sBAAsB,CAAA;CAC9D;AAED;;;GAGG;AACH,wBAAgB,4BAA4B,CAC1C,OAAO,EAAE,6BAA6B,GACrC,oBAAoB,CAwBtB"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Notification provider that delivers SMS through the Voyant Cloud
|
|
3
|
+
* `/sms/v1/messages` endpoint.
|
|
4
|
+
*/
|
|
5
|
+
export function createVoyantCloudSmsProvider(options) {
|
|
6
|
+
return {
|
|
7
|
+
name: "voyant-cloud-sms",
|
|
8
|
+
channels: ["sms"],
|
|
9
|
+
async send(payload) {
|
|
10
|
+
if (payload.channel !== "sms") {
|
|
11
|
+
throw new Error(`Voyant Cloud SMS provider only supports the "sms" channel, got "${payload.channel}"`);
|
|
12
|
+
}
|
|
13
|
+
const rendered = options.renderTemplate
|
|
14
|
+
? await options.renderTemplate(payload.template, payload.data)
|
|
15
|
+
: { text: payload.text ?? JSON.stringify(payload.data ?? {}) };
|
|
16
|
+
const message = await options.client.sms.sendMessage({
|
|
17
|
+
to: payload.to,
|
|
18
|
+
from: payload.from ?? options.from ?? null,
|
|
19
|
+
body: payload.text ?? rendered.text,
|
|
20
|
+
});
|
|
21
|
+
return { id: message.id, provider: "voyant-cloud-sms" };
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
}
|