@voidhash/mimic-react 1.0.0-beta.14 → 1.0.0-beta.15
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/.turbo/turbo-build.log
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
> @voidhash/mimic-react@1.0.0-beta.
|
|
2
|
+
> @voidhash/mimic-react@1.0.0-beta.15 build /home/runner/work/mimic/mimic/packages/mimic-react
|
|
3
3
|
> tsdown
|
|
4
4
|
|
|
5
5
|
[34mℹ[39m tsdown [2mv0.18.2[22m powered by rolldown [2mv1.0.0-beta.55[22m
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
[34mℹ[39m [33m[CJS][39m [2mdist/[22m[32mzustand/middleware.d.cts[39m [2m2.13 kB[22m [2m│ gzip: 0.85 kB[22m
|
|
39
39
|
[34mℹ[39m [33m[CJS][39m [2mdist/[22m[32mzustand/useDraft.d.cts[39m [2m1.54 kB[22m [2m│ gzip: 0.66 kB[22m
|
|
40
40
|
[34mℹ[39m [33m[CJS][39m 15 files, total: 28.71 kB
|
|
41
|
-
[32m✔[39m Build complete in [
|
|
41
|
+
[32m✔[39m Build complete in [32m3497ms[39m
|
|
42
42
|
[34mℹ[39m [34m[ESM][39m [2mdist/[22m[1mzustand-commander/index.mjs[22m [2m 0.53 kB[22m [2m│ gzip: 0.21 kB[22m
|
|
43
43
|
[34mℹ[39m [34m[ESM][39m [2mdist/[22m[1mzustand/index.mjs[22m [2m 0.11 kB[22m [2m│ gzip: 0.09 kB[22m
|
|
44
44
|
[34mℹ[39m [34m[ESM][39m [2mdist/[22m[1mindex.mjs[22m [2m 0.01 kB[22m [2m│ gzip: 0.03 kB[22m
|
|
@@ -73,4 +73,4 @@
|
|
|
73
73
|
[34mℹ[39m [34m[ESM][39m [2mdist/[22m[32mzustand/middleware.d.mts[39m [2m 2.13 kB[22m [2m│ gzip: 0.85 kB[22m
|
|
74
74
|
[34mℹ[39m [34m[ESM][39m [2mdist/[22m[32mzustand/useDraft.d.mts[39m [2m 1.54 kB[22m [2m│ gzip: 0.66 kB[22m
|
|
75
75
|
[34mℹ[39m [34m[ESM][39m 33 files, total: 97.77 kB
|
|
76
|
-
[32m✔[39m Build complete in [
|
|
76
|
+
[32m✔[39m Build complete in [32m3504ms[39m
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@voidhash/mimic-react",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.15",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -37,12 +37,12 @@
|
|
|
37
37
|
"typescript": "5.8.3",
|
|
38
38
|
"vite-tsconfig-paths": "^5.1.4",
|
|
39
39
|
"vitest": "^3.2.4",
|
|
40
|
-
"@voidhash/tsconfig": "1.0.0-beta.
|
|
40
|
+
"@voidhash/tsconfig": "1.0.0-beta.15"
|
|
41
41
|
},
|
|
42
42
|
"peerDependencies": {
|
|
43
43
|
"effect": "^3.16.0",
|
|
44
44
|
"react": "^18.0.0 || ^19.0.0",
|
|
45
|
-
"@voidhash/mimic": "1.0.0-beta.
|
|
45
|
+
"@voidhash/mimic": "1.0.0-beta.15"
|
|
46
46
|
},
|
|
47
47
|
"scripts": {
|
|
48
48
|
"build": "tsdown",
|
|
@@ -668,6 +668,327 @@ describe("Command Dispatch in Context", () => {
|
|
|
668
668
|
});
|
|
669
669
|
});
|
|
670
670
|
|
|
671
|
+
describe("ctx.transaction routing (draft vs document)", () => {
|
|
672
|
+
it("should route ctx.transaction to document.transaction when no draft is active", () => {
|
|
673
|
+
const commander = createCommander<TestState>();
|
|
674
|
+
|
|
675
|
+
// Track calls to document.transaction
|
|
676
|
+
const transactionCalls: Array<(root: any) => void> = [];
|
|
677
|
+
const mockDocument = {
|
|
678
|
+
transaction: (fn: (root: any) => void) => {
|
|
679
|
+
transactionCalls.push(fn);
|
|
680
|
+
},
|
|
681
|
+
};
|
|
682
|
+
|
|
683
|
+
// Create store with mimic slice containing the mock document
|
|
684
|
+
const store = createStore<TestStore & { mimic: { document: typeof mockDocument } }>(
|
|
685
|
+
commander.middleware(() => ({
|
|
686
|
+
count: 0,
|
|
687
|
+
items: [],
|
|
688
|
+
selectedId: null,
|
|
689
|
+
mimic: { document: mockDocument },
|
|
690
|
+
}))
|
|
691
|
+
);
|
|
692
|
+
|
|
693
|
+
// Create a command that uses ctx.transaction
|
|
694
|
+
const updateViaTransaction = commander.action<{ value: number }>(
|
|
695
|
+
(ctx, params) => {
|
|
696
|
+
ctx.transaction((root: any) => {
|
|
697
|
+
root.count.set(params.value);
|
|
698
|
+
});
|
|
699
|
+
}
|
|
700
|
+
);
|
|
701
|
+
|
|
702
|
+
const storeApi = store as unknown as StoreApi<TestStore & { mimic: { document: typeof mockDocument } }>;
|
|
703
|
+
|
|
704
|
+
// Build a proper context with transaction routing
|
|
705
|
+
const ctx: CommandContext<TestStore> = {
|
|
706
|
+
getState: () => storeApi.getState(),
|
|
707
|
+
setState: (partial: Partial<TestStore>) => storeApi.setState(partial as any),
|
|
708
|
+
dispatch: <TParams, TReturn>(cmd: Command<TestStore, TParams, TReturn>) => {
|
|
709
|
+
return (params: TParams): TReturn => cmd.fn(ctx, params);
|
|
710
|
+
},
|
|
711
|
+
transaction: (fn: (root: any) => void) => {
|
|
712
|
+
const state = storeApi.getState();
|
|
713
|
+
const draft = state._commander.activeDraft;
|
|
714
|
+
if (draft) {
|
|
715
|
+
draft.update(fn);
|
|
716
|
+
} else {
|
|
717
|
+
(state as any).mimic.document.transaction(fn);
|
|
718
|
+
}
|
|
719
|
+
},
|
|
720
|
+
};
|
|
721
|
+
|
|
722
|
+
// Execute command - should route to document.transaction
|
|
723
|
+
updateViaTransaction.fn(ctx, { value: 42 });
|
|
724
|
+
|
|
725
|
+
expect(transactionCalls.length).toBe(1);
|
|
726
|
+
});
|
|
727
|
+
|
|
728
|
+
it("should route ctx.transaction to draft.update when draft is active", () => {
|
|
729
|
+
const commander = createCommander<TestState>();
|
|
730
|
+
|
|
731
|
+
// Track calls to both document.transaction and draft.update
|
|
732
|
+
const documentTransactionCalls: Array<(root: any) => void> = [];
|
|
733
|
+
const draftUpdateCalls: Array<(root: any) => void> = [];
|
|
734
|
+
|
|
735
|
+
const mockDocument = {
|
|
736
|
+
transaction: (fn: (root: any) => void) => {
|
|
737
|
+
documentTransactionCalls.push(fn);
|
|
738
|
+
},
|
|
739
|
+
};
|
|
740
|
+
|
|
741
|
+
const mockDraft = {
|
|
742
|
+
update: (fn: (root: any) => void) => {
|
|
743
|
+
draftUpdateCalls.push(fn);
|
|
744
|
+
},
|
|
745
|
+
commit: () => {},
|
|
746
|
+
discard: () => {},
|
|
747
|
+
id: "mock-draft-id",
|
|
748
|
+
};
|
|
749
|
+
|
|
750
|
+
// Create store with mimic slice
|
|
751
|
+
const store = createStore<TestStore & { mimic: { document: typeof mockDocument } }>(
|
|
752
|
+
commander.middleware(() => ({
|
|
753
|
+
count: 0,
|
|
754
|
+
items: [],
|
|
755
|
+
selectedId: null,
|
|
756
|
+
mimic: { document: mockDocument },
|
|
757
|
+
}))
|
|
758
|
+
);
|
|
759
|
+
|
|
760
|
+
// Set the active draft
|
|
761
|
+
store.setState((state) => ({
|
|
762
|
+
...state,
|
|
763
|
+
_commander: {
|
|
764
|
+
...state._commander,
|
|
765
|
+
activeDraft: mockDraft as any,
|
|
766
|
+
},
|
|
767
|
+
}));
|
|
768
|
+
|
|
769
|
+
// Create a command that uses ctx.transaction
|
|
770
|
+
const updateViaTransaction = commander.action<{ value: number }>(
|
|
771
|
+
(ctx, params) => {
|
|
772
|
+
ctx.transaction((root: any) => {
|
|
773
|
+
root.count.set(params.value);
|
|
774
|
+
});
|
|
775
|
+
}
|
|
776
|
+
);
|
|
777
|
+
|
|
778
|
+
const storeApi = store as unknown as StoreApi<TestStore & { mimic: { document: typeof mockDocument } }>;
|
|
779
|
+
|
|
780
|
+
// Build a proper context with transaction routing
|
|
781
|
+
const ctx: CommandContext<TestStore> = {
|
|
782
|
+
getState: () => storeApi.getState(),
|
|
783
|
+
setState: (partial: Partial<TestStore>) => storeApi.setState(partial as any),
|
|
784
|
+
dispatch: <TParams, TReturn>(cmd: Command<TestStore, TParams, TReturn>) => {
|
|
785
|
+
return (params: TParams): TReturn => cmd.fn(ctx, params);
|
|
786
|
+
},
|
|
787
|
+
transaction: (fn: (root: any) => void) => {
|
|
788
|
+
const state = storeApi.getState();
|
|
789
|
+
const draft = state._commander.activeDraft;
|
|
790
|
+
if (draft) {
|
|
791
|
+
draft.update(fn);
|
|
792
|
+
} else {
|
|
793
|
+
(state as any).mimic.document.transaction(fn);
|
|
794
|
+
}
|
|
795
|
+
},
|
|
796
|
+
};
|
|
797
|
+
|
|
798
|
+
// Execute command - should route to draft.update, NOT document.transaction
|
|
799
|
+
updateViaTransaction.fn(ctx, { value: 42 });
|
|
800
|
+
|
|
801
|
+
expect(draftUpdateCalls.length).toBe(1);
|
|
802
|
+
expect(documentTransactionCalls.length).toBe(0);
|
|
803
|
+
});
|
|
804
|
+
|
|
805
|
+
it("should never call document.transaction while draft is active (explicit verification)", () => {
|
|
806
|
+
const commander = createCommander<TestState>();
|
|
807
|
+
|
|
808
|
+
// Track ALL calls
|
|
809
|
+
const documentTransactionCalls: Array<{ fn: (root: any) => void; timestamp: number }> = [];
|
|
810
|
+
const draftUpdateCalls: Array<{ fn: (root: any) => void; timestamp: number }> = [];
|
|
811
|
+
|
|
812
|
+
const mockDocument = {
|
|
813
|
+
transaction: (fn: (root: any) => void) => {
|
|
814
|
+
documentTransactionCalls.push({ fn, timestamp: Date.now() });
|
|
815
|
+
},
|
|
816
|
+
};
|
|
817
|
+
|
|
818
|
+
const mockDraft = {
|
|
819
|
+
update: (fn: (root: any) => void) => {
|
|
820
|
+
draftUpdateCalls.push({ fn, timestamp: Date.now() });
|
|
821
|
+
},
|
|
822
|
+
commit: () => {},
|
|
823
|
+
discard: () => {},
|
|
824
|
+
id: "mock-draft-id",
|
|
825
|
+
};
|
|
826
|
+
|
|
827
|
+
// Create store
|
|
828
|
+
const store = createStore<TestStore & { mimic: { document: typeof mockDocument } }>(
|
|
829
|
+
commander.middleware(() => ({
|
|
830
|
+
count: 0,
|
|
831
|
+
items: [],
|
|
832
|
+
selectedId: null,
|
|
833
|
+
mimic: { document: mockDocument },
|
|
834
|
+
}))
|
|
835
|
+
);
|
|
836
|
+
|
|
837
|
+
const storeApi = store as unknown as StoreApi<TestStore & { mimic: { document: typeof mockDocument } }>;
|
|
838
|
+
|
|
839
|
+
// Helper to build context
|
|
840
|
+
const buildCtx = (): CommandContext<TestStore> => ({
|
|
841
|
+
getState: () => storeApi.getState(),
|
|
842
|
+
setState: (partial: Partial<TestStore>) => storeApi.setState(partial as any),
|
|
843
|
+
dispatch: <TParams, TReturn>(cmd: Command<TestStore, TParams, TReturn>) => {
|
|
844
|
+
return (params: TParams): TReturn => cmd.fn(buildCtx(), params);
|
|
845
|
+
},
|
|
846
|
+
transaction: (fn: (root: any) => void) => {
|
|
847
|
+
const state = storeApi.getState();
|
|
848
|
+
const draft = state._commander.activeDraft;
|
|
849
|
+
if (draft) {
|
|
850
|
+
draft.update(fn);
|
|
851
|
+
} else {
|
|
852
|
+
(state as any).mimic.document.transaction(fn);
|
|
853
|
+
}
|
|
854
|
+
},
|
|
855
|
+
});
|
|
856
|
+
|
|
857
|
+
// Command that uses transaction
|
|
858
|
+
const doUpdate = commander.action<{ value: number }>(
|
|
859
|
+
(ctx, params) => {
|
|
860
|
+
ctx.transaction((root: any) => {
|
|
861
|
+
root.count.set(params.value);
|
|
862
|
+
});
|
|
863
|
+
}
|
|
864
|
+
);
|
|
865
|
+
|
|
866
|
+
// Test 1: No draft - should go to document.transaction
|
|
867
|
+
doUpdate.fn(buildCtx(), { value: 1 });
|
|
868
|
+
expect(documentTransactionCalls.length).toBe(1);
|
|
869
|
+
expect(draftUpdateCalls.length).toBe(0);
|
|
870
|
+
|
|
871
|
+
// Test 2: Set active draft
|
|
872
|
+
store.setState((state) => ({
|
|
873
|
+
...state,
|
|
874
|
+
_commander: {
|
|
875
|
+
...state._commander,
|
|
876
|
+
activeDraft: mockDraft as any,
|
|
877
|
+
},
|
|
878
|
+
}));
|
|
879
|
+
|
|
880
|
+
// Test 3: With draft active - should go to draft.update
|
|
881
|
+
doUpdate.fn(buildCtx(), { value: 2 });
|
|
882
|
+
expect(documentTransactionCalls.length).toBe(1); // Still 1 - no new calls
|
|
883
|
+
expect(draftUpdateCalls.length).toBe(1);
|
|
884
|
+
|
|
885
|
+
// Test 4: Multiple updates while draft is active
|
|
886
|
+
doUpdate.fn(buildCtx(), { value: 3 });
|
|
887
|
+
doUpdate.fn(buildCtx(), { value: 4 });
|
|
888
|
+
doUpdate.fn(buildCtx(), { value: 5 });
|
|
889
|
+
|
|
890
|
+
expect(documentTransactionCalls.length).toBe(1); // Still 1 - no new calls
|
|
891
|
+
expect(draftUpdateCalls.length).toBe(4); // 4 draft updates
|
|
892
|
+
|
|
893
|
+
// Test 5: Clear draft
|
|
894
|
+
store.setState((state) => ({
|
|
895
|
+
...state,
|
|
896
|
+
_commander: {
|
|
897
|
+
...state._commander,
|
|
898
|
+
activeDraft: null,
|
|
899
|
+
},
|
|
900
|
+
}));
|
|
901
|
+
|
|
902
|
+
// Test 6: Without draft - should go back to document.transaction
|
|
903
|
+
doUpdate.fn(buildCtx(), { value: 6 });
|
|
904
|
+
expect(documentTransactionCalls.length).toBe(2);
|
|
905
|
+
expect(draftUpdateCalls.length).toBe(4);
|
|
906
|
+
});
|
|
907
|
+
|
|
908
|
+
it("should route nested command dispatches to draft when active", () => {
|
|
909
|
+
const commander = createCommander<TestState>();
|
|
910
|
+
|
|
911
|
+
const documentTransactionCalls: Array<(root: any) => void> = [];
|
|
912
|
+
const draftUpdateCalls: Array<(root: any) => void> = [];
|
|
913
|
+
|
|
914
|
+
const mockDocument = {
|
|
915
|
+
transaction: (fn: (root: any) => void) => {
|
|
916
|
+
documentTransactionCalls.push(fn);
|
|
917
|
+
},
|
|
918
|
+
};
|
|
919
|
+
|
|
920
|
+
const mockDraft = {
|
|
921
|
+
update: (fn: (root: any) => void) => {
|
|
922
|
+
draftUpdateCalls.push(fn);
|
|
923
|
+
},
|
|
924
|
+
commit: () => {},
|
|
925
|
+
discard: () => {},
|
|
926
|
+
id: "mock-draft-id",
|
|
927
|
+
};
|
|
928
|
+
|
|
929
|
+
const store = createStore<TestStore & { mimic: { document: typeof mockDocument } }>(
|
|
930
|
+
commander.middleware(() => ({
|
|
931
|
+
count: 0,
|
|
932
|
+
items: [],
|
|
933
|
+
selectedId: null,
|
|
934
|
+
mimic: { document: mockDocument },
|
|
935
|
+
}))
|
|
936
|
+
);
|
|
937
|
+
|
|
938
|
+
// Set active draft
|
|
939
|
+
store.setState((state) => ({
|
|
940
|
+
...state,
|
|
941
|
+
_commander: {
|
|
942
|
+
...state._commander,
|
|
943
|
+
activeDraft: mockDraft as any,
|
|
944
|
+
},
|
|
945
|
+
}));
|
|
946
|
+
|
|
947
|
+
const storeApi = store as unknown as StoreApi<TestStore & { mimic: { document: typeof mockDocument } }>;
|
|
948
|
+
|
|
949
|
+
const buildCtx = (): CommandContext<TestStore> => ({
|
|
950
|
+
getState: () => storeApi.getState(),
|
|
951
|
+
setState: (partial: Partial<TestStore>) => storeApi.setState(partial as any),
|
|
952
|
+
dispatch: <TParams, TReturn>(cmd: Command<TestStore, TParams, TReturn>) => {
|
|
953
|
+
return (params: TParams): TReturn => cmd.fn(buildCtx(), params);
|
|
954
|
+
},
|
|
955
|
+
transaction: (fn: (root: any) => void) => {
|
|
956
|
+
const state = storeApi.getState();
|
|
957
|
+
const draft = state._commander.activeDraft;
|
|
958
|
+
if (draft) {
|
|
959
|
+
draft.update(fn);
|
|
960
|
+
} else {
|
|
961
|
+
(state as any).mimic.document.transaction(fn);
|
|
962
|
+
}
|
|
963
|
+
},
|
|
964
|
+
});
|
|
965
|
+
|
|
966
|
+
// Inner command that uses transaction
|
|
967
|
+
const setCount = commander.action<{ value: number }>(
|
|
968
|
+
(ctx, params) => {
|
|
969
|
+
ctx.transaction((root: any) => {
|
|
970
|
+
root.count.set(params.value);
|
|
971
|
+
});
|
|
972
|
+
}
|
|
973
|
+
);
|
|
974
|
+
|
|
975
|
+
// Outer command that dispatches inner command
|
|
976
|
+
const setMultiple = commander.action<{ values: number[] }>(
|
|
977
|
+
(ctx, params) => {
|
|
978
|
+
for (const value of params.values) {
|
|
979
|
+
ctx.dispatch(setCount)({ value });
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
);
|
|
983
|
+
|
|
984
|
+
// Execute outer command - all nested transactions should go to draft
|
|
985
|
+
setMultiple.fn(buildCtx(), { values: [1, 2, 3, 4, 5] });
|
|
986
|
+
|
|
987
|
+
expect(draftUpdateCalls.length).toBe(5);
|
|
988
|
+
expect(documentTransactionCalls.length).toBe(0);
|
|
989
|
+
});
|
|
990
|
+
});
|
|
991
|
+
|
|
671
992
|
describe("Undo/Redo Integration", () => {
|
|
672
993
|
it("should handle multiple undo operations", () => {
|
|
673
994
|
const commander = createCommander<TestState>();
|