formstand 0.13.0 → 0.15.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.
package/dist/index.js CHANGED
@@ -1,3 +1,5 @@
1
+ import { getAtPath, slotAtPath, setAtPath, isPlainObject, hasOwnSlot, valuesEqual, isFieldDirty, useFormSelector, useFormSelectorShallow, parsePath } from './chunk-N7WPXYCU.js';
2
+ export { getAtPath, parsePath, setAtPath, useFormSelector, useFormSelectorShallow } from './chunk-N7WPXYCU.js';
1
3
  import { createStore } from 'zustand/vanilla';
2
4
  import { devtools } from 'zustand/middleware';
3
5
  import { z } from 'zod';
@@ -6,8 +8,6 @@ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
6
8
  import { useStore } from 'zustand/react';
7
9
  import { useShallow } from 'zustand/react/shallow';
8
10
 
9
- // src/core/createForm.ts
10
-
11
11
  // src/core/array.ts
12
12
  var removeAt = (i) => (n) => n === i ? null : n > i ? n - 1 : n;
13
13
  var insertAt = (i) => (n) => n >= i ? n + 1 : n;
@@ -101,138 +101,6 @@ var clearArrayOpsUnder = (store, path, keepSelf = false) => {
101
101
  );
102
102
  related.forEach((p) => byPath.delete(p));
103
103
  };
104
-
105
- // src/core/equality.ts
106
- var isPlainObject = (v) => {
107
- if (typeof v !== "object" || v === null) return false;
108
- const proto = Object.getPrototypeOf(v);
109
- return proto === Object.prototype || proto === null;
110
- };
111
- var valuesEqual = (a, b) => {
112
- if (Object.is(a, b)) return true;
113
- if (a instanceof Date && b instanceof Date) {
114
- return a.getTime() === b.getTime();
115
- }
116
- if (Array.isArray(a) && Array.isArray(b)) {
117
- return a.length === b.length && a.every((v, i) => valuesEqual(v, b[i]));
118
- }
119
- if (isPlainObject(a) && isPlainObject(b)) {
120
- const aKeys = Object.keys(a);
121
- const bKeys = Object.keys(b);
122
- return aKeys.length === bKeys.length && aKeys.every((k) => Object.hasOwn(b, k) && valuesEqual(a[k], b[k]));
123
- }
124
- return false;
125
- };
126
- var dirtyCache = /* @__PURE__ */ new WeakMap();
127
- var MAX_VERDICTS = 4;
128
- var isFieldDirty = (value, initialValue) => {
129
- if (Object.is(value, initialValue)) return false;
130
- if (typeof value !== "object" || value === null) {
131
- return !valuesEqual(value, initialValue);
132
- }
133
- const cached = dirtyCache.get(value) ?? [];
134
- const hit = cached.find((v) => Object.is(v.initial, initialValue));
135
- if (hit !== void 0) return hit.dirty;
136
- const dirty = !valuesEqual(value, initialValue);
137
- dirtyCache.set(
138
- value,
139
- [{ initial: initialValue, dirty }, ...cached].slice(0, MAX_VERDICTS)
140
- );
141
- return dirty;
142
- };
143
-
144
- // src/core/path.ts
145
- var MAX_ARRAY_INDEX = 1e5;
146
- var isIndexSegment = (s) => {
147
- const n = Number(s);
148
- return Number.isInteger(n) && n >= 0 && String(n) === s;
149
- };
150
- var PARSE_CACHE_MAX = 4096;
151
- var parseCache = /* @__PURE__ */ new Map();
152
- var parsePath = (path) => {
153
- const cached = parseCache.get(path);
154
- if (cached !== void 0) return cached;
155
- const segments = Object.freeze(
156
- path === "" ? [] : path.split(".").map((s) => isIndexSegment(s) ? Number(s) : s)
157
- );
158
- if (parseCache.size >= PARSE_CACHE_MAX) parseCache.clear();
159
- parseCache.set(path, segments);
160
- return segments;
161
- };
162
- var isPlainRecord = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
163
- var readSegment = (obj, segment) => {
164
- if (obj == null) return void 0;
165
- if (Array.isArray(obj)) {
166
- return typeof segment === "number" ? obj[segment] : void 0;
167
- }
168
- return isPlainRecord(obj) && Object.hasOwn(obj, String(segment)) ? obj[String(segment)] : void 0;
169
- };
170
- var getAtPath = (obj, path) => parsePath(path).reduce(readSegment, obj);
171
- var slotAtPath = (obj, path) => {
172
- const walked = parsePath(path).reduce(
173
- (acc, segment) => {
174
- const container = acc.current;
175
- const addressable = typeof segment === "number" ? Array.isArray(container) ? segment < container.length : isPlainRecord(container) : isPlainRecord(container);
176
- return !acc.ok || !addressable ? { ok: false, current: void 0 } : { ok: true, current: readSegment(container, segment) };
177
- },
178
- { ok: true, current: obj }
179
- );
180
- return walked.ok ? { exists: true, value: walked.current } : { exists: false };
181
- };
182
- var hasOwnSlot = (obj, path) => parsePath(path).reduce(
183
- (acc, segment) => {
184
- const container = acc.current;
185
- const owns = Array.isArray(container) ? typeof segment === "number" && segment < container.length : isPlainRecord(container) && Object.hasOwn(container, String(segment));
186
- return !acc.ok || !owns ? { ok: false, current: void 0 } : { ok: true, current: readSegment(container, segment) };
187
- },
188
- { ok: true, current: obj }
189
- ).ok;
190
- var writeIntoArray = (arr, index, rest, value) => {
191
- const length = Math.max(arr.length, index + 1);
192
- return Array.from(
193
- { length },
194
- (_, i) => i === index ? writeSegments(arr[i], rest, value) : arr[i]
195
- );
196
- };
197
- var refusesHugeIndex = (index) => {
198
- if (index > MAX_ARRAY_INDEX) {
199
- console.warn(
200
- `[formstand] refusing to write array index ${index} (max ${MAX_ARRAY_INDEX}); value left unchanged.`
201
- );
202
- return true;
203
- }
204
- return false;
205
- };
206
- var writeSegments = (obj, segments, value) => {
207
- const [head, ...rest] = segments;
208
- if (head === void 0) return value;
209
- if (Array.isArray(obj)) {
210
- if (typeof head !== "number") {
211
- console.warn(
212
- `[formstand] cannot write string key "${head}" into an array; value left unchanged.`
213
- );
214
- return obj;
215
- }
216
- if (refusesHugeIndex(head)) return obj;
217
- return writeIntoArray(obj, head, rest, value);
218
- }
219
- if (isPlainRecord(obj)) {
220
- if (!isPlainObject(obj)) {
221
- console.warn(
222
- `[formstand] cannot write key "${String(head)}" through a non-plain object (Date/Map/Set/class instance); value left unchanged.`
223
- );
224
- return obj;
225
- }
226
- const key = String(head);
227
- return { ...obj, [key]: writeSegments(obj[key], rest, value) };
228
- }
229
- if (typeof head === "number") {
230
- if (refusesHugeIndex(head)) return obj;
231
- return writeIntoArray([], head, rest, value);
232
- }
233
- return { [head]: writeSegments(void 0, rest, value) };
234
- };
235
- var setAtPath = (obj, path, value) => writeSegments(obj, parsePath(path), value);
236
104
  var issuePathToFormPath = (path) => path.filter((segment) => typeof segment !== "symbol").join(".");
