@getmicdrop/svelte-components 5.17.1 → 5.17.4

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 (199) hide show
  1. package/dist/calendar/Calendar/MiniMonthCalendar.svelte +5 -7
  2. package/dist/calendar/Calendar/MiniMonthCalendar.svelte.d.ts.map +1 -1
  3. package/dist/calendar/MonthSwitcher/MonthSwitcher.svelte +2 -3
  4. package/dist/calendar/MonthSwitcher/MonthSwitcher.svelte.d.ts.map +1 -1
  5. package/dist/calendar/PublicCard/PublicCard.svelte +23 -14
  6. package/dist/calendar/PublicCard/PublicCard.svelte.d.ts.map +1 -1
  7. package/dist/calendar/ShowCard/ShowCard.spec.js +1 -7
  8. package/dist/calendar/ShowCard/ShowCard.svelte +10 -1
  9. package/dist/calendar/ShowCard/ShowCard.svelte.d.ts.map +1 -1
  10. package/dist/calendar/ShowTimeCard/ShowTimeCard.svelte +11 -0
  11. package/dist/calendar/ShowTimeCard/ShowTimeCard.svelte.d.ts +2 -0
  12. package/dist/calendar/ShowTimeCard/ShowTimeCard.svelte.d.ts.map +1 -1
  13. package/dist/components/Heading.spec.d.ts +2 -0
  14. package/dist/components/Heading.spec.d.ts.map +1 -0
  15. package/dist/components/Heading.spec.js +89 -0
  16. package/dist/components/Layout/__tests__/AppShell.test.js +140 -0
  17. package/dist/components/Text.spec.d.ts +2 -0
  18. package/dist/components/Text.spec.d.ts.map +1 -0
  19. package/dist/components/Text.spec.js +89 -0
  20. package/dist/config.d.ts +102 -0
  21. package/dist/config.js +147 -1
  22. package/dist/datetime/README.md +323 -0
  23. package/dist/forms/createFormStore.svelte.spec.d.ts +2 -0
  24. package/dist/forms/createFormStore.svelte.spec.d.ts.map +1 -0
  25. package/dist/forms/createFormStore.svelte.spec.js +387 -0
  26. package/dist/messages.d.ts +43 -0
  27. package/dist/messages.d.ts.map +1 -0
  28. package/dist/messages.js +57 -0
  29. package/dist/patterns/chat/ChatActivityNotice.spec.d.ts +2 -0
  30. package/dist/patterns/chat/ChatActivityNotice.spec.d.ts.map +1 -0
  31. package/dist/patterns/chat/ChatActivityNotice.spec.js +59 -0
  32. package/dist/patterns/chat/ChatBubble.spec.d.ts +2 -0
  33. package/dist/patterns/chat/ChatBubble.spec.d.ts.map +1 -0
  34. package/dist/patterns/chat/ChatBubble.spec.js +91 -0
  35. package/dist/patterns/chat/ChatContainer.spec.d.ts +2 -0
  36. package/dist/patterns/chat/ChatContainer.spec.d.ts.map +1 -0
  37. package/dist/patterns/chat/ChatContainer.spec.js +30 -0
  38. package/dist/patterns/chat/ChatDateDivider.spec.d.ts +2 -0
  39. package/dist/patterns/chat/ChatDateDivider.spec.d.ts.map +1 -0
  40. package/dist/patterns/chat/ChatDateDivider.spec.js +30 -0
  41. package/dist/patterns/chat/ChatInvitationBubble.spec.d.ts +2 -0
  42. package/dist/patterns/chat/ChatInvitationBubble.spec.d.ts.map +1 -0
  43. package/dist/patterns/chat/ChatInvitationBubble.spec.js +46 -0
  44. package/dist/patterns/chat/ChatInvitationNotice.spec.d.ts +2 -0
  45. package/dist/patterns/chat/ChatInvitationNotice.spec.d.ts.map +1 -0
  46. package/dist/patterns/chat/ChatInvitationNotice.spec.js +32 -0
  47. package/dist/patterns/chat/ChatMessageGroup.spec.d.ts +2 -0
  48. package/dist/patterns/chat/ChatMessageGroup.spec.d.ts.map +1 -0
  49. package/dist/patterns/chat/ChatMessageGroup.spec.js +58 -0
  50. package/dist/patterns/chat/ChatSlotUpdate.spec.d.ts +2 -0
  51. package/dist/patterns/chat/ChatSlotUpdate.spec.d.ts.map +1 -0
  52. package/dist/patterns/chat/ChatSlotUpdate.spec.js +65 -0
  53. package/dist/patterns/chat/ChatStatusBadge.spec.d.ts +2 -0
  54. package/dist/patterns/chat/ChatStatusBadge.spec.d.ts.map +1 -0
  55. package/dist/patterns/chat/ChatStatusBadge.spec.js +79 -0
  56. package/dist/patterns/chat/ChatStatusTransition.spec.d.ts +2 -0
  57. package/dist/patterns/chat/ChatStatusTransition.spec.d.ts.map +1 -0
  58. package/dist/patterns/chat/ChatStatusTransition.spec.js +81 -0
  59. package/dist/patterns/chat/ChatTextBubble.spec.d.ts +2 -0
  60. package/dist/patterns/chat/ChatTextBubble.spec.d.ts.map +1 -0
  61. package/dist/patterns/chat/ChatTextBubble.spec.js +35 -0
  62. package/dist/patterns/data/DataTable.spec.js +61 -0
  63. package/dist/patterns/forms/FormGrid.spec.js +34 -0
  64. package/dist/patterns/layout/Sidebar.spec.js +240 -1
  65. package/dist/patterns/layout/SidebarTestWrapper.svelte +34 -0
  66. package/dist/patterns/layout/SidebarTestWrapper.svelte.d.ts +23 -0
  67. package/dist/patterns/layout/SidebarTestWrapper.svelte.d.ts.map +1 -0
  68. package/dist/patterns/navigation/Header.spec.js +123 -0
  69. package/dist/primitives/Accordion/Accordion.spec.js +112 -2
  70. package/dist/primitives/Accordion/AccordionToggleWrapper.test.svelte +28 -0
  71. package/dist/primitives/Accordion/AccordionToggleWrapper.test.svelte.d.ts +7 -0
  72. package/dist/primitives/Accordion/AccordionToggleWrapper.test.svelte.d.ts.map +1 -0
  73. package/dist/primitives/Avatar/Avatar.spec.js +23 -0
  74. package/dist/primitives/BottomSheet/BottomSheet.spec.js +102 -0
  75. package/dist/primitives/BottomSheet/BottomSheetWithActions.test.svelte +20 -0
  76. package/dist/primitives/BottomSheet/BottomSheetWithActions.test.svelte.d.ts +10 -0
  77. package/dist/primitives/BottomSheet/BottomSheetWithActions.test.svelte.d.ts.map +1 -0
  78. package/dist/primitives/Button/ButtonGroup.spec.d.ts +2 -0
  79. package/dist/primitives/Button/ButtonGroup.spec.d.ts.map +1 -0
  80. package/dist/primitives/Button/ButtonGroup.spec.js +44 -0
  81. package/dist/primitives/Checkbox/Checkbox.spec.js +32 -0
  82. package/dist/primitives/Drawer/Drawer.spec.js +437 -0
  83. package/dist/primitives/Drawer/DrawerTestWrapper.svelte +86 -0
  84. package/dist/primitives/Drawer/DrawerTestWrapper.svelte.d.ts +26 -0
  85. package/dist/primitives/Drawer/DrawerTestWrapper.svelte.d.ts.map +1 -0
  86. package/dist/primitives/Dropdown/Dropdown.spec.js +116 -0
  87. package/dist/primitives/Dropdown/DropdownDivider.spec.d.ts +2 -0
  88. package/dist/primitives/Dropdown/DropdownDivider.spec.d.ts.map +1 -0
  89. package/dist/primitives/Dropdown/DropdownDivider.spec.js +30 -0
  90. package/dist/primitives/Dropdown/DropdownItem.spec.js +155 -1
  91. package/dist/primitives/Dropdown/DropdownItemTestWrapper.svelte +43 -0
  92. package/dist/primitives/Dropdown/DropdownItemTestWrapper.svelte.d.ts +17 -0
  93. package/dist/primitives/Dropdown/DropdownItemTestWrapper.svelte.d.ts.map +1 -0
  94. package/dist/primitives/Helper/Helper.spec.d.ts +2 -0
  95. package/dist/primitives/Helper/Helper.spec.d.ts.map +1 -0
  96. package/dist/primitives/Helper/Helper.spec.js +57 -0
  97. package/dist/primitives/Input/Input.spec.js +664 -0
  98. package/dist/primitives/Input/Input.svelte +18 -10
  99. package/dist/primitives/Input/Input.svelte.d.ts.map +1 -1
  100. package/dist/primitives/Input/Select.spec.js +414 -0
  101. package/dist/primitives/Label/Label.spec.js +9 -0
  102. package/dist/primitives/LandingButton/LandingButton.spec.d.ts +2 -0
  103. package/dist/primitives/LandingButton/LandingButton.spec.d.ts.map +1 -0
  104. package/dist/primitives/LandingButton/LandingButton.spec.js +61 -0
  105. package/dist/primitives/MenuItem/MenuItem.spec.d.ts +2 -0
  106. package/dist/primitives/MenuItem/MenuItem.spec.d.ts.map +1 -0
  107. package/dist/primitives/MenuItem/MenuItem.spec.js +130 -0
  108. package/dist/primitives/Modal/Modal.spec.js +215 -0
  109. package/dist/primitives/NavItem/NavItem.spec.d.ts +2 -0
  110. package/dist/primitives/NavItem/NavItem.spec.d.ts.map +1 -0
  111. package/dist/primitives/NavItem/NavItem.spec.js +97 -0
  112. package/dist/primitives/SearchResultItem/SearchResultItem.spec.d.ts +2 -0
  113. package/dist/primitives/SearchResultItem/SearchResultItem.spec.d.ts.map +1 -0
  114. package/dist/primitives/SearchResultItem/SearchResultItem.spec.js +78 -0
  115. package/dist/primitives/SidebarToggle/SidebarToggle.spec.d.ts +2 -0
  116. package/dist/primitives/SidebarToggle/SidebarToggle.spec.d.ts.map +1 -0
  117. package/dist/primitives/SidebarToggle/SidebarToggle.spec.js +61 -0
  118. package/dist/primitives/Spinner/Spinner.spec.js +13 -0
  119. package/dist/primitives/Toggle.spec.js +75 -0
  120. package/dist/primitives/ToggleTestWrapper.svelte +30 -0
  121. package/dist/primitives/ToggleTestWrapper.svelte.d.ts +29 -0
  122. package/dist/primitives/ToggleTestWrapper.svelte.d.ts.map +1 -0
  123. package/dist/primitives/Tooltip/Tooltip.spec.d.ts +2 -0
  124. package/dist/primitives/Tooltip/Tooltip.spec.d.ts.map +1 -0
  125. package/dist/primitives/Tooltip/Tooltip.spec.js +126 -0
  126. package/dist/recipes/inputs/Search.spec.js +66 -2
  127. package/dist/recipes/modals/ConfirmationModal.spec.js +190 -0
  128. package/dist/schemas/__tests__/auth.test.d.ts +2 -0
  129. package/dist/schemas/__tests__/auth.test.d.ts.map +1 -0
  130. package/dist/schemas/__tests__/auth.test.js +210 -0
  131. package/dist/schemas/__tests__/common.test.d.ts +2 -0
  132. package/dist/schemas/__tests__/common.test.d.ts.map +1 -0
  133. package/dist/schemas/__tests__/common.test.js +340 -0
  134. package/dist/schemas/__tests__/domain.test.d.ts +2 -0
  135. package/dist/schemas/__tests__/domain.test.d.ts.map +1 -0
  136. package/dist/schemas/__tests__/domain.test.js +293 -0
  137. package/dist/schemas/__tests__/order.test.d.ts +2 -0
  138. package/dist/schemas/__tests__/order.test.d.ts.map +1 -0
  139. package/dist/schemas/__tests__/order.test.js +349 -0
  140. package/dist/schemas/__tests__/user.test.d.ts +2 -0
  141. package/dist/schemas/__tests__/user.test.d.ts.map +1 -0
  142. package/dist/schemas/__tests__/user.test.js +325 -0
  143. package/dist/schemas/auth.d.ts +41 -0
  144. package/dist/schemas/auth.d.ts.map +1 -0
  145. package/dist/schemas/auth.js +69 -0
  146. package/dist/schemas/common.d.ts +43 -0
  147. package/dist/schemas/common.d.ts.map +1 -0
  148. package/dist/schemas/common.js +157 -0
  149. package/dist/schemas/event.d.ts +82 -0
  150. package/dist/schemas/event.d.ts.map +1 -0
  151. package/dist/schemas/event.js +58 -0
  152. package/dist/schemas/index.d.ts +10 -0
  153. package/dist/schemas/index.d.ts.map +1 -0
  154. package/dist/schemas/index.js +9 -0
  155. package/dist/schemas/order.d.ts +111 -0
  156. package/dist/schemas/order.d.ts.map +1 -0
  157. package/dist/schemas/order.js +73 -0
  158. package/dist/schemas/performer.d.ts +133 -0
  159. package/dist/schemas/performer.d.ts.map +1 -0
  160. package/dist/schemas/performer.js +73 -0
  161. package/dist/schemas/promo.d.ts +87 -0
  162. package/dist/schemas/promo.d.ts.map +1 -0
  163. package/dist/schemas/promo.js +98 -0
  164. package/dist/schemas/ticket.d.ts +104 -0
  165. package/dist/schemas/ticket.d.ts.map +1 -0
  166. package/dist/schemas/ticket.js +82 -0
  167. package/dist/schemas/user.d.ts +92 -0
  168. package/dist/schemas/user.d.ts.map +1 -0
  169. package/dist/schemas/user.js +53 -0
  170. package/dist/schemas/venue.d.ts +95 -0
  171. package/dist/schemas/venue.d.ts.map +1 -0
  172. package/dist/schemas/venue.js +52 -0
  173. package/dist/stores/auth.svelte.spec.d.ts +2 -0
  174. package/dist/stores/auth.svelte.spec.d.ts.map +1 -0
  175. package/dist/stores/auth.svelte.spec.js +112 -0
  176. package/dist/stores/formDataStore.svelte.spec.d.ts +2 -0
  177. package/dist/stores/formDataStore.svelte.spec.d.ts.map +1 -0
  178. package/dist/stores/formDataStore.svelte.spec.js +150 -0
  179. package/dist/stores/formSave.svelte.spec.d.ts +2 -0
  180. package/dist/stores/formSave.svelte.spec.d.ts.map +1 -0
  181. package/dist/stores/formSave.svelte.spec.js +196 -0
  182. package/dist/stores/navigation.spec.d.ts +2 -0
  183. package/dist/stores/navigation.spec.d.ts.map +1 -0
  184. package/dist/stores/navigation.spec.js +53 -0
  185. package/dist/telemetry.spec.js +5 -0
  186. package/dist/tokens/__tests__/sizing.test.js +2 -2
  187. package/dist/tokens/sizing.d.ts +5 -0
  188. package/dist/tokens/sizing.d.ts.map +1 -1
  189. package/dist/tokens/sizing.js +6 -0
  190. package/dist/utils/haptic.spec.d.ts +2 -0
  191. package/dist/utils/haptic.spec.d.ts.map +1 -0
  192. package/dist/utils/haptic.spec.js +153 -0
  193. package/dist/utils/imageOptimizer.spec.d.ts +2 -0
  194. package/dist/utils/imageOptimizer.spec.d.ts.map +1 -0
  195. package/dist/utils/imageOptimizer.spec.js +201 -0
  196. package/dist/utils/logger.spec.d.ts +2 -0
  197. package/dist/utils/logger.spec.d.ts.map +1 -0
  198. package/dist/utils/logger.spec.js +95 -0
  199. package/package.json +1 -2
