cogsbox-state 0.5.435 → 0.5.437

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/src/Functions.tsx CHANGED
@@ -1,260 +1,29 @@
1
1
  import { type FormOptsType } from './CogsState';
2
2
 
3
- import { useEffect, useRef, useState } from 'react';
4
3
  import React from 'react';
5
4
  import { getGlobalStore } from './store';
6
5
 
7
- export const useStoreSubscription = <T,>(
8
- fullPath: string,
9
- selector: (
10
- store: ReturnType<typeof getGlobalStore.getState>,
11
- path: string
12
- ) => T,
13
- compare: (a: T, b: T) => boolean = (a, b) =>
14
- JSON.stringify(a) === JSON.stringify(b)
15
- ) => {
16
- const [value, setValue] = useState<T>(() =>
17
- selector(getGlobalStore.getState(), fullPath)
18
- );
19
- const previousValueRef = useRef<T>(value);
20
- const fullPathRef = useRef(fullPath);
21
- useEffect(() => {
22
- fullPathRef.current = fullPath; // Ensure latest fullPath is always used
23
-
24
- setValue(selector(getGlobalStore.getState(), fullPath));
25
-
26
- const callback = (store: any) => {
27
- const newValue = selector(store, fullPathRef.current);
28
-
29
- if (!compare(previousValueRef.current, newValue)) {
30
- previousValueRef.current = newValue;
31
- setValue(newValue);
32
- }
33
- };
34
- const unsubscribe = getGlobalStore.subscribe(callback);
35
- return () => {
36
- unsubscribe();
37
- };
38
- }, [fullPath]);
39
- return value;
40
- };
41
- export const useGetValidationErrors = (
42
- validationKey: string,
43
- path: string[],
44
- validIndices?: number[]
45
- ) => {
46
- const fullPath =
47
- validationKey +
48
- '.' +
49
- (path.length > 0 ? [path.join('.')] : []) +
50
- (validIndices && validIndices.length > 0 ? '.' + validIndices : '');
51
-
52
- const returnresult = useStoreSubscription(
53
- fullPath,
54
- (store, path) => store.getValidationErrors(path) || []
55
- );
56
-
57
- return returnresult;
58
- };
59
-
60
- // Find FormControlComponent in your Functions.ts or equivalent file
61
-
62
- // export const FormControlComponent = <TStateObject,>({
63
- // setState, // This is the real effectiveSetState from the hook
64
- // path,
65
- // child,
66
- // formOpts,
67
- // stateKey,
68
- // rebuildStateShape,
69
- // }: FormControlComponentProps<TStateObject>) => {
70
- // const { registerFormRef, getFormRef } = formRefStore.getState();
71
- // const {
72
- // getValidationErrors,
73
- // addValidationError,
74
- // getInitialOptions,
75
- // removeValidationError,
76
- // } = getGlobalStore.getState();
77
- // const stateKeyPathKey = [stateKey, ...path].join('.');
78
- // const [, forceUpdate] = useState<any>();
79
- // getGlobalStore.getState().subscribeToPath(stateKeyPathKey, () => {
80
- // forceUpdate({});
81
- // });
82
-
83
- // const refKey = stateKey + '.' + path.join('.');
84
- // const localFormRef = useRef<HTMLInputElement>(null);
85
- // const existingRef = getFormRef(refKey);
86
- // if (!existingRef) {
87
- // registerFormRef(refKey, localFormRef);
88
- // }
89
- // const formRef = existingRef || localFormRef;
90
-
91
- // // --- START CHANGES ---
92
-
93
- // const globalStateValue = getGlobalStore
94
- // .getState()
95
- // .getShadowValue(stateKeyPathKey);
96
- // const [localValue, setLocalValue] = useState<any>(globalStateValue);
97
- // const isCurrentlyDebouncing = useRef(false);
98
- // const debounceTimeoutRef = useRef<NodeJS.Timeout | null>(null);
99
-
100
- // // Effect to sync local state if global state changes externally
101
- // useEffect(() => {
102
- // // Only update local if not actively debouncing a local change
103
- // if (!isCurrentlyDebouncing.current && globalStateValue !== localValue) {
104
- // setLocalValue(globalStateValue);
105
- // }
106
- // }, [globalStateValue]); // Removed localValue dependency
107
-
108
- // // Effect for cleanup
109
- // useEffect(() => {
110
- // return () => {
111
- // if (debounceTimeoutRef.current) {
112
- // clearTimeout(debounceTimeoutRef.current);
113
- // debounceTimeoutRef.current = null; // Explicitly nullify
114
- // isCurrentlyDebouncing.current = false;
115
- // }
116
- // };
117
- // }, []);
118
-
119
- // const debouncedUpdater = (payload: UpdateArg<TStateObject>) => {
120
- // setLocalValue(payload); // Update local state immediately
121
- // isCurrentlyDebouncing.current = true;
122
-
123
- // if (payload === '') {
124
- // if (debounceTimeoutRef.current) {
125
- // clearTimeout(debounceTimeoutRef.current); // Clear pending timer
126
- // debounceTimeoutRef.current = null;
127
- // }
128
-
129
- // setState(payload, path, { updateType: 'update' });
130
- // isCurrentlyDebouncing.current = false; // No longer debouncing
131
- // return; // Don't proceed to set another timeout
132
- // }
133
-
134
- // // If not empty, proceed with normal debouncing
135
- // if (debounceTimeoutRef.current) {
136
- // clearTimeout(debounceTimeoutRef.current);
137
- // }
138
-
139
- // debounceTimeoutRef.current = setTimeout(
140
- // () => {
141
- // isCurrentlyDebouncing.current = false;
142
- // console.log('debouncedUpdater', payload);
143
- // setState(payload, path, { updateType: 'update' });
144
- // },
145
- // formOpts?.debounceTime ??
146
- // (typeof globalStateValue == 'boolean' ? 20 : 200)
147
- // );
148
- // };
149
-
150
- // const initialOptions = getInitialOptions(stateKey);
151
-
152
- // const validationKey = initialOptions?.validation?.key;
153
- // const validateOnBlur = initialOptions?.validation?.onBlur === true;
154
-
155
- // const handleBlur = async () => {
156
- // // --- Ensure latest value is flushed if debouncing ---
157
- // if (debounceTimeoutRef.current) {
158
- // clearTimeout(debounceTimeoutRef.current); // Clear pending timer
159
- // debounceTimeoutRef.current = null;
160
- // isCurrentlyDebouncing.current = false;
161
- // // Ensure the absolute latest local value is committed on blur
162
- // setState(localValue, path, { updateType: 'update' });
163
- // }
164
- // // --- End modification ---
165
-
166
- // if (!initialOptions?.validation?.zodSchema || !validateOnBlur) return;
167
- // removeValidationError(validationKey + '.' + path.join('.'));
168
- // try {
169
- // // Use the potentially just flushed value
170
- // if (!validationKey) return;
171
- // const fieldValue = getGlobalStore
172
- // .getState()
173
- // .getShadowValue(stateKeyPathKey);
174
- // await validateZodPathFunc(
175
- // validationKey,
176
- // initialOptions.validation.zodSchema,
177
- // path,
178
- // fieldValue
179
- // );
180
- // // forceUpdate might be needed if validation state update doesn't trigger render
181
- // // Consider using useGetValidationErrors hook result directly for validation display
182
- // } catch (error) {
183
- // console.error('Validation error on blur:', error);
184
- // }
185
- // };
186
-
187
- // const childElement = child({
188
- // state: setter,
189
- // // --- START CHANGES ---
190
- // get: () => localValue, // Get should return the immediate local value
191
- // set: debouncedUpdater, // Use the new debounced updater
192
- // // --- END CHANGES ---
193
-
194
- // path: path,
195
- // validationErrors: () =>
196
- // getValidationErrors(validationKey + '.' + path.join('.')),
197
- // addValidationError: (message?: string) => {
198
- // removeValidationError(validationKey + '.' + path.join('.'));
199
- // addValidationError(validationKey + '.' + path.join('.'), message ?? '');
200
- // },
201
- // inputProps: {
202
- // // --- START CHANGES ---
203
- // value: localValue ?? '', // Input value is always the local state
204
- // onChange: (e: any) => debouncedUpdater(e.target.value), // Use debounced updater
205
- // // --- END CHANGES ---
206
- // onBlur: handleBlur,
207
- // ref: formRef,
208
- // },
209
- // });
210
-
211
- // return (
212
- // <>
213
- // <ValidationWrapper {...{ formOpts, path, stateKey }}>
214
- // {childElement}
215
- // </ValidationWrapper>
216
- // </>
217
- // );
218
- // };
219
6
  export type ValidationWrapperProps = {
220
7
  formOpts?: FormOptsType;
221
8
  path: string[];
222
9
  stateKey: string;
223
10
  children: React.ReactNode;
224
- validIndices?: number[];
225
11
  };
