@dalmore/api-contracts 0.0.0-dev.d07da18 → 0.0.0-dev.d6badc9

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 (44) hide show
  1. package/common/types/account-setting.types.ts +65 -0
  2. package/common/types/account.types.ts +1 -0
  3. package/common/types/activity.types.ts +1 -1
  4. package/common/types/bonus-tier.types.ts +33 -0
  5. package/common/types/cart.types.ts +4 -1
  6. package/common/types/common.types.ts +16 -6
  7. package/common/types/dashboard.types.ts +2 -9
  8. package/common/types/disbursements.types.ts +119 -3
  9. package/common/types/file.types.ts +17 -1
  10. package/common/types/i-will-do-it-later.types.ts +68 -0
  11. package/common/types/index.ts +2 -0
  12. package/common/types/individuals.types.ts +2 -15
  13. package/common/types/issuer-offering.types.ts +13 -17
  14. package/common/types/issuer-payment-method.types.ts +41 -0
  15. package/common/types/issuer.types.ts +9 -0
  16. package/common/types/notification.types.ts +515 -29
  17. package/common/types/offering.types.ts +4 -9
  18. package/common/types/site.types.ts +2 -9
  19. package/common/types/{trade-line-item.type.ts → trade-line-item.types.ts} +2 -9
  20. package/common/types/trade.types.ts +1 -1
  21. package/common/types/transaction.types.ts +12 -1
  22. package/common/types/user.types.ts +15 -28
  23. package/contracts/clients/cart/index.ts +21 -1
  24. package/contracts/clients/index.ts +6 -0
  25. package/contracts/clients/issuer-payment-methods/index.ts +39 -0
  26. package/contracts/clients/payment-methods/index.ts +85 -0
  27. package/contracts/clients/trade-line-items/index.ts +1 -1
  28. package/contracts/clients/trades/index.ts +1 -1
  29. package/contracts/clients/transactions/index.ts +37 -0
  30. package/contracts/compliance/account-settings/index.ts +59 -0
  31. package/contracts/compliance/bonus-tiers/index.ts +21 -2
  32. package/contracts/compliance/index.ts +4 -0
  33. package/contracts/compliance/notification-channels/index.ts +251 -0
  34. package/contracts/compliance/trade-line-items/index.ts +1 -1
  35. package/contracts/compliance/users/index.ts +21 -0
  36. package/contracts/investors/bonus-tiers/index.ts +18 -0
  37. package/contracts/investors/individuals/index.ts +22 -0
  38. package/contracts/investors/trade-line-items/index.ts +1 -1
  39. package/contracts/issuers/account-settings/index.ts +36 -0
  40. package/contracts/issuers/bonus-tiers/index.ts +18 -0
  41. package/contracts/issuers/disbursements/index.ts +36 -0
  42. package/contracts/issuers/index.ts +4 -0
  43. package/contracts/issuers/notification-channels/index.ts +251 -0
  44. package/package.json +1 -1
@@ -1,58 +1,544 @@
1
1
  import { extendZodWithOpenApi } from '@anatine/zod-openapi';
2
2
  import { z } from 'zod';
3
3
  import { TypeID } from 'typeid-js';
4
- import { accountIdSchema } from './account.types';
4
+ import { IBaseEntity } from './entity.types';
5
+ import { IPaginationMeta, StringToBooleanSchema } from './common.types';
6
+ import {
7
+ ActivityTypeAction,
8
+ ActivityTypeCategory,
9
+ ActivityTypeResource,
10
+ } from './activity.types';
5
11
 
6
12
  extendZodWithOpenApi(z);