237
105
  var expandIssue = (issue, prefix) => {
238
106
  const at = [...prefix, ...issue.path];
@@ -1138,6 +1006,25 @@ var shouldValidateOn = (trigger, mode, reValidateMode, submitAttempted, touched
1138
1006
  };
1139
1007
 
1140
1008
  // src/core/persist.ts
1009
+ var kindOf = (value) => value === null ? "null" : Array.isArray(value) ? "array" : typeof value;
1010
+ var conflictsWith = (draft, reference) => {
1011
+ if (draft === void 0 || draft === null) return false;
1012
+ if (reference === void 0 || reference === null) return false;
1013
+ const draftKind = kindOf(draft);
1014
+ const referenceKind = kindOf(reference);
1015
+ if (draftKind !== referenceKind) return true;
1016
+ if (draftKind === "array") {
1017
+ const [d] = draft;
1018
+ const [r] = reference;
1019
+ return conflictsWith(d, r);
1020
+ }
1021
+ if (draftKind === "object") {
1022
+ const d = draft;
1023
+ const r = reference;
1024
+ return Object.keys(d).some((key) => conflictsWith(d[key], r[key]));
1025
+ }
1026
+ return false;
1027
+ };
1141
1028
  var defaultStorage = () => {
1142
1029
  try {
1143
1030
  return typeof globalThis.localStorage === "undefined" ? null : globalThis.localStorage;
@@ -1150,11 +1037,29 @@ var persistForm = (form, options) => {
1150
1037
  const debounceMs = options.debounceMs ?? 300;
1151
1038
  const apply = options.apply ?? "adopt";
1152
1039
  const applyDraft = (values) => apply === "restore" ? form.setValues(values) : form.adoptValues(values);
1040
+ const referenceShape = () => {
1041
+ try {
1042
+ return JSON.parse(JSON.stringify(form.getState().initialValues));
1043
+ } catch {
1044
+ return void 0;
1045
+ }
1046
+ };
1153
1047
  const restore = () => {
1154
1048
  try {
1155
1049
  const raw = storage === null ? null : storage.getItem(options.key);
1156
1050
  if (raw === null) return false;
1157
- applyDraft(JSON.parse(raw));
1051
+ const parsed = JSON.parse(raw);
1052
+ const versioned = options.version !== void 0 && typeof parsed === "object" && parsed !== null && "__v" in parsed;
1053
+ if (options.version !== void 0 && !versioned) return false;
1054
+ const wrapper = parsed;
1055
+ if (versioned && wrapper.__v !== options.version) return false;
1056
+ if (versioned && wrapper.values === void 0) return false;
1057
+ const values = versioned ? wrapper.values : parsed;
1058
+ const reference = referenceShape();
1059
+ if (reference !== void 0 && conflictsWith(values, reference)) {
1060
+ return false;
1061
+ }
1062
+ applyDraft(values);
1158
1063
  return true;
1159
1064
  } catch {
1160
1065
  return false;
@@ -1162,7 +1067,12 @@ var persistForm = (form, options) => {
1162
1067
  };
1163
1068
  const writeDraft = (values) => {
1164
1069
  try {
1165
- storage?.setItem(options.key, JSON.stringify(values));
1070
+ storage?.setItem(
1071
+ options.key,
1072
+ JSON.stringify(
1073
+ options.version === void 0 ? values : { __v: options.version, values }
1074
+ )
1075
+ );
1166
1076
  } catch {
1167
1077
  }
1168
1078
  };
@@ -1582,12 +1492,6 @@ function useIsValid(form, path) {
1582
1492
  }
1583
1493
  var useIsSubmitting = (form) => useStore(form.store, (state) => state.isSubmitting);
1584
1494
  var useSubmitCount = (form) => useStore(form.store, (state) => state.submitCount);
1585
- function useFormSelector(form, selector) {
1586
- return useStore(form.store, selector);
1587
- }
1588
- function useFormSelectorShallow(form, selector) {
1589
- return useStore(form.store, useShallow(selector));
1590
- }
1591
1495
 
1592
1496
  // src/react/createFormHooks.ts
1593
1497
  var capitalize = (name) => name.length === 0 ? name : name.charAt(0).toUpperCase() + name.slice(1);
@@ -2013,6 +1917,6 @@ var focusField = (path, root) => {
2013
1917
  return path === "" ? (root !== void 0 || document.querySelectorAll("form").length <= 1) && focusFirstOf(namedControls(scope).filter(isFocusCandidate)) : focusFirstOf(pathCandidates(scope, [path]).filter(isFocusCandidate));
2014
1918
  };
2015
1919
 
2016
- export { CheckboxField, DateField, NumberField, SelectField, TextField, checkboxProps, createForm, createFormContext, createFormHooks, dateInputProps, dateToInputText, emptyValueForSchema, flattenIssues, focusField, focusFirstError, getAtPath, hasFieldError, isAsyncRequiredError, numberInputProps, numberToInputText, parseDateText, parseNumberText, parsePath, persistForm, selectProps, setAtPath, shouldValidateOn, textInputProps, useField, useFieldArray, useForm, useFormError, useFormSelector, useFormSelectorShallow, useFormValues, useIsDirty, useIsSubmitting, useIsValid, useNumberInput, useSubmitCount, useVariantField, validateAsync, validateSync };
1920
+ export { CheckboxField, DateField, NumberField, SelectField, TextField, checkboxProps, createForm, createFormContext, createFormHooks, dateInputProps, dateToInputText, emptyValueForSchema, flattenIssues, focusField, focusFirstError, hasFieldError, isAsyncRequiredError, numberInputProps, numberToInputText, parseDateText, parseNumberText, persistForm, selectProps, shouldValidateOn, textInputProps, useField, useFieldArray, useForm, useFormError, useFormValues, useIsDirty, useIsSubmitting, useIsValid, useNumberInput, useSubmitCount, useVariantField, validateAsync, validateSync };
2017
1921
  //# sourceMappingURL=index.js.map
2018
1922
  //# sourceMappingURL=index.js.map