cogsbox-state 0.5.431 → 0.5.434
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/README.md +596 -238
- package/dist/CogsState.d.ts +93 -104
- package/dist/CogsState.jsx +1540 -1033
- package/dist/CogsState.jsx.map +1 -1
- package/dist/Functions.d.ts +1 -15
- package/dist/Functions.jsx +40 -187
- package/dist/Functions.jsx.map +1 -1
- package/dist/index.js +18 -19
- package/dist/store.d.ts +94 -92
- package/dist/store.js +230 -295
- package/dist/store.js.map +1 -1
- package/dist/useValidateZodPath.d.ts +1 -1
- package/dist/utility.d.ts +2 -2
- package/dist/utility.js +152 -169
- package/dist/utility.js.map +1 -1
- package/package.json +2 -1
- package/src/CogsState.tsx +2858 -1614
- package/src/Functions.tsx +167 -303
- package/src/store.ts +440 -440
- package/src/utility.ts +76 -95
- package/dist/useValidateZodPath.js +0 -59
- package/dist/useValidateZodPath.js.map +0 -1
package/src/store.ts
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import { create } from
|
|
2
|
-
import { ulid } from
|
|
1
|
+
import { create } from 'zustand';
|
|
2
|
+
import { ulid } from 'ulid';
|
|
3
3
|
import type {
|
|
4
4
|
OptionsType,
|
|
5
5
|
ReactivityType,
|
|
6
6
|
StateKeys,
|
|
7
|
-
SyncActionsType,
|
|
8
7
|
SyncInfo,
|
|
9
8
|
UpdateTypeDetail,
|
|
10
|
-
} from
|
|
9
|
+
} from './CogsState.js';
|
|
10
|
+
|
|
11
|
+
import type { ReactNode } from 'react';
|
|
11
12
|
|
|
12
13
|
type StateUpdater<StateValue> =
|
|
13
14
|
| StateValue
|
|
@@ -29,18 +30,7 @@ export type TrieNode = {
|
|
|
29
30
|
subscribers: Set<string>;
|
|
30
31
|
children: Map<string, TrieNode>;
|
|
31
32
|
};
|
|
32
|
-
|
|
33
|
-
components: Map<
|
|
34
|
-
string,
|
|
35
|
-
{
|
|
36
|
-
forceUpdate: () => void;
|
|
37
|
-
paths: Set<string>;
|
|
38
|
-
deps?: any[];
|
|
39
|
-
depsFunction?: (state: any) => any[] | true;
|
|
40
|
-
reactiveType: ReactivityType[] | ReactivityType;
|
|
41
|
-
}
|
|
42
|
-
>;
|
|
43
|
-
};
|
|
33
|
+
|
|
44
34
|
export type FormRefStoreState = {
|
|
45
35
|
formRefs: Map<string, React.RefObject<any>>;
|
|
46
36
|
registerFormRef: (id: string, ref: React.RefObject<any>) => void;
|
|
@@ -74,7 +64,7 @@ export const formRefStore = create<FormRefStoreState>((set, get) => ({
|
|
|
74
64
|
// Get all refs that start with the stateKey prefix
|
|
75
65
|
getFormRefsByStateKey: (stateKey) => {
|
|
76
66
|
const allRefs = get().formRefs;
|
|
77
|
-
const stateKeyPrefix = stateKey +
|
|
67
|
+
const stateKeyPrefix = stateKey + '.';
|
|
78
68
|
const filteredRefs = new Map();
|
|
79
69
|
|
|
80
70
|
allRefs.forEach((ref, id) => {
|
|
@@ -86,21 +76,95 @@ export const formRefStore = create<FormRefStoreState>((set, get) => ({
|
|
|
86
76
|
return filteredRefs;
|
|
87
77
|
},
|
|
88
78
|
}));
|
|
89
|
-
|
|
79
|
+
export type ComponentsType = {
|
|
80
|
+
components?: Map<
|
|
81
|
+
string,
|
|
82
|
+
{
|
|
83
|
+
forceUpdate: () => void;
|
|
84
|
+
paths: Set<string>;
|
|
85
|
+
deps?: any[];
|
|
86
|
+
prevDeps?: any[];
|
|
87
|
+
depsFunction?: (state: any) => any[] | true;
|
|
88
|
+
reactiveType: ReactivityType[] | ReactivityType;
|
|
89
|
+
}
|
|
90
|
+
>;
|
|
91
|
+
};
|
|
90
92
|
export type ShadowMetadata = {
|
|
91
|
-
id
|
|
93
|
+
id?: string;
|
|
94
|
+
|
|
95
|
+
stateSource?: 'default' | 'server' | 'localStorage';
|
|
96
|
+
lastServerSync?: number;
|
|
97
|
+
isDirty?: boolean;
|
|
98
|
+
baseServerState?: any;
|
|
99
|
+
|
|
92
100
|
arrayKeys?: string[];
|
|
101
|
+
|
|
102
|
+
fields?: Record<string, any>;
|
|
93
103
|
virtualizer?: {
|
|
94
104
|
itemHeight?: number;
|
|
95
105
|
domRef?: HTMLElement | null;
|
|
96
106
|
};
|
|
97
107
|
syncInfo?: { status: string };
|
|
98
108
|
lastUpdated?: number;
|
|
99
|
-
|
|
109
|
+
value?: any;
|
|
110
|
+
classSignals?: Array<{
|
|
111
|
+
// <-- ADD THIS BLOCK
|
|
112
|
+
id: string;
|
|
113
|
+
effect: string;
|
|
114
|
+
lastClasses: string;
|
|
115
|
+
deps: any[];
|
|
116
|
+
}>;
|
|
117
|
+
signals?: Array<{
|
|
118
|
+
instanceId: string;
|
|
119
|
+
parentId: string;
|
|
120
|
+
position: number;
|
|
121
|
+
effect?: string;
|
|
122
|
+
}>;
|
|
123
|
+
mapWrappers?: Array<{
|
|
124
|
+
instanceId: string;
|
|
125
|
+
path: string[];
|
|
126
|
+
componentId: string;
|
|
127
|
+
meta?: any;
|
|
128
|
+
mapFn: (
|
|
129
|
+
setter: any,
|
|
130
|
+
index: number,
|
|
131
|
+
|
|
132
|
+
arraySetter: any
|
|
133
|
+
) => ReactNode;
|
|
134
|
+
containerRef: HTMLDivElement | null;
|
|
135
|
+
rebuildStateShape: any;
|
|
136
|
+
}>;
|
|
137
|
+
transformCaches?: Map<
|
|
138
|
+
string,
|
|
139
|
+
{
|
|
140
|
+
validIds: string[];
|
|
141
|
+
computedAt: number;
|
|
142
|
+
transforms: Array<{ type: 'filter' | 'sort'; fn: Function }>;
|
|
143
|
+
}
|
|
144
|
+
>;
|
|
145
|
+
pathComponents?: Set<string>;
|
|
146
|
+
streams?: Map<
|
|
147
|
+
string,
|
|
148
|
+
{
|
|
149
|
+
buffer: any[];
|
|
150
|
+
flushTimer: NodeJS.Timeout | null;
|
|
151
|
+
}
|
|
152
|
+
>;
|
|
153
|
+
} & ComponentsType;
|
|
154
|
+
export type CogsEvent =
|
|
155
|
+
| { type: 'INSERT'; path: string; itemKey: string; index: number }
|
|
156
|
+
| { type: 'REMOVE'; path: string; itemKey: string }
|
|
157
|
+
| { type: 'UPDATE'; path: string; newValue: any }
|
|
158
|
+
| { type: 'ITEMHEIGHT'; itemKey: string; height: number } // For full re-initializations (e.g., when a component is removed)
|
|
159
|
+
| { type: 'RELOAD'; path: string }; // For full re-initializations
|
|
100
160
|
export type CogsGlobalState = {
|
|
101
161
|
// --- Shadow State and Subscription System ---
|
|
102
162
|
shadowStateStore: Map<string, ShadowMetadata>;
|
|
103
|
-
|
|
163
|
+
markAsDirty: (
|
|
164
|
+
key: string,
|
|
165
|
+
path: string[],
|
|
166
|
+
options: { bubble: boolean }
|
|
167
|
+
) => void;
|
|
104
168
|
// These method signatures stay the same
|
|
105
169
|
initializeShadowState: (key: string, initialState: any) => void;
|
|
106
170
|
updateShadowAtPath: (key: string, path: string[], newValue: any) => void;
|
|
@@ -110,6 +174,12 @@ export type CogsGlobalState = {
|
|
|
110
174
|
newItem: any
|
|
111
175
|
) => void;
|
|
112
176
|
removeShadowArrayElement: (key: string, arrayPath: string[]) => void;
|
|
177
|
+
getShadowValue: (
|
|
178
|
+
key: string,
|
|
179
|
+
|
|
180
|
+
validArrayIds?: string[]
|
|
181
|
+
) => any;
|
|
182
|
+
|
|
113
183
|
getShadowMetadata: (
|
|
114
184
|
key: string,
|
|
115
185
|
path: string[]
|
|
@@ -117,60 +187,36 @@ export type CogsGlobalState = {
|
|
|
117
187
|
setShadowMetadata: (
|
|
118
188
|
key: string,
|
|
119
189
|
path: string[],
|
|
120
|
-
metadata: Omit<ShadowMetadata,
|
|
190
|
+
metadata: Omit<ShadowMetadata, 'id'>
|
|
121
191
|
) => void;
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
getSelectedIndex: (
|
|
128
|
-
stateKey: string,
|
|
129
|
-
parentPath: string
|
|
130
|
-
) => number | undefined;
|
|
131
|
-
setSelectedIndex: (
|
|
132
|
-
stateKey: string,
|
|
133
|
-
parentPath: string,
|
|
134
|
-
index: number | undefined
|
|
192
|
+
setTransformCache: (
|
|
193
|
+
key: string,
|
|
194
|
+
path: string[],
|
|
195
|
+
cacheKey: string,
|
|
196
|
+
cacheData: any
|
|
135
197
|
) => void;
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
198
|
+
|
|
199
|
+
pathSubscribers: Map<string, Set<(newValue: any) => void>>;
|
|
200
|
+
subscribeToPath: (
|
|
201
|
+
path: string,
|
|
202
|
+
callback: (newValue: any) => void
|
|
203
|
+
) => () => void;
|
|
204
|
+
notifyPathSubscribers: (updatedPath: string, newValue: any) => void;
|
|
205
|
+
|
|
206
|
+
selectedIndicesMap: Map<string, string>; // stateKey -> (parentPath -> selectedIndex)
|
|
207
|
+
getSelectedIndex: (stateKey: string, validArrayIds?: string[]) => number;
|
|
208
|
+
setSelectedIndex: (key: string, itemKey: string) => void;
|
|
209
|
+
clearSelectedIndex: ({ arrayKey }: { arrayKey: string }) => void;
|
|
143
210
|
clearSelectedIndexesForState: (stateKey: string) => void;
|
|
144
211
|
|
|
145
212
|
// --- Core State and Updaters ---
|
|
146
|
-
|
|
213
|
+
|
|
147
214
|
initialStateOptions: { [key: string]: OptionsType };
|
|
148
|
-
|
|
149
|
-
isLoadingGlobal: { [key: string]: boolean };
|
|
215
|
+
|
|
150
216
|
initialStateGlobal: { [key: string]: StateValue };
|
|
151
|
-
|
|
152
|
-
serverState: { [key: string]: StateValue };
|
|
153
|
-
|
|
154
|
-
getUpdaterState: (key: string) => StateUpdater<StateValue>;
|
|
155
|
-
setUpdaterState: (key: string, newUpdater: any) => void;
|
|
156
|
-
getKeyState: <StateKey extends StateKeys>(key: StateKey) => StateValue;
|
|
157
|
-
getNestedState: <StateKey extends StateKeys>(
|
|
158
|
-
key: StateKey,
|
|
159
|
-
path: string[]
|
|
160
|
-
) => StateValue;
|
|
161
|
-
setState: <StateKey extends StateKeys>(
|
|
162
|
-
key: StateKey,
|
|
163
|
-
value: StateUpdater<StateValue>
|
|
164
|
-
) => void;
|
|
165
|
-
setInitialStates: (initialState: StateValue) => void;
|
|
166
|
-
setCreatedState: (initialState: StateValue) => void;
|
|
217
|
+
|
|
167
218
|
updateInitialStateGlobal: (key: string, newState: StateValue) => void;
|
|
168
|
-
|
|
169
|
-
setIsLoadingGlobal: (key: string, value: boolean) => void;
|
|
170
|
-
setServerState: <StateKey extends StateKeys>(
|
|
171
|
-
key: StateKey,
|
|
172
|
-
value: StateValue
|
|
173
|
-
) => void;
|
|
219
|
+
|
|
174
220
|
getInitialOptions: (key: string) => OptionsType | undefined;
|
|
175
221
|
setInitialStateOptions: (key: string, value: OptionsType) => void;
|
|
176
222
|
|
|
@@ -181,275 +227,432 @@ export type CogsGlobalState = {
|
|
|
181
227
|
removeValidationError: (path: string) => void;
|
|
182
228
|
|
|
183
229
|
// --- Server Sync and Logging ---
|
|
184
|
-
|
|
185
|
-
|
|
230
|
+
|
|
231
|
+
serverStateUpdates: Map<
|
|
232
|
+
string,
|
|
233
|
+
{
|
|
234
|
+
data: any;
|
|
235
|
+
status: 'loading' | 'success' | 'error';
|
|
236
|
+
timestamp: number;
|
|
237
|
+
}
|
|
238
|
+
>;
|
|
239
|
+
|
|
240
|
+
setServerStateUpdate: (key: string, serverState: any) => void;
|
|
241
|
+
|
|
186
242
|
stateLog: { [key: string]: UpdateTypeDetail[] };
|
|
187
243
|
syncInfoStore: Map<string, SyncInfo>;
|
|
188
|
-
|
|
189
|
-
setServerSyncLog: (key: string, newValue: SyncLogType) => void;
|
|
190
|
-
setServerSideOrNot: (key: string, value: boolean) => void;
|
|
191
|
-
getServerSideOrNot: (key: string) => boolean | undefined;
|
|
192
|
-
getThisLocalUpdate: (key: string) => UpdateTypeDetail[] | undefined;
|
|
193
|
-
setServerSyncActions: (key: string, value: SyncActionsType<any>) => void;
|
|
244
|
+
|
|
194
245
|
setStateLog: (
|
|
195
246
|
key: string,
|
|
196
247
|
updater: (prevUpdates: UpdateTypeDetail[]) => UpdateTypeDetail[]
|
|
197
248
|
) => void;
|
|
198
249
|
setSyncInfo: (key: string, syncInfo: SyncInfo) => void;
|
|
199
250
|
getSyncInfo: (key: string) => SyncInfo | null;
|
|
251
|
+
};
|
|
252
|
+
const isSimpleObject = (value: any): boolean => {
|
|
253
|
+
if (value === null || typeof value !== 'object') return false;
|
|
254
|
+
|
|
255
|
+
// Handle special cases that should be treated as primitives
|
|
256
|
+
if (
|
|
257
|
+
value instanceof Uint8Array ||
|
|
258
|
+
value instanceof Int8Array ||
|
|
259
|
+
value instanceof Uint16Array ||
|
|
260
|
+
value instanceof Int16Array ||
|
|
261
|
+
value instanceof Uint32Array ||
|
|
262
|
+
value instanceof Int32Array ||
|
|
263
|
+
value instanceof Float32Array ||
|
|
264
|
+
value instanceof Float64Array ||
|
|
265
|
+
value instanceof ArrayBuffer ||
|
|
266
|
+
value instanceof Date ||
|
|
267
|
+
value instanceof RegExp ||
|
|
268
|
+
value instanceof Map ||
|
|
269
|
+
value instanceof Set
|
|
270
|
+
) {
|
|
271
|
+
return false; // Treat as primitive
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Arrays and plain objects are complex
|
|
275
|
+
return Array.isArray(value) || value.constructor === Object;
|
|
276
|
+
};
|
|
277
|
+
export const getGlobalStore = create<CogsGlobalState>((set, get) => ({
|
|
278
|
+
markAsDirty: (key: string, path: string[], options = { bubble: true }) => {
|
|
279
|
+
const newShadowStore = new Map(get().shadowStateStore);
|
|
280
|
+
let changed = false;
|
|
281
|
+
|
|
282
|
+
// This function marks a single path as dirty if it was previously synced.
|
|
283
|
+
const setDirty = (currentPath: string[]) => {
|
|
284
|
+
const fullKey = [key, ...currentPath].join('.');
|
|
285
|
+
const meta = newShadowStore.get(fullKey);
|
|
286
|
+
|
|
287
|
+
// We only mark something as dirty if it was previously synced from the server.
|
|
288
|
+
// We also check `isDirty !== true` to avoid redundant updates.
|
|
289
|
+
if (meta && meta.stateSource === 'server' && meta.isDirty !== true) {
|
|
290
|
+
newShadowStore.set(fullKey, { ...meta, isDirty: true });
|
|
291
|
+
changed = true;
|
|
292
|
+
}
|
|
293
|
+
};
|
|
200
294
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
string,
|
|
204
|
-
Set<{
|
|
205
|
-
instanceId: string;
|
|
206
|
-
parentId: string;
|
|
207
|
-
position: number;
|
|
208
|
-
effect?: string;
|
|
209
|
-
map?: string;
|
|
210
|
-
}>
|
|
211
|
-
>;
|
|
212
|
-
addSignalElement: (
|
|
213
|
-
signalId: string,
|
|
214
|
-
elementInfo: {
|
|
215
|
-
instanceId: string;
|
|
216
|
-
parentId: string;
|
|
217
|
-
position: number;
|
|
218
|
-
effect?: string;
|
|
219
|
-
map?: string;
|
|
220
|
-
}
|
|
221
|
-
) => void;
|
|
222
|
-
removeSignalElement: (signalId: string, instanceId: string) => void;
|
|
223
|
-
stateComponents: Map<string, ComponentsType>;
|
|
295
|
+
// 1. Mark the target path itself as dirty.
|
|
296
|
+
setDirty(path);
|
|
224
297
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
depsFunction: ((state: any) => any[] | true) | null;
|
|
298
|
+
// 2. If `bubble` is true, walk up the path and mark all parents as dirty.
|
|
299
|
+
if (options.bubble) {
|
|
300
|
+
let parentPath = [...path];
|
|
301
|
+
while (parentPath.length > 0) {
|
|
302
|
+
parentPath.pop();
|
|
303
|
+
setDirty(parentPath);
|
|
304
|
+
}
|
|
233
305
|
}
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
deps: any[];
|
|
239
|
-
updaters: Set<() => void>;
|
|
240
|
-
depsFunction: ((state: any) => any[] | true) | null;
|
|
306
|
+
|
|
307
|
+
// Only update the global state if something actually changed.
|
|
308
|
+
if (changed) {
|
|
309
|
+
set({ shadowStateStore: newShadowStore });
|
|
241
310
|
}
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
311
|
+
},
|
|
312
|
+
serverStateUpdates: new Map(),
|
|
313
|
+
setServerStateUpdate: (key, serverState) => {
|
|
314
|
+
set((state) => {
|
|
315
|
+
const newMap = new Map(state.serverStateUpdates);
|
|
316
|
+
newMap.set(key, serverState);
|
|
317
|
+
return { serverStateUpdates: newMap };
|
|
318
|
+
});
|
|
246
319
|
|
|
247
|
-
|
|
320
|
+
// Notify all subscribers for this key
|
|
321
|
+
get().notifyPathSubscribers(key, {
|
|
322
|
+
type: 'SERVER_STATE_UPDATE',
|
|
323
|
+
serverState,
|
|
324
|
+
});
|
|
325
|
+
},
|
|
248
326
|
shadowStateStore: new Map(),
|
|
249
|
-
|
|
327
|
+
pathSubscribers: new Map<string, Set<(newValue: any) => void>>(),
|
|
250
328
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
newSubs.set(key, subsForKey);
|
|
257
|
-
return { shadowStateSubscribers: newSubs };
|
|
258
|
-
});
|
|
329
|
+
subscribeToPath: (path, callback) => {
|
|
330
|
+
const subscribers = get().pathSubscribers;
|
|
331
|
+
const subsForPath = subscribers.get(path) || new Set();
|
|
332
|
+
subsForPath.add(callback);
|
|
333
|
+
subscribers.set(path, subsForPath);
|
|
259
334
|
|
|
260
|
-
// Return unsubscribe function
|
|
261
335
|
return () => {
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
if (
|
|
266
|
-
|
|
267
|
-
if (subsForKey.size === 0) {
|
|
268
|
-
newSubs.delete(key);
|
|
269
|
-
}
|
|
336
|
+
const currentSubs = get().pathSubscribers.get(path);
|
|
337
|
+
if (currentSubs) {
|
|
338
|
+
currentSubs.delete(callback);
|
|
339
|
+
if (currentSubs.size === 0) {
|
|
340
|
+
get().pathSubscribers.delete(path);
|
|
270
341
|
}
|
|
271
|
-
|
|
272
|
-
});
|
|
342
|
+
}
|
|
273
343
|
};
|
|
274
344
|
},
|
|
345
|
+
|
|
346
|
+
notifyPathSubscribers: (updatedPath, newValue) => {
|
|
347
|
+
// <-- Now accepts newValue
|
|
348
|
+
const subscribers = get().pathSubscribers;
|
|
349
|
+
const subs = subscribers.get(updatedPath);
|
|
350
|
+
|
|
351
|
+
if (subs) {
|
|
352
|
+
// Pass the newValue to every callback
|
|
353
|
+
subs.forEach((callback) => callback(newValue));
|
|
354
|
+
}
|
|
355
|
+
},
|
|
275
356
|
initializeShadowState: (key: string, initialState: any) => {
|
|
276
|
-
const
|
|
357
|
+
const existingShadowStore = new Map(get().shadowStateStore);
|
|
277
358
|
|
|
278
359
|
const processValue = (value: any, path: string[]) => {
|
|
279
|
-
const nodeKey = [key, ...path].join(
|
|
360
|
+
const nodeKey = [key, ...path].join('.');
|
|
280
361
|
|
|
281
362
|
if (Array.isArray(value)) {
|
|
363
|
+
// Handle arrays as before
|
|
282
364
|
const childIds: string[] = [];
|
|
283
365
|
|
|
284
366
|
value.forEach((item) => {
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
367
|
+
const itemId = `id:${ulid()}`;
|
|
368
|
+
childIds.push(nodeKey + '.' + itemId);
|
|
369
|
+
});
|
|
288
370
|
|
|
289
|
-
|
|
290
|
-
childIds.push(itemId);
|
|
371
|
+
existingShadowStore.set(nodeKey, { arrayKeys: childIds });
|
|
291
372
|
|
|
292
|
-
|
|
293
|
-
|
|
373
|
+
value.forEach((item, index) => {
|
|
374
|
+
const itemId = childIds[index]!.split('.').pop();
|
|
375
|
+
processValue(item, [...path!, itemId!]);
|
|
294
376
|
});
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
} else if (typeof value === "object" && value !== null) {
|
|
302
|
-
newShadowStore.set(nodeKey, { id: ulid() });
|
|
377
|
+
} else if (isSimpleObject(value)) {
|
|
378
|
+
// Only create field mappings for simple objects
|
|
379
|
+
const fields = Object.fromEntries(
|
|
380
|
+
Object.keys(value).map((k) => [k, nodeKey + '.' + k])
|
|
381
|
+
);
|
|
382
|
+
existingShadowStore.set(nodeKey, { fields });
|
|
303
383
|
|
|
304
384
|
Object.keys(value).forEach((k) => {
|
|
305
385
|
processValue(value[k], [...path, k]);
|
|
306
386
|
});
|
|
307
387
|
} else {
|
|
308
|
-
|
|
388
|
+
// Treat everything else (including Uint8Array) as primitive values
|
|
389
|
+
existingShadowStore.set(nodeKey, { value });
|
|
309
390
|
}
|
|
310
391
|
};
|
|
311
392
|
|
|
312
393
|
processValue(initialState, []);
|
|
394
|
+
set({ shadowStateStore: existingShadowStore });
|
|
395
|
+
},
|
|
313
396
|
|
|
314
|
-
|
|
397
|
+
getShadowValue: (fullKey: string, validArrayIds?: string[]) => {
|
|
398
|
+
const shadowMeta = get().shadowStateStore.get(fullKey);
|
|
399
|
+
|
|
400
|
+
// If no metadata found, return undefined
|
|
401
|
+
if (!shadowMeta) {
|
|
402
|
+
return undefined;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// For primitive values, return the value
|
|
406
|
+
if (shadowMeta.value !== undefined) {
|
|
407
|
+
return shadowMeta.value;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// For arrays, reconstruct with possible validArrayIds
|
|
411
|
+
if (shadowMeta.arrayKeys) {
|
|
412
|
+
const arrayKeys = validArrayIds ?? shadowMeta.arrayKeys;
|
|
413
|
+
const items = arrayKeys.map((itemKey) => {
|
|
414
|
+
// RECURSIVELY call getShadowValue for each item
|
|
415
|
+
return get().getShadowValue(itemKey);
|
|
416
|
+
});
|
|
417
|
+
return items;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// For objects with fields, reconstruct object
|
|
421
|
+
if (shadowMeta.fields) {
|
|
422
|
+
const reconstructedObject: any = {};
|
|
423
|
+
Object.entries(shadowMeta.fields).forEach(([key, fieldPath]) => {
|
|
424
|
+
// RECURSIVELY call getShadowValue for each field
|
|
425
|
+
reconstructedObject[key] = get().getShadowValue(fieldPath as string);
|
|
426
|
+
});
|
|
427
|
+
return reconstructedObject;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
return undefined;
|
|
315
431
|
},
|
|
316
|
-
getShadowMetadata: (
|
|
317
|
-
|
|
432
|
+
getShadowMetadata: (
|
|
433
|
+
key: string,
|
|
434
|
+
path: string[],
|
|
435
|
+
validArrayIds?: string[]
|
|
436
|
+
) => {
|
|
437
|
+
const fullKey = [key, ...path].join('.');
|
|
438
|
+
let data = get().shadowStateStore.get(fullKey);
|
|
439
|
+
|
|
318
440
|
return get().shadowStateStore.get(fullKey);
|
|
319
441
|
},
|
|
320
442
|
|
|
321
443
|
setShadowMetadata: (key: string, path: string[], metadata: any) => {
|
|
322
|
-
const fullKey = [key, ...path].join(
|
|
444
|
+
const fullKey = [key, ...path].join('.');
|
|
323
445
|
const newShadowStore = new Map(get().shadowStateStore);
|
|
324
446
|
const existing = newShadowStore.get(fullKey) || { id: ulid() };
|
|
325
447
|
newShadowStore.set(fullKey, { ...existing, ...metadata });
|
|
326
448
|
set({ shadowStateStore: newShadowStore });
|
|
449
|
+
},
|
|
450
|
+
setTransformCache: (
|
|
451
|
+
key: string,
|
|
452
|
+
path: string[],
|
|
453
|
+
cacheKey: string,
|
|
454
|
+
cacheData: any
|
|
455
|
+
) => {
|
|
456
|
+
const fullKey = [key, ...path].join('.');
|
|
457
|
+
const newShadowStore = new Map(get().shadowStateStore);
|
|
458
|
+
const existing = newShadowStore.get(fullKey) || {};
|
|
327
459
|
|
|
328
|
-
if
|
|
329
|
-
|
|
330
|
-
|
|
460
|
+
// Initialize transformCaches if it doesn't exist
|
|
461
|
+
if (!existing.transformCaches) {
|
|
462
|
+
existing.transformCaches = new Map();
|
|
331
463
|
}
|
|
332
|
-
},
|
|
333
464
|
|
|
465
|
+
// Update just the specific cache entry
|
|
466
|
+
existing.transformCaches.set(cacheKey, cacheData);
|
|
467
|
+
|
|
468
|
+
// Update shadow store WITHOUT notifying path subscribers
|
|
469
|
+
newShadowStore.set(fullKey, existing);
|
|
470
|
+
set({ shadowStateStore: newShadowStore });
|
|
471
|
+
|
|
472
|
+
// Don't call notifyPathSubscribers here - cache updates shouldn't trigger renders
|
|
473
|
+
},
|
|
334
474
|
insertShadowArrayElement: (
|
|
335
475
|
key: string,
|
|
336
476
|
arrayPath: string[],
|
|
337
477
|
newItem: any
|
|
338
478
|
) => {
|
|
339
479
|
const newShadowStore = new Map(get().shadowStateStore);
|
|
340
|
-
const arrayKey = [key, ...arrayPath].join(
|
|
480
|
+
const arrayKey = [key, ...arrayPath].join('.');
|
|
341
481
|
const parentMeta = newShadowStore.get(arrayKey);
|
|
342
|
-
const newArrayState = get().getNestedState(key, arrayPath) as any[];
|
|
343
482
|
|
|
344
483
|
if (!parentMeta || !parentMeta.arrayKeys) return;
|
|
345
484
|
|
|
346
|
-
const newItemId = `id:${
|
|
347
|
-
const
|
|
348
|
-
|
|
349
|
-
if (newIndex === -1) return;
|
|
485
|
+
const newItemId = `id:${ulid()}`;
|
|
486
|
+
const fullItemKey = arrayKey + '.' + newItemId;
|
|
350
487
|
|
|
488
|
+
// Just add to the end (or at a specific index if provided)
|
|
351
489
|
const newArrayKeys = [...parentMeta.arrayKeys];
|
|
352
|
-
newArrayKeys.
|
|
490
|
+
newArrayKeys.push(fullItemKey); // Or use splice if you have an index
|
|
353
491
|
newShadowStore.set(arrayKey, { ...parentMeta, arrayKeys: newArrayKeys });
|
|
354
492
|
|
|
493
|
+
// Process the new item - but use the correct logic
|
|
355
494
|
const processNewItem = (value: any, path: string[]) => {
|
|
356
|
-
const nodeKey = [key, ...path].join(
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
495
|
+
const nodeKey = [key, ...path].join('.');
|
|
496
|
+
|
|
497
|
+
if (Array.isArray(value)) {
|
|
498
|
+
// Handle arrays...
|
|
499
|
+
} else if (typeof value === 'object' && value !== null) {
|
|
500
|
+
// Create fields mapping
|
|
501
|
+
const fields = Object.fromEntries(
|
|
502
|
+
Object.keys(value).map((k) => [k, nodeKey + '.' + k])
|
|
503
|
+
);
|
|
504
|
+
newShadowStore.set(nodeKey, { fields });
|
|
505
|
+
|
|
506
|
+
// Process each field
|
|
507
|
+
Object.entries(value).forEach(([k, v]) => {
|
|
508
|
+
processNewItem(v, [...path, k]);
|
|
361
509
|
});
|
|
362
510
|
} else {
|
|
363
|
-
|
|
511
|
+
// Primitive value
|
|
512
|
+
newShadowStore.set(nodeKey, { value });
|
|
364
513
|
}
|
|
365
514
|
};
|
|
366
515
|
|
|
367
516
|
processNewItem(newItem, [...arrayPath, newItemId]);
|
|
368
|
-
|
|
369
517
|
set({ shadowStateStore: newShadowStore });
|
|
370
|
-
},
|
|
371
518
|
|
|
519
|
+
get().notifyPathSubscribers(arrayKey, {
|
|
520
|
+
type: 'INSERT',
|
|
521
|
+
path: arrayKey,
|
|
522
|
+
itemKey: fullItemKey,
|
|
523
|
+
});
|
|
524
|
+
},
|
|
372
525
|
removeShadowArrayElement: (key: string, itemPath: string[]) => {
|
|
373
526
|
const newShadowStore = new Map(get().shadowStateStore);
|
|
374
|
-
const itemKey = [key, ...itemPath].join(".");
|
|
375
|
-
const itemIdToRemove = itemPath[itemPath.length - 1];
|
|
376
527
|
|
|
528
|
+
// Get the full item key (e.g., "stateKey.products.id:xxx")
|
|
529
|
+
const itemKey = [key, ...itemPath].join('.');
|
|
530
|
+
|
|
531
|
+
// Extract parent path and item ID
|
|
377
532
|
const parentPath = itemPath.slice(0, -1);
|
|
378
|
-
const parentKey = [key, ...parentPath].join(
|
|
533
|
+
const parentKey = [key, ...parentPath].join('.');
|
|
534
|
+
|
|
535
|
+
// Get parent metadata
|
|
379
536
|
const parentMeta = newShadowStore.get(parentKey);
|
|
380
537
|
|
|
381
538
|
if (parentMeta && parentMeta.arrayKeys) {
|
|
382
|
-
|
|
383
|
-
|
|
539
|
+
// Find the index of the item to remove
|
|
540
|
+
const indexToRemove = parentMeta.arrayKeys.findIndex(
|
|
541
|
+
(arrayItemKey) => arrayItemKey === itemKey
|
|
384
542
|
);
|
|
385
|
-
newShadowStore.set(parentKey, { ...parentMeta, arrayKeys: newArrayKeys });
|
|
386
|
-
}
|
|
387
543
|
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
544
|
+
if (indexToRemove !== -1) {
|
|
545
|
+
// Create new array keys with the item removed
|
|
546
|
+
const newArrayKeys = parentMeta.arrayKeys.filter(
|
|
547
|
+
(arrayItemKey) => arrayItemKey !== itemKey
|
|
548
|
+
);
|
|
549
|
+
|
|
550
|
+
// Update parent with new array keys
|
|
551
|
+
newShadowStore.set(parentKey, {
|
|
552
|
+
...parentMeta,
|
|
553
|
+
arrayKeys: newArrayKeys,
|
|
554
|
+
});
|
|
555
|
+
|
|
556
|
+
// Delete all data associated with the removed item
|
|
557
|
+
const prefixToDelete = itemKey + '.';
|
|
558
|
+
for (const k of Array.from(newShadowStore.keys())) {
|
|
559
|
+
if (k === itemKey || k.startsWith(prefixToDelete)) {
|
|
560
|
+
newShadowStore.delete(k);
|
|
561
|
+
}
|
|
562
|
+
}
|
|
392
563
|
}
|
|
393
564
|
}
|
|
394
565
|
|
|
395
566
|
set({ shadowStateStore: newShadowStore });
|
|
567
|
+
|
|
568
|
+
get().notifyPathSubscribers(parentKey, {
|
|
569
|
+
type: 'REMOVE',
|
|
570
|
+
path: parentKey,
|
|
571
|
+
itemKey: itemKey, // The exact ID of the removed item
|
|
572
|
+
});
|
|
396
573
|
},
|
|
397
|
-
updateShadowAtPath: (key
|
|
398
|
-
const fullKey = [key, ...path].join(".");
|
|
574
|
+
updateShadowAtPath: (key, path, newValue) => {
|
|
399
575
|
const newShadowStore = new Map(get().shadowStateStore);
|
|
400
|
-
const
|
|
401
|
-
|
|
576
|
+
const fullKey = [key, ...path].join('.');
|
|
577
|
+
|
|
578
|
+
const updateValue = (currentKey: string, valueToSet: any) => {
|
|
579
|
+
const meta = newShadowStore.get(currentKey);
|
|
580
|
+
|
|
581
|
+
// If it's a simple object with fields, update recursively
|
|
582
|
+
if (isSimpleObject(valueToSet) && meta && meta.fields) {
|
|
583
|
+
for (const fieldKey in valueToSet) {
|
|
584
|
+
if (Object.prototype.hasOwnProperty.call(valueToSet, fieldKey)) {
|
|
585
|
+
const childPath = meta.fields[fieldKey];
|
|
586
|
+
const childValue = valueToSet[fieldKey];
|
|
587
|
+
|
|
588
|
+
if (childPath) {
|
|
589
|
+
updateValue(childPath as string, childValue);
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
} else {
|
|
594
|
+
// For primitives (including Uint8Array), just replace the value
|
|
595
|
+
// This gives you useState-like behavior
|
|
596
|
+
const existing = newShadowStore.get(currentKey) || {};
|
|
597
|
+
newShadowStore.set(currentKey, { ...existing, value: valueToSet });
|
|
598
|
+
}
|
|
599
|
+
};
|
|
600
|
+
|
|
601
|
+
updateValue(fullKey, newValue);
|
|
602
|
+
get().notifyPathSubscribers(fullKey, { type: 'UPDATE', newValue });
|
|
402
603
|
set({ shadowStateStore: newShadowStore });
|
|
403
604
|
},
|
|
605
|
+
selectedIndicesMap: new Map<string, string>(),
|
|
606
|
+
getSelectedIndex: (arrayKey: string, validIds?: string[]): number => {
|
|
607
|
+
const itemKey = get().selectedIndicesMap.get(arrayKey);
|
|
608
|
+
|
|
609
|
+
if (!itemKey) return -1;
|
|
404
610
|
|
|
405
|
-
|
|
611
|
+
// Use validIds if provided (for filtered views), otherwise use all arrayKeys
|
|
612
|
+
const arrayKeys =
|
|
613
|
+
validIds ||
|
|
614
|
+
getGlobalStore.getState().getShadowMetadata(arrayKey, [])?.arrayKeys;
|
|
406
615
|
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
if (!stateMap) return undefined;
|
|
411
|
-
return stateMap.get(parentPath);
|
|
616
|
+
if (!arrayKeys) return -1;
|
|
617
|
+
|
|
618
|
+
return arrayKeys.indexOf(itemKey);
|
|
412
619
|
},
|
|
413
620
|
|
|
414
|
-
setSelectedIndex: (
|
|
415
|
-
stateKey: string,
|
|
416
|
-
parentPath: string,
|
|
417
|
-
index: number | undefined
|
|
418
|
-
) => {
|
|
621
|
+
setSelectedIndex: (arrayKey: string, itemKey: string | undefined) => {
|
|
419
622
|
set((state) => {
|
|
420
|
-
const newMap =
|
|
421
|
-
let stateMap = newMap.get(stateKey);
|
|
422
|
-
|
|
423
|
-
if (!stateMap) {
|
|
424
|
-
stateMap = new Map<string, number>();
|
|
425
|
-
newMap.set(stateKey, stateMap);
|
|
426
|
-
}
|
|
623
|
+
const newMap = state.selectedIndicesMap;
|
|
427
624
|
|
|
428
|
-
if (
|
|
429
|
-
|
|
625
|
+
if (itemKey === undefined) {
|
|
626
|
+
newMap.delete(arrayKey);
|
|
430
627
|
} else {
|
|
431
|
-
|
|
432
|
-
|
|
628
|
+
if (newMap.has(arrayKey)) {
|
|
629
|
+
get().notifyPathSubscribers(newMap.get(arrayKey)!, {
|
|
630
|
+
type: 'THIS_UNSELECTED',
|
|
631
|
+
});
|
|
632
|
+
}
|
|
633
|
+
newMap.set(arrayKey, itemKey);
|
|
433
634
|
|
|
635
|
+
get().notifyPathSubscribers(itemKey, {
|
|
636
|
+
type: 'THIS_SELECTED',
|
|
637
|
+
});
|
|
638
|
+
}
|
|
639
|
+
get().notifyPathSubscribers(arrayKey, {
|
|
640
|
+
type: 'GET_SELECTED',
|
|
641
|
+
});
|
|
434
642
|
return {
|
|
435
643
|
...state,
|
|
436
644
|
selectedIndicesMap: newMap,
|
|
437
645
|
};
|
|
438
646
|
});
|
|
439
647
|
},
|
|
440
|
-
clearSelectedIndex: ({
|
|
441
|
-
stateKey,
|
|
442
|
-
path,
|
|
443
|
-
}: {
|
|
444
|
-
stateKey: string;
|
|
445
|
-
path: string[];
|
|
446
|
-
}) => {
|
|
648
|
+
clearSelectedIndex: ({ arrayKey }: { arrayKey: string }): void => {
|
|
447
649
|
set((state) => {
|
|
448
|
-
const newMap =
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
650
|
+
const newMap = state.selectedIndicesMap;
|
|
651
|
+
|
|
652
|
+
newMap.delete(arrayKey);
|
|
653
|
+
get().notifyPathSubscribers(arrayKey, {
|
|
654
|
+
type: 'CLEAR_SELECTION',
|
|
655
|
+
});
|
|
453
656
|
return {
|
|
454
657
|
...state,
|
|
455
658
|
selectedIndicesMap: newMap,
|
|
@@ -461,126 +664,23 @@ export const getGlobalStore = create<CogsGlobalState>((set, get) => ({
|
|
|
461
664
|
const newOuterMap = new Map(state.selectedIndicesMap);
|
|
462
665
|
const changed = newOuterMap.delete(stateKey);
|
|
463
666
|
if (changed) {
|
|
464
|
-
console.log(
|
|
465
|
-
`Cleared selected indices map entry for stateKey: ${stateKey}`
|
|
466
|
-
);
|
|
467
667
|
return { selectedIndicesMap: newOuterMap };
|
|
468
668
|
} else {
|
|
469
669
|
return {};
|
|
470
670
|
}
|
|
471
671
|
});
|
|
472
672
|
},
|
|
473
|
-
stateComponents: new Map(),
|
|
474
|
-
subscribe: (listener: () => void) => {
|
|
475
|
-
// zustand's subscribe returns an unsubscribe function
|
|
476
|
-
return get().subscribe(listener);
|
|
477
|
-
},
|
|
478
|
-
|
|
479
|
-
reactiveDeps: {},
|
|
480
|
-
setReactiveDeps: (key, record) =>
|
|
481
|
-
set((state) => ({
|
|
482
|
-
...state,
|
|
483
|
-
reactiveDeps: {
|
|
484
|
-
...state.reactiveDeps,
|
|
485
|
-
[key]: record,
|
|
486
|
-
},
|
|
487
|
-
})),
|
|
488
|
-
deleteReactiveDeps: (key) =>
|
|
489
|
-
set((state) => {
|
|
490
|
-
const { [key]: _, ...rest } = state.reactiveDeps;
|
|
491
|
-
return {
|
|
492
|
-
...state,
|
|
493
|
-
reactiveDeps: rest,
|
|
494
|
-
};
|
|
495
|
-
}),
|
|
496
|
-
|
|
497
|
-
reRenderTriggerPrevValue: {},
|
|
498
|
-
signalDomElements: new Map(),
|
|
499
|
-
addSignalElement: (
|
|
500
|
-
signalId: string,
|
|
501
|
-
elementInfo: { instanceId: string; parentId: string; position: number }
|
|
502
|
-
) => {
|
|
503
|
-
const current = get().signalDomElements;
|
|
504
|
-
if (!current.has(signalId)) {
|
|
505
|
-
current.set(signalId, new Set());
|
|
506
|
-
}
|
|
507
|
-
current.get(signalId)!.add(elementInfo);
|
|
508
673
|
|
|
509
|
-
set({ signalDomElements: new Map(current) }); // Create new reference to trigger update
|
|
510
|
-
},
|
|
511
|
-
removeSignalElement: (signalId: string, instanceId: string) => {
|
|
512
|
-
const current = get().signalDomElements;
|
|
513
|
-
const elements = current.get(signalId);
|
|
514
|
-
if (elements) {
|
|
515
|
-
elements.forEach((el) => {
|
|
516
|
-
if (el.instanceId === instanceId) {
|
|
517
|
-
elements.delete(el);
|
|
518
|
-
}
|
|
519
|
-
});
|
|
520
|
-
}
|
|
521
|
-
set({ signalDomElements: new Map(current) });
|
|
522
|
-
},
|
|
523
674
|
initialStateOptions: {},
|
|
524
|
-
|
|
675
|
+
|
|
525
676
|
stateTimeline: {},
|
|
526
677
|
cogsStateStore: {},
|
|
527
678
|
stateLog: {},
|
|
528
|
-
isLoadingGlobal: {},
|
|
529
679
|
|
|
530
680
|
initialStateGlobal: {},
|
|
531
|
-
iniitialCreatedState: {},
|
|
532
|
-
updateInitialCreatedState: (key, newState) => {
|
|
533
|
-
set((prev) => ({
|
|
534
|
-
iniitialCreatedState: {
|
|
535
|
-
...prev.iniitialCreatedState,
|
|
536
|
-
[key]: newState,
|
|
537
|
-
},
|
|
538
|
-
}));
|
|
539
|
-
},
|
|
540
681
|
|
|
541
682
|
validationErrors: new Map(),
|
|
542
683
|
|
|
543
|
-
serverState: {},
|
|
544
|
-
|
|
545
|
-
serverSyncActions: {},
|
|
546
|
-
|
|
547
|
-
serverSyncLog: {},
|
|
548
|
-
serverSideOrNot: {},
|
|
549
|
-
setServerSyncLog: (key, newValue) => {
|
|
550
|
-
set((state) => ({
|
|
551
|
-
serverSyncLog: {
|
|
552
|
-
...state.serverSyncLog,
|
|
553
|
-
[key]: [...(state.serverSyncLog[key] ?? []), newValue],
|
|
554
|
-
},
|
|
555
|
-
}));
|
|
556
|
-
},
|
|
557
|
-
setServerSideOrNot: (key, value) => {
|
|
558
|
-
set((state) => ({
|
|
559
|
-
serverSideOrNot: {
|
|
560
|
-
...state.serverSideOrNot,
|
|
561
|
-
[key]: value,
|
|
562
|
-
},
|
|
563
|
-
}));
|
|
564
|
-
},
|
|
565
|
-
getServerSideOrNot: (key) => {
|
|
566
|
-
return get().serverSideOrNot[key];
|
|
567
|
-
},
|
|
568
|
-
|
|
569
|
-
getThisLocalUpdate: (key: string) => {
|
|
570
|
-
return get().stateLog[key];
|
|
571
|
-
},
|
|
572
|
-
setServerState: <StateKey extends StateKeys>(
|
|
573
|
-
key: StateKey,
|
|
574
|
-
value: StateValue
|
|
575
|
-
) => {
|
|
576
|
-
set((prev) => ({
|
|
577
|
-
serverState: {
|
|
578
|
-
...prev.serverState,
|
|
579
|
-
[key]: value,
|
|
580
|
-
},
|
|
581
|
-
}));
|
|
582
|
-
},
|
|
583
|
-
|
|
584
684
|
setStateLog: (
|
|
585
685
|
key: string,
|
|
586
686
|
updater: (prevUpdates: UpdateTypeDetail[]) => UpdateTypeDetail[]
|
|
@@ -596,28 +696,12 @@ export const getGlobalStore = create<CogsGlobalState>((set, get) => ({
|
|
|
596
696
|
};
|
|
597
697
|
});
|
|
598
698
|
},
|
|
599
|
-
|
|
600
|
-
set((prev) => ({
|
|
601
|
-
isLoadingGlobal: {
|
|
602
|
-
...prev.isLoadingGlobal,
|
|
603
|
-
[key]: value,
|
|
604
|
-
},
|
|
605
|
-
}));
|
|
606
|
-
},
|
|
607
|
-
setServerSyncActions: (key: string, value: SyncActionsType<any>) => {
|
|
608
|
-
set((prev) => ({
|
|
609
|
-
serverSyncActions: {
|
|
610
|
-
...prev.serverSyncActions,
|
|
611
|
-
[key]: value,
|
|
612
|
-
},
|
|
613
|
-
}));
|
|
614
|
-
},
|
|
699
|
+
|
|
615
700
|
addValidationError: (path, message) => {
|
|
616
|
-
console.log("addValidationError---");
|
|
617
701
|
set((prev) => {
|
|
618
702
|
const updatedErrors = new Map(prev.validationErrors);
|
|
619
703
|
const existingMessages = updatedErrors.get(path) || [];
|
|
620
|
-
console.log(
|
|
704
|
+
console.log('addValidationError', path, message, existingMessages);
|
|
621
705
|
// Append the new message instead of replacing
|
|
622
706
|
updatedErrors.set(path, [...existingMessages, message]);
|
|
623
707
|
return { validationErrors: updatedErrors };
|
|
@@ -628,9 +712,9 @@ export const getGlobalStore = create<CogsGlobalState>((set, get) => ({
|
|
|
628
712
|
const updatedErrors = new Map(prev.validationErrors);
|
|
629
713
|
|
|
630
714
|
let doSomething = false;
|
|
631
|
-
const pathArray = path.split(
|
|
715
|
+
const pathArray = path.split('.');
|
|
632
716
|
Array.from(updatedErrors.keys()).forEach((key) => {
|
|
633
|
-
const keyArray = key.split(
|
|
717
|
+
const keyArray = key.split('.');
|
|
634
718
|
if (keyArray.length >= pathArray.length) {
|
|
635
719
|
let match = true;
|
|
636
720
|
for (let i = 0; i < pathArray.length; i++) {
|
|
@@ -653,11 +737,11 @@ export const getGlobalStore = create<CogsGlobalState>((set, get) => ({
|
|
|
653
737
|
getValidationErrors: (path: string) => {
|
|
654
738
|
const errors: string[] = [];
|
|
655
739
|
const valErrors = get().validationErrors;
|
|
656
|
-
const pathArray = path.split(
|
|
740
|
+
const pathArray = path.split('.');
|
|
657
741
|
|
|
658
742
|
// Helper to check if an index matches either a wildcard or is in an array of indices
|
|
659
743
|
const isIndexMatch = (pathSegment: string, keySegment: string) => {
|
|
660
|
-
if (pathSegment ===
|
|
744
|
+
if (pathSegment === '[*]') return true;
|
|
661
745
|
if (Array.isArray(pathSegment)) {
|
|
662
746
|
return pathSegment.includes(parseInt(keySegment));
|
|
663
747
|
}
|
|
@@ -665,7 +749,7 @@ export const getGlobalStore = create<CogsGlobalState>((set, get) => ({
|
|
|
665
749
|
};
|
|
666
750
|
|
|
667
751
|
Array.from(valErrors.keys()).forEach((key) => {
|
|
668
|
-
const keyArray = key.split(
|
|
752
|
+
const keyArray = key.split('.');
|
|
669
753
|
if (keyArray.length >= pathArray.length) {
|
|
670
754
|
let match = true;
|
|
671
755
|
for (let i = 0; i < pathArray.length; i++) {
|
|
@@ -673,7 +757,7 @@ export const getGlobalStore = create<CogsGlobalState>((set, get) => ({
|
|
|
673
757
|
const keySegment = keyArray[i]!;
|
|
674
758
|
|
|
675
759
|
// If current path segment is a number or [*], we need special handling
|
|
676
|
-
if (pathSegment ===
|
|
760
|
+
if (pathSegment === '[*]' || Array.isArray(pathSegment)) {
|
|
677
761
|
// Key segment should be a number if we're using [*] or array indices
|
|
678
762
|
const keyIndex = parseInt(keySegment);
|
|
679
763
|
if (isNaN(keyIndex)) {
|
|
@@ -705,48 +789,7 @@ export const getGlobalStore = create<CogsGlobalState>((set, get) => ({
|
|
|
705
789
|
getInitialOptions: (key) => {
|
|
706
790
|
return get().initialStateOptions[key];
|
|
707
791
|
},
|
|
708
|
-
getNestedState: (key: string, path: string[]) => {
|
|
709
|
-
const rootState = get().cogsStateStore[key];
|
|
710
|
-
|
|
711
|
-
const resolvePath = (obj: any, pathArray: string[]): any => {
|
|
712
|
-
if (pathArray.length === 0 || obj === undefined) {
|
|
713
|
-
return obj;
|
|
714
|
-
}
|
|
715
|
-
|
|
716
|
-
const currentSegment = pathArray[0];
|
|
717
|
-
const remainingPath = pathArray.slice(1);
|
|
718
|
-
|
|
719
|
-
// FIX: Handle ID-based array access like 'id:xyz'
|
|
720
|
-
if (
|
|
721
|
-
Array.isArray(obj) &&
|
|
722
|
-
typeof currentSegment === "string" &&
|
|
723
|
-
currentSegment.startsWith("id:")
|
|
724
|
-
) {
|
|
725
|
-
const targetId = currentSegment.split(":")[1];
|
|
726
|
-
const foundItem = obj.find(
|
|
727
|
-
(item) => item && String(item.id) === targetId
|
|
728
|
-
);
|
|
729
|
-
return resolvePath(foundItem, remainingPath);
|
|
730
|
-
}
|
|
731
|
-
|
|
732
|
-
// Handle wildcard array access: '[*]'
|
|
733
|
-
if (currentSegment === "[*]") {
|
|
734
|
-
if (!Array.isArray(obj)) {
|
|
735
|
-
console.warn("Asterisk notation used on non-array value");
|
|
736
|
-
return undefined;
|
|
737
|
-
}
|
|
738
|
-
if (remainingPath.length === 0) return obj;
|
|
739
|
-
const results = obj.map((item) => resolvePath(item, remainingPath));
|
|
740
|
-
return Array.isArray(results[0]) ? results.flat() : results;
|
|
741
|
-
}
|
|
742
|
-
|
|
743
|
-
// Handle standard object property access and numeric array indices
|
|
744
|
-
const nextObj = obj[currentSegment as keyof typeof obj];
|
|
745
|
-
return resolvePath(nextObj, remainingPath);
|
|
746
|
-
};
|
|
747
792
|
|
|
748
|
-
return resolvePath(rootState, path);
|
|
749
|
-
},
|
|
750
793
|
setInitialStateOptions: (key, value) => {
|
|
751
794
|
set((prev) => ({
|
|
752
795
|
initialStateOptions: {
|
|
@@ -763,49 +806,6 @@ export const getGlobalStore = create<CogsGlobalState>((set, get) => ({
|
|
|
763
806
|
},
|
|
764
807
|
}));
|
|
765
808
|
},
|
|
766
|
-
getUpdaterState: (key) => {
|
|
767
|
-
return get().updaterState[key];
|
|
768
|
-
},
|
|
769
|
-
setUpdaterState: (key, newUpdater) => {
|
|
770
|
-
const current = get().updaterState;
|
|
771
|
-
|
|
772
|
-
if (!key || !newUpdater) return;
|
|
773
|
-
|
|
774
|
-
set({ updaterState: { ...(current ?? {}), [key]: newUpdater } });
|
|
775
|
-
},
|
|
776
|
-
getKeyState: <StateKey extends StateKeys>(key: StateKey) => {
|
|
777
|
-
return get().cogsStateStore[key];
|
|
778
|
-
},
|
|
779
|
-
|
|
780
|
-
setState: <StateKey extends StateKeys>(key: StateKey, value: StateValue) => {
|
|
781
|
-
set((prev) => {
|
|
782
|
-
return {
|
|
783
|
-
cogsStateStore: {
|
|
784
|
-
...prev.cogsStateStore,
|
|
785
|
-
[key]:
|
|
786
|
-
typeof value === "function"
|
|
787
|
-
? value(prev.cogsStateStore[key])
|
|
788
|
-
: value,
|
|
789
|
-
},
|
|
790
|
-
};
|
|
791
|
-
});
|
|
792
|
-
},
|
|
793
|
-
setInitialStates: <StateKey extends StateKeys>(initialState: StateValue) => {
|
|
794
|
-
set((prev) => ({
|
|
795
|
-
cogsStateStore: {
|
|
796
|
-
...prev.cogsStateStore,
|
|
797
|
-
...initialState,
|
|
798
|
-
},
|
|
799
|
-
}));
|
|
800
|
-
},
|
|
801
|
-
setCreatedState: (initialState: StateValue) => {
|
|
802
|
-
set((prev) => ({
|
|
803
|
-
iniitialCreatedState: {
|
|
804
|
-
...prev.cogsStateStore,
|
|
805
|
-
...initialState,
|
|
806
|
-
},
|
|
807
|
-
}));
|
|
808
|
-
},
|
|
809
809
|
|
|
810
810
|
syncInfoStore: new Map<string, SyncInfo>(),
|
|
811
811
|
setSyncInfo: (key: string, syncInfo: SyncInfo) =>
|