@keysdown/form-wrapper 1.0.5 → 2.0.1

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 (65) hide show
  1. package/README.md +335 -31
  2. package/dist/form-wrapper.cjs +1 -1
  3. package/dist/form-wrapper.js +114 -28
  4. package/dist/form-wrapper.umd.cjs +1 -1
  5. package/dist/plugins/formValidation.cjs +1 -0
  6. package/dist/plugins/formValidation.d.ts +4 -0
  7. package/dist/plugins/formValidation.js +7 -0
  8. package/dist/plugins/locales.cjs +1 -0
  9. package/dist/plugins/locales.d.ts +2 -0
  10. package/dist/plugins/locales.js +115 -0
  11. package/dist/plugins/rules.cjs +1 -0
  12. package/dist/plugins/rules.d.ts +2 -0
  13. package/dist/plugins/rules.js +2 -0
  14. package/dist/rules-BIt-pifQ.cjs +1 -0
  15. package/dist/rules-DZmnRoUy.js +196 -0
  16. package/dist/src/core/Errors.d.ts +1 -1
  17. package/dist/src/core/Form.d.ts +11 -0
  18. package/dist/src/plugins/formValidation.d.ts +3 -0
  19. package/dist/src/plugins/locales/en.d.ts +3 -0
  20. package/dist/src/plugins/locales/es.d.ts +3 -0
  21. package/dist/src/plugins/locales/index.d.ts +3 -0
  22. package/dist/src/plugins/locales/pt.d.ts +3 -0
  23. package/dist/src/plugins/rules/alpha.d.ts +2 -0
  24. package/dist/src/plugins/rules/alphaNumeric.d.ts +2 -0
  25. package/dist/src/plugins/rules/array.d.ts +2 -0
  26. package/dist/src/plugins/rules/between.d.ts +2 -0
  27. package/dist/src/plugins/rules/boolean.d.ts +2 -0
  28. package/dist/src/plugins/rules/confirmed.d.ts +2 -0
  29. package/dist/src/plugins/rules/date.d.ts +2 -0
  30. package/dist/src/plugins/rules/different.d.ts +2 -0
  31. package/dist/src/plugins/rules/digits.d.ts +2 -0
  32. package/dist/src/plugins/rules/digitsBetween.d.ts +2 -0
  33. package/dist/src/plugins/rules/email.d.ts +2 -0
  34. package/dist/src/plugins/rules/endsWith.d.ts +2 -0
  35. package/dist/src/plugins/rules/greaterThan.d.ts +2 -0
  36. package/dist/src/plugins/rules/greaterThanOrEqual.d.ts +2 -0
  37. package/dist/src/plugins/rules/in.d.ts +2 -0
  38. package/dist/src/plugins/rules/index.d.ts +35 -0
  39. package/dist/src/plugins/rules/integer.d.ts +2 -0
  40. package/dist/src/plugins/rules/ip.d.ts +2 -0
  41. package/dist/src/plugins/rules/json.d.ts +2 -0
  42. package/dist/src/plugins/rules/lessThan.d.ts +2 -0
  43. package/dist/src/plugins/rules/lessThanOrEqual.d.ts +2 -0
  44. package/dist/src/plugins/rules/max.d.ts +2 -0
  45. package/dist/src/plugins/rules/min.d.ts +2 -0
  46. package/dist/src/plugins/rules/notIn.d.ts +2 -0
  47. package/dist/src/plugins/rules/nullable.d.ts +2 -0
  48. package/dist/src/plugins/rules/numeric.d.ts +2 -0
  49. package/dist/src/plugins/rules/regex.d.ts +2 -0
  50. package/dist/src/plugins/rules/required.d.ts +2 -0
  51. package/dist/src/plugins/rules/same.d.ts +2 -0
  52. package/dist/src/plugins/rules/size.d.ts +2 -0
  53. package/dist/src/plugins/rules/startsWith.d.ts +2 -0
  54. package/dist/src/plugins/rules/string.d.ts +2 -0
  55. package/dist/src/plugins/rules/url.d.ts +2 -0
  56. package/dist/src/plugins/rules/uuid.d.ts +2 -0
  57. package/dist/src/types/fields.d.ts +1 -0
  58. package/dist/src/types/locale.d.ts +7 -0
  59. package/dist/src/types/plugin.d.ts +2 -0
  60. package/dist/src/types/rules.d.ts +7 -1
  61. package/dist/src/types/validations.d.ts +3 -2
  62. package/dist/src/utils/rule.d.ts +2 -0
  63. package/package.json +17 -2
  64. package/dist/form-wrapper.iife.js +0 -1
  65. package/dist/src/utils/validations.d.ts +0 -3
