@voidhash/mimic-react 1.0.0-beta.12 → 1.0.0-beta.13

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.
@@ -1,5 +1,5 @@
1
1
 
2
- > @voidhash/mimic-react@1.0.0-beta.12 build /home/runner/work/mimic/mimic/packages/mimic-react
2
+ > @voidhash/mimic-react@1.0.0-beta.13 build /home/runner/work/mimic/mimic/packages/mimic-react
3
3
  > tsdown
4
4
 
5
5
  ℹ tsdown v0.18.2 powered by rolldown v1.0.0-beta.55
@@ -9,47 +9,53 @@
9
9
  ℹ tsconfig: tsconfig.json
10
10
  ℹ Build start
11
11
  ℹ [CJS] dist/zustand-commander/index.cjs 0.78 kB │ gzip: 0.24 kB
12
- ℹ [CJS] dist/zustand/index.cjs 0.10 kB │ gzip: 0.08 kB
12
+ ℹ [CJS] dist/zustand/index.cjs 0.20 kB │ gzip: 0.11 kB
13
13
  ℹ [CJS] dist/index.cjs 0.00 kB │ gzip: 0.02 kB
14
14
  ℹ [CJS] dist/zustand-commander/commander.cjs 5.39 kB │ gzip: 1.54 kB
15
15
  ℹ [CJS] dist/zustand-commander/hooks.cjs 4.18 kB │ gzip: 1.46 kB
16
- ℹ [CJS] dist/zustand/middleware.cjs 3.06 kB │ gzip: 1.12 kB
16
+ ℹ [CJS] dist/zustand/middleware.cjs 3.17 kB │ gzip: 1.14 kB
17
+ ℹ [CJS] dist/zustand/useDraft.cjs 1.85 kB │ gzip: 0.64 kB
17
18
  ℹ [CJS] dist/_virtual/_@oxc-project_runtime@0.103.0/helpers/objectSpread2.cjs 0.95 kB │ gzip: 0.43 kB
18
19
  ℹ [CJS] dist/zustand-commander/types.cjs 0.91 kB │ gzip: 0.34 kB
19
20
  ℹ [CJS] dist/_virtual/_@oxc-project_runtime@0.103.0/helpers/typeof.cjs 0.53 kB │ gzip: 0.29 kB
20
21
  ℹ [CJS] dist/_virtual/_@oxc-project_runtime@0.103.0/helpers/toPrimitive.cjs 0.52 kB │ gzip: 0.32 kB
21
22
  ℹ [CJS] dist/_virtual/_@oxc-project_runtime@0.103.0/helpers/defineProperty.cjs 0.40 kB │ gzip: 0.24 kB
22
23
  ℹ [CJS] dist/_virtual/_@oxc-project_runtime@0.103.0/helpers/toPropertyKey.cjs 0.37 kB │ gzip: 0.23 kB
23
- ℹ [CJS] 12 files, total: 17.16 kB
24
+ ℹ [CJS] 13 files, total: 19.23 kB
24
25
  ℹ [CJS] dist/zustand-commander/types.d.cts.map 2.75 kB │ gzip: 1.17 kB
25
26
  ℹ [CJS] dist/zustand/types.d.cts.map 1.24 kB │ gzip: 0.59 kB
27
+ ℹ [CJS] dist/zustand/useDraft.d.cts.map 0.60 kB │ gzip: 0.30 kB
26
28
  ℹ [CJS] dist/zustand-commander/hooks.d.cts.map 0.48 kB │ gzip: 0.30 kB
27
29
  ℹ [CJS] dist/zustand/middleware.d.cts.map 0.38 kB │ gzip: 0.23 kB
28
30
  ℹ [CJS] dist/zustand-commander/commander.d.cts.map 0.30 kB │ gzip: 0.20 kB
29
31
  ℹ [CJS] dist/zustand-commander/index.d.cts 1.14 kB │ gzip: 0.36 kB
30
- ℹ [CJS] dist/zustand/index.d.cts 0.24 kB │ gzip: 0.14 kB
32
+ ℹ [CJS] dist/zustand/index.d.cts 0.34 kB │ gzip: 0.17 kB
31
33
  ℹ [CJS] dist/index.d.cts 0.01 kB │ gzip: 0.03 kB
32
34
  ℹ [CJS] dist/zustand-commander/types.d.cts 7.04 kB │ gzip: 2.06 kB
33
- ℹ [CJS] dist/zustand/types.d.cts 3.15 kB │ gzip: 1.02 kB
35
+ ℹ [CJS] dist/zustand/types.d.cts 3.23 kB │ gzip: 1.04 kB
34
36
  ℹ [CJS] dist/zustand-commander/hooks.d.cts 2.52 kB │ gzip: 0.89 kB
35
37
  ℹ [CJS] dist/zustand/middleware.d.cts 2.13 kB │ gzip: 0.85 kB
36
38
  ℹ [CJS] dist/zustand-commander/commander.d.cts 1.63 kB │ gzip: 0.64 kB
37
- ℹ [CJS] 13 files, total: 23.02 kB
38
- ✔ Build complete in 3873ms
39
+ ℹ [CJS] dist/zustand/useDraft.d.cts 1.45 kB │ gzip: 0.61 kB
40
+ ℹ [CJS] 15 files, total: 25.23 kB
41
+ ✔ Build complete in 3602ms
39
42
  ℹ [ESM] dist/zustand-commander/index.mjs  0.47 kB │ gzip: 0.20 kB
40
- ℹ [ESM] dist/zustand/index.mjs  0.06 kB │ gzip: 0.07 kB
43
+ ℹ [ESM] dist/zustand/index.mjs  0.11 kB │ gzip: 0.09 kB
41
44
  ℹ [ESM] dist/index.mjs  0.01 kB │ gzip: 0.03 kB
42
45
  ℹ [ESM] dist/zustand-commander/commander.mjs.map 11.46 kB │ gzip: 3.07 kB
43
46
  ℹ [ESM] dist/zustand-commander/types.mjs.map 10.16 kB │ gzip: 2.60 kB
44
47
  ℹ [ESM] dist/zustand-commander/hooks.mjs.map  9.15 kB │ gzip: 2.82 kB
45
- ℹ [ESM] dist/zustand/middleware.mjs.map  6.71 kB │ gzip: 2.08 kB
48
+ ℹ [ESM] dist/zustand/middleware.mjs.map  6.89 kB │ gzip: 2.11 kB
46
49
  ℹ [ESM] dist/zustand-commander/commander.mjs  5.05 kB │ gzip: 1.54 kB
50
+ ℹ [ESM] dist/zustand/useDraft.mjs.map  4.58 kB │ gzip: 1.56 kB
47
51
  ℹ [ESM] dist/zustand-commander/hooks.mjs  3.95 kB │ gzip: 1.46 kB
48
- ℹ [ESM] dist/zustand/middleware.mjs  3.00 kB │ gzip: 1.13 kB
52
+ ℹ [ESM] dist/zustand/middleware.mjs  3.10 kB │ gzip: 1.15 kB
49
53
  ℹ [ESM] dist/zustand-commander/types.d.mts.map  2.75 kB │ gzip: 1.17 kB
54
+ ℹ [ESM] dist/zustand/useDraft.mjs  1.81 kB │ gzip: 0.66 kB
50
55
  ℹ [ESM] dist/zustand/types.d.mts.map  1.24 kB │ gzip: 0.59 kB
51
56
  ℹ [ESM] dist/_virtual/_@oxc-project_runtime@0.103.0/helpers/objectSpread2.mjs  0.90 kB │ gzip: 0.42 kB
52
57
  ℹ [ESM] dist/zustand-commander/types.mjs  0.84 kB │ gzip: 0.35 kB
58
+ ℹ [ESM] dist/zustand/useDraft.d.mts.map  0.60 kB │ gzip: 0.30 kB
53
59
  ℹ [ESM] dist/zustand-commander/hooks.d.mts.map  0.48 kB │ gzip: 0.30 kB
54
60
  ℹ [ESM] dist/_virtual/_@oxc-project_runtime@0.103.0/helpers/toPrimitive.mjs  0.47 kB │ gzip: 0.30 kB
55
61
  ℹ [ESM] dist/_virtual/_@oxc-project_runtime@0.103.0/helpers/typeof.mjs  0.43 kB │ gzip: 0.24 kB
@@ -58,12 +64,13 @@
58
64
  ℹ [ESM] dist/_virtual/_@oxc-project_runtime@0.103.0/helpers/toPropertyKey.mjs  0.30 kB │ gzip: 0.21 kB
