@keysdown/form-wrapper 1.0.2 → 2.0.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 (66) hide show
  1. package/README.md +342 -31
  2. package/dist/form-wrapper.cjs +1 -1
  3. package/dist/form-wrapper.js +115 -29
  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 +12 -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/collections.d.ts +1 -1
  63. package/dist/src/utils/rule.d.ts +2 -0
  64. package/package.json +17 -2
  65. package/dist/form-wrapper.iife.js +0 -1
  66. package/dist/src/utils/validations.d.ts +0 -3
package/README.md CHANGED
@@ -9,7 +9,11 @@
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
+
14
+ <p align="center">
15
+ <strong>Bundle size (minified + gzip):</strong> ~1.5 kB core / ~3 kB with all 33 rules
16
+ </p>
13
17
 
14
18
  ## Installation
15
19
 
@@ -39,6 +43,211 @@ const form = ref(new FormWrapper({
39
43
  }))
40
44
  ```
41
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
+
42
251
  ## Basic example
43
252
 
44
253
  Basic example using Vue.
@@ -99,29 +308,38 @@ Basic example with validation using Vue.
99
308
  <script setup lang="ts">
100
309
  import axios from 'axios'
101
310
  import {ref} from 'vue'
102
- import {createForm} from '@keysdown/form-wrapper'
311
+ import FormWrapper from '@keysdown/form-wrapper'
312
+ import formValidation from '@keysdown/form-wrapper/plugins/formValidation'
103
313
 
104
- const form = ref(createForm({
314
+ FormWrapper.extend(formValidation)
315
+
316
+ const form = ref(new FormWrapper({
105
317
  first_name: {
106
318
  value: null,
107
- rules: ['required'],
108
- messages: {
109
- required: 'The first name field is required.'
319
+ validation: {
320
+ rules: ['required'],
321
+ messages: {
322
+ required: 'The first name field is required.'
323
+ }
110
324
  }
111
325
  },
112
326
  last_name: {
113
327
  value: null,
114
- rules: ['required'],
115
- messages: {
116
- required: 'The last name field is required.'
328
+ validation: {
329
+ rules: ['required'],
330
+ messages: {
331
+ required: 'The last name field is required.'
332
+ }
117
333
  }
118
334
  },
119
335
  username: {
120
336
  value: null,
121
- rules: ['required', 'min:6'],
122
- messages: {
123
- required: 'The username field is required.',
124
- 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
+ }
125
343
  }
126
344
  }
127
345
  }))
@@ -150,7 +368,6 @@ Basic example with validation using Vue.
150
368
  </script>
151
369
  ```
152
370
 
153
-
154
371
  ## Form methods
155
372
 
156
373
  ### addField(field, value)
@@ -161,6 +378,7 @@ Method used to add a single field to the form.
161
378
  form.addField('username', null)
162
379
 