package/README.md CHANGED
@@ -9,10 +9,10 @@
9
9
  <img src="https://img.shields.io/github/license/keysdown/form-wrapper.svg" alt="MIT"/>
10
10
  </p>
11
11
 
12
- > A package that allows you to easily manage forms, with Form Wrapper it is possible to perform validations with error messages, in addition to managing the state of the forms.
12
+ > A zero-dependency, framework-agnostic form state management library with a plugin-based validation system. Manage form fields, validate with customizable error messages, and extend with built-in validation rules, only importing what you need.
13
13
 
14
14
  <p align="center">
15
- <strong>Bundle size (minified + gzip):</strong> ~1.4 kB
15
+ <strong>Bundle size (minified + gzip):</strong> ~1.5 kB core / ~3 kB with all 33 rules
16
16
  </p>
17
17
 
18
18
  ## Installation
@@ -43,6 +43,211 @@ const form = ref(new FormWrapper({
43
43
  }))
44
44
  ```
45
45
 
46
+ ## Validation Plugins
47
+
48
+ Validation rules are loaded via a plugin system, no rules are bundled in core. This keeps the base library minimal and lets you import only what you need.
49
+
50
+ ### Loading all rules
51
+
52
+ Use the `formValidation` plugin to register all 33 validation rules at once:
53
+
54
+ ```js
55
+ import FormWrapper from '@keysdown/form-wrapper'
56
+ import formValidation from '@keysdown/form-wrapper/plugins/formValidation'
57
+
58
+ FormWrapper.extend(formValidation)
59
+ ```
60
+
61
+ ### Default error messages with locales
62
+
63
+ Validation rules have built-in default error messages. By default, messages are in **English**. You can change the language by loading a locale:
64
+
65
+ ```js
66
+ import FormWrapper from '@keysdown/form-wrapper'
67
+ import formValidation from '@keysdown/form-wrapper/plugins/formValidation'
68
+ import pt from '@keysdown/form-wrapper/plugins/locales/pt'
69
+
70
+ FormWrapper.extend(formValidation)
71
+ FormWrapper.locale(pt) // switch to Portuguese
72
+ ```
73
+
74
+ Available locales: `en` (English, default), `pt` (Portuguese), `es` (Spanish).
75
+
76
+ Default messages support **interpolation**, placeholders like `:field`, `:min`, `:max`, `:size`, `:digits`, `:other`, `:values` are replaced with actual values:
77
+
78
+ ```
79
+ The first name field is required.
80
+ The password must be at least 6.
81
+ ```
82
+
83
+ ### Custom field display names with `attribute`
84
+
85
+ By default, the `:field` placeholder uses the field name with underscores replaced by spaces (e.g., `first_name` → "first name"). You can customize this with the `attribute` property:
86
+
87
+ ```js
88
+ const form = new FormWrapper({
89
+ email: {
90
+ value: null,
91
+ validation: {
92
+ rules: ['required', 'email']
93
+ },
94
+ attribute: 'contact email'
95
+ }
96
+ })
97
+
98
+ // Default error message would show:
99
+ // "The contact email field is required."
100
+ // instead of "The email field is required."
101
+ ```
102
+
103
+ ```js
104
+ const form = new FormWrapper({
105
+ password: {
106
+ value: null,
107
+ validation: {
108
+ rules: ['required', 'min:8']
109
+ },
110
+ attribute: 'senha'
111
+ }
112
+ })
113
+
114
+ // With pt locale: "O campo senha deve ter no mínimo 8."
115
+ ```
116
+
117
+ User-provided messages always **override** default messages:
118
+
119
+ ```js
120
+ const form = new FormWrapper({
121
+ email: {
122
+ value: null,
123
+ validation: {
124
+ rules: ['required', 'email'],
125
+ messages: {
126
+ required: 'Email is required.' // overrides the default message
127
+ // email uses the default locale message
128
+ }
129
+ }
130
+ }
131
+ })
132
+ ```
133
+
134
+ ### Tree-shaking individual rules
135
+
136
+ Import only the rules you need and use them directly in the `rules` array for tree-shaking:
137
+
138
+ ```js
139
+ import FormWrapper from '@keysdown/form-wrapper'
140
+ import { required, email } from '@keysdown/form-wrapper/plugins/rules'
141
+
142
+ const form = new FormWrapper({
143
+ email: {
144
+ value: null,
145
+ validation: {
146
+ rules: [required, email],
147
+ messages: {
148
+ required: 'The email field is required.',
149
+ email: 'The email must be a valid address.'
150
+ }
151
+ }
152
+ }
153
+ })
154
+ ```
155
+
156
+ You can also mix function rules with string rules (useful for parameterized rules like `min:6`):
157
+
158
+ ```js
159
+ import { required, email } from '@keysdown/form-wrapper/plugins/rules'
160
+
161
+ rules: [required, email, 'min:6']
162
+ ```
163
+
164
+ ### Custom rules
165
+
166
+ Add inline validation rules directly in the `rules` array. A custom rule receives a destructured object `{value, fail, form, field}`:
167
+
168
+ ```js
169
+ const form = createForm({
170
+ email: {
171
+ value: null,
172
+ validation: {
173
+ rules: [({value, fail}) => {
174
+ if (!value) fail('The email field is required.')
175
+ }]
176
+ }
177
+ }
178
+ })
179
+ ```
180
+
181
+ Access other form fields via the `form` parameter:
182
+
183
+ ```js
184
+ const form = createForm({
185
+ password: { value: null, validation: {rules: []} },
186
+ password_confirmation: {
187
+ value: null,
188
+ validation: {
189
+ rules: [({value, fail, form}) => {
190
+ if (value !== form.password) fail('Passwords do not match.')
191
+ }]
192
+ }
193
+ }
194
+ })
195
+ ```
196
+
197
+ Custom rules support **async** validation (e.g., API calls):
198
+
199
+ ```js
200
+ const form = createForm({
201
+ email: {
202
+ value: null,
203
+ validation: {
204
+ rules: [async ({value, fail}) => {
205
+ const response = await fetch(`/api/check-email?email=${encodeURIComponent(value)}`)
206
+ const { available } = await response.json()
207
+ if (!available) fail('This email is already taken.')
208
+ }]
209
+ }
210
+ }
211
+ })
212
+ ```
213
+
214
+ You can mix custom rules with built-in rules:
215
+
216
+ ```js
217
+ import { required, email } from '@keysdown/form-wrapper/plugins/rules'
218
+
219
+ const form = createForm({
220
+ email: {
221
+ value: null,
222
+ validation: {
223
+ rules: [
224
+ required,
225
+ email,
226
+ async ({value, fail}) => {
227
+ const response = await fetch(`/api/check-email?email=${encodeURIComponent(value)}`)
228
+ const { available } = await response.json()
229
+ if (!available) fail('This email is already taken.')
230
+ }
231
+ ],
232
+ messages: {
233
+ required: 'The email field is required.',
234
+ email: 'The email must be a valid address.'
235
+ }
236
+ }
237
+ }
238
+ })
239
+ ```
240
+
241
+ ### Static methods on FormWrapper
242
+
243
+ | Method | Description |
244
+ |---|---|
245
+ | `FormWrapper.extend(plugin)` | Register a plugin (e.g., `formValidation`) |
246
+ | `FormWrapper.addRule(name, handler)` | Register a single validation rule |
247
+ | `FormWrapper.locale(locale)` | Set the default error messages locale |
248
+ | `FormWrapper.rules` | Static rule registry |
249
+ | `FormWrapper.defaultMessages` | Static default messages registry |
250
+
46
251
  ## Basic example
47
252
 
48
253
  Basic example using Vue.
@@ -103,29 +308,38 @@ Basic example with validation using Vue.
103
308
  <script setup lang="ts">
104
309
  import axios from 'axios'
105
310
  import {ref} from 'vue'
106
- import {createForm} from '@keysdown/form-wrapper'
311
+ import FormWrapper from '@keysdown/form-wrapper'
312
+ import formValidation from '@keysdown/form-wrapper/plugins/formValidation'
107
313
 
108
- const form = ref(createForm({
314
+ FormWrapper.extend(formValidation)
315
+
316
+ const form = ref(new FormWrapper({
109
317
  first_name: {
110
318
  value: null,
111
- rules: ['required'],
112
- messages: {
113
- required: 'The first name field is required.'
319
+ validation: {
320
+ rules: ['required'],
321
+ messages: {
322
+ required: 'The first name field is required.'
323
+ }
114
324
  }
115
325
  },
116
326
  last_name: {
117
327
  value: null,
118
- rules: ['required'],
119
- messages: {
120
- required: 'The last name field is required.'
328
+ validation: {
329
+ rules: ['required'],
330
+ messages: {
331
+ required: 'The last name field is required.'
332
+ }
121
333
  }
122
334
  },
123
335
  username: {
124
336
  value: null,
125
- rules: ['required', 'min:6'],
126
- messages: {
127
- required: 'The username field is required.',
128
- min: 'The username field must have at least 6 characters'
337
+ validation: {
338
+ rules: ['required', 'min:6'],
339
+ messages: {
340
+ required: 'The username field is required.',
341
+ min: 'The username field must have at least 6 characters'
342
+ }
129
343
  }
130
344
  }
131
345
  }))
@@ -154,7 +368,6 @@ Basic example with validation using Vue.
154
368
  </script>
155
369
  ```
