@voyantjs/notifications 0.3.0 → 0.4.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 (39) hide show
  1. package/README.md +22 -0
  2. package/dist/index.d.ts +8 -4
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +5 -3
  5. package/dist/provider-resolution.d.ts +5 -0
  6. package/dist/provider-resolution.d.ts.map +1 -1
  7. package/dist/provider-resolution.js +22 -0
  8. package/dist/providers/resend.d.ts.map +1 -1
  9. package/dist/providers/resend.js +8 -0
  10. package/dist/providers/twilio.d.ts +24 -0
  11. package/dist/providers/twilio.d.ts.map +1 -0
  12. package/dist/providers/twilio.js +48 -0
  13. package/dist/routes.d.ts +128 -10
  14. package/dist/routes.d.ts.map +1 -1
  15. package/dist/routes.js +42 -1
  16. package/dist/schema.d.ts +6 -6
  17. package/dist/schema.d.ts.map +1 -1
  18. package/dist/schema.js +1 -0
  19. package/dist/service-booking-documents.d.ts +119 -0
  20. package/dist/service-booking-documents.d.ts.map +1 -0
  21. package/dist/service-booking-documents.js +261 -0
  22. package/dist/service-deliveries.d.ts +5 -5
  23. package/dist/service-deliveries.d.ts.map +1 -1
  24. package/dist/service-deliveries.js +25 -2
  25. package/dist/service-reminders.d.ts.map +1 -1
  26. package/dist/service-reminders.js +187 -19
  27. package/dist/service-shared.d.ts +11 -2
  28. package/dist/service-shared.d.ts.map +1 -1
  29. package/dist/service-shared.js +12 -0
  30. package/dist/service-templates.d.ts +5 -5
  31. package/dist/service.d.ts +111 -1
  32. package/dist/service.d.ts.map +1 -1
  33. package/dist/service.js +6 -1
  34. package/dist/types.d.ts +22 -0
  35. package/dist/types.d.ts.map +1 -1
  36. package/dist/validation.d.ts +178 -6
  37. package/dist/validation.d.ts.map +1 -1
  38. package/dist/validation.js +64 -1
  39. package/package.json +7 -6
@@ -1,8 +1,8 @@
1
1
  import { bookingParticipants, bookings } from "@voyantjs/bookings/schema";
2
- import { bookingPaymentSchedules } from "@voyantjs/finance";
3
- import { and, desc, eq, or } from "drizzle-orm";
2
+ import { bookingPaymentSchedules, invoices } from "@voyantjs/finance";
3
+ import { and, desc, eq, gt, or } from "drizzle-orm";
4
4
  import { notificationReminderRules, notificationReminderRuns } from "./schema.js";
5
- import { sendNotification } from "./service-deliveries.js";
5
+ import { sendInvoiceNotification, sendNotification } from "./service-deliveries.js";
6
6
  import { addUtcDays, buildReminderDedupeKey, resolveReminderRecipient, startOfUtcDay, toDateString, toTimestamp, } from "./service-shared.js";
7
7
  async function sendBookingPaymentScheduleReminder(db, dispatcher, rule, schedule, now) {
8
8
  const runDate = toDateString(startOfUtcDay(now));
@@ -176,6 +176,151 @@ async function sendBookingPaymentScheduleReminder(db, dispatcher, rule, schedule
176
176
  return run ?? null;
177
177
  }
178
178
  }
