@codeleap/form 7.0.0 → 7.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 (51) hide show
  1. package/dist/fields/bool.js +15 -0
  2. package/dist/fields/bool.js.map +1 -0
  3. package/dist/fields/date.js +56 -0
  4. package/dist/fields/date.js.map +1 -0
  5. package/dist/fields/file.js +16 -0
  6. package/dist/fields/file.js.map +1 -0
  7. package/dist/fields/group.js +16 -0
  8. package/dist/fields/group.js.map +1 -0
  9. package/dist/fields/index.js +33 -0
  10. package/dist/fields/index.js.map +1 -0
  11. package/dist/fields/list.js +50 -0
  12. package/dist/fields/list.js.map +1 -0
  13. package/dist/fields/number.js +35 -0
  14. package/dist/fields/number.js.map +1 -0
  15. package/dist/fields/selectable.js +22 -0
  16. package/dist/fields/selectable.js.map +1 -0
  17. package/dist/fields/text.js +16 -0
  18. package/dist/fields/text.js.map +1 -0
  19. package/dist/hooks/index.js +3 -0
  20. package/dist/hooks/index.js.map +1 -0
  21. package/dist/hooks/useField.js +24 -0
  22. package/dist/hooks/useField.js.map +1 -0
  23. package/dist/hooks/useValidate.js +54 -0
  24. package/dist/hooks/useValidate.js.map +1 -0
  25. package/dist/index.js +6 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/lib/Field.js +346 -0
  28. package/dist/lib/Field.js.map +1 -0
  29. package/dist/lib/Form.js +197 -0
  30. package/dist/lib/Form.js.map +1 -0
  31. package/dist/lib/factories.js +15 -0
  32. package/dist/lib/factories.js.map +1 -0
  33. package/dist/lib/index.js +3 -0
  34. package/dist/lib/index.js.map +1 -0
  35. package/dist/lib/useFieldBinding.js +48 -0
  36. package/dist/lib/useFieldBinding.js.map +1 -0
  37. package/dist/types/field.js +2 -0
  38. package/dist/types/field.js.map +1 -0
  39. package/dist/types/form.js +2 -0
  40. package/dist/types/form.js.map +1 -0
  41. package/dist/types/globals.js +2 -0
  42. package/dist/types/globals.js.map +1 -0
  43. package/dist/types/index.js +2 -0
  44. package/dist/types/index.js.map +1 -0
  45. package/dist/types/validation.js +2 -0
  46. package/dist/types/validation.js.map +1 -0
  47. package/dist/validators/index.js +2 -0
  48. package/dist/validators/index.js.map +1 -0
  49. package/dist/validators/zod.js +43 -0
  50. package/dist/validators/zod.js.map +1 -0
  51. package/package.json +10 -10
