cogsbox-state 0.5.460 → 0.5.462
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/CogsState.d.ts +8 -8
- package/dist/CogsState.d.ts.map +1 -1
- package/dist/CogsState.jsx +1425 -1468
- package/dist/CogsState.jsx.map +1 -1
- package/dist/TRPCValidationLink.d.ts.map +1 -1
- package/dist/TRPCValidationLink.js.map +1 -1
- package/dist/store.d.ts +5 -4
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +179 -165
- package/dist/store.js.map +1 -1
- package/package.json +1 -1
- package/src/CogsState.tsx +1061 -1128
- package/src/TRPCValidationLink.ts +0 -1
- package/src/store.ts +91 -48
|
@@ -12,7 +12,6 @@ export const useCogsTrpcValidationLink = <
|
|
|
12
12
|
log?: boolean;
|
|
13
13
|
}) => {
|
|
14
14
|
const addValidationError = getGlobalStore.getState().addValidationError;
|
|
15
|
-
|
|
16
15
|
const TrpcValidationLink = (): TRPCLink<TRouter> => {
|
|
17
16
|
return (opts) => {
|
|
18
17
|
return ({ next, op }: { next: any; op: Operation }) => {
|
package/src/store.ts
CHANGED
|
@@ -8,7 +8,7 @@ import type {
|
|
|
8
8
|
UpdateTypeDetail,
|
|
9
9
|
} from './CogsState.js';
|
|
10
10
|
|
|
11
|
-
import type
|
|
11
|
+
import { startTransition, type ReactNode } from 'react';
|
|
12
12
|
|
|
13
13
|
type StateUpdater<StateValue> =
|
|
14
14
|
| StateValue
|
|
@@ -177,6 +177,11 @@ export type CogsEvent =
|
|
|
177
177
|
| { type: 'ITEMHEIGHT'; itemKey: string; height: number } // For full re-initializations (e.g., when a component is removed)
|
|
178
178
|
| { type: 'RELOAD'; path: string }; // For full re-initializations
|
|
179
179
|
export type CogsGlobalState = {
|
|
180
|
+
updateQueue: Set<() => void>;
|
|
181
|
+
isFlushScheduled: boolean;
|
|
182
|
+
|
|
183
|
+
flushUpdates: () => void;
|
|
184
|
+
|
|
180
185
|
// --- Shadow State and Subscription System ---
|
|
181
186
|
registerComponent: (
|
|
182
187
|
stateKey: string,
|
|
@@ -190,6 +195,7 @@ export type CogsGlobalState = {
|
|
|
190
195
|
fullComponentId: string
|
|
191
196
|
) => void;
|
|
192
197
|
shadowStateStore: Map<string, ShadowMetadata>;
|
|
198
|
+
|
|
193
199
|
markAsDirty: (
|
|
194
200
|
key: string,
|
|
195
201
|
path: string[],
|
|
@@ -265,13 +271,10 @@ export type CogsGlobalState = {
|
|
|
265
271
|
|
|
266
272
|
setServerStateUpdate: (key: string, serverState: any) => void;
|
|
267
273
|
|
|
268
|
-
stateLog:
|
|
274
|
+
stateLog: Map<string, Map<string, UpdateTypeDetail>>;
|
|
269
275
|
syncInfoStore: Map<string, SyncInfo>;
|
|
276
|
+
addStateLog: (key: string, update: UpdateTypeDetail) => void;
|
|
270
277
|
|
|
271
|
-
setStateLog: (
|
|
272
|
-
key: string,
|
|
273
|
-
updater: (prevUpdates: UpdateTypeDetail[]) => UpdateTypeDetail[]
|
|
274
|
-
) => void;
|
|
275
278
|
setSyncInfo: (key: string, syncInfo: SyncInfo) => void;
|
|
276
279
|
getSyncInfo: (key: string) => SyncInfo | null;
|
|
277
280
|
};
|
|
@@ -301,6 +304,23 @@ const isSimpleObject = (value: any): boolean => {
|
|
|
301
304
|
return Array.isArray(value) || value.constructor === Object;
|
|
302
305
|
};
|
|
303
306
|
export const getGlobalStore = create<CogsGlobalState>((set, get) => ({
|
|
307
|
+
updateQueue: new Set<() => void>(),
|
|
308
|
+
// A flag to ensure we only schedule the flush once per event-loop tick.
|
|
309
|
+
isFlushScheduled: false,
|
|
310
|
+
|
|
311
|
+
// This function is called by queueMicrotask to execute all queued updates.
|
|
312
|
+
flushUpdates: () => {
|
|
313
|
+
const { updateQueue } = get();
|
|
314
|
+
|
|
315
|
+
if (updateQueue.size > 0) {
|
|
316
|
+
startTransition(() => {
|
|
317
|
+
updateQueue.forEach((updateFn) => updateFn());
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// Clear the queue and reset the flag for the next event loop tick.
|
|
322
|
+
set({ updateQueue: new Set(), isFlushScheduled: false });
|
|
323
|
+
},
|
|
304
324
|
addPathComponent: (stateKey, dependencyPath, fullComponentId) => {
|
|
305
325
|
set((state) => {
|
|
306
326
|
const newShadowStore = new Map(state.shadowStateStore);
|
|
@@ -432,6 +452,7 @@ export const getGlobalStore = create<CogsGlobalState>((set, get) => ({
|
|
|
432
452
|
});
|
|
433
453
|
},
|
|
434
454
|
shadowStateStore: new Map(),
|
|
455
|
+
getShadowNode: (key: string) => get().shadowStateStore.get(key),
|
|
435
456
|
pathSubscribers: new Map<string, Set<(newValue: any) => void>>(),
|
|
436
457
|
|
|
437
458
|
subscribeToPath: (path, callback) => {
|
|
@@ -521,39 +542,57 @@ export const getGlobalStore = create<CogsGlobalState>((set, get) => ({
|
|
|
521
542
|
},
|
|
522
543
|
|
|
523
544
|
getShadowValue: (fullKey: string, validArrayIds?: string[]) => {
|
|
524
|
-
|
|
545
|
+
// The cache is created here. It's temporary and exists only for this one top-level call.
|
|
546
|
+
const memo = new Map<string, any>();
|
|
547
|
+
|
|
548
|
+
// This is the inner recursive function that does the real work.
|
|
549
|
+
const reconstruct = (keyToBuild: string, ids?: string[]): any => {
|
|
550
|
+
// --- STEP 1: Check the cache first ---
|
|
551
|
+
// If we have already built the object for this path *during this call*, return it instantly.
|
|
552
|
+
if (memo.has(keyToBuild)) {
|
|
553
|
+
return memo.get(keyToBuild);
|
|
554
|
+
}
|
|
525
555
|
|
|
526
|
-
|
|
527
|
-
if (!shadowMeta) {
|
|
528
|
-
return undefined;
|
|
529
|
-
}
|
|
556
|
+
const shadowMeta = get().shadowStateStore.get(keyToBuild);
|
|
530
557
|
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
}
|
|
558
|
+
if (!shadowMeta) {
|
|
559
|
+
return undefined;
|
|
560
|
+
}
|
|
535
561
|
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
const items = arrayKeys.map((itemKey) => {
|
|
540
|
-
// RECURSIVELY call getShadowValue for each item
|
|
541
|
-
return get().getShadowValue(itemKey);
|
|
542
|
-
});
|
|
543
|
-
return items;
|
|
544
|
-
}
|
|
562
|
+
if (shadowMeta.value !== undefined) {
|
|
563
|
+
return shadowMeta.value;
|
|
564
|
+
}
|
|
545
565
|
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
//
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
566
|
+
let result: any; // The value we are about to build.
|
|
567
|
+
|
|
568
|
+
if (shadowMeta.arrayKeys) {
|
|
569
|
+
const keys = ids ?? shadowMeta.arrayKeys;
|
|
570
|
+
// --- IMPORTANT: Set the cache BEFORE the recursive calls ---
|
|
571
|
+
// This handles circular references gracefully. We put an empty array in the cache now.
|
|
572
|
+
result = [];
|
|
573
|
+
memo.set(keyToBuild, result);
|
|
574
|
+
// Now, fill the array with the recursively constructed items.
|
|
575
|
+
keys.forEach((itemKey) => {
|
|
576
|
+
result.push(reconstruct(itemKey)); // Pass the memo cache along implicitly via closure
|
|
577
|
+
});
|
|
578
|
+
} else if (shadowMeta.fields) {
|
|
579
|
+
// --- IMPORTANT: Set the cache BEFORE the recursive calls ---
|
|
580
|
+
result = {};
|
|
581
|
+
memo.set(keyToBuild, result);
|
|
582
|
+
// Now, fill the object with the recursively constructed items.
|
|
583
|
+
Object.entries(shadowMeta.fields).forEach(([key, fieldPath]) => {
|
|
584
|
+
result[key] = reconstruct(fieldPath as string);
|
|
585
|
+
});
|
|
586
|
+
} else {
|
|
587
|
+
result = undefined;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
// Return the final, fully populated result.
|
|
591
|
+
return result;
|
|
592
|
+
};
|
|
555
593
|
|
|
556
|
-
|
|
594
|
+
// Start the process by calling the inner function on the root key.
|
|
595
|
+
return reconstruct(fullKey, validArrayIds);
|
|
557
596
|
},
|
|
558
597
|
getShadowMetadata: (
|
|
559
598
|
key: string,
|
|
@@ -837,25 +876,29 @@ export const getGlobalStore = create<CogsGlobalState>((set, get) => ({
|
|
|
837
876
|
|
|
838
877
|
stateTimeline: {},
|
|
839
878
|
cogsStateStore: {},
|
|
840
|
-
stateLog:
|
|
879
|
+
stateLog: new Map(),
|
|
841
880
|
|
|
842
881
|
initialStateGlobal: {},
|
|
843
882
|
|
|
844
883
|
validationErrors: new Map(),
|
|
884
|
+
addStateLog: (key, update) => {
|
|
885
|
+
set((state) => {
|
|
886
|
+
const newLog = new Map(state.stateLog);
|
|
887
|
+
const stateLogForKey = new Map(newLog.get(key));
|
|
888
|
+
const uniquePathKey = JSON.stringify(update.path);
|
|
889
|
+
|
|
890
|
+
const existing = stateLogForKey.get(uniquePathKey);
|
|
891
|
+
if (existing) {
|
|
892
|
+
// If an update for this path already exists, just modify it. (Fast)
|
|
893
|
+
existing.newValue = update.newValue;
|
|
894
|
+
existing.timeStamp = update.timeStamp;
|
|
895
|
+
} else {
|
|
896
|
+
// Otherwise, add the new update. (Fast)
|
|
897
|
+
stateLogForKey.set(uniquePathKey, { ...update });
|
|
898
|
+
}
|
|
845
899
|
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
updater: (prevUpdates: UpdateTypeDetail[]) => UpdateTypeDetail[]
|
|
849
|
-
) => {
|
|
850
|
-
set((prev) => {
|
|
851
|
-
const currentUpdates = prev.stateLog[key] ?? [];
|
|
852
|
-
const newUpdates = updater(currentUpdates);
|
|
853
|
-
return {
|
|
854
|
-
stateLog: {
|
|
855
|
-
...prev.stateLog,
|
|
856
|
-
[key]: newUpdates,
|
|
857
|
-
},
|
|
858
|
-
};
|
|
900
|
+
newLog.set(key, stateLogForKey);
|
|
901
|
+
return { stateLog: newLog };
|
|
859
902
|
});
|
|
860
903
|
},
|
|
861
904
|
|