@ainsleydev/sveltekit-helper 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/LICENSE +28 -0
  2. package/README.md +248 -0
  3. package/dist/components/Grid/index.d.ts +4 -0
  4. package/dist/components/Grid/index.d.ts.map +1 -0
  5. package/dist/components/Grid/index.js +5 -0
  6. package/dist/components/Grid/index.js.map +1 -0
  7. package/dist/components/payload/index.d.ts +4 -0
  8. package/dist/components/payload/index.d.ts.map +1 -0
  9. package/dist/components/payload/index.js +4 -0
  10. package/dist/components/payload/index.js.map +1 -0
  11. package/dist/components/payload/types.d.ts +33 -0
  12. package/dist/components/payload/types.d.ts.map +1 -0
  13. package/dist/components/payload/types.js +3 -0
  14. package/dist/components/payload/types.js.map +1 -0
  15. package/dist/index.d.ts +10 -0
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.js +11 -0
  18. package/dist/index.js.map +1 -0
  19. package/dist/utils/forms/helpers.d.ts +24 -0
  20. package/dist/utils/forms/helpers.d.ts.map +1 -0
  21. package/dist/utils/forms/helpers.js +31 -0
  22. package/dist/utils/forms/helpers.js.map +1 -0
  23. package/dist/utils/forms/index.d.ts +3 -0
  24. package/dist/utils/forms/index.d.ts.map +1 -0
  25. package/dist/utils/forms/index.js +4 -0
  26. package/dist/utils/forms/index.js.map +1 -0
  27. package/dist/utils/forms/schema.d.ts +79 -0
  28. package/dist/utils/forms/schema.d.ts.map +1 -0
  29. package/dist/utils/forms/schema.js +53 -0
  30. package/dist/utils/forms/schema.js.map +1 -0
  31. package/dist/utils/forms/schema.test.js +115 -0
  32. package/dist/utils/forms/schema.test.js.map +1 -0
  33. package/dist/utils/strings/index.d.ts +14 -0
  34. package/dist/utils/strings/index.d.ts.map +1 -0
  35. package/dist/utils/strings/index.js +20 -0
  36. package/dist/utils/strings/index.js.map +1 -0
  37. package/dist/utils/strings/index.test.js +28 -0
  38. package/dist/utils/strings/index.test.js.map +1 -0
  39. package/package.json +96 -0
