@voyantjs/notifications 0.28.3 → 0.29.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.
@@ -27,6 +27,24 @@ export const notificationReminderRunStatusSchema = z.enum([
27
27
  "skipped",
28
28
  "failed",
29
29
  ]);
30
+ export const notificationReminderStageAnchorSchema = z.enum([
31
+ "due_date",
32
+ "booking_created_at",
33
+ "departure_date",
34
+ "invoice_issued_at",
35
+ "last_send_at",
36
+ ]);
37
+ export const notificationReminderStageCadenceKindSchema = z.enum([
38
+ "once",
39
+ "every_n_days",
40
+ "escalating",
41
+ ]);
42
+ export const notificationStageRecipientKindSchema = z.enum(["primary", "cc", "bcc"]);
43
+ export const notificationReminderStageCadenceIntervalSchema = z.object({
44
+ whenDaysUntilDueGT: z.coerce.number().int().optional().nullable(),
45
+ whenDaysUntilDueLT: z.coerce.number().int().optional().nullable(),
46
+ repeatEveryDays: z.coerce.number().int().min(1).max(365),
47
+ });
30
48
  export const notificationDocumentTypeSchema = z.enum(["contract", "invoice", "proforma"]);
31
49
  export const notificationDocumentSourceSchema = z.enum(["legal", "finance"]);
32
50
  export const notificationAttachmentSchema = z
@@ -89,18 +107,104 @@ const notificationReminderRuleCoreSchema = z.object({
89
107
  provider: z.string().max(255).optional().nullable(),
90
108
  templateId: z.string().optional().nullable(),
91
109
  templateSlug: z.string().max(255).optional().nullable(),
92
- relativeDaysFromDueDate: z.coerce.number().int().min(-365).max(365).default(0),
110
+ priority: z.coerce.number().int().min(0).max(1000).default(0),
111
+ suppressionGroup: z.string().max(255).optional().nullable(),
93
112
  isSystem: z.boolean().default(false),
94
113
  metadata: z.record(z.string(), z.unknown()).optional().nullable(),
95
114
  });
