@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
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { bookings, bookingTravelers } from "@voyant-travel/bookings/schema";
|
|
2
|
+
import { and, desc, eq } from "drizzle-orm";
|
|
3
|
+
import { notificationReminderRules, notificationReminderRuns } from "./schema.js";
|
|
4
|
+
import { sendNotification } from "./service-deliveries.js";
|
|
5
|
+
import { getBookingEventDocumentContext, getBookingPaymentNotificationContext, hasOutstandingBookingBalance, serializeBookingPaymentContext, } from "./service-reminder-booking-context.js";
|
|
6
|
+
import { markReminderRunFailed, markReminderRunSent, markReminderRunSkipped, } from "./service-reminder-run-state.js";
|
|
7
|
+
import { buildReminderDedupeKey, listBookingNotificationItems, resolveReminderRecipient, } from "./service-shared.js";
|
|
8
|
+
async function sendBookingEventReminder(db, dispatcher, rule, input, runtime = {}) {
|
|
9
|
+
const now = new Date();
|
|
10
|
+
const dedupeKey = buildReminderDedupeKey(rule.id, input.bookingId, input.targetType);
|
|
11
|
+
const [existingRun] = await db
|
|
12
|
+
.select({ id: notificationReminderRuns.id })
|
|
13
|
+
.from(notificationReminderRuns)
|
|
14
|
+
.where(eq(notificationReminderRuns.dedupeKey, dedupeKey))
|
|
15
|
+
.limit(1);
|
|
16
|
+
if (existingRun) {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
const [booking] = await db
|
|
20
|
+
.select()
|
|
21
|
+
.from(bookings)
|
|
22
|
+
.where(eq(bookings.id, input.bookingId))
|
|
23
|
+
.limit(1);
|
|
24
|
+
if (!booking) {
|
|
25
|
+
const [run] = await db
|
|
26
|
+
.insert(notificationReminderRuns)
|
|
27
|
+
.values({
|
|
28
|
+
reminderRuleId: rule.id,
|
|
29
|
+
targetType: input.targetType,
|
|
30
|
+
targetId: input.bookingId,
|
|
31
|
+
dedupeKey,
|
|
32
|
+
bookingId: input.bookingId,
|
|
33
|
+
personId: null,
|
|
34
|
+
organizationId: null,
|
|
35
|
+
paymentSessionId: input.paymentSessionId ?? null,
|
|
36
|
+
notificationDeliveryId: null,
|
|
37
|
+
status: "skipped",
|
|
38
|
+
recipient: null,
|
|
39
|
+
scheduledFor: now,
|
|
40
|
+
processedAt: now,
|
|
41
|
+
errorMessage: "Booking not found for notification event",
|
|
42
|
+
metadata: {
|
|
43
|
+
eventTargetType: input.targetType,
|
|
44
|
+
...(input.eventData ?? {}),
|
|
45
|
+
},
|
|
46
|
+
})
|
|
47
|
+
.returning();
|
|
48
|
+
return run ?? null;
|
|
49
|
+
}
|
|
50
|
+
const [participants, items, paymentContext, documentContext] = await Promise.all([
|
|
51
|
+
db
|
|
52
|
+
.select({
|
|
53
|
+
id: bookingTravelers.id,
|
|
54
|
+
firstName: bookingTravelers.firstName,
|
|
55
|
+
lastName: bookingTravelers.lastName,
|
|
56
|
+
email: bookingTravelers.email,
|
|
57
|
+
participantType: bookingTravelers.participantType,
|
|
58
|
+
isPrimary: bookingTravelers.isPrimary,
|
|
59
|
+
})
|
|
60
|
+
.from(bookingTravelers)
|
|
61
|
+
.where(eq(bookingTravelers.bookingId, booking.id))
|
|
62
|
+
.orderBy(desc(bookingTravelers.isPrimary), bookingTravelers.createdAt),
|
|
63
|
+
listBookingNotificationItems(db, booking.id),
|
|
64
|
+
getBookingPaymentNotificationContext(db, booking.id),
|
|
65
|
+
input.targetType === "booking_confirmed" && rule.channel === "email"
|
|
66
|
+
? getBookingEventDocumentContext(db, booking.id, runtime.documentAttachmentResolver)
|
|
67
|
+
: Promise.resolve({ documents: [], attachments: [] }),
|
|
68
|
+
]);
|
|
69
|
+
const recipient = resolveReminderRecipient(booking, participants);
|
|
70
|
+
const [processingRun] = await db
|
|
71
|
+
.insert(notificationReminderRuns)
|
|
72
|
+
.values({
|
|
73
|
+
reminderRuleId: rule.id,
|
|
74
|
+
targetType: input.targetType,
|
|
75
|
+
targetId: booking.id,
|
|
76
|
+
dedupeKey,
|
|
77
|
+
bookingId: booking.id,
|
|
78
|
+
personId: booking.personId ?? null,
|
|
79
|
+
organizationId: booking.organizationId ?? null,
|
|
80
|
+
paymentSessionId: input.paymentSessionId ?? null,
|
|
81
|
+
notificationDeliveryId: null,
|
|
82
|
+
status: "processing",
|
|
83
|
+
recipient: recipient?.email ?? null,
|
|
84
|
+
scheduledFor: now,
|
|
85
|
+
processedAt: now,
|
|
86
|
+
errorMessage: null,
|
|
87
|
+
metadata: {
|
|
88
|
+
eventTargetType: input.targetType,
|
|
89
|
+
bookingNumber: booking.bookingNumber,
|
|
90
|
+
...(input.eventData ?? {}),
|
|
91
|
+
},
|
|
92
|
+
})
|
|
93
|
+
.onConflictDoNothing({ target: notificationReminderRuns.dedupeKey })
|
|
94
|
+
.returning();
|
|
95
|
+
if (!processingRun) {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
if (!recipient?.email) {
|
|
99
|
+
return markReminderRunSkipped(db, processingRun.id, now, "No traveler email available for booking notification event");
|
|
100
|
+
}
|
|
101
|
+
try {
|
|
102
|
+
const delivery = await sendNotification(db, dispatcher, {
|
|
103
|
+
templateId: rule.templateId ?? null,
|
|
104
|
+
templateSlug: rule.templateSlug ?? null,
|
|
105
|
+
channel: rule.channel,
|
|
106
|
+
provider: rule.provider ?? null,
|
|
107
|
+
to: recipient.email,
|
|
108
|
+
data: {
|
|
109
|
+
bookingId: booking.id,
|
|
110
|
+
bookingNumber: booking.bookingNumber,
|
|
111
|
+
trigger: input.targetType,
|
|
112
|
+
event: input.eventData ?? {},
|
|
113
|
+
traveler: {
|
|
114
|
+
firstName: recipient.firstName,
|
|
115
|
+
lastName: recipient.lastName,
|
|
116
|
+
email: recipient.email,
|
|
117
|
+
participantType: recipient.participantType,
|
|
118
|
+
isPrimary: recipient.isPrimary,
|
|
119
|
+
},
|
|
120
|
+
travelers: participants,
|
|
121
|
+
booking: {
|
|
122
|
+
id: booking.id,
|
|
123
|
+
bookingNumber: booking.bookingNumber,
|
|
124
|
+
status: booking.status,
|
|
125
|
+
startDate: booking.startDate,
|
|
126
|
+
endDate: booking.endDate,
|
|
127
|
+
sellCurrency: booking.sellCurrency,
|
|
128
|
+
sellAmountCents: booking.sellAmountCents,
|
|
129
|
+
},
|
|
130
|
+
...serializeBookingPaymentContext(paymentContext),
|
|
131
|
+
payment: input.targetType === "payment_complete"
|
|
132
|
+
? {
|
|
133
|
+
isPaidInFull: true,
|
|
134
|
+
paymentSessionId: input.paymentSessionId ?? null,
|
|
135
|
+
}
|
|
136
|
+
: null,
|
|
137
|
+
documents: documentContext.documents,
|
|
138
|
+
items,
|
|
139
|
+
},
|
|
140
|
+
attachments: documentContext.attachments.length > 0 ? documentContext.attachments : null,
|
|
141
|
+
targetType: input.targetType === "payment_complete" ? "payment_session" : "booking",
|
|
142
|
+
targetId: input.targetType === "payment_complete"
|
|
143
|
+
? (input.paymentSessionId ?? booking.id)
|
|
144
|
+
: booking.id,
|
|
145
|
+
bookingId: booking.id,
|
|
146
|
+
paymentSessionId: input.paymentSessionId ?? null,
|
|
147
|
+
personId: booking.personId ?? null,
|
|
148
|
+
organizationId: booking.organizationId ?? null,
|
|
149
|
+
metadata: {
|
|
150
|
+
reminderRuleId: rule.id,
|
|
151
|
+
reminderRunId: processingRun.id,
|
|
152
|
+
eventTargetType: input.targetType,
|
|
153
|
+
bookingDocumentKeys: documentContext.documents.map((document) => document.key),
|
|
154
|
+
},
|
|
155
|
+
scheduledFor: now.toISOString(),
|
|
156
|
+
});
|
|
157
|
+
return markReminderRunSent(db, processingRun.id, new Date(), delivery?.id ?? null);
|
|
158
|
+
}
|
|
159
|
+
catch (error) {
|
|
160
|
+
const message = error instanceof Error ? error.message : "Notification event delivery failed";
|
|
161
|
+
return markReminderRunFailed(db, processingRun.id, new Date(), message);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
export async function dispatchReminderEventRules(db, dispatcher, input, runtime = {}) {
|
|
165
|
+
const rules = await db
|
|
166
|
+
.select()
|
|
167
|
+
.from(notificationReminderRules)
|
|
168
|
+
.where(and(eq(notificationReminderRules.status, "active"), eq(notificationReminderRules.targetType, input.targetType)))
|
|
169
|
+
.orderBy(notificationReminderRules.createdAt);
|
|
170
|
+
const results = [];
|
|
171
|
+
for (const rule of rules) {
|
|
172
|
+
results.push(await sendBookingEventReminder(db, dispatcher, rule, input, runtime));
|
|
173
|
+
}
|
|
174
|
+
return results;
|
|
175
|
+
}
|
|
176
|
+
export async function bookingIsPaidInFullForNotification(db, bookingId) {
|
|
177
|
+
return !(await hasOutstandingBookingBalance(db, bookingId));
|
|
178
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import type { PostgresJsDatabase } from "drizzle-orm/postgres-js";
|
|
2
|
+
import { notificationReminderRuns } from "./schema.js";
|
|
3
|
+
import type { NotificationReminderRuleRow, ReminderQueueResult, ReminderSweepResult } from "./service-shared.js";
|
|
4
|
+
export type ReminderDeliveryEnqueuer = (input: {
|
|
5
|
+
reminderRunId: string;
|
|
6
|
+
}) => Promise<void>;
|
|
7
|
+
export type NotificationReminderRunRow = typeof notificationReminderRuns.$inferSelect;
|
|
8
|
+
export declare function buildReminderSweepSummary(): ReminderSweepResult;
|
|
9
|
+
export declare function buildReminderQueueSummary(): ReminderQueueResult;
|
|
10
|
+
export declare function getReminderRuleById(db: PostgresJsDatabase, reminderRuleId: string): Promise<{
|
|
11
|
+
id: string;
|
|
12
|
+
slug: string;
|
|
13
|
+
name: string;
|
|
14
|
+
status: "draft" | "active" | "archived";
|
|
15
|
+
targetType: "invoice" | "booking_payment_schedule" | "booking_confirmed" | "payment_complete" | "booking_cancelled_non_payment";
|
|
16
|
+
channel: "email" | "sms";
|
|
17
|
+
provider: string | null;
|
|
18
|
+
templateId: string | null;
|
|
19
|
+
templateSlug: string | null;
|
|
20
|
+
priority: number;
|
|
21
|
+
suppressionGroup: string | null;
|
|
22
|
+
isSystem: boolean;
|
|
23
|
+
metadata: Record<string, unknown> | null;
|
|
24
|
+
createdAt: Date;
|
|
25
|
+
updatedAt: Date;
|
|
26
|
+
} | null>;
|
|
27
|
+
export declare function getReminderRunById(db: PostgresJsDatabase, reminderRunId: string): Promise<{
|
|
28
|
+
id: string;
|
|
29
|
+
reminderRuleId: string;
|
|
30
|
+
targetType: "invoice" | "booking_payment_schedule" | "booking_confirmed" | "payment_complete" | "booking_cancelled_non_payment";
|
|
31
|
+
targetId: string;
|
|
32
|
+
dedupeKey: string;
|
|
33
|
+
bookingId: string | null;
|
|
34
|
+
personId: string | null;
|
|
35
|
+
organizationId: string | null;
|
|
36
|
+
paymentSessionId: string | null;
|
|
37
|
+
notificationDeliveryId: string | null;
|
|
38
|
+
status: "sent" | "failed" | "queued" | "processing" | "skipped";
|
|
39
|
+
recipient: string | null;
|
|
40
|
+
scheduledFor: Date;
|
|
41
|
+
processedAt: Date;
|
|
42
|
+
errorMessage: string | null;
|
|
43
|
+
metadata: Record<string, unknown> | null;
|
|
44
|
+
createdAt: Date;
|
|
45
|
+
updatedAt: Date;
|
|
46
|
+
} | null>;
|
|
47
|
+
export declare function markReminderRunSkipped(db: PostgresJsDatabase, reminderRunId: string, now: Date, errorMessage: string): Promise<{
|
|
48
|
+
id: string;
|
|
49
|
+
reminderRuleId: string;
|
|
50
|
+
targetType: "invoice" | "booking_payment_schedule" | "booking_confirmed" | "payment_complete" | "booking_cancelled_non_payment";
|
|
51
|
+
targetId: string;
|
|
52
|
+
dedupeKey: string;
|
|
53
|
+
bookingId: string | null;
|
|
54
|
+
personId: string | null;
|
|
55
|
+
organizationId: string | null;
|
|
56
|
+
paymentSessionId: string | null;
|
|
57
|
+
notificationDeliveryId: string | null;
|
|
58
|
+
status: "sent" | "failed" | "queued" | "processing" | "skipped";
|
|
59
|
+
recipient: string | null;
|
|
60
|
+
scheduledFor: Date;
|
|
61
|
+
processedAt: Date;
|
|
62
|
+
errorMessage: string | null;
|
|
63
|
+
metadata: Record<string, unknown> | null;
|
|
64
|
+
createdAt: Date;
|
|
65
|
+
updatedAt: Date;
|
|
66
|
+
} | null>;
|
|
67
|
+
export declare function markReminderRunFailed(db: PostgresJsDatabase, reminderRunId: string, now: Date, errorMessage: string): Promise<{
|
|
68
|
+
id: string;
|
|
69
|
+
reminderRuleId: string;
|
|
70
|
+
targetType: "invoice" | "booking_payment_schedule" | "booking_confirmed" | "payment_complete" | "booking_cancelled_non_payment";
|
|
71
|
+
targetId: string;
|
|
72
|
+
dedupeKey: string;
|
|
73
|
+
bookingId: string | null;
|
|
74
|
+
personId: string | null;
|
|
75
|
+
organizationId: string | null;
|
|
76
|
+
paymentSessionId: string | null;
|
|
77
|
+
notificationDeliveryId: string | null;
|
|
78
|
+
status: "sent" | "failed" | "queued" | "processing" | "skipped";
|
|
79
|
+
recipient: string | null;
|
|
80
|
+
scheduledFor: Date;
|
|
81
|
+
processedAt: Date;
|
|
82
|
+
errorMessage: string | null;
|
|
83
|
+
metadata: Record<string, unknown> | null;
|
|
84
|
+
createdAt: Date;
|
|
85
|
+
updatedAt: Date;
|
|
86
|
+
} | null>;
|
|
87
|
+
export declare function markReminderRunSent(db: PostgresJsDatabase, reminderRunId: string, now: Date, notificationDeliveryId: string | null): Promise<{
|
|
88
|
+
id: string;
|
|
89
|
+
reminderRuleId: string;
|
|
90
|
+
targetType: "invoice" | "booking_payment_schedule" | "booking_confirmed" | "payment_complete" | "booking_cancelled_non_payment";
|
|
91
|
+
targetId: string;
|
|
92
|
+
dedupeKey: string;
|
|
93
|
+
bookingId: string | null;
|
|
94
|
+
personId: string | null;
|
|
95
|
+
organizationId: string | null;
|
|
96
|
+
paymentSessionId: string | null;
|
|
97
|
+
notificationDeliveryId: string | null;
|
|
98
|
+
status: "sent" | "failed" | "queued" | "processing" | "skipped";
|
|
99
|
+
recipient: string | null;
|
|
100
|
+
scheduledFor: Date;
|
|
101
|
+
processedAt: Date;
|
|
102
|
+
errorMessage: string | null;
|
|
103
|
+
metadata: Record<string, unknown> | null;
|
|
104
|
+
createdAt: Date;
|
|
105
|
+
updatedAt: Date;
|
|
106
|
+
} | null>;
|
|
107
|
+
export type ChannelOverride = {
|
|
108
|
+
channel: "email" | "sms";
|
|
109
|
+
templateId: string | null;
|
|
110
|
+
templateSlug: string | null;
|
|
111
|
+
provider: string | null;
|
|
112
|
+
};
|
|
113
|
+
export declare function resolveChannelOverride(db: PostgresJsDatabase, run: NotificationReminderRunRow, rule: NotificationReminderRuleRow): Promise<ChannelOverride>;
|
|
114
|
+
//# sourceMappingURL=service-reminder-run-state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service-reminder-run-state.d.ts","sourceRoot":"","sources":["../src/service-reminder-run-state.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAEjE,OAAO,EAEL,wBAAwB,EAEzB,MAAM,aAAa,CAAA;AACpB,OAAO,KAAK,EACV,2BAA2B,EAC3B,mBAAmB,EACnB,mBAAmB,EACpB,MAAM,qBAAqB,CAAA;AAE5B,MAAM,MAAM,wBAAwB,GAAG,CAAC,KAAK,EAAE;IAAE,aAAa,EAAE,MAAM,CAAA;CAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;AAE1F,MAAM,MAAM,0BAA0B,GAAG,OAAO,wBAAwB,CAAC,YAAY,CAAA;AAErF,wBAAgB,yBAAyB,IAAI,mBAAmB,CAO/D;AAED,wBAAgB,yBAAyB,IAAI,mBAAmB,CAO/D;AAED,wBAAsB,mBAAmB,CAAC,EAAE,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM;;;;;;;;;;;;;;;;UAOvF;AAED,wBAAsB,kBAAkB,CAAC,EAAE,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM;;;;;;;;;;;;;;;;;;;UAOrF;AAED,wBAAsB,sBAAsB,CAC1C,EAAE,EAAE,kBAAkB,EACtB,aAAa,EAAE,MAAM,EACrB,GAAG,EAAE,IAAI,EACT,YAAY,EAAE,MAAM;;;;;;;;;;;;;;;;;;;UAcrB;AAED,wBAAsB,qBAAqB,CACzC,EAAE,EAAE,kBAAkB,EACtB,aAAa,EAAE,MAAM,EACrB,GAAG,EAAE,IAAI,EACT,YAAY,EAAE,MAAM;;;;;;;;;;;;;;;;;;;UAcrB;AAED,wBAAsB,mBAAmB,CACvC,EAAE,EAAE,kBAAkB,EACtB,aAAa,EAAE,MAAM,EACrB,GAAG,EAAE,IAAI,EACT,sBAAsB,EAAE,MAAM,GAAG,IAAI;;;;;;;;;;;;;;;;;;;UAetC;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,OAAO,EAAE,OAAO,GAAG,KAAK,CAAA;IACxB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;CACxB,CAAA;AAED,wBAAsB,sBAAsB,CAC1C,EAAE,EAAE,kBAAkB,EACtB,GAAG,EAAE,0BAA0B,EAC/B,IAAI,EAAE,2BAA2B,GAChC,OAAO,CAAC,eAAe,CAAC,CA0B1B"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { eq } from "drizzle-orm";
|
|
2
|
+
import { notificationReminderRules, notificationReminderRuns, notificationReminderStageChannels, } from "./schema.js";
|
|
3
|
+
export function buildReminderSweepSummary() {
|
|
4
|
+
return {
|
|
5
|
+
processed: 0,
|
|
6
|
+
sent: 0,
|
|
7
|
+
skipped: 0,
|
|
8
|
+
failed: 0,
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
export function buildReminderQueueSummary() {
|
|
12
|
+
return {
|
|
13
|
+
processed: 0,
|
|
14
|
+
queued: 0,
|
|
15
|
+
skipped: 0,
|
|
16
|
+
failed: 0,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
export async function getReminderRuleById(db, reminderRuleId) {
|
|
20
|
+
const [rule] = await db
|
|
21
|
+
.select()
|
|
22
|
+
.from(notificationReminderRules)
|
|
23
|
+
.where(eq(notificationReminderRules.id, reminderRuleId))
|
|
24
|
+
.limit(1);
|
|
25
|
+
return rule ?? null;
|
|
26
|
+
}
|
|
27
|
+
export async function getReminderRunById(db, reminderRunId) {
|
|
28
|
+
const [run] = await db
|
|
29
|
+
.select()
|
|
30
|
+
.from(notificationReminderRuns)
|
|
31
|
+
.where(eq(notificationReminderRuns.id, reminderRunId))
|
|
32
|
+
.limit(1);
|
|
33
|
+
return run ?? null;
|
|
34
|
+
}
|
|
35
|
+
export async function markReminderRunSkipped(db, reminderRunId, now, errorMessage) {
|
|
36
|
+
const [run] = await db
|
|
37
|
+
.update(notificationReminderRuns)
|
|
38
|
+
.set({
|
|
39
|
+
status: "skipped",
|
|
40
|
+
errorMessage,
|
|
41
|
+
processedAt: now,
|
|
42
|
+
updatedAt: now,
|
|
43
|
+
})
|
|
44
|
+
.where(eq(notificationReminderRuns.id, reminderRunId))
|
|
45
|
+
.returning();
|
|
46
|
+
return run ?? null;
|
|
47
|
+
}
|
|
48
|
+
export async function markReminderRunFailed(db, reminderRunId, now, errorMessage) {
|
|
49
|
+
const [run] = await db
|
|
50
|
+
.update(notificationReminderRuns)
|
|
51
|
+
.set({
|
|
52
|
+
status: "failed",
|
|
53
|
+
errorMessage,
|
|
54
|
+
processedAt: now,
|
|
55
|
+
updatedAt: now,
|
|
56
|
+
})
|
|
57
|
+
.where(eq(notificationReminderRuns.id, reminderRunId))
|
|
58
|
+
.returning();
|
|
59
|
+
return run ?? null;
|
|
60
|
+
}
|
|
61
|
+
export async function markReminderRunSent(db, reminderRunId, now, notificationDeliveryId) {
|
|
62
|
+
const [run] = await db
|
|
63
|
+
.update(notificationReminderRuns)
|
|
64
|
+
.set({
|
|
65
|
+
notificationDeliveryId,
|
|
66
|
+
status: "sent",
|
|
67
|
+
processedAt: now,
|
|
68
|
+
updatedAt: now,
|
|
69
|
+
errorMessage: null,
|
|
70
|
+
})
|
|
71
|
+
.where(eq(notificationReminderRuns.id, reminderRunId))
|
|
72
|
+
.returning();
|
|
73
|
+
return run ?? null;
|
|
74
|
+
}
|
|
75
|
+
export async function resolveChannelOverride(db, run, rule) {
|
|
76
|
+
const stageChannelId = run.metadata && typeof run.metadata === "object"
|
|
77
|
+
? run.metadata.stageChannelId
|
|
78
|
+
: undefined;
|
|
79
|
+
if (stageChannelId) {
|
|
80
|
+
const [stageChannel] = await db
|
|
81
|
+
.select()
|
|
82
|
+
.from(notificationReminderStageChannels)
|
|
83
|
+
.where(eq(notificationReminderStageChannels.id, stageChannelId))
|
|
84
|
+
.limit(1);
|
|
85
|
+
if (stageChannel) {
|
|
86
|
+
return {
|
|
87
|
+
channel: stageChannel.channel,
|
|
88
|
+
templateId: stageChannel.templateId ?? null,
|
|
89
|
+
templateSlug: stageChannel.templateSlug ?? null,
|
|
90
|
+
provider: stageChannel.provider ?? null,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
channel: rule.channel,
|
|
96
|
+
templateId: rule.templateId ?? null,
|
|
97
|
+
templateSlug: rule.templateSlug ?? null,
|
|
98
|
+
provider: rule.provider ?? null,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { PostgresJsDatabase } from "drizzle-orm/postgres-js";
|
|
2
|
+
import { type ReminderDeliveryEnqueuer } from "./service-reminder-run-state.js";
|
|
3
|
+
import type { NotificationService, ReminderQueueResult, ReminderSweepResult, RunDueRemindersInput } from "./service-shared.js";
|
|
4
|
+
export declare function runStageBasedDueReminders(db: PostgresJsDatabase, dispatcher: NotificationService, input?: RunDueRemindersInput): Promise<ReminderSweepResult>;
|
|
5
|
+
export declare function queueStageBasedDueReminders(db: PostgresJsDatabase, enqueueDelivery: ReminderDeliveryEnqueuer, input?: RunDueRemindersInput): Promise<ReminderQueueResult>;
|
|
6
|
+
//# sourceMappingURL=service-reminder-stage-runs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service-reminder-stage-runs.d.ts","sourceRoot":"","sources":["../src/service-reminder-stage-runs.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAcjE,OAAO,EAML,KAAK,wBAAwB,EAC9B,MAAM,iCAAiC,CAAA;AAcxC,OAAO,KAAK,EAEV,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,EACnB,oBAAoB,EACrB,MAAM,qBAAqB,CAAA;AAsV5B,wBAAsB,yBAAyB,CAC7C,EAAE,EAAE,kBAAkB,EACtB,UAAU,EAAE,mBAAmB,EAC/B,KAAK,GAAE,oBAAyB,GAC/B,OAAO,CAAC,mBAAmB,CAAC,CA6B9B;AAED,wBAAsB,2BAA2B,CAC/C,EAAE,EAAE,kBAAkB,EACtB,eAAe,EAAE,wBAAwB,EACzC,KAAK,GAAE,oBAAyB,GAC/B,OAAO,CAAC,mBAAmB,CAAC,CA6B9B"}
|