package/LICENSE ADDED
@@ -0,0 +1,28 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2025, ainsley.dev
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright notice, this
9
+ list of conditions and the following disclaimer.
10
+
11
+ 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ this list of conditions and the following disclaimer in the documentation
13
+ and/or other materials provided with the distribution.
14
+
15
+ 3. Neither the name of the copyright holder nor the names of its
16
+ contributors may be used to endorse or promote products derived from
17
+ this software without specific prior written permission.
18
+
19
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package/README.md ADDED
@@ -0,0 +1,248 @@
1
+ # @ainsleydev/sveltekit-helper
2
+
3
+ SvelteKit utilities, components and helpers for ainsley.dev builds.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pnpm add @ainsleydev/sveltekit-helper
9
+ ```
10
+
11
+ ## Features
12
+
13
+ - **Grid System**: Responsive Container, Row, and Column components with CSS variables
14
+ - **Form Utilities**: Schema generation and error helpers for Zod validation
15
+ - **Payload CMS Integration**: Ready-to-use components for Payload CMS forms and media
16
+ - **SCSS with BEM**: All components use SCSS with BEM naming convention
17
+
18
+ ## Grid Components
19
+
20
+ ### Container
21
+
22
+ Center content horizontally with predefined max-width and support for breakout layouts.
23
+
24
+ ```svelte
25
+ <script>
26
+ import { Container, Row, Column } from '@ainsleydev/sveltekit-helper/components/Grid'
27
+ </script>
28
+
29
+ <Container>
30
+ <Row>
31
+ <Column class="col-12 col-desk-6">
32
+ Content
33
+ </Column>
34
+ </Row>
35
+ </Container>
36
+ ```
37
+
38
+ #### Customisation
39
+
40
+ Override CSS variables to customise the container:
41
+
42
+ ```css
43
+ .container {
44
+ --container-padding: 2rem;
45
+ --container-max-width: 1400px;
46
+ --container-breakout-max-width: 1600px;
47
+ }
48
+ ```
49
+
50
+ ### Row
51
+
52
+ Flexbox row container with gap management.
53
+
54
+ ```svelte
55
+ <Row>
56
+ <Column class="col-12 col-tab-6">
57
+ Column 1
58
+ </Column>
59
+ <Column class="col-12 col-tab-6">
60
+ Column 2
61
+ </Column>
62
+ </Row>
63
+
64
+ <!-- No gaps -->
65
+ <Row noGaps>
66
+ <Column class="col-6">Content</Column>
67
+ </Row>
68
+ ```
69
+
70
+ #### Customisation
71
+
72
+ ```css
73
+ .row {
74
+ --row-gap: 1.5rem;
75
+ }
76
+ ```
77
+
78
+ ### Column
79
+
80
+ Base column component with customisable gap. Consumers should define their own grid classes in global styles.
81
+
82
+ ```svelte
83
+ <Column class="col-12 col-tab-6 col-desk-4">
84
+ Content
85
+ </Column>
86
+ ```
87
+
88
+ #### Customisation
89
+
90
+ ```css
91
+ .col {
92
+ --col-gap: 1.5rem;
93
+ }
94
+
95
+ /* Define your own grid classes */
96
+ .col-12 { width: 100%; }
97
+ .col-6 { width: 50%; }
98
+
99
+ @media (min-width: 768px) {
100
+ .col-tab-6 { width: 50%; }
101
+ }
102
+ ```
103
+
104
+ ## Form Utilities
105
+
106
+ ### generateFormSchema
107
+
108
+ Generates a Zod schema from Payload CMS form fields.
109
+
110
+ ```typescript
111
+ import { generateFormSchema } from '@ainsleydev/sveltekit-helper/utils/forms'
112
+
113
+ const fields = [
114
+ { blockType: 'text', name: 'name', label: 'Name', required: true },
115
+ { blockType: 'email', name: 'email', label: 'Email', required: true },
116
+ { blockType: 'textarea', name: 'message', label: 'Message', required: false }
117
+ ]
118
+
119
+ const schema = generateFormSchema(fields)
120
+ // Returns Zod schema with appropriate validation
121
+ ```
122
+
123
+ ### flattenZodErrors
124
+
125
+ Converts Zod validation errors into a simple key-value object.
126
+
127
+ ```typescript
128
+ import { flattenZodErrors } from '@ainsleydev/sveltekit-helper/utils/forms'
129
+ import { z } from 'zod'
130
+
131
+ const schema = z.object({ email: z.string().email() })
132
+ const result = schema.safeParse({ email: 'invalid' })
133
+
134
+ if (!result.success) {
135
+ const errors = flattenZodErrors(result.error)
136
+ // { email: 'Invalid email' }
137
+ }
138
+ ```
139
+
140
+ ## Payload CMS Components
141
+
142
+ ### PayloadForm
143
+
144
+ Renders a form dynamically from Payload CMS form builder fields.
145
+
146
+ ```svelte
147
+ <script>
148
+ import { PayloadForm } from '@ainsleydev/sveltekit-helper/components/payload'
149
+
150
+ export let data
151
+ </script>
152
+
153
+ <PayloadForm
154
+ form={data.form}
155
+ apiEndpoint="/api/forms"
156
+ />
157
+ ```
158
+
159
+ #### Custom Submission
160
+
161
+ ```svelte
162
+ <PayloadForm
163
+ form={data.form}
164
+ onSubmit={async (formData) => {
165
+ // Custom submission logic
166
+ await customAPI.submit(formData)
167
+ }}
168
+ />
169
+ ```
170
+
171
+ #### Customisation
172
+
173
+ Style the form using CSS variables:
174
+
175
+ ```css
176
+ .payload-form {
177
+ --form-gap: 1.5rem;
178
+ --form-input-padding: 1rem;
179
+ --form-input-border: 1px solid #e5e7eb;
180
+ --form-input-border-radius: 0.5rem;
181
+ --form-input-bg: #ffffff;
182
+ --form-input-text: #111827;
183
+ --form-error-color: #ef4444;
184
+ --form-success-color: #10b981;
185
+ --form-button-bg: #3b82f6;
186
+ --form-button-text: #ffffff;
187
+ --form-button-hover-bg: #2563eb;
188
+ --form-button-disabled-bg: #9ca3af;
189
+ }
190
+ ```
191
+
192
+ ### PayloadMedia
193
+
194
+ Renders responsive images and videos from Payload CMS media fields with automatic format prioritisation (AVIF → WebP → JPEG/PNG).
195
+
196
+ ```svelte
197
+ <script>
198
+ import { PayloadMedia } from '@ainsleydev/sveltekit-helper/components/payload'
199
+
200
+ export let data
201
+ </script>
202
+
203
+ <PayloadMedia
204
+ data={data.image}
205
+ loading="lazy"
206
+ maxWidth={1200}
207
+ />
208
+ ```
209
+
210
+ #### Props
211
+
212
+ - `data`: Payload media object with `url`, `sizes`, `mimeType`, etc.
213
+ - `loading`: Optional `'lazy'` or `'eager'` loading strategy
214
+ - `maxWidth`: Optional maximum width to limit responsive sources
215
+ - `breakpointBuffer`: Pixels to add to breakpoint media queries (default: 50)
216
+ - `className`: Optional CSS class name
217
+ - `onload`: Optional load event handler
218
+
219
+ ## Peer Dependencies
220
+
221
+ - `svelte@^5.0.0`
222
+ - `@sveltejs/kit@^2.0.0`
223
+
224
+ ### Optional Dependencies
225
+
226
+ - `payload@^3.0.0` - For Payload CMS components
227
+ - `zod@^3.0.0` - For form validation
228
+
229
+ ## Development
230
+
231
+ ```bash
232
+ # Install dependencies
233
+ pnpm install
234
+
235
+ # Build the package
236
+ pnpm build
237
+
238
+ # Run tests
239
+ pnpm test
240
+
241
+ # Lint and format
242
+ pnpm lint
243
+ pnpm format
244
+ ```
245
+
246
+ ## License
247
+
248
+ MIT
@@ -0,0 +1,4 @@
1
+ export { default as Container } from './Container.svelte';
2
+ export { default as Row } from './Row.svelte';
3
+ export { default as Column } from './Column.svelte';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/Grid/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,OAAO,IAAI,GAAG,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,iBAAiB,CAAC"}
@@ -0,0 +1,5 @@
1
+ export { default as Container } from './Container.svelte';
2
+ export { default as Row } from './Row.svelte';
3
+ export { default as Column } from './Column.svelte';
4
+
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/Grid/index.ts"],"sourcesContent":["export { default as Container } from './Container.svelte';\nexport { default as Row } from './Row.svelte';\nexport { default as Column } from './Column.svelte';\n"],"names":["default","Container","Row","Column"],"mappings":"AAAA,SAASA,WAAWC,SAAS,QAAQ,qBAAqB;AAC1D,SAASD,WAAWE,GAAG,QAAQ,eAAe;AAC9C,SAASF,WAAWG,MAAM,QAAQ,kBAAkB"}
@@ -0,0 +1,4 @@
1
+ export { default as PayloadForm } from './PayloadForm.svelte';
2
+ export { default as PayloadMedia } from './PayloadMedia.svelte';
3
+ export type { Media, MediaSizes, PayloadMediaProps } from './types.js';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/payload/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAChE,YAAY,EAAE,KAAK,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,4 @@
1
+ export { default as PayloadForm } from './PayloadForm.svelte';
2
+ export { default as PayloadMedia } from './PayloadMedia.svelte';
3
+
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/payload/index.ts"],"sourcesContent":["export { default as PayloadForm } from './PayloadForm.svelte';\nexport { default as PayloadMedia } from './PayloadMedia.svelte';\nexport type { Media, MediaSizes, PayloadMediaProps } from './types.js';\n"],"names":["default","PayloadForm","PayloadMedia"],"mappings":"AAAA,SAASA,WAAWC,WAAW,QAAQ,uBAAuB;AAC9D,SAASD,WAAWE,YAAY,QAAQ,wBAAwB"}
@@ -0,0 +1,33 @@
1
+ import type { HTMLAttributes } from 'svelte/elements';
2
+ export type MediaSizes = Record<string, Partial<{
3
+ url?: string | null;
4
+ width?: number | null;
5
+ height?: number | null;
6
+ mimeType?: string | null;
7
+ }> | undefined>;
8
+ export type PayloadMediaProps = HTMLAttributes<HTMLElement> & {
9
+ data: Media;
10
+ loading?: 'lazy' | 'eager' | undefined;
11
+ className?: string;
12
+ breakpointBuffer?: number;
13
+ maxWidth?: number | undefined;
14
+ onload?: (event: Event) => void;
15
+ };
16
+ export type Media = {
17
+ id: number;
18
+ alt?: string;
19
+ updatedAt: string;
20
+ createdAt: string;
21
+ deletedAt?: string | null;
22
+ url?: string | null;
23
+ thumbnailURL?: string | null;
24
+ filename?: string | null;
25
+ mimeType?: string | null;
26
+ filesize?: number | null;
27
+ width?: number | null;
28
+ height?: number | null;
29
+ focalX?: number | null;
30
+ focalY?: number | null;
31
+ sizes?: MediaSizes;
32
+ };
33
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/components/payload/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEtD,MAAM,MAAM,UAAU,GAAG,MAAM,CAC9B,MAAM,EACJ,OAAO,CAAC;IACR,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB,CAAC,GACF,SAAS,CACX,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG,cAAc,CAAC,WAAW,CAAC,GAAG;IAC7D,IAAI,EAAE,KAAK,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC;IACvC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,KAAK,GAAG;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,KAAK,CAAC,EAAE,UAAU,CAAC;CACnB,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { };
2
+
3
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/payload/types.ts"],"sourcesContent":["import type { HTMLAttributes } from 'svelte/elements';\n\nexport type MediaSizes = Record<\n\tstring,\n\t| Partial<{\n\t\t\turl?: string | null;\n\t\t\twidth?: number | null;\n\t\t\theight?: number | null;\n\t\t\tmimeType?: string | null;\n\t }>\n\t| undefined\n>;\n\nexport type PayloadMediaProps = HTMLAttributes<HTMLElement> & {\n\tdata: Media;\n\tloading?: 'lazy' | 'eager' | undefined;\n\tclassName?: string;\n\tbreakpointBuffer?: number;\n\tmaxWidth?: number | undefined;\n\tonload?: (event: Event) => void;\n};\n\nexport type Media = {\n\tid: number;\n\talt?: string;\n\tupdatedAt: string;\n\tcreatedAt: string;\n\tdeletedAt?: string | null;\n\turl?: string | null;\n\tthumbnailURL?: string | null;\n\tfilename?: string | null;\n\tmimeType?: string | null;\n\tfilesize?: number | null;\n\twidth?: number | null;\n\theight?: number | null;\n\tfocalX?: number | null;\n\tfocalY?: number | null;\n\tsizes?: MediaSizes;\n};\n"],"names":[],"mappings":"AAsBA,WAgBE"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * @ainsleydev/sveltekit-helper
3
+ *
4
+ * SvelteKit utilities, components and helpers for ainsley.dev builds.
5
+ * Provides form utilities, grid components, and Payload CMS integrations.
6
+ */
7
+ export * from './components/Grid/index.js';
8
+ export * from './components/payload/index.js';
9
+ export * from './utils/forms/index.js';
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,cAAc,4BAA4B,CAAC;AAC3C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,wBAAwB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,11 @@
1
+ /**
2
+ * @ainsleydev/sveltekit-helper
3
+ *
4
+ * SvelteKit utilities, components and helpers for ainsley.dev builds.
5
+ * Provides form utilities, grid components, and Payload CMS integrations.
6
+ */ // Re-export all exports from submodules
7
+ export * from './components/Grid/index.js';
8
+ export * from './components/payload/index.js';
9
+ export * from './utils/forms/index.js';
10
+
11
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * @ainsleydev/sveltekit-helper\n *\n * SvelteKit utilities, components and helpers for ainsley.dev builds.\n * Provides form utilities, grid components, and Payload CMS integrations.\n */\n\n// Re-export all exports from submodules\nexport * from './components/Grid/index.js';\nexport * from './components/payload/index.js';\nexport * from './utils/forms/index.js';\n"],"names":[],"mappings":"AAAA;;;;;CAKC,GAED,wCAAwC;AACxC,cAAc,6BAA6B;AAC3C,cAAc,gCAAgC;AAC9C,cAAc,yBAAyB"}
@@ -0,0 +1,24 @@
1
+ import type { z } from 'zod';
2
+ export type FormErrors = Record<string, string>;
3
+ /**
4
+ * Flattens Zod validation errors into a simple key-value object.
5
+ * Takes only the first error message for each field.
6
+ *
7
+ * @param error - ZodError from validation failure.
8
+ * @returns Object mapping field names to error messages.
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * import { flattenZodErrors } from '@ainsleydev/sveltekit-helper/utils/forms'
13
+ *
14
+ * const schema = z.object({ email: z.string().email() })
15
+ * const result = schema.safeParse({ email: 'invalid' })
16
+ *
17
+ * if (!result.success) {
18
+ * const errors = flattenZodErrors(result.error)
19
+ * // { email: 'Invalid email' }
20
+ * }
21
+ * ```
22
+ */
23
+ export declare const flattenZodErrors: (error: z.ZodError) => FormErrors;
24
+ //# sourceMappingURL=helpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../../src/utils/forms/helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAE7B,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAEhD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,gBAAgB,GAAI,OAAO,CAAC,CAAC,QAAQ,KAAG,UAWpD,CAAC"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Flattens Zod validation errors into a simple key-value object.
3
+ * Takes only the first error message for each field.
4
+ *
5
+ * @param error - ZodError from validation failure.
6
+ * @returns Object mapping field names to error messages.
7
+ *
8
+ * @example
9
+ * ```typescript
10
+ * import { flattenZodErrors } from '@ainsleydev/sveltekit-helper/utils/forms'
11
+ *
12
+ * const schema = z.object({ email: z.string().email() })
13
+ * const result = schema.safeParse({ email: 'invalid' })
14
+ *
15
+ * if (!result.success) {
16
+ * const errors = flattenZodErrors(result.error)
17
+ * // { email: 'Invalid email' }
18
+ * }
19
+ * ```
20
+ */ export const flattenZodErrors = (error)=>{
21
+ const errors = {};
22
+ for (const issue of error.issues){
23
+ const path = issue.path.join('.');
24
+ if (!errors[path]) {
25
+ errors[path] = issue.message;
26
+ }
27
+ }
28
+ return errors;
29
+ };
30
+
31
+ //# sourceMappingURL=helpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/utils/forms/helpers.ts"],"sourcesContent":["import type { z } from 'zod';\n\nexport type FormErrors = Record<string, string>;\n\n/**\n * Flattens Zod validation errors into a simple key-value object.\n * Takes only the first error message for each field.\n *\n * @param error - ZodError from validation failure.\n * @returns Object mapping field names to error messages.\n *\n * @example\n * ```typescript\n * import { flattenZodErrors } from '@ainsleydev/sveltekit-helper/utils/forms'\n *\n * const schema = z.object({ email: z.string().email() })\n * const result = schema.safeParse({ email: 'invalid' })\n *\n * if (!result.success) {\n * const errors = flattenZodErrors(result.error)\n * // { email: 'Invalid email' }\n * }\n * ```\n */\nexport const flattenZodErrors = (error: z.ZodError): FormErrors => {\n\tconst errors: FormErrors = {};\n\n\tfor (const issue of error.issues) {\n\t\tconst path = issue.path.join('.');\n\t\tif (!errors[path]) {\n\t\t\terrors[path] = issue.message;\n\t\t}\n\t}\n\n\treturn errors;\n};\n"],"names":["flattenZodErrors","error","errors","issue","issues","path","join","message"],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;CAmBC,GACD,OAAO,MAAMA,mBAAmB,CAACC;IAChC,MAAMC,SAAqB,CAAC;IAE5B,KAAK,MAAMC,SAASF,MAAMG,MAAM,CAAE;QACjC,MAAMC,OAAOF,MAAME,IAAI,CAACC,IAAI,CAAC;QAC7B,IAAI,CAACJ,MAAM,CAACG,KAAK,EAAE;YAClBH,MAAM,CAACG,KAAK,GAAGF,MAAMI,OAAO;QAC7B;IACD;IAEA,OAAOL;AACR,EAAE"}
@@ -0,0 +1,3 @@
1
+ export { flattenZodErrors, type FormErrors } from './helpers.js';
2
+ export { generateFormSchema, type PayloadFormField } from './schema.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/utils/forms/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,KAAK,UAAU,EAAE,MAAM,cAAc,CAAC;AACjE,OAAO,EAAE,kBAAkB,EAAE,KAAK,gBAAgB,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,4 @@
1
+ export { flattenZodErrors } from './helpers.js';
2
+ export { generateFormSchema } from './schema.js';
3
+
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/utils/forms/index.ts"],"sourcesContent":["export { flattenZodErrors, type FormErrors } from './helpers.js';\nexport { generateFormSchema, type PayloadFormField } from './schema.js';\n"],"names":["flattenZodErrors","generateFormSchema"],"mappings":"AAAA,SAASA,gBAAgB,QAAyB,eAAe;AACjE,SAASC,kBAAkB,QAA+B,cAAc"}
@@ -0,0 +1,79 @@
1
+ import { z } from 'zod';
2
+ /**
3
+ * Represents a form field from Payload CMS form builder.
4
+ * This is a simplified type that covers the most common field types.
5
+ */
6
+ export type PayloadFormField = {
7
+ name: string;
8
+ label?: string | null;
9
+ placeholder?: string | null;
10
+ defaultValue?: string | null;
11
+ required?: boolean | null;
12
+ id?: string | null;
13
+ blockName?: string | null;
14
+ blockType: 'text';
15
+ } | {
16
+ name: string;
17
+ label?: string | null;
18
+ placeholder?: string | null;
19
+ defaultValue?: string | null;
20
+ required?: boolean | null;
21
+ id?: string | null;
22
+ blockName?: string | null;
23
+ blockType: 'email';
24
+ } | {
25
+ name: string;
26
+ label?: string | null;
27
+ placeholder?: string | null;
28
+ defaultValue?: number | null;
29
+ required?: boolean | null;
30
+ id?: string | null;
31
+ blockName?: string | null;
32
+ blockType: 'number';
33
+ } | {
34
+ name: string;
35
+ label?: string | null;
36
+ placeholder?: string | null;
37
+ defaultValue?: string | null;
38
+ required?: boolean | null;
39
+ id?: string | null;
40
+ blockName?: string | null;
41
+ blockType: 'textarea';
42
+ } | {
43
+ name: string;
44
+ label?: string | null;
45
+ defaultValue?: boolean | null;
46
+ required?: boolean | null;
47
+ id?: string | null;
48
+ blockName?: string | null;
49
+ blockType: 'checkbox';
50
+ };
51
+ /**
52
+ * Generates a dynamic Zod schema from Payload CMS Form Fields.
53
+ *
54
+ * This utility automatically creates validation schemas for Payload form fields,
55
+ * respecting required flags and field types.
56
+ *
57
+ * @param fields - Array of form fields from Payload CMS form builder.
58
+ * @returns Zod object schema for validating the form.
59
+ *
60
+ * @example
61
+ * ```typescript
62
+ * import { generateFormSchema } from '@ainsleydev/sveltekit-helper/utils/forms'
63
+ *
64
+ * const fields = [
65
+ * { blockType: 'text', name: 'name', label: 'Name', required: true },
66
+ * { blockType: 'email', name: 'email', label: 'Email', required: true },
67
+ * { blockType: 'textarea', name: 'message', label: 'Message', required: false }
68
+ * ]
69
+ *
70
+ * const schema = generateFormSchema(fields)
71
+ * // Returns z.object({
72
+ * // name: z.string().min(1, { message: 'Name is required' }),
73
+ * // email: z.string().email({ message: 'Invalid email' }),
74
+ * // message: z.string().optional()
75
+ * // })
76
+ * ```
77
+ */
78
+ export declare const generateFormSchema: (fields?: PayloadFormField[] | null) => z.ZodObject<Record<string, z.ZodTypeAny>>;
79
+ //# sourceMappingURL=schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../src/utils/forms/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;GAGG;AACH,MAAM,MAAM,gBAAgB,GACzB;IACA,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,QAAQ,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IAC1B,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;CACjB,GACD;IACA,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,QAAQ,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IAC1B,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,SAAS,EAAE,OAAO,CAAC;CAClB,GACD;IACA,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,QAAQ,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IAC1B,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,SAAS,EAAE,QAAQ,CAAC;CACnB,GACD;IACA,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,QAAQ,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IAC1B,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,SAAS,EAAE,UAAU,CAAC;CACrB,GACD;IACA,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,YAAY,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IAC9B,QAAQ,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IAC1B,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,SAAS,EAAE,UAAU,CAAC;CACrB,CAAC;AAEL;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,eAAO,MAAM,kBAAkB,GAC9B,SAAS,gBAAgB,EAAE,GAAG,IAAI,KAChC,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,UAAU,CAAC,CA0B1C,CAAC"}
@@ -0,0 +1,53 @@
1
+ import { z } from 'zod';
2
+ /**
3
+ * Generates a dynamic Zod schema from Payload CMS Form Fields.
4
+ *
5
+ * This utility automatically creates validation schemas for Payload form fields,
6
+ * respecting required flags and field types.
7
+ *
8
+ * @param fields - Array of form fields from Payload CMS form builder.
9
+ * @returns Zod object schema for validating the form.
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * import { generateFormSchema } from '@ainsleydev/sveltekit-helper/utils/forms'
14
+ *
15
+ * const fields = [
16
+ * { blockType: 'text', name: 'name', label: 'Name', required: true },
17
+ * { blockType: 'email', name: 'email', label: 'Email', required: true },
18
+ * { blockType: 'textarea', name: 'message', label: 'Message', required: false }
19
+ * ]
20
+ *
21
+ * const schema = generateFormSchema(fields)
22
+ * // Returns z.object({
23
+ * // name: z.string().min(1, { message: 'Name is required' }),
24
+ * // email: z.string().email({ message: 'Invalid email' }),
25
+ * // message: z.string().optional()
26
+ * // })
27
+ * ```
28
+ */ export const generateFormSchema = (fields)=>{
29
+ if (!fields?.length) return z.object({});
30
+ const shape = {};
31
+ for (const field of fields){
32
+ if (field.blockType === 'text' || field.blockType === 'textarea') {
33
+ shape[field.name] = field.required ? z.string().min(1, {
34
+ message: `${field.label || field.name} is required`
35
+ }) : z.string().optional();
36
+ } else if (field.blockType === 'email') {
37
+ shape[field.name] = field.required ? z.string().email({
38
+ message: 'Invalid email'
39
+ }) : z.string().email({
40
+ message: 'Invalid email'
41
+ }).optional();
42
+ } else if (field.blockType === 'number') {
43
+ shape[field.name] = field.required ? z.number() : z.number().optional();
44
+ } else if (field.blockType === 'checkbox') {
45
+ shape[field.name] = field.required ? z.boolean().refine((val)=>val === true, {
46
+ message: `${field.label || field.name} must be checked`
47
+ }) : z.boolean().optional();
48
+ }
49
+ }
50
+ return z.object(shape);
51
+ };
52
+
53
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/utils/forms/schema.ts"],"sourcesContent":["import { z } from 'zod';\n\n/**\n * Represents a form field from Payload CMS form builder.\n * This is a simplified type that covers the most common field types.\n */\nexport type PayloadFormField =\n\t| {\n\t\t\tname: string;\n\t\t\tlabel?: string | null;\n\t\t\tplaceholder?: string | null;\n\t\t\tdefaultValue?: string | null;\n\t\t\trequired?: boolean | null;\n\t\t\tid?: string | null;\n\t\t\tblockName?: string | null;\n\t\t\tblockType: 'text';\n\t }\n\t| {\n\t\t\tname: string;\n\t\t\tlabel?: string | null;\n\t\t\tplaceholder?: string | null;\n\t\t\tdefaultValue?: string | null;\n\t\t\trequired?: boolean | null;\n\t\t\tid?: string | null;\n\t\t\tblockName?: string | null;\n\t\t\tblockType: 'email';\n\t }\n\t| {\n\t\t\tname: string;\n\t\t\tlabel?: string | null;\n\t\t\tplaceholder?: string | null;\n\t\t\tdefaultValue?: number | null;\n\t\t\trequired?: boolean | null;\n\t\t\tid?: string | null;\n\t\t\tblockName?: string | null;\n\t\t\tblockType: 'number';\n\t }\n\t| {\n\t\t\tname: string;\n\t\t\tlabel?: string | null;\n\t\t\tplaceholder?: string | null;\n\t\t\tdefaultValue?: string | null;\n\t\t\trequired?: boolean | null;\n\t\t\tid?: string | null;\n\t\t\tblockName?: string | null;\n\t\t\tblockType: 'textarea';\n\t }\n\t| {\n\t\t\tname: string;\n\t\t\tlabel?: string | null;\n\t\t\tdefaultValue?: boolean | null;\n\t\t\trequired?: boolean | null;\n\t\t\tid?: string | null;\n\t\t\tblockName?: string | null;\n\t\t\tblockType: 'checkbox';\n\t };\n\n/**\n * Generates a dynamic Zod schema from Payload CMS Form Fields.\n *\n * This utility automatically creates validation schemas for Payload form fields,\n * respecting required flags and field types.\n *\n * @param fields - Array of form fields from Payload CMS form builder.\n * @returns Zod object schema for validating the form.\n *\n * @example\n * ```typescript\n * import { generateFormSchema } from '@ainsleydev/sveltekit-helper/utils/forms'\n *\n * const fields = [\n * { blockType: 'text', name: 'name', label: 'Name', required: true },\n * { blockType: 'email', name: 'email', label: 'Email', required: true },\n * { blockType: 'textarea', name: 'message', label: 'Message', required: false }\n * ]\n *\n * const schema = generateFormSchema(fields)\n * // Returns z.object({\n * // name: z.string().min(1, { message: 'Name is required' }),\n * // email: z.string().email({ message: 'Invalid email' }),\n * // message: z.string().optional()\n * // })\n * ```\n */\nexport const generateFormSchema = (\n\tfields?: PayloadFormField[] | null,\n): z.ZodObject<Record<string, z.ZodTypeAny>> => {\n\tif (!fields?.length) return z.object({}) as z.ZodObject<Record<string, z.ZodTypeAny>>;\n\n\tconst shape: Record<string, z.ZodTypeAny> = {};\n\n\tfor (const field of fields) {\n\t\tif (field.blockType === 'text' || field.blockType === 'textarea') {\n\t\t\tshape[field.name] = field.required\n\t\t\t\t? z.string().min(1, { message: `${field.label || field.name} is required` })\n\t\t\t\t: z.string().optional();\n\t\t} else if (field.blockType === 'email') {\n\t\t\tshape[field.name] = field.required\n\t\t\t\t? z.string().email({ message: 'Invalid email' })\n\t\t\t\t: z.string().email({ message: 'Invalid email' }).optional();\n\t\t} else if (field.blockType === 'number') {\n\t\t\tshape[field.name] = field.required ? z.number() : z.number().optional();\n\t\t} else if (field.blockType === 'checkbox') {\n\t\t\tshape[field.name] = field.required\n\t\t\t\t? z.boolean().refine((val) => val === true, {\n\t\t\t\t\t\tmessage: `${field.label || field.name} must be checked`,\n\t\t\t\t\t})\n\t\t\t\t: z.boolean().optional();\n\t\t}\n\t}\n\n\treturn z.object(shape) as z.ZodObject<Record<string, z.ZodTypeAny>>;\n};\n"],"names":["z","generateFormSchema","fields","length","object","shape","field","blockType","name","required","string","min","message","label","optional","email","number","boolean","refine","val"],"mappings":"AAAA,SAASA,CAAC,QAAQ,MAAM;AAyDxB;;;;;;;;;;;;;;;;;;;;;;;;;;CA0BC,GACD,OAAO,MAAMC,qBAAqB,CACjCC;IAEA,IAAI,CAACA,QAAQC,QAAQ,OAAOH,EAAEI,MAAM,CAAC,CAAC;IAEtC,MAAMC,QAAsC,CAAC;IAE7C,KAAK,MAAMC,SAASJ,OAAQ;QAC3B,IAAII,MAAMC,SAAS,KAAK,UAAUD,MAAMC,SAAS,KAAK,YAAY;YACjEF,KAAK,CAACC,MAAME,IAAI,CAAC,GAAGF,MAAMG,QAAQ,GAC/BT,EAAEU,MAAM,GAAGC,GAAG,CAAC,GAAG;gBAAEC,SAAS,GAAGN,MAAMO,KAAK,IAAIP,MAAME,IAAI,CAAC,YAAY,CAAC;YAAC,KACxER,EAAEU,MAAM,GAAGI,QAAQ;QACvB,OAAO,IAAIR,MAAMC,SAAS,KAAK,SAAS;YACvCF,KAAK,CAACC,MAAME,IAAI,CAAC,GAAGF,MAAMG,QAAQ,GAC/BT,EAAEU,MAAM,GAAGK,KAAK,CAAC;gBAAEH,SAAS;YAAgB,KAC5CZ,EAAEU,MAAM,GAAGK,KAAK,CAAC;gBAAEH,SAAS;YAAgB,GAAGE,QAAQ;QAC3D,OAAO,IAAIR,MAAMC,SAAS,KAAK,UAAU;YACxCF,KAAK,CAACC,MAAME,IAAI,CAAC,GAAGF,MAAMG,QAAQ,GAAGT,EAAEgB,MAAM,KAAKhB,EAAEgB,MAAM,GAAGF,QAAQ;QACtE,OAAO,IAAIR,MAAMC,SAAS,KAAK,YAAY;YAC1CF,KAAK,CAACC,MAAME,IAAI,CAAC,GAAGF,MAAMG,QAAQ,GAC/BT,EAAEiB,OAAO,GAAGC,MAAM,CAAC,CAACC,MAAQA,QAAQ,MAAM;gBAC1CP,SAAS,GAAGN,MAAMO,KAAK,IAAIP,MAAME,IAAI,CAAC,gBAAgB,CAAC;YACxD,KACCR,EAAEiB,OAAO,GAAGH,QAAQ;QACxB;IACD;IAEA,OAAOd,EAAEI,MAAM,CAACC;AACjB,EAAE"}
@@ -0,0 +1,115 @@
1
+ import { describe, expect, test } from 'vitest';
2
+ import { z } from 'zod';
3
+ import { generateFormSchema } from './schema';
4
+ describe('generateFormSchema', ()=>{
5
+ test('returns empty object schema when no fields provided', ()=>{
6
+ const schema = generateFormSchema(null);
7
+ expect(schema).toBeInstanceOf(z.ZodObject);
8
+ expect(schema.shape).toEqual({});
9
+ });
10
+ test('returns empty object schema for empty array', ()=>{
11
+ const schema = generateFormSchema([]);
12
+ expect(schema).toBeInstanceOf(z.ZodObject);
13
+ expect(schema.shape).toEqual({});
14
+ });
15
+ test('generates required text field schema', ()=>{
16
+ const fields = [
17
+ {
18
+ blockType: 'text',
19
+ name: 'firstName',
20
+ label: 'First Name',
21
+ required: true
22
+ }
23
+ ];
24
+ const schema = generateFormSchema(fields);
25
+ const result = schema.safeParse({
26
+ firstName: ''
27
+ });
28
+ expect(result.success).toBe(false);
29
+ const validResult = schema.safeParse({
30
+ firstName: 'John'
31
+ });
32
+ expect(validResult.success).toBe(true);
33
+ });
34
+ test('generates optional text field schema', ()=>{
35
+ const fields = [
36
+ {
37
+ blockType: 'text',
38
+ name: 'middleName',
39
+ label: 'Middle Name',
40
+ required: false
41
+ }
42
+ ];
43
+ const schema = generateFormSchema(fields);
44
+ const result = schema.safeParse({});
45
+ expect(result.success).toBe(true);
46
+ });
47
+ test('generates required email field schema', ()=>{
48
+ const fields = [
49
+ {
50
+ blockType: 'email',
51
+ name: 'email',
52
+ label: 'Email',
53
+ required: true
54
+ }
55
+ ];
56
+ const schema = generateFormSchema(fields);
57
+ const invalidResult = schema.safeParse({
58
+ email: 'not-an-email'
59
+ });
60
+ expect(invalidResult.success).toBe(false);
61
+ const validResult = schema.safeParse({
62
+ email: 'test@example.com'
63
+ });
64
+ expect(validResult.success).toBe(true);
65
+ });
66
+ test('generates required checkbox field schema', ()=>{
67
+ const fields = [
68
+ {
69
+ blockType: 'checkbox',
70
+ name: 'terms',
71
+ label: 'Accept Terms',
72
+ required: true
73
+ }
74
+ ];
75
+ const schema = generateFormSchema(fields);
76
+ const falseResult = schema.safeParse({
77
+ terms: false
78
+ });
79
+ expect(falseResult.success).toBe(false);
80
+ const trueResult = schema.safeParse({
81
+ terms: true
82
+ });
83
+ expect(trueResult.success).toBe(true);
84
+ });
85
+ test('generates schema with multiple fields', ()=>{
86
+ const fields = [
87
+ {
88
+ blockType: 'text',
89
+ name: 'name',
90
+ label: 'Name',
91
+ required: true
92
+ },
93
+ {
94
+ blockType: 'email',
95
+ name: 'email',
96
+ label: 'Email',
97
+ required: true
98
+ },
99
+ {
100
+ blockType: 'textarea',
101
+ name: 'message',
102
+ label: 'Message',
103
+ required: false
104
+ }
105
+ ];
106
+ const schema = generateFormSchema(fields);
107
+ const result = schema.safeParse({
108
+ name: 'John',
109
+ email: 'john@example.com'
110
+ });
111
+ expect(result.success).toBe(true);
112
+ });
113
+ });
114
+
115
+ //# sourceMappingURL=schema.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/utils/forms/schema.test.ts"],"sourcesContent":["import { describe, expect, test } from 'vitest';\nimport { z } from 'zod';\n\nimport { generateFormSchema } from './schema';\n\ndescribe('generateFormSchema', () => {\n\ttest('returns empty object schema when no fields provided', () => {\n\t\tconst schema = generateFormSchema(null);\n\t\texpect(schema).toBeInstanceOf(z.ZodObject);\n\t\texpect(schema.shape).toEqual({});\n\t});\n\n\ttest('returns empty object schema for empty array', () => {\n\t\tconst schema = generateFormSchema([]);\n\t\texpect(schema).toBeInstanceOf(z.ZodObject);\n\t\texpect(schema.shape).toEqual({});\n\t});\n\n\ttest('generates required text field schema', () => {\n\t\tconst fields = [\n\t\t\t{\n\t\t\t\tblockType: 'text' as const,\n\t\t\t\tname: 'firstName',\n\t\t\t\tlabel: 'First Name',\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t];\n\n\t\tconst schema = generateFormSchema(fields);\n\t\tconst result = schema.safeParse({ firstName: '' });\n\t\texpect(result.success).toBe(false);\n\n\t\tconst validResult = schema.safeParse({ firstName: 'John' });\n\t\texpect(validResult.success).toBe(true);\n\t});\n\n\ttest('generates optional text field schema', () => {\n\t\tconst fields = [\n\t\t\t{\n\t\t\t\tblockType: 'text' as const,\n\t\t\t\tname: 'middleName',\n\t\t\t\tlabel: 'Middle Name',\n\t\t\t\trequired: false,\n\t\t\t},\n\t\t];\n\n\t\tconst schema = generateFormSchema(fields);\n\t\tconst result = schema.safeParse({});\n\t\texpect(result.success).toBe(true);\n\t});\n\n\ttest('generates required email field schema', () => {\n\t\tconst fields = [\n\t\t\t{\n\t\t\t\tblockType: 'email' as const,\n\t\t\t\tname: 'email',\n\t\t\t\tlabel: 'Email',\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t];\n\n\t\tconst schema = generateFormSchema(fields);\n\t\tconst invalidResult = schema.safeParse({ email: 'not-an-email' });\n\t\texpect(invalidResult.success).toBe(false);\n\n\t\tconst validResult = schema.safeParse({ email: 'test@example.com' });\n\t\texpect(validResult.success).toBe(true);\n\t});\n\n\ttest('generates required checkbox field schema', () => {\n\t\tconst fields = [\n\t\t\t{\n\t\t\t\tblockType: 'checkbox' as const,\n\t\t\t\tname: 'terms',\n\t\t\t\tlabel: 'Accept Terms',\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t];\n\n\t\tconst schema = generateFormSchema(fields);\n\t\tconst falseResult = schema.safeParse({ terms: false });\n\t\texpect(falseResult.success).toBe(false);\n\n\t\tconst trueResult = schema.safeParse({ terms: true });\n\t\texpect(trueResult.success).toBe(true);\n\t});\n\n\ttest('generates schema with multiple fields', () => {\n\t\tconst fields = [\n\t\t\t{\n\t\t\t\tblockType: 'text' as const,\n\t\t\t\tname: 'name',\n\t\t\t\tlabel: 'Name',\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t\t{\n\t\t\t\tblockType: 'email' as const,\n\t\t\t\tname: 'email',\n\t\t\t\tlabel: 'Email',\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t\t{\n\t\t\t\tblockType: 'textarea' as const,\n\t\t\t\tname: 'message',\n\t\t\t\tlabel: 'Message',\n\t\t\t\trequired: false,\n\t\t\t},\n\t\t];\n\n\t\tconst schema = generateFormSchema(fields);\n\t\tconst result = schema.safeParse({\n\t\t\tname: 'John',\n\t\t\temail: 'john@example.com',\n\t\t});\n\t\texpect(result.success).toBe(true);\n\t});\n});\n"],"names":["describe","expect","test","z","generateFormSchema","schema","toBeInstanceOf","ZodObject","shape","toEqual","fields","blockType","name","label","required","result","safeParse","firstName","success","toBe","validResult","invalidResult","email","falseResult","terms","trueResult"],"mappings":"AAAA,SAASA,QAAQ,EAAEC,MAAM,EAAEC,IAAI,QAAQ,SAAS;AAChD,SAASC,CAAC,QAAQ,MAAM;AAExB,SAASC,kBAAkB,QAAQ,WAAW;AAE9CJ,SAAS,sBAAsB;IAC9BE,KAAK,uDAAuD;QAC3D,MAAMG,SAASD,mBAAmB;QAClCH,OAAOI,QAAQC,cAAc,CAACH,EAAEI,SAAS;QACzCN,OAAOI,OAAOG,KAAK,EAAEC,OAAO,CAAC,CAAC;IAC/B;IAEAP,KAAK,+CAA+C;QACnD,MAAMG,SAASD,mBAAmB,EAAE;QACpCH,OAAOI,QAAQC,cAAc,CAACH,EAAEI,SAAS;QACzCN,OAAOI,OAAOG,KAAK,EAAEC,OAAO,CAAC,CAAC;IAC/B;IAEAP,KAAK,wCAAwC;QAC5C,MAAMQ,SAAS;YACd;gBACCC,WAAW;gBACXC,MAAM;gBACNC,OAAO;gBACPC,UAAU;YACX;SACA;QAED,MAAMT,SAASD,mBAAmBM;QAClC,MAAMK,SAASV,OAAOW,SAAS,CAAC;YAAEC,WAAW;QAAG;QAChDhB,OAAOc,OAAOG,OAAO,EAAEC,IAAI,CAAC;QAE5B,MAAMC,cAAcf,OAAOW,SAAS,CAAC;YAAEC,WAAW;QAAO;QACzDhB,OAAOmB,YAAYF,OAAO,EAAEC,IAAI,CAAC;IAClC;IAEAjB,KAAK,wCAAwC;QAC5C,MAAMQ,SAAS;YACd;gBACCC,WAAW;gBACXC,MAAM;gBACNC,OAAO;gBACPC,UAAU;YACX;SACA;QAED,MAAMT,SAASD,mBAAmBM;QAClC,MAAMK,SAASV,OAAOW,SAAS,CAAC,CAAC;QACjCf,OAAOc,OAAOG,OAAO,EAAEC,IAAI,CAAC;IAC7B;IAEAjB,KAAK,yCAAyC;QAC7C,MAAMQ,SAAS;YACd;gBACCC,WAAW;gBACXC,MAAM;gBACNC,OAAO;gBACPC,UAAU;YACX;SACA;QAED,MAAMT,SAASD,mBAAmBM;QAClC,MAAMW,gBAAgBhB,OAAOW,SAAS,CAAC;YAAEM,OAAO;QAAe;QAC/DrB,OAAOoB,cAAcH,OAAO,EAAEC,IAAI,CAAC;QAEnC,MAAMC,cAAcf,OAAOW,SAAS,CAAC;YAAEM,OAAO;QAAmB;QACjErB,OAAOmB,YAAYF,OAAO,EAAEC,IAAI,CAAC;IAClC;IAEAjB,KAAK,4CAA4C;QAChD,MAAMQ,SAAS;YACd;gBACCC,WAAW;gBACXC,MAAM;gBACNC,OAAO;gBACPC,UAAU;YACX;SACA;QAED,MAAMT,SAASD,mBAAmBM;QAClC,MAAMa,cAAclB,OAAOW,SAAS,CAAC;YAAEQ,OAAO;QAAM;QACpDvB,OAAOsB,YAAYL,OAAO,EAAEC,IAAI,CAAC;QAEjC,MAAMM,aAAapB,OAAOW,SAAS,CAAC;YAAEQ,OAAO;QAAK;QAClDvB,OAAOwB,WAAWP,OAAO,EAAEC,IAAI,CAAC;IACjC;IAEAjB,KAAK,yCAAyC;QAC7C,MAAMQ,SAAS;YACd;gBACCC,WAAW;gBACXC,MAAM;gBACNC,OAAO;gBACPC,UAAU;YACX;YACA;gBACCH,WAAW;gBACXC,MAAM;gBACNC,OAAO;gBACPC,UAAU;YACX;YACA;gBACCH,WAAW;gBACXC,MAAM;gBACNC,OAAO;gBACPC,UAAU;YACX;SACA;QAED,MAAMT,SAASD,mBAAmBM;QAClC,MAAMK,SAASV,OAAOW,SAAS,CAAC;YAC/BJ,MAAM;YACNU,OAAO;QACR;QACArB,OAAOc,OAAOG,OAAO,EAAEC,IAAI,CAAC;IAC7B;AACD"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Generates an alphanumeric random string.
3
+ *
4
+ * @param length - The length of the string to generate.
5
+ * @returns The generated random string.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * const id = generateRandomString(10)
10
+ * // Returns something like: "a3k9mxp2q1"
11
+ * ```
12
+ */
13
+ export declare const generateRandomString: (length: number) => string;
14
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/utils/strings/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,oBAAoB,GAAI,QAAQ,MAAM,KAAG,MAMrD,CAAC"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Generates an alphanumeric random string.
3
+ *
4
+ * @param length - The length of the string to generate.
5
+ * @returns The generated random string.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * const id = generateRandomString(10)
10
+ * // Returns something like: "a3k9mxp2q1"
11
+ * ```
12
+ */ export const generateRandomString = (length)=>{
13
+ let result = '';
14
+ while(result.length < length){
15
+ result += (Math.random() + 1).toString(36).substring(2);
16
+ }
17
+ return result.substring(0, length);
18
+ };
19
+
20
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/utils/strings/index.ts"],"sourcesContent":["/**\n * Generates an alphanumeric random string.\n *\n * @param length - The length of the string to generate.\n * @returns The generated random string.\n *\n * @example\n * ```typescript\n * const id = generateRandomString(10)\n * // Returns something like: \"a3k9mxp2q1\"\n * ```\n */\nexport const generateRandomString = (length: number): string => {\n\tlet result = '';\n\twhile (result.length < length) {\n\t\tresult += (Math.random() + 1).toString(36).substring(2);\n\t}\n\treturn result.substring(0, length);\n};\n"],"names":["generateRandomString","length","result","Math","random","toString","substring"],"mappings":"AAAA;;;;;;;;;;;CAWC,GACD,OAAO,MAAMA,uBAAuB,CAACC;IACpC,IAAIC,SAAS;IACb,MAAOA,OAAOD,MAAM,GAAGA,OAAQ;QAC9BC,UAAU,AAACC,CAAAA,KAAKC,MAAM,KAAK,CAAA,EAAGC,QAAQ,CAAC,IAAIC,SAAS,CAAC;IACtD;IACA,OAAOJ,OAAOI,SAAS,CAAC,GAAGL;AAC5B,EAAE"}
@@ -0,0 +1,28 @@
1
+ import { describe, expect, test } from 'vitest';
2
+ import { generateRandomString } from './index';
3
+ describe('generateRandomString', ()=>{
4
+ test('generates a string of the specified length', ()=>{
5
+ const length = 10;
6
+ const result = generateRandomString(length);
7
+ expect(result).toHaveLength(length);
8
+ });
9
+ test('generates alphanumeric characters only', ()=>{
10
+ const result = generateRandomString(100);
11
+ expect(result).toMatch(/^[a-z0-9]+$/);
12
+ });
13
+ test('generates different strings on multiple calls', ()=>{
14
+ const first = generateRandomString(20);
15
+ const second = generateRandomString(20);
16
+ expect(first).not.toBe(second);
17
+ });
18
+ test('handles length of 0', ()=>{
19
+ const result = generateRandomString(0);
20
+ expect(result).toBe('');
21
+ });
22
+ test('handles length of 1', ()=>{
23
+ const result = generateRandomString(1);
24
+ expect(result).toHaveLength(1);
25
+ });
26
+ });
27
+
28
+ //# sourceMappingURL=index.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/utils/strings/index.test.ts"],"sourcesContent":["import { describe, expect, test } from 'vitest';\n\nimport { generateRandomString } from './index';\n\ndescribe('generateRandomString', () => {\n\ttest('generates a string of the specified length', () => {\n\t\tconst length = 10;\n\t\tconst result = generateRandomString(length);\n\t\texpect(result).toHaveLength(length);\n\t});\n\n\ttest('generates alphanumeric characters only', () => {\n\t\tconst result = generateRandomString(100);\n\t\texpect(result).toMatch(/^[a-z0-9]+$/);\n\t});\n\n\ttest('generates different strings on multiple calls', () => {\n\t\tconst first = generateRandomString(20);\n\t\tconst second = generateRandomString(20);\n\t\texpect(first).not.toBe(second);\n\t});\n\n\ttest('handles length of 0', () => {\n\t\tconst result = generateRandomString(0);\n\t\texpect(result).toBe('');\n\t});\n\n\ttest('handles length of 1', () => {\n\t\tconst result = generateRandomString(1);\n\t\texpect(result).toHaveLength(1);\n\t});\n});\n"],"names":["describe","expect","test","generateRandomString","length","result","toHaveLength","toMatch","first","second","not","toBe"],"mappings":"AAAA,SAASA,QAAQ,EAAEC,MAAM,EAAEC,IAAI,QAAQ,SAAS;AAEhD,SAASC,oBAAoB,QAAQ,UAAU;AAE/CH,SAAS,wBAAwB;IAChCE,KAAK,8CAA8C;QAClD,MAAME,SAAS;QACf,MAAMC,SAASF,qBAAqBC;QACpCH,OAAOI,QAAQC,YAAY,CAACF;IAC7B;IAEAF,KAAK,0CAA0C;QAC9C,MAAMG,SAASF,qBAAqB;QACpCF,OAAOI,QAAQE,OAAO,CAAC;IACxB;IAEAL,KAAK,iDAAiD;QACrD,MAAMM,QAAQL,qBAAqB;QACnC,MAAMM,SAASN,qBAAqB;QACpCF,OAAOO,OAAOE,GAAG,CAACC,IAAI,CAACF;IACxB;IAEAP,KAAK,uBAAuB;QAC3B,MAAMG,SAASF,qBAAqB;QACpCF,OAAOI,QAAQM,IAAI,CAAC;IACrB;IAEAT,KAAK,uBAAuB;QAC3B,MAAMG,SAASF,qBAAqB;QACpCF,OAAOI,QAAQC,YAAY,CAAC;IAC7B;AACD"}
package/package.json ADDED
@@ -0,0 +1,96 @@
1
+ {
2
+ "name": "@ainsleydev/sveltekit-helper",
3
+ "version": "0.1.0",
4
+ "description": "SvelteKit utilities, components and helpers for ainsley.dev builds",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "keywords": [
8
+ "sveltekit",
9
+ "svelte",
10
+ "utilities",
11
+ "components",
12
+ "typescript"
13
+ ],
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "https://github.com/ainsleydev/webkit.git",
17
+ "directory": "packages/sveltekit-helper"
18
+ },
19
+ "author": {
20
+ "name": "ainsley.dev",
21
+ "email": "hello@ainsley.dev",
22
+ "url": "https://ainsley.dev"
23
+ },
24
+ "main": "./dist/index.js",
25
+ "types": "./dist/index.d.ts",
26
+ "exports": {
27
+ ".": {
28
+ "types": "./dist/index.d.ts",
29
+ "import": "./dist/index.js",
30
+ "default": "./dist/index.js"
31
+ },
32
+ "./components/Grid": {
33
+ "types": "./dist/components/Grid/index.d.ts",
34
+ "import": "./dist/components/Grid/index.js"
35
+ },
36
+ "./components/payload": {
37
+ "types": "./dist/components/payload/index.d.ts",
38
+ "import": "./dist/components/payload/index.js"
39
+ },
40
+ "./utils/forms": {
41
+ "types": "./dist/utils/forms/index.d.ts",
42
+ "import": "./dist/utils/forms/index.js"
43
+ },
44
+ "./utils/strings": {
45
+ "types": "./dist/utils/strings/index.d.ts",
46
+ "import": "./dist/utils/strings/index.js"
47
+ },
48
+ "./package.json": "./package.json"
49
+ },
50
+ "files": [
51
+ "dist"
52
+ ],
53
+ "publishConfig": {
54
+ "access": "public"
55
+ },
56
+ "peerDependencies": {
57
+ "@sveltejs/kit": "^2.0.0",
58
+ "svelte": "^5.0.0"
59
+ },
60
+ "peerDependenciesMeta": {
61
+ "payload": {
62
+ "optional": true
63
+ },
64
+ "zod": {
65
+ "optional": true
66
+ }
67
+ },
68
+ "dependencies": {},
69
+ "devDependencies": {
70
+ "@sveltejs/kit": "^2.0.0",
71
+ "@sveltejs/package": "^2.0.0",
72
+ "@swc/cli": "^0.7.8",
73
+ "@swc/core": "^1.7.2",
74
+ "@types/node": "^24.10.1",
75
+ "payload": "3.63.0",
76
+ "rimraf": "6.1.0",
77
+ "svelte": "^5.0.0",
78
+ "typescript": "^5.5.4",
79
+ "vitest": "^2.0.5",
80
+ "zod": "^3.24.3",
81
+ "@ainsleydev/eslint-config": "0.0.9"
82
+ },
83
+ "engines": {
84
+ "node": ">=18",
85
+ "pnpm": ">=9"
86
+ },
87
+ "scripts": {
88
+ "build": "pnpm clean && pnpm build:types && pnpm build:swc",
89
+ "build:swc": "swc ./src -d ./dist --config-file .swcrc --strip-leading-paths",
90
+ "build:types": "tsc --emitDeclarationOnly --outDir dist",
91
+ "format": "biome check --write --unsafe .",
92
+ "lint": "biome lint --write .",
93
+ "clean": "rimraf {dist,*.tsbuildinfo}",
94
+ "test": "vitest run"
95
+ }
96
+ }