59
65
  ℹ [ESM] dist/zustand-commander/commander.d.mts.map  0.30 kB │ gzip: 0.20 kB
60
66
  ℹ [ESM] dist/zustand-commander/index.d.mts  1.14 kB │ gzip: 0.36 kB
61
- ℹ [ESM] dist/zustand/index.d.mts  0.24 kB │ gzip: 0.14 kB
67
+ ℹ [ESM] dist/zustand/index.d.mts  0.34 kB │ gzip: 0.17 kB
62
68
  ℹ [ESM] dist/index.d.mts  0.01 kB │ gzip: 0.03 kB
63
69
  ℹ [ESM] dist/zustand-commander/types.d.mts  7.04 kB │ gzip: 2.06 kB
64
- ℹ [ESM] dist/zustand/types.d.mts  3.15 kB │ gzip: 1.02 kB
70
+ ℹ [ESM] dist/zustand/types.d.mts  3.23 kB │ gzip: 1.04 kB
65
71
  ℹ [ESM] dist/zustand-commander/hooks.d.mts  2.52 kB │ gzip: 0.89 kB
66
72
  ℹ [ESM] dist/zustand/middleware.d.mts  2.13 kB │ gzip: 0.85 kB
67
73
  ℹ [ESM] dist/zustand-commander/commander.d.mts  1.63 kB │ gzip: 0.64 kB
68
- ℹ [ESM] 29 files, total: 76.36 kB
69
- ✔ Build complete in 3878ms
74
+ ℹ [ESM] dist/zustand/useDraft.d.mts  1.45 kB │ gzip: 0.61 kB
75
+ ℹ [ESM] 33 files, total: 85.29 kB
76
+ ✔ Build complete in 3628ms
@@ -1,3 +1,5 @@
1
1
  const require_middleware = require('./middleware.cjs');
2
+ const require_useDraft = require('./useDraft.cjs');
2
3
 
3
- exports.mimic = require_middleware.mimic;
4
+ exports.mimic = require_middleware.mimic;
5
+ exports.useDraft = require_useDraft.useDraft;
@@ -1,3 +1,4 @@
1
1
  import { MimicMiddlewareOptions, MimicObject, MimicSlice, MimicStateCreator } from "./types.cjs";
2
2
  import { mimic } from "./middleware.cjs";
3
- export { type MimicMiddlewareOptions, type MimicObject, type MimicSlice, type MimicStateCreator, mimic };
3
+ import { UseDraftReturn, useDraft } from "./useDraft.cjs";
4
+ export { type MimicMiddlewareOptions, type MimicObject, type MimicSlice, type MimicStateCreator, type UseDraftReturn, mimic, useDraft };
@@ -1,3 +1,4 @@
1
1
  import { MimicMiddlewareOptions, MimicObject, MimicSlice, MimicStateCreator } from "./types.mjs";
2
2
  import { mimic } from "./middleware.mjs";
3
- export { type MimicMiddlewareOptions, type MimicObject, type MimicSlice, type MimicStateCreator, mimic };
3
+ import { UseDraftReturn, useDraft } from "./useDraft.mjs";
4
+ export { type MimicMiddlewareOptions, type MimicObject, type MimicSlice, type MimicStateCreator, type UseDraftReturn, mimic, useDraft };
@@ -1,3 +1,4 @@
1
1
  import { mimic } from "./middleware.mjs";
2
+ import { useDraft } from "./useDraft.mjs";
2
3
 
3
- export { mimic };
4
+ export { mimic, useDraft };
@@ -18,7 +18,8 @@ const createMimicObject = (document) => {
18
18
  isConnected: document.isConnected(),
19
19
  isReady: document.isReady(),
20
20
  pendingCount: document.getPendingCount(),
21
- hasPendingChanges: document.hasPendingChanges()
21
+ hasPendingChanges: document.hasPendingChanges(),
22
+ activeDraftIds: document.getActiveDraftIds()
22
23
  };
23
24
  };