179
+ async function sendInvoiceReminder(db, dispatcher, rule, invoice, now) {
180
+ const runDate = toDateString(startOfUtcDay(now));
181
+ const dedupeKey = buildReminderDedupeKey(rule.id, invoice.id, runDate);
182
+ const [existingRun] = await db
183
+ .select({ id: notificationReminderRuns.id })
184
+ .from(notificationReminderRuns)
185
+ .where(eq(notificationReminderRuns.dedupeKey, dedupeKey))
186
+ .limit(1);
187
+ if (existingRun) {
188
+ return null;
189
+ }
190
+ const [booking] = await db
191
+ .select()
192
+ .from(bookings)
193
+ .where(eq(bookings.id, invoice.bookingId))
194
+ .limit(1);
195
+ if (!booking) {
196
+ const [run] = await db
197
+ .insert(notificationReminderRuns)
198
+ .values({
199
+ reminderRuleId: rule.id,
200
+ targetType: "invoice",
201
+ targetId: invoice.id,
202
+ dedupeKey,
203
+ bookingId: invoice.bookingId,
204
+ personId: invoice.personId ?? null,
205
+ organizationId: invoice.organizationId ?? null,
206
+ paymentSessionId: null,
207
+ notificationDeliveryId: null,
208
+ status: "skipped",
209
+ recipient: null,
210
+ scheduledFor: now,
211
+ processedAt: now,
212
+ errorMessage: "Booking not found for invoice reminder",
213
+ metadata: {
214
+ dueDate: invoice.dueDate,
215
+ relativeDaysFromDueDate: rule.relativeDaysFromDueDate,
216
+ invoiceNumber: invoice.invoiceNumber,
217
+ invoiceType: invoice.invoiceType,
218
+ },
219
+ })
220
+ .returning();
221
+ return run ?? null;
222
+ }
223
+ const participants = await db
224
+ .select({
225
+ id: bookingParticipants.id,
226
+ firstName: bookingParticipants.firstName,
227
+ lastName: bookingParticipants.lastName,
228
+ email: bookingParticipants.email,
229
+ participantType: bookingParticipants.participantType,
230
+ isPrimary: bookingParticipants.isPrimary,
231
+ })
232
+ .from(bookingParticipants)
233
+ .where(eq(bookingParticipants.bookingId, booking.id))
234
+ .orderBy(desc(bookingParticipants.isPrimary), bookingParticipants.createdAt);
235
+ const recipient = resolveReminderRecipient(participants);
236
+ const [processingRun] = await db
237
+ .insert(notificationReminderRuns)
238
+ .values({
239
+ reminderRuleId: rule.id,
240
+ targetType: "invoice",
241
+ targetId: invoice.id,
242
+ dedupeKey,
243
+ bookingId: booking.id,
244
+ personId: invoice.personId ?? booking.personId ?? null,
245
+ organizationId: invoice.organizationId ?? booking.organizationId ?? null,
246
+ paymentSessionId: null,
247
+ notificationDeliveryId: null,
248
+ status: "processing",
249
+ recipient: recipient?.email ?? null,
250
+ scheduledFor: now,
251
+ processedAt: now,
252
+ errorMessage: null,
253
+ metadata: {
254
+ dueDate: invoice.dueDate,
255
+ relativeDaysFromDueDate: rule.relativeDaysFromDueDate,
256
+ bookingNumber: booking.bookingNumber,
257
+ invoiceNumber: invoice.invoiceNumber,
258
+ invoiceType: invoice.invoiceType,
259
+ },
260
+ })
261
+ .onConflictDoNothing({ target: notificationReminderRuns.dedupeKey })
262
+ .returning();
263
+ if (!processingRun) {
264
+ return null;
265
+ }
266
+ if (!recipient?.email) {
267
+ const [run] = await db
268
+ .update(notificationReminderRuns)
269
+ .set({
270
+ status: "skipped",
271
+ errorMessage: "No participant email available for invoice reminder",
272
+ processedAt: now,
273
+ updatedAt: now,
274
+ })
275
+ .where(eq(notificationReminderRuns.id, processingRun.id))
276
+ .returning();
277
+ return run ?? null;
278
+ }
279
+ try {
280
+ const delivery = await sendInvoiceNotification(db, dispatcher, invoice.id, {
281
+ templateId: rule.templateId ?? null,
282
+ templateSlug: rule.templateSlug ?? null,
283
+ channel: rule.channel,
284
+ provider: rule.provider ?? null,
285
+ to: recipient.email,
286
+ data: {
287
+ reminderOffsetDays: rule.relativeDaysFromDueDate,
288
+ reminderRunId: processingRun.id,
289
+ },
290
+ metadata: {
291
+ reminderRuleId: rule.id,
292
+ reminderRunId: processingRun.id,
293
+ },
294
+ scheduledFor: now.toISOString(),
295
+ });
296
+ const [run] = await db
297
+ .update(notificationReminderRuns)
298
+ .set({
299
+ notificationDeliveryId: delivery?.id ?? null,
300
+ status: "sent",
301
+ processedAt: new Date(),
302
+ updatedAt: new Date(),
303
+ errorMessage: null,
304
+ })
305
+ .where(eq(notificationReminderRuns.id, processingRun.id))
306
+ .returning();
307
+ return run ?? null;
308
+ }
309
+ catch (error) {
310
+ const message = error instanceof Error ? error.message : "Invoice reminder failed";
311
+ const [run] = await db
312
+ .update(notificationReminderRuns)
313
+ .set({
314
+ status: "failed",
315
+ errorMessage: message,
316
+ processedAt: new Date(),
317
+ updatedAt: new Date(),
318
+ })
319
+ .where(eq(notificationReminderRuns.id, processingRun.id))
320
+ .returning();
321
+ return run ?? null;
322
+ }
323
+ }
179
324
  export async function runDueReminders(db, dispatcher, input = {}) {
180
325
  const now = toTimestamp(input.now) ?? new Date();
181
326
  const today = startOfUtcDay(now);
@@ -192,23 +337,46 @@ export async function runDueReminders(db, dispatcher, input = {}) {
192
337
  };
193
338
  for (const rule of activeRules) {
194
339
  const matchingDueDate = toDateString(addUtcDays(today, -rule.relativeDaysFromDueDate));
195
- const schedules = await db
196
- .select()
197
- .from(bookingPaymentSchedules)
198
- .where(and(eq(bookingPaymentSchedules.dueDate, matchingDueDate), or(eq(bookingPaymentSchedules.status, "pending"), eq(bookingPaymentSchedules.status, "due"))))
199
- .orderBy(bookingPaymentSchedules.createdAt);
200
- for (const schedule of schedules) {
201
- const run = await sendBookingPaymentScheduleReminder(db, dispatcher, rule, schedule, now);
202
- if (!run) {
203
- continue;
340
+ if (rule.targetType === "booking_payment_schedule") {
341
+ const schedules = await db
342
+ .select()
343
+ .from(bookingPaymentSchedules)
344
+ .where(and(eq(bookingPaymentSchedules.dueDate, matchingDueDate), or(eq(bookingPaymentSchedules.status, "pending"), eq(bookingPaymentSchedules.status, "due"))))
345
+ .orderBy(bookingPaymentSchedules.createdAt);
346
+ for (const schedule of schedules) {
347
+ const run = await sendBookingPaymentScheduleReminder(db, dispatcher, rule, schedule, now);
348
+ if (!run) {
349
+ continue;
350
+ }
351
+ summary.processed += 1;
352
+ if (run.status === "sent")
353
+ summary.sent += 1;
354
+ if (run.status === "skipped")
355
+ summary.skipped += 1;
356
+ if (run.status === "failed")
357
+ summary.failed += 1;
358
+ }
359
+ continue;
360
+ }
361
+ if (rule.targetType === "invoice") {
362
+ const dueInvoices = await db
363
+ .select()
364
+ .from(invoices)
365
+ .where(and(eq(invoices.dueDate, matchingDueDate), gt(invoices.balanceDueCents, 0), or(eq(invoices.invoiceType, "invoice"), eq(invoices.invoiceType, "proforma")), or(eq(invoices.status, "sent"), eq(invoices.status, "partially_paid"), eq(invoices.status, "overdue"))))
366
+ .orderBy(invoices.createdAt);
367
+ for (const invoice of dueInvoices) {
368
+ const run = await sendInvoiceReminder(db, dispatcher, rule, invoice, now);
369
+ if (!run) {
370
+ continue;
371
+ }
372
+ summary.processed += 1;
373
+ if (run.status === "sent")
374
+ summary.sent += 1;
375
+ if (run.status === "skipped")
376
+ summary.skipped += 1;
377
+ if (run.status === "failed")
378
+ summary.failed += 1;
204
379
  }
205
- summary.processed += 1;
206
- if (run.status === "sent")
207
- summary.sent += 1;
208
- if (run.status === "skipped")
209
- summary.skipped += 1;
210
- if (run.status === "failed")
211
- summary.failed += 1;
212
380
  }
213
381
  }
214
382
  return summary;
@@ -3,8 +3,8 @@ import type { PostgresJsDatabase } from "drizzle-orm/postgres-js";
3
3
  import type { SQLWrapper } from "drizzle-orm/sql";
4
4
  import type { z } from "zod";
5
5
  import type { notificationReminderRules } from "./schema.js";
6
- import type { NotificationChannel, NotificationPayload, NotificationProvider, NotificationResult } from "./types.js";
7
- import type { insertNotificationReminderRuleSchema, insertNotificationTemplateSchema, notificationDeliveryListQuerySchema, notificationReminderRuleListQuerySchema, notificationReminderRunListQuerySchema, notificationTemplateListQuerySchema, runDueRemindersSchema, sendInvoiceNotificationSchema, sendNotificationSchema, sendPaymentSessionNotificationSchema, updateNotificationReminderRuleSchema, updateNotificationTemplateSchema } from "./validation.js";
6
+ import type { NotificationAttachment, NotificationChannel, NotificationPayload, NotificationProvider, NotificationResult } from "./types.js";
7
+ import type { bookingDocumentBundleItemSchema, insertNotificationReminderRuleSchema, insertNotificationTemplateSchema, notificationDeliveryListQuerySchema, notificationReminderRuleListQuerySchema, notificationReminderRunListQuerySchema, notificationTemplateListQuerySchema, runDueRemindersSchema, sendBookingDocumentsNotificationSchema, sendInvoiceNotificationSchema, sendNotificationSchema, sendPaymentSessionNotificationSchema, updateNotificationReminderRuleSchema, updateNotificationTemplateSchema } from "./validation.js";
8
8
  export type NotificationTemplateListQuery = z.infer<typeof notificationTemplateListQuerySchema>;
9
9
  export type NotificationDeliveryListQuery = z.infer<typeof notificationDeliveryListQuerySchema>;
10
10
  export type CreateNotificationTemplateInput = z.infer<typeof insertNotificationTemplateSchema>;
@@ -17,6 +17,8 @@ export type UpdateNotificationReminderRuleInput = z.infer<typeof updateNotificat
17
17
  export type RunDueRemindersInput = z.infer<typeof runDueRemindersSchema>;
18
18
  export type SendPaymentSessionNotificationInput = z.infer<typeof sendPaymentSessionNotificationSchema>;
19
19
  export type SendInvoiceNotificationInput = z.infer<typeof sendInvoiceNotificationSchema>;
20
+ export type SendBookingDocumentsNotificationInput = z.infer<typeof sendBookingDocumentsNotificationSchema>;
21
+ export type BookingDocumentBundleItem = z.infer<typeof bookingDocumentBundleItemSchema>;
20
22
  export type ReminderSweepResult = {
21
23
  processed: number;
22
24
  sent: number;
@@ -34,6 +36,13 @@ export interface NotificationService {
34
36
  getProvider(channel: NotificationChannel): NotificationProvider | undefined;
35
37
  }
36
38
  export declare function createNotificationService(providers: ReadonlyArray<NotificationProvider>): NotificationService;
39
+ export declare function summarizeNotificationAttachments(attachments: ReadonlyArray<NotificationAttachment> | null | undefined): {
40
+ filename: string;
41
+ path: string | null;
42
+ contentType: string | null;
43
+ disposition: "attachment" | "inline" | null;
44
+ contentId: string | null;
45
+ }[];
37
46
  export declare function renderNotificationTemplate(template: string | null | undefined, data: Record<string, unknown>): string | null;
38
47
  export declare function toTimestamp(value?: string | null): Date | null;
39
48
  export declare function startOfUtcDay(value: Date): Date;
@@ -1 +1 @@
1
- {"version":3,"file":"service-shared.d.ts","sourceRoot":"","sources":["../src/service-shared.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"service-shared.d.ts","sourceRoot":"","sources":["../src/service-shared.ts"],"names":[],"mappings":"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"}
@@ -36,6 +36,18 @@ export function createNotificationService(providers) {
36
36
  },
37
37
  };
38
38
  }
39
+ export function summarizeNotificationAttachments(attachments) {
40
+ if (!attachments || attachments.length === 0) {
41
+ return [];
42
+ }
43
+ return attachments.map((attachment) => ({
44
+ filename: attachment.filename,
45
+ path: attachment.path ?? null,
46
+ contentType: attachment.contentType ?? null,
47
+ disposition: attachment.disposition ?? null,
48
+ contentId: attachment.contentId ?? null,
49
+ }));
50
+ }
39
51
  function resolveMustachePath(path, scope) {
40
52
  const parts = path.match(/[^.[\]]+/g) ?? [];
41
53
  let current = scope;
@@ -91,7 +91,7 @@ export declare function listReminderRules(db: PostgresJsDatabase, query: Notific
91
91
  slug: string;
92
92
  name: string;
93
93
  status: "draft" | "active" | "archived";
94
- targetType: "booking_payment_schedule";
94
+ targetType: "invoice" | "booking_payment_schedule";
95
95
  channel: "email" | "sms";
96
96
  provider: string | null;
97
97
  templateId: string | null;
@@ -111,7 +111,7 @@ export declare function getReminderRuleById(db: PostgresJsDatabase, id: string):
111
111
  slug: string;
112
112
  name: string;
113
113
  status: "draft" | "active" | "archived";
114
- targetType: "booking_payment_schedule";
114
+ targetType: "invoice" | "booking_payment_schedule";
115
115
  channel: "email" | "sms";
116
116
  provider: string | null;
117
117
  templateId: string | null;
@@ -131,7 +131,7 @@ export declare function createReminderRule(db: PostgresJsDatabase, data: CreateN
131
131
  metadata: Record<string, unknown> | null;
132
132
  channel: "email" | "sms";
133
133
  provider: string | null;
134
- targetType: "booking_payment_schedule";
134
+ targetType: "invoice" | "booking_payment_schedule";
135
135
  templateId: string | null;
136
136
  slug: string;
137
137
  isSystem: boolean;
@@ -143,7 +143,7 @@ export declare function updateReminderRule(db: PostgresJsDatabase, id: string, d
143
143
  slug: string;
144
144
  name: string;
145
145
  status: "draft" | "active" | "archived";
146
- targetType: "booking_payment_schedule";
146
+ targetType: "invoice" | "booking_payment_schedule";
147
147
  channel: "email" | "sms";
148
148
  provider: string | null;
149
149
  templateId: string | null;
@@ -158,7 +158,7 @@ export declare function listReminderRuns(db: PostgresJsDatabase, query: Notifica
158
158
  data: {
159
159
  id: string;
160
160
  reminderRuleId: string;
161
- targetType: "booking_payment_schedule";
161
+ targetType: "invoice" | "booking_payment_schedule";
162
162
  targetId: string;
163
163
  dedupeKey: string;
164
164
  bookingId: string | null;
package/dist/service.d.ts CHANGED
@@ -1,5 +1,7 @@
1
+ export { createDefaultBookingDocumentAttachment } from "./service-booking-documents.js";
1
2
  export type { NotificationService } from "./service-shared.js";
2
- export { createNotificationService, NotificationError, renderNotificationTemplate, } from "./service-shared.js";
3
+ export { createNotificationService, NotificationError, renderNotificationTemplate, summarizeNotificationAttachments, } from "./service-shared.js";
4
+ import { createDefaultBookingDocumentAttachment } from "./service-booking-documents.js";
3
5
  import { getDeliveryById, listDeliveries, sendInvoiceNotification, sendNotification, sendPaymentSessionNotification } from "./service-deliveries.js";
4
6
  import { runDueReminders } from "./service-reminders.js";
5
7
  import { createReminderRule, createTemplate, getReminderRuleById, getTemplateById, getTemplateBySlug, listReminderRules, listReminderRuns, listTemplates, updateReminderRule, updateTemplate } from "./service-templates.js";
@@ -20,5 +22,113 @@ export declare const notificationsService: {
20
22
  runDueReminders: typeof runDueReminders;
21
23
  sendPaymentSessionNotification: typeof sendPaymentSessionNotification;
22
24
  sendInvoiceNotification: typeof sendInvoiceNotification;
25
+ listBookingDocumentBundle: (db: import("drizzle-orm/postgres-js").PostgresJsDatabase, bookingId: string) => Promise<{
26
+ bookingId: string;
27
+ documents: {
28
+ key: string;
29
+ source: "finance" | "legal";
30
+ documentType: "invoice" | "proforma" | "contract";
31
+ bookingId: string;
32
+ name: string;
33
+ createdAt: string;
34
+ contractId?: string | null | undefined;
35
+ invoiceId?: string | null | undefined;
36
+ attachmentId?: string | null | undefined;
37
+ renditionId?: string | null | undefined;
38
+ contractStatus?: string | null | undefined;
39
+ invoiceStatus?: string | null | undefined;
40
+ format?: string | null | undefined;
41
+ mimeType?: string | null | undefined;
42
+ storageKey?: string | null | undefined;
43
+ downloadUrl?: string | null | undefined;
44
+ language?: string | null | undefined;
45
+ metadata?: Record<string, unknown> | null | undefined;
46
+ }[];
47
+ } | null>;
48
+ sendBookingDocumentsNotification: (db: import("drizzle-orm/postgres-js").PostgresJsDatabase, dispatcher: import("./service-shared.js").NotificationService, bookingId: string, input: import("./service-shared.js").SendBookingDocumentsNotificationInput, runtime?: import("./service-booking-documents.js").SendBookingDocumentsRuntimeOptions) => Promise<{
49
+ status: "not_found";
50
+ bookingId?: undefined;
51
+ recipient?: undefined;
52
+ documents?: undefined;
53
+ delivery?: undefined;
54
+ } | {
55
+ status: "no_documents";
56
+ bookingId?: undefined;
57
+ recipient?: undefined;
58
+ documents?: undefined;
59
+ delivery?: undefined;
60
+ } | {
61
+ status: "no_recipient";
62
+ bookingId?: undefined;
63
+ recipient?: undefined;
64
+ documents?: undefined;
65
+ delivery?: undefined;
66
+ } | {
67
+ status: "no_attachments";
68
+ bookingId?: undefined;
69
+ recipient?: undefined;
70
+ documents?: undefined;
71
+ delivery?: undefined;
72
+ } | {
73
+ status: "send_failed";
74
+ bookingId?: undefined;
75
+ recipient?: undefined;
76
+ documents?: undefined;
77
+ delivery?: undefined;
78
+ } | {
79
+ status: "sent";
80
+ bookingId: string;
81
+ recipient: string;
82
+ documents: {
83
+ key: string;
84
+ source: "finance" | "legal";
85
+ documentType: "invoice" | "proforma" | "contract";
86
+ bookingId: string;
87
+ name: string;
88
+ createdAt: string;
89
+ contractId?: string | null | undefined;
90
+ invoiceId?: string | null | undefined;
91
+ attachmentId?: string | null | undefined;
92
+ renditionId?: string | null | undefined;
93
+ contractStatus?: string | null | undefined;
94
+ invoiceStatus?: string | null | undefined;
95
+ format?: string | null | undefined;
96
+ mimeType?: string | null | undefined;
97
+ storageKey?: string | null | undefined;
98
+ downloadUrl?: string | null | undefined;
99
+ language?: string | null | undefined;
100
+ metadata?: Record<string, unknown> | null | undefined;
101
+ }[];
102
+ delivery: {
103
+ id: string;
104
+ templateId: string | null;
105
+ templateSlug: string | null;
106
+ targetType: "other" | "booking" | "invoice" | "booking_payment_schedule" | "booking_guarantee" | "organization" | "person" | "payment_session";
107
+ targetId: string | null;
108
+ personId: string | null;
109
+ organizationId: string | null;
110
+ bookingId: string | null;
111
+ invoiceId: string | null;
112
+ paymentSessionId: string | null;
113
+ channel: "email" | "sms";
114
+ provider: string;
115
+ providerMessageId: string | null;
116
+ status: "cancelled" | "pending" | "failed" | "sent";
117
+ toAddress: string;
118
+ fromAddress: string | null;
119
+ subject: string | null;
120
+ htmlBody: string | null;
121
+ textBody: string | null;
122
+ payloadData: Record<string, unknown> | null;
123
+ metadata: Record<string, unknown> | null;
124
+ errorMessage: string | null;
125
+ scheduledFor: Date | null;
126
+ sentAt: Date | null;
127
+ failedAt: Date | null;
128
+ createdAt: Date;
129
+ updatedAt: Date;
130
+ };
131
+ }>;
132
+ createDefaultBookingDocumentAttachment: typeof createDefaultBookingDocumentAttachment;
23
133
  };
24
134
  //# sourceMappingURL=service.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAA;AAC9D,OAAO,EACL,yBAAyB,EACzB,iBAAiB,EACjB,0BAA0B,GAC3B,MAAM,qBAAqB,CAAA;AAE5B,OAAO,EACL,eAAe,EACf,cAAc,EACd,uBAAuB,EACvB,gBAAgB,EAChB,8BAA8B,EAC/B,MAAM,yBAAyB,CAAA;AAChC,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAA;AACxD,OAAO,EACL,kBAAkB,EAClB,cAAc,EACd,mBAAmB,EACnB,eAAe,EACf,iBAAiB,EACjB,iBAAiB,EACjB,gBAAgB,EAChB,aAAa,EACb,kBAAkB,EAClB,cAAc,EACf,MAAM,wBAAwB,CAAA;AAE/B,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;CAiBhC,CAAA"}
1
+ {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sCAAsC,EAAE,MAAM,gCAAgC,CAAA;AACvF,YAAY,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAA;AAC9D,OAAO,EACL,yBAAyB,EACzB,iBAAiB,EACjB,0BAA0B,EAC1B,gCAAgC,GACjC,MAAM,qBAAqB,CAAA;AAE5B,OAAO,EAEL,sCAAsC,EACvC,MAAM,gCAAgC,CAAA;AACvC,OAAO,EACL,eAAe,EACf,cAAc,EACd,uBAAuB,EACvB,gBAAgB,EAChB,8BAA8B,EAC/B,MAAM,yBAAyB,CAAA;AAChC,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAA;AACxD,OAAO,EACL,kBAAkB,EAClB,cAAc,EACd,mBAAmB,EACnB,eAAe,EACf,iBAAiB,EACjB,iBAAiB,EACjB,gBAAgB,EAChB,aAAa,EACb,kBAAkB,EAClB,cAAc,EACf,MAAM,wBAAwB,CAAA;AAE/B,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqBhC,CAAA"}
package/dist/service.js CHANGED
@@ -1,4 +1,6 @@
1
- export { createNotificationService, NotificationError, renderNotificationTemplate, } from "./service-shared.js";
1
+ export { createDefaultBookingDocumentAttachment } from "./service-booking-documents.js";
2
+ export { createNotificationService, NotificationError, renderNotificationTemplate, summarizeNotificationAttachments, } from "./service-shared.js";
3
+ import { bookingDocumentNotificationsService, createDefaultBookingDocumentAttachment, } from "./service-booking-documents.js";
2
4
  import { getDeliveryById, listDeliveries, sendInvoiceNotification, sendNotification, sendPaymentSessionNotification, } from "./service-deliveries.js";
3
5
  import { runDueReminders } from "./service-reminders.js";
4
6
  import { createReminderRule, createTemplate, getReminderRuleById, getTemplateById, getTemplateBySlug, listReminderRules, listReminderRuns, listTemplates, updateReminderRule, updateTemplate, } from "./service-templates.js";
@@ -19,4 +21,7 @@ export const notificationsService = {
19
21
  runDueReminders,
20
22
  sendPaymentSessionNotification,
21
23
  sendInvoiceNotification,
24
+ listBookingDocumentBundle: bookingDocumentNotificationsService.listBookingDocumentBundle,
25
+ sendBookingDocumentsNotification: bookingDocumentNotificationsService.sendBookingDocumentsNotification,
26
+ createDefaultBookingDocumentAttachment,
22
27
  };
package/dist/types.d.ts CHANGED
@@ -4,6 +4,26 @@
4
4
  * identifiers (e.g. `"slack"`, `"push"`).
5
5
  */
6
6
  export type NotificationChannel = "email" | "sms" | (string & {});
7
+ /**
8
+ * Attachment payload for channels that support file delivery, such as email.
9
+ *
10
+ * Use `contentBase64` when the caller already has the rendered bytes, or `path`
11
+ * when the downstream provider can fetch the attachment from a URL/file path.
12
+ */
13
+ export interface NotificationAttachment {
14
+ /** User-visible file name. */
15
+ filename: string;
16
+ /** Base64-encoded content for inline upload. */
17
+ contentBase64?: string;
18
+ /** Provider-resolvable URL or path. */
19
+ path?: string;
20
+ /** MIME type hint. */
21
+ contentType?: string;
22
+ /** Optional disposition override. */
23
+ disposition?: "attachment" | "inline";
24
+ /** Optional inline content id. */
25
+ contentId?: string;
26
+ }
7
27
  /**
8
28
  * Payload describing a single notification to send. The `template` and
9
29
  * `data` fields are interpreted by the handling provider.
@@ -27,6 +47,8 @@ export interface NotificationPayload {
27
47
  html?: string;
28
48
  /** Optional pre-rendered text body. */
29
49
  text?: string;
50
+ /** Optional attachments for providers that support them. */
51
+ attachments?: ReadonlyArray<NotificationAttachment>;
30
52
  }
31
53
  /**
32
54
  * Result returned after a provider handles a send.
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,MAAM,mBAAmB,GAAG,OAAO,GAAG,KAAK,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC,CAAA;AAEjE;;;GAGG;AACH,MAAM,WAAW,mBAAmB;IAClC,wEAAwE;IACxE,EAAE,EAAE,MAAM,CAAA;IACV,yCAAyC;IACzC,OAAO,EAAE,mBAAmB,CAAA;IAC5B,wEAAwE;IACxE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,iEAAiE;IACjE,QAAQ,EAAE,MAAM,CAAA;IAChB,iDAAiD;IACjD,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,uEAAuE;IACvE,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,0CAA0C;IAC1C,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,uCAAuC;IACvC,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,uCAAuC;IACvC,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,uDAAuD;IACvD,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,kDAAkD;IAClD,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,oBAAoB;IACnC,+DAA+D;IAC/D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,yCAAyC;IACzC,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC,mBAAmB,CAAC,CAAA;IACrD,mDAAmD;IACnD,IAAI,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAA;CAChE"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,MAAM,mBAAmB,GAAG,OAAO,GAAG,KAAK,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC,CAAA;AAEjE;;;;;GAKG;AACH,MAAM,WAAW,sBAAsB;IACrC,8BAA8B;IAC9B,QAAQ,EAAE,MAAM,CAAA;IAChB,gDAAgD;IAChD,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,uCAAuC;IACvC,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,sBAAsB;IACtB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,qCAAqC;IACrC,WAAW,CAAC,EAAE,YAAY,GAAG,QAAQ,CAAA;IACrC,kCAAkC;IAClC,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED;;;GAGG;AACH,MAAM,WAAW,mBAAmB;IAClC,wEAAwE;IACxE,EAAE,EAAE,MAAM,CAAA;IACV,yCAAyC;IACzC,OAAO,EAAE,mBAAmB,CAAA;IAC5B,wEAAwE;IACxE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,iEAAiE;IACjE,QAAQ,EAAE,MAAM,CAAA;IAChB,iDAAiD;IACjD,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,uEAAuE;IACvE,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,0CAA0C;IAC1C,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,uCAAuC;IACvC,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,uCAAuC;IACvC,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,4DAA4D;IAC5D,WAAW,CAAC,EAAE,aAAa,CAAC,sBAAsB,CAAC,CAAA;CACpD;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,uDAAuD;IACvD,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,kDAAkD;IAClD,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,oBAAoB;IACnC,+DAA+D;IAC/D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,yCAAyC;IACzC,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC,mBAAmB,CAAC,CAAA;IACrD,mDAAmD;IACnD,IAAI,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAA;CAChE"}