@@ -0,0 +1,325 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { teamInviteSchema, teamMemberSchema, teamMemberUpdateSchema, userFilterSchema, userPreferencesSchema, userProfileSchema, userProfileUpdateSchema, userRoleSchema, } from '../user';
3
+ describe('userRoleSchema', () => {
4
+ it('accepts valid roles', () => {
5
+ const roles = ['owner', 'admin', 'manager', 'staff', 'viewer'];
6
+ roles.forEach(role => {
7
+ const result = userRoleSchema.safeParse(role);
8
+ expect(result.success).toBe(true);
9
+ });
10
+ });
11
+ it('rejects invalid role', () => {
12
+ const result = userRoleSchema.safeParse('superadmin');
13
+ expect(result.success).toBe(false);
14
+ });
15
+ });
16
+ describe('userProfileSchema', () => {
17
+ const validProfile = {
18
+ firstName: 'John',
19
+ lastName: 'Doe',
20
+ email: 'john@example.com',
21
+ };
22
+ it('accepts valid profile', () => {
23
+ const result = userProfileSchema.safeParse(validProfile);
24
+ expect(result.success).toBe(true);
25
+ });
26
+ it('accepts profile with phone', () => {
27
+ const result = userProfileSchema.safeParse({
28
+ ...validProfile,
29
+ phone: '555-123-4567',
30
+ });
31
+ expect(result.success).toBe(true);
32
+ });
33
+ it('accepts profile with bio', () => {
34
+ const result = userProfileSchema.safeParse({
35
+ ...validProfile,
36
+ bio: 'A comedy venue owner from San Francisco',
37
+ });
38
+ expect(result.success).toBe(true);
39
+ });
40
+ it('rejects missing first name', () => {
41
+ const { firstName, ...profileWithoutFirst } = validProfile;
42
+ const result = userProfileSchema.safeParse(profileWithoutFirst);
43
+ expect(result.success).toBe(false);
44
+ });
45
+ it('rejects missing last name', () => {
46
+ const { lastName, ...profileWithoutLast } = validProfile;
47
+ const result = userProfileSchema.safeParse(profileWithoutLast);
48
+ expect(result.success).toBe(false);
49
+ });
50
+ it('rejects invalid email', () => {
51
+ const result = userProfileSchema.safeParse({
52
+ ...validProfile,
53
+ email: 'invalid-email',
54
+ });
55
+ expect(result.success).toBe(false);
56
+ });
57
+ it('rejects bio over 500 characters', () => {
58
+ const result = userProfileSchema.safeParse({
59
+ ...validProfile,
60
+ bio: 'x'.repeat(501),
61
+ });
62
+ expect(result.success).toBe(false);
63
+ });
64
+ });
65
+ describe('userProfileUpdateSchema', () => {
66
+ it('accepts empty update (all fields optional)', () => {
67
+ const result = userProfileUpdateSchema.safeParse({});
68
+ expect(result.success).toBe(true);
69
+ });
70
+ it('accepts partial update', () => {
71
+ const result = userProfileUpdateSchema.safeParse({
72
+ firstName: 'Jane',
73
+ });
74
+ expect(result.success).toBe(true);
75
+ });
76
+ it('accepts full update', () => {
77
+ const result = userProfileUpdateSchema.safeParse({
78
+ firstName: 'Jane',
79
+ lastName: 'Smith',
80
+ email: 'jane@example.com',
81
+ phone: '555-987-6543',
82
+ bio: 'Updated bio',
83
+ });
84
+ expect(result.success).toBe(true);
85
+ });
86
+ it('rejects invalid email even when optional', () => {
87
+ const result = userProfileUpdateSchema.safeParse({
88
+ email: 'invalid-email',
89
+ });
90
+ expect(result.success).toBe(false);
91
+ });
92
+ });
93
+ describe('userPreferencesSchema', () => {
94
+ it('accepts empty preferences (uses defaults)', () => {
95
+ const result = userPreferencesSchema.safeParse({});
96
+ expect(result.success).toBe(true);
97
+ if (result.success) {
98
+ expect(result.data.emailNotifications).toBe(true);
99
+ expect(result.data.smsNotifications).toBe(false);
100
+ expect(result.data.marketingEmails).toBe(false);
101
+ expect(result.data.language).toBe('en');
102
+ }
103
+ });
104
+ it('accepts custom preferences', () => {
105
+ const result = userPreferencesSchema.safeParse({
106
+ emailNotifications: false,
107
+ smsNotifications: true,
108
+ marketingEmails: true,
109
+ timezone: 'America/Los_Angeles',
110
+ language: 'es',
111
+ });
112
+ expect(result.success).toBe(true);
113
+ if (result.success) {
114
+ expect(result.data.emailNotifications).toBe(false);
115
+ expect(result.data.smsNotifications).toBe(true);
116
+ expect(result.data.marketingEmails).toBe(true);
117
+ expect(result.data.timezone).toBe('America/Los_Angeles');
118
+ expect(result.data.language).toBe('es');
119
+ }
120
+ });
121
+ it('accepts partial preferences', () => {
122
+ const result = userPreferencesSchema.safeParse({
123
+ smsNotifications: true,
124
+ });
125
+ expect(result.success).toBe(true);
126
+ if (result.success) {
127
+ expect(result.data.smsNotifications).toBe(true);
128
+ expect(result.data.emailNotifications).toBe(true);
129
+ }
130
+ });
131
+ });
132
+ describe('teamMemberSchema', () => {
133
+ const validMember = {
134
+ email: 'team@example.com',
135
+ firstName: 'Team',
136
+ lastName: 'Member',
137
+ role: 'staff',
138
+ };
139
+ it('accepts valid team member', () => {
140
+ const result = teamMemberSchema.safeParse(validMember);
141
+ expect(result.success).toBe(true);
142
+ });
143
+ it('accepts team member with venue IDs', () => {
144
+ const result = teamMemberSchema.safeParse({
145
+ ...validMember,
146
+ venueIds: [1, 2, 3],
147
+ });
148
+ expect(result.success).toBe(true);
149
+ });
150
+ it('accepts all roles', () => {
151
+ const roles = ['owner', 'admin', 'manager', 'staff', 'viewer'];
152
+ roles.forEach(role => {
153
+ const result = teamMemberSchema.safeParse({
154
+ ...validMember,
155
+ role,
156
+ });
157
+ expect(result.success).toBe(true);
158
+ });
159
+ });
160
+ it('rejects missing email', () => {
161
+ const { email, ...memberWithoutEmail } = validMember;
162
+ const result = teamMemberSchema.safeParse(memberWithoutEmail);
163
+ expect(result.success).toBe(false);
164
+ });
165
+ it('rejects invalid role', () => {
166
+ const result = teamMemberSchema.safeParse({
167
+ ...validMember,
168
+ role: 'invalid',
169
+ });
170
+ expect(result.success).toBe(false);
171
+ });
172
+ it('rejects invalid venue ID', () => {
173
+ const result = teamMemberSchema.safeParse({
174
+ ...validMember,
175
+ venueIds: [-1, 0],
176
+ });
177
+ expect(result.success).toBe(false);
178
+ });
179
+ });
180
+ describe('teamMemberUpdateSchema', () => {
181
+ it('accepts empty update', () => {
182
+ const result = teamMemberUpdateSchema.safeParse({});
183
+ expect(result.success).toBe(true);
184
+ });
185
+ it('accepts partial update with role', () => {
186
+ const result = teamMemberUpdateSchema.safeParse({
187
+ role: 'manager',
188
+ });
189
+ expect(result.success).toBe(true);
190
+ });
191
+ it('accepts partial update with venue IDs', () => {
192
+ const result = teamMemberUpdateSchema.safeParse({
193
+ venueIds: [4, 5, 6],
194
+ });
195
+ expect(result.success).toBe(true);
196
+ });
197
+ it('rejects invalid role', () => {
198
+ const result = teamMemberUpdateSchema.safeParse({
199
+ role: 'invalid',
200
+ });
201
+ expect(result.success).toBe(false);
202
+ });
203
+ });
204
+ describe('teamInviteSchema', () => {
205
+ const validInvite = {
206
+ email: 'newmember@example.com',
207
+ role: 'staff',
208
+ venueIds: [1],
209
+ };
210
+ it('accepts valid invite', () => {
211
+ const result = teamInviteSchema.safeParse(validInvite);
212
+ expect(result.success).toBe(true);
213
+ });
214
+ it('accepts invite with message', () => {
215
+ const result = teamInviteSchema.safeParse({
216
+ ...validInvite,
217
+ message: 'Welcome to the team!',
218
+ });
219
+ expect(result.success).toBe(true);
220
+ });
221
+ it('accepts invite with multiple venues', () => {
222
+ const result = teamInviteSchema.safeParse({
223
+ ...validInvite,
224
+ venueIds: [1, 2, 3, 4, 5],
225
+ });
226
+ expect(result.success).toBe(true);
227
+ });
228
+ it('rejects empty venueIds', () => {
229
+ const result = teamInviteSchema.safeParse({
230
+ ...validInvite,
231
+ venueIds: [],
232
+ });
233
+ expect(result.success).toBe(false);
234
+ if (!result.success) {
235
+ const error = result.error.issues.find(e => e.path.includes('venueIds'));
236
+ expect(error?.message).toBe('At least one venue must be selected');
237
+ }
238
+ });
239
+ it('rejects missing email', () => {
240
+ const { email, ...inviteWithoutEmail } = validInvite;
241
+ const result = teamInviteSchema.safeParse(inviteWithoutEmail);
242
+ expect(result.success).toBe(false);
243
+ });
244
+ it('rejects invalid email', () => {
245
+ const result = teamInviteSchema.safeParse({
246
+ ...validInvite,
247
+ email: 'invalid',
248
+ });
249
+ expect(result.success).toBe(false);
250
+ });
251
+ it('rejects message over 500 characters', () => {
252
+ const result = teamInviteSchema.safeParse({
253
+ ...validInvite,
254
+ message: 'x'.repeat(501),
255
+ });
256
+ expect(result.success).toBe(false);
257
+ });
258
+ });
259
+ describe('userFilterSchema', () => {
260
+ it('accepts empty filter', () => {
261
+ const result = userFilterSchema.safeParse({});
262
+ expect(result.success).toBe(true);
263
+ });
264
+ it('accepts filter with role', () => {
265
+ const result = userFilterSchema.safeParse({
266
+ role: 'admin',
267
+ });
268
+ expect(result.success).toBe(true);
269
+ });
270
+ it('accepts filter with search', () => {
271
+ const result = userFilterSchema.safeParse({
272
+ search: 'john',
273
+ });
274
+ expect(result.success).toBe(true);
275
+ });
276
+ it('accepts filter with pagination', () => {
277
+ const result = userFilterSchema.safeParse({
278
+ limit: 25,
279
+ offset: 50,
280
+ });
281
+ expect(result.success).toBe(true);
282
+ });
283
+ it('accepts filter with venue and active status', () => {
284
+ const result = userFilterSchema.safeParse({
285
+ venueId: 123,
286
+ isActive: true,
287
+ });
288
+ expect(result.success).toBe(true);
289
+ });
290
+ it('accepts filter with all options', () => {
291
+ const result = userFilterSchema.safeParse({
292
+ search: 'manager',
293
+ role: 'manager',
294
+ venueId: 1,
295
+ isActive: true,
296
+ limit: 10,
297
+ offset: 0,
298
+ });
299
+ expect(result.success).toBe(true);
300
+ });
301
+ it('rejects invalid role in filter', () => {
302
+ const result = userFilterSchema.safeParse({
303
+ role: 'invalid',
304
+ });
305
+ expect(result.success).toBe(false);
306
+ });
307
+ it('rejects negative limit', () => {
308
+ const result = userFilterSchema.safeParse({
309
+ limit: -1,
310
+ });
311
+ expect(result.success).toBe(false);
312
+ });
313
+ it('rejects negative offset', () => {
314
+ const result = userFilterSchema.safeParse({
315
+ offset: -1,
316
+ });
317
+ expect(result.success).toBe(false);
318
+ });
319
+ it('rejects zero limit', () => {
320
+ const result = userFilterSchema.safeParse({
321
+ limit: 0,
322
+ });
323
+ expect(result.success).toBe(false);
324
+ });
325
+ });
@@ -0,0 +1,41 @@
1
+ import { z } from 'zod';
2
+ export declare const passwordSchema: z.ZodString;
3
+ export declare const loginSchema: z.ZodObject<{
4
+ email: z.ZodPipe<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>, z.ZodString>;
5
+ password: z.ZodString;
6
+ rememberMe: z.ZodDefault<z.ZodBoolean>;
7
+ }, z.core.$strip>;
8
+ export type LoginInput = z.infer<typeof loginSchema>;
9
+ export declare const registerSchema: z.ZodObject<{
10
+ email: z.ZodPipe<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>, z.ZodString>;
11
+ password: z.ZodString;
12
+ confirmPassword: z.ZodString;
13
+ firstName: z.ZodString;
14
+ lastName: z.ZodString;
15
+ acceptTerms: z.ZodLiteral<true>;
16
+ }, z.core.$strip>;
17
+ export type RegisterInput = z.infer<typeof registerSchema>;
18
+ export declare const passwordChangeSchema: z.ZodObject<{
19
+ currentPassword: z.ZodString;
20
+ newPassword: z.ZodString;
21
+ confirmNewPassword: z.ZodString;
22
+ }, z.core.$strip>;
23
+ export type PasswordChangeInput = z.infer<typeof passwordChangeSchema>;
24
+ export declare const forgotPasswordSchema: z.ZodObject<{
25
+ email: z.ZodPipe<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>, z.ZodString>;
26
+ }, z.core.$strip>;
27
+ export type ForgotPasswordInput = z.infer<typeof forgotPasswordSchema>;
28
+ export declare const resetPasswordSchema: z.ZodObject<{
29
+ token: z.ZodString;
30
+ password: z.ZodString;
31
+ confirmPassword: z.ZodString;
32
+ }, z.core.$strip>;
33
+ export type ResetPasswordInput = z.infer<typeof resetPasswordSchema>;
34
+ export declare const profileUpdateSchema: z.ZodObject<{
35
+ firstName: z.ZodString;
36
+ lastName: z.ZodString;
37
+ phone: z.ZodPipe<z.ZodUnion<[z.ZodOptional<z.ZodString>, z.ZodLiteral<"">]>, z.ZodTransform<string | undefined, string | undefined>>;
38
+ bio: z.ZodUnion<[z.ZodOptional<z.ZodString>, z.ZodLiteral<"">]>;
39
+ }, z.core.$strip>;
40
+ export type ProfileUpdateInput = z.infer<typeof profileUpdateSchema>;
41
+ //# sourceMappingURL=auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/lib/schemas/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAQxB,eAAO,MAAM,cAAc,aAyBxB,CAAC;AAEJ,eAAO,MAAM,WAAW;;;;iBAItB,CAAC;AAEH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAErD,eAAO,MAAM,cAAc;;;;;;;iBAcvB,CAAC;AAEL,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAE3D,eAAO,MAAM,oBAAoB;;;;iBAa7B,CAAC;AAEL,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAEvE,eAAO,MAAM,oBAAoB;;iBAE/B,CAAC;AAEH,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAEvE,eAAO,MAAM,mBAAmB;;;;iBAS5B,CAAC;AAEL,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAErE,eAAO,MAAM,mBAAmB;;;;;iBAS9B,CAAC;AAEH,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC"}
@@ -0,0 +1,69 @@
1
+ import { z } from 'zod';
2
+ import { PASSWORD } from '../config';
3
+ import { VALIDATION_MESSAGES } from '../messages';
4
+ import { emailSchema, phoneSchema, requiredString } from './common';
5
+ export const passwordSchema = z
6
+ .string()
7
+ .min(PASSWORD.MIN_LENGTH, VALIDATION_MESSAGES.passwordTooShort(PASSWORD.MIN_LENGTH))
8
+ .max(PASSWORD.MAX_LENGTH, VALIDATION_MESSAGES.passwordTooLong(PASSWORD.MAX_LENGTH))
9
+ .refine(val => !PASSWORD.REQUIRE_UPPERCASE || /[A-Z]/.test(val), VALIDATION_MESSAGES.PASSWORD_NEEDS_UPPERCASE)
10
+ .refine(val => !PASSWORD.REQUIRE_LOWERCASE || /[a-z]/.test(val), VALIDATION_MESSAGES.PASSWORD_NEEDS_LOWERCASE)
11
+ .refine(val => !PASSWORD.REQUIRE_NUMBER || /[0-9]/.test(val), VALIDATION_MESSAGES.PASSWORD_NEEDS_NUMBER)
12
+ .refine(val => !PASSWORD.REQUIRE_SPECIAL || /[^A-Za-z0-9]/.test(val), VALIDATION_MESSAGES.PASSWORD_NEEDS_SPECIAL);
13
+ export const loginSchema = z.object({
14
+ email: emailSchema,
15
+ password: z.string().min(1, VALIDATION_MESSAGES.REQUIRED),
16
+ rememberMe: z.boolean().default(false),
17
+ });
18
+ export const registerSchema = z
19
+ .object({
20
+ email: emailSchema,
21
+ password: passwordSchema,
22
+ confirmPassword: z.string(),
23
+ firstName: requiredString(50, 'First name'),
24
+ lastName: requiredString(50, 'Last name'),
25
+ acceptTerms: z.literal(true, {
26
+ message: VALIDATION_MESSAGES.ACCEPT_TERMS_REQUIRED,
27
+ }),
28
+ })
29
+ .refine(data => data.password === data.confirmPassword, {
30
+ message: VALIDATION_MESSAGES.PASSWORDS_MUST_MATCH,
31
+ path: ['confirmPassword'],
32
+ });
33
+ export const passwordChangeSchema = z
34
+ .object({
35
+ currentPassword: z.string().min(1, VALIDATION_MESSAGES.REQUIRED),
36
+ newPassword: passwordSchema,
37
+ confirmNewPassword: z.string(),
38
+ })
39
+ .refine(data => data.newPassword === data.confirmNewPassword, {
40
+ message: VALIDATION_MESSAGES.PASSWORDS_MUST_MATCH,
41
+ path: ['confirmNewPassword'],
42
+ })
43
+ .refine(data => data.currentPassword !== data.newPassword, {
44
+ message: VALIDATION_MESSAGES.PASSWORD_SAME_AS_CURRENT,
45
+ path: ['newPassword'],
46
+ });
47
+ export const forgotPasswordSchema = z.object({
48
+ email: emailSchema,
49
+ });
50
+ export const resetPasswordSchema = z
51
+ .object({
52
+ token: z.string().min(1),
53
+ password: passwordSchema,
54
+ confirmPassword: z.string(),
55
+ })
56
+ .refine(data => data.password === data.confirmPassword, {
57
+ message: VALIDATION_MESSAGES.PASSWORDS_MUST_MATCH,
58
+ path: ['confirmPassword'],
59
+ });
60
+ export const profileUpdateSchema = z.object({
61
+ firstName: requiredString(50, 'First name'),
62
+ lastName: requiredString(50, 'Last name'),
63
+ phone: phoneSchema,
64
+ bio: z
65
+ .string()
66
+ .max(500, VALIDATION_MESSAGES.tooLong('Bio', 500))
67
+ .optional()
68
+ .or(z.literal('')),
69
+ });
@@ -0,0 +1,43 @@
1
+ import { z } from 'zod';
2
+ export declare const emailSchema: z.ZodPipe<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>, z.ZodString>;
3
+ export declare const phoneSchema: z.ZodPipe<z.ZodUnion<[z.ZodOptional<z.ZodString>, z.ZodLiteral<"">]>, z.ZodTransform<string | undefined, string | undefined>>;
4
+ export declare const urlSchema: z.ZodUnion<[z.ZodOptional<z.ZodString>, z.ZodLiteral<"">]>;
5
+ export declare const uuidSchema: z.ZodString;
6
+ export declare const optionalUuidSchema: z.ZodUnion<[z.ZodOptional<z.ZodString>, z.ZodLiteral<"">]>;
7
+ export declare const positiveIntSchema: z.ZodNumber;
8
+ export declare const nonNegativeIntSchema: z.ZodNumber;
9
+ export declare const nonNegativeSchema: z.ZodNumber;
10
+ export declare const priceSchema: z.ZodPipe<z.ZodNumber, z.ZodTransform<number, number>>;
11
+ export declare const percentageSchema: z.ZodNumber;
12
+ export declare const dateSchema: z.ZodPipe<z.ZodUnion<readonly [z.ZodString, z.ZodDate]>, z.ZodTransform<Date, string | Date>>;
13
+ export declare const futureDateSchema: z.ZodPipe<z.ZodUnion<readonly [z.ZodString, z.ZodDate]>, z.ZodTransform<Date, string | Date>>;
14
+ export declare const optionalDateSchema: z.ZodPipe<z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodDate, z.ZodNull]>>, z.ZodTransform<Date | null, string | Date | null | undefined>>;
15
+ export declare const timezoneSchema: z.ZodString;
16
+ export declare const usStateSchema: z.ZodString;
17
+ export declare const zipCodeSchema: z.ZodString;
18
+ export declare const addressSchema: z.ZodObject<{
19
+ street: z.ZodString;
20
+ street2: z.ZodUnion<[z.ZodOptional<z.ZodString>, z.ZodLiteral<"">]>;
21
+ city: z.ZodString;
22
+ state: z.ZodString;
23
+ zipCode: z.ZodString;
24
+ country: z.ZodDefault<z.ZodString>;
25
+ }, z.core.$strip>;
26
+ export type AddressInput = z.infer<typeof addressSchema>;
27
+ export declare const imageFileSchema: z.ZodCustom<File, File>;
28
+ export declare const imageSchema: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodCustom<File, File>, z.ZodNull]>>;
29
+ export declare const socialLinksSchema: z.ZodObject<{
30
+ facebook: z.ZodOptional<z.ZodUnion<[z.ZodOptional<z.ZodString>, z.ZodLiteral<"">]>>;
31
+ instagram: z.ZodOptional<z.ZodUnion<[z.ZodOptional<z.ZodString>, z.ZodLiteral<"">]>>;
32
+ twitter: z.ZodOptional<z.ZodUnion<[z.ZodOptional<z.ZodString>, z.ZodLiteral<"">]>>;
33
+ tiktok: z.ZodOptional<z.ZodUnion<[z.ZodOptional<z.ZodString>, z.ZodLiteral<"">]>>;
34
+ youtube: z.ZodOptional<z.ZodUnion<[z.ZodOptional<z.ZodString>, z.ZodLiteral<"">]>>;
35
+ website: z.ZodOptional<z.ZodUnion<[z.ZodOptional<z.ZodString>, z.ZodLiteral<"">]>>;
36
+ }, z.core.$strip>;
37
+ export type SocialLinksInput = z.infer<typeof socialLinksSchema>;
38
+ export declare function requiredString(maxLength: number, fieldName: string): z.ZodString;
39
+ export declare function optionalString(maxLength: number, fieldName: string): z.ZodUnion<[z.ZodOptional<z.ZodString>, z.ZodLiteral<"">]>;
40
+ export declare function numberRange(min: number, max: number, fieldName: string): z.ZodNumber;
41
+ export declare function stringToNumber(): z.ZodPipe<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>, z.ZodTransform<number, string | number>>;
42
+ export declare function stringToInt(): z.ZodPipe<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>, z.ZodTransform<number, string | number>>;
43
+ //# sourceMappingURL=common.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"common.d.ts","sourceRoot":"","sources":["../../src/lib/schemas/common.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAWxB,eAAO,MAAM,WAAW,gFAYrB,CAAC;AAEJ,eAAO,MAAM,WAAW,+HAK6B,CAAC;AAEtD,eAAO,MAAM,SAAS,4DAQF,CAAC;AAErB,eAAO,MAAM,UAAU,aAAoD,CAAC;AAE5E,eAAO,MAAM,kBAAkB,4DAA0C,CAAC;AAE1E,eAAO,MAAM,iBAAiB,aAGmB,CAAC;AAElD,eAAO,MAAM,oBAAoB,aAGkB,CAAC;AAEpD,eAAO,MAAM,iBAAiB,aAEqB,CAAC;AAEpD,eAAO,MAAM,WAAW,wDAIoB,CAAC;AAE7C,eAAO,MAAM,gBAAgB,aAGuB,CAAC;AAErD,eAAO,MAAM,UAAU,+FAG8C,CAAC;AAEtE,eAAO,MAAM,gBAAgB,+FAG5B,CAAC;AAEF,eAAO,MAAM,kBAAkB,mJAO3B,CAAC;AAEL,eAAO,MAAM,cAAc,aAUe,CAAC;AAE3C,eAAO,MAAM,aAAa,aAGV,CAAC;AAEjB,eAAO,MAAM,aAAa,aAEkC,CAAC;AAE7D,eAAO,MAAM,aAAa;;;;;;;iBAiBxB,CAAC;AAEH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AASzD,eAAO,MAAM,eAAe,yBAYzB,CAAC;AAEJ,eAAO,MAAM,WAAW,uFAEX,CAAC;AAEd,eAAO,MAAM,iBAAiB;;;;;;;iBASlB,CAAC;AAEb,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAEjE,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,eAMlE;AAED,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,8DAOlE;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,eAKtE;AAED,wBAAgB,cAAc,wGAK7B;AAED,wBAAgB,WAAW,wGAU1B"}
@@ -0,0 +1,157 @@
1
+ import { z } from 'zod';
2
+ import { FILE_LIMITS, PATTERNS, STRING_LIMITS, } from '../config';
3
+ import { VALIDATION_MESSAGES } from '../messages';
4
+ export const emailSchema = z
5
+ .string()
6
+ .transform(v => v.trim().toLowerCase())
7
+ .pipe(z
8
+ .string()
9
+ .min(1, VALIDATION_MESSAGES.REQUIRED)
10
+ .max(STRING_LIMITS.EMAIL_MAX, VALIDATION_MESSAGES.tooLong('Email', STRING_LIMITS.EMAIL_MAX))
11
+ .email(VALIDATION_MESSAGES.INVALID_EMAIL));
12
+ export const phoneSchema = z
13
+ .string()
14
+ .regex(PATTERNS.PHONE, VALIDATION_MESSAGES.INVALID_PHONE)
15
+ .optional()
16
+ .or(z.literal(''))
17
+ .transform(v => v?.replace(/\D/g, '') || undefined);
18
+ export const urlSchema = z
19
+ .string()
20
+ .max(STRING_LIMITS.URL_MAX, VALIDATION_MESSAGES.tooLong('URL', STRING_LIMITS.URL_MAX))
21
+ .url(VALIDATION_MESSAGES.INVALID_URL)
22
+ .optional()
23
+ .or(z.literal(''));
24
+ export const uuidSchema = z.string().uuid(VALIDATION_MESSAGES.INVALID_UUID);
25
+ export const optionalUuidSchema = uuidSchema.optional().or(z.literal(''));
26
+ export const positiveIntSchema = z
27
+ .number()
28
+ .int(VALIDATION_MESSAGES.MUST_BE_INTEGER)
29
+ .positive(VALIDATION_MESSAGES.MUST_BE_POSITIVE);
30
+ export const nonNegativeIntSchema = z
31
+ .number()
32
+ .int(VALIDATION_MESSAGES.MUST_BE_INTEGER)
33
+ .min(0, VALIDATION_MESSAGES.MUST_BE_NON_NEGATIVE);
34
+ export const nonNegativeSchema = z
35
+ .number()
36
+ .min(0, VALIDATION_MESSAGES.MUST_BE_NON_NEGATIVE);
37
+ export const priceSchema = z
38
+ .number()
39
+ .min(0, VALIDATION_MESSAGES.MUST_BE_NON_NEGATIVE)
40
+ .max(10000, VALIDATION_MESSAGES.PRICE_TOO_HIGH)
41
+ .transform(v => Math.round(v * 100) / 100);
42
+ export const percentageSchema = z
43
+ .number()
44
+ .min(0, VALIDATION_MESSAGES.MUST_BE_NON_NEGATIVE)
45
+ .max(100, VALIDATION_MESSAGES.PERCENTAGE_TOO_HIGH);
46
+ export const dateSchema = z
47
+ .union([z.string(), z.date()])
48
+ .transform(v => (typeof v === 'string' ? new Date(v) : v))
49
+ .refine(d => !isNaN(d.getTime()), VALIDATION_MESSAGES.INVALID_DATE);
50
+ export const futureDateSchema = dateSchema.refine(d => d > new Date(), VALIDATION_MESSAGES.DATE_MUST_BE_FUTURE);
51
+ export const optionalDateSchema = z
52
+ .union([z.string(), z.date(), z.null()])
53
+ .optional()
54
+ .transform(v => {
55
+ if (!v)
56
+ return null;
57
+ const date = typeof v === 'string' ? new Date(v) : v;
58
+ return isNaN(date.getTime()) ? null : date;
59
+ });
60
+ export const timezoneSchema = z
61
+ .string()
62
+ .min(1, VALIDATION_MESSAGES.REQUIRED)
63
+ .refine(tz => {
64
+ try {
65
+ Intl.DateTimeFormat(undefined, { timeZone: tz });
66
+ return true;
67
+ }
68
+ catch {
69
+ return false;
70
+ }
71
+ }, VALIDATION_MESSAGES.INVALID_TIMEZONE);
72
+ export const usStateSchema = z
73
+ .string()
74
+ .length(2, VALIDATION_MESSAGES.INVALID_STATE)
75
+ .toUpperCase();
76
+ export const zipCodeSchema = z
77
+ .string()
78
+ .regex(PATTERNS.ZIP_CODE, VALIDATION_MESSAGES.INVALID_ZIP);
79
+ export const addressSchema = z.object({
80
+ street: z
81
+ .string()
82
+ .min(1, VALIDATION_MESSAGES.REQUIRED)
83
+ .max(STRING_LIMITS.ADDRESS_MAX),
84
+ street2: z
85
+ .string()
86
+ .max(STRING_LIMITS.ADDRESS_MAX)
87
+ .optional()
88
+ .or(z.literal('')),
89
+ city: z
90
+ .string()
91
+ .min(1, VALIDATION_MESSAGES.REQUIRED)
92
+ .max(STRING_LIMITS.CITY_MAX),
93
+ state: usStateSchema,
94
+ zipCode: zipCodeSchema,
95
+ country: z.string().default('US'),
96
+ });
97
+ const ALLOWED_IMAGE_TYPES = [
98
+ 'image/jpeg',
99
+ 'image/png',
100
+ 'image/gif',
101
+ 'image/webp',
102
+ ];
103
+ export const imageFileSchema = z
104
+ .instanceof(File)
105
+ .refine(file => file.size <= FILE_LIMITS.MAX_IMAGE_SIZE, VALIDATION_MESSAGES.fileTooLarge(FILE_LIMITS.MAX_IMAGE_SIZE / (1024 * 1024)))
106
+ .refine(file => ALLOWED_IMAGE_TYPES.includes(file.type), VALIDATION_MESSAGES.invalidFileType(ALLOWED_IMAGE_TYPES));
107
+ export const imageSchema = z
108
+ .union([z.string().url(), imageFileSchema, z.null()])
109
+ .optional();
110
+ export const socialLinksSchema = z
111
+ .object({
112
+ facebook: urlSchema,
113
+ instagram: urlSchema,
114
+ twitter: urlSchema,
115
+ tiktok: urlSchema,
116
+ youtube: urlSchema,
117
+ website: urlSchema,
118
+ })
119
+ .partial();
120
+ export function requiredString(maxLength, fieldName) {
121
+ return z
122
+ .string()
123
+ .min(1, `${fieldName} is required`)
124
+ .max(maxLength, VALIDATION_MESSAGES.tooLong(fieldName, maxLength))
125
+ .trim();
126
+ }
127
+ export function optionalString(maxLength, fieldName) {
128
+ return z
129
+ .string()
130
+ .max(maxLength, VALIDATION_MESSAGES.tooLong(fieldName, maxLength))
131
+ .trim()
132
+ .optional()
133
+ .or(z.literal(''));
134
+ }
135
+ export function numberRange(min, max, fieldName) {
136
+ return z
137
+ .number()
138
+ .min(min, VALIDATION_MESSAGES.tooSmall(fieldName, min))
139
+ .max(max, VALIDATION_MESSAGES.tooLarge(fieldName, max));
140
+ }
141
+ export function stringToNumber() {
142
+ return z
143
+ .union([z.string(), z.number()])
144
+ .transform(v => (typeof v === 'string' ? parseFloat(v) : v))
145
+ .refine(v => !isNaN(v), VALIDATION_MESSAGES.INVALID_FORMAT);
146
+ }
147
+ export function stringToInt() {
148
+ return z
149
+ .union([z.string(), z.number()])
150
+ .refine(v => {
151
+ if (typeof v === 'string') {
152
+ return /^-?\d+$/.test(v.trim());
153
+ }
154
+ return Number.isInteger(v);
155
+ }, VALIDATION_MESSAGES.MUST_BE_INTEGER)
156
+ .transform(v => (typeof v === 'string' ? parseInt(v, 10) : v));
157
+ }