24
25
  /**
@@ -43,6 +44,9 @@ const mimicImpl = (document, config, options = {}) => {
43
44
  },
44
45
  onReady: () => {
45
46
  updateMimicState();
47
+ },
48
+ onDraftChange: () => {
49
+ updateMimicState();
46
50
  }
47
51
  });
48
52
  (_document$presence = document.presence) === null || _document$presence === void 0 || _document$presence.subscribe({ onPresenceChange: () => {
@@ -1 +1 @@
1
- {"version":3,"file":"middleware.d.cts","names":[],"sources":["../../src/zustand/middleware.ts"],"sourcesContent":[],"mappings":";;;;;;KAaK,eAAA,oBACa,SAAA,CAAU,gCACR,QAAA,CAAS,6EAEd,sDACA,mDAEH,cAAA,CAAe,eAAe,SAAS,oBACzC,aAAa,IAAI,WAAW,SAAS,YAAY,KAAK,KAAK,cACzD,2BACP,aAAa,IAAI,WAAW,SAAS,YAAY,KAAK,KAAK,IAAI,WAAW,SAAS;;AAhBvE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmKjB;;;;;;cAAa,OAAgC"}
1
+ {"version":3,"file":"middleware.d.cts","names":[],"sources":["../../src/zustand/middleware.ts"],"sourcesContent":[],"mappings":";;;;;;KAaK,eAAA,oBACa,SAAA,CAAU,gCACR,QAAA,CAAS,6EAEd,sDACA,mDAEH,cAAA,CAAe,eAAe,SAAS,oBACzC,aAAa,IAAI,WAAW,SAAS,YAAY,KAAK,KAAK,cACzD,2BACP,aAAa,IAAI,WAAW,SAAS,YAAY,KAAK,KAAK,IAAI,WAAW,SAAS;;AAhBvE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuKjB;;;;;;cAAa,OAAgC"}
@@ -1 +1 @@
1
- {"version":3,"file":"middleware.d.mts","names":[],"sources":["../../src/zustand/middleware.ts"],"sourcesContent":[],"mappings":";;;;;;KAaK,eAAA,oBACa,SAAA,CAAU,gCACR,QAAA,CAAS,6EAEd,sDACA,mDAEH,cAAA,CAAe,eAAe,SAAS,oBACzC,aAAa,IAAI,WAAW,SAAS,YAAY,KAAK,KAAK,cACzD,2BACP,aAAa,IAAI,WAAW,SAAS,YAAY,KAAK,KAAK,IAAI,WAAW,SAAS;;AAhBvE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmKjB;;;;;;cAAa,OAAgC"}
1
+ {"version":3,"file":"middleware.d.mts","names":[],"sources":["../../src/zustand/middleware.ts"],"sourcesContent":[],"mappings":";;;;;;KAaK,eAAA,oBACa,SAAA,CAAU,gCACR,QAAA,CAAS,6EAEd,sDACA,mDAEH,cAAA,CAAe,eAAe,SAAS,oBACzC,aAAa,IAAI,WAAW,SAAS,YAAY,KAAK,KAAK,cACzD,2BACP,aAAa,IAAI,WAAW,SAAS,YAAY,KAAK,KAAK,IAAI,WAAW,SAAS;;AAhBvE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuKjB;;;;;;cAAa,OAAgC"}
@@ -18,7 +18,8 @@ const createMimicObject = (document) => {
18
18
  isConnected: document.isConnected(),
19
19
  isReady: document.isReady(),
20
20
  pendingCount: document.getPendingCount(),
21
- hasPendingChanges: document.hasPendingChanges()
21
+ hasPendingChanges: document.hasPendingChanges(),
22
+ activeDraftIds: document.getActiveDraftIds()
22
23
  };
23
24
  };
24
25
  /**
@@ -43,6 +44,9 @@ const mimicImpl = (document, config, options = {}) => {
43
44
  },
44
45
  onReady: () => {
45
46
  updateMimicState();
47
+ },
48
+ onDraftChange: () => {
49
+ updateMimicState();
46
50
  }
47
51
  });
48
52
  (_document$presence = document.presence) === null || _document$presence === void 0 || _document$presence.subscribe({ onPresenceChange: () => {
@@ -1 +1 @@
1
- {"version":3,"file":"middleware.mjs","names":["mimicImpl: MimicMiddlewareImpl"],"sources":["../../src/zustand/middleware.ts"],"sourcesContent":["import type { StateCreator, StoreMutatorIdentifier } from \"zustand\";\nimport type { ClientDocument } from \"@voidhash/mimic/client\";\nimport type { Primitive, Presence } from \"@voidhash/mimic\";\nimport type {\n MimicSlice,\n MimicObject,\n MimicMiddlewareOptions,\n} from \"./types\";\n\n// =============================================================================\n// Middleware Implementation\n// =============================================================================\n\ntype MimicMiddleware = <\n TSchema extends Primitive.AnyPrimitive,\n TPresence extends Presence.AnyPresence | undefined = undefined,\n T extends object = object,\n Mps extends [StoreMutatorIdentifier, unknown][] = [],\n Mcs extends [StoreMutatorIdentifier, unknown][] = [],\n>(\n document: ClientDocument.ClientDocument<TSchema, TPresence>,\n config: StateCreator<T & MimicSlice<TSchema, TPresence>, Mps, Mcs, T>,\n options?: MimicMiddlewareOptions\n) => StateCreator<T & MimicSlice<TSchema, TPresence>, Mps, Mcs, T & MimicSlice<TSchema, TPresence>>;\n\ntype MimicMiddlewareImpl = <\n TSchema extends Primitive.AnyPrimitive,\n TPresence extends Presence.AnyPresence | undefined = undefined,\n T extends object = object,\n>(\n document: ClientDocument.ClientDocument<TSchema, TPresence>,\n config: StateCreator<T & MimicSlice<TSchema, TPresence>, [], [], T>,\n options?: MimicMiddlewareOptions\n) => StateCreator<T & MimicSlice<TSchema, TPresence>, [], [], T & MimicSlice<TSchema, TPresence>>;\n\n/**\n * Creates a MimicObject from the current document state.\n */\nconst createMimicObject = <\n TSchema extends Primitive.AnyPrimitive,\n TPresence extends Presence.AnyPresence | undefined = undefined\n>(\n document: ClientDocument.ClientDocument<TSchema, TPresence>\n): MimicObject<TSchema, TPresence> => {\n const presence = document.presence\n ? {\n selfId: document.presence.selfId(),\n self: document.presence.self(),\n // Important: clone Maps to ensure zustand selectors re-render\n // when presence changes (the underlying ClientDocument mutates Maps in-place).\n others: new Map(document.presence.others()),\n all: new Map(document.presence.all()),\n }\n : undefined;\n\n return {\n document,\n snapshot: document.root.toSnapshot() as Primitive.InferSnapshot<TSchema>,\n presence: presence as MimicObject<TSchema, TPresence>[\"presence\"],\n isConnected: document.isConnected(),\n isReady: document.isReady(),\n pendingCount: document.getPendingCount(),\n hasPendingChanges: document.hasPendingChanges(),\n };\n};\n\n/**\n * Implementation of the mimic middleware.\n */\nconst mimicImpl: MimicMiddlewareImpl = <\n TSchema extends Primitive.AnyPrimitive,\n TPresence extends Presence.AnyPresence | undefined = undefined,\n _T extends object = object\n>(\n document: ClientDocument.ClientDocument<TSchema, TPresence>,\n config: any,\n options: MimicMiddlewareOptions = {}\n) => {\n const { autoSubscribe = true, autoConnect = true } = options;\n\n return (set: any, get: any, api: any) => {\n // Create initial mimic slice\n const initialMimic = createMimicObject(document);\n\n // Helper to update mimic state\n const updateMimicState = () => {\n const newMimic = createMimicObject(document);\n set(\n (state: any) => ({\n ...state,\n mimic: newMimic,\n }),\n false\n );\n };\n\n // Subscribe to document changes\n if (autoSubscribe) {\n document.subscribe({\n onStateChange: () => {\n updateMimicState();\n },\n onConnectionChange: () => {\n updateMimicState();\n },\n onReady: () => {\n updateMimicState();\n },\n });\n\n // Subscribe to presence changes (if presence schema is enabled)\n document.presence?.subscribe({\n onPresenceChange: () => {\n updateMimicState();\n },\n });\n }\n\n if (autoConnect) {\n document.connect();\n }\n\n // Get user's state - pass through set/get/api directly\n // The user's set calls won't affect mimic state since we update it separately\n const userState = config(set, get, api);\n\n // Combine user state with mimic slice\n return {\n ...userState,\n mimic: initialMimic,\n };\n };\n};\n\n/**\n * Zustand middleware that integrates a ClientDocument.\n * \n * Adds a `mimic` object to the store containing:\n * - `document`: The ClientDocument instance for performing transactions\n * - `snapshot`: Read-only snapshot of the document state (reactive)\n * - `presence`: Reactive presence snapshot (self + others). Undefined if presence is not enabled on the ClientDocument.\n * - `isConnected`: Connection status\n * - `isReady`: Ready status\n * - `pendingCount`: Number of pending transactions\n * - `hasPendingChanges`: Whether there are pending changes\n * \n * @example\n * ```ts\n * import { create } from 'zustand'\n * import { mimic } from '@voidhash/mimic-react/zustand'\n * \n * const useStore = create(\n * mimic(clientDocument, (set, get) => ({\n * // Your additional store state\n * }))\n * )\n * \n * // Read snapshot (reactive)\n * const snapshot = useStore(state => state.mimic.snapshot)\n * \n * // Read presence (reactive, if enabled)\n * const myPresence = useStore(state => state.mimic.presence?.self)\n * const othersPresence = useStore(state => state.mimic.presence?.others)\n * \n * // Write via document\n * store.getState().mimic.document.transaction(root => {\n * root.name.set(\"New Name\")\n * })\n * ```\n */\nexport const mimic = mimicImpl as unknown as MimicMiddleware;\n"],"mappings":";;;;;;AAsCA,MAAM,qBAIJ,aACoC;CACpC,MAAM,WAAW,SAAS,WACtB;EACE,QAAQ,SAAS,SAAS,QAAQ;EAClC,MAAM,SAAS,SAAS,MAAM;EAG9B,QAAQ,IAAI,IAAI,SAAS,SAAS,QAAQ,CAAC;EAC3C,KAAK,IAAI,IAAI,SAAS,SAAS,KAAK,CAAC;EACtC,GACD;AAEJ,QAAO;EACL;EACA,UAAU,SAAS,KAAK,YAAY;EAC1B;EACV,aAAa,SAAS,aAAa;EACnC,SAAS,SAAS,SAAS;EAC3B,cAAc,SAAS,iBAAiB;EACxC,mBAAmB,SAAS,mBAAmB;EAChD;;;;;AAMH,MAAMA,aAKJ,UACA,QACA,UAAkC,EAAE,KACjC;CACH,MAAM,EAAE,gBAAgB,MAAM,cAAc,SAAS;AAErD,SAAQ,KAAU,KAAU,QAAa;EAEvC,MAAM,eAAe,kBAAkB,SAAS;EAGhD,MAAM,yBAAyB;GAC7B,MAAM,WAAW,kBAAkB,SAAS;AAC5C,QACG,4CACI,cACH,OAAO,aAET,MACD;;AAIH,MAAI,eAAe;;AACjB,YAAS,UAAU;IACjB,qBAAqB;AACnB,uBAAkB;;IAEpB,0BAA0B;AACxB,uBAAkB;;IAEpB,eAAe;AACb,uBAAkB;;IAErB,CAAC;AAGF,kCAAS,0EAAU,UAAU,EAC3B,wBAAwB;AACtB,sBAAkB;MAErB,CAAC;;AAGJ,MAAI,YACF,UAAS,SAAS;AAQpB,2CAHkB,OAAO,KAAK,KAAK,IAAI,SAKrC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyCb,MAAa,QAAQ"}
1
+ {"version":3,"file":"middleware.mjs","names":["mimicImpl: MimicMiddlewareImpl"],"sources":["../../src/zustand/middleware.ts"],"sourcesContent":["import type { StateCreator, StoreMutatorIdentifier } from \"zustand\";\nimport type { ClientDocument } from \"@voidhash/mimic/client\";\nimport type { Primitive, Presence } from \"@voidhash/mimic\";\nimport type {\n MimicSlice,\n MimicObject,\n MimicMiddlewareOptions,\n} from \"./types\";\n\n// =============================================================================\n// Middleware Implementation\n// =============================================================================\n\ntype MimicMiddleware = <\n TSchema extends Primitive.AnyPrimitive,\n TPresence extends Presence.AnyPresence | undefined = undefined,\n T extends object = object,\n Mps extends [StoreMutatorIdentifier, unknown][] = [],\n Mcs extends [StoreMutatorIdentifier, unknown][] = [],\n>(\n document: ClientDocument.ClientDocument<TSchema, TPresence>,\n config: StateCreator<T & MimicSlice<TSchema, TPresence>, Mps, Mcs, T>,\n options?: MimicMiddlewareOptions\n) => StateCreator<T & MimicSlice<TSchema, TPresence>, Mps, Mcs, T & MimicSlice<TSchema, TPresence>>;\n\ntype MimicMiddlewareImpl = <\n TSchema extends Primitive.AnyPrimitive,\n TPresence extends Presence.AnyPresence | undefined = undefined,\n T extends object = object,\n>(\n document: ClientDocument.ClientDocument<TSchema, TPresence>,\n config: StateCreator<T & MimicSlice<TSchema, TPresence>, [], [], T>,\n options?: MimicMiddlewareOptions\n) => StateCreator<T & MimicSlice<TSchema, TPresence>, [], [], T & MimicSlice<TSchema, TPresence>>;\n\n/**\n * Creates a MimicObject from the current document state.\n */\nconst createMimicObject = <\n TSchema extends Primitive.AnyPrimitive,\n TPresence extends Presence.AnyPresence | undefined = undefined\n>(\n document: ClientDocument.ClientDocument<TSchema, TPresence>\n): MimicObject<TSchema, TPresence> => {\n const presence = document.presence\n ? {\n selfId: document.presence.selfId(),\n self: document.presence.self(),\n // Important: clone Maps to ensure zustand selectors re-render\n // when presence changes (the underlying ClientDocument mutates Maps in-place).\n others: new Map(document.presence.others()),\n all: new Map(document.presence.all()),\n }\n : undefined;\n\n return {\n document,\n snapshot: document.root.toSnapshot() as Primitive.InferSnapshot<TSchema>,\n presence: presence as MimicObject<TSchema, TPresence>[\"presence\"],\n isConnected: document.isConnected(),\n isReady: document.isReady(),\n pendingCount: document.getPendingCount(),\n hasPendingChanges: document.hasPendingChanges(),\n activeDraftIds: document.getActiveDraftIds(),\n };\n};\n\n/**\n * Implementation of the mimic middleware.\n */\nconst mimicImpl: MimicMiddlewareImpl = <\n TSchema extends Primitive.AnyPrimitive,\n TPresence extends Presence.AnyPresence | undefined = undefined,\n _T extends object = object\n>(\n document: ClientDocument.ClientDocument<TSchema, TPresence>,\n config: any,\n options: MimicMiddlewareOptions = {}\n) => {\n const { autoSubscribe = true, autoConnect = true } = options;\n\n return (set: any, get: any, api: any) => {\n // Create initial mimic slice\n const initialMimic = createMimicObject(document);\n\n // Helper to update mimic state\n const updateMimicState = () => {\n const newMimic = createMimicObject(document);\n set(\n (state: any) => ({\n ...state,\n mimic: newMimic,\n }),\n false\n );\n };\n\n // Subscribe to document changes\n if (autoSubscribe) {\n document.subscribe({\n onStateChange: () => {\n updateMimicState();\n },\n onConnectionChange: () => {\n updateMimicState();\n },\n onReady: () => {\n updateMimicState();\n },\n onDraftChange: () => {\n updateMimicState();\n },\n });\n\n // Subscribe to presence changes (if presence schema is enabled)\n document.presence?.subscribe({\n onPresenceChange: () => {\n updateMimicState();\n },\n });\n }\n\n if (autoConnect) {\n document.connect();\n }\n\n // Get user's state - pass through set/get/api directly\n // The user's set calls won't affect mimic state since we update it separately\n const userState = config(set, get, api);\n\n // Combine user state with mimic slice\n return {\n ...userState,\n mimic: initialMimic,\n };\n };\n};\n\n/**\n * Zustand middleware that integrates a ClientDocument.\n * \n * Adds a `mimic` object to the store containing:\n * - `document`: The ClientDocument instance for performing transactions\n * - `snapshot`: Read-only snapshot of the document state (reactive)\n * - `presence`: Reactive presence snapshot (self + others). Undefined if presence is not enabled on the ClientDocument.\n * - `isConnected`: Connection status\n * - `isReady`: Ready status\n * - `pendingCount`: Number of pending transactions\n * - `hasPendingChanges`: Whether there are pending changes\n * \n * @example\n * ```ts\n * import { create } from 'zustand'\n * import { mimic } from '@voidhash/mimic-react/zustand'\n * \n * const useStore = create(\n * mimic(clientDocument, (set, get) => ({\n * // Your additional store state\n * }))\n * )\n * \n * // Read snapshot (reactive)\n * const snapshot = useStore(state => state.mimic.snapshot)\n * \n * // Read presence (reactive, if enabled)\n * const myPresence = useStore(state => state.mimic.presence?.self)\n * const othersPresence = useStore(state => state.mimic.presence?.others)\n * \n * // Write via document\n * store.getState().mimic.document.transaction(root => {\n * root.name.set(\"New Name\")\n * })\n * ```\n */\nexport const mimic = mimicImpl as unknown as MimicMiddleware;\n"],"mappings":";;;;;;AAsCA,MAAM,qBAIJ,aACoC;CACpC,MAAM,WAAW,SAAS,WACtB;EACE,QAAQ,SAAS,SAAS,QAAQ;EAClC,MAAM,SAAS,SAAS,MAAM;EAG9B,QAAQ,IAAI,IAAI,SAAS,SAAS,QAAQ,CAAC;EAC3C,KAAK,IAAI,IAAI,SAAS,SAAS,KAAK,CAAC;EACtC,GACD;AAEJ,QAAO;EACL;EACA,UAAU,SAAS,KAAK,YAAY;EAC1B;EACV,aAAa,SAAS,aAAa;EACnC,SAAS,SAAS,SAAS;EAC3B,cAAc,SAAS,iBAAiB;EACxC,mBAAmB,SAAS,mBAAmB;EAC/C,gBAAgB,SAAS,mBAAmB;EAC7C;;;;;AAMH,MAAMA,aAKJ,UACA,QACA,UAAkC,EAAE,KACjC;CACH,MAAM,EAAE,gBAAgB,MAAM,cAAc,SAAS;AAErD,SAAQ,KAAU,KAAU,QAAa;EAEvC,MAAM,eAAe,kBAAkB,SAAS;EAGhD,MAAM,yBAAyB;GAC7B,MAAM,WAAW,kBAAkB,SAAS;AAC5C,QACG,4CACI,cACH,OAAO,aAET,MACD;;AAIH,MAAI,eAAe;;AACjB,YAAS,UAAU;IACjB,qBAAqB;AACnB,uBAAkB;;IAEpB,0BAA0B;AACxB,uBAAkB;;IAEpB,eAAe;AACb,uBAAkB;;IAEpB,qBAAqB;AACnB,uBAAkB;;IAErB,CAAC;AAGF,kCAAS,0EAAU,UAAU,EAC3B,wBAAwB;AACtB,sBAAkB;MAErB,CAAC;;AAGJ,MAAI,YACF,UAAS,SAAS;AAQpB,2CAHkB,OAAO,KAAK,KAAK,IAAI,SAKrC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyCb,MAAa,QAAQ"}
@@ -49,6 +49,8 @@ interface MimicObject<TSchema extends Primitive.AnyPrimitive, TPresence extends
49
49
  readonly pendingCount: number;
50
50
  /** Whether there are pending changes */
51
51
  readonly hasPendingChanges: boolean;
52
+ /** Set of active draft IDs */
53
+ readonly activeDraftIds: ReadonlySet<string>;
52
54
  }
53
55
  /**
54
56
  * The state slice added by the mimic middleware.
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.cts","names":[],"sources":["../../src/zustand/types.ts"],"sourcesContent":[],"mappings":";;;;;;;;AAWA;AAAiD,UAAhC,aAAyC,CAAA,kBAAT,QAAA,CAAS,WAAA,CAAA,CAAA;EAW1B;;;;EAO5B,SAAS,MAAA,EAAA,MAAA,GAAA,SAAA;EAFM;;;;EAQH,SAAA,IAAA,EAbC,QAAA,CAAS,KAaV,CAbgB,SAahB,CAAA,GAAA,SAAA;EAAW;AAU3B;;EAEoB,SAAS,MAAA,EApBV,WAoBU,CAAA,MAAA,EAlBzB,QAAA,CAAS,aAkBgB,CAlBF,QAAA,CAAS,KAkBP,CAlBa,SAkBb,CAAA,CAAA,CAAA;EAGsB;;;EAEN,SAAA,GAAA,EAjB7B,WAiB6B,CAAA,MAAA,EAfzC,QAAA,CAAS,aAegC,CAflB,QAAA,CAAS,KAeS,CAfH,SAeG,CAAA,CAAA,CAAA;;;;;;AAM1B,UAbF,WAaE,CAAA,gBAZD,SAAA,CAAU,YAYT,EAAA,kBAXC,QAAA,CAAS,WAWV,GAAA,SAAA,GAAA,SAAA,CAAA,CAAA;EAeF;EACC,SAAU,QAAA,EAxBP,cAAA,CAAe,cAwBR,CAxBuB,OAwBvB,EAxBgC,SAwBhC,CAAA;EACR;EAGU,SAAA,QAAA,EA1BT,SAAA,CAAU,aA0BD,CA1Be,OA0Bf,CAAA;EAAS;;;AAsBvC;EACkB,SAAU,QAAA,EA5CP,SA4CO,SA5CW,QAAA,CAAS,WA4CpB,GA3CtB,aA2CsB,CA3CR,SA2CQ,CAAA,GAAA,SAAA;EACR;EAEL,SAAA,WAAA,EAAA,OAAA;EACA;EACE,SAAA,OAAA,EAAA,OAAA;EAAe;EAAS,SAAA,YAAA,EAAA,MAAA;EAApB;EAAgC,SAAA,iBAAA,EAAA,OAAA;;;;;AAKpC,UAtCA,UAsCA,CAAA,gBArCC,SAAA,CAAU,YAqCW,EAAA,kBApCnB,QAAA,CAAS,WAoCU,GAAA,SAAA,GAAA,SAAA,CAAA,CAAA;;kBAjCrB,YAAY,SAAS;;;;;KAsB3B,kCACM,SAAA,CAAU,gCACR,QAAA,CAAS,yCAEd,sDACA,2CACX,aAAa,IAAI,WAAW,SAAS,YAAY,KAAK,KAAK;;;;UAK9C,sBAAA"}
1
+ {"version":3,"file":"types.d.cts","names":[],"sources":["../../src/zustand/types.ts"],"sourcesContent":[],"mappings":";;;;;;;;AAWA;AAAiD,UAAhC,aAAyC,CAAA,kBAAT,QAAA,CAAS,WAAA,CAAA,CAAA;EAW1B;;;;EAO5B,SAAS,MAAA,EAAA,MAAA,GAAA,SAAA;EAFM;;;;EAQH,SAAA,IAAA,EAbC,QAAA,CAAS,KAaV,CAbgB,SAahB,CAAA,GAAA,SAAA;EAAW;AAU3B;;EAEoB,SAAS,MAAA,EApBV,WAoBU,CAAA,MAAA,EAlBzB,QAAA,CAAS,aAkBgB,CAlBF,QAAA,CAAS,KAkBP,CAlBa,SAkBb,CAAA,CAAA,CAAA;EAGsB;;;EAEN,SAAA,GAAA,EAjB7B,WAiB6B,CAAA,MAAA,EAfzC,QAAA,CAAS,aAegC,CAflB,QAAA,CAAS,KAeS,CAfH,SAeG,CAAA,CAAA,CAAA;;;;;;AAiBlB,UAxBV,WAwBU,CAAA,gBAvBT,SAAA,CAAU,YAuBD,EAAA,kBAtBP,QAAA,CAAS,WAsBF,GAAA,SAAA,GAAA,SAAA,CAAA,CAAA;EAAW;EAMrB,SAAA,QAAU,EAzBN,cAAA,CAAe,cAyBT,CAzBwB,OAyBxB,EAzBiC,SAyBjC,CAAA;EACT;EACE,SAAS,QAAA,EAzBR,SAAA,CAAU,aAyBF,CAzBgB,OAyBhB,CAAA;EAGC;;;;EAsBlB,SAAA,QAAA,EA7CS,SA6CQ,SA7CU,QAAA,CAAS,WA6CnB,GA5CvB,aA4CuB,CA5CT,SA4CS,CAAA,GAAA,SAAA;EACX;EACE,SAAS,WAAA,EAAA,OAAA;EAEd;EACA,SAAA,OAAA,EAAA,OAAA;EACE;EAAe,SAAA,YAAA,EAAA,MAAA;EAAS;EAApB,SAAA,iBAAA,EAAA,OAAA;EAAgC;EAAK,SAAA,cAAA,EAvC/B,WAuC+B,CAAA,MAAA,CAAA;;;;AAK1D;UAtCiB,2BACC,SAAA,CAAU,gCACR,QAAA,CAAS;;kBAGX,YAAY,SAAS;;;;;KAsB3B,kCACM,SAAA,CAAU,gCACR,QAAA,CAAS,yCAEd,sDACA,2CACX,aAAa,IAAI,WAAW,SAAS,YAAY,KAAK,KAAK;;;;UAK9C,sBAAA"}
@@ -49,6 +49,8 @@ interface MimicObject<TSchema extends Primitive.AnyPrimitive, TPresence extends
49
49
  readonly pendingCount: number;
50
50
  /** Whether there are pending changes */
51
51
  readonly hasPendingChanges: boolean;
52
+ /** Set of active draft IDs */
53
+ readonly activeDraftIds: ReadonlySet<string>;
52
54
  }