@@ -0,0 +1,15 @@
1
+ import { Field } from "../lib/Field";
2
+ import { z } from 'zod';
3
+ import { zodValidator } from "../validators";
4
+ /**
5
+ * Field for boolean values (checkboxes, toggles). `Form.setValues` treats
6
+ * this field specially: it will only set a value when the incoming value is
7
+ * strictly `boolean`, because falsy coercion would prevent setting `false`.
8
+ */
9
+ export class BooleanField extends Field {
10
+ constructor(options) {
11
+ super(Object.assign({ validate: zodValidator(z.boolean()) }, options));
12
+ this._type = "BOOLEAN";
13
+ }
14
+ }
15
+ //# sourceMappingURL=bool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bool.js","sourceRoot":"","sources":["../../src/fields/bool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,cAAc,CAAA;AACpC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAS5C;;;;GAIG;AACH,MAAM,OAAO,YAAgD,SAAQ,KAAwB;IAG3F,YAAY,OAAsC;QAChD,KAAK,iBACH,QAAQ,EAAE,YAAY,CAAC,CAAC,CAAC,OAAO,EAAE,CAAwB,IACvD,OAAO,EACV,CAAA;QANJ,UAAK,GAAG,SAAS,CAAA;IAOjB,CAAC;CACF"}
@@ -0,0 +1,56 @@
1
+ var __rest = (this && this.__rest) || function (s, e) {
2
+ var t = {};
3
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
4
+ t[p] = s[p];
5
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
6
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
7
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
8
+ t[p[i]] = s[p[i]];
9
+ }
10
+ return t;
11
+ };
12
+ import { Field } from "../lib/Field";
13
+ import { z } from 'zod';
14
+ import { zodValidator } from "../validators";
15
+ import dayjs from 'dayjs';
16
+ function normalizeToDate(val) {
17
+ if (!val)
18
+ return undefined;
19
+ if (val instanceof Date)
20
+ return val;
21
+ if (dayjs.isDayjs(val))
22
+ return val.startOf('day').toDate();
23
+ if (typeof val === 'string') {
24
+ const parsed = dayjs(val);
25
+ return parsed.isValid() ? parsed.startOf('day').toDate() : undefined;
26
+ }
27
+ return undefined;
28
+ }
29
+ /**
30
+ * Field for `Date` values with optional `minimumDate` / `maximumDate` bounds.
31
+ * The built-in validator normalises the raw input via `dayjs` before running
32
+ * boundary checks, so it transparently accepts `Date` objects, dayjs instances,
33
+ * and ISO date strings. Invalid or unparseable values are normalised to
34
+ * `undefined`, which fails the `z.date()` check. Bounds are enforced at the
35
+ * start of the day (`startOf('day')`) for dayjs inputs.
36
+ */
37
+ export class DateField extends Field {
38
+ constructor(options) {
39
+ const { minimumDate, maximumDate } = options, rest = __rest(options, ["minimumDate", "maximumDate"]);
40
+ const schema = z.preprocess((val) => {
41
+ const normalized = normalizeToDate(val);
42
+ if (!normalized || isNaN(normalized.getTime()))
43
+ return undefined;
44
+ return normalized;
45
+ }, z.date()
46
+ .refine(d => !minimumDate || d >= minimumDate, {
47
+ message: `Date must be on or after ${minimumDate === null || minimumDate === void 0 ? void 0 : minimumDate.toDateString()}`,
48
+ })
49
+ .refine(d => !maximumDate || d <= maximumDate, {
50
+ message: `Date must be on or before ${maximumDate === null || maximumDate === void 0 ? void 0 : maximumDate.toDateString()}`,
51
+ }));
52
+ super(Object.assign({ validate: zodValidator(schema) }, options));
53
+ this._type = "DATE";
54
+ }
55
+ }
56
+ //# sourceMappingURL=date.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"date.js","sourceRoot":"","sources":["../../src/fields/date.ts"],"names":[],"mappings":";;;;;;;;;;;AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,cAAc,CAAA;AACpC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAC5C,OAAO,KAAK,MAAM,OAAO,CAAA;AAUzB,SAAS,eAAe,CAAC,GAAY;IACnC,IAAI,CAAC,GAAG;QAAE,OAAO,SAAS,CAAA;IAE1B,IAAI,GAAG,YAAY,IAAI;QAAE,OAAO,GAAG,CAAA;IACnC,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC;QAAE,OAAO,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,MAAM,EAAE,CAAA;IAC1D,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;QAC5B,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,CAAA;QACzB,OAAO,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,SAAS,CAAA;IACtE,CAAC;IAED,OAAO,SAAS,CAAA;AAClB,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,OAAO,SAA0C,SAAQ,KAAqB;IAGlF,YAAY,OAAmC;QAC7C,MAAM,EAAE,WAAW,EAAE,WAAW,KAAc,OAAO,EAAhB,IAAI,UAAK,OAAO,EAA/C,8BAAqC,CAAU,CAAA;QAErD,MAAM,MAAM,GAAG,CAAC,CAAC,UAAU,CAAC,CAAC,GAAY,EAAE,EAAE;YAC3C,MAAM,UAAU,GAAG,eAAe,CAAC,GAAG,CAAC,CAAA;YAEvC,IAAI,CAAC,UAAU,IAAI,KAAK,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC;gBAAE,OAAO,SAAS,CAAA;YAChE,OAAO,UAAU,CAAA;QACnB,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE;aACR,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,WAAW,IAAI,CAAC,IAAI,WAAW,EAAE;YAC7C,OAAO,EAAE,4BAA4B,WAAW,aAAX,WAAW,uBAAX,WAAW,CAAE,YAAY,EAAE,EAAE;SACnE,CAAC;aACD,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,WAAW,IAAI,CAAC,IAAI,WAAW,EAAE;YAC7C,OAAO,EAAE,6BAA6B,WAAW,aAAX,WAAW,uBAAX,WAAW,CAAE,YAAY,EAAE,EAAE;SACpE,CAAC,CACH,CAAA;QAED,KAAK,iBACH,QAAQ,EAAE,YAAY,CAAC,MAAM,CAAwB,IAClD,OAAO,EACV,CAAA;QAtBJ,UAAK,GAAG,MAAM,CAAA;IAuBd,CAAC;CACF"}
@@ -0,0 +1,16 @@
1
+ import { Field } from "../lib/Field";
2
+ import { z } from 'zod';
3
+ import { zodValidator } from "../validators";
4
+ /**
5
+ * Field for file uploads. The value type is `any` because the shape of a
6
+ * "file" differs between web (`File` / `FileList`) and React Native (asset
7
+ * descriptor objects). Supply a custom `validate` function to enforce whatever
8
+ * shape your platform produces.
9
+ */
10
+ export class FileField extends Field {
11
+ constructor(options) {
12
+ super(Object.assign({ validate: zodValidator(z.any()) }, options));
13
+ this._type = "FILE";
14
+ }
15
+ }
16
+ //# sourceMappingURL=file.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file.js","sourceRoot":"","sources":["../../src/fields/file.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,cAAc,CAAA;AACpC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAO5C;;;;;GAKG;AACH,MAAM,OAAO,SAA0C,SAAQ,KAAoB;IAGjF,YAAY,OAAmC;QAC7C,KAAK,iBACH,QAAQ,EAAE,YAAY,CAAC,CAAC,CAAC,GAAG,EAAE,CAAwB,IACnD,OAAO,EACV,CAAA;QANJ,UAAK,GAAG,MAAM,CAAA;IAOd,CAAC;CACF"}
@@ -0,0 +1,16 @@
1
+ import { Field } from "../lib/Field";
2
+ import { z } from 'zod';
3
+ import { zodValidator } from "../validators";
4
+ /**
5
+ * Field that represents a selected group or category, stored as a string key.
6
+ * Structurally identical to `TextField` but carries the `"group"` type tag so
7
+ * platform renderers can distinguish it and render a group-picker control
8
+ * instead of a free-text input.
9
+ */
10
+ export class GroupField extends Field {
11
+ constructor(options) {
12
+ super(Object.assign({ validate: zodValidator(z.string()) }, options));
13
+ this._type = "group";
14
+ }
15
+ }
16
+ //# sourceMappingURL=group.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"group.js","sourceRoot":"","sources":["../../src/fields/group.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,cAAc,CAAA;AACpC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAQ5C;;;;;GAKG;AACH,MAAM,OAAO,UAA4C,SAAQ,KAAuB;IAGtF,YAAY,OAAoC;QAC9C,KAAK,iBACH,QAAQ,EAAE,YAAY,CAAC,CAAC,CAAC,MAAM,EAAE,CAAwB,IACtD,OAAO,EACV,CAAA;QANJ,UAAK,GAAG,OAAO,CAAA;IAOf,CAAC;CACF"}
@@ -0,0 +1,33 @@
1
+ import { fieldFactory } from "../lib/factories";
2
+ import { TextField } from "./text";
3
+ import { ListField, listFieldFactory } from "./list";
4
+ import { NumberField } from "./number";
5
+ import { BooleanField } from "./bool";
6
+ import { SelectableField } from "./selectable";
7
+ import { DateField } from "./date";
8
+ import { FileField } from "./file";
9
+ /**
10
+ * Convenience namespace that exposes factory functions for every built-in field
11
+ * type. Prefer these over calling constructors directly — they provide better
12
+ * generic inference for the `validate` option.
13
+ *
14
+ * ```ts
15
+ * const emailField = fields.text({ validate: zodValidator(z.string().email()) })
16
+ * const tagsField = fields.list({ item: fields.text(), defaultValue: [] })
17
+ * ```
18
+ *
19
+ * Note: `fields.list` is the only entry that is not wrapped with
20
+ * `fieldFactory`, because `ListField` requires the `item` option's type to be
21
+ * inferred at the call site.
22
+ */
23
+ export const fields = {
24
+ text: fieldFactory(TextField),
25
+ list: listFieldFactory,
26
+ number: fieldFactory(NumberField),
27
+ boolean: fieldFactory(BooleanField),
28
+ selectable: fieldFactory(SelectableField),
29
+ date: fieldFactory(DateField),
30
+ file: fieldFactory(FileField),
31
+ };
32
+ export { TextField, ListField, NumberField, BooleanField, SelectableField, DateField, FileField, };
33
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/fields/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAC/C,OAAO,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAA;AAClC,OAAO,EAAE,SAAS,EAAG,gBAAgB,EAAC,MAAM,QAAQ,CAAA;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AACtC,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAA;AACrC,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAA;AAClC,OAAO,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAA;AAGlC;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,IAAI,EAAE,YAAY,CAAC,SAAS,CAAC;IAC7B,IAAI,EAAE,gBAAgB;IACtB,MAAM,EAAE,YAAY,CAAC,WAAW,CAAC;IACjC,OAAO,EAAE,YAAY,CAAC,YAAY,CAAC;IACnC,UAAU,EAAE,YAAY,CAAC,eAAe,CAAC;IACzC,IAAI,EAAE,YAAY,CAAC,SAAS,CAAC;IAC7B,IAAI,EAAE,YAAY,CAAC,SAAS,CAAC;CAC9B,CAAA;AAED,OAAO,EACL,SAAS,EACT,SAAS,EACT,WAAW,EACX,YAAY,EACZ,eAAe,EACf,SAAS,EACT,SAAS,GACV,CAAA"}
@@ -0,0 +1,50 @@
1
+ import { Field } from "../lib/Field";
2
+ /**
3
+ * Field that holds an array of values, each validated by a delegate `item`
4
+ * field. The built-in validator iterates every element through
5
+ * `options.item.validate` and collects all failures into an error array. The
6
+ * list is considered valid only when every element passes. If `item` has no
7
+ * validator, the list is always valid.
8
+ *
9
+ * The `item` field acts as a prototype — it is not mounted independently and
10
+ * does not own its own atom. Do not call `use()` on it directly.
11
+ */
12
+ export class ListField extends Field {
13
+ constructor(options) {
14
+ super(Object.assign(Object.assign({}, options), { validate: ((v, form) => {
15
+ var _a, _b, _c;
16
+ if (!((_a = options.item) === null || _a === void 0 ? void 0 : _a.validate))
17
+ return {
18
+ isValid: true
19
+ };
20
+ const errors = [];
21
+ for (const value of v) {
22
+ const validation = (_c = (_b = options.item) === null || _b === void 0 ? void 0 : _b.validate) === null || _c === void 0 ? void 0 : _c.call(_b, value);
23
+ if (!validation.isValid) {
24
+ errors.push(validation);
25
+ }
26
+ }
27
+ if (errors.length) {
28
+ return {
29
+ isValid: false,
30
+ error: errors,
31
+ };
32
+ }
33
+ return {
34
+ isValid: true,
35
+ };
36
+ }) }));
37
+ this._type = "LIST";
38
+ }
39
+ }
40
+ /**
41
+ * Convenience factory for `ListField`. Unlike `fieldFactory`-based helpers,
42
+ * `ListField` requires an `item` option whose generic type must be inferred at
43
+ * the call site, so it cannot share the same factory wrapper. This function
44
+ * exists to provide a consistent `fields.list(...)` call signature alongside
45
+ * the other entries in the `fields` namespace.
46
+ */
47
+ export function listFieldFactory(options) {
48
+ return new ListField(options);
49
+ }
50
+ //# sourceMappingURL=list.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list.js","sourceRoot":"","sources":["../../src/fields/list.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,cAAc,CAAA;AA6BpC;;;;;;;;;GASG;AACH,MAAM,OAAO,SAAqC,SAAQ,KAAiD;IAGzG,YAAY,OAA4B;QACtC,KAAK,CAAC,gCACD,OAAO,KACV,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE;;gBACrB,IAAI,CAAC,CAAA,MAAA,OAAO,CAAC,IAAI,0CAAE,QAAQ,CAAA;oBAAE,OAAO;wBAClC,OAAO,EAAE,IAAI;qBACd,CAAA;gBAED,MAAM,MAAM,GAAG,EAAE,CAAA;gBAEjB,KAAK,MAAM,KAAK,IAAI,CAAC,EAAE,CAAC;oBACtB,MAAM,UAAU,GAAG,MAAA,MAAA,OAAO,CAAC,IAAI,0CAAE,QAAQ,mDAAG,KAAK,CAAC,CAAA;oBAElD,IAAI,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC;wBACxB,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;oBACzB,CAAC;gBACH,CAAC;gBAED,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;oBAClB,OAAO;wBACL,OAAO,EAAE,KAAK;wBACd,KAAK,EAAE,MAAM;qBACd,CAAA;gBACH,CAAC;gBAED,OAAO;oBACL,OAAO,EAAE,IAAI;iBACd,CAAA;YACH,CAAC,CAAuB,GAC8C,CAAC,CAAA;QA/B3E,UAAK,GAAG,MAAM,CAAA;IAgCd,CAAC;CACF;AAED;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAA2B,OAA4B;IACrF,OAAO,IAAI,SAAS,CAAC,OAAO,CAAC,CAAA;AAC/B,CAAC"}
@@ -0,0 +1,35 @@
1
+ var __rest = (this && this.__rest) || function (s, e) {
2
+ var t = {};
3
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
4
+ t[p] = s[p];
5
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
6
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
7
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
8
+ t[p[i]] = s[p[i]];
9
+ }
10
+ return t;
11
+ };
12
+ import { Field } from "../lib/Field";
13
+ import { z } from 'zod';
14
+ import { zodValidator } from "../validators";
15
+ const MAX_VALID_DIGITS = 1000000000000000; // maximum number of digits that the input supports to perform operations
16
+ /**
17
+ * Field for numeric values. Supports both single numbers and arrays of numbers
18
+ * (range / multi-handle sliders) — the mode is inferred from whether
19
+ * `defaultValue` is an array. `min` defaults to `0` and `max` defaults to
20
+ * `1_000_000_000_000_000` (the maximum safe integer range the underlying input
21
+ * supports for arithmetic operations). Values outside the range fail
22
+ * validation.
23
+ */
24
+ export class NumberField extends Field {
25
+ constructor(options) {
26
+ const { min = 0, max = MAX_VALID_DIGITS, defaultValue } = options, others = __rest(options, ["min", "max", "defaultValue"]);
27
+ const isMultiple = Array.isArray(defaultValue);
28
+ const zScheme = z.number().min(min).max(max);
29
+ super(Object.assign({ validate: zodValidator(isMultiple ? z.array(zScheme) : zScheme), min,
30
+ max,
31
+ defaultValue }, others));
32
+ this._type = "NUMBER";
33
+ }
34
+ }
35
+ //# sourceMappingURL=number.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"number.js","sourceRoot":"","sources":["../../src/fields/number.ts"],"names":[],"mappings":";;;;;;;;;;;AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,cAAc,CAAA;AACpC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAY5C,MAAM,gBAAgB,GAAG,gBAAgB,CAAA,CAAC,yEAAyE;AAEnH;;;;;;;GAOG;AACH,MAAM,OAAO,WAA8C,SAAQ,KAAkC;IAGnG,YAAY,OAAqC;QAC/C,MAAM,EAAE,GAAG,GAAG,CAAC,EAAE,GAAG,GAAG,gBAAgB,EAAE,YAAY,KAAgB,OAAO,EAAlB,MAAM,UAAK,OAAO,EAAtE,8BAA4D,CAAU,CAAA;QAE5E,MAAM,UAAU,GAAG,KAAK,CAAC,OAAO,CAAC,YAAY,CAAC,CAAA;QAE9C,MAAM,OAAO,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;QAE5C,KAAK,CAAC,gBACJ,QAAQ,EAAE,YAAY,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAwB,EACtF,GAAG;YACH,GAAG;YACH,YAAY,IACT,MAAM,CACsB,CAAC,CAAA;QAfpC,UAAK,GAAG,QAAQ,CAAA;IAgBhB,CAAC;CACF"}
@@ -0,0 +1,22 @@
1
+ import { Field } from "../lib/Field";
2
+ import { z } from 'zod';
3
+ import { zodValidator } from "../validators";
4
+ /**
5
+ * Field for single or multi-select inputs whose options are a fixed list of
6
+ * string/number keys. `minItems` defaults to `1` (at least one selection
7
+ * required) and `maxItems` defaults to the length of the provided `options`
8
+ * array (all items selectable). The built-in validator accepts either a single
9
+ * value or an array, so the same field type covers both radio-style and
10
+ * checkbox-style UIs.
11
+ */
12
+ export class SelectableField extends Field {
13
+ constructor(options) {
14
+ var _a;
15
+ const { minItems = 1, maxItems = (_a = options === null || options === void 0 ? void 0 : options.options) === null || _a === void 0 ? void 0 : _a.length } = options;
16
+ const zScheme = z.string().or(z.number());
17
+ super(Object.assign({ validate: zodValidator(z.array(zScheme).min(minItems).max(maxItems).or(zScheme)), minItems,
18
+ maxItems }, options));
19
+ this._type = "SELECTABLE";
20
+ }
21
+ }
22
+ //# sourceMappingURL=selectable.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"selectable.js","sourceRoot":"","sources":["../../src/fields/selectable.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,cAAc,CAAA;AACpC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAc5C;;;;;;;GAOG;AACH,MAAM,OAAO,eAA0E,SAAQ,KAAkB;IAG/G,YAAY,OAA4C;;QACtD,MAAM,EAAE,QAAQ,GAAG,CAAC,EAAE,QAAQ,GAAG,MAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,OAAO,0CAAE,MAAM,EAAE,GAAG,OAAO,CAAA;QAErE,MAAM,OAAO,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAA;QAEzC,KAAK,iBACH,QAAQ,EAAE,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,CAAwB,EACvG,QAAQ;YACR,QAAQ,IACL,OAAO,EACV,CAAA;QAZJ,UAAK,GAAG,YAAY,CAAA;IAapB,CAAC;CACF"}
@@ -0,0 +1,16 @@
1
+ import { Field } from "../lib/Field";
2
+ import { z } from 'zod';
3
+ import { zodValidator } from "../validators";
4
+ /**
5
+ * Field for plain string values. Ships with a `z.string()` fallback validator
6
+ * so the field is always valid when no custom `validate` option is provided.
7
+ * Accepts `secure` (password masking) and `multiline` hints that are forwarded
8
+ * as props to the underlying input component.
9
+ */
10
+ export class TextField extends Field {
11
+ constructor(options) {
12
+ super(Object.assign({ validate: zodValidator(z.string().optional()) }, options));
13
+ this._type = "TEXT";
14
+ }
15
+ }
16
+ //# sourceMappingURL=text.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"text.js","sourceRoot":"","sources":["../../src/fields/text.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,cAAc,CAAA;AACpC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAY5C;;;;;GAKG;AACH,MAAM,OAAO,SAA0C,SAAQ,KAAuB;IAGpF,YAAY,OAAmC;QAC7C,KAAK,iBACH,QAAQ,EAAE,YAAY,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAwB,IACjE,OAAO,EACV,CAAA;QANJ,UAAK,GAAG,MAAM,CAAA;IAOd,CAAC;CACF"}
@@ -0,0 +1,3 @@
1
+ export { useField } from './useField';
2
+ export { useValidate } from './useValidate';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AACrC,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA"}
@@ -0,0 +1,24 @@
1
+ import { useMemo } from "react";
2
+ /**
3
+ * Convenience hook for components that accept an optional `field` prop. When
4
+ * `field` is provided it is used directly; when it is absent (falsy), a
5
+ * temporary field created by `defaultField` is memoised for the component's
6
+ * lifetime.
7
+ *
8
+ * This lets components remain uncontrolled by default (using their own
9
+ * internal field) while still accepting external control when a `field` prop
10
+ * is supplied — without conditional hook calls.
11
+ *
12
+ * `params` are forwarded to `field.use()` in either branch, so imperative
13
+ * ref bindings work regardless of which field is active.
14
+ */
15
+ export function useField(field, params, defaultField) {
16
+ if (field) {
17
+ return field.use(params === null || params === void 0 ? void 0 : params[0], params === null || params === void 0 ? void 0 : params[1]);
18
+ }
19
+ const tempField = useMemo(() => {
20
+ return defaultField();
21
+ }, []);
22
+ return tempField.use(params[0], params === null || params === void 0 ? void 0 : params[1]);
23
+ }
24
+ //# sourceMappingURL=useField.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useField.js","sourceRoot":"","sources":["../../src/hooks/useField.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAA;AAG/B;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,QAAQ,CAAkC,KAAQ,EAAE,MAA4B,EAAE,YAAqB;IACrH,IAAI,KAAK,EAAE,CAAC;QACV,OAAO,KAAK,CAAC,GAAG,CAAC,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAG,CAAC,CAAC,EAAE,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAG,CAAC,CAAC,CAAyB,CAAA;IACpE,CAAC;IAED,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,EAAE;QAC7B,OAAO,YAAY,EAAE,CAAA;IACvB,CAAC,EAAE,EAAE,CAAC,CAAA;IAEN,OAAO,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAG,CAAC,CAAC,CAAyB,CAAA;AACtE,CAAC"}
@@ -0,0 +1,54 @@
1
+ import { useCallback, useRef, useState } from 'react';
2
+ import { ValidationError } from '../lib/Field';
3
+ /**
4
+ * Standalone validation hook for values managed outside a `Field` instance.
5
+ * Useful when you have a raw state value and a validator function but do not
6
+ * want to create a full `Field` object.
7
+ *
8
+ * Error display follows the same blur policy as `Field.useValidation`: if
9
+ * `value` is `undefined` on the first render (`startedUnset`), the error is
10
+ * hidden until the user has blurred the input. Call the returned
11
+ * `onInputBlurred` handler on the input's blur event to trigger visibility.
12
+ *
13
+ * `message` is resolved from `readableError` first, then from the first
14
+ * element's `.message` when `error` is an array.
15
+ *
16
+ * A {@link ValidationError} thrown by `providedValidate` is caught and
17
+ * normalised; other exceptions propagate.
18
+ */
19
+ export const useValidate = (value, providedValidate) => {
20
+ var _a, _b, _c, _d;
21
+ const [hasBlurred, setHasBlurred] = useState(false);
22
+ const isUnset = typeof value === 'undefined';
23
+ const startedUnset = useRef(isUnset).current;
24
+ const validate = useCallback((value) => {
25
+ try {
26
+ const result = providedValidate === null || providedValidate === void 0 ? void 0 : providedValidate(value, {});
27
+ return result;
28
+ }
29
+ catch (e) {
30
+ if (e instanceof ValidationError) {
31
+ return {
32
+ isValid: false,
33
+ error: e.data
34
+ };
35
+ }
36
+ throw e;
37
+ }
38
+ }, []);
39
+ const onInputBlurred = useCallback(() => {
40
+ setHasBlurred(true);
41
+ }, []);
42
+ const validation = validate(value);
43
+ const isValid = (_a = validation === null || validation === void 0 ? void 0 : validation.isValid) !== null && _a !== void 0 ? _a : true;
44
+ const isInvalid = !isValid;
45
+ const message = (_b = validation === null || validation === void 0 ? void 0 : validation.readableError) !== null && _b !== void 0 ? _b : (_d = (_c = validation === null || validation === void 0 ? void 0 : validation.error) === null || _c === void 0 ? void 0 : _c[0]) === null || _d === void 0 ? void 0 : _d.message;
46
+ const errorDisplayRequiresBlur = startedUnset;
47
+ const showError = isInvalid && (errorDisplayRequiresBlur ? hasBlurred : true);
48
+ return {
49
+ onInputBlurred,
50
+ showError,
51
+ message,
52
+ };
53
+ };
54
+ //# sourceMappingURL=useValidate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useValidate.js","sourceRoot":"","sources":["../../src/hooks/useValidate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAErD,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AAE9C;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,CAAsC,KAAQ,EAAE,gBAAmB,EAAE,EAAE;;IAChG,MAAM,CAAC,UAAU,EAAE,aAAa,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAA;IAEnD,MAAM,OAAO,GAAG,OAAO,KAAK,KAAK,WAAW,CAAA;IAE5C,MAAM,YAAY,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC,OAAO,CAAA;IAE5C,MAAM,QAAQ,GAAG,WAAW,CAAC,CAAC,KAAQ,EAAE,EAAE;QACxC,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,gBAAgB,aAAhB,gBAAgB,uBAAhB,gBAAgB,CAAG,KAAK,EAAE,EAAE,CAAC,CAAA;YAE5C,OAAO,MAAM,CAAA;QACf,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,IAAI,CAAC,YAAY,eAAe,EAAE,CAAC;gBACjC,OAAO;oBACL,OAAO,EAAE,KAAK;oBACd,KAAK,EAAE,CAAC,CAAC,IAAI;iBACd,CAAA;YACH,CAAC;YAED,MAAM,CAAC,CAAA;QACT,CAAC;IACH,CAAC,EAAE,EAAE,CAAC,CAAA;IAEN,MAAM,cAAc,GAAG,WAAW,CAAC,GAAG,EAAE;QACtC,aAAa,CAAC,IAAI,CAAC,CAAA;IACrB,CAAC,EAAE,EAAE,CAAC,CAAA;IAEN,MAAM,UAAU,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAA;IAElC,MAAM,OAAO,GAAG,MAAA,UAAU,aAAV,UAAU,uBAAV,UAAU,CAAE,OAAO,mCAAI,IAAI,CAAA;IAE3C,MAAM,SAAS,GAAG,CAAC,OAAO,CAAA;IAE1B,MAAM,OAAO,GAAG,MAAA,UAAU,aAAV,UAAU,uBAAV,UAAU,CAAE,aAAa,mCAAI,MAAA,MAAA,UAAU,aAAV,UAAU,uBAAV,UAAU,CAAE,KAAK,0CAAG,CAAC,CAAC,0CAAE,OAAO,CAAA;IAE5E,MAAM,wBAAwB,GAAG,YAAY,CAAA;IAE7C,MAAM,SAAS,GAAG,SAAS,IAAI,CAAC,wBAAwB,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,CAAA;IAE7E,OAAO;QACL,cAAc;QACd,SAAS;QACT,OAAO;KACR,CAAA;AACH,CAAC,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,6 @@
1
+ export * from './fields';
2
+ export * from './types';
3
+ export * from './lib';
4
+ export * from './validators';
5
+ export * from './hooks';
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAA;AACxB,cAAc,SAAS,CAAA;AACvB,cAAc,OAAO,CAAA;AACrB,cAAc,cAAc,CAAA;AAC5B,cAAc,SAAS,CAAA"}
@@ -0,0 +1,346 @@
1
+ import { atom } from 'nanostores';
2
+ import { TypeGuards } from '@codeleap/types';
3
+ import { useStore } from '@nanostores/react';
4
+ import { useFieldBinding } from './useFieldBinding';
5
+ import { createRef, useRef, useState } from 'react';
6
+ import { logger } from '@codeleap/logger';
7
+ /**
8
+ * Thrown inside a validator function to signal a structured validation failure
9
+ * without propagating as an unhandled exception. The `data` payload is forwarded
10
+ * verbatim as the `error` field of the resulting {@link ValidationResult}.
11
+ *
12
+ * Throw this instead of returning `{ isValid: false }` when you need to exit
13
+ * validation from a nested helper that cannot easily return a value.
14
+ */
15
+ export class ValidationError extends Error {
16
+ constructor(data) {
17
+ super();
18
+ this.data = data;
19
+ }
20
+ }
21
+ /**
22
+ * Base class for all form fields. Owns the nanostores atom that holds the
23
+ * field's current value, runs synchronous validation on demand, and exposes
24
+ * React hooks (`use`, `useValue`, `useValidation`) that components call to
25
+ * subscribe to reactive updates.
26
+ *
27
+ * **Validation lifecycle**
28
+ * Validation is always synchronous and called eagerly — there is no deferred
29
+ * or debounced step. `validate()` runs against the current atom value each
30
+ * time it is called, so repeated calls are cheap but not memoised. Error
31
+ * visibility is decoupled from validity: errors are hidden until
32
+ * `revealError()` is called (or the field has been blurred while starting
33
+ * with an undefined value), allowing UX to control when messages appear.
34
+ *
35
+ * **Initialization order constraint**
36
+ * The constructor calls `loadState()` synchronously. If `defaultValue` is a
37
+ * Promise or a function that returns a Promise, the atom is initialised with
38
+ * `undefined` immediately and the resolved value is set asynchronously once
39
+ * the promise settles. Do not read `initialValue` before the promise resolves.
40
+ *
41
+ * **Platform methods**
42
+ * `measurePosition`, `scrollTo`, and `getPadding` delegate to static method
43
+ * slots (`methodMeasurePosition`, `methodScrollTo`, `methodGetPadding`) that
44
+ * must be assigned by the platform layer (web or mobile) before use; calling
45
+ * them without an implementation throws.
46
+ *
47
+ * **Prop transformers**
48
+ * `props()` pipes the field's properties through every registered transformer
49
+ * in insertion order. Transformers are global and shared across all field
50
+ * instances via `Field.transformers`.
51
+ */
52
+ export class Field {
53
+ /**
54
+ * Marks the field's error as visible. Typically called by `Form.validate`
55
+ * when `revealErrors: true` is passed, or imperatively after a failed submit.
56
+ * Has no effect on the underlying validity — only on whether the error
57
+ * message is shown to the user.
58
+ */
59
+ revealError() {
60
+ this.errorRevealed.set(true);
61
+ }
62
+ /**
63
+ * Resets error visibility to hidden without changing the field's value or
64
+ * validity state. Useful when clearing a form section programmatically
65
+ * while preserving the current value.
66
+ */
67
+ hideError() {
68
+ this.errorRevealed.set(false);
69
+ }
70
+ get isErrorRevealed() {
71
+ return this.errorRevealed.get();
72
+ }
73
+ constructor(options) {
74
+ this.properties = {};
75
+ this.options = options;
76
+ this.ref = createRef();
77
+ this.loadState();
78
+ this.errorRevealed = atom(false);
79
+ this.setValue = this.setValue.bind(this);
80
+ this.use = this.use.bind(this);
81
+ this.useBinding = this.useBinding.bind(this);
82
+ this.revealError = this.revealError.bind(this);
83
+ this.hideError = this.hideError.bind(this);
84
+ this.properties = this.toInternalProperties(options);
85
+ }
86
+ get name() {
87
+ return this.options.name;
88
+ }
89
+ get value() {
90
+ return this.state.get();
91
+ }
92
+ get isValid() {
93
+ const res = this.validate();
94
+ return res.isValid;
95
+ }
96
+ toInternalProperties(options) {
97
+ const internalKeys = new Set(['name', 'defaultValue', 'state', 'validate', 'loader', 'onValueChange']);
98
+ const values = Object.assign({ name: this.name }, Object.fromEntries(Object.entries(options).filter(([key]) => !internalKeys.has(key))));
99
+ return Object.assign({ field: this }, values);
100
+ }
101
+ /**
102
+ * Replaces the field's internal atom with a slice of the owning `Form`'s
103
+ * global state atom and migrates the current value into it. Called
104
+ * automatically by `Form.attachState()` during construction — do not call
105
+ * this manually unless you are building a custom form container.
106
+ */
107
+ attach(to) {
108
+ const val = this.value;
109
+ this.state = to;
110
+ this.setValue(val);
111
+ }
112
+ setValue(to) {
113
+ if (this.options.onValueChange)
114
+ this.options.onValueChange(to);
115
+ return this.state.set(to);
116
+ }
117
+ useValue() {
118
+ const value = useStore(this.state);
119
+ return value;
120
+ }
121
+ resetValue() {
122
+ this.setValue(this.initialValue);
123
+ this.errorRevealed.set(false);
124
+ }
125
+ /**
126
+ * Primary React hook for consuming a field inside a component. Subscribes to
127
+ * the field's value atom and validation state, and optionally wires an
128
+ * imperative ref handle via `useFieldBinding`. Must be called
129
+ * unconditionally at the component's top level.
130
+ *
131
+ * The `changed` flag compares the current value to the value captured at
132
+ * field construction time (or after the last `resetValue`), not to any
133
+ * previous render.
134
+ */
135
+ use(impl, deps) {
136
+ const value = this.useValue();
137
+ const validation = this.useValidation();
138
+ // Yes, this is dangerous and doesn't follow the rules, but not passing an implementation to imperative handle after passing it once would break the app anyway
139
+ if (impl) {
140
+ this.useBinding(impl, deps);
141
+ }
142
+ const changed = value != this.initialValue;
143
+ return {
144
+ validation,
145
+ value,
146
+ setValue: this.setValue,
147
+ changed,
148
+ representation: this.toRepresentation(value),
149
+ options: this.options,
150
+ };
151
+ }
152
+ useBinding(...args) {
153
+ useFieldBinding(this.ref, ...args);
154
+ }
155
+ changed() {
156
+ return this.state.get() != this.initialValue;
157
+ }
158
+ // If we make this async, the js engine will not delay further execution while the funcion is not done. This way we wait until we know wheter there's a promise or not
159
+ loadState() {
160
+ var _a, _b, _c;
161
+ let defaultValuePromise = undefined;
162
+ let defaultValue = undefined;
163
+ if (TypeGuards.isFunction(this.options.defaultValue)) {
164
+ const v = this.options.defaultValue();
165
+ if (TypeGuards.isPromise(v)) {
166
+ defaultValuePromise = v;
167
+ }
168
+ else {
169
+ defaultValue = v;
170
+ }
171
+ }
172
+ else {
173
+ const v = (_a = this.options) === null || _a === void 0 ? void 0 : _a.defaultValue;
174
+ if (TypeGuards.isPromise(v)) {
175
+ defaultValuePromise = v;
176
+ }
177
+ else {
178
+ defaultValue = v;
179
+ }
180
+ }
181
+ this.state = (_c = (_b = this === null || this === void 0 ? void 0 : this.options) === null || _b === void 0 ? void 0 : _b.state) !== null && _c !== void 0 ? _c : atom(defaultValue);
182
+ if (!defaultValuePromise) {
183
+ this.initialValue = defaultValue;
184
+ }
185
+ if (!!defaultValuePromise) {
186
+ this.log('debug', 'Waiting for initial value');
187
+ return defaultValuePromise
188
+ .then((v) => {
189
+ this.state.set(v);
190
+ this.initialValue = v;
191
+ this.log('debug', `Got initial value`, v);
192
+ })
193
+ .catch(e => {
194
+ this.log('error', `Failed to resolve default value`, e);
195
+ });
196
+ }
197
+ else {
198
+ return Promise.resolve();
199
+ }
200
+ }
201
+ formatLog(...args) {
202
+ const [firstArgument, ...otherArgs] = args;
203
+ let PREFIX = `(FIELD: ${this.name})`;
204
+ if (TypeGuards.isString(firstArgument)) {
205
+ PREFIX += ` ${firstArgument}`;
206
+ }
207
+ else {
208
+ otherArgs.unshift(firstArgument);
209
+ }
210
+ const logArgs = [
211
+ PREFIX,
212
+ ...otherArgs
213
+ ];
214
+ return logArgs;
215
+ }
216
+ /**
217
+ * Runs the field's validator synchronously against `value` (or the current
218
+ * atom value when omitted). A {@link ValidationError} thrown inside the
219
+ * validator is caught and converted to `{ isValid: false, error: e.data }`;
220
+ * any other exception is re-thrown.
221
+ *
222
+ * This method is called on every render inside `useValidation` — keep
223
+ * validators fast and free of side effects.
224
+ */
225
+ validate(value) {
226
+ const validate = this.options.validate;
227
+ const valueToCheck = TypeGuards.isUndefined(value) ? this.state.get() : this.toInternalValue(value);
228
+ try {
229
+ const result = validate(valueToCheck, {});
230
+ return result;
231
+ }
232
+ catch (e) {
233
+ if (e instanceof ValidationError) {
234
+ return {
235
+ isValid: false,
236
+ error: e.data
237
+ };
238
+ }
239
+ throw e;
240
+ }
241
+ }
242
+ /**
243
+ * React hook that returns reactive validation state for the field's current
244
+ * value. Error display policy:
245
+ * - If the field started with an `undefined` value (`startedUnset`), the
246
+ * error is hidden until the user has blurred the input **or** until
247
+ * `revealError()` has been called.
248
+ * - If the field started with a defined value, the error is shown
249
+ * immediately whenever the field is invalid.
250
+ *
251
+ * `message` is resolved from `readableError` first, falling back to the
252
+ * first element's `.message` when `error` is an array.
253
+ */
254
+ useValidation() {
255
+ var _a, _b;
256
+ const value = this.useValue();
257
+ const revealed = useStore(this.errorRevealed);
258
+ const isUnset = typeof value === 'undefined';
259
+ const startedUnset = useRef(isUnset).current;
260
+ const isSet = !isUnset;
261
+ const validation = this.validate(value);
262
+ const isValid = validation.isValid;
263
+ const isInvalid = !isValid;
264
+ const hasChanged = this.initialValue != value;
265
+ const message = (_a = validation.readableError) !== null && _a !== void 0 ? _a : (Array.isArray(validation.error) ? (_b = validation.error[0]) === null || _b === void 0 ? void 0 : _b.message : undefined);
266
+ const errorDisplayRequiresBlur = startedUnset;
267
+ const [hasBlurred, setHasBlurred] = useState(false);
268
+ const showError = isInvalid && (errorDisplayRequiresBlur ? revealed || hasBlurred : true);
269
+ return {
270
+ onInputBlurred() {
271
+ setHasBlurred(true);
272
+ },
273
+ hasBlurred,
274
+ hasChanged,
275
+ startedUnset,
276
+ isSet,
277
+ isInvalid,
278
+ isValid,
279
+ message,
280
+ showError,
281
+ isUnset,
282
+ validation,
283
+ value
284
+ };
285
+ }
286
+ log(level = 'log', ...args) {
287
+ if (Field.enableLogs)
288
+ logger[level](...this.formatLog(...args));
289
+ }
290
+ toInternalValue(v) {
291
+ return v;
292
+ }
293
+ toRepresentation(v) {
294
+ return v;
295
+ }
296
+ /**
297
+ * Registers a global prop transformer under `key`. Transformers are applied
298
+ * in insertion order by `props()`. Re-registering an existing key replaces
299
+ * the previous function.
300
+ */
301
+ static attachTransformer(key, fn) {
302
+ Field.transformers.set(key, fn);
303
+ }
304
+ /**
305
+ * Removes the transformer registered under `key`. Idempotent — no-op if the
306
+ * key is not present.
307
+ */
308
+ static detachTransformer(key) {
309
+ Field.transformers.delete(key);
310
+ }
311
+ /**
312
+ * Returns the field's properties after running them through every registered
313
+ * global transformer. The raw `properties` object is built from `options`
314
+ * with internal keys (`name`, `defaultValue`, `state`, `validate`, `loader`,
315
+ * `onValueChange`) stripped out, plus a `field` reference to `this`.
316
+ * Use this when passing field metadata to a component that does not use the
317
+ * `use()` hook directly.
318
+ */
319
+ props() {
320
+ return Array.from(Field.transformers.values()).reduce((acc, transformer) => transformer(acc), this.properties);
321
+ }
322
+ measurePosition(wrapperRef) {
323
+ return Field.methodMeasurePosition(this, wrapperRef);
324
+ }
325
+ scrollTo(scrollRef, measure) {
326
+ return Field.methodScrollTo(this, scrollRef, measure);
327
+ }
328
+ getPadding() {
329
+ return Field.methodGetPadding(this);
330
+ }
331
+ }
332
+ Field.transformers = new Map();
333
+ Field.enableLogs = false;
334
+ Field.getProps = (field) => {
335
+ throw new Error('Field.getProps not implemented');
336
+ };
337
+ Field.methodMeasurePosition = (field, wrapperRef) => {
338
+ throw new Error('Field.measurePosition not implemented');
339
+ };
340
+ Field.methodScrollTo = (field, scrollRef, measure) => {
341
+ throw new Error('Field.scrollTo not implemented');
342
+ };
343
+ Field.methodGetPadding = (field) => {
344
+ throw new Error('Field.getPadding not implemented');
345
+ };
346
+ //# sourceMappingURL=Field.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Field.js","sourceRoot":"","sources":["../../src/lib/Field.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,197 @@
1
+ import { createStateSlice, globalState } from "@codeleap/store";
2
+ import { TypeGuards } from "@codeleap/types";
3
+ import { useCallback, useEffect, useMemo, useState } from "react";
4
+ import { useUnmount } from "@codeleap/hooks";
5
+ function buildState(def) {
6
+ const stateArg = {};
7
+ for (const [name, field] of Object.entries(def)) {
8
+ stateArg[name] = field.value;
9
+ }
10
+ return stateArg;
11
+ }
12
+ /**
13
+ * Container that owns the shared nanostores `GlobalState` atom for a group of
14
+ * fields and wires each field's individual atom to a slice of that shared
15
+ * state. This ensures a single source of truth: reading `form.values` always
16
+ * reflects the live atom values of every field.
17
+ *
18
+ * **Initialization order**
19
+ * The constructor calls `attachState()` synchronously, which replaces each
20
+ * field's own atom with a derived slice and sets the field's `name` from its
21
+ * object key in the shape. Fields must therefore be fully constructed before
22
+ * being passed to `Form`.
23
+ *
24
+ * **React usage**
25
+ * - `use(selector)` — mounts the form in a component, subscribes to state
26
+ * changes via `selector`, and resets all field values on unmount. Prefer
27
+ * this for full-page forms.
28
+ * - `useShared(selector)` — subscribes without the unmount-reset side-effect.
29
+ * Use when the form outlives the component (e.g. a global singleton).
30
+ *
31
+ * **Validation**
32
+ * `validate()` runs every field's synchronous validator and returns a map of
33
+ * results keyed by field name. Pass `revealErrors: true` to simultaneously
34
+ * flip `errorRevealed` on every field, triggering error display in the UI.
35
+ */
36
+ class Form {
37
+ constructor(id, shape) {
38
+ this.id = id;
39
+ this.fields = shape;
40
+ this.state = globalState(buildState(this.fields));
41
+ this.attachState();
42
+ this.use = this.use.bind(this);
43
+ this.useShared = this.useShared.bind(this);
44
+ this.useReset = this.useReset.bind(this);
45
+ }
46
+ get values() {
47
+ return this.state.get();
48
+ }
49
+ get isChanged() {
50
+ return this.changed();
51
+ }
52
+ changed() {
53
+ for (const [fieldName, field] of Object.entries(this.fields)) {
54
+ if (field.changed())
55
+ return true;
56
+ }
57
+ return false;
58
+ }
59
+ get isValid() {
60
+ const res = this.validate();
61
+ return Object.values(res).every((result) => result.isValid);
62
+ }
63
+ slice(field) {
64
+ const fieldSlice = createStateSlice(this.state, (v) => v[field], (value) => {
65
+ return {
66
+ [field]: value
67
+ };
68
+ });
69
+ return fieldSlice;
70
+ }
71
+ iterFields(cb) {
72
+ const results = [];
73
+ let index = 0;
74
+ for (const [name, field] of Object.entries(this.fields)) {
75
+ const result = cb([name, field], index);
76
+ results.push(result);
77
+ index++;
78
+ }
79
+ return results;
80
+ }
81
+ /**
82
+ * Bulk-sets field values from a partial record. Boolean fields require an
83
+ * explicit `boolean` value; all other fields skip `undefined` and falsy
84
+ * values. Use `resetValues()` to restore all fields to their initial values.
85
+ */
86
+ setValues(values) {
87
+ this.iterFields(([name, field]) => {
88
+ const value = values === null || values === void 0 ? void 0 : values[name];
89
+ if (field._type === 'BOOLEAN' && TypeGuards.isBoolean(value))
90
+ field.setValue(value);
91
+ else if (value)
92
+ field.setValue(value);
93
+ });
94
+ }
95
+ resetValues() {
96
+ this.iterFields(([name, field]) => {
97
+ field.resetValue();
98
+ });
99
+ }
100
+ attachState() {
101
+ this.iterFields(([name, field]) => {
102
+ field.options.name = name;
103
+ field.attach(this.slice(name));
104
+ });
105
+ }
106
+ /**
107
+ * Returns the first field (in definition order) that fails validation, along
108
+ * with its `ValidationResult`. Returns `undefined` when all fields are
109
+ * valid. Useful for scrolling to the first error on submit.
110
+ */
111
+ firstInvalid() {
112
+ for (const [fieldName, field] of Object.entries(this.fields)) {
113
+ const validation = field.validate();
114
+ if (!validation.isValid)
115
+ return {
116
+ field,
117
+ validation,
118
+ };
119
+ }
120
+ }
121
+ validate(options) {
122
+ const { fields, revealErrors } = options !== null && options !== void 0 ? options : {};
123
+ const validateFields = fields !== null && fields !== void 0 ? fields : Object.keys(this.fields);
124
+ const results = this.iterFields(([name, field]) => {
125
+ if (!validateFields.includes(name))
126
+ return null;
127
+ return [name, field.validate()];
128
+ });
129
+ const resultMap = Object.fromEntries(results.filter((v) => !TypeGuards.isNil(v)));
130
+ if (revealErrors) {
131
+ this.iterFields(([_, field]) => {
132
+ field.revealError();
133
+ });
134
+ }
135
+ return resultMap;
136
+ }
137
+ /**
138
+ * Returns the transformed props for `field` via `Field.props()`. Throws if
139
+ * the field key does not exist in this form's shape, making typos a runtime
140
+ * error rather than a silent no-op.
141
+ */
142
+ register(field) {
143
+ if (!this.fields[field]) {
144
+ throw new Error(`Field "${field}" not found in "${this.id}" form`);
145
+ }
146
+ return this.fields[field].props();
147
+ }
148
+ use(selector) {
149
+ const value = this.useShared(selector);
150
+ this.useReset();
151
+ return value;
152
+ }
153
+ useReset() {
154
+ useUnmount(() => {
155
+ this.resetValues();
156
+ });
157
+ }
158
+ useShared(selector) {
159
+ const [selected, setSelected] = useState(() => selector(this));
160
+ const reselect = useCallback(() => {
161
+ setSelected(selector(this));
162
+ }, [selector]);
163
+ useEffect(() => {
164
+ return this.state.listen((value, previous) => {
165
+ if (value != previous) {
166
+ reselect();
167
+ }
168
+ });
169
+ }, [reselect]);
170
+ return selected;
171
+ }
172
+ }
173
+ /**
174
+ * Creates a `Form` instance that is stable for the lifetime of the component
175
+ * (memoised on `name`). Use this when the form is local to a single mounted
176
+ * component. For forms that must survive unmounts (e.g. multi-step flows),
177
+ * construct the `Form` outside React via `form()`.
178
+ *
179
+ * Note: `def` is only read on the first render — changing it after mount has
180
+ * no effect.
181
+ */
182
+ export function useForm(name, def) {
183
+ const form = useMemo(() => {
184
+ return new Form(name, def);
185
+ }, [name]);
186
+ return form;
187
+ }
188
+ /**
189
+ * Constructs a `Form` outside of React — suitable for module-level singletons,
190
+ * server-side construction, or multi-step flows where the form must outlive
191
+ * any individual component. Values are **not** automatically reset on unmount;
192
+ * call `resetValues()` explicitly when needed.
193
+ */
194
+ export function form(...args) {
195
+ return new Form(...args);
196
+ }
197
+ //# sourceMappingURL=Form.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Form.js","sourceRoot":"","sources":["../../src/lib/Form.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Wraps a `Field` subclass constructor in a factory function, eliminating the
3
+ * need to call `new` at the call site and improving inference of the
4
+ * `validate` option's generic type. The returned factory accepts the same
5
+ * options as the class constructor and produces a fully typed `Field` instance.
6
+ *
7
+ * All entries in the `fields` namespace are built with this helper.
8
+ */
9
+ export function fieldFactory(cls) {
10
+ return (options) => {
11
+ // @ts-expect-error
12
+ return new cls(options !== null && options !== void 0 ? options : {});
13
+ };
14
+ }
15
+ //# sourceMappingURL=factories.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"factories.js","sourceRoot":"","sources":["../../src/lib/factories.tsx"],"names":[],"mappings":"AAMA;;;;;;;GAOG;AACH,MAAM,UAAU,YAAY,CAI1B,GAAM;IACN,OAAO,CAAwC,OAAW,EAAgC,EAAE;QAE1F,mBAAmB;QACnB,OAAO,IAAI,GAAG,CAAC,OAAO,aAAP,OAAO,cAAP,OAAO,GAAI,EAAE,CAAC,CAAA;IAC/B,CAAC,CAAA;AACH,CAAC"}
@@ -0,0 +1,3 @@
1
+ export * from './Form';
2
+ export { Field } from './Field';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/lib/index.ts"],"names":[],"mappings":"AAAA,cAAc,QAAQ,CAAA;AACtB,OAAO,EAAC,KAAK,EAAC,MAAM,SAAS,CAAA"}
@@ -0,0 +1,48 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { useImperativeHandle } from "react";
11
+ /**
12
+ * Attaches an imperative handle to `ref` via `useImperativeHandle`. Every
13
+ * method in {@link IFieldRef} is given a default implementation that throws
14
+ * `"ref.<method> not implemented"`, so callers get a clear error rather than a
15
+ * silent no-op when a method is missing from `impl`.
16
+ *
17
+ * `impl` is a partial override — only supply the methods your component
18
+ * actually supports. Unimplemented methods remain as throwing stubs.
19
+ * `deps` is forwarded directly to `useImperativeHandle`; include anything
20
+ * `impl` closes over that can change between renders.
21
+ */
22
+ export function useFieldBinding(ref, impl, deps = []) {
23
+ const notImplemented = (method) => {
24
+ throw new Error(`ref.${method} not implemented`);
25
+ };
26
+ useImperativeHandle(ref, () => (Object.assign({ blur: () => {
27
+ notImplemented('blur');
28
+ }, emit: () => {
29
+ notImplemented('emit');
30
+ }, focus: () => {
31
+ notImplemented('focus');
32
+ },
33
+ // @ts-expect-error
34
+ getValue: () => {
35
+ notImplemented('getValue');
36
+ }, scrollIntoView: () => __awaiter(this, void 0, void 0, function* () {
37
+ notImplemented('scrollIntoView');
38
+ }), hideValue() {
39
+ notImplemented('hideValue');
40
+ },
41
+ revealValue() {
42
+ notImplemented('revealValue');
43
+ },
44
+ toggleValueVisibility() {
45
+ notImplemented('toggleValueVisibility');
46
+ } }, impl)), deps);
47
+ }
48
+ //# sourceMappingURL=useFieldBinding.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useFieldBinding.js","sourceRoot":"","sources":["../../src/lib/useFieldBinding.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,OAAO,CAAA;AAG3C;;;;;;;;;;GAUG;AACH,MAAM,UAAU,eAAe,CAAI,GAA+C,EAAE,IAA2B,EAAE,OAA6B,EAAE;IAE9I,MAAM,cAAc,GAAG,CAAC,MAAc,EAAE,EAAE;QACxC,MAAM,IAAI,KAAK,CAAC,OAAO,MAAM,kBAAkB,CAAC,CAAA;IAClD,CAAC,CAAA;IAGD,mBAAmB,CAAC,GAAG,EAAE,GAAI,EAAE,CAAC,iBAC9B,IAAI,EAAE,GAAG,EAAE;YACT,cAAc,CAAC,MAAM,CAAC,CAAA;QACxB,CAAC,EACD,IAAI,EAAE,GAAG,EAAE;YACT,cAAc,CAAC,MAAM,CAAC,CAAA;QACxB,CAAC,EACD,KAAK,EAAE,GAAG,EAAE;YACV,cAAc,CAAC,OAAO,CAAC,CAAA;QACzB,CAAC;QACD,mBAAmB;QACnB,QAAQ,EAAE,GAAG,EAAE;YACb,cAAc,CAAC,UAAU,CAAC,CAAA;QAC5B,CAAC,EACD,cAAc,EAAE,GAAS,EAAE;YACzB,cAAc,CAAC,gBAAgB,CAAC,CAAA;QAClC,CAAC,CAAA,EACD,SAAS;YACP,cAAc,CAAC,WAAW,CAAC,CAAA;QAC7B,CAAC;QACD,WAAW;YACT,cAAc,CAAC,aAAa,CAAC,CAAA;QAC/B,CAAC;QACD,qBAAqB;YACnB,cAAc,CAAC,uBAAuB,CAAC,CAAA;QACzC,CAAC,IACE,IAAI,EACP,EAAE,IAAI,CAAC,CAAA;AAEX,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=field.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"field.js","sourceRoot":"","sources":["../../src/types/field.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=form.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"form.js","sourceRoot":"","sources":["../../src/types/form.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=globals.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"globals.js","sourceRoot":"","sources":["../../src/types/globals.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=validation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validation.js","sourceRoot":"","sources":["../../src/types/validation.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export * from './zod';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/validators/index.ts"],"names":[],"mappings":"AAAA,cAAc,OAAO,CAAA"}
@@ -0,0 +1,43 @@
1
+ import { TypeGuards } from '@codeleap/types';
2
+ /**
3
+ * Adapts a Zod schema into the `Validator` contract expected by `Field` and
4
+ * `useValidate`. Uses `safeParse` internally so Zod errors are captured as
5
+ * `{ isValid: false, error: ZodIssue[] }` rather than thrown exceptions.
6
+ *
7
+ * The returned function is synchronous — async Zod schemas (`z.promise`,
8
+ * `.parseAsync`) are not supported.
9
+ */
10
+ export function zodValidator(model) {
11
+ return (value) => {
12
+ var _a;
13
+ const result = model.safeParse(value);
14
+ return {
15
+ isValid: result.success,
16
+ error: (_a = result.error) === null || _a === void 0 ? void 0 : _a.issues,
17
+ result: result.data
18
+ };
19
+ };
20
+ }
21
+ const isZodIssue = (val) => {
22
+ return ['code', 'expected', 'received', 'path', 'message'].every(x => x in val);
23
+ };
24
+ /**
25
+ * Type guard that narrows a `ValidationResult`-shaped value to one produced by
26
+ * `zodValidator`. A valid result must have `result` present; an invalid result
27
+ * must have `error` as an array of `ZodIssue` objects. Use this to
28
+ * distinguish Zod results from results produced by custom validators when
29
+ * handling errors generically.
30
+ */
31
+ export function isZodValidationResult(val) {
32
+ const isValidABoolean = TypeGuards.isBoolean(val.isValid);
33
+ if (isValidABoolean) {
34
+ if (!val.isValid) {
35
+ return TypeGuards.isArray(val.error) && val.error.every(isZodIssue);
36
+ }
37
+ else {
38
+ return 'result' in val;
39
+ }
40
+ }
41
+ return false;
42
+ }
43
+ //# sourceMappingURL=zod.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zod.js","sourceRoot":"","sources":["../../src/validators/zod.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAI5C;;;;;;;GAOG;AACH,MAAM,UAAU,YAAY,CAAsB,KAAQ;IACxD,OAAO,CAAC,KAAc,EAA0B,EAAE;;QAChD,MAAM,MAAM,GAAG,KAAK,CAAC,SAAS,CAAC,KAAK,CAAC,CAAA;QAErC,OAAO;YACL,OAAO,EAAE,MAAM,CAAC,OAAO;YACvB,KAAK,EAAE,MAAA,MAAM,CAAC,KAAK,0CAAE,MAAM;YAC3B,MAAM,EAAE,MAAM,CAAC,IAAI;SACpB,CAAA;IACH,CAAC,CAAA;AACH,CAAC;AAGD,MAAM,UAAU,GAAG,CAAC,GAAQ,EAAmB,EAAE;IAC/C,OAAO,CAAC,MAAM,EAAC,UAAU,EAAC,UAAU,EAAC,MAAM,EAAC,SAAS,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,GAAG,CAAC,CAAA;AAC7E,CAAC,CAAA;AAED;;;;;;GAMG;AACH,MAAM,UAAU,qBAAqB,CAAC,GAAQ;IAC5C,MAAM,eAAe,GAAG,UAAU,CAAC,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;IAEzD,IAAG,eAAe,EAAE,CAAC;QACnB,IAAG,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;YAChB,OAAO,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,UAAU,CAAC,CAAA;QACrE,CAAC;aAAM,CAAC;YACN,OAAO,QAAQ,IAAI,GAAG,CAAA;QACxB,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAA;AAEd,CAAC"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@codeleap/form",
3
- "version": "7.0.0",
4
- "main": "src/index.ts",
3
+ "version": "7.0.1",
4
+ "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "exports": {
7
7
  ".": {
@@ -22,10 +22,10 @@
22
22
  "directory": "packages/form"
23
23
  },
24
24
  "devDependencies": {
25
- "@codeleap/config": "7.0.0",
26
- "@codeleap/types": "7.0.0",
27
- "@codeleap/store": "7.0.0",
28
- "@codeleap/hooks": "7.0.0",
25
+ "@codeleap/config": "7.0.1",
26
+ "@codeleap/types": "7.0.1",
27
+ "@codeleap/store": "7.0.1",
28
+ "@codeleap/hooks": "7.0.1",
29
29
  "zod": "4.4.3",
30
30
  "ts-node-dev": "1.1.8"
31
31
  },
@@ -35,10 +35,10 @@
35
35
  "playground": "bun src/test.ts"
36
36
  },
37
37
  "peerDependencies": {
38
- "@codeleap/types": "7.0.0",
39
- "@codeleap/store": "7.0.0",
40
- "@codeleap/logger": "7.0.0",
41
- "@codeleap/hooks": "7.0.0",
38
+ "@codeleap/types": "7.0.1",
39
+ "@codeleap/store": "7.0.1",
40
+ "@codeleap/logger": "7.0.1",
41
+ "@codeleap/hooks": "7.0.1",
42
42
  "zod": "*",
43
43
  "react": "19.1.0",
44
44
  "typescript": "5.5.2"