@dragonmastery/dragoncore-shared 0.0.9 → 0.0.10

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/dist/index.mjs ADDED
@@ -0,0 +1,1965 @@
1
+ import { z } from "zod";
2
+
3
+ //#region src/validation/attachment/attachment_filters_zod.ts
4
+ const AttachmentFiltersSchema = z.object({
5
+ record_id: z.string(),
6
+ record_type: z.string(),
7
+ filters: z.object({
8
+ start_date: z.string().optional().nullable(),
9
+ end_date: z.string().optional().nullable(),
10
+ limit: z.number().optional().nullable(),
11
+ cursor: z.string().optional().nullable(),
12
+ folder_id: z.string().optional().nullable(),
13
+ include_folders: z.boolean().optional().default(true)
14
+ }).optional().nullable()
15
+ });
16
+
17
+ //#endregion
18
+ //#region src/validation/attachment/attachment_folder_input_zod.ts
19
+ const AttachmentFolderBaseSchema = z.object({
20
+ record_id: z.string(),
21
+ record_type: z.string(),
22
+ sanitized_name: z.string(),
23
+ original_name: z.string(),
24
+ description: z.string().optional().nullable(),
25
+ metadata: z.string().optional().nullable(),
26
+ parent_folder_id: z.string().optional().nullable(),
27
+ file_count: z.number().optional()
28
+ });
29
+ const AttachmentFolderCreateSchema = AttachmentFolderBaseSchema;
30
+ const AttachmentFolderUpdateSchema = AttachmentFolderBaseSchema.extend({
31
+ id: z.string(),
32
+ record_id: z.string().optional(),
33
+ record_type: z.string().optional(),
34
+ sanitized_name: z.string().optional(),
35
+ original_name: z.string().optional(),
36
+ parent_folder_id: z.string().optional().nullable()
37
+ });
38
+
39
+ //#endregion
40
+ //#region src/validation/attachment/attachment_folder_read_zod.ts
41
+ const AttachmentFolderReadSchema = AttachmentFolderCreateSchema.extend({
42
+ id: z.string(),
43
+ file_count: z.number().optional(),
44
+ created_at: z.string(),
45
+ created_by: z.string(),
46
+ updated_at: z.string().optional().nullable(),
47
+ updated_by: z.string().optional().nullable(),
48
+ archived_at: z.string().optional().nullable(),
49
+ archived_by: z.string().optional().nullable(),
50
+ deleted_at: z.string().optional().nullable(),
51
+ deleted_by: z.string().optional().nullable()
52
+ });
53
+
54
+ //#endregion
55
+ //#region src/validation/attachment/attachment_input_zod.ts
56
+ const AttachmentBaseSchema = z.object({
57
+ record_id: z.string(),
58
+ record_type: z.string(),
59
+ sanitized_name: z.string(),
60
+ original_name: z.string(),
61
+ content_type: z.string(),
62
+ file_size: z.string(),
63
+ description: z.string().optional().nullable(),
64
+ metadata: z.string().optional().nullable(),
65
+ folder_id: z.string().optional().nullable()
66
+ });
67
+ const AttachmentCreateSchema = AttachmentBaseSchema;
68
+ const AttachmentUpdateSchema = AttachmentBaseSchema.extend({
69
+ id: z.string(),
70
+ record_id: z.string().optional(),
71
+ record_type: z.string().optional(),
72
+ sanitized_name: z.string().optional(),
73
+ original_name: z.string().optional(),
74
+ content_type: z.string().optional(),
75
+ file_size: z.string().optional(),
76
+ folder_id: z.string().optional().nullable()
77
+ });
78
+
79
+ //#endregion
80
+ //#region src/validation/attachment/attachment_read_zod.ts
81
+ const AttachmentReadSchema = AttachmentCreateSchema.extend({
82
+ id: z.string(),
83
+ created_at: z.string(),
84
+ created_by: z.string(),
85
+ updated_at: z.string().optional().nullable(),
86
+ updated_by: z.string().optional().nullable(),
87
+ archived_at: z.string().optional().nullable(),
88
+ archived_by: z.string().optional().nullable(),
89
+ deleted_at: z.string().optional().nullable(),
90
+ deleted_by: z.string().optional().nullable()
91
+ });
92
+
93
+ //#endregion
94
+ //#region src/validation/attachment/attachment_page_zod.ts
95
+ const AttachmentPageSchema = z.object({
96
+ files: z.array(AttachmentReadSchema),
97
+ folders: z.array(AttachmentFolderReadSchema),
98
+ pageInfo: z.object({
99
+ hasNextPage: z.boolean(),
100
+ endCursor: z.string().optional().nullable()
101
+ })
102
+ });
103
+
104
+ //#endregion
105
+ //#region src/validation/login_zod.ts
106
+ const isCommonPassword = (password) => {
107
+ return [
108
+ "password",
109
+ "12345678",
110
+ "qwerty123",
111
+ "admin1234"
112
+ ].includes(password.toLowerCase());
113
+ };
114
+ const passwordSchema = z.string().min(8, { message: "Password must be at least 8 characters long" }).max(64, { message: "Password must not exceed 64 characters" }).refine((password) => !/^\s|\s$/.test(password), { message: "Password must not have leading or trailing whitespace" }).refine((password) => !isCommonPassword(password), { message: "Password is too common and easily guessed" });
115
+ const loginSchema = z.object({
116
+ email: z.string().trim().toLowerCase().min(3, "Please enter your email.").email("The email address is badly formatted."),
117
+ password: passwordSchema
118
+ });
119
+
120
+ //#endregion
121
+ //#region src/validation/change_password_zod.ts
122
+ const changePasswordSchema = z.object({ passwords: z.object({
123
+ current_password: z.string().min(8).max(64),
124
+ new_password: passwordSchema,
125
+ new_password_confirm: z.string()
126
+ }).refine((data) => data.new_password === data.new_password_confirm, {
127
+ message: "Passwords do not match",
128
+ path: ["new_password_confirm"]
129
+ }).refine((data) => data.new_password !== data.current_password, {
130
+ message: "New password must be different from current password",
131
+ path: ["new_password"]
132
+ }) });
133
+
134
+ //#endregion
135
+ //#region src/validation/common/filter_operators_zod.ts
136
+ /**
137
+ * Individual operator constants - use these instead of string literals
138
+ * All operators use shorthand names for URL compatibility
139
+ */
140
+ const OPERATORS = {
141
+ EQUALS: "eq",
142
+ NOT_EQUALS: "ne",
143
+ GREATER_THAN: "gt",
144
+ GREATER_THAN_OR_EQUAL: "gte",
145
+ LESS_THAN: "lt",
146
+ LESS_THAN_OR_EQUAL: "lte",
147
+ BETWEEN: "between",
148
+ CONTAINS: "contains",
149
+ STARTS_WITH: "sw",
150
+ ENDS_WITH: "ew",
151
+ IS_ONE_OF: "in",
152
+ IS_NOT_ONE_OF: "notIn",
153
+ IS_EMPTY: "isEmpty",
154
+ IS_NOT_EMPTY: "isNotEmpty"
155
+ };
156
+ /**
157
+ * String operators - shorthand names for URL compatibility
158
+ * Only operators that make sense for string filtering
159
+ */
160
+ const StringOperatorSchema = z.enum([
161
+ OPERATORS.EQUALS,
162
+ OPERATORS.NOT_EQUALS,
163
+ OPERATORS.CONTAINS,
164
+ OPERATORS.STARTS_WITH,
165
+ OPERATORS.ENDS_WITH,
166
+ OPERATORS.IS_ONE_OF,
167
+ OPERATORS.IS_NOT_ONE_OF
168
+ ]);
169
+ /**
170
+ * Date operators - shorthand names for URL compatibility
171
+ * Only operators that make sense for date filtering
172
+ */
173
+ const DateOperatorSchema = z.enum([
174
+ OPERATORS.EQUALS,
175
+ OPERATORS.NOT_EQUALS,
176
+ OPERATORS.GREATER_THAN,
177
+ OPERATORS.GREATER_THAN_OR_EQUAL,
178
+ OPERATORS.LESS_THAN,
179
+ OPERATORS.LESS_THAN_OR_EQUAL,
180
+ OPERATORS.BETWEEN,
181
+ OPERATORS.IS_EMPTY,
182
+ OPERATORS.IS_NOT_EMPTY
183
+ ]);
184
+ /**
185
+ * Number operators - shorthand names for URL compatibility
186
+ * Only operators that make sense for number filtering
187
+ */
188
+ const NumberOperatorSchema = z.enum([
189
+ OPERATORS.EQUALS,
190
+ OPERATORS.NOT_EQUALS,
191
+ OPERATORS.GREATER_THAN,
192
+ OPERATORS.GREATER_THAN_OR_EQUAL,
193
+ OPERATORS.LESS_THAN,
194
+ OPERATORS.LESS_THAN_OR_EQUAL,
195
+ OPERATORS.BETWEEN,
196
+ OPERATORS.IS_ONE_OF,
197
+ OPERATORS.IS_NOT_ONE_OF
198
+ ]);
199
+ /**
200
+ * Shared operator enum for equality comparisons (used by boolean and enum filters)
201
+ */
202
+ const EqualityOperatorSchema = z.enum([OPERATORS.EQUALS, OPERATORS.NOT_EQUALS]);
203
+ /**
204
+ * Shared operator enum for equality and array operations (used by enum filters)
205
+ */
206
+ const EqualityArrayOperatorSchema = z.enum([
207
+ OPERATORS.EQUALS,
208
+ OPERATORS.NOT_EQUALS,
209
+ OPERATORS.IS_ONE_OF,
210
+ OPERATORS.IS_NOT_ONE_OF
211
+ ]);
212
+ /**
213
+ * Data type enum for filters (spec-compliant)
214
+ */
215
+ const DataTypeSchema = z.enum([
216
+ "string",
217
+ "number",
218
+ "date",
219
+ "boolean",
220
+ "enum",
221
+ "money",
222
+ "percentage"
223
+ ]);
224
+ /**
225
+ * Base filter schema - spec-compliant structure
226
+ * Field name is the object key, not part of the filter object
227
+ * All filters must include: operator
228
+ * Optional: value, values, caseSensitive
229
+ * Type is inferred from registry using the field name (object key)
230
+ */
231
+ const BaseFilterSchema = z.object({
232
+ operator: z.string(),
233
+ value: z.any().optional(),
234
+ values: z.array(z.any()).optional(),
235
+ caseSensitive: z.boolean().optional()
236
+ });
237
+ /**
238
+ * String filter input - Spec-compliant structure
239
+ * Restricted operators: only operators that make sense for string filtering
240
+ * Field name is the object key, type is inferred from registry
241
+ *
242
+ * Examples:
243
+ * - Equality: { operator: "eq", value: "search" }
244
+ * - Contains: { operator: "contains", value: "search", caseSensitive: false }
245
+ * - Starts with: { operator: "sw", value: "prefix" }
246
+ * - In array: { operator: "in", values: ["option1", "option2"] }
247
+ */
248
+ const StringFilterSchema = BaseFilterSchema.extend({
249
+ operator: StringOperatorSchema,
250
+ value: z.string().optional(),
251
+ values: z.array(z.string()).optional(),
252
+ caseSensitive: z.boolean().optional()
253
+ }).refine((data) => {
254
+ if (data.operator === OPERATORS.IS_ONE_OF || data.operator === OPERATORS.IS_NOT_ONE_OF) return data.values !== void 0 && data.values.length > 0;
255
+ return data.value !== void 0;
256
+ }, { message: "value is required for non-array operators, values array is required for in/notIn" });
257
+ /**
258
+ * Boolean filter input - Spec-compliant structure
259
+ * Field name is the object key, type is inferred from registry
260
+ *
261
+ * Examples:
262
+ * - Equality: { operator: "eq", value: true }
263
+ * - Not equals: { operator: "ne", value: false }
264
+ */
265
+ const BooleanFilterSchema = BaseFilterSchema.extend({
266
+ operator: EqualityOperatorSchema,
267
+ value: z.boolean()
268
+ });
269
+ /**
270
+ * Number filter input - Spec-compliant structure
271
+ * Restricted operators: only operators that make sense for number filtering
272
+ * Useful for ranges (credits > 100, priority <= 3, etc.)
273
+ *
274
+ * Examples:
275
+ * - Equality: { field: "age", operator: "eq", value: 100, type: "number" }
276
+ * - Greater than: { field: "price", operator: "gt", value: 100, type: "number" }
277
+ * - Range: { field: "score", operator: "gte", value: 50, type: "number" }
278
+ * - In array: { field: "priority", operator: "in", values: [1, 2, 3], type: "number" }
279
+ */
280
+ const NumberFilterSchema = BaseFilterSchema.extend({
281
+ operator: NumberOperatorSchema,
282
+ value: z.number().optional(),
283
+ values: z.array(z.number()).optional()
284
+ }).refine((data) => {
285
+ if (data.operator === OPERATORS.BETWEEN) return data.values !== void 0 && data.values.length === 2;
286
+ if (data.operator === OPERATORS.IS_ONE_OF || data.operator === OPERATORS.IS_NOT_ONE_OF) return data.values !== void 0 && data.values.length > 0;
287
+ return data.value !== void 0;
288
+ }, { message: "value is required for non-array operators, values array is required for in/notIn, exactly 2 values required for between" }).refine((data) => {
289
+ if (data.operator === OPERATORS.BETWEEN && data.values && data.values.length === 2) {
290
+ const [min, max] = data.values;
291
+ return min !== void 0 && max !== void 0 && min <= max;
292
+ }
293
+ return true;
294
+ }, {
295
+ message: "For between operator, values[0] must be less than or equal to values[1]",
296
+ path: ["values"]
297
+ });
298
+ /**
299
+ * Date/DateTime string filter - Spec-compliant structure
300
+ * Restricted operators: only operators that make sense for date filtering
301
+ * Dates should be in ISO 8601 format: YYYY-MM-DD or full datetime
302
+ *
303
+ * Examples:
304
+ * - Exact match: { field: "createdAt", operator: "eq", value: "2024-01-01", type: "date" }
305
+ * - After date: { field: "expiresAt", operator: "gte", value: "2024-01-01", type: "date" }
306
+ * - Before date: { field: "deadline", operator: "lt", value: "2024-12-31", type: "date" }
307
+ * - Is empty: { field: "deletedAt", operator: "isEmpty", type: "date" } // No value property
308
+ * - Is not empty: { field: "publishedAt", operator: "isNotEmpty", type: "date" } // No value property
309
+ */
310
+ const DateFilterSchema = z.union([
311
+ BaseFilterSchema.extend({ operator: z.enum([OPERATORS.IS_EMPTY, OPERATORS.IS_NOT_EMPTY]) }).refine((data) => data.value === void 0 && data.values === void 0, { message: "isEmpty and isNotEmpty operators must not have value or values properties" }),
312
+ BaseFilterSchema.extend({
313
+ operator: z.literal(OPERATORS.BETWEEN),
314
+ values: z.array(z.string()).length(2)
315
+ }).refine((data) => data.values !== void 0 && data.values.length === 2, { message: "between operator requires exactly 2 values" }).refine((data) => {
316
+ if (data.values && data.values.length === 2) {
317
+ const [startDate, endDate] = data.values;
318
+ if (startDate !== void 0 && endDate !== void 0) return new Date(startDate) <= new Date(endDate);
319
+ }
320
+ return true;
321
+ }, {
322
+ message: "For between operator, values[0] must represent an earlier or equal date/time compared to values[1]",
323
+ path: ["values"]
324
+ }),
325
+ BaseFilterSchema.extend({
326
+ operator: z.enum([
327
+ OPERATORS.EQUALS,
328
+ OPERATORS.NOT_EQUALS,
329
+ OPERATORS.GREATER_THAN,
330
+ OPERATORS.GREATER_THAN_OR_EQUAL,
331
+ OPERATORS.LESS_THAN,
332
+ OPERATORS.LESS_THAN_OR_EQUAL
333
+ ]),
334
+ value: z.string()
335
+ }).refine((data) => data.value !== void 0, { message: "value is required for non-null operators" })
336
+ ]);
337
+ /**
338
+ * Enum filter input - Spec-compliant structure
339
+ * Supports both single value (eq/ne) and multiple values (in/notIn)
340
+ * Field name is the object key, type is inferred from registry
341
+ *
342
+ * Usage: createEnumFilter(MyEnumSchema)
343
+ *
344
+ * Examples:
345
+ * - Equality: { operator: "eq", value: "OPTION_A" }
346
+ * - Not equals: { operator: "ne", value: "OPTION_B" }
347
+ * - In array: { operator: "in", values: ["OPTION_A", "OPTION_B"] }
348
+ * - Not in array: { operator: "notIn", values: ["OPTION_C", "OPTION_D"] }
349
+ */
350
+ function createEnumFilter(enumSchema) {
351
+ return BaseFilterSchema.extend({
352
+ operator: EqualityArrayOperatorSchema,
353
+ value: enumSchema.optional(),
354
+ values: z.array(enumSchema).optional()
355
+ }).refine((data) => {
356
+ if (data.operator === OPERATORS.IS_ONE_OF || data.operator === OPERATORS.IS_NOT_ONE_OF) return data.values !== void 0 && data.values.length > 0;
357
+ return data.value !== void 0;
358
+ }, { message: "value is required for non-array operators, values array is required for in/notIn" });
359
+ }
360
+ /**
361
+ * Filter configuration - Spec-compliant structure
362
+ * Uses a flat object mapping field names to filter value objects
363
+ * Field name is the object key, type is inferred from registry
364
+ * All filters are combined with implicit AND logic
365
+ * OR logic within a single field is supported through isOneOf/isNotOneOf operators
366
+ *
367
+ * Examples:
368
+ * {
369
+ * "status": { operator: "eq", value: "active" },
370
+ * "age": { operator: "gt", value: 18 }
371
+ * }
372
+ */
373
+ const FilterConfigSchema = z.record(z.string(), BaseFilterSchema);
374
+
375
+ //#endregion
376
+ //#region src/validation/common/pagination_zod.ts
377
+ /**
378
+ * Sort direction enum
379
+ */
380
+ const SortDirectionSchema = z.enum(["asc", "desc"]);
381
+ const PaginationFiltersSchema = z.object({
382
+ first: z.number().min(1).max(100).optional(),
383
+ after: z.string().optional(),
384
+ sortBy: z.string().optional(),
385
+ sortDirection: SortDirectionSchema.optional(),
386
+ paginationToken: z.string().optional()
387
+ });
388
+ const PageInfoSchema = z.object({
389
+ hasNextPage: z.boolean(),
390
+ hasPreviousPage: z.boolean(),
391
+ prevPageCursor: z.string().nullish(),
392
+ nextPageCursor: z.string().optional(),
393
+ currentPageIndex: z.number().optional(),
394
+ paginationToken: z.string().optional()
395
+ });
396
+ function createPaginatedSchema(itemSchema) {
397
+ return z.object({
398
+ items: z.array(itemSchema),
399
+ pageInfo: PageInfoSchema
400
+ });
401
+ }
402
+
403
+ //#endregion
404
+ //#region src/validation/common/regex_patterns.ts
405
+ /**
406
+ * Common regex patterns for validation
407
+ */
408
+ /**
409
+ * Decimal amount regex - allows up to 2 decimal places
410
+ * Matches: "100", "100.00", "100.5", "0.99", etc.
411
+ * Also allows empty string for optional fields
412
+ */
413
+ const DECIMAL_AMOUNT_REGEX = /^((\d*\.?\d{1,2}|\d+)|)$/;
414
+ /**
415
+ * Decimal amount regex error message
416
+ */
417
+ const DECIMAL_AMOUNT_ERROR_MESSAGE = "Must be a valid dollar amount with up to 2 decimal places or empty";
418
+ /**
419
+ * Decimal amount regex for 2 decimals or empty
420
+ * Matches: "100.00", "0.99", "1234.56", etc. or empty string
421
+ * Requires exactly 2 decimal places when not empty
422
+ */
423
+ const DECIMAL_AMOUNT_2_DECIMALS_OR_EMPTY_REGEX = /^((\d*\.?\d{1,2}|\d+)|)$/;
424
+ /**
425
+ * Decimal amount regex for 2 decimals or empty error message
426
+ */
427
+ const DECIMAL_AMOUNT_2_DECIMALS_OR_EMPTY_ERROR_MESSAGE = "Must be a valid dollar amount with up to 2 decimal places or empty";
428
+
429
+ //#endregion
430
+ //#region src/validation/user/user_enums_zod.ts
431
+ const USER_TYPES = [
432
+ "consumer",
433
+ "lead",
434
+ "staff",
435
+ "super_admin"
436
+ ];
437
+ const DEFAULT_USER_TYPE = USER_TYPES[0];
438
+ const UserTypeEnum = z.enum([...USER_TYPES]);
439
+
440
+ //#endregion
441
+ //#region src/validation/user/user_create_zod.ts
442
+ const createUserSchema = z.object({
443
+ email: z.string().trim().toLowerCase().min(3, "Please enter your email.").email("The email address is badly formatted."),
444
+ user_type: UserTypeEnum,
445
+ password: z.string().optional().nullable()
446
+ });
447
+ const createUserSchemaOutput = z.object({
448
+ id: z.string(),
449
+ email: z.string()
450
+ });
451
+
452
+ //#endregion
453
+ //#region src/validation/credit_transaction/credit_balance_schema.ts
454
+ const CreditBalanceSchema = z.object({
455
+ monthly: z.string(),
456
+ rollover: z.string()
457
+ });
458
+ const AddCreditsSchema = z.object({
459
+ amount: z.string().regex(DECIMAL_AMOUNT_REGEX, DECIMAL_AMOUNT_ERROR_MESSAGE),
460
+ reason: z.string().optional()
461
+ });
462
+ const SetMonthlyAllocationSchema = z.object({ amount: z.string().regex(DECIMAL_AMOUNT_REGEX, DECIMAL_AMOUNT_ERROR_MESSAGE) });
463
+ const ResetMonthlyBalanceSchema = z.object({}).optional();
464
+
465
+ //#endregion
466
+ //#region src/validation/credit_transaction/credit_transaction_read_zod.ts
467
+ /**
468
+ * Transaction types for credit operations
469
+ */
470
+ const CreditTransactionTypeEnum = z.enum([
471
+ "DEDUCTION",
472
+ "REFUND",
473
+ "PURCHASE_ONETIME",
474
+ "PURCHASE_RECURRING",
475
+ "ADJUSTMENT"
476
+ ]);
477
+ /**
478
+ * Schema for a credit transaction record
479
+ */
480
+ const CreditTransactionReadSchema = z.object({
481
+ id: z.string(),
482
+ support_ticket_id: z.string().nullable().optional(),
483
+ amount: z.string(),
484
+ type: CreditTransactionTypeEnum,
485
+ description: z.string().nullable().optional(),
486
+ balance_after: z.string(),
487
+ created_at: z.string(),
488
+ created_by: z.string()
489
+ });
490
+ /**
491
+ * Paginated schema for credit transactions
492
+ */
493
+ const CreditTransactionPageSchema = createPaginatedSchema(CreditTransactionReadSchema);
494
+
495
+ //#endregion
496
+ //#region src/validation/credit_transaction/credit_transaction_filters_zod.ts
497
+ /**
498
+ * Filters for querying credit transactions
499
+ * Supports operator-based filtering for advanced queries
500
+ *
501
+ * All filters support operators for advanced filtering:
502
+ * - Enums: eq, ne, in, notIn
503
+ * - Strings: eq, ne, contains, startsWith, endsWith
504
+ * - Numbers: eq, ne, gt, gte, lt, lte, between
505
+ * - Dates: eq, ne, gt, gte, lt, lte, between
506
+ *
507
+ * Examples:
508
+ * - { type: { operator: "eq", value: "DEDUCTION" } }
509
+ * - { amount: { operator: "gte", value: 100 } }
510
+ * - { created_at: { operator: "gte", value: "2024-01-01" } }
511
+ */
512
+ const CreditTransactionFiltersSchema = PaginationFiltersSchema.extend({
513
+ type: createEnumFilter(CreditTransactionTypeEnum).optional(),
514
+ support_ticket_id: StringFilterSchema.optional(),
515
+ amount: NumberFilterSchema.optional(),
516
+ balance_after: NumberFilterSchema.optional(),
517
+ created_at: DateFilterSchema.optional(),
518
+ created_by: StringFilterSchema.optional(),
519
+ search: z.object({
520
+ query: z.string(),
521
+ searchableFields: z.array(z.string())
522
+ }).optional()
523
+ });
524
+
525
+ //#endregion
526
+ //#region src/validation/forgot_password_zod.ts
527
+ const forgot_password_zod = z.object({ email: z.string().email().trim().toLowerCase() });
528
+
529
+ //#endregion
530
+ //#region src/types/record_types.ts
531
+ /**
532
+ * Record type constants and types for the application
533
+ * These define all the different types of records that can exist in the system
534
+ */
535
+ const RecordTypeValues = [
536
+ "attachment_folder",
537
+ "attachment",
538
+ "user",
539
+ "user_session",
540
+ "user_profile",
541
+ "refresh_token",
542
+ "refresh_token_family",
543
+ "user_subscription",
544
+ "password_reset",
545
+ "record_version",
546
+ "support_ticket",
547
+ "support_ticket_activity",
548
+ "credit_transaction",
549
+ "team_member",
550
+ "client_contact",
551
+ "client_location",
552
+ "client_profile",
553
+ "business_profile",
554
+ "tracker",
555
+ "tracker_activity",
556
+ "team",
557
+ "quote",
558
+ "note",
559
+ "followup",
560
+ "saved_filter",
561
+ "user_pinned_preset",
562
+ "record_subscriber",
563
+ "support_staff"
564
+ ];
565
+ const RecordConst = {
566
+ ATTACHMENT: "attachment",
567
+ ATTACHMENT_FOLDER: "attachment_folder",
568
+ USER: "user",
569
+ USER_SESSION: "user_session",
570
+ USER_PROFILE: "user_profile",
571
+ REFRESH_TOKEN: "refresh_token",
572
+ REFRESH_TOKEN_FAMILY: "refresh_token_family",
573
+ USER_SUBSCRIPTION: "user_subscription",
574
+ PASSWORD_RESET: "password_reset",
575
+ RECORD_VERSION: "record_version",
576
+ SUPPORT_TICKET: "support_ticket",
577
+ SUPPORT_TICKET_ACTIVITY: "support_ticket_activity",
578
+ CREDIT_TRANSACTION: "credit_transaction",
579
+ TEAM_MEMBER: "team_member",
580
+ CLIENT_CONTACT: "client_contact",
581
+ CLIENT_LOCATION: "client_location",
582
+ CLIENT_PROFILE: "client_profile",
583
+ BUSINESS_PROFILE: "business_profile",
584
+ TRACKER: "tracker",
585
+ TRACKER_ACTIVITY: "tracker_activity",
586
+ TEAM: "team",
587
+ NOTE: "note",
588
+ FOLLOWUP: "followup",
589
+ SAVED_FILTER: "saved_filter",
590
+ USER_PINNED_PRESET: "user_pinned_preset",
591
+ RECORD_SUBSCRIBER: "record_subscriber",
592
+ SUPPORT_STAFF: "support_staff"
593
+ };
594
+
595
+ //#endregion
596
+ //#region src/validation/note/note_read_zod.ts
597
+ const RecordTypeEnum$5 = z.enum(RecordTypeValues);
598
+ const NoteReadSchema = z.object({
599
+ id: z.string(),
600
+ record_id: z.string(),
601
+ record_type: RecordTypeEnum$5,
602
+ tag: z.string().optional().nullable(),
603
+ title: z.string().optional().nullable(),
604
+ body: z.string().optional().nullable(),
605
+ original_id: z.number().optional().nullable(),
606
+ is_internal: z.boolean(),
607
+ created_by: z.string(),
608
+ created_by_display_name: z.string().optional().nullable(),
609
+ created_at: z.string(),
610
+ updated_by: z.string().optional().nullable(),
611
+ updated_by_display_name: z.string().optional().nullable(),
612
+ updated_at: z.string().optional().nullable(),
613
+ archived_by: z.string().optional().nullable(),
614
+ archived_at: z.string().optional().nullable(),
615
+ deleted_by: z.string().optional().nullable(),
616
+ deleted_at: z.string().optional().nullable()
617
+ });
618
+
619
+ //#endregion
620
+ //#region src/validation/note/note_create_zod.ts
621
+ const RecordTypeEnum$4 = z.enum(RecordTypeValues);
622
+ const NoteCreateSchema = z.object({
623
+ record_id: z.string(),
624
+ record_type: RecordTypeEnum$4,
625
+ tag: z.string().optional().nullable(),
626
+ title: z.string().optional().nullable(),
627
+ body: z.string().optional().nullable(),
628
+ original_id: z.number().optional().nullable(),
629
+ is_internal: z.boolean().optional().default(false)
630
+ });
631
+
632
+ //#endregion
633
+ //#region src/validation/note/note_update_zod.ts
634
+ const RecordTypeEnum$3 = z.enum(RecordTypeValues);
635
+ const NoteUpdateSchema = z.object({
636
+ id: z.string(),
637
+ record_id: z.string().optional(),
638
+ record_type: RecordTypeEnum$3.optional(),
639
+ tag: z.string().optional().nullable(),
640
+ title: z.string().optional().nullable(),
641
+ body: z.string().optional().nullable(),
642
+ original_id: z.number().optional().nullable(),
643
+ is_internal: z.boolean().optional()
644
+ });
645
+
646
+ //#endregion
647
+ //#region src/validation/note/note_filters_zod.ts
648
+ const RecordTypeFilterEnum = z.enum(RecordTypeValues);
649
+ /**
650
+ * Filters for note
651
+ * Supports operator-based filtering for advanced queries
652
+ *
653
+ * All filters support operators for advanced filtering:
654
+ * - Booleans: eq, ne
655
+ * - Strings: eq, ne, contains, startsWith, endsWith
656
+ * - Dates: eq, ne, gt, gte, lt, lte
657
+ *
658
+ * Examples:
659
+ * - { record_type: { operator: "eq", value: "SUPPORT_TICKET" } }
660
+ * - { title: { operator: "contains", value: "search" } }
661
+ */
662
+ const NoteFiltersSchema = PaginationFiltersSchema.extend({
663
+ record_id: StringFilterSchema.optional(),
664
+ record_type: createEnumFilter(RecordTypeFilterEnum).optional(),
665
+ title: StringFilterSchema.optional(),
666
+ body: StringFilterSchema.optional(),
667
+ tag: StringFilterSchema.optional(),
668
+ created_at: DateFilterSchema.optional(),
669
+ updated_at: DateFilterSchema.optional(),
670
+ is_internal: BooleanFilterSchema.optional(),
671
+ search: z.object({
672
+ query: z.string(),
673
+ searchableFields: z.array(z.string())
674
+ }).optional()
675
+ });
676
+ const NoteFiltersSortDirectionEnum = z.enum(["asc", "desc"]);
677
+
678
+ //#endregion
679
+ //#region src/validation/record_version_zod.ts
680
+ const RecordTypeEnum$2 = z.enum(RecordTypeValues);
681
+ const recordVersionSchema = z.object({
682
+ id: z.string(),
683
+ record_id: z.string(),
684
+ operation: z.string(),
685
+ recorded_at: z.string(),
686
+ record_type: z.string(),
687
+ record: z.string().optional().nullable(),
688
+ old_record: z.string().optional().nullable(),
689
+ auth_uid: z.string().optional().nullable(),
690
+ auth_role: z.string().optional().nullable(),
691
+ auth_username: z.string().optional().nullable()
692
+ });
693
+ const recordVersionPageSchema = z.object({
694
+ items: z.array(recordVersionSchema),
695
+ pageInfo: z.object({
696
+ hasNextPage: z.boolean(),
697
+ endCursor: z.string().optional().nullable()
698
+ })
699
+ });
700
+ const pageInfoSchema = z.object({
701
+ hasNextPage: z.boolean(),
702
+ endCursor: z.string().optional().nullable()
703
+ });
704
+ const recordVersionFiltersSchema = z.object({
705
+ start_date: z.string().optional().nullable(),
706
+ end_date: z.string().optional().nullable(),
707
+ limit: z.number().optional().nullable(),
708
+ cursor: z.string().optional().nullable()
709
+ });
710
+ const recordVersionFiltersInputSchema = z.object({
711
+ record_id: z.string(),
712
+ record_type: RecordTypeEnum$2,
713
+ filters: recordVersionFiltersSchema.optional().nullable()
714
+ });
715
+ /**
716
+ * NEW: Breadcrumb-paginated record version response
717
+ * Use this for new implementations
718
+ * user_display_map: optional map of user_id -> display_name for timeline display (assigned_to, created_by, etc.)
719
+ */
720
+ const recordVersionPageBreadcrumbSchema = createPaginatedSchema(recordVersionSchema).extend({ user_display_map: z.record(z.string(), z.string()).optional() });
721
+ /**
722
+ * NEW: Filters with breadcrumb pagination support
723
+ * Extends PaginationFiltersSchema for consistent pagination
724
+ */
725
+ const recordVersionFiltersBreadcrumbSchema = z.object({
726
+ first: z.number().min(1).max(100).optional(),
727
+ after: z.string().optional(),
728
+ last: z.number().min(1).max(100).optional(),
729
+ before: z.string().optional(),
730
+ sortBy: z.string().optional(),
731
+ sortDirection: SortDirectionSchema.optional(),
732
+ paginationToken: z.string().optional(),
733
+ start_date: z.string().optional().nullable(),
734
+ end_date: z.string().optional().nullable(),
735
+ record_types: z.array(RecordTypeEnum$2).min(1).optional(),
736
+ record_ids: z.array(z.string()).min(1).optional()
737
+ });
738
+ const recordVersionFiltersInputBreadcrumbSchema = z.object({
739
+ record_id: z.string(),
740
+ record_type: RecordTypeEnum$2,
741
+ filters: recordVersionFiltersBreadcrumbSchema.optional().nullable()
742
+ });
743
+ /** Input for listTrackerActivityVersions - tracker ID + optional filters */
744
+ const recordVersionTrackerActivityInputSchema = z.object({
745
+ tracker_id: z.string(),
746
+ filters: recordVersionFiltersBreadcrumbSchema.optional().nullable()
747
+ });
748
+
749
+ //#endregion
750
+ //#region src/validation/saved_filter/saved_filter_create_zod.ts
751
+ /**
752
+ * Schema for creating a saved filter preset
753
+ */
754
+ const SavedFilterCreateSchema = z.object({
755
+ name: z.string().min(1, "Name is required").max(100),
756
+ context: z.string().min(1, "Context is required"),
757
+ route_path: z.string().min(1, "Route path is required"),
758
+ filters: z.record(z.union([z.string(), z.array(z.string())])),
759
+ sort_by: z.string().optional(),
760
+ sort_direction: z.enum(["asc", "desc"]).optional()
761
+ });
762
+
763
+ //#endregion
764
+ //#region src/validation/saved_filter/saved_filter_read_zod.ts
765
+ const SavedFilterReadSchema = SavedFilterCreateSchema.extend({
766
+ id: z.string(),
767
+ user_id: z.string(),
768
+ created_at: z.string(),
769
+ updated_at: z.string().optional()
770
+ });
771
+
772
+ //#endregion
773
+ //#region src/validation/saved_filter/saved_filter_update_zod.ts
774
+ const SavedFilterUpdateSchema = SavedFilterCreateSchema.partial().extend({ id: z.string() });
775
+
776
+ //#endregion
777
+ //#region src/validation/reset_password_zod.ts
778
+ const resetPasswordInputSchema = z.object({ passwords: z.object({
779
+ password: z.string().min(8, { message: "Password must be at least 8 characters long" }).max(64, { message: "Password must not exceed 64 characters" }).refine((password) => !/^\s|\s$/.test(password), { message: "Password must not have leading or trailing whitespace" }).refine((password) => !isCommonPassword(password), { message: "Password is too common and easily guessed" }),
780
+ password_confirm: z.string().min(8)
781
+ }).refine((data) => data.password === data.password_confirm, {
782
+ message: "Passwords do not match",
783
+ path: ["password_confirm"]
784
+ }) });
785
+ const resetPasswordSchema = resetPasswordInputSchema.extend({ token: z.string().min(20) });
786
+
787
+ //#endregion
788
+ //#region src/validation/signup_zod.ts
789
+ const signupSchema = z.object({
790
+ email: z.string().trim().toLowerCase().min(3, "Please enter your email.").email("The email address is badly formatted."),
791
+ passwords: z.object({
792
+ password: passwordSchema,
793
+ password_confirm: z.string().min(1)
794
+ }).refine((data) => data.password === data.password_confirm, {
795
+ message: "Passwords do not match",
796
+ path: ["password_confirm"]
797
+ })
798
+ });
799
+
800
+ //#endregion
801
+ //#region src/validation/record_subscriber/record_subscriber_create_zod.ts
802
+ const RecordTypeEnum$1 = z.enum(RecordTypeValues);
803
+ const RecordSubscriberCreateSchema = z.object({
804
+ record_type: RecordTypeEnum$1,
805
+ record_id: z.string().min(1, "Record ID is required"),
806
+ user_id: z.string().min(1, "User ID is required"),
807
+ subscribed_events: z.array(z.string()).optional().nullable()
808
+ });
809
+
810
+ //#endregion
811
+ //#region src/validation/record_subscriber/record_subscriber_read_zod.ts
812
+ const RecordTypeEnum = z.enum(RecordTypeValues);
813
+ const RecordSubscriberReadSchema = z.object({
814
+ id: z.string().min(1, "ID is required"),
815
+ record_type: RecordTypeEnum,
816
+ record_id: z.string().min(1, "Record ID is required"),
817
+ user_id: z.string().min(1, "User ID is required"),
818
+ user_id_display_name: z.string().optional().nullable(),
819
+ subscribed_events: z.array(z.string()).optional().nullable(),
820
+ created_at: z.string(),
821
+ created_by: z.string(),
822
+ created_by_display_name: z.string().optional().nullable()
823
+ });
824
+
825
+ //#endregion
826
+ //#region src/validation/record_subscriber/record_subscriber_update_zod.ts
827
+ const RecordSubscriberUpdateSchema = z.object({
828
+ id: z.string().min(1, "ID is required"),
829
+ subscribed_events: z.array(z.string()).optional().nullable()
830
+ });
831
+
832
+ //#endregion
833
+ //#region src/validation/support_ticket/support_ticket_shared/support_ticket_enums_zod.ts
834
+ const SupportTicketTypeEnum = [
835
+ "IMPROVEMENT",
836
+ "BUG",
837
+ "FEATURE_REQUEST",
838
+ "OPERATIONAL"
839
+ ];
840
+ const SupportTicketTypeSchema = z.enum(SupportTicketTypeEnum);
841
+ const SupportTicketPriorityEnum = [
842
+ "LOW",
843
+ "MEDIUM",
844
+ "HIGH",
845
+ "CRITICAL"
846
+ ];
847
+ const SupportTicketPriorityNumberEnum = [
848
+ 1,
849
+ 2,
850
+ 3,
851
+ 4
852
+ ];
853
+ /**
854
+ * Enum for feature request priority levels
855
+ */
856
+ const SupportTicketPrioritySchema = z.enum(SupportTicketPriorityEnum);
857
+ /**
858
+ * Numeric priority for create/update DTOs (1–4).
859
+ * Used in forms and API input; read DTOs still return string for display.
860
+ */
861
+ const SupportTicketPriorityNumberSchema = z.number().int().min(1, "Priority must be between 1 and 4").max(4, "Priority must be between 1 and 4");
862
+ /**
863
+ * Numeric mapping for DB storage and proper sorting.
864
+ * Higher number = higher priority.
865
+ */
866
+ const SUPPORT_TICKET_PRIORITY_TO_NUMBER = {
867
+ LOW: 1,
868
+ MEDIUM: 2,
869
+ HIGH: 3,
870
+ CRITICAL: 4
871
+ };
872
+ const SUPPORT_TICKET_NUMBER_TO_PRIORITY = {
873
+ 1: "LOW",
874
+ 2: "MEDIUM",
875
+ 3: "HIGH",
876
+ 4: "CRITICAL"
877
+ };
878
+ function supportTicketPriorityToNumber(priority) {
879
+ return SUPPORT_TICKET_PRIORITY_TO_NUMBER[priority];
880
+ }
881
+ function supportTicketNumberToPriority(num) {
882
+ const p = SUPPORT_TICKET_NUMBER_TO_PRIORITY[num];
883
+ if (!p) return "MEDIUM";
884
+ return p;
885
+ }
886
+ /**
887
+ * Label/value options for priority filter dropdowns (Zinia columns)
888
+ */
889
+ const SUPPORT_TICKET_PRIORITY_FILTER_OPTIONS = [
890
+ {
891
+ label: "Low",
892
+ value: 1
893
+ },
894
+ {
895
+ label: "Medium",
896
+ value: 2
897
+ },
898
+ {
899
+ label: "High",
900
+ value: 3
901
+ },
902
+ {
903
+ label: "Critical",
904
+ value: 4
905
+ }
906
+ ];
907
+ /**
908
+ * Maps numeric priority (1–4) to display label for Zinia SelectField.
909
+ * Use with valueToLabel + valueType: 'number' for number-backed select fields.
910
+ */
911
+ const SUPPORT_TICKET_PRIORITY_NUMBER_TO_LABEL = {
912
+ 1: "Low",
913
+ 2: "Medium",
914
+ 3: "High",
915
+ 4: "Critical"
916
+ };
917
+ /**
918
+ * Enum for customer-facing support_ticket status (computed from approval_status + dev_lifecycle)
919
+ * - PENDING: Awaiting admin review
920
+ * - FOLLOWUP: Approved but not started
921
+ * - IN_PROGRESS: Actively being worked on
922
+ * - VERIFICATION: Work deployed; consumer should verify and leave a comment
923
+ * - COMPLETED: Deployed to production and verified
924
+ * - CANCELLED: Rejected by admin
925
+ */
926
+ const SupportTicketStatusEnum = [
927
+ "PENDING",
928
+ "FOLLOWUP",
929
+ "IN_PROGRESS",
930
+ "VERIFICATION",
931
+ "COMPLETED",
932
+ "CANCELLED"
933
+ ];
934
+ const SupportTicketStatusSchema = z.enum(SupportTicketStatusEnum);
935
+ /**
936
+ * Enum for feature request approval status
937
+ * - PENDING: Awaiting staff decision (customer-submitted)
938
+ * - APPROVED: Approved by staff (with customer credits)
939
+ * - REJECTED: Rejected by staff
940
+ * - INTERNAL: Internal staff support_ticket (no approval/credits needed)
941
+ */
942
+ const SupportTicketApprovalEnum = [
943
+ "PENDING",
944
+ "APPROVED",
945
+ "REJECTED",
946
+ "INTERNAL"
947
+ ];
948
+ const SupportTicketApprovalSchema = z.enum(SupportTicketApprovalEnum);
949
+ /**
950
+ * Enum for internal development lifecycle stages
951
+ * - BACKLOG: Approved but not started
952
+ * - PLANNING: Scoping/design phase
953
+ * - DEVELOPMENT: Actively coding
954
+ * - CODE_REVIEW: In review
955
+ * - TESTING: QA testing
956
+ * - STAGING: On staging environment
957
+ * - PO_APPROVAL: Waiting for PO/customer sign-off
958
+ * - VERIFICATION: Deployed; waiting for consumer to verify and leave a comment
959
+ * - DEPLOYED: Live in production and verified
960
+ * - CANCELLED: Internal task cancelled (only for INTERNAL approval status)
961
+ */
962
+ const SupportTicketDevLifecycleEnum = [
963
+ "PENDING",
964
+ "BACKLOG",
965
+ "PLANNING",
966
+ "DEVELOPMENT",
967
+ "CODE_REVIEW",
968
+ "TESTING",
969
+ "STAGING",
970
+ "PO_APPROVAL",
971
+ "VERIFICATION",
972
+ "DEPLOYED",
973
+ "CANCELLED"
974
+ ];
975
+ const SupportTicketDevLifecycleSchema = z.enum(SupportTicketDevLifecycleEnum);
976
+ /**
977
+ * Dev lifecycle stages that can be manually set via update form
978
+ * Excludes workflow-only stages:
979
+ * - PENDING: Set by revert workflow
980
+ * - DEPLOYED: Set by completeSupportTicket workflow
981
+ * Staff can manually progress through: BACKLOG → PLANNING → DEVELOPMENT → CODE_REVIEW → TESTING → STAGING → PO_APPROVAL → VERIFICATION
982
+ */
983
+ const SupportTicketDevLifecycleUpdateEnum = [
984
+ "BACKLOG",
985
+ "PLANNING",
986
+ "DEVELOPMENT",
987
+ "CODE_REVIEW",
988
+ "TESTING",
989
+ "STAGING",
990
+ "PO_APPROVAL",
991
+ "VERIFICATION"
992
+ ];
993
+ const SupportTicketDevLifecycleUpdateSchema = z.enum(SupportTicketDevLifecycleUpdateEnum);
994
+ /**
995
+ * Label/value options for type, approval, dev lifecycle, and status filter dropdowns
996
+ */
997
+ const SUPPORT_TICKET_TYPE_FILTER_OPTIONS = [
998
+ {
999
+ label: "Bug",
1000
+ value: "BUG"
1001
+ },
1002
+ {
1003
+ label: "Feature",
1004
+ value: "FEATURE_REQUEST"
1005
+ },
1006
+ {
1007
+ label: "Improvement",
1008
+ value: "IMPROVEMENT"
1009
+ },
1010
+ {
1011
+ label: "Operational",
1012
+ value: "OPERATIONAL"
1013
+ }
1014
+ ];
1015
+ const SUPPORT_TICKET_APPROVAL_FILTER_OPTIONS = [
1016
+ {
1017
+ label: "Pending",
1018
+ value: "PENDING"
1019
+ },
1020
+ {
1021
+ label: "Approved",
1022
+ value: "APPROVED"
1023
+ },
1024
+ {
1025
+ label: "Rejected",
1026
+ value: "REJECTED"
1027
+ },
1028
+ {
1029
+ label: "Internal",
1030
+ value: "INTERNAL"
1031
+ }
1032
+ ];
1033
+ const SUPPORT_TICKET_DEV_LIFECYCLE_FILTER_OPTIONS = [
1034
+ {
1035
+ label: "Pending",
1036
+ value: "PENDING"
1037
+ },
1038
+ {
1039
+ label: "Backlog",
1040
+ value: "BACKLOG"
1041
+ },
1042
+ {
1043
+ label: "Planning",
1044
+ value: "PLANNING"
1045
+ },
1046
+ {
1047
+ label: "Dev",
1048
+ value: "DEVELOPMENT"
1049
+ },
1050
+ {
1051
+ label: "Review",
1052
+ value: "CODE_REVIEW"
1053
+ },
1054
+ {
1055
+ label: "Testing",
1056
+ value: "TESTING"
1057
+ },
1058
+ {
1059
+ label: "Staging",
1060
+ value: "STAGING"
1061
+ },
1062
+ {
1063
+ label: "PO Approval",
1064
+ value: "PO_APPROVAL"
1065
+ },
1066
+ {
1067
+ label: "Verification",
1068
+ value: "VERIFICATION"
1069
+ },
1070
+ {
1071
+ label: "Live",
1072
+ value: "DEPLOYED"
1073
+ },
1074
+ {
1075
+ label: "Cancelled",
1076
+ value: "CANCELLED"
1077
+ }
1078
+ ];
1079
+ const SUPPORT_TICKET_STATUS_FILTER_OPTIONS = [
1080
+ {
1081
+ label: "Pending",
1082
+ value: "PENDING"
1083
+ },
1084
+ {
1085
+ label: "Todo",
1086
+ value: "FOLLOWUP"
1087
+ },
1088
+ {
1089
+ label: "Active",
1090
+ value: "IN_PROGRESS"
1091
+ },
1092
+ {
1093
+ label: "Verification",
1094
+ value: "VERIFICATION"
1095
+ },
1096
+ {
1097
+ label: "Done",
1098
+ value: "COMPLETED"
1099
+ },
1100
+ {
1101
+ label: "Cancelled",
1102
+ value: "CANCELLED"
1103
+ }
1104
+ ];
1105
+ /**
1106
+ * Filter-specific enum instances
1107
+ * These are separate Zod objects with the same values as above,
1108
+ * allowing GraphQL to generate distinct enum types for filters vs domain objects
1109
+ */
1110
+ const SupportTicketTypeFilterSchema = z.enum(SupportTicketTypeEnum);
1111
+ const SupportTicketPriorityFilterSchema = z.enum(SupportTicketPriorityEnum);
1112
+ const SupportTicketStatusFilterSchema = z.enum(SupportTicketStatusEnum);
1113
+ const SupportTicketApprovalFilterSchema = z.enum(SupportTicketApprovalEnum);
1114
+ const SupportTicketDevLifecycleFilterSchema = z.enum(SupportTicketDevLifecycleEnum);
1115
+
1116
+ //#endregion
1117
+ //#region src/validation/support_ticket/support_ticket_customer/customer_input_zod.ts
1118
+ /**
1119
+ * Schema for creating new support_ticket (customer)
1120
+ */
1121
+ const CustomerSupportTicketCreateSchema = z.object({
1122
+ title: z.string().min(5, "Please add a more descriptive title (at least 5 characters)").max(256, "Title is too long - keep it brief (under 256 characters)"),
1123
+ description: z.string().optional().nullable(),
1124
+ type: SupportTicketTypeSchema.default("FEATURE_REQUEST"),
1125
+ priority: SupportTicketPriorityNumberSchema.default(SUPPORT_TICKET_PRIORITY_TO_NUMBER.MEDIUM)
1126
+ });
1127
+ /**
1128
+ * Schema for updating support_ticket (customer - PENDING items only)
1129
+ */
1130
+ const CustomerSupportTicketUpdateSchema = CustomerSupportTicketCreateSchema.extend({ id: z.string() });
1131
+
1132
+ //#endregion
1133
+ //#region src/validation/support_ticket/support_ticket_customer/customer_read_zod.ts
1134
+ /**
1135
+ * Customer-specific read schema - ONLY includes fields customers can see
1136
+ * Excludes all staff-only fields like internalNotes, devLifecycle, timeline tracking, etc.
1137
+ */
1138
+ const CustomerSupportTicketReadSchema = z.object({
1139
+ id: z.string(),
1140
+ display_id: z.string().optional().nullable(),
1141
+ display_id_prefix: z.string().optional().nullable(),
1142
+ title: z.string(),
1143
+ description: z.string(),
1144
+ type: SupportTicketTypeSchema,
1145
+ priority: SupportTicketPrioritySchema,
1146
+ status: SupportTicketStatusSchema,
1147
+ is_locked: z.boolean(),
1148
+ created_by_display_name: z.string().optional().nullable(),
1149
+ credit_value: z.string().optional().nullable(),
1150
+ start_at: z.string().optional().nullable(),
1151
+ target_at: z.string().optional().nullable(),
1152
+ completed_at: z.string().optional().nullable(),
1153
+ locked_approval_at: z.string().optional().nullable(),
1154
+ created_by: z.string().optional().nullable(),
1155
+ created_at: z.string().optional().nullable(),
1156
+ updated_by: z.string().optional().nullable(),
1157
+ updated_at: z.string().optional().nullable(),
1158
+ archived_at: z.string().optional().nullable(),
1159
+ my_subscription: RecordSubscriberReadSchema.nullable().optional()
1160
+ });
1161
+ /**
1162
+ * Paginated customer support_ticket response
1163
+ */
1164
+ const CustomerSupportTicketPageSchema = createPaginatedSchema(CustomerSupportTicketReadSchema);
1165
+
1166
+ //#endregion
1167
+ //#region src/validation/support_ticket/support_ticket_customer/customer_support_ticket_filters_zod.ts
1168
+ /**
1169
+ * Customer-facing filters for support_ticket
1170
+ * Includes all fields customers can see and filter on
1171
+ *
1172
+ * - Cannot filter by internal fields (isInternalOnly, approvalStatus, devLifecycle)
1173
+ * - Can filter on all visible fields including credits, requester info, etc.
1174
+ */
1175
+ const CustomerSupportTicketFiltersSchema = PaginationFiltersSchema.extend({
1176
+ type: createEnumFilter(SupportTicketTypeFilterSchema).optional(),
1177
+ status: createEnumFilter(SupportTicketStatusFilterSchema).optional(),
1178
+ priority: NumberFilterSchema.optional(),
1179
+ title: StringFilterSchema.optional(),
1180
+ description: StringFilterSchema.optional(),
1181
+ created_by: StringFilterSchema.optional(),
1182
+ is_locked: BooleanFilterSchema.optional(),
1183
+ credit_value: NumberFilterSchema.optional(),
1184
+ created_at: DateFilterSchema.optional(),
1185
+ updated_at: DateFilterSchema.optional(),
1186
+ start_at: DateFilterSchema.optional(),
1187
+ target_at: DateFilterSchema.optional(),
1188
+ completed_at: DateFilterSchema.optional(),
1189
+ search: z.object({
1190
+ query: z.string(),
1191
+ searchableFields: z.array(z.string())
1192
+ }).optional()
1193
+ });
1194
+
1195
+ //#endregion
1196
+ //#region src/validation/support_ticket/support_ticket_staff/staff_read_zod.ts
1197
+ /**
1198
+ * Staff-specific read schema - includes ALL fields including staff-only data
1199
+ * This is what staff see when they query support_ticket (includes internal notes, dev lifecycle, etc.)
1200
+ */
1201
+ const StaffSupportTicketReadSchema = z.object({
1202
+ id: z.string(),
1203
+ display_id: z.string().optional().nullable(),
1204
+ display_id_prefix: z.string().optional().nullable(),
1205
+ title: z.string(),
1206
+ description: z.string(),
1207
+ type: SupportTicketTypeSchema,
1208
+ priority: SupportTicketPrioritySchema,
1209
+ status: SupportTicketStatusSchema,
1210
+ approval_status: SupportTicketApprovalSchema,
1211
+ is_locked: z.boolean(),
1212
+ can_delete: z.boolean(),
1213
+ created_by_display_name: z.string().optional().nullable(),
1214
+ assigned_to: z.string().optional().nullable(),
1215
+ assigned_to_display_name: z.string().optional().nullable(),
1216
+ dev_lifecycle: SupportTicketDevLifecycleSchema.optional().nullable(),
1217
+ credit_value: z.string().optional().nullable(),
1218
+ delivered_value: z.string().optional().nullable(),
1219
+ start_at: z.string().optional().nullable(),
1220
+ target_at: z.string().optional().nullable(),
1221
+ completed_at: z.string().optional().nullable(),
1222
+ locked_approval_at: z.string().optional().nullable(),
1223
+ created_by: z.string().optional().nullable(),
1224
+ created_at: z.string().optional().nullable(),
1225
+ updated_by: z.string().optional().nullable(),
1226
+ updated_by_display_name: z.string().optional().nullable(),
1227
+ updated_at: z.string().optional().nullable(),
1228
+ archived_at: z.string().optional().nullable(),
1229
+ archived_by: z.string().optional().nullable()
1230
+ });
1231
+ /**
1232
+ * Paginated staff support_ticket response
1233
+ */
1234
+ const StaffSupportTicketPageSchema = createPaginatedSchema(StaffSupportTicketReadSchema);
1235
+
1236
+ //#endregion
1237
+ //#region src/validation/support_ticket/support_ticket_staff/staff_support_ticket_filters_zod.ts
1238
+ /**
1239
+ * Staff/Admin filters for support_ticket
1240
+ * Full access to all filtering capabilities including internal fields
1241
+ *
1242
+ * All filters support operators for advanced filtering:
1243
+ * - Enums: eq, ne
1244
+ * - Booleans: eq, ne
1245
+ * - Strings: eq, ne, contains, startsWith, endsWith
1246
+ * - Numbers: eq, ne, gt, gte, lt, lte
1247
+ * - Dates: eq, ne, gt, gte, lt, lte
1248
+ *
1249
+ * Examples:
1250
+ * - { type: { operator: "eq", value: "BUG" } }
1251
+ * - { approvalStatus: { operator: "eq", value: "INTERNAL" } } // Filter for internal tasks
1252
+ * - { title: { operator: "contains", value: "search" } }
1253
+ * - { creditValue: { operator: "gte", value: 100 } }
1254
+ * - { createdAt: { operator: "gte", value: "2024-01-01" } }
1255
+ */
1256
+ const StaffSupportTicketFiltersSchema = PaginationFiltersSchema.extend({
1257
+ type: createEnumFilter(SupportTicketTypeFilterSchema).optional(),
1258
+ status: createEnumFilter(SupportTicketStatusFilterSchema).optional(),
1259
+ approval_status: createEnumFilter(SupportTicketApprovalFilterSchema).optional(),
1260
+ priority: NumberFilterSchema.optional(),
1261
+ dev_lifecycle: createEnumFilter(SupportTicketDevLifecycleFilterSchema).optional(),
1262
+ created_by: StringFilterSchema.optional(),
1263
+ title: StringFilterSchema.optional(),
1264
+ description: StringFilterSchema.optional(),
1265
+ is_locked: BooleanFilterSchema.optional(),
1266
+ credit_value: NumberFilterSchema.optional(),
1267
+ delivered_value: NumberFilterSchema.optional(),
1268
+ created_at: DateFilterSchema.optional(),
1269
+ updated_at: DateFilterSchema.optional(),
1270
+ start_at: DateFilterSchema.optional(),
1271
+ target_at: DateFilterSchema.optional(),
1272
+ completed_at: DateFilterSchema.optional(),
1273
+ search: z.object({
1274
+ query: z.string(),
1275
+ searchableFields: z.array(z.string())
1276
+ }).optional()
1277
+ });
1278
+
1279
+ //#endregion
1280
+ //#region src/validation/support_ticket/support_ticket_staff/staff_update_zod.ts
1281
+ /**
1282
+ * Base schema for staff support_ticket updates (general editing)
1283
+ *
1284
+ * Workflow-specific fields NOT included here:
1285
+ * - approvalStatus: Use approve/reject/revert workflows, or set to INTERNAL at creation
1286
+ *
1287
+ * Note: completeSupportTicket workflow sets deliveredValue + completedAt initially,
1288
+ * but they can be adjusted here afterwards for corrections.
1289
+ *
1290
+ * Business rules validated in backend:
1291
+ * - Can't edit creditValue unless PENDING (INTERNAL support_ticket can't have credits)
1292
+ * - Can't edit devLifecycle unless APPROVED or INTERNAL
1293
+ * - Can't edit deliveredValue unless DEPLOYED
1294
+ * - Can't edit completedAt unless DEPLOYED
1295
+ * - etc.
1296
+ */
1297
+ const StaffSupportTicketInputBaseSchema = z.object({
1298
+ title: z.string().min(5, "Please add a more descriptive title (at least 5 characters)").max(256, "Title is too long - keep it brief (under 256 characters)"),
1299
+ description: z.string().optional().nullable(),
1300
+ type: SupportTicketTypeSchema,
1301
+ priority: SupportTicketPriorityNumberSchema,
1302
+ dev_lifecycle: SupportTicketDevLifecycleUpdateSchema.optional(),
1303
+ credit_value: z.string().regex(DECIMAL_AMOUNT_REGEX, DECIMAL_AMOUNT_ERROR_MESSAGE).optional().nullable(),
1304
+ delivered_value: z.string().regex(DECIMAL_AMOUNT_2_DECIMALS_OR_EMPTY_REGEX, DECIMAL_AMOUNT_2_DECIMALS_OR_EMPTY_ERROR_MESSAGE).optional().nullable(),
1305
+ start_at: z.string().regex(/\d{4}-\d{2}-\d{2}/, "Date must be in the format YYYY-MM-DD").optional().nullable(),
1306
+ target_at: z.string().regex(/\d{4}-\d{2}-\d{2}/, "Date must be in the format YYYY-MM-DD").optional().nullable(),
1307
+ completed_at: z.string().regex(/\d{4}-\d{2}-\d{2}/, "Date must be in the format YYYY-MM-DD").optional().nullable(),
1308
+ assigned_to: z.string().optional().nullable()
1309
+ });
1310
+ const StaffSupportTicketCreateSchema = StaffSupportTicketInputBaseSchema.extend({ is_internal: z.boolean().optional() });
1311
+ const StaffSupportTicketUpdateSchema = StaffSupportTicketInputBaseSchema.extend({ id: z.string() });
1312
+ const StaffSupportTicketInputSchema = StaffSupportTicketUpdateSchema;
1313
+
1314
+ //#endregion
1315
+ //#region src/validation/support_ticket/support_ticket_staff/staff_workflow_zod.ts
1316
+ /**
1317
+ * Staff workflow schemas
1318
+ * These are for critical state transitions in the support_ticket lifecycle
1319
+ */
1320
+ /**
1321
+ * Approve support_ticket (PENDING → APPROVED)
1322
+ * Deducts credits and sets dev_lifecycle to BACKLOG
1323
+ * credit_value is required - can be set/updated in the approve flow
1324
+ */
1325
+ const ApproveSupportTicketSchema = z.object({
1326
+ id: z.string(),
1327
+ credit_value: z.string().min(0, "Credit value is required to approve").regex(DECIMAL_AMOUNT_REGEX, DECIMAL_AMOUNT_ERROR_MESSAGE).refine((val) => parseFloat(val) >= 0, "Must be 0 or greater")
1328
+ });
1329
+ /**
1330
+ * Reject support_ticket (PENDING → REJECTED)
1331
+ * Locks the support_ticket without deducting credits
1332
+ */
1333
+ const RejectSupportTicketSchema = z.object({ id: z.string() });
1334
+ /**
1335
+ * Revert support_ticket (APPROVED/REJECTED → PENDING)
1336
+ * Refunds credits if applicable and unlocks the support_ticket
1337
+ */
1338
+ const RevertSupportTicketSchema = z.object({ id: z.string() });
1339
+ /**
1340
+ * Complete support_ticket (devLifecycle → DEPLOYED)
1341
+ * Marks support_ticket as deployed and captures actual delivered value
1342
+ * Auto-sets completedAt timestamp
1343
+ */
1344
+ const CompleteSupportTicketSchema = z.object({
1345
+ id: z.string(),
1346
+ delivered_value: z.string().regex(/^(\d*\.?\d{1,2}|\d+)$/, "Must be a valid dollar amount with up to 2 decimal places")
1347
+ });
1348
+ /**
1349
+ * Delete support_ticket (soft delete)
1350
+ * Cannot delete APPROVED tickets (charged to customer)
1351
+ * Can delete PENDING, REJECTED, INTERNAL tickets
1352
+ */
1353
+ const DeleteSupportTicketSchema = z.object({ id: z.string() });
1354
+ /**
1355
+ * Cancel internal task (devLifecycle → CANCELLED)
1356
+ * Only for INTERNAL approval_status tickets
1357
+ * Customer tickets use reject workflow instead
1358
+ */
1359
+ const CancelInternalTaskSchema = z.object({ id: z.string() });
1360
+ /**
1361
+ * Reactivate terminal internal task (CANCELLED or DEPLOYED → BACKLOG)
1362
+ * Only for INTERNAL approval_status tickets with dev_lifecycle = CANCELLED or DEPLOYED
1363
+ * Clears completed_at timestamp and reopens task for more work
1364
+ */
1365
+ const ReactivateInternalTaskSchema = z.object({ id: z.string() });
1366
+ /**
1367
+ * Archive support ticket (COMPLETED only)
1368
+ * Only completed tickets (dev_lifecycle = DEPLOYED) can be archived.
1369
+ * Archiving locks comments for both customer and staff.
1370
+ */
1371
+ const ArchiveSupportTicketSchema = z.object({ id: z.string() });
1372
+
1373
+ //#endregion
1374
+ //#region src/validation/support_ticket/support_ticket_staff/support_ticket_event_types_zod.ts
1375
+ const SupportTicketEventTypeValues = [
1376
+ "TICKET_CREATED",
1377
+ "TICKET_ASSIGNED",
1378
+ "TICKET_UPDATED",
1379
+ "APPROVAL_STATUS_CHANGED",
1380
+ "DEV_LIFECYCLE_CHANGED",
1381
+ "PRIORITY_CHANGED",
1382
+ "NEW_CUSTOMER_NOTE",
1383
+ "NEW_INTERNAL_NOTE",
1384
+ "NEW_FOLLOWUP",
1385
+ "FOLLOWUP_COMPLETED"
1386
+ ];
1387
+ const SupportTicketEventTypeEnum = z.enum(SupportTicketEventTypeValues);
1388
+
1389
+ //#endregion
1390
+ //#region src/validation/support_ticket/support_ticket_staff/support_ticket_subscriber_zod.ts
1391
+ /** Support-ticket-specific create input: uses support_ticket_id instead of generic record_type/record_id */
1392
+ const SupportTicketSubscriberCreateSchema = z.object({
1393
+ support_ticket_id: z.string().min(1, "Support ticket ID is required"),
1394
+ user_id: z.string().min(1, "User ID is required"),
1395
+ subscribed_events: z.array(SupportTicketEventTypeEnum).optional().nullable()
1396
+ });
1397
+
1398
+ //#endregion
1399
+ //#region src/validation/support_ticket/support_ticket_enrichment.ts
1400
+ /**
1401
+ * Schema for support ticket record data in enriched contexts
1402
+ * This defines the minimal fields needed for display/enrichment
1403
+ * Includes id for mapping purposes, even though it's also in record_id
1404
+ */
1405
+ const SupportTicketRecordDataSchema = z.object({
1406
+ id: z.string(),
1407
+ display_id: z.string().optional().nullable(),
1408
+ title: z.string()
1409
+ });
1410
+
1411
+ //#endregion
1412
+ //#region src/validation/team/team_enums_zod.ts
1413
+ const TeamStatusSchema = z.enum(["active", "inactive"]).describe("TeamStatus");
1414
+
1415
+ //#endregion
1416
+ //#region src/validation/team/team_filters_zod.ts
1417
+ /**
1418
+ * Filters for teams
1419
+ *
1420
+ * All filters support operators for advanced filtering:
1421
+ * - Booleans: eq, ne
1422
+ * - Strings: eq, ne, contains, startsWith, endsWith
1423
+ * - Dates: eq, ne, gt, gte, lt, lte
1424
+ *
1425
+ * Examples:
1426
+ * - { display_name: { operator: "contains", value: "search" } }
1427
+ * - { contact_email: { operator: "eq", value: "test@example.com" } }
1428
+ * - { created_at: { operator: "gte", value: "2024-01-01" } }
1429
+ */
1430
+ const TeamFiltersSchema = PaginationFiltersSchema.extend({
1431
+ unique_name: StringFilterSchema.optional(),
1432
+ display_name: StringFilterSchema.optional(),
1433
+ legal_name: StringFilterSchema.optional(),
1434
+ contact_email: StringFilterSchema.optional(),
1435
+ address_city: StringFilterSchema.optional(),
1436
+ address_zip: StringFilterSchema.optional(),
1437
+ includeArchived: BooleanFilterSchema.optional(),
1438
+ created_at: DateFilterSchema.optional(),
1439
+ updated_at: DateFilterSchema.optional(),
1440
+ archived_at: DateFilterSchema.optional(),
1441
+ search: z.object({
1442
+ query: z.string(),
1443
+ searchableFields: z.array(z.string())
1444
+ }).optional()
1445
+ });
1446
+
1447
+ //#endregion
1448
+ //#region src/validation/team/team_input_zod.ts
1449
+ const TeamInputBaseSchema = z.object({
1450
+ unique_name: z.string().min(3, "Unique name must be between 3 and 64 characters").max(64).optional().nullable(),
1451
+ display_name: z.string().min(3, "Display name must be between 3 and 64 characters").max(64),
1452
+ legal_name: z.string().optional().nullable(),
1453
+ description: z.string().optional().nullable(),
1454
+ contact_name: z.string().optional().nullable(),
1455
+ contact_email: z.string().email().optional().nullable(),
1456
+ contact_business_phone: z.string().optional().nullable(),
1457
+ contact_mobile_phone: z.string().optional().nullable(),
1458
+ contact_time_zone: z.string().optional().nullable(),
1459
+ address_full: z.string().optional().nullable(),
1460
+ address_city: z.string().optional().nullable(),
1461
+ address_zip: z.string().optional().nullable(),
1462
+ twitter_username: z.string().optional().nullable(),
1463
+ url: z.string().url().optional().nullable(),
1464
+ logo: z.string().optional().nullable(),
1465
+ email_sent_from: z.string().email().optional().nullable(),
1466
+ email_reply_to: z.string().email().optional().nullable()
1467
+ });
1468
+ const TeamCreateSchema = TeamInputBaseSchema;
1469
+ const TeamUpdateSchema = TeamInputBaseSchema.extend({ id: z.string() });
1470
+
1471
+ //#endregion
1472
+ //#region src/validation/team/team_read_zod.ts
1473
+ const TeamReadSchema = TeamCreateSchema.extend({
1474
+ id: z.string(),
1475
+ original_id: z.string().optional().nullable(),
1476
+ path: z.string().optional().nullable(),
1477
+ created_at: z.string(),
1478
+ updated_at: z.string().optional().nullable(),
1479
+ created_by: z.string(),
1480
+ created_by_display_name: z.string().optional().nullable(),
1481
+ updated_by: z.string().optional().nullable(),
1482
+ updated_by_display_name: z.string().optional().nullable(),
1483
+ archived_at: z.string().optional().nullable(),
1484
+ archived_by: z.string().optional().nullable()
1485
+ });
1486
+
1487
+ //#endregion
1488
+ //#region src/validation/team/team_page_zod.ts
1489
+ const TeamPageSchema = createPaginatedSchema(TeamReadSchema);
1490
+
1491
+ //#endregion
1492
+ //#region src/validation/team/team_user_teams_zod.ts
1493
+ /**
1494
+ * Schema for a team option in user teams list
1495
+ * Includes all team fields to match the GraphQL query
1496
+ */
1497
+ const TeamOptionSchema = TeamReadSchema;
1498
+ /**
1499
+ * Schema for the list of teams that a user is part of
1500
+ */
1501
+ const UserTeamsSchema = z.object({ items: z.array(TeamOptionSchema) });
1502
+
1503
+ //#endregion
1504
+ //#region src/validation/team_member/team_member_enums_zod.ts
1505
+ /**
1506
+ * Team Member Role Enum
1507
+ * - owner: Full permissions (create, read, update, delete)
1508
+ * - manager: Can create and read (cannot update or delete)
1509
+ * - member: Can create and read (cannot update or delete)
1510
+ * - client: Client role (permissions TBD)
1511
+ */
1512
+ const TeamMemberRoleEnum = [
1513
+ "owner",
1514
+ "manager",
1515
+ "member",
1516
+ "client"
1517
+ ];
1518
+ const TeamMemberRoleSchema = z.enum(TeamMemberRoleEnum);
1519
+ /**
1520
+ * Filter-specific enum instance
1521
+ * This is a separate Zod object with the same values as above,
1522
+ * allowing GraphQL to generate distinct enum types for filters vs domain objects
1523
+ */
1524
+ const TeamMemberRoleFilterSchema = z.enum(TeamMemberRoleEnum);
1525
+
1526
+ //#endregion
1527
+ //#region src/validation/team_member/team_member_filters_zod.ts
1528
+ /**
1529
+ * Filters for team_member
1530
+ * Supports operator-based filtering for advanced queries
1531
+ *
1532
+ * All filters support operators for advanced filtering:
1533
+ * - Numbers: eq, ne, gt, gte, lt, lte
1534
+ * - Strings: eq, ne, contains, startsWith, endsWith
1535
+ * - Dates: eq, ne, gt, gte, lt, lte
1536
+ *
1537
+ * Examples:
1538
+ * - { original_id: { operator: "eq", value: 123 } }
1539
+ * - { team_id: { operator: "eq", value: "team_123" } }
1540
+ * - { display_name: { operator: "contains", value: "John" } }
1541
+ * - { created_at: { operator: "gte", value: "2024-01-01" } }
1542
+ */
1543
+ const TeamMemberFiltersSchema = PaginationFiltersSchema.extend({
1544
+ original_id: NumberFilterSchema.optional(),
1545
+ team_id: StringFilterSchema.optional(),
1546
+ original_team_id: StringFilterSchema.optional(),
1547
+ user_id: StringFilterSchema.optional(),
1548
+ original_user_id: StringFilterSchema.optional(),
1549
+ role: createEnumFilter(TeamMemberRoleFilterSchema).optional(),
1550
+ display_name: StringFilterSchema.optional(),
1551
+ business_phone: StringFilterSchema.optional(),
1552
+ mobile_phone: StringFilterSchema.optional(),
1553
+ email_address: StringFilterSchema.optional(),
1554
+ website_address: StringFilterSchema.optional(),
1555
+ time_zone: StringFilterSchema.optional(),
1556
+ created_at: DateFilterSchema.optional(),
1557
+ updated_at: DateFilterSchema.optional(),
1558
+ deleted_at: DateFilterSchema.optional(),
1559
+ search: z.object({
1560
+ query: z.string(),
1561
+ searchableFields: z.array(z.string())
1562
+ }).optional()
1563
+ });
1564
+
1565
+ //#endregion
1566
+ //#region src/validation/team_member/team_member_input_zod.ts
1567
+ const TeamMemberBaseSchema = z.object({
1568
+ team_id: z.string(),
1569
+ user_id: z.string(),
1570
+ role: TeamMemberRoleSchema,
1571
+ display_name: z.string().min(3, "Display name must be at least 3 characters").max(64, "Display name must be at most 64 characters"),
1572
+ business_phone: z.string().optional().nullable(),
1573
+ mobile_phone: z.string().optional().nullable(),
1574
+ email_address: z.string().email(),
1575
+ website_address: z.string().url().optional().nullable(),
1576
+ time_zone: z.string().optional().nullable()
1577
+ });
1578
+ const TeamMemberCreateSchema = TeamMemberBaseSchema;
1579
+ const TeamMemberUpdateSchema = TeamMemberBaseSchema.extend({ id: z.string() });
1580
+
1581
+ //#endregion
1582
+ //#region src/validation/team_member/team_member_read_zod.ts
1583
+ const TeamMemberReadSchema = z.object({
1584
+ id: z.string(),
1585
+ original_id: z.number().optional().nullable(),
1586
+ team_id: z.string(),
1587
+ original_team_id: z.string().optional().nullable(),
1588
+ user_id: z.string(),
1589
+ original_user_id: z.string().optional().nullable(),
1590
+ role: TeamMemberRoleSchema,
1591
+ display_name: z.string().optional().nullable(),
1592
+ business_phone: z.string().optional().nullable(),
1593
+ mobile_phone: z.string().optional().nullable(),
1594
+ email_address: z.string().email(),
1595
+ website_address: z.string().optional().nullable(),
1596
+ time_zone: z.string().optional().nullable(),
1597
+ created_at: z.string(),
1598
+ created_by: z.string(),
1599
+ created_by_display_name: z.string().optional().nullable(),
1600
+ updated_at: z.string().optional().nullable(),
1601
+ updated_by: z.string().optional().nullable(),
1602
+ updated_by_display_name: z.string().optional().nullable(),
1603
+ deleted_at: z.string().optional().nullable(),
1604
+ deleted_by: z.string().optional().nullable()
1605
+ });
1606
+
1607
+ //#endregion
1608
+ //#region src/validation/team_member/team_member_user_team_members_zod.ts
1609
+ /**
1610
+ * Schema for a team member option in user team members list
1611
+ * Includes all team member fields to match the query
1612
+ */
1613
+ const TeamMemberOptionSchema = TeamMemberReadSchema;
1614
+ /**
1615
+ * Schema for the list of team members that a user can see (from their teams)
1616
+ */
1617
+ const UserTeamMembersSchema = z.object({ items: z.array(TeamMemberOptionSchema) });
1618
+
1619
+ //#endregion
1620
+ //#region src/validation/user/user_read_zod.ts
1621
+ const UserReadSchema = z.object({
1622
+ id: z.string(),
1623
+ username: z.string(),
1624
+ email: z.string(),
1625
+ email_verified: z.boolean(),
1626
+ user_type: UserTypeEnum,
1627
+ created_at: z.string(),
1628
+ updated_at: z.string().nullable()
1629
+ });
1630
+
1631
+ //#endregion
1632
+ //#region src/validation/user/user_update_zod.ts
1633
+ const UserUpdateSchema = z.object({
1634
+ id: z.string(),
1635
+ user_type: UserTypeEnum
1636
+ });
1637
+
1638
+ //#endregion
1639
+ //#region src/validation/user_profile_zod.ts
1640
+ /**
1641
+ * Base schema for user profile
1642
+ */
1643
+ const UserProfileBaseSchema = z.object({
1644
+ first_name: z.string().max(255, "First name is too long").nullable().optional(),
1645
+ last_name: z.string().max(255, "Last name is too long").nullable().optional(),
1646
+ bio: z.string().max(1e3, "Bio is too long").nullable().optional(),
1647
+ notification_email: z.string().email("Invalid notification email").nullable().optional()
1648
+ });
1649
+ /**
1650
+ * Schema for updating a user profile
1651
+ */
1652
+ const UserProfileUpdateSchema = UserProfileBaseSchema.extend({ user_id: z.string().min(1, "User ID is required") });
1653
+ /**
1654
+ * Schema for reading a user profile
1655
+ */
1656
+ const UserProfileReadSchema = UserProfileBaseSchema.extend({
1657
+ id: z.string().min(1, "ID is required"),
1658
+ user_id: z.string().min(1, "User ID is required"),
1659
+ created_at: z.string().datetime(),
1660
+ created_by: z.string().min(1, "Created by is required"),
1661
+ updated_at: z.string().datetime().nullable(),
1662
+ updated_by: z.string().nullable()
1663
+ });
1664
+
1665
+ //#endregion
1666
+ //#region src/validation/user_session_zod.ts
1667
+ const userSessionSchema = z.object({
1668
+ created_at: z.string(),
1669
+ expires_at: z.string(),
1670
+ status: z.string(),
1671
+ user_agent: z.string().optional().nullable(),
1672
+ ip_address: z.string().optional().nullable(),
1673
+ user: z.object({
1674
+ userId: z.string(),
1675
+ email: z.string(),
1676
+ username: z.string(),
1677
+ email_verified: z.boolean(),
1678
+ user_type: z.string(),
1679
+ first_name: z.string().optional().nullable(),
1680
+ last_name: z.string().optional().nullable(),
1681
+ avatar_url: z.string().optional().nullable(),
1682
+ subscriptions: z.array(z.object({
1683
+ subscription_id: z.string(),
1684
+ subscription_status: z.string().optional().nullable(),
1685
+ subscription_created: z.string(),
1686
+ subscription_current_period_start: z.string(),
1687
+ subscription_current_period_end: z.string(),
1688
+ subscription_cancel_at: z.string().optional().nullable(),
1689
+ subscription_canceled_at: z.string().optional().nullable(),
1690
+ product_name: z.string().optional().nullable(),
1691
+ price_amount: z.number().nullable(),
1692
+ price_currency: z.string().optional().nullable()
1693
+ })).optional().nullable()
1694
+ })
1695
+ });
1696
+ const loginResponseSchema = z.object({
1697
+ frontend_session: userSessionSchema,
1698
+ access_token: z.string(),
1699
+ refresh_token: z.string(),
1700
+ user_details_token: z.string()
1701
+ });
1702
+
1703
+ //#endregion
1704
+ //#region src/api/saved-filter-api.ts
1705
+ /** Maximum presets per context (e.g. per page like trackers, followups) */
1706
+ const MAX_PRESETS_PER_CONTEXT = 10;
1707
+
1708
+ //#endregion
1709
+ //#region src/utils/enum_labels.ts
1710
+ /**
1711
+ * Utility functions for converting enum values to human-readable display labels
1712
+ */
1713
+ /**
1714
+ * Normalizes and validates input
1715
+ */
1716
+ function normalizeInput(value) {
1717
+ if (typeof value !== "string") return String(value);
1718
+ return value.trim().toLowerCase();
1719
+ }
1720
+ /**
1721
+ * Converts a snake_case string to Title Case
1722
+ * Handles edge cases like empty strings, single characters, numbers, etc.
1723
+ *
1724
+ * Examples:
1725
+ * - "order_signed" -> "Order Signed"
1726
+ * - "active_mtm" -> "Active Mtm"
1727
+ * - "create_a_quote" -> "Create A Quote"
1728
+ */
1729
+ function snakeCaseToTitleCase(value) {
1730
+ const normalized = normalizeInput(value);
1731
+ if (!normalized) return value;
1732
+ const parts = normalized.split("_").filter((part) => part.length > 0);
1733
+ if (parts.length === 0) return value;
1734
+ return parts.map((part) => {
1735
+ if (part.length === 0) return part;
1736
+ return part.charAt(0).toUpperCase() + part.slice(1);
1737
+ }).join(" ");
1738
+ }
1739
+ /**
1740
+ * Determines if a value should use dash formatting based on structure
1741
+ * Uses heuristics: length of first part, total parts, etc.
1742
+ */
1743
+ function shouldUseDashes(parts) {
1744
+ if (parts.length < 2) return false;
1745
+ const firstPart = parts[0];
1746
+ if (!firstPart) return false;
1747
+ if (firstPart.length <= 2) return true;
1748
+ if (firstPart.length >= 3 && firstPart.length <= 8 && parts.length >= 2) return true;
1749
+ if (parts.length >= 3) return true;
1750
+ return parts.length >= 2;
1751
+ }
1752
+ /**
1753
+ * Formats a value with dashes - intelligently splits based on structure
1754
+ *
1755
+ * Examples:
1756
+ * - "order_signed" -> "Order - Signed"
1757
+ * - "closed_lost" -> "Closed - Lost"
1758
+ * - "active_in_term" -> "Active - In Term"
1759
+ * - "on_hold_broker" -> "On Hold - Broker"
1760
+ */
1761
+ function formatWithDashes(value) {
1762
+ const parts = normalizeInput(value).split("_").filter((part) => part.length > 0);
1763
+ if (parts.length < 2) return snakeCaseToTitleCase(value);
1764
+ const firstPart = parts[0];
1765
+ const secondPart = parts[1];
1766
+ if (!firstPart) return snakeCaseToTitleCase(value);
1767
+ if (parts.length === 2) {
1768
+ if (!secondPart) return snakeCaseToTitleCase(value);
1769
+ return `${snakeCaseToTitleCase(firstPart)} - ${snakeCaseToTitleCase(secondPart)}`;
1770
+ }
1771
+ if (firstPart.length <= 3) return `${snakeCaseToTitleCase(firstPart)} - ${snakeCaseToTitleCase(parts.slice(1).join("_"))}`;
1772
+ if (secondPart && firstPart.length > secondPart.length) return `${snakeCaseToTitleCase(firstPart)} - ${snakeCaseToTitleCase(parts.slice(1).join("_"))}`;
1773
+ else return `${snakeCaseToTitleCase(parts.slice(0, 2).join("_"))} - ${snakeCaseToTitleCase(parts.slice(2).join("_"))}`;
1774
+ }
1775
+ /**
1776
+ * Converts an enum value to a display label
1777
+ * Uses intelligent formatting based on structure
1778
+ *
1779
+ * @param value - The enum value in snake_case (or any case)
1780
+ * @param customOverrides - Optional map of custom overrides for specific values
1781
+ * @returns The formatted display label
1782
+ */
1783
+ function enumToDisplayLabel(value, customOverrides) {
1784
+ if (!value || typeof value !== "string") return String(value || "");
1785
+ const normalized = normalizeInput(value);
1786
+ if (customOverrides) {
1787
+ if (customOverrides[value]) return customOverrides[value];
1788
+ if (customOverrides[normalized]) return customOverrides[normalized];
1789
+ }
1790
+ const parts = normalized.split("_").filter((part) => part.length > 0);
1791
+ if (parts.length === 0) return value;
1792
+ if (shouldUseDashes(parts)) return formatWithDashes(value);
1793
+ return snakeCaseToTitleCase(value);
1794
+ }
1795
+ /**
1796
+ * Creates a label map for an array of enum values
1797
+ *
1798
+ * @param enumValues - Array of enum values
1799
+ * @param customOverrides - Optional map of custom overrides for specific values
1800
+ * @returns Record mapping enum values to display labels
1801
+ */
1802
+ function createEnumLabelMap(enumValues, customOverrides) {
1803
+ const labelMap = {};
1804
+ for (const value of enumValues) labelMap[value] = enumToDisplayLabel(value, customOverrides);
1805
+ return labelMap;
1806
+ }
1807
+ /**
1808
+ * Converts an enum label map to select options for dropdowns
1809
+ *
1810
+ * @param labelMap - Record mapping enum values to display labels
1811
+ * @returns Array of select options
1812
+ */
1813
+ function createSelectOptionsFromLabelMap(labelMap) {
1814
+ return Object.entries(labelMap).map(([value, label]) => ({
1815
+ value,
1816
+ label
1817
+ }));
1818
+ }
1819
+
1820
+ //#endregion
1821
+ //#region src/utils/file.ts
1822
+ /**
1823
+ * File utility functions for sanitizing and processing file names.
1824
+ */
1825
+ /**
1826
+ * Sanitizes a file name by replacing non-alphanumeric characters with underscores.
1827
+ * Provides several improvements over basic sanitization:
1828
+ * - Converts dashes to underscores first (to avoid mixed dash/underscore patterns)
1829
+ * - Collapses multiple consecutive underscores
1830
+ * - Trims leading/trailing underscores
1831
+ * - Handles files without extensions properly
1832
+ * - Sanitizes the extension too
1833
+ * - Enforces 800-byte limit for R2 compatibility
1834
+ * - Provides fallback for completely invalid names
1835
+ *
1836
+ * This function enforces an 800-byte limit to comply with Cloudflare R2's 1,024 byte
1837
+ * object key limit (leaving room for path overhead).
1838
+ *
1839
+ * @param fileName - The original file name to sanitize
1840
+ * @returns The sanitized file name with underscores replacing non-alphanumeric characters
1841
+ *
1842
+ * @example
1843
+ * ```typescript
1844
+ * sanitizeFileName('Sun Life Sheet 2021-10-13.xls')
1845
+ * // Returns: 'Sun_Life_Sheet_2021_10_13.xls'
1846
+ *
1847
+ * sanitizeFileName('my-file@2023.pdf')
1848
+ * // Returns: 'my_file_2023.pdf'
1849
+ *
1850
+ * sanitizeFileName('document.txt')
1851
+ * // Returns: 'document.txt'
1852
+ *
1853
+ * sanitizeFileName('file---name.txt')
1854
+ * // Returns: 'file_name.txt' (collapsed underscores)
1855
+ *
1856
+ * sanitizeFileName('file-name_test.txt')
1857
+ * // Returns: 'file_name_test.txt' (dashes converted first, no mixed patterns)
1858
+ *
1859
+ * sanitizeFileName('!!!file.txt')
1860
+ * // Returns: 'file.txt' (trimmed underscores)
1861
+ *
1862
+ * sanitizeFileName('')
1863
+ * // Returns: 'unnamed_file' (fallback)
1864
+ * ```
1865
+ */
1866
+ function sanitizeFileName(fileName) {
1867
+ if (!fileName || fileName.trim() === "") return "unnamed_file";
1868
+ const lastDotIndex = fileName.lastIndexOf(".");
1869
+ const hasExtension = lastDotIndex > 0;
1870
+ let name = hasExtension ? fileName.substring(0, lastDotIndex) : fileName;
1871
+ let extension = hasExtension ? fileName.substring(lastDotIndex + 1) : "";
1872
+ const sanitize = (str) => str.replace(/-/g, "_").replace(/[^a-zA-Z0-9]/g, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "");
1873
+ name = sanitize(name);
1874
+ extension = sanitize(extension);
1875
+ if (!name) name = "file";
1876
+ const sanitized = extension ? `${name}.${extension}` : name;
1877
+ const encoder = new TextEncoder();
1878
+ if (encoder.encode(sanitized).length > 800) {
1879
+ const maxNameBytes = 800 - (extension ? encoder.encode(`.${extension}`).length : 0);
1880
+ let truncatedName = "";
1881
+ let currentBytes = 0;
1882
+ for (const char of name) {
1883
+ const charBytes = encoder.encode(char).length;
1884
+ if (currentBytes + charBytes > maxNameBytes) break;
1885
+ truncatedName += char;
1886
+ currentBytes += charBytes;
1887
+ }
1888
+ return extension ? `${truncatedName}.${extension}` : truncatedName;
1889
+ }
1890
+ return sanitized;
1891
+ }
1892
+
1893
+ //#endregion
1894
+ //#region src/utils/money.ts
1895
+ /**
1896
+ * Money calculation utilities using strings for storage and numbers for calculations.
1897
+ * All monetary values are stored as strings.
1898
+ */
1899
+ /**
1900
+ * Adds two or more monetary values
1901
+ * @param values Monetary values as strings
1902
+ * @returns Sum as string
1903
+ */
1904
+ const addMoney = (...values) => values.reduce((sum, value) => sum + Number(value), 0).toString();
1905
+ /**
1906
+ * Subtracts monetary values from the first value
1907
+ * @param minuend Value to subtract from
1908
+ * @param subtrahends Values to subtract
1909
+ * @returns Difference as string
1910
+ */
1911
+ const subtractMoney = (minuend, ...subtrahends) => subtrahends.reduce((diff, value) => diff - Number(value), Number(minuend)).toString();
1912
+ /**
1913
+ * Multiplies a monetary value by a number
1914
+ * @param amount Amount as string
1915
+ * @param multiplier Number to multiply by
1916
+ * @returns Product as string
1917
+ */
1918
+ const multiplyMoney = (amount, multiplier) => (Number(amount) * multiplier).toString();
1919
+ /**
1920
+ * Divides a monetary value by a number
1921
+ * @param amount Amount as string
1922
+ * @param divisor Number to divide by
1923
+ * @returns Quotient as string
1924
+ */
1925
+ const divideMoney = (amount, divisor) => (Number(amount) / divisor).toString();
1926
+ /**
1927
+ * Applies a percentage to a monetary value
1928
+ * @param amount Amount as string
1929
+ * @param percentage Percentage as decimal (e.g., 0.15 for 15%)
1930
+ * @returns Result as string
1931
+ */
1932
+ const applyPercentage = (amount, percentage) => multiplyMoney(amount, 1 + percentage);
1933
+ /**
1934
+ * Rounds a monetary value
1935
+ * @param amount Amount to round
1936
+ * @returns Rounded amount as string
1937
+ */
1938
+ const roundMoney = (amount) => (Math.round(Number(amount) * 100) / 100).toString();
1939
+ /**
1940
+ * Formats a number as currency
1941
+ * @param value Number to format
1942
+ * @returns Formatted currency string
1943
+ */
1944
+ const formatCurrency = (value) => {
1945
+ return new Intl.NumberFormat("en-US", {
1946
+ style: "currency",
1947
+ currency: "USD"
1948
+ }).format(value);
1949
+ };
1950
+ /**
1951
+ * Formats a dollar amount as currency
1952
+ * @param amount Amount as string
1953
+ * @returns Formatted currency string
1954
+ */
1955
+ const formatDollar = (amount) => {
1956
+ if (amount === null || amount === void 0) return "$0.00";
1957
+ return new Intl.NumberFormat("en-US", {
1958
+ style: "currency",
1959
+ currency: "USD"
1960
+ }).format(Number(amount));
1961
+ };
1962
+
1963
+ //#endregion
1964
+ export { AddCreditsSchema, ApproveSupportTicketSchema, ArchiveSupportTicketSchema, AttachmentCreateSchema, AttachmentFiltersSchema, AttachmentFiltersSchema as attachmentFilters_zod, AttachmentFolderCreateSchema, AttachmentFolderReadSchema, AttachmentFolderUpdateSchema, AttachmentPageSchema, AttachmentPageSchema as attachmentPage_zod, AttachmentReadSchema, AttachmentReadSchema as attachment_zod, AttachmentUpdateSchema, BaseFilterSchema, BooleanFilterSchema, CancelInternalTaskSchema, CompleteSupportTicketSchema, CreditBalanceSchema, CreditTransactionFiltersSchema, CreditTransactionPageSchema, CreditTransactionReadSchema, CreditTransactionTypeEnum, CustomerSupportTicketCreateSchema, CustomerSupportTicketFiltersSchema, CustomerSupportTicketPageSchema, CustomerSupportTicketReadSchema, CustomerSupportTicketUpdateSchema, DECIMAL_AMOUNT_2_DECIMALS_OR_EMPTY_ERROR_MESSAGE, DECIMAL_AMOUNT_2_DECIMALS_OR_EMPTY_REGEX, DECIMAL_AMOUNT_ERROR_MESSAGE, DECIMAL_AMOUNT_REGEX, DEFAULT_USER_TYPE, DataTypeSchema, DateFilterSchema, DateOperatorSchema, DeleteSupportTicketSchema, EqualityArrayOperatorSchema, EqualityOperatorSchema, FilterConfigSchema, MAX_PRESETS_PER_CONTEXT, NoteCreateSchema, NoteFiltersSchema, NoteFiltersSortDirectionEnum, NoteReadSchema, NoteUpdateSchema, NumberFilterSchema, NumberOperatorSchema, OPERATORS, PageInfoSchema, PaginationFiltersSchema, ReactivateInternalTaskSchema, RecordConst, RecordSubscriberCreateSchema, RecordSubscriberReadSchema, RecordSubscriberUpdateSchema, RecordTypeValues, RejectSupportTicketSchema, ResetMonthlyBalanceSchema, RevertSupportTicketSchema, SUPPORT_TICKET_APPROVAL_FILTER_OPTIONS, SUPPORT_TICKET_DEV_LIFECYCLE_FILTER_OPTIONS, SUPPORT_TICKET_NUMBER_TO_PRIORITY, SUPPORT_TICKET_PRIORITY_FILTER_OPTIONS, SUPPORT_TICKET_PRIORITY_NUMBER_TO_LABEL, SUPPORT_TICKET_PRIORITY_TO_NUMBER, SUPPORT_TICKET_STATUS_FILTER_OPTIONS, SUPPORT_TICKET_TYPE_FILTER_OPTIONS, SavedFilterCreateSchema, SavedFilterReadSchema, SavedFilterUpdateSchema, SetMonthlyAllocationSchema, SortDirectionSchema, StaffSupportTicketCreateSchema, StaffSupportTicketFiltersSchema, StaffSupportTicketInputSchema, StaffSupportTicketPageSchema, StaffSupportTicketReadSchema, StaffSupportTicketUpdateSchema, StringFilterSchema, StringOperatorSchema, SupportTicketApprovalEnum, SupportTicketApprovalFilterSchema, SupportTicketApprovalSchema, SupportTicketDevLifecycleEnum, SupportTicketDevLifecycleFilterSchema, SupportTicketDevLifecycleSchema, SupportTicketDevLifecycleUpdateEnum, SupportTicketDevLifecycleUpdateSchema, SupportTicketEventTypeEnum, SupportTicketEventTypeValues, SupportTicketPriorityEnum, SupportTicketPriorityFilterSchema, SupportTicketPriorityNumberEnum, SupportTicketPriorityNumberSchema, SupportTicketPrioritySchema, SupportTicketRecordDataSchema, SupportTicketStatusEnum, SupportTicketStatusFilterSchema, SupportTicketStatusSchema, SupportTicketSubscriberCreateSchema, SupportTicketTypeEnum, SupportTicketTypeFilterSchema, SupportTicketTypeSchema, TeamCreateSchema, TeamFiltersSchema, TeamInputBaseSchema, TeamMemberCreateSchema, TeamMemberFiltersSchema, TeamMemberOptionSchema, TeamMemberReadSchema, TeamMemberRoleEnum, TeamMemberRoleFilterSchema, TeamMemberRoleSchema, TeamMemberUpdateSchema, TeamOptionSchema, TeamPageSchema, TeamReadSchema, TeamStatusSchema, TeamUpdateSchema, USER_TYPES, UserProfileBaseSchema, UserProfileReadSchema, UserProfileUpdateSchema, UserReadSchema, UserTeamMembersSchema, UserTeamsSchema, UserTypeEnum, UserUpdateSchema, addMoney, applyPercentage, changePasswordSchema, createEnumFilter, createEnumLabelMap, createPaginatedSchema, createSelectOptionsFromLabelMap, createUserSchema, createUserSchemaOutput, divideMoney, enumToDisplayLabel, forgot_password_zod, formatCurrency, formatDollar, isCommonPassword, loginResponseSchema, loginSchema, multiplyMoney, pageInfoSchema, passwordSchema, recordVersionFiltersBreadcrumbSchema, recordVersionFiltersInputBreadcrumbSchema, recordVersionFiltersInputSchema, recordVersionFiltersSchema, recordVersionPageBreadcrumbSchema, recordVersionPageSchema, recordVersionSchema, recordVersionTrackerActivityInputSchema, resetPasswordInputSchema, resetPasswordSchema, roundMoney, sanitizeFileName, signupSchema, subtractMoney, supportTicketNumberToPriority, supportTicketPriorityToNumber, userSessionSchema };
1965
+ //# sourceMappingURL=index.mjs.map