53
55
  /**
54
56
  * The state slice added by the mimic middleware.
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.mts","names":[],"sources":["../../src/zustand/types.ts"],"sourcesContent":[],"mappings":";;;;;;;;AAWA;AAAiD,UAAhC,aAAyC,CAAA,kBAAT,QAAA,CAAS,WAAA,CAAA,CAAA;EAW1B;;;;EAO5B,SAAS,MAAA,EAAA,MAAA,GAAA,SAAA;EAFM;;;;EAQH,SAAA,IAAA,EAbC,QAAA,CAAS,KAaV,CAbgB,SAahB,CAAA,GAAA,SAAA;EAAW;AAU3B;;EAEoB,SAAS,MAAA,EApBV,WAoBU,CAAA,MAAA,EAlBzB,QAAA,CAAS,aAkBgB,CAlBF,QAAA,CAAS,KAkBP,CAlBa,SAkBb,CAAA,CAAA,CAAA;EAGsB;;;EAEN,SAAA,GAAA,EAjB7B,WAiB6B,CAAA,MAAA,EAfzC,QAAA,CAAS,aAegC,CAflB,QAAA,CAAS,KAeS,CAfH,SAeG,CAAA,CAAA,CAAA;;;;;;AAM1B,UAbF,WAaE,CAAA,gBAZD,SAAA,CAAU,YAYT,EAAA,kBAXC,QAAA,CAAS,WAWV,GAAA,SAAA,GAAA,SAAA,CAAA,CAAA;EAeF;EACC,SAAU,QAAA,EAxBP,cAAA,CAAe,cAwBR,CAxBuB,OAwBvB,EAxBgC,SAwBhC,CAAA;EACR;EAGU,SAAA,QAAA,EA1BT,SAAA,CAAU,aA0BD,CA1Be,OA0Bf,CAAA;EAAS;;;AAsBvC;EACkB,SAAU,QAAA,EA5CP,SA4CO,SA5CW,QAAA,CAAS,WA4CpB,GA3CtB,aA2CsB,CA3CR,SA2CQ,CAAA,GAAA,SAAA;EACR;EAEL,SAAA,WAAA,EAAA,OAAA;EACA;EACE,SAAA,OAAA,EAAA,OAAA;EAAe;EAAS,SAAA,YAAA,EAAA,MAAA;EAApB;EAAgC,SAAA,iBAAA,EAAA,OAAA;;;;;AAKpC,UAtCA,UAsCA,CAAA,gBArCC,SAAA,CAAU,YAqCW,EAAA,kBApCnB,QAAA,CAAS,WAoCU,GAAA,SAAA,GAAA,SAAA,CAAA,CAAA;;kBAjCrB,YAAY,SAAS;;;;;KAsB3B,kCACM,SAAA,CAAU,gCACR,QAAA,CAAS,yCAEd,sDACA,2CACX,aAAa,IAAI,WAAW,SAAS,YAAY,KAAK,KAAK;;;;UAK9C,sBAAA"}
1
+ {"version":3,"file":"types.d.mts","names":[],"sources":["../../src/zustand/types.ts"],"sourcesContent":[],"mappings":";;;;;;;;AAWA;AAAiD,UAAhC,aAAyC,CAAA,kBAAT,QAAA,CAAS,WAAA,CAAA,CAAA;EAW1B;;;;EAO5B,SAAS,MAAA,EAAA,MAAA,GAAA,SAAA;EAFM;;;;EAQH,SAAA,IAAA,EAbC,QAAA,CAAS,KAaV,CAbgB,SAahB,CAAA,GAAA,SAAA;EAAW;AAU3B;;EAEoB,SAAS,MAAA,EApBV,WAoBU,CAAA,MAAA,EAlBzB,QAAA,CAAS,aAkBgB,CAlBF,QAAA,CAAS,KAkBP,CAlBa,SAkBb,CAAA,CAAA,CAAA;EAGsB;;;EAEN,SAAA,GAAA,EAjB7B,WAiB6B,CAAA,MAAA,EAfzC,QAAA,CAAS,aAegC,CAflB,QAAA,CAAS,KAeS,CAfH,SAeG,CAAA,CAAA,CAAA;;;;;;AAiBlB,UAxBV,WAwBU,CAAA,gBAvBT,SAAA,CAAU,YAuBD,EAAA,kBAtBP,QAAA,CAAS,WAsBF,GAAA,SAAA,GAAA,SAAA,CAAA,CAAA;EAAW;EAMrB,SAAA,QAAU,EAzBN,cAAA,CAAe,cAyBT,CAzBwB,OAyBxB,EAzBiC,SAyBjC,CAAA;EACT;EACE,SAAS,QAAA,EAzBR,SAAA,CAAU,aAyBF,CAzBgB,OAyBhB,CAAA;EAGC;;;;EAsBlB,SAAA,QAAA,EA7CS,SA6CQ,SA7CU,QAAA,CAAS,WA6CnB,GA5CvB,aA4CuB,CA5CT,SA4CS,CAAA,GAAA,SAAA;EACX;EACE,SAAS,WAAA,EAAA,OAAA;EAEd;EACA,SAAA,OAAA,EAAA,OAAA;EACE;EAAe,SAAA,YAAA,EAAA,MAAA;EAAS;EAApB,SAAA,iBAAA,EAAA,OAAA;EAAgC;EAAK,SAAA,cAAA,EAvC/B,WAuC+B,CAAA,MAAA,CAAA;;;;AAK1D;UAtCiB,2BACC,SAAA,CAAU,gCACR,QAAA,CAAS;;kBAGX,YAAY,SAAS;;;;;KAsB3B,kCACM,SAAA,CAAU,gCACR,QAAA,CAAS,yCAEd,sDACA,2CACX,aAAa,IAAI,WAAW,SAAS,YAAY,KAAK,KAAK;;;;UAK9C,sBAAA"}
@@ -0,0 +1,64 @@
1
+ let react = require("react");
2
+
3
+ //#region src/zustand/useDraft.ts
4
+ /**
5
+ * React hook for managing a draft lifecycle with auto-cleanup on unmount.
6
+ *
7
+ * @param store - The zustand store containing the mimic slice
8
+ * @returns Draft control methods
9
+ */
10
+ const useDraft = (store) => {
11
+ const draftRef = (0, react.useRef)(null);
12
+ const versionRef = (0, react.useRef)(0);
13
+ const subscribe = (0, react.useCallback)((onStoreChange) => {
14
+ return store.subscribe(onStoreChange);
15
+ }, [store]);
16
+ const getSnapshot = (0, react.useCallback)(() => {
17
+ return versionRef.current;
18
+ }, []);
19
+ (0, react.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
20
+ const bumpVersion = (0, react.useCallback)(() => {
21
+ versionRef.current++;
22
+ }, []);
23
+ const begin = (0, react.useCallback)(() => {
24
+ if (draftRef.current !== null) throw new Error("A draft is already active. Commit or discard it first.");
25
+ draftRef.current = store.getState().mimic.document.createDraft();
26
+ bumpVersion();
27
+ }, [store, bumpVersion]);
28
+ const commit = (0, react.useCallback)(() => {
29
+ if (draftRef.current === null) return;
30
+ draftRef.current.commit();
31
+ draftRef.current = null;
32
+ bumpVersion();
33
+ }, [bumpVersion]);
34
+ const discard = (0, react.useCallback)(() => {
35
+ if (draftRef.current === null) return;
36
+ draftRef.current.discard();
37
+ draftRef.current = null;
38
+ bumpVersion();
39
+ }, [bumpVersion]);
40
+ const update = (0, react.useCallback)((fn) => {
41
+ if (draftRef.current === null) throw new Error("No active draft. Call begin() first.");
42
+ draftRef.current.update(fn);
43
+ }, []);
44
+ (0, react.useEffect)(() => {
45
+ return () => {
46
+ if (draftRef.current !== null) {
47
+ try {
48
+ draftRef.current.discard();
49
+ } catch (_unused) {}
50
+ draftRef.current = null;
51
+ }
52
+ };
53
+ }, []);
54
+ return {
55
+ draft: draftRef.current,
56
+ begin,
57
+ commit,
58
+ discard,
59
+ update
60
+ };
61
+ };
62
+
63
+ //#endregion
64
+ exports.useDraft = useDraft;
@@ -0,0 +1,32 @@
1
+ import { MimicSlice } from "./types.cjs";
2
+ import { StoreApi } from "zustand";
3
+ import { ClientDocument } from "@voidhash/mimic/client";
4
+ import * as _voidhash_mimic0 from "@voidhash/mimic";
5
+ import { Primitive } from "@voidhash/mimic";
6
+
7
+ //#region src/zustand/useDraft.d.ts
8
+ /**
9
+ * Return type of the useDraft hook.
10
+ */
11
+ interface UseDraftReturn<TSchema extends Primitive.AnyPrimitive> {
12
+ /** The active draft handle, or null if no draft is active */
13
+ readonly draft: ClientDocument.DraftHandle<TSchema> | null;
14
+ /** Creates a new draft. Throws if a draft is already active. */
15
+ readonly begin: () => void;
16
+ /** Commits the active draft. No-op if no draft is active. */
17
+ readonly commit: () => void;
18
+ /** Discards the active draft. No-op if no draft is active. */
19
+ readonly discard: () => void;
20
+ /** Runs an update on the active draft. Throws if no draft is active. */
21
+ readonly update: (fn: (root: Primitive.InferProxy<TSchema>) => void) => void;
22
+ }
23
+ /**
24
+ * React hook for managing a draft lifecycle with auto-cleanup on unmount.
25
+ *
26
+ * @param store - The zustand store containing the mimic slice
27
+ * @returns Draft control methods
28
+ */
29
+ declare const useDraft: <TSchema extends Primitive.AnyPrimitive, TPresence extends _voidhash_mimic0.Presence.AnyPresence | undefined = undefined>(store: StoreApi<MimicSlice<TSchema, TPresence>>) => UseDraftReturn<TSchema>;
30
+ //#endregion
31
+ export { UseDraftReturn, useDraft };
32
+ //# sourceMappingURL=useDraft.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useDraft.d.cts","names":[],"sources":["../../src/zustand/useDraft.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;AAUiB,UAAA,cAAc,CAAA,gBAAiB,SAAA,CAAU,YAA3B,CAAA,CAAA;EAAiB;EAEH,SAAA,KAAA,EAA3B,cAAA,CAAe,WAAY,CAAA,OAAA,CAAA,GAAA,IAAA;EAA3B;EAQkC,SAAA,KAAA,EAAA,GAAA,GAAA,IAAA;EAArB;EAAoB,SAAA,MAAA,EAAA,GAAA,GAAA,IAAA;EAStC;EACK,SAAU,OAAA,EAAA,GAAA,GAAA,IAAA;EACkB;EAEjB,SAAA,MAAA,EAAA,CAAA,EAAA,EAAA,CAAA,IAAA,EAbE,SAAA,CAAU,UAaZ,CAbuB,OAavB,CAAA,EAAA,GAAA,IAAA,EAAA,GAAA,IAAA;;;;;;;;cAJhB,2BACK,SAAA,CAAU,gCACkB,gBAAA,CAAA,QAAA,CAAS,WAAA,iCAE9C,SAAS,WAAW,SAAS,gBACnC,eAAe"}
@@ -0,0 +1,32 @@
1
+ import { MimicSlice } from "./types.mjs";
2
+ import { StoreApi } from "zustand";
3
+ import { ClientDocument } from "@voidhash/mimic/client";
4
+ import * as _voidhash_mimic0 from "@voidhash/mimic";
5
+ import { Primitive } from "@voidhash/mimic";
6
+
7
+ //#region src/zustand/useDraft.d.ts
8
+ /**
9
+ * Return type of the useDraft hook.
10
+ */
11
+ interface UseDraftReturn<TSchema extends Primitive.AnyPrimitive> {
12
+ /** The active draft handle, or null if no draft is active */
13
+ readonly draft: ClientDocument.DraftHandle<TSchema> | null;
14
+ /** Creates a new draft. Throws if a draft is already active. */
15
+ readonly begin: () => void;
16
+ /** Commits the active draft. No-op if no draft is active. */
17
+ readonly commit: () => void;
18
+ /** Discards the active draft. No-op if no draft is active. */
19
+ readonly discard: () => void;
20
+ /** Runs an update on the active draft. Throws if no draft is active. */
21
+ readonly update: (fn: (root: Primitive.InferProxy<TSchema>) => void) => void;
22
+ }
23
+ /**
24
+ * React hook for managing a draft lifecycle with auto-cleanup on unmount.
25
+ *
26
+ * @param store - The zustand store containing the mimic slice
27
+ * @returns Draft control methods
28
+ */
29
+ declare const useDraft: <TSchema extends Primitive.AnyPrimitive, TPresence extends _voidhash_mimic0.Presence.AnyPresence | undefined = undefined>(store: StoreApi<MimicSlice<TSchema, TPresence>>) => UseDraftReturn<TSchema>;
30
+ //#endregion
31
+ export { UseDraftReturn, useDraft };
32
+ //# sourceMappingURL=useDraft.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useDraft.d.mts","names":[],"sources":["../../src/zustand/useDraft.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;AAUiB,UAAA,cAAc,CAAA,gBAAiB,SAAA,CAAU,YAA3B,CAAA,CAAA;EAAiB;EAEH,SAAA,KAAA,EAA3B,cAAA,CAAe,WAAY,CAAA,OAAA,CAAA,GAAA,IAAA;EAA3B;EAQkC,SAAA,KAAA,EAAA,GAAA,GAAA,IAAA;EAArB;EAAoB,SAAA,MAAA,EAAA,GAAA,GAAA,IAAA;EAStC;EACK,SAAU,OAAA,EAAA,GAAA,GAAA,IAAA;EACkB;EAEjB,SAAA,MAAA,EAAA,CAAA,EAAA,EAAA,CAAA,IAAA,EAbE,SAAA,CAAU,UAaZ,CAbuB,OAavB,CAAA,EAAA,GAAA,IAAA,EAAA,GAAA,IAAA;;;;;;;;cAJhB,2BACK,SAAA,CAAU,gCACkB,gBAAA,CAAA,QAAA,CAAS,WAAA,iCAE9C,SAAS,WAAW,SAAS,gBACnC,eAAe"}
@@ -0,0 +1,65 @@
1
+ import { useCallback, useEffect, useRef, useSyncExternalStore } from "react";
2
+
3
+ //#region src/zustand/useDraft.ts
4
+ /**
5
+ * React hook for managing a draft lifecycle with auto-cleanup on unmount.
6
+ *
7
+ * @param store - The zustand store containing the mimic slice
8
+ * @returns Draft control methods
9
+ */
10
+ const useDraft = (store) => {
11
+ const draftRef = useRef(null);
12
+ const versionRef = useRef(0);
13
+ const subscribe = useCallback((onStoreChange) => {
14
+ return store.subscribe(onStoreChange);
15
+ }, [store]);
16
+ const getSnapshot = useCallback(() => {
17
+ return versionRef.current;
18
+ }, []);
19
+ useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
20
+ const bumpVersion = useCallback(() => {
21
+ versionRef.current++;
22
+ }, []);
23
+ const begin = useCallback(() => {
24
+ if (draftRef.current !== null) throw new Error("A draft is already active. Commit or discard it first.");
25
+ draftRef.current = store.getState().mimic.document.createDraft();
26
+ bumpVersion();
27
+ }, [store, bumpVersion]);
28
+ const commit = useCallback(() => {
29
+ if (draftRef.current === null) return;
30
+ draftRef.current.commit();
31
+ draftRef.current = null;
32
+ bumpVersion();
33
+ }, [bumpVersion]);
34
+ const discard = useCallback(() => {
35
+ if (draftRef.current === null) return;
36
+ draftRef.current.discard();
37
+ draftRef.current = null;
38
+ bumpVersion();
39
+ }, [bumpVersion]);
40
+ const update = useCallback((fn) => {
41
+ if (draftRef.current === null) throw new Error("No active draft. Call begin() first.");
42
+ draftRef.current.update(fn);
43
+ }, []);
44
+ useEffect(() => {
45
+ return () => {
46
+ if (draftRef.current !== null) {
47
+ try {
48
+ draftRef.current.discard();
49
+ } catch (_unused) {}
50
+ draftRef.current = null;
51
+ }
52
+ };
53
+ }, []);
54
+ return {
55
+ draft: draftRef.current,
56
+ begin,
57
+ commit,
58
+ discard,
59
+ update
60
+ };
61
+ };
62
+
63
+ //#endregion
64
+ export { useDraft };
65
+ //# sourceMappingURL=useDraft.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useDraft.mjs","names":[],"sources":["../../src/zustand/useDraft.ts"],"sourcesContent":["import { useRef, useCallback, useEffect } from \"react\";\nimport { useSyncExternalStore } from \"react\";\nimport type { StoreApi } from \"zustand\";\nimport type { ClientDocument } from \"@voidhash/mimic/client\";\nimport type { Primitive } from \"@voidhash/mimic\";\nimport type { MimicSlice } from \"./types\";\n\n/**\n * Return type of the useDraft hook.\n */\nexport interface UseDraftReturn<TSchema extends Primitive.AnyPrimitive> {\n /** The active draft handle, or null if no draft is active */\n readonly draft: ClientDocument.DraftHandle<TSchema> | null;\n /** Creates a new draft. Throws if a draft is already active. */\n readonly begin: () => void;\n /** Commits the active draft. No-op if no draft is active. */\n readonly commit: () => void;\n /** Discards the active draft. No-op if no draft is active. */\n readonly discard: () => void;\n /** Runs an update on the active draft. Throws if no draft is active. */\n readonly update: (fn: (root: Primitive.InferProxy<TSchema>) => void) => void;\n}\n\n/**\n * React hook for managing a draft lifecycle with auto-cleanup on unmount.\n *\n * @param store - The zustand store containing the mimic slice\n * @returns Draft control methods\n */\nexport const useDraft = <\n TSchema extends Primitive.AnyPrimitive,\n TPresence extends import(\"@voidhash/mimic\").Presence.AnyPresence | undefined = undefined,\n>(\n store: StoreApi<MimicSlice<TSchema, TPresence>>\n): UseDraftReturn<TSchema> => {\n const draftRef = useRef<ClientDocument.DraftHandle<TSchema> | null>(null);\n // Use a counter to force re-renders when draft state changes\n const versionRef = useRef(0);\n\n const subscribe = useCallback(\n (onStoreChange: () => void) => {\n // We piggyback on the zustand store subscription to know when drafts change\n return store.subscribe(onStoreChange);\n },\n [store]\n );\n\n const getSnapshot = useCallback(() => {\n return versionRef.current;\n }, []);\n\n useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n\n const bumpVersion = useCallback(() => {\n versionRef.current++;\n }, []);\n\n const begin = useCallback(() => {\n if (draftRef.current !== null) {\n throw new Error(\"A draft is already active. Commit or discard it first.\");\n }\n const document = store.getState().mimic.document;\n draftRef.current = document.createDraft();\n bumpVersion();\n }, [store, bumpVersion]);\n\n const commit = useCallback(() => {\n if (draftRef.current === null) return;\n draftRef.current.commit();\n draftRef.current = null;\n bumpVersion();\n }, [bumpVersion]);\n\n const discard = useCallback(() => {\n if (draftRef.current === null) return;\n draftRef.current.discard();\n draftRef.current = null;\n bumpVersion();\n }, [bumpVersion]);\n\n const update = useCallback(\n (fn: (root: Primitive.InferProxy<TSchema>) => void) => {\n if (draftRef.current === null) {\n throw new Error(\"No active draft. Call begin() first.\");\n }\n draftRef.current.update(fn);\n },\n []\n );\n\n // Auto-discard on unmount\n useEffect(() => {\n return () => {\n if (draftRef.current !== null) {\n try {\n draftRef.current.discard();\n } catch {\n // Draft may already be consumed\n }\n draftRef.current = null;\n }\n };\n }, []);\n\n return {\n draft: draftRef.current,\n begin,\n commit,\n discard,\n update,\n };\n};\n"],"mappings":";;;;;;;;;AA6BA,MAAa,YAIX,UAC4B;CAC5B,MAAM,WAAW,OAAmD,KAAK;CAEzE,MAAM,aAAa,OAAO,EAAE;CAE5B,MAAM,YAAY,aACf,kBAA8B;AAE7B,SAAO,MAAM,UAAU,cAAc;IAEvC,CAAC,MAAM,CACR;CAED,MAAM,cAAc,kBAAkB;AACpC,SAAO,WAAW;IACjB,EAAE,CAAC;AAEN,sBAAqB,WAAW,aAAa,YAAY;CAEzD,MAAM,cAAc,kBAAkB;AACpC,aAAW;IACV,EAAE,CAAC;CAEN,MAAM,QAAQ,kBAAkB;AAC9B,MAAI,SAAS,YAAY,KACvB,OAAM,IAAI,MAAM,yDAAyD;AAG3E,WAAS,UADQ,MAAM,UAAU,CAAC,MAAM,SACZ,aAAa;AACzC,eAAa;IACZ,CAAC,OAAO,YAAY,CAAC;CAExB,MAAM,SAAS,kBAAkB;AAC/B,MAAI,SAAS,YAAY,KAAM;AAC/B,WAAS,QAAQ,QAAQ;AACzB,WAAS,UAAU;AACnB,eAAa;IACZ,CAAC,YAAY,CAAC;CAEjB,MAAM,UAAU,kBAAkB;AAChC,MAAI,SAAS,YAAY,KAAM;AAC/B,WAAS,QAAQ,SAAS;AAC1B,WAAS,UAAU;AACnB,eAAa;IACZ,CAAC,YAAY,CAAC;CAEjB,MAAM,SAAS,aACZ,OAAsD;AACrD,MAAI,SAAS,YAAY,KACvB,OAAM,IAAI,MAAM,uCAAuC;AAEzD,WAAS,QAAQ,OAAO,GAAG;IAE7B,EAAE,CACH;AAGD,iBAAgB;AACd,eAAa;AACX,OAAI,SAAS,YAAY,MAAM;AAC7B,QAAI;AACF,cAAS,QAAQ,SAAS;sBACpB;AAGR,aAAS,UAAU;;;IAGtB,EAAE,CAAC;AAEN,QAAO;EACL,OAAO,SAAS;EAChB;EACA;EACA;EACA;EACD"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voidhash/mimic-react",
3
- "version": "1.0.0-beta.12",
3
+ "version": "1.0.0-beta.13",
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.12"
40
+ "@voidhash/tsconfig": "1.0.0-beta.13"
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.12"
45
+ "@voidhash/mimic": "1.0.0-beta.13"
46
46
  },