156
370
 
157
-
158
371
  ## Form methods
159
372
 
160
373
  ### addField(field, value)
@@ -165,6 +378,7 @@ Method used to add a single field to the form.
165
378
  form.addField('username', null)
166
379
 
167
380
  form.addField('username', {
381
+ value: null,
168
382
  validation: {
169
383
  rules: ['required'],
170
384
  messages: {
@@ -186,6 +400,7 @@ form.addFields({
186
400
 
187
401
  form.addFields({
188
402
  full_name: {
403
+ value: null,
189
404
  validation: {
190
405
  rules: ['required'],
191
406
  messages: {
@@ -194,6 +409,7 @@ form.addFields({
194
409
  }
195
410
  },
196
411
  username: {
412
+ value: null,
197
413
  validation: {
198
414
  rules: ['required'],
199
415
  messages: {
@@ -338,9 +554,11 @@ Method used to validate the entire form or a specific field.
338
554
  const form = createForm({
339
555
  username: {
340
556
  value: null,
341
- rules: ['required'],
342
- messages: {
343
- required: 'The username field is required.'
557
+ validation: {
558
+ rules: ['required'],
559
+ messages: {
560
+ required: 'The username field is required.'
561
+ }
344
562
  }
345
563
  }
346
564
  })
@@ -372,9 +590,11 @@ Method used to validate a specific field.
372
590
  const form = createForm({
373
591
  username: {
374
592
  value: null,
375
- rules: ['required'],
376
- messages: {
377
- required: 'The username field is required.'
593
+ validation: {
594
+ rules: ['required'],
595
+ messages: {
596
+ required: 'The username field is required.'
597
+ }
378
598
  }
379
599
  }
380
600
  })
@@ -406,9 +626,11 @@ Method used to validate the entire form.
406
626
  const form = createForm({
407
627
  username: {
408
628
  value: null,
409
- rules: ['required'],
410
- messages: {
411
- required: 'The username field is required.'
629
+ validation: {
630
+ rules: ['required'],
631
+ messages: {
632
+ required: 'The username field is required.'
633
+ }
412
634
  }
413
635
  }
414
636
  })
@@ -463,6 +685,42 @@ form.first_name = 'keysdown'
463
685
  axios.post('some-api', form.values(['first_name']))
464
686
  ```
465
687
 
688
+ ### filledValues()
689
+
690
+ Method used to access form values, excluding fields with `null` or `undefined` values.
691
+
692
+ ```js
693
+ const form = createForm({
694
+ username: null,
695
+ email: null,
696
+ role: 'admin'
697
+ })
698
+
699
+ form.username = 'keysdown'
700
+
701
+ axios.post('some-api', form.filledValues())
702
+ // sends { username: 'keysdown', role: 'admin' }
703
+ ```
704
+
705
+ Note that `0`, `false`, and `''` (empty string) are considered filled values and will be included.
706
+
707
+ #### Filtering values
708
+
709
+ It is also possible to filter which fields are considered.
710
+
711
+ ```js
712
+ const form = createForm({
713
+ first_name: null,
714
+ last_name: null,
715
+ role: 'admin'
716
+ })
717
+
718
+ form.first_name = 'keysdown'
719
+
720
+ axios.post('some-api', form.filledValues(['first_name', 'last_name']))
721
+ // sends { first_name: 'keysdown' }
722
+ ```
723
+
466
724
  ### valuesAsFormData()
467
725
 
468
726
  Method used to access all form values as form data.
@@ -542,6 +800,16 @@ form.messages.first('username')
542
800
  form.rules.first('username')
543
801
  ```
544
802
 
803
+ #### get(key)
804
+
805
+ Returns the value for the given key, or `null` if not found.
806
+
807
+ ```js
808
+ form.errors.get('username')
809
+ form.messages.get('username')
810
+ form.rules.get('username')
811
+ ```
812
+
545
813
  #### any()
546
814
 
547
815
  Returns true if the collection has any items and false if it is empty.
@@ -585,7 +853,7 @@ form.messages.push('username', {
585
853
  required: 'The username field is required.'
586
854
  })
587
855
 
588
- form.rules.push('username', 'required')
856
+ form.rules.push('username', ['required'])
589
857
  ```
590
858
 
591
859
  #### has(key)
@@ -609,7 +877,7 @@ form.messages.unset('username')
609
877
  form.rules.unset('username')
610
878
  ```
611
879
 
612
- #### clear(key)
880
+ #### clear()
613
881
 
614
882
  Clears the entire collection, making it empty.
615
883
 
@@ -621,8 +889,44 @@ form.rules.clear()
621
889
 
622
890
  ### Validation rules
623
891
 
624
- There are some predefined validation rules that you can use in the form:
625
-
626
- #### required
627
-
628
- Used when a field is required
892
+ The `formValidation` plugin provides 33 validation rules. Rules are loaded via plugins, see [Validation Plugins](#validation-plugins) for setup instructions.
893
+
894
+ | Rule | Parameters | Description |
895
+ |---|---|---|
896
+ | `required` | | Field must be present and not empty |
897
+ | `email` | — | Must be a valid email address |
898
+ | `url` | — | Must be a valid URL |
899
+ | `min` | `min:6` | Minimum length (string) or value (number) or count (array) |
900
+ | `max` | `max:255` | Maximum length/value/count |
901
+ | `between` | `between:1,10` | Between min and max |
902
+ | `size` | `size:10` | Exact length/value/count |
903
+ | `alpha` | — | Only alphabetic characters |
904
+ | `alphaNumeric` | — | Only letters and numbers |
905
+ | `string` | — | Must be a string |
906
+ | `integer` | — | Must be an integer |
907
+ | `numeric` | — | Must be numeric |
908
+ | `array` | — | Must be an array |
909
+ | `boolean` | — | Must be boolean (true, false, 0, 1, '0', '1') |
910
+ | `date` | — | Must be a valid date |
911
+ | `same` | `same:field` | Must match another field |
912
+ | `different` | `different:field` | Must differ from another field |
913
+ | `confirmed` | `confirmed:field` | Must match field_confirmation |
914
+ | `in` | `in:admin,user` | Value must be in list |
915
+ | `notIn` | `notIn:admin,user` | Value must not be in list |
916
+ | `regex` | `regex:/pattern/` | Must match regex pattern |
917
+ | `startsWith` | `startsWith:foo,bar` | Must start with one of the values |
918
+ | `endsWith` | `endsWith:foo,bar` | Must end with one of the values |
919
+ | `digits` | `digits:4` | Must have exactly N digits |
920
+ | `digitsBetween` | `digitsBetween:3,6` | Must have between min and max digits |
921
+ | `ip` | — | Must be a valid IP (v4 or v6) |
922
+ | `json` | — | Must be a valid JSON string |
923
+ | `uuid` | — | Must be a valid UUID |
924
+ | `lessThan` | `lessThan:field` | Numeric value less than another field |
925
+ | `greaterThan` | `greaterThan:field` | Numeric value greater than another field |
926
+ | `lessThanOrEqual` | `lessThanOrEqual:field` | Less than or equal to another field |
927
+ | `greaterThanOrEqual` | `greaterThanOrEqual:field` | Greater than or equal to another field |
928
+ | `nullable` | — | Allows null/empty (always passes) |
929
+
930
+ ## License
931
+
932
+ [MIT](LICENSE)
@@ -1 +1 @@
1
- Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});var e=e=>typeof e==`string`?e.split(`|`):e,t=t=>({validation:{rules:t.validation?.rules?e(t.validation.rules):[],messages:t.validation?.messages??{}},value:t.value??null}),n=class{collection={};all(){return this.collection}first(e){let t=this.get(e);if(t){let e=Array.isArray(t)?t[0]:t;if(e)return String(e)}return null}any(){return Object.keys(this.collection).length>0}fill(e){return this.collection=e,this}push(e,t){return this.collection={...this.collection,[e]:t},this}has(e){return Array.isArray(e)?e.some(e=>this.collection.hasOwnProperty(e)):this.collection.hasOwnProperty(e)}get(e,t=null){return this.has(e)?this.collection[e]:t}unset(e){return this.has(e)&&delete this.collection[e],this}clear(){return this.collection={},this}},r=class extends n{},i=class extends n{},a=class extends n{push(e,t){let n=this.get(e)||[];return this.collection={...this.collection,[e]:[...n,t]},this}get(e){return super.get(e,[])}},o=class{errors=new a;messages=new r;rules=new i},s={required:e=>new Promise((t,n)=>{if(e==null)return n();String(e).replace(/\s/g,``).length>0?t(e):n()})},c=e=>typeof e==`object`&&!!e&&!Array.isArray(e),l=(e,t,n=null)=>{let r=t||new FormData;return Object.keys(e).forEach(t=>{let i=e[t];if(t=n?`${n}[${t}]`:t,!([void 0,null].indexOf(i)>-1)){if(c(i)&&!(i instanceof File)||Array.isArray(i)){l(i,r,t);return}r.append(t,i)}}),r},u=class{awaiting=!1;originalValues={};validation=new o;constructor(e){this.addFields(e)}addField(e,n){if(typeof n==`object`&&n&&Object.prototype.toString.call(n)===`[object Object]`&&`value`in n){let r=t(n);this[e]=r.value,this.originalValues[e]=r.value,this.validation.messages.push(e,r.validation.messages),this.validation.rules.push(e,r.validation.rules)}else this[e]=n,this.originalValues[e]=n;return this}addFields(e){return Object.keys(e).forEach(t=>{this.addField(t,e[t])}),this}get errors(){return this.validation.errors}fill(e,t=!1){return Object.keys(e).forEach(n=>{let r=e[n];t&&(this.originalValues[n]=r),this[n]=r}),this}get messages(){return this.validation.messages}removeField(e){return delete this[e],delete this.originalValues[e],this.validation.errors.unset(e),this.validation.messages.unset(e),this.validation.rules.unset(e),this}removeFields(e){return e.forEach(e=>{this.removeField(e)}),this}reset(){return this.validation.errors.clear(),Object.keys(this.originalValues).forEach(e=>{this[e]=this.originalValues[e]}),this}wasChanged(e){return Array.isArray(e)?e.some(e=>this[e]!==this.originalValues[e]):this[e]!==this.originalValues[e]}filled(e){return Array.isArray(e)?e.every(e=>this[e]!==null&&this[e]!==void 0&&this[e]!==``):this[e]!==null&&this[e]!==void 0&&this[e]!==``}get rules(){return this.validation.rules}setAwaiting(e=!0){return this.awaiting=e,this}validate(e=null){return e?this.validateField(e):this.validateForm()}validateField(e){this.validation.errors.unset(e);let t=this.validation.rules.get(e);if(t&&t.length>0){let n=t.map(t=>{let n=t.split(`:`),r=n[0],i=n.length===2?n[1].split(`,`):[];return r in s?s[r](this[e],i).catch(t=>{let n=this.validation.messages.get(e);return n&&r in n&&this.validation.errors.push(e,n[r]),Promise.reject(t)}):Promise.reject(Error(`There is no validation rule called "${r}"`))});return Promise.all(n).then(()=>{})}return Promise.resolve()}validateForm(){let e=Object.keys(this.originalValues).map(e=>this.validateField(e));return Promise.all(e).then(()=>Promise.resolve(this)).catch(()=>Promise.reject(this))}values(e){let t={};return Object.keys(this.originalValues).forEach(n=>{(!e||e.includes(n))&&(t[n]=this[n])}),t}valuesAsFormData(e){return l(this.values(e))}},d=e=>new u(e),f=u;exports.createForm=d,exports.default=f;
1
+ Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});var e=e=>{if(typeof e==`string`){let t=[],n=e;for(;n.length>0;){let e=n.indexOf(`regex:`);if(e!==-1){let r=n.substring(0,e);r&&t.push(...r.split(`|`).filter(Boolean));let i=n.substring(e),a=i.indexOf(`:/`);if(a!==-1){let e=a+1,r=i.lastIndexOf(`/`);if(r>e){let e=i.substring(r+1),a=e.match(/^[gimsuy]*/)[0],o=i.substring(0,r+1+a.length);t.push(o),n=e.substring(a.length),n.startsWith(`|`)&&(n=n.substring(1));continue}}t.push(i);break}else{t.push(...n.split(`|`).filter(Boolean));break}}return t}return e},t=t=>({validation:{rules:t.validation?.rules?e(t.validation.rules):[],messages:t.validation?.messages??{}},value:t.value??null}),n=class{collection={};all(){return this.collection}first(e){let t=this.get(e);if(t!==null){let e=Array.isArray(t)?t[0]:t;if(e!=null)return String(e)}return null}any(){return Object.keys(this.collection).length>0}fill(e){return this.collection=e,this}push(e,t){return this.collection={...this.collection,[e]:t},this}has(e){return Array.isArray(e)?e.some(e=>this.collection.hasOwnProperty(e)):this.collection.hasOwnProperty(e)}get(e,t=null){return this.has(e)?this.collection[e]:t}unset(e){return this.has(e)&&delete this.collection[e],this}clear(){return this.collection={},this}},r=class extends n{},i=class extends n{},a=class extends n{push(e,t){let n=this.get(e)||[];return this.collection={...this.collection,[e]:[...n,t]},this}get(e){return super.get(e,[])}},o=class{errors=new a;messages=new r;rules=new i},s=e=>typeof e==`object`&&!!e&&!Array.isArray(e),c=(e,t,n=null)=>{let r=t||new FormData;return Object.keys(e).forEach(t=>{let i=e[t];if(t=n?`${n}[${t}]`:t,!([void 0,null].indexOf(i)>-1)){if(s(i)&&!(i instanceof File)||Array.isArray(i)){c(i,r,t);return}r.append(t,i)}}),r},l=class e{static rules={};static defaultMessages={};static interpolateMessage(e,t,n,r){let i=r[t]||t.replace(/_/g,` `);return e.replace(/:field/g,i).replace(/:min/g,n[0]||``).replace(/:max/g,n[1]||``).replace(/:size/g,n[0]||``).replace(/:digits/g,n[0]||``).replace(/:other/g,n[0]?r[n[0]]||n[0].replace(/_/g,` `):``).replace(/:values/g,n.join(`, `))}awaiting=!1;originalValues={};fieldAttributes={};validation=new o;constructor(e){this.addFields(e)}static extend(t){return t(e,e.rules),e}static addRule(t,n){return e.rules[t]=Object.assign(n,{ruleName:t}),e}static locale(t){return e.defaultMessages=t.messages,e}addField(e,n){if(typeof n==`object`&&n&&Object.prototype.toString.call(n)===`[object Object]`&&`value`in n){let r=t(n);this[e]=r.value,this.originalValues[e]=r.value,this.validation.messages.push(e,r.validation.messages),this.validation.rules.push(e,r.validation.rules),n.attribute&&(this.fieldAttributes[e]=n.attribute)}else this[e]=n,this.originalValues[e]=n;return this}addFields(e){return Object.keys(e).forEach(t=>{this.addField(t,e[t])}),this}get errors(){return this.validation.errors}fill(e,t=!1){return Object.keys(e).forEach(n=>{let r=e[n];t&&(this.originalValues[n]=r),this[n]=r,n in this.originalValues||(this.originalValues[n]=r)}),this}get messages(){return this.validation.messages}removeField(e){return delete this[e],delete this.originalValues[e],delete this.fieldAttributes[e],this.validation.errors.unset(e),this.validation.messages.unset(e),this.validation.rules.unset(e),this}removeFields(e){return e.forEach(e=>{this.removeField(e)}),this}reset(){return this.validation.errors.clear(),Object.keys(this.originalValues).forEach(e=>{this[e]=this.originalValues[e]}),this}wasChanged(e){return Array.isArray(e)?e.some(e=>this[e]!==this.originalValues[e]):this[e]!==this.originalValues[e]}filled(e){return Array.isArray(e)?e.every(e=>this[e]!==null&&this[e]!==void 0&&this[e]!==``):this[e]!==null&&this[e]!==void 0&&this[e]!==``}get rules(){return this.validation.rules}setAwaiting(e=!0){return this.awaiting=e,this}validate(e=null){return e?this.validateField(e):this.validateForm()}validateField(t){this.validation.errors.unset(t);let n=this.validation.rules.get(t);if(n&&n.length>0){let r=this.constructor.rules;return(async()=>{for(let i of n){let n=i;if(typeof n==`function`){if(`ruleName`in n){let r=n.ruleName;if(r===`nullable`&&(this[t]===null||this[t]===void 0||this[t]===``))return;try{await n(this[t],[],this,t)}catch(n){let i=this.validation.messages.get(t);throw r&&i&&r in i?this.validation.errors.push(t,i[r]):r in e.defaultMessages&&this.validation.errors.push(t,e.interpolateMessage(e.defaultMessages[r],t,[],this.fieldAttributes)),n}}else await new Promise((e,r)=>{let i=!1,a=e=>{i=!0,e&&this.validation.errors.push(t,e)};try{let o=n({value:this[t],fail:a,form:this,field:t});o instanceof Promise?o.then(()=>{i?r():e()}).catch(()=>{r()}):i?r():e()}catch{r()}});continue}let a=n.indexOf(`:`),o=a===-1?n:n.substring(0,a);if(o===`nullable`&&(this[t]===null||this[t]===void 0||this[t]===``))return;let s=a===-1?[]:n.substring(a+1).split(`,`);if(o in r)try{await r[o](this[t],s,this,t)}catch(n){let r=this.validation.messages.get(t);throw r&&o in r?this.validation.errors.push(t,r[o]):o in e.defaultMessages&&this.validation.errors.push(t,e.interpolateMessage(e.defaultMessages[o],t,s,this.fieldAttributes)),n}else throw Error(`There is no validation rule called "${o}"`)}})()}return Promise.resolve()}validateForm(){let e=Object.keys(this.originalValues).map(e=>this.validateField(e));return Promise.all(e).then(()=>Promise.resolve(this)).catch(()=>Promise.reject(this))}values(e){let t={};return Object.keys(this.originalValues).forEach(n=>{(!e||e.includes(n))&&(t[n]=this[n])}),t}filledValues(e){let t={};return Object.keys(this.originalValues).forEach(n=>{(!e||e.includes(n))&&this[n]!=null&&(t[n]=this[n])}),t}valuesAsFormData(e){return c(this.values(e))}},u=e=>new l(e),d=l;exports.createForm=u,exports.default=d;