@voidhash/mimic-react 0.0.1-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs ADDED
File without changes
@@ -0,0 +1 @@
1
+ export { };
@@ -0,0 +1 @@
1
+ export { };
package/dist/index.mjs ADDED
@@ -0,0 +1 @@
1
+ export { };
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "@voidhash/mimic-react",
3
+ "version": "0.0.1-alpha.2",
4
+ "type": "module",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "https://github.com/voidhashcom/mimic",
8
+ "directory": "packages/mimic-react"
9
+ },
10
+ "main": "./src/index.ts",
11
+ "exports": {
12
+ "./zustand": "./src/zustand/index.ts"
13
+ },
14
+ "dependencies": {
15
+ "zustand": "^5.0.9"
16
+ },
17
+ "devDependencies": {
18
+ "@effect/vitest": "^0.26.0",
19
+ "tsdown": "^0.18.2",
20
+ "typescript": "5.8.3",
21
+ "vite-tsconfig-paths": "^5.1.4",
22
+ "vitest": "^3.2.4",
23
+ "@voidhash/tsconfig": "0.0.1-alpha.2"
24
+ },
25
+ "peerDependencies": {
26
+ "@voidhash/mimic": "0.0.1-alpha.2"
27
+ },
28
+ "scripts": {
29
+ "build": "tsdown",
30
+ "lint": "biome check .",
31
+ "typecheck": "tsc --noEmit",
32
+ "test": "vitest run -c vitest.mts"
33
+ }
34
+ }
package/src/index.ts ADDED
File without changes
@@ -0,0 +1,24 @@
1
+ /**
2
+ * @voidhash/mimic-react/zustand
3
+ *
4
+ * Zustand middleware for integrating mimic ClientDocument with reactive state.
5
+ *
6
+ * @since 0.0.1
7
+ */
8
+
9
+ // =============================================================================
10
+ // Middleware
11
+ // =============================================================================
12
+
13
+ export { mimic } from "./middleware.js";
14
+
15
+ // =============================================================================
16
+ // Types
17
+ // =============================================================================
18
+
19
+ export type {
20
+ MimicObject,
21
+ MimicSlice,
22
+ MimicMiddlewareOptions,
23
+ MimicStateCreator,
24
+ } from "./types.js";
@@ -0,0 +1,171 @@
1
+ import type { StateCreator, StoreMutatorIdentifier } from "zustand";
2
+ import type { ClientDocument } from "@voidhash/mimic/client";
3
+ import type { Primitive, Presence } from "@voidhash/mimic";
4
+ import type {
5
+ MimicSlice,
6
+ MimicObject,
7
+ MimicMiddlewareOptions,
8
+ } from "./types.js";
9
+
10
+ // =============================================================================
11
+ // Middleware Implementation
12
+ // =============================================================================
13
+
14
+ type MimicMiddleware = <
15
+ TSchema extends Primitive.AnyPrimitive,
16
+ TPresence extends Presence.AnyPresence | undefined = undefined,
17
+ T extends object = object,
18
+ Mps extends [StoreMutatorIdentifier, unknown][] = [],
19
+ Mcs extends [StoreMutatorIdentifier, unknown][] = [],
20
+ >(
21
+ document: ClientDocument.ClientDocument<TSchema, TPresence>,
22
+ config: StateCreator<T & MimicSlice<TSchema, TPresence>, Mps, Mcs, T>,
23
+ options?: MimicMiddlewareOptions
24
+ ) => StateCreator<T & MimicSlice<TSchema, TPresence>, Mps, Mcs, T & MimicSlice<TSchema, TPresence>>;
25
+
26
+ type MimicMiddlewareImpl = <
27
+ TSchema extends Primitive.AnyPrimitive,
28
+ TPresence extends Presence.AnyPresence | undefined = undefined,
29
+ T extends object = object,
30
+ >(
31
+ document: ClientDocument.ClientDocument<TSchema, TPresence>,
32
+ config: StateCreator<T & MimicSlice<TSchema, TPresence>, [], [], T>,
33
+ options?: MimicMiddlewareOptions
34
+ ) => StateCreator<T & MimicSlice<TSchema, TPresence>, [], [], T & MimicSlice<TSchema, TPresence>>;
35
+
36
+ /**
37
+ * Creates a MimicObject from the current document state.
38
+ */
39
+ const createMimicObject = <
40
+ TSchema extends Primitive.AnyPrimitive,
41
+ TPresence extends Presence.AnyPresence | undefined = undefined
42
+ >(
43
+ document: ClientDocument.ClientDocument<TSchema, TPresence>
44
+ ): MimicObject<TSchema, TPresence> => {
45
+ const presence = document.presence
46
+ ? {
47
+ selfId: document.presence.selfId(),
48
+ self: document.presence.self(),
49
+ // Important: clone Maps to ensure zustand selectors re-render
50
+ // when presence changes (the underlying ClientDocument mutates Maps in-place).
51
+ others: new Map(document.presence.others()),
52
+ all: new Map(document.presence.all()),
53
+ }
54
+ : undefined;
55
+
56
+ return {
57
+ document,
58
+ snapshot: document.root.toSnapshot() as Primitive.InferSnapshot<TSchema>,
59
+ presence: presence as MimicObject<TSchema, TPresence>["presence"],
60
+ isConnected: document.isConnected(),
61
+ isReady: document.isReady(),
62
+ pendingCount: document.getPendingCount(),
63
+ hasPendingChanges: document.hasPendingChanges(),
64
+ };
65
+ };
66
+
67
+ /**
68
+ * Implementation of the mimic middleware.
69
+ */
70
+ const mimicImpl: MimicMiddlewareImpl = <
71
+ TSchema extends Primitive.AnyPrimitive,
72
+ TPresence extends Presence.AnyPresence | undefined = undefined,
73
+ T extends object = object
74
+ >(
75
+ document: ClientDocument.ClientDocument<TSchema, TPresence>,
76
+ config: any,
77
+ options: MimicMiddlewareOptions = {}
78
+ ) => {
79
+ const { autoSubscribe = true, autoConnect = true } = options;
80
+
81
+ return (set: any, get: any, api: any) => {
82
+ // Create initial mimic slice
83
+ const initialMimic = createMimicObject(document);
84
+
85
+ // Helper to update mimic state
86
+ const updateMimicState = () => {
87
+ const newMimic = createMimicObject(document);
88
+ set(
89
+ (state: any) => ({
90
+ ...state,
91
+ mimic: newMimic,
92
+ }),
93
+ false
94
+ );
95
+ };
96
+
97
+ // Subscribe to document changes
98
+ if (autoSubscribe) {
99
+ document.subscribe({
100
+ onStateChange: () => {
101
+ updateMimicState();
102
+ },
103
+ onConnectionChange: () => {
104
+ updateMimicState();
105
+ },
106
+ onReady: () => {
107
+ updateMimicState();
108
+ },
109
+ });
110
+
111
+ // Subscribe to presence changes (if presence schema is enabled)
112
+ document.presence?.subscribe({
113
+ onPresenceChange: () => {
114
+ updateMimicState();
115
+ },
116
+ });
117
+ }
118
+
119
+ if (autoConnect) {
120
+ document.connect();
121
+ }
122
+
123
+ // Get user's state - pass through set/get/api directly
124
+ // The user's set calls won't affect mimic state since we update it separately
125
+ const userState = config(set, get, api);
126
+
127
+ // Combine user state with mimic slice
128
+ return {
129
+ ...userState,
130
+ mimic: initialMimic,
131
+ };
132
+ };
133
+ };
134
+
135
+ /**
136
+ * Zustand middleware that integrates a ClientDocument.
137
+ *
138
+ * Adds a `mimic` object to the store containing:
139
+ * - `document`: The ClientDocument instance for performing transactions
140
+ * - `snapshot`: Read-only snapshot of the document state (reactive)
141
+ * - `presence`: Reactive presence snapshot (self + others). Undefined if presence is not enabled on the ClientDocument.
142
+ * - `isConnected`: Connection status
143
+ * - `isReady`: Ready status
144
+ * - `pendingCount`: Number of pending transactions
145
+ * - `hasPendingChanges`: Whether there are pending changes
146
+ *
147
+ * @example
148
+ * ```ts
149
+ * import { create } from 'zustand'
150
+ * import { mimic } from '@voidhash/mimic-react/zustand'
151
+ *
152
+ * const useStore = create(
153
+ * mimic(clientDocument, (set, get) => ({
154
+ * // Your additional store state
155
+ * }))
156
+ * )
157
+ *
158
+ * // Read snapshot (reactive)
159
+ * const snapshot = useStore(state => state.mimic.snapshot)
160
+ *
161
+ * // Read presence (reactive, if enabled)
162
+ * const myPresence = useStore(state => state.mimic.presence?.self)
163
+ * const othersPresence = useStore(state => state.mimic.presence?.others)
164
+ *
165
+ * // Write via document
166
+ * store.getState().mimic.document.transaction(root => {
167
+ * root.name.set("New Name")
168
+ * })
169
+ * ```
170
+ */
171
+ export const mimic = mimicImpl as unknown as MimicMiddleware;
@@ -0,0 +1,117 @@
1
+ import type { StateCreator, StoreMutatorIdentifier } from "zustand";
2
+ import type { ClientDocument } from "@voidhash/mimic/client";
3
+ import type { Primitive, Presence } from "@voidhash/mimic";
4
+
5
+ // =============================================================================
6
+ // Mimic State Types
7
+ // =============================================================================
8
+
9
+ /**
10
+ * Presence data exposed on the zustand store (reactive snapshot).
11
+ */
12
+ export interface MimicPresence<TPresence extends Presence.AnyPresence> {
13
+ /**
14
+ * This client's connection ID (set after receiving presence snapshot).
15
+ * Undefined before the snapshot is received.
16
+ */
17
+ readonly selfId: string | undefined;
18
+
19
+ /**
20
+ * This client's current presence data.
21
+ * Undefined if not set.
22
+ */
23
+ readonly self: Presence.Infer<TPresence> | undefined;
24
+
25
+ /**
26
+ * Other clients' presence entries (connectionId -> entry).
27
+ */
28
+ readonly others: ReadonlyMap<
29
+ string,
30
+ Presence.PresenceEntry<Presence.Infer<TPresence>>
31
+ >;
32
+
33
+ /**
34
+ * All presence entries including self (connectionId -> entry).
35
+ */
36
+ readonly all: ReadonlyMap<
37
+ string,
38
+ Presence.PresenceEntry<Presence.Infer<TPresence>>
39
+ >;
40
+ }
41
+
42
+ /**
43
+ * The mimic object containing the document and client state.
44
+ * This is added to the zustand store by the middleware.
45
+ */
46
+ export interface MimicObject<
47
+ TSchema extends Primitive.AnyPrimitive,
48
+ TPresence extends Presence.AnyPresence | undefined = undefined
49
+ > {
50
+ /** The ClientDocument instance for performing transactions */
51
+ readonly document: ClientDocument.ClientDocument<TSchema, TPresence>;
52
+ /** Read-only snapshot of the document state */
53
+ readonly snapshot: Primitive.InferSnapshot<TSchema>;
54
+ /**
55
+ * Reactive presence snapshot (self + others).
56
+ * Undefined when the ClientDocument was created without a presence schema.
57
+ */
58
+ readonly presence: TPresence extends Presence.AnyPresence
59
+ ? MimicPresence<TPresence>
60
+ : undefined;
61
+ /** Whether the client is connected to the server */
62
+ readonly isConnected: boolean;
63
+ /** Whether the client is fully initialized and ready */
64
+ readonly isReady: boolean;
65
+ /** Number of pending transactions */
66
+ readonly pendingCount: number;
67
+ /** Whether there are pending changes */
68
+ readonly hasPendingChanges: boolean;
69
+ }
70
+
71
+ /**
72
+ * The state slice added by the mimic middleware.
73
+ */
74
+ export interface MimicSlice<
75
+ TSchema extends Primitive.AnyPrimitive,
76
+ TPresence extends Presence.AnyPresence | undefined = undefined
77
+ > {
78
+ /** The mimic object containing document and state */
79
+ readonly mimic: MimicObject<TSchema, TPresence>;
80
+ }
81
+
82
+ // =============================================================================
83
+ // Middleware Types
84
+ // =============================================================================
85
+
86
+ /**
87
+ * Type for the mimic middleware mutator.
88
+ */
89
+ export type MimicMutator<
90
+ TSchema extends Primitive.AnyPrimitive,
91
+ TPresence extends Presence.AnyPresence | undefined = undefined
92
+ > = [
93
+ "mimic",
94
+ TSchema,
95
+ TPresence
96
+ ];
97
+
98
+ /**
99
+ * Type for state creator with mimic slice merged.
100
+ */
101
+ export type MimicStateCreator<
102
+ TSchema extends Primitive.AnyPrimitive,
103
+ TPresence extends Presence.AnyPresence | undefined,
104
+ T,
105
+ Mps extends [StoreMutatorIdentifier, unknown][] = [],
106
+ Mcs extends [StoreMutatorIdentifier, unknown][] = [],
107
+ > = StateCreator<T & MimicSlice<TSchema, TPresence>, Mps, Mcs, T>;
108
+
109
+ /**
110
+ * Options for the mimic middleware.
111
+ */
112
+ export interface MimicMiddlewareOptions {
113
+ /** If true, automatically subscribe when store is created (default: true) */
114
+ readonly autoSubscribe?: boolean;
115
+ /** If true, automatically attempt to connect the document to the remote server */
116
+ readonly autoConnect?: boolean;
117
+ }