47
47
  "scripts": {
48
48
  "build": "tsdown",
@@ -11,6 +11,7 @@
11
11
  // =============================================================================
12
12
 
13
13
  export { mimic } from "./middleware";
14
+ export { useDraft } from "./useDraft";
14
15
 
15
16
  // =============================================================================
16
17
  // Types
@@ -22,3 +23,5 @@ export type {
22
23
  MimicMiddlewareOptions,
23
24
  MimicStateCreator,
24
25
  } from "./types";
26
+
27
+ export type { UseDraftReturn } from "./useDraft";
@@ -61,6 +61,7 @@ const createMimicObject = <
61
61
  isReady: document.isReady(),
62
62
  pendingCount: document.getPendingCount(),
63
63
  hasPendingChanges: document.hasPendingChanges(),
64
+ activeDraftIds: document.getActiveDraftIds(),
64
65
  };
65
66
  };
66
67
 
@@ -106,6 +107,9 @@ const mimicImpl: MimicMiddlewareImpl = <
106
107
  onReady: () => {
107
108
  updateMimicState();
108
109
  },
110
+ onDraftChange: () => {
111
+ updateMimicState();
112
+ },
109
113
  });
110
114
 
111
115
  // Subscribe to presence changes (if presence schema is enabled)
@@ -66,6 +66,8 @@ export interface MimicObject<
66
66
  readonly pendingCount: number;