163
380
  form.addField('username', {
381
+ value: null,
164
382
  validation: {
165
383
  rules: ['required'],
166
384
  messages: {
@@ -182,6 +400,7 @@ form.addFields({
182
400
 
183
401
  form.addFields({
184
402
  full_name: {
403
+ value: null,
185
404
  validation: {
186
405
  rules: ['required'],
187
406
  messages: {
@@ -190,6 +409,7 @@ form.addFields({
190
409
  }
191
410
  },
192
411
  username: {
412
+ value: null,
193
413
  validation: {
194
414
  rules: ['required'],
195
415
  messages: {
@@ -268,6 +488,44 @@ form.reset()
268
488
  console.log(form.values()) // {username: null}
269
489
  ```
270
490
 
491
+ ### wasChanged(field)
492
+
493
+ Method used to check if one or more fields have been changed from their original values. When an array is provided, returns `true` if **any** of the fields were changed.
494
+
495
+ ```js
496
+ const form = createForm({
497
+ name: null,
498
+ username: null
499
+ })
500
+
501
+ console.log(form.wasChanged('username')) // false
502
+
503
+ form.username = 'keysdown'
504
+
505
+ console.log(form.wasChanged('username')) // true
506
+
507
+ console.log(form.wasChanged(['name', 'username'])) // true (any changed)
508
+ ```
509
+
510
+ ### filled(field)
511
+
512
+ Method used to check if one or more fields are filled (not `null`, not `undefined`, not empty string `''`). When an array is provided, returns `true` only if **all** fields are filled.
513
+
514
+ ```js
515
+ const form = createForm({
516
+ name: null,
517
+ username: null
518
+ })
519
+
520
+ form.username = 'keysdown'
521
+
522
+ console.log(form.filled('username')) // true
523
+
524
+ console.log(form.filled('name')) // false
525
+
526
+ console.log(form.filled(['name', 'username'])) // false (name is not filled)
527
+ ```
528
+
271
529
  ### setAwaiting(awaiting = true)
272
530
 
273
531
  Method used to change the form loading state.
@@ -296,9 +554,11 @@ Method used to validate the entire form or a specific field.
296
554
  const form = createForm({
297
555
  username: {
298
556
  value: null,
299
- rules: ['required'],
300
- messages: {
301
- required: 'The username field is required.'
557
+ validation: {
558
+ rules: ['required'],
559
+ messages: {
560
+ required: 'The username field is required.'
561
+ }
302
562
  }
303
563
  }
304
564
  })
@@ -330,9 +590,11 @@ Method used to validate a specific field.
330
590
  const form = createForm({
331
591
  username: {
332
592
  value: null,
333
- rules: ['required'],
334
- messages: {
335
- required: 'The username field is required.'
593
+ validation: {
594
+ rules: ['required'],
595
+ messages: {
596
+ required: 'The username field is required.'
597
+ }
336
598
  }
337
599
  }
338
600
  })
@@ -364,9 +626,11 @@ Method used to validate the entire form.
364
626
  const form = createForm({
365
627
  username: {
366
628
  value: null,
367
- rules: ['required'],
368
- messages: {
369
- required: 'The username field is required.'
629
+ validation: {
630
+ rules: ['required'],
631
+ messages: {
632
+ required: 'The username field is required.'
633
+ }
370
634
  }
371
635
  }
372
636
  })
@@ -500,6 +764,16 @@ form.messages.first('username')
500
764
  form.rules.first('username')
501
765
  ```
502
766
 
767
+ #### get(key)
768
+
769
+ Returns the value for the given key, or `null` if not found.
770
+
771
+ ```js
772
+ form.errors.get('username')
773
+ form.messages.get('username')
774
+ form.rules.get('username')
775
+ ```
776
+
503
777
  #### any()
504
778
 
505
779
  Returns true if the collection has any items and false if it is empty.
@@ -543,15 +817,16 @@ form.messages.push('username', {
543
817
  required: 'The username field is required.'
544
818
  })
545
819
 
546
- form.rules.push('username', 'required')
820
+ form.rules.push('username', ['required'])
547
821
  ```
548
822
 
549
823
  #### has(key)
550
824
 
551
- Checking if the collection has an item with the key.
825
+ Checking if the collection has an item with the key. Accepts a single key or an array of keys. When an array is provided, returns `true` if **any** of the keys exist.
552
826
 
553
827
  ```js
554
828
  form.errors.has('username')
829
+ form.errors.has(['username', 'email'])
555
830
  form.messages.has('username')
556
831
  form.rules.has('username')
557
832
  ```
@@ -566,7 +841,7 @@ form.messages.unset('username')
566
841
  form.rules.unset('username')
567
842
  ```
568
843
 
569
- #### clear(key)
844
+ #### clear()
570
845
 
571
846
  Clears the entire collection, making it empty.
572
847
 
@@ -578,8 +853,44 @@ form.rules.clear()
578
853
 
579
854
  ### Validation rules
580
855
 
581
- There are some predefined validation rules that you can use in the form:
582
-
583
- #### required
584
-
585
- Used when a field is required
856
+ The `formValidation` plugin provides 33 validation rules. Rules are loaded via plugins, see [Validation Plugins](#validation-plugins) for setup instructions.
857
+
858
+ | Rule | Parameters | Description |
859
+ |---|---|---|
860
+ | `required` | | Field must be present and not empty |
861
+ | `email` | — | Must be a valid email address |
862
+ | `url` | — | Must be a valid URL |
863
+ | `min` | `min:6` | Minimum length (string) or value (number) or count (array) |
864
+ | `max` | `max:255` | Maximum length/value/count |
865
+ | `between` | `between:1,10` | Between min and max |
866
+ | `size` | `size:10` | Exact length/value/count |
867
+ | `alpha` | — | Only alphabetic characters |
868
+ | `alphaNumeric` | — | Only letters and numbers |
869
+ | `string` | — | Must be a string |
870
+ | `integer` | — | Must be an integer |
871
+ | `numeric` | — | Must be numeric |
872
+ | `array` | — | Must be an array |
873
+ | `boolean` | — | Must be boolean (true, false, 0, 1, '0', '1') |
874
+ | `date` | — | Must be a valid date |
875
+ | `same` | `same:field` | Must match another field |
876
+ | `different` | `different:field` | Must differ from another field |
877
+ | `confirmed` | `confirmed:field` | Must match field_confirmation |
878
+ | `in` | `in:admin,user` | Value must be in list |
879
+ | `notIn` | `notIn:admin,user` | Value must not be in list |
880
+ | `regex` | `regex:/pattern/` | Must match regex pattern |
881
+ | `startsWith` | `startsWith:foo,bar` | Must start with one of the values |
882
+ | `endsWith` | `endsWith:foo,bar` | Must end with one of the values |
883
+ | `digits` | `digits:4` | Must have exactly N digits |
884
+ | `digitsBetween` | `digitsBetween:3,6` | Must have between min and max digits |
885
+ | `ip` | — | Must be a valid IP (v4 or v6) |
886
+ | `json` | — | Must be a valid JSON string |
887
+ | `uuid` | — | Must be a valid UUID |
888
+ | `lessThan` | `lessThan:field` | Numeric value less than another field |
889
+ | `greaterThan` | `greaterThan:field` | Numeric value greater than another field |
890
+ | `lessThanOrEqual` | `lessThanOrEqual:field` | Less than or equal to another field |
891
+ | `greaterThanOrEqual` | `greaterThanOrEqual:field` | Greater than or equal to another field |
892
+ | `nullable` | — | Allows null/empty (always passes) |
893
+
894
+ ## License
895
+
896
+ [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 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}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}valuesAsFormData(e){return c(this.values(e))}},u=e=>new l(e),d=l;exports.createForm=u,exports.default=d;