226
12
  export function ValidationWrapper({
227
13
  formOpts,
228
14
  path,
229
-
230
15
  stateKey,
231
16
  children,
232
- validIndices,
233
17
  }: ValidationWrapperProps) {
234
- const { getInitialOptions } = getGlobalStore.getState();
18
+ const { getInitialOptions, getShadowMetadata } = getGlobalStore.getState();
235
19
  const thisStateOpts = getInitialOptions(stateKey!);
236
- const validationKey = thisStateOpts?.validation?.key ?? stateKey!;
237
- const validationErrors = useGetValidationErrors(
238
- validationKey,
239
- path,
240
- validIndices
241
- );
242
- // console.log(
243
- // "validationErrors ValidationWrapper",
244
- // stateKey,
245
- // validationKey,
246
- // path,
247
- // validationErrors
248
- // );
249
- const thesMessages: string[] = [];
250
20
 
251
- if (validationErrors) {
252
- const newMessage = validationErrors!.join(', ');
253
- if (!thesMessages.includes(newMessage)) {
254
- thesMessages.push(newMessage);
255
- }
256
- }
21
+ // GET VALIDATION FROM SHADOW METADATA
22
+ const shadowMeta = getShadowMetadata(stateKey!, path);
23
+ const validationState = shadowMeta?.validation;
24
+ const status = validationState?.status || 'PRISTINE';
257
25
 
26
+ const message = validationState?.message;
258
27
  return (
259
28
  <>
260
29
  {thisStateOpts?.formElements?.validation &&
@@ -263,12 +32,10 @@ export function ValidationWrapper({
263
32
  children: (
264
33
  <React.Fragment key={path.toString()}>{children}</React.Fragment>
265
34
  ),
266
- active: validationErrors.length > 0 ? true : false,
35
+ status, // Pass status instead of active
267
36
  message: formOpts?.validation?.hideMessage
268
37
  ? ''
269
- : formOpts?.validation?.message
270
- ? formOpts?.validation?.message
271
- : thesMessages.map((m) => m).join(', '),
38
+ : formOpts?.validation?.message || message || '',
272
39
  path: path,
273
40
  })
274
41
  ) : (
@@ -1,10 +1,10 @@
1
- import { observable } from "@trpc/server/observable";
2
- import type { AnyRouter } from "@trpc/server";
3
- import type { TRPCLink } from "@trpc/client";
4
- import type { Operation } from "@trpc/client";
5
- import type { TRPCClientError } from "@trpc/client";
6
- import { getGlobalStore } from "./store";
7
- import type { Observer } from "@trpc/server/observable";
1
+ import { observable } from '@trpc/server/observable';
2
+ import type { AnyRouter } from '@trpc/server';
3
+ import type { TRPCLink } from '@trpc/client';
4
+ import type { Operation } from '@trpc/client';
5
+ import type { TRPCClientError } from '@trpc/client';
6
+ import { getGlobalStore } from './store';
7
+ import type { Observer } from '@trpc/server/observable';
8
8
  export const useCogsTrpcValidationLink = <
9
9
  TRouter extends AnyRouter,
10
10
  >(passedOpts?: {
@@ -25,27 +25,27 @@ export const useCogsTrpcValidationLink = <
25
25
  try {
26
26
  const errorObject = JSON.parse(err.message);
27
27
  if (passedOpts?.log) {
28
- console.log("errorObject", errorObject);
28
+ console.log('errorObject', errorObject);
29
29
  }
30
30
  if (Array.isArray(errorObject)) {
31
31
  errorObject.forEach(
32
32
  (error: { path: string[]; message: string }) => {
33
- const fullpath = `${op.path}.${error.path.join(".")}`;
33
+ const fullpath = `${op.path}.${error.path.join('.')}`;
34
34
  // In your TRPC link
35
35
  if (passedOpts?.log) {
36
- console.log("fullpath 1", fullpath);
36
+ console.log('fullpath 1', fullpath);
37
37
  }
38
38
  addValidationError(fullpath, error.message);
39
39
  }
40
40
  );
41
41
  } else if (
42
- typeof errorObject === "object" &&
42
+ typeof errorObject === 'object' &&
43
43
  errorObject !== null
44
44
  ) {
45
45
  Object.entries(errorObject).forEach(([key, value]) => {
46
46
  const fullpath = `${op.path}.${key}`;
47
47
  if (passedOpts?.log) {
48
- console.log("fullpath 2", fullpath);
48
+ console.log('fullpath 2', fullpath);
49
49
  }
50
50
  addValidationError(fullpath, value as string);
51
51
  });
package/src/index.ts CHANGED
@@ -1,4 +1,5 @@
1
- export * from "./CogsState.js";
2
- export * from "./CogsStateClient.js";
3
- export * from "./utility.js";
4
- export * from "./TRPCValidationLink.js";
1
+ export * from './CogsState.js';
2
+ export * from './CogsStateClient.js';
3
+ export * from './utility.js';
4
+ export * from './TRPCValidationLink.js';
5
+ export * from './store.js';
package/src/store.ts CHANGED
@@ -105,6 +105,7 @@ export type ShadowMetadata = {
105
105
  domRef?: HTMLElement | null;
106
106
  };
107
107
  syncInfo?: { status: string };
108
+ validation?: ValidationState;
108
109
  lastUpdated?: number;
109
110
  value?: any;
110
111
  classSignals?: Array<{
@@ -151,6 +152,24 @@ export type ShadowMetadata = {
151
152
  }
152
153
  >;
153
154
  } & ComponentsType;
155
+
156
+ export type ValidationStatus =
157
+ | 'PRISTINE' // Untouched, matches initial state.
158
+ | 'DIRTY' // Changed, but no validation run yet.
159
+ | 'VALID_LIVE' // Valid while typing.
160
+ | 'INVALID_LIVE' // Gentle error during typing.
161
+ | 'VALIDATION_FAILED' // Hard error on blur/submit.
162
+ | 'VALID_PENDING_SYNC' // Passed validation, ready for sync.
163
+ | 'SYNCING' // Actively being sent to the server.
164
+ | 'SYNCED' // Server confirmed success.
165
+ | 'SYNC_FAILED'; // Server rejected the data.
166
+
167
+ export type ValidationState = {
168
+ status: ValidationStatus;
169
+ message?: string;
170
+ lastValidated?: number;
171
+ validatedValue?: any;
172
+ };
154
173
  export type CogsEvent =
155
174
  | { type: 'INSERT'; path: string; itemKey: string; index: number }
156
175
  | { type: 'REMOVE'; path: string; itemKey: string }
@@ -159,6 +178,17 @@ export type CogsEvent =
159
178
  | { type: 'RELOAD'; path: string }; // For full re-initializations
160
179
  export type CogsGlobalState = {
161
180
  // --- Shadow State and Subscription System ---
181
+ registerComponent: (
182
+ stateKey: string,
183
+ componentId: string,
184
+ registration: any
185
+ ) => void;
186
+ unregisterComponent: (stateKey: string, componentId: string) => void;
187
+ addPathComponent: (
188
+ stateKey: string,
189
+ dependencyPath: string[],
190
+ fullComponentId: string
191
+ ) => void;
162
192
  shadowStateStore: Map<string, ShadowMetadata>;
163
193
  markAsDirty: (
164
194
  key: string,
@@ -275,6 +305,85 @@ const isSimpleObject = (value: any): boolean => {
275
305
  return Array.isArray(value) || value.constructor === Object;
276
306
  };
277
307
  export const getGlobalStore = create<CogsGlobalState>((set, get) => ({
308
+ addPathComponent: (stateKey, dependencyPath, fullComponentId) => {
309
+ set((state) => {
310
+ const newShadowStore = new Map(state.shadowStateStore);
311
+ const dependencyKey = [stateKey, ...dependencyPath].join('.');
312
+
313
+ // --- Part 1: Update the path's own metadata ---
314
+ const pathMeta = newShadowStore.get(dependencyKey) || {};
315
+ // Create a *new* Set to ensure immutability
316
+ const pathComponents = new Set(pathMeta.pathComponents);
317
+ pathComponents.add(fullComponentId);
318
+ // Update the metadata for the specific path
319
+ newShadowStore.set(dependencyKey, { ...pathMeta, pathComponents });
320
+
321
+ // --- Part 2: Update the component's own list of paths ---
322
+ const rootMeta = newShadowStore.get(stateKey) || {};
323
+ const component = rootMeta.components?.get(fullComponentId);
324
+
325
+ // If the component exists, update its `paths` set immutably
326
+ if (component) {
327
+ const newPaths = new Set(component.paths);
328
+ newPaths.add(dependencyKey);
329
+
330
+ const newComponentRegistration = { ...component, paths: newPaths };
331
+ const newComponentsMap = new Map(rootMeta.components);
332
+ newComponentsMap.set(fullComponentId, newComponentRegistration);
333
+
334
+ // Update the root metadata with the new components map
335
+ newShadowStore.set(stateKey, {
336
+ ...rootMeta,
337
+ components: newComponentsMap,
338
+ });
339
+ }
340
+
341
+ // Return the final, updated state
342
+ return { shadowStateStore: newShadowStore };
343
+ });
344
+ },
345
+ registerComponent: (stateKey, fullComponentId, registration) => {
346
+ set((state) => {
347
+ // Create a new Map to ensure Zustand detects the change
348
+ const newShadowStore = new Map(state.shadowStateStore);
349
+
350
+ // Get the metadata for the ROOT of the state (where the components map lives)
351
+ const rootMeta = newShadowStore.get(stateKey) || {};
352
+
353
+ // Also clone the components map to avoid direct mutation
354
+ const components = new Map(rootMeta.components);
355
+ components.set(fullComponentId, registration);
356
+
357
+ // Update the root metadata with the new components map
358
+ newShadowStore.set(stateKey, { ...rootMeta, components });
359
+
360
+ // Return the updated state
361
+ return { shadowStateStore: newShadowStore };
362
+ });
363
+ },
364
+
365
+ unregisterComponent: (stateKey, fullComponentId) => {
366
+ set((state) => {
367
+ const newShadowStore = new Map(state.shadowStateStore);
368
+ const rootMeta = newShadowStore.get(stateKey);
369
+
370
+ // If there's no metadata or no components map, do nothing
371
+ if (!rootMeta?.components) {
372
+ return state; // Return original state, no change needed
373
+ }
374
+
375
+ const components = new Map(rootMeta.components);
376
+ const wasDeleted = components.delete(fullComponentId);
377
+
378
+ // Only update state if something was actually deleted
379
+ if (wasDeleted) {
380
+ newShadowStore.set(stateKey, { ...rootMeta, components });
381
+ return { shadowStateStore: newShadowStore };
382
+ }
383
+
384
+ return state; // Nothing changed
385
+ });
386
+ },
278
387
  markAsDirty: (key: string, path: string[], options = { bubble: true }) => {
279
388
  const newShadowStore = new Map(get().shadowStateStore);
280
389
  let changed = false;
@@ -347,54 +456,72 @@ export const getGlobalStore = create<CogsGlobalState>((set, get) => ({
347
456
  },
348
457
 
349
458
  notifyPathSubscribers: (updatedPath, newValue) => {
350
- // <-- Now accepts newValue
351
459
  const subscribers = get().pathSubscribers;
352
460
  const subs = subscribers.get(updatedPath);
353
461
 
354
462
  if (subs) {
355
- // Pass the newValue to every callback
356
463
  subs.forEach((callback) => callback(newValue));
357
464
  }
358
465
  },
359
- initializeShadowState: (key: string, initialState: any) => {
360
- const existingShadowStore = new Map(get().shadowStateStore);
361
466
 
362
- const processValue = (value: any, path: string[]) => {
363
- const nodeKey = [key, ...path].join('.');
364
-
365
- if (Array.isArray(value)) {
366
- // Handle arrays as before
367
- const childIds: string[] = [];
368
-
369
- value.forEach((item) => {
370
- const itemId = `id:${ulid()}`;
371
- childIds.push(nodeKey + '.' + itemId);
372
- });
373
-
374
- existingShadowStore.set(nodeKey, { arrayKeys: childIds });
467
+ initializeShadowState: (key: string, initialState: any) => {
468
+ set((state) => {
469
+ // 1. Make a copy of the current store to modify it
470
+ const newShadowStore = new Map(state.shadowStateStore);
471
+
472
+ // 2. PRESERVE the existing components map before doing anything else
473
+ const existingRootMeta = newShadowStore.get(key);
474
+ const preservedComponents = existingRootMeta?.components;
475
+
476
+ // 3. Wipe all old shadow entries for this state key
477
+ const prefixToDelete = key + '.';
478
+ for (const k of Array.from(newShadowStore.keys())) {
479
+ if (k === key || k.startsWith(prefixToDelete)) {
480
+ newShadowStore.delete(k);
481
+ }
482
+ }
375
483
 
376
- value.forEach((item, index) => {
377
- const itemId = childIds[index]!.split('.').pop();
378
- processValue(item, [...path!, itemId!]);
379
- });
380
- } else if (isSimpleObject(value)) {
381
- // Only create field mappings for simple objects
382
- const fields = Object.fromEntries(
383
- Object.keys(value).map((k) => [k, nodeKey + '.' + k])
384
- );
385
- existingShadowStore.set(nodeKey, { fields });
484
+ // 4. Run your original logic to rebuild the state tree from scratch
485
+ const processValue = (value: any, path: string[]) => {
486
+ const nodeKey = [key, ...path].join('.');
386
487
 
387
- Object.keys(value).forEach((k) => {
388
- processValue(value[k], [...path, k]);
488
+ if (Array.isArray(value)) {
489
+ const childIds: string[] = [];
490
+ value.forEach(() => {
491
+ const itemId = `id:${ulid()}`;
492
+ childIds.push(nodeKey + '.' + itemId);
493
+ });
494
+ newShadowStore.set(nodeKey, { arrayKeys: childIds });
495
+ value.forEach((item, index) => {
496
+ const itemId = childIds[index]!.split('.').pop();
497
+ processValue(item, [...path!, itemId!]);
498
+ });
499
+ } else if (isSimpleObject(value)) {
500
+ const fields = Object.fromEntries(
501
+ Object.keys(value).map((k) => [k, nodeKey + '.' + k])
502
+ );
503
+ newShadowStore.set(nodeKey, { fields });
504
+ Object.keys(value).forEach((k) => {
505
+ processValue(value[k], [...path, k]);
506
+ });
507
+ } else {
508
+ newShadowStore.set(nodeKey, { value });
509
+ }
510
+ };
511
+ processValue(initialState, []);
512
+
513
+ // 5. RESTORE the preserved components map onto the new root metadata
514
+ if (preservedComponents) {
515
+ const newRootMeta = newShadowStore.get(key) || {};
516
+ newShadowStore.set(key, {
517
+ ...newRootMeta,
518
+ components: preservedComponents,
389
519
  });
390
- } else {
391
- // Treat everything else (including Uint8Array) as primitive values
392
- existingShadowStore.set(nodeKey, { value });
393
520
  }
394
- };
395
521
 
396
- processValue(initialState, []);
397
- set({ shadowStateStore: existingShadowStore });
522
+ // 6. Return the completely updated state
523
+ return { shadowStateStore: newShadowStore };
524
+ });
398
525
  },
399
526
 
400
527
  getShadowValue: (fullKey: string, validArrayIds?: string[]) => {
@@ -443,11 +570,41 @@ export const getGlobalStore = create<CogsGlobalState>((set, get) => ({
443
570
  return get().shadowStateStore.get(fullKey);
444
571
  },
445
572
 
446
- setShadowMetadata: (key: string, path: string[], metadata: any) => {
573
+ setShadowMetadata: (key, path, metadata) => {
447
574
  const fullKey = [key, ...path].join('.');
575
+ const existingMeta = get().shadowStateStore.get(fullKey);
576
+
577
+ // --- THIS IS THE TRAP ---
578
+ // If the existing metadata HAS a components map, but the NEW metadata DOES NOT,
579
+ // it means we are about to wipe it out. This is the bug.
580
+ if (existingMeta?.components && !metadata.components) {
581
+ console.group(
582
+ '%c🚨 RACE CONDITION DETECTED! 🚨',
583
+ 'color: red; font-size: 18px; font-weight: bold;'
584
+ );
585
+ console.error(
586
+ `An overwrite is about to happen on stateKey: "${key}" at path: [${path.join(', ')}]`
587
+ );
588
+ console.log(
589
+ 'The EXISTING metadata had a components map:',
590
+ existingMeta.components
591
+ );
592
+ console.log(
593
+ 'The NEW metadata is trying to save WITHOUT a components map:',
594
+ metadata
595
+ );
596
+ console.log(
597
+ '%cStack trace to the function that caused this overwrite:',
598
+ 'font-weight: bold;'
599
+ );
600
+ console.trace(); // This prints the call stack, leading you to the bad code.
601
+ console.groupEnd();
602
+ }
603
+ // --- END OF TRAP ---
604
+
448
605
  const newShadowStore = new Map(get().shadowStateStore);
449
- const existing = newShadowStore.get(fullKey) || { id: ulid() };
450
- newShadowStore.set(fullKey, { ...existing, ...metadata });
606
+ const finalMeta = { ...(existingMeta || {}), ...metadata };
607
+ newShadowStore.set(fullKey, finalMeta);
451
608
  set({ shadowStateStore: newShadowStore });
452
609
  },
453
610
  setTransformCache: (
@@ -1,34 +0,0 @@
1
- import { ZodObject, ZodRawShape, ZodTypeAny } from 'zod';
2
-
3
- export type ResultItem = {
4
- status: "loading" | "success" | "failure" | "error";
5
- message?: string;
6
- };
7
- type RequestType = Array<{
8
- path: string[];
9
- data: any;
10
- key: string;
11
- }>;
12
- type ResultsState = {
13
- results: Record<string, Record<string, ResultItem>>;
14
- request: Record<string, RequestType>;
15
- getResultsByKey: (key: string) => Record<string, ResultItem> | undefined;
16
- setResults: (key: string) => (result: Record<string, ResultItem> | ((prevState: Record<string, ResultItem>) => Record<string, ResultItem>)) => void;
17
- setRequest: (key: string) => (request: RequestType | ((prevState: RequestType) => RequestType)) => void;
18
- getRequestsByKey: (key: string) => Array<{
19
- path: string[];
20
- data: any;
21
- key: string;
22
- }> | undefined;
23
- };
24
- export declare const useResultsStore: import('zustand').UseBoundStore<import('zustand').StoreApi<ResultsState>>;
25
- export default function useValidateZodPath<T extends ZodRawShape>(validationKey: string, schema: ZodObject<T>, stateKey?: string): {
26
- validateZodPath: (path: string[], data: any, results?: Record<string, ResultItem> | undefined) => "loading" | "success" | "error" | "failure";
27
- getZodPathResults: (path: string[]) => string[] | ResultItem;
28
- zodPathResults: Record<string, ResultItem> | undefined;
29
- };
30
- export declare function validateZodPathFunc<T extends ZodRawShape, U>(validationKey: string, schema: ZodTypeAny, path: string[], data: U): Promise<{
31
- success: boolean;
32
- message?: string;
33
- }>;
34
- export {};