7
- export enum NotificationChannel {
13
+
14
+ export enum NotificationChannelType {
8
15
  EMAIL = 'EMAIL',
9
16
  SLACK = 'SLACK',
17
+ WEBHOOK = 'WEBHOOK',
10
18
  }
11
- export enum NotificationType {
12
- COMPLIANCE_TRADE_ALERT = 'COMPLIANCE_TRADE_ALERT',
13
- TRADE_ALERT = 'TRADE_ALERT',
19
+
20
+ export enum NotificationRecordStatus {
21
+ PENDING = 'PENDING',
22
+ SENT = 'SENT',
23
+ FAILED = 'FAILED',
14
24
  }
15
25
 
16
- export const notificationIdSchema = z.string().refine(
26
+ export type TriggerExclusion = {
27
+ category?: ActivityTypeCategory;
28
+ resource?: ActivityTypeResource;
29
+ action?: ActivityTypeAction;
30
+ };
31
+
32
+ // Excluded notifications
33
+ export const EXCLUDED_NOTIFICATION_TRIGGERS: TriggerExclusion[] = [
34
+ // { action: ActivityTypeAction.RE_SIGN },
35
+ // { action: ActivityTypeAction.RESET_2FA },
36
+ // { action: ActivityTypeAction.LOGIN },
37
+ // { action: ActivityTypeAction.RESET_PASSWORD },
38
+ // { action: ActivityTypeAction.TWO_FACTOR_LOGIN },
39
+ // { action: ActivityTypeAction.FORGOT_PASSWORD },
40
+ // { resource: ActivityTypeResource.USERS },
41
+ // { resource: ActivityTypeResource.THEME_SETTINGS },
42
+ ];
43
+
44
+ export const ISSUER_ALLOWED_CATEGORIES: ActivityTypeCategory[] = [
45
+ ActivityTypeCategory.ISSUER,
46
+ ];
47
+
48
+ /**
49
+ * Checks if an activity type should be excluded from notifications.
50
+ *
51
+ * Each exclusion pattern can specify any combination of category, resource, and action.
52
+ * Omitted fields act as wildcards (match any value).
53
+ *
54
+ * @example
55
+ * // Excludes ALL activities with action RE_SIGN (any category, any resource)
56
+ * { action: ActivityTypeAction.RE_SIGN }
57
+ *
58
+ * @example
59
+ * // Excludes ALL USERS activities (any category, any action)
60
+ * { resource: ActivityTypeResource.USERS }
61
+ *
62
+ * @example
63
+ * // Excludes only COMPLIANCE + TRADES + CREATE (all three must match)
64
+ * { category: ActivityTypeCategory.COMPLIANCE, resource: ActivityTypeResource.TRADES, action: ActivityTypeAction.CREATE }
65
+ *
66
+ * @param category - The activity type category to check
67
+ * @param resource - The activity type resource to check
68
+ * @param action - The activity type action to check
69
+ * @param exclusions - Array of exclusion patterns (defaults to EXCLUDED_NOTIFICATION_TRIGGERS)
70
+ * @returns true if the activity type matches any exclusion pattern
71
+ */
72
+ export function isTriggerExcluded(
73
+ category: string,
74
+ resource: string,
75
+ action: string,
76
+ exclusions: TriggerExclusion[] = EXCLUDED_NOTIFICATION_TRIGGERS,
77
+ ): boolean {
78
+ return exclusions.some((exclusion) => {
79
+ const categoryMatch =
80
+ !exclusion.category || exclusion.category === category;
81
+ const resourceMatch =
82
+ !exclusion.resource || exclusion.resource === resource;
83
+ const actionMatch = !exclusion.action || exclusion.action === action;
84
+ return categoryMatch && resourceMatch && actionMatch;
85
+ });
86
+ }
87
+
88
+ /**
89
+ * Checks if a category is allowed for notification triggers.
90
+ *
91
+ * Used to restrict which activity type categories a user can subscribe to.
92
+ * For example, issuers can only subscribe to ISSUER category triggers.
93
+ *
94
+ * @param category - The activity type category to check
95
+ * @param allowedCategories - Optional array of allowed categories. If empty or undefined, all categories are allowed.
96
+ * @returns true if the category is allowed (or if no restrictions are set)
97
+ */
98
+ export function isCategoryAllowed(
99
+ category: string,
100
+ allowedCategories?: ActivityTypeCategory[],
101
+ ): boolean {
102
+ if (!allowedCategories || allowedCategories.length === 0) {
103
+ return true;
104
+ }
105
+ return allowedCategories.includes(category as ActivityTypeCategory);
106
+ }
107
+
108
+ export const notificationChannelIdSchema = z.string().refine(
17
109
  (value) => {
18
110
  try {
19
111
  const tid = TypeID.fromString(value);
20
- return tid.getType() === 'notification';
112
+ return tid.getType() === 'notification_channel';
21
113
  } catch {
22
114
  return false;
23
115
  }
24
116
  },
25
117
  {
26
118
  message:
27
- 'Invalid notification ID format. Must be a valid TypeID with "notification" prefix.',
119
+ 'Invalid notification channel ID format. Must be a valid TypeID with "notification_channel" prefix.',
28
120
  },
29
121
  );
30
122
 
31
- export const SendNotificationZod = z.object({
32
- message: z.string(),
123
+ export const notificationChannelTriggerIdSchema = z.string().refine(
124
+ (value) => {
125
+ try {
126
+ const tid = TypeID.fromString(value);
127
+ return tid.getType() === 'notification_channel_trigger';
128
+ } catch {
129
+ return false;
130
+ }
131
+ },
132
+ {
133
+ message:
134
+ 'Invalid notification channel trigger ID format. Must be a valid TypeID with "notification_channel_trigger" prefix.',
135
+ },
136
+ );
137
+
138
+ export const notificationRecordIdSchema = z.string().refine(
139
+ (value) => {
140
+ try {
141
+ const tid = TypeID.fromString(value);
142
+ return tid.getType() === 'notification_record';
143
+ } catch {
144
+ return false;
145
+ }
146
+ },
147
+ {
148
+ message:
149
+ 'Invalid notification record ID format. Must be a valid TypeID with "notification_record" prefix.',
150
+ },
151
+ );
152
+
153
+ export const EmailChannelSettingsSchema = z.object({
154
+ recipients: z
155
+ .array(z.string().email())
156
+ .min(1)
157
+ .max(100, 'At most 100 recipients are allowed'),
158
+ subjectTemplate: z.string().optional(),
159
+ });
160
+ export type EmailChannelSettings = z.infer<typeof EmailChannelSettingsSchema>;
161
+
162
+ export const SlackChannelSettingsSchema = z
163
+ .object({
164
+ webhookUrl: z.string().url().optional(),
165
+ channelEmail: z.string().email().optional(),
166
+ })
167
+ .refine((data) => data.webhookUrl || data.channelEmail, {
168
+ message: 'Either webhookUrl or channelEmail is required',
169
+ });
170
+ export type SlackChannelSettings = z.infer<typeof SlackChannelSettingsSchema>;
171
+
172
+ export const WebhookChannelSettingsSchema = z.object({
173
+ url: z.string().url(),
174
+ method: z.enum(['POST', 'PUT']),
175
+ headers: z.record(z.string()).optional(),
176
+ authType: z.enum(['none', 'bearer', 'basic', 'api_key']).default('none'),
177
+ authValue: z.string().optional(),
178
+ });
179
+ export type WebhookChannelSettings = z.infer<
180
+ typeof WebhookChannelSettingsSchema
181
+ >;
182
+
183
+ export const NotificationChannelSettingsSchema = z.discriminatedUnion('type', [
184
+ z.object({
185
+ type: z.literal(NotificationChannelType.EMAIL),
186
+ config: EmailChannelSettingsSchema,
187
+ }),
188
+ z.object({
189
+ type: z.literal(NotificationChannelType.SLACK),
190
+ config: SlackChannelSettingsSchema,
191
+ }),
192
+ z.object({
193
+ type: z.literal(NotificationChannelType.WEBHOOK),
194
+ config: WebhookChannelSettingsSchema,
195
+ }),
196
+ ]);
197
+ export type NotificationChannelSettings = z.infer<
198
+ typeof NotificationChannelSettingsSchema
199
+ >;
200
+
201
+ export const EmailNotificationPayloadSchema = z.object({
202
+ to: z.array(z.string().email()),
33
203
  subject: z.string(),
34
- buttonText: z.string(),
35
- link: z.string(),
36
- debug: z.boolean().default(false),
37
- notificationChannel: z.nativeEnum(NotificationChannel),
38
- channelName: z.string(),
39
- channelEmail: z.string().email(),
40
- notificationType: z.nativeEnum(NotificationType),
204
+ body: z.string(),
205
+ html: z.string().optional(),
206
+ });
207
+ export type EmailNotificationPayload = z.infer<
208
+ typeof EmailNotificationPayloadSchema
209
+ >;
210
+
211
+ export const SlackNotificationPayloadSchema = z.object({
212
+ webhookUrl: z.string().url().optional(),
213
+ channelEmail: z.string().email().optional(),
214
+ message: z.string(),
215
+ blocks: z.array(z.record(z.unknown())).optional(),
216
+ });
217
+ export type SlackNotificationPayload = z.infer<
218
+ typeof SlackNotificationPayloadSchema
219
+ >;
220
+
221
+ export const WebhookNotificationPayloadSchema = z.object({
222
+ url: z.string().url(),
223
+ method: z.enum(['POST', 'PUT']),
224
+ headers: z.record(z.string()),
225
+ body: z.record(z.unknown()),
41
226
  });
42
- export type SendNotificationZod = z.infer<typeof SendNotificationZod>;
227
+ export type WebhookNotificationPayload = z.infer<
228
+ typeof WebhookNotificationPayloadSchema
229
+ >;
230
+
231
+ export const NotificationPayloadSchema = z.discriminatedUnion('type', [
232
+ z.object({
233
+ type: z.literal(NotificationChannelType.EMAIL),
234
+ data: EmailNotificationPayloadSchema,
235
+ }),
236
+ z.object({
237
+ type: z.literal(NotificationChannelType.SLACK),
238
+ data: SlackNotificationPayloadSchema,
239
+ }),
240
+ z.object({
241
+ type: z.literal(NotificationChannelType.WEBHOOK),
242
+ data: WebhookNotificationPayloadSchema,
243
+ }),
244
+ ]);
245
+ export type NotificationPayload = z.infer<typeof NotificationPayloadSchema>;
43
246
 
44
- const SlackSettingsSchema = z.object({
45
- channelName: z.string().min(1).max(50),
46
- channelEmail: z.string().email(),
247
+ export const EmailNotificationResponseSchema = z.object({
248
+ messageId: z.string().optional(),
249
+ accepted: z.array(z.string()).optional(),
250
+ rejected: z.array(z.string()).optional(),
47
251
  });
252
+ export type EmailNotificationResponse = z.infer<
253
+ typeof EmailNotificationResponseSchema
254
+ >;
48
255
 
49
- export const PostNotificationZod = z.object({
50
- accountId: accountIdSchema,
51
- notificationChannel: z.literal(NotificationChannel.SLACK), // Only Slack for now
52
- notificationType: z.nativeEnum(NotificationType),
53
- enabled: z.boolean().default(true),
54
- debug: z.boolean().default(false),
55
- settings: SlackSettingsSchema,
256
+ export const SlackNotificationResponseSchema = z.object({
257
+ ok: z.boolean().optional(),
258
+ error: z.string().optional(),
259
+ responseBody: z.string().optional(),
56
260
  });
261
+ export type SlackNotificationResponse = z.infer<
262
+ typeof SlackNotificationResponseSchema
263
+ >;
57
264
 
58
- export type PostNotificationZod = z.infer<typeof PostNotificationZod>;
265
+ export const WebhookNotificationResponseSchema = z.object({
266
+ statusCode: z.number(),
267
+ headers: z.record(z.string()).optional(),
268
+ body: z.unknown().optional(),
269
+ });
270
+ export type WebhookNotificationResponse = z.infer<
271
+ typeof WebhookNotificationResponseSchema
272
+ >;
273
+
274
+ export const NotificationResponseSchema = z.discriminatedUnion('type', [
275
+ z.object({
276
+ type: z.literal(NotificationChannelType.EMAIL),
277
+ data: EmailNotificationResponseSchema,
278
+ }),
279
+ z.object({
280
+ type: z.literal(NotificationChannelType.SLACK),
281
+ data: SlackNotificationResponseSchema,
282
+ }),
283
+ z.object({
284
+ type: z.literal(NotificationChannelType.WEBHOOK),
285
+ data: WebhookNotificationResponseSchema,
286
+ }),
287
+ ]);
288
+ export type NotificationResponse = z.infer<typeof NotificationResponseSchema>;
289
+
290
+ export const INotificationChannelZod = IBaseEntity.extend({
291
+ id: notificationChannelIdSchema.openapi({
292
+ example: 'notification_channel_01j5y5ghx8fvc83dmx3pznq7hv',
293
+ }),
294
+ accountSettingsId: z.string().openapi({
295
+ example: 'account_setting_01j5y5ghx8fvc83dmx3pznq7hv',
296
+ }),
297
+ channelType: z.nativeEnum(NotificationChannelType).openapi({
298
+ example: NotificationChannelType.SLACK,
299
+ }),
300
+ name: z.string().openapi({
301
+ example: 'Compliance Alerts',
302
+ }),
303
+ enabled: z.boolean().openapi({
304
+ example: true,
305
+ }),
306
+ settings: NotificationChannelSettingsSchema.openapi({
307
+ example: {
308
+ type: NotificationChannelType.SLACK,
309
+ config: {
310
+ webhookUrl: 'https://hooks.slack.com/test',
311
+ },
312
+ },
313
+ }),
314
+ });
315
+ export type INotificationChannelZod = z.infer<typeof INotificationChannelZod>;
316
+
317
+ export const INotificationChannelTriggerZod = IBaseEntity.extend({
318
+ id: notificationChannelTriggerIdSchema.openapi({
319
+ example: 'notification_channel_trigger_01j5y5ghx8fvc83dmx3pznq7hv',
320
+ }),
321
+ notificationChannelId: notificationChannelIdSchema.openapi({
322
+ example: 'notification_channel_01j5y5ghx8fvc83dmx3pznq7hv',
323
+ }),
324
+ activityTypeId: z.string().openapi({
325
+ example: 'activity_type_01j5y5ghx8fvc83dmx3pznq7hv',
326
+ }),
327
+ enabled: z.boolean().openapi({
328
+ example: true,
329
+ }),
330
+ });
331
+ export type INotificationChannelTriggerZod = z.infer<
332
+ typeof INotificationChannelTriggerZod
333
+ >;
334
+
335
+ export const IAvailableTriggerZod = z.object({
336
+ activityTypeId: z.string().openapi({
337
+ example: 'activity_type_01j5y5ghx8fvc83dmx3pznq7hv',
338
+ }),
339
+ category: z.string().openapi({
340
+ example: 'COMPLIANCE',
341
+ }),
342
+ resource: z.string().openapi({
343
+ example: 'TRADES',
344
+ }),
345
+ action: z.string().openapi({
346
+ example: 'CREATE',
347
+ }),
348
+ enabled: z.boolean().openapi({
349
+ description: 'Whether the trigger is enabled for this activity type',
350
+ example: false,
351
+ }),
352
+ trigger: INotificationChannelTriggerZod.nullable().openapi({
353
+ description: 'The configured trigger, or null if not configured',
354
+ }),
355
+ });
356
+ export type IAvailableTriggerZod = z.infer<typeof IAvailableTriggerZod>;
357
+
358
+ export const IAvailableTriggersResponse = z.object({
359
+ items: z.array(IAvailableTriggerZod),
360
+ });
361
+ export type IAvailableTriggersResponse = z.infer<
362
+ typeof IAvailableTriggersResponse
363
+ >;
364
+
365
+ export const INotificationRecordZod = IBaseEntity.extend({
366
+ id: notificationRecordIdSchema.openapi({
367
+ example: 'notification_record_01j5y5ghx8fvc83dmx3pznq7hv',
368
+ }),
369
+ notificationChannelId: notificationChannelIdSchema.openapi({
370
+ example: 'notification_channel_01j5y5ghx8fvc83dmx3pznq7hv',
371
+ }),
372
+ activityId: z.string().nullable().openapi({
373
+ example: 'activity_01j5y5ghx8fvc83dmx3pznq7hv',
374
+ }),
375
+ targetTable: z.string().openapi({
376
+ example: 'trades',
377
+ }),
378
+ targetId: z.string().openapi({
379
+ example: 'trade_01j5y5ghx8fvc83dmx3pznq7hv',
380
+ }),
381
+ status: z.nativeEnum(NotificationRecordStatus).openapi({
382
+ example: NotificationRecordStatus.SENT,
383
+ }),
384
+ payload: NotificationPayloadSchema,
385
+ response: NotificationResponseSchema.nullable(),
386
+ errorMessage: z.string().nullable().openapi({
387
+ example: null,
388
+ }),
389
+ retryCount: z.number().openapi({
390
+ example: 0,
391
+ }),
392
+ sentAt: z.date().nullable().openapi({
393
+ example: new Date(),
394
+ }),
395
+ });
396
+ export type INotificationRecordZod = z.infer<typeof INotificationRecordZod>;
397
+
398
+ export const CreateChannelInputSchema = z
399
+ .object({
400
+ accountSettingsId: z.string(),
401
+ channelType: z.nativeEnum(NotificationChannelType),
402
+ name: z
403
+ .string()
404
+ .min(1, 'Name is required')
405
+ .max(255, 'Name must be less than 255 characters'),
406
+ enabled: z.boolean().optional(),
407
+ settings: NotificationChannelSettingsSchema,
408
+ })
409
+ .refine((data) => data.channelType === data.settings.type, {
410
+ message: 'Channel type must match settings type',
411
+ path: ['settings', 'type'],
412
+ });
413
+ export type CreateChannelInput = z.infer<typeof CreateChannelInputSchema>;
414
+
415
+ export const IssuerCreateChannelInputSchema = z
416
+ .object({
417
+ channelType: z.nativeEnum(NotificationChannelType),
418
+ name: z
419
+ .string()
420
+ .min(1, 'Name is required')
421
+ .max(255, 'Name must be less than 255 characters'),
422
+ enabled: z.boolean().optional(),
423
+ settings: NotificationChannelSettingsSchema,
424
+ })
425
+ .refine((data) => data.channelType === data.settings.type, {
426
+ message: 'Channel type must match settings type',
427
+ path: ['settings', 'type'],
428
+ });
429
+ export type IssuerCreateChannelInput = z.infer<
430
+ typeof IssuerCreateChannelInputSchema
431
+ >;
432
+
433
+ export const UpdateChannelInputSchema = z.object({
434
+ name: z.string().optional(),
435
+ enabled: z.boolean().optional(),
436
+ settings: NotificationChannelSettingsSchema.optional(),
437
+ });
438
+ export type UpdateChannelInput = z.infer<typeof UpdateChannelInputSchema>;
439
+
440
+ export const CreateTriggerInputSchema = z.object({
441
+ notificationChannelId: notificationChannelIdSchema,
442
+ activityTypeId: z.string(),
443
+ enabled: z.boolean().optional(),
444
+ });
445
+ export type CreateTriggerInput = z.infer<typeof CreateTriggerInputSchema>;
446
+
447
+ export const IPaginatedNotificationChannel = z.object({
448
+ items: z.array(INotificationChannelZod),
449
+ meta: IPaginationMeta,
450
+ });
451
+ export type IPaginatedNotificationChannel = z.infer<
452
+ typeof IPaginatedNotificationChannel
453
+ >;
454
+
455
+ export const IPaginatedNotificationChannelTrigger = z.object({
456
+ items: z.array(INotificationChannelTriggerZod),
457
+ meta: IPaginationMeta,
458
+ });
459
+ export type IPaginatedNotificationChannelTrigger = z.infer<
460
+ typeof IPaginatedNotificationChannelTrigger
461
+ >;
462
+
463
+ export const IPaginatedNotificationRecord = z.object({
464
+ items: z.array(INotificationRecordZod),
465
+ meta: IPaginationMeta,
466
+ });
467
+ export type IPaginatedNotificationRecord = z.infer<
468
+ typeof IPaginatedNotificationRecord
469
+ >;
470
+
471
+ export const NotificationChannelFilters = z.object({
472
+ accountSettingsId: z.string().optional(),
473
+ channelType: z.nativeEnum(NotificationChannelType).optional(),
474
+ enabled: StringToBooleanSchema.optional(),
475
+ });
476
+ export type NotificationChannelFilters = z.infer<
477
+ typeof NotificationChannelFilters
478
+ >;
479
+
480
+ export const NotificationChannelTriggerFilters = z.object({
481
+ notificationChannelId: notificationChannelIdSchema.optional(),
482
+ activityTypeId: z.string().optional(),
483
+ enabled: StringToBooleanSchema.optional(),
484
+ });
485
+ export type NotificationChannelTriggerFilters = z.infer<
486
+ typeof NotificationChannelTriggerFilters
487
+ >;
488
+
489
+ export const NotificationRecordFilters = z.object({
490
+ notificationChannelId: notificationChannelIdSchema.optional(),
491
+ status: z.nativeEnum(NotificationRecordStatus).optional(),
492
+ });
493
+ export type NotificationRecordFilters = z.infer<
494
+ typeof NotificationRecordFilters
495
+ >;
496
+
497
+ export const notificationChannelsInclude = z.enum([
498
+ 'triggers',
499
+ 'accountSettings',
500
+ 'notificationRecords',
501
+ ]);
502
+
503
+ export const NotificationChannelsIncludeQuery = z.object({
504
+ include: z
505
+ .string()
506
+ .optional()
507
+ .transform((str) => (str ? str.split(',') : []))
508
+ .refine(
509
+ (includes) =>
510
+ includes.every((include) =>
511
+ notificationChannelsInclude.options.includes(include as any),
512
+ ),
513
+ {
514
+ message: `Invalid include value. Valid values are: ${notificationChannelsInclude.options.join(', ')}`,
515
+ },
516
+ ),
517
+ });
518
+ export type NotificationChannelsIncludeQuery = z.infer<
519
+ typeof NotificationChannelsIncludeQuery
520
+ >;
521
+
522
+ export const notificationRecordsInclude = z.enum([
523
+ 'notificationChannel',
524
+ 'activity',
525
+ ]);
526
+
527
+ export const NotificationRecordsIncludeQuery = z.object({
528
+ include: z
529
+ .string()
530
+ .optional()
531
+ .transform((str) => (str ? str.split(',') : []))
532
+ .refine(
533
+ (includes) =>
534
+ includes.every((include) =>
535
+ notificationRecordsInclude.options.includes(include as any),
536
+ ),
537
+ {
538
+ message: `Invalid include value. Valid values are: ${notificationRecordsInclude.options.join(', ')}`,
539
+ },
540
+ ),
541
+ });
542
+ export type NotificationRecordsIncludeQuery = z.infer<
543
+ typeof NotificationRecordsIncludeQuery
544
+ >;
@@ -17,6 +17,7 @@ import {
17
17
  OfferingOnboardingStatus,
18
18
  AssetType,
19
19
  DurationType,
20
+ StringToBooleanSchema,
20
21
  } from './common.types';
21
22
  import { IBaseEntity } from './entity.types';
22
23
  import { fileIdSchema, FileZod } from './file.types';
@@ -230,6 +231,7 @@ export const PatchOffering = PatchOfferingBase.merge(
230
231
  .nullable()
231
232
  .optional(),
232
233
  tiers: z.array(z.number().positive()).nullable().optional(),
234
+ enableBonus: z.boolean().optional(),
233
235
  }),
234
236
  );
235
237
  export type PatchOffering = z.infer<typeof PatchOffering>;
@@ -276,6 +278,7 @@ export const PostComplianceOffering = PatchOfferingBase.merge(
276
278
  .default(AssetTemplateType.STANDARD)
277
279
  .openapi({ example: AssetTemplateType.STANDARD }),
278
280
  tiers: z.array(z.number().positive()).nullable().optional(),
281
+ enableBonus: z.boolean().default(false).optional(),
279
282
  }),
280
283
  ).superRefine(postAssetRefinement);
281
284
  export type PostComplianceOffering = z.infer<typeof PostComplianceOffering>;
@@ -344,15 +347,7 @@ export const OfferingFiltersZod = z.object({
344
347
  issuerId: z.lazy(() => issuerIdSchema).optional(),
345
348
  type: z.nativeEnum(OfferingType).optional(),
346
349
  status: z.nativeEnum(ComplianceReview).optional(),
347
- enabled: z.preprocess(
348
- (val) =>
349
- val === 'true' || val === '1'
350
- ? true
351
- : val === 'false' || val === '0'
352
- ? false
353
- : val,
354
- z.boolean().optional(),
355
- ),
350
+ enabled: StringToBooleanSchema.optional(),
356
351
  managedBy: z.nativeEnum(ManagedByType).optional(),
357
352
  versioningType: z.nativeEnum(OfferingVersioningType).optional(),
358
353
  combinedStatus: z.nativeEnum(OfferingStatus).optional(),
@@ -5,6 +5,7 @@ import {
5
5
  AccountStatus,
6
6
  AccountZod,
7
7
  IPaginationMeta,
8
+ StringToBooleanSchema,
8
9
  UrlSchema,
9
10
  } from './common.types';
10
11
  import { accountIdSchema } from './account.types';
@@ -73,15 +74,7 @@ export const SitesParamSchema = z.object({
73
74
  export const SitesFiltersZod = z.object({
74
75
  url: z.string().optional(),
75
76
  accountId: accountIdSchema.optional(),
76
- enabled: z.preprocess(
77
- (val) =>
78
- val === 'true' || val === '1'
79
- ? true
80
- : val === 'false' || val === '0'
81
- ? false
82
- : val,
83
- z.boolean().optional(),
84
- ),
77
+ enabled: StringToBooleanSchema.optional(),
85
78
  });
86
79
 
87
80
  const sitesInclude = z.enum(['account']);
@@ -8,6 +8,7 @@ import {
8
8
  IPaginationMeta,
9
9
  OfferingType,
10
10
  SignatureStatus,
11
+ StringToBooleanSchema,
11
12
  } from './common.types';
12
13
  import { fileIdSchema } from './file.types';
13
14
  import { accountIdSchema } from './account.types';
@@ -107,15 +108,7 @@ export type TradeLineItemQuery = z.infer<typeof TradeLineItemQuery>;
107
108
 
108
109
  export const TradeLineItemFiltersQuery = z.object({
109
110
  accountId: accountIdSchema.optional(),
110
- hasSubdoc: z.preprocess(
111
- (val) =>
112
- val === 'true' || val === '1'
113
- ? true
114
- : val === 'false' || val === '0'
115
- ? false
116
- : val,
117
- z.boolean().optional(),
118
- ),
111
+ hasSubdoc: StringToBooleanSchema.optional(),
119
112
  saStatus: z.nativeEnum(SignatureStatus).optional(),
120
113
  offeringId: z.lazy(() => offeringIdSchema).optional(),
121
114
  search: z.string().max(50).optional(),
@@ -39,7 +39,7 @@ import {
39
39
  import {
40
40
  tradeLineItemIdSchema,
41
41
  TradeLineItemZod,
42
- } from './trade-line-item.type';
42
+ } from './trade-line-item.types';
43
43
  import { TradeAdjustmentZod } from './trade-adjustment.type';
44
44
  import { TransactionZod } from './transaction.types';
45
45
  import { accountIdSchema } from './account.types';