67
67
  /** Whether there are pending changes */
68
68
  readonly hasPendingChanges: boolean;
69
+ /** Set of active draft IDs */
70
+ readonly activeDraftIds: ReadonlySet<string>;
69
71
  }
70
72
 
71
73
  /**
@@ -0,0 +1,112 @@
1
+ import { useRef, useCallback, useEffect } from "react";
2
+ import { useSyncExternalStore } from "react";
3
+ import type { StoreApi } from "zustand";
4
+ import type { ClientDocument } from "@voidhash/mimic/client";
5
+ import type { Primitive } from "@voidhash/mimic";
6
+ import type { MimicSlice } from "./types";
7
+
8
+ /**
9
+ * Return type of the useDraft hook.
10
+ */
11
+ export interface UseDraftReturn<TSchema extends Primitive.AnyPrimitive> {
12
+ /** The active draft handle, or null if no draft is active */
13
+ readonly draft: ClientDocument.DraftHandle<TSchema> | null;
14
+ /** Creates a new draft. Throws if a draft is already active. */
15
+ readonly begin: () => void;
16
+ /** Commits the active draft. No-op if no draft is active. */
17
+ readonly commit: () => void;
18
+ /** Discards the active draft. No-op if no draft is active. */
19
+ readonly discard: () => void;
20
+ /** Runs an update on the active draft. Throws if no draft is active. */
21
+ readonly update: (fn: (root: Primitive.InferProxy<TSchema>) => void) => void;
22
+ }
23
+
24
+ /**
25
+ * React hook for managing a draft lifecycle with auto-cleanup on unmount.
26
+ *
27
+ * @param store - The zustand store containing the mimic slice
28
+ * @returns Draft control methods
29
+ */
30
+ export const useDraft = <
31
+ TSchema extends Primitive.AnyPrimitive,
32
+ TPresence extends import("@voidhash/mimic").Presence.AnyPresence | undefined = undefined,
33
+ >(
34
+ store: StoreApi<MimicSlice<TSchema, TPresence>>
35
+ ): UseDraftReturn<TSchema> => {
36
+ const draftRef = useRef<ClientDocument.DraftHandle<TSchema> | null>(null);
37
+ // Use a counter to force re-renders when draft state changes
38
+ const versionRef = useRef(0);
39
+
40
+ const subscribe = useCallback(
41
+ (onStoreChange: () => void) => {
42
+ // We piggyback on the zustand store subscription to know when drafts change
43
+ return store.subscribe(onStoreChange);
44
+ },
45
+ [store]
46
+ );
47
+
48
+ const getSnapshot = useCallback(() => {
49
+ return versionRef.current;
50
+ }, []);
51
+
52
+ useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
53
+
54
+ const bumpVersion = useCallback(() => {
55
+ versionRef.current++;
56
+ }, []);
57
+
58
+ const begin = useCallback(() => {
59
+ if (draftRef.current !== null) {
60
+ throw new Error("A draft is already active. Commit or discard it first.");
61
+ }
62
+ const document = store.getState().mimic.document;
63
+ draftRef.current = document.createDraft();
64
+ bumpVersion();
65
+ }, [store, bumpVersion]);
66
+
67
+ const commit = useCallback(() => {
68
+ if (draftRef.current === null) return;
69
+ draftRef.current.commit();
70
+ draftRef.current = null;
71
+ bumpVersion();
72
+ }, [bumpVersion]);
73
+
74
+ const discard = useCallback(() => {
75
+ if (draftRef.current === null) return;
76
+ draftRef.current.discard();
77
+ draftRef.current = null;
78
+ bumpVersion();
79
+ }, [bumpVersion]);
80
+
81
+ const update = useCallback(
82
+ (fn: (root: Primitive.InferProxy<TSchema>) => void) => {
83
+ if (draftRef.current === null) {
84
+ throw new Error("No active draft. Call begin() first.");
85
+ }
86
+ draftRef.current.update(fn);
87
+ },
88
+ []
89
+ );
90
+
91
+ // Auto-discard on unmount
92
+ useEffect(() => {
93
+ return () => {
94
+ if (draftRef.current !== null) {
95
+ try {
96
+ draftRef.current.discard();
97
+ } catch {
98
+ // Draft may already be consumed
99
+ }
100
+ draftRef.current = null;
101
+ }
102
+ };
103
+ }, []);
104
+
105
+ return {
106
+ draft: draftRef.current,
107
+ begin,
108
+ commit,
109
+ discard,
110
+ update,
111
+ };
112
+ };
@@ -79,6 +79,10 @@ const createMockClientDocument = (
79
79
  isReady: () => state.isReady,
80
80
  getPendingCount: () => state.pendingCount,
81
81
  hasPendingChanges: () => state.hasPendingChanges,
82
+ getActiveDraftIds: () => new Set<string>(),
83
+ createDraft: () => {
84
+ throw new Error("Not implemented in mock");
85
+ },
82
86
  connect: () => {
83
87
  // Mock connect - does nothing in tests
84
88
  },