96
- export const insertNotificationReminderRuleSchema = notificationReminderRuleCoreSchema.refine((value) => Boolean(value.templateId || value.templateSlug), {
115
+ // templateId / templateSlug are both optional now: stage channels carry
116
+ // their own templates and override the rule-level default. A rule with no
117
+ // template is valid as long as it has stages (or the legacy
118
+ // relativeDaysFromDueDate path stays unused).
119
+ export const insertNotificationReminderRuleSchema = notificationReminderRuleCoreSchema;
120
+ export const updateNotificationReminderRuleSchema = notificationReminderRuleCoreSchema.partial();
121
+ const notificationReminderRuleStageBaseSchema = z.object({
122
+ name: z.string().max(255).optional().nullable(),
123
+ orderIndex: z.coerce.number().int().min(0).max(1000),
124
+ anchor: notificationReminderStageAnchorSchema,
125
+ windowStartDays: z.coerce.number().int().min(-3650).max(3650),
126
+ windowEndDays: z.coerce.number().int().min(-3650).max(3650),
127
+ cadenceKind: notificationReminderStageCadenceKindSchema,
128
+ cadenceEveryDays: z.coerce.number().int().min(1).max(365).optional().nullable(),
129
+ cadenceIntervals: z
130
+ .array(notificationReminderStageCadenceIntervalSchema)
131
+ .min(1)
132
+ .max(20)
133
+ .optional()
134
+ .nullable(),
135
+ maxSendsInStage: z.coerce.number().int().min(1).max(100).optional().nullable(),
136
+ respectQuietHours: z.boolean().default(true),
137
+ metadata: z.record(z.string(), z.unknown()).optional().nullable(),
138
+ });
139
+ export const insertNotificationReminderRuleStageSchema = notificationReminderRuleStageBaseSchema.superRefine((value, ctx) => {
140
+ if (value.windowEndDays < value.windowStartDays) {
141
+ ctx.addIssue({
142
+ code: "custom",
143
+ path: ["windowEndDays"],
144
+ message: "windowEndDays must be >= windowStartDays",
145
+ });
146
+ }
147
+ if (value.cadenceKind === "every_n_days" && !value.cadenceEveryDays) {
148
+ ctx.addIssue({
149
+ code: "custom",
150
+ path: ["cadenceEveryDays"],
151
+ message: "cadenceEveryDays is required when cadenceKind=every_n_days",
152
+ });
153
+ }
154
+ if (value.cadenceKind === "escalating" &&
155
+ (!value.cadenceIntervals || value.cadenceIntervals.length === 0)) {
156
+ ctx.addIssue({
157
+ code: "custom",
158
+ path: ["cadenceIntervals"],
159
+ message: "cadenceIntervals is required when cadenceKind=escalating",
160
+ });
161
+ }
162
+ });
163
+ export const updateNotificationReminderRuleStageSchema = notificationReminderRuleStageBaseSchema.partial();
164
+ export const reorderReminderRuleStagesSchema = z.object({
165
+ stageIds: z.array(z.string().min(1)).min(1).max(50),
166
+ });
167
+ const notificationReminderStageChannelBaseSchema = z.object({
168
+ orderIndex: z.coerce.number().int().min(0).max(50).default(0),
169
+ channel: notificationChannelSchema,
170
+ provider: z.string().max(255).optional().nullable(),
171
+ templateId: z.string().optional().nullable(),
172
+ templateSlug: z.string().max(255).optional().nullable(),
173
+ recipientKind: notificationStageRecipientKindSchema.default("primary"),
174
+ recipientRole: z.string().max(255).optional().nullable(),
175
+ metadata: z.record(z.string(), z.unknown()).optional().nullable(),
176
+ });
177
+ export const insertNotificationReminderStageChannelSchema = notificationReminderStageChannelBaseSchema.refine((value) => Boolean(value.templateId || value.templateSlug), {
97
178
  message: "templateId or templateSlug is required",
98
179
  path: ["templateId"],
99
180
  });
100
- export const updateNotificationReminderRuleSchema = notificationReminderRuleCoreSchema
101
- .partial()
102
- .refine((value) => value.templateId !== "" && value.templateSlug !== "", {
103
- message: "templateId and templateSlug cannot be empty strings",
181
+ export const updateNotificationReminderStageChannelSchema = notificationReminderStageChannelBaseSchema.partial();
182
+ const notificationQuietHoursConfigSchema = z.object({
183
+ start: z.string().regex(/^\d{2}:\d{2}$/),
184
+ end: z.string().regex(/^\d{2}:\d{2}$/),
185
+ tz: z.string().min(1).max(255),
186
+ });
187
+ const notificationSettingsCoreSchema = z.object({
188
+ scope: z.string().min(1).max(64).default("default"),
189
+ quietHoursLocal: notificationQuietHoursConfigSchema.nullable().optional(),
190
+ blackoutDates: z
191
+ .array(z.string().regex(/^\d{4}-\d{2}-\d{2}$/))
192
+ .max(366)
193
+ .nullable()
194
+ .optional(),
195
+ skipWeekends: z.boolean().default(false),
196
+ recipientRateLimitPerDay: z.coerce.number().int().min(1).max(10000).nullable().optional(),
197
+ suppressionWindowHours: z.coerce.number().int().min(0).max(720).default(24),
198
+ metadata: z.record(z.string(), z.unknown()).optional().nullable(),
199
+ });
200
+ export const updateNotificationSettingsSchema = notificationSettingsCoreSchema.partial();
201
+ export const previewRemindersQuerySchema = z.object({
202
+ date: z
203
+ .string()
204
+ .regex(/^\d{4}-\d{2}-\d{2}$/)
205
+ .optional(),
206
+ ruleId: z.string().optional(),
207
+ targetId: z.string().optional(),
104
208
  });
105
209
  export const notificationReminderRuleListQuerySchema = paginationSchema.extend({
106
210
  status: notificationReminderStatusSchema.optional(),
@@ -132,7 +236,6 @@ export const notificationReminderRunRuleSummarySchema = z.object({
132
236
  provider: z.string().nullable(),
133
237
  templateId: z.string().nullable(),
134
238
  templateSlug: z.string().nullable(),
135
- relativeDaysFromDueDate: z.number().int(),
136
239
  });
137
240
  export const notificationReminderRunDeliverySummarySchema = z.object({
138
241
  id: z.string(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voyantjs/notifications",
3
- "version": "0.28.3",
3
+ "version": "0.29.0",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "exports": {
@@ -61,12 +61,12 @@
61
61
  "hono": "^4.12.10",
62
62
  "liquidjs": "^10.24.0",
63
63
  "zod": "^4.3.6",
64
- "@voyantjs/bookings": "0.28.3",
65
- "@voyantjs/core": "0.28.3",
66
- "@voyantjs/db": "0.28.3",
67
- "@voyantjs/finance": "0.28.3",
68
- "@voyantjs/hono": "0.28.3",
69
- "@voyantjs/legal": "0.28.3"
64
+ "@voyantjs/bookings": "0.29.0",
65
+ "@voyantjs/core": "0.29.0",
66
+ "@voyantjs/db": "0.29.0",
67
+ "@voyantjs/finance": "0.29.0",
68
+ "@voyantjs/hono": "0.29.0",
69
+ "@voyantjs/legal": "0.29.0"
70
70
  },
71
71
  "devDependencies": {
72
72
  "typescript": "^6.0.2",