opshot 0.1.1 → 0.3.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Matt Cavender
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -2,12 +2,7 @@
2
2
 
3
3
  # opshot
4
4
 
5
- Plain-object state for React: mutate it directly, re-render only the components that read what changed, and track every change operation.
6
-
7
- - **Self-contained**: a state carries its data, its methods, and its own subscription; pass it around like any object.
8
- - **Reactive reads**: components read plain properties, and reads are tracked per component: a component re-renders only when a property it read changes.
9
- - **Safe mutation**: write by mutating the actual object directly inside `mutate`.
10
- - **Ops events**: every mutation emits its changes as `{ do, undo }` JSON Patch pairs, ready for history, sync, and persistence.
5
+ Mutable state for React, with re-render for only the components that read what changed. (It's like [valtio](https://github.com/pmndrs/valtio), but not a footgun.)
11
6
 
12
7
  ## Install
13
8
 
@@ -15,109 +10,248 @@ Plain-object state for React: mutate it directly, re-render only the components
15
10
  npm install opshot
16
11
  ```
17
12
 
18
- ## Quick start
13
+ ## Mutable state
14
+
15
+ React state is immutable: changing one field means spreading the old object into a new one.
19
16
 
20
17
  ```tsx
21
- import { useCreateState } from "opshot/react";
18
+ const [user, setUser] = useState({ name: "Ada", age: 36 });
22
19
 
23
- const Counter = () => {
24
- const counter = useCreateState({ count: 0 });
20
+ setUser((prev) => ({ ...prev, age: 37 }));
21
+ ```
22
+
23
+ **opshot** state is a live mutable object: you assign the field.
24
+
25
+ ```tsx
26
+ const user = useMutableState({ name: "Ada", age: 36 });
27
+
28
+ user.age = 37;
29
+ ```
30
+
31
+ ## Bounded re-renders
32
+
33
+ React re-renders a component and its children when its state changes.
34
+
35
+ ```tsx
36
+ interface User {
37
+ name: string;
38
+ age: number;
39
+ }
40
+
41
+ const Parent = () => {
42
+ const [user, setUser] = useState<User>({ name: "Ada", age: 36 });
43
+
44
+ const birthday = () => setUser((prev) => ({ ...prev, age: prev.age + 1 }));
45
+
46
+ // A click re-renders Parent and Child.
47
+ return (
48
+ <>
49
+ <button onClick={birthday}>+</button>
50
+ <Child user={user} />
51
+ </>
52
+ );
53
+ };
54
+
55
+ const Child = ({ user }: { user: User }) => <p>{user.age}</p>;
56
+ ```
57
+
58
+ **opshot** re-renders only what read the change. Wrap a child in `scope` and it subscribes to the fields it reads. **Where the mutation happens doesn't matter** — here Parent writes, and only Child re-renders, because renders follow reads, not writes.
25
59
 
26
- // Mutate the object directly: assignments, push, delete all work.
27
- const increment = () => counter.op.mutate((proxy) => proxy.count++);
60
+ ```tsx
61
+ const Parent = () => {
62
+ const user = useMutableState<User>({ name: "Ada", age: 36 });
63
+
64
+ const birthday = () => {
65
+ user.age++;
66
+ };
28
67
 
29
- return <button onClick={increment}>{counter.count}</button>;
68
+ // A click re-renders only Child.
69
+ return (
70
+ <>
71
+ <button onClick={birthday}>+</button>
72
+ <Child user={user} />
73
+ </>
74
+ );
30
75
  };
76
+
77
+ const Child = scope<{ user: User }>(({ user }) => <p>{user.age}</p>);
31
78
  ```
32
79
 
33
- ## Creating state
80
+ This is how you optimize re-rendering across your component tree: place `scope` boundaries where you want re-renders contained, and each boundary re-renders only when a field it read changes. `useMutableState` is a boundary itself.
81
+
82
+ ## Creating State
34
83
 
35
84
  ```tsx
36
- import { ref } from "opshot";
37
- import { useCreateState } from "opshot/react";
85
+ import { ignore, unsafeTrack, useMutableState, type Ignored, type UnsafeTracked } from "opshot";
86
+
87
+ interface PlayerState {
88
+ position: number;
89
+ element: Ignored<HTMLAudioElement>;
90
+ queue: UnsafeTracked<Playlist>;
91
+ seek: (position: number) => void;
92
+ }
38
93
 
39
94
  const Player = () => {
40
- const player = useCreateState((mutate, get) => ({
95
+ const player = useMutableState<PlayerState>({
41
96
  position: 0,
42
97
 
43
- // ref() keeps a value out of reactivity and ops.
44
- element: ref(new Audio()),
98
+ // ignore() keeps a value out of reactivity and ops.
99
+ element: ignore(new Audio()),
45
100
 
46
- // get() reads the current values.
47
- seek: (position: number) => {
48
- get().element.currentTime = position;
101
+ // unsafeTrack() tracks all the values it can, even if there is weird behaviour
102
+ queue: unsafeTrack(new Playlist()),
49
103
 
50
- mutate((proxy) => (proxy.position = position));
104
+ seek(position: number) {
105
+ this.element.currentTime = position;
106
+
107
+ if (this.position === position) return;
108
+
109
+ this.position = position;
51
110
  },
52
- }));
111
+ });
53
112
 
54
113
  // ...
55
114
  };
56
115
  ```
57
116
 
58
- ## state.op
117
+ ## Constraints
118
+
119
+ opshot tracks plain data.
59
120
 
60
- Everything opshot attaches lives under one reserved key, `op`.
121
+ It can't track:
122
+
123
+ - Internal slots (language level features like in Map)
124
+ - #private fields (hidden at the language level)
125
+ - Arrow methods on classes (they write to the original object, not the tracked one)
126
+ - Array subclasses (the prototype is lost when copied)
127
+
128
+ Use `ignore` or `unsafeTrack` when dealing with these.
129
+
130
+ ## Tracked collections
131
+
132
+ `TrackedMap`, `TrackedSet`, and `TrackedDate` stand in for the built-ins opshot rejects. They have the exact same API as their counterparts.
61
133
 
62
134
  ```ts
63
- // The write path. An optional second argument is passed to every subscriber.
64
- counter.op.mutate((proxy) => {}, { transactionKey: "drag" });
135
+ import { TrackedMap, useMutableState } from "opshot";
65
136
 
66
- // Hears every op this state emits; returns an unsubscribe.
67
- const unsubscribe = counter.op.subscribe((state, ops, options) => {
68
- // ...
69
- });
137
+ const state = useMutableState({ index: new TrackedMap<string, number>() });
138
+
139
+ state.index.set("a", 1);
140
+ ```
141
+
142
+ ## Subscribe
70
143
 
71
- // State references are not reliable for equality: every mutation produces a new one. Use this instead.
72
- counter.op.isSameState(other);
144
+ `subscribe` hears every change to a state.
73
145
 
74
- // True while a mutate callback is running.
75
- counter.op.isMutating;
146
+ ```tsx
147
+ import { useEffect } from "react";
148
+ import { subscribe, useMutableState } from "opshot";
76
149
 
77
- // The current values as your plain object, op stripped: for serializing and reads outside render.
78
- counter.op.unwrap();
150
+ const Counter = () => {
151
+ const counter = useMutableState({ count: 0 });
79
152
 
80
- // The underlying valtio proxy, typed object: an escape hatch.
81
- counter.op.proxy;
153
+ useEffect(
154
+ () =>
155
+ subscribe(counter, (ops, meta) => {
156
+ // ops: [{
157
+ // do: { op: "replace", path: ["count"], value: 1 },
158
+ // undo: { op: "replace", path: ["count"], value: 0 },
159
+ // }]
160
+ // meta: whatever the writer passed, or undefined for bare writes
161
+ }),
162
+ [counter],
163
+ );
164
+
165
+ // ...
166
+ };
82
167
  ```
83
168
 
84
169
  ## Ops
85
170
 
171
+ An op is an invertible pair of `Operation` halves. Every half uses one of three verbs:
172
+
86
173
  ```ts
87
- const unsubscribe = counter.op.subscribe((state, ops, options) => {
88
- // state: the snapshot these ops produced
89
- // ops: [{
90
- // do: { op: "replace", path: "/count", value: 1 },
91
- // undo: { op: "replace", path: "/count", value: 0 },
92
- // }]
93
- });
174
+ type OperationPath = ReadonlyArray<string | number>;
175
+
176
+ type Operation =
177
+ | { readonly op: "add"; readonly path: OperationPath; readonly value: unknown }
178
+ | { readonly op: "replace"; readonly path: OperationPath; readonly value: unknown }
179
+ | { readonly op: "remove"; readonly path: OperationPath };
180
+
181
+ interface Op {
182
+ readonly do: Operation;
183
+ readonly undo: Operation;
184
+ }
94
185
  ```
95
186
 
96
- An op is a pair of [RFC 6902](https://datatracker.ietf.org/doc/html/rfc6902) patch operations, each half carrying its own value, so any JSON Patch tool applies and inverts them.
187
+ `applyOps` puts them back on a state, so a history is a list of ops and an undo is their `undo` halves in reverse.
97
188
 
98
- A subscriber must not write to the state it subscribes to; writing to a different state is fine.
189
+ ```tsx
190
+ import { useEffect, useRef } from "react";
191
+ import { applyOps, subscribe, useMutableState, type Op } from "opshot";
192
+
193
+ const replay = {};
99
194
 
100
- Ops cost nothing until someone listens: a state with no subscribers, on itself or its group, skips computing them entirely.
195
+ const Counter = () => {
196
+ const counter = useMutableState({ count: 0 });
197
+ const history = useRef<Array<ReadonlyArray<Op>>>([]);
198
+
199
+ useEffect(
200
+ () =>
201
+ subscribe(counter, (ops, meta) => {
202
+ // Skip our own replays, so undo doesn't record itself.
203
+ if (meta === replay) return;
204
+
205
+ history.current.push(ops);
206
+ }),
207
+ [counter],
208
+ );
209
+
210
+ const undo = () => {
211
+ const ops = history.current.pop();
212
+
213
+ if (!ops) return;
214
+
215
+ applyOps(
216
+ counter,
217
+ [...ops].reverse().map((op) => op.undo),
218
+ replay,
219
+ );
220
+ };
221
+
222
+ return (
223
+ <>
224
+ <button onClick={() => counter.count++}>+</button>
225
+ <button onClick={undo}>Undo</button>
226
+ </>
227
+ );
228
+ };
229
+ ```
230
+
231
+ Replay is exact for anything opshot can see: plain data. State behind a constraint is the exception.
232
+
233
+ If your state is JSON serializable, **then ops are too**.
101
234
 
102
235
  ## Groups
103
236
 
104
- A group creates states and hears every op from the states it created: one stream for history, sync, and persistence.
237
+ A group creates states and hears every op from the states it created: one stream for history, sync, persistence, etc.
105
238
 
106
239
  ```tsx
107
240
  import { useEffect } from "react";
108
- import { useCreateGroup, useCreateState } from "opshot/react";
241
+ import { subscribe, useGroup, useMutableState } from "opshot";
109
242
 
110
243
  const Editor = () => {
111
- // A lifetime-stable group.
112
- const group = useCreateGroup();
244
+ const group = useGroup();
113
245
 
114
- // Created through the group, so its ops reach the group's subscribers.
115
- const doc = useCreateState({ items: new Array<string>() }, group);
246
+ // Created through the group, so their ops reach the group's subscribers.
247
+ const doc = useMutableState({ items: new Array<string>() }, group);
248
+ const selection = useMutableState({ index: 0 }, group);
116
249
 
117
250
  useEffect(
118
251
  () =>
119
- // Fires for doc and every other state the group created.
120
- group.subscribe((state, ops, options) => {
252
+ // Fires for doc, selection, and every other state the group created.
253
+ // state is whichever one changed.
254
+ subscribe(group, (state, ops, meta) => {
121
255
  // ...
122
256
  }),
123
257
  [group],
@@ -127,47 +261,48 @@ const Editor = () => {
127
261
  };
128
262
  ```
129
263
 
130
- ## resnapshot
264
+ ## Channels
131
265
 
132
- A component re-renders when a property it read changes. Reads belong to the nearest subscribed component above them: `useCreateState` subscribes the component that created the state, and `resnapshot` subscribes the component it wraps.
133
-
134
- This is a lever for bounding re-renders. Here `CounterButton` is plain, so its `count` read belongs to `App`, and every click re-renders `App` and everything under it:
266
+ A channel binds `transact`, `subscribe`, and `applyOps` to a typed meta convention, so a listener can tell its own writes from everyone else's.
135
267
 
136
268
  ```tsx
137
- import type { State } from "opshot";
138
- import { useCreateState } from "opshot/react";
269
+ import { useEffect } from "react";
270
+ import { createChannel, useMutableState } from "opshot";
139
271
 
140
- interface Counter {
141
- title: string;
142
- count: number;
272
+ interface DocumentMeta {
273
+ replay?: boolean;
274
+ source?: string;
143
275
  }
144
276
 
145
- // Every click re-renders App and its whole subtree.
146
- const App = () => {
147
- const counter = useCreateState<Counter>({ title: "Hits", count: 0 });
277
+ const docChannel = createChannel<DocumentMeta>({ source: "editor" }); // set defaults
148
278
 
149
- return (
150
- <>
151
- <h1>{counter.title}</h1>
152
- <CounterButton counter={counter} />
153
- </>
154
- );
155
- };
279
+ const TitleBar = () => {
280
+ const doc = useMutableState({ title: "Untitled" });
156
281
 
157
- const CounterButton = ({ counter }: { counter: State<Counter> }) => (
158
- <button onClick={() => counter.op.mutate((proxy) => proxy.count++)}>{counter.count}</button>
159
- );
160
- ```
282
+ useEffect(
283
+ () =>
284
+ docChannel.subscribe(doc, (ops, context) => {
285
+ // A bare write, or a transact from another channel: meta is unknown.
286
+ if (!context.isTransaction) return;
161
287
 
162
- Wrapping `CounterButton` in `resnapshot` subscribes it, so its `count` read becomes its own. A click now re-renders `CounterButton` alone:
288
+ // Own-channel transaction: meta is typed, with defaults merged.
289
+ if (context.meta.replay) return;
163
290
 
164
- ```tsx
165
- import { resnapshot } from "opshot/react";
291
+ // ...
292
+ }),
293
+ [doc],
294
+ );
295
+
296
+ const rename = () => {
297
+ docChannel.transact(doc, () => {
298
+ doc.title = "Draft";
299
+ });
300
+ };
166
301
 
167
- // A click re-renders only CounterButton. A title change would still re-render App.
168
- const CounterButton = resnapshot<{ counter: State<Counter> }>(({ counter }) => (
169
- <button onClick={() => counter.op.mutate((proxy) => proxy.count++)}>{counter.count}</button>
170
- ));
302
+ // ...
303
+ };
171
304
  ```
172
305
 
173
- Subscribed components re-render independently: had `App` also read `count`, both would re-render.
306
+ ## License
307
+
308
+ [MIT](LICENSE)
package/dist/index.d.ts CHANGED
@@ -1,47 +1,185 @@
1
- import { Snapshot } from 'valtio/vanilla';
2
- export { Snapshot, ref } from 'valtio/vanilla';
1
+ import { ComponentType, FC } from 'react';
3
2
 
4
- type PatchOperation = {
3
+ type OperationPath = ReadonlyArray<string | number>;
4
+
5
+ interface AddOperation {
5
6
  readonly op: "add";
6
- readonly path: string;
7
+ readonly path: OperationPath;
7
8
  readonly value: unknown;
8
- } | {
9
+ }
10
+ interface ReplaceOperation {
9
11
  readonly op: "replace";
10
- readonly path: string;
12
+ readonly path: OperationPath;
11
13
  readonly value: unknown;
12
- } | {
14
+ }
15
+ interface RemoveOperation {
13
16
  readonly op: "remove";
14
- readonly path: string;
15
- };
17
+ readonly path: OperationPath;
18
+ }
19
+ type Operation = AddOperation | ReplaceOperation | RemoveOperation;
16
20
  interface Op {
17
- readonly do: PatchOperation;
18
- readonly undo: PatchOperation;
19
- }
20
- declare function diffSnapshots(before: unknown, after: unknown): Array<Op>;
21
-
22
- type MutateOptions = Record<string, unknown>;
23
- type Mutate<T extends object> = (callback: (proxy: T) => void, options?: MutateOptions) => void;
24
- type StateListener<T extends object> = (state: State<T>, ops: Array<Op>, options: MutateOptions) => void;
25
- interface OpshotHandle<T extends object> {
26
- readonly proxy: object;
27
- readonly isMutating: boolean;
28
- readonly mutate: Mutate<T>;
29
- readonly subscribe: (listener: StateListener<T>) => () => void;
30
- readonly isSameState: (other: unknown) => boolean;
31
- readonly unwrap: () => Snapshot<T>;
32
- }
33
- type State<T extends object> = Snapshot<T> & {
34
- readonly op: OpshotHandle<T>;
35
- };
36
- type DefineCallback<T extends object> = (mutate: Mutate<T>, get: () => State<T>) => T;
37
- type Define<T extends object> = DefineCallback<T> | T;
38
- declare function createState<T extends object>(define: Define<T>): State<T>;
39
- declare function isState(value: unknown): value is State<object>;
21
+ readonly do: Operation;
22
+ readonly undo: Operation;
23
+ }
24
+
25
+ type StateListener = (ops: ReadonlyArray<Op>, meta: unknown) => void;
26
+ type GroupListener = (state: object, ops: ReadonlyArray<Op>, meta: unknown) => void;
40
27
 
41
28
  interface Group {
42
- createState<T extends object>(define: Define<T>): State<T>;
43
- subscribe(listener: StateListener<object>): () => void;
29
+ createMutableState<T extends object>(properties: T): T;
44
30
  }
45
31
  declare function createGroup(): Group;
46
32
 
47
- export { type Define, type DefineCallback, type Group, type Mutate, type MutateOptions, type Op, type OpshotHandle, type PatchOperation, type State, type StateListener, createGroup, createState, diffSnapshots, isState };
33
+ type Context<M> = {
34
+ readonly isTransaction: true;
35
+ readonly meta: M;
36
+ } | {
37
+ readonly isTransaction: false;
38
+ readonly meta: unknown;
39
+ };
40
+ declare function subscribe(group: Group, listener: GroupListener): () => void;
41
+ declare function subscribe(state: object, listener: StateListener): () => void;
42
+
43
+ interface Channel<M extends object> {
44
+ transact(state: object, mutate: () => void, meta?: Partial<M>): void;
45
+ subscribe(group: Group, listener: (state: object, ops: ReadonlyArray<Op>, context: Context<M>) => void): () => void;
46
+ subscribe(state: object, listener: (ops: ReadonlyArray<Op>, context: Context<M>) => void): () => void;
47
+ applyOps(state: object, operations: ReadonlyArray<Operation>, meta?: Partial<M>): void;
48
+ }
49
+ declare function createChannel<M extends object>(defaults?: M): Channel<M>;
50
+
51
+ declare function createMutableState<T extends object>(properties: T, group?: Group): T;
52
+
53
+ declare function identify(value: object): object;
54
+ declare function isSameIdentity(first: object, second: object): boolean;
55
+
56
+ declare function isState(value: unknown): value is object;
57
+
58
+ declare function applyOps(state: object, operations: ReadonlyArray<Operation>, meta?: unknown): void;
59
+
60
+ declare function diffSnapshots(before: object, after: object): Array<Op>;
61
+
62
+ declare const ignoredMarker: unique symbol;
63
+ type Ignored<T extends object> = T & {
64
+ readonly [ignoredMarker]: true;
65
+ };
66
+ declare const ignore: <T extends object>(value: T) => Ignored<T>;
67
+
68
+ declare const unsafeTrackedBrand: unique symbol;
69
+ type UnsafeTracked<T extends object> = T & {
70
+ readonly [unsafeTrackedBrand]: true;
71
+ };
72
+ declare function unsafeTrack<T extends object>(value: T): UnsafeTracked<T>;
73
+
74
+ type DateConstructorArgs = [] | [value: number | string] | [
75
+ year: number,
76
+ monthIndex: number,
77
+ date?: number,
78
+ hours?: number,
79
+ minutes?: number,
80
+ seconds?: number,
81
+ milliseconds?: number
82
+ ];
83
+ declare class TrackedDate {
84
+ private epochMs;
85
+ constructor(...args: DateConstructorArgs);
86
+ private readDate;
87
+ private write;
88
+ toString(): string;
89
+ toDateString(): string;
90
+ toTimeString(): string;
91
+ toLocaleString(locales?: Intl.LocalesArgument, options?: Intl.DateTimeFormatOptions): string;
92
+ toLocaleDateString(locales?: Intl.LocalesArgument, options?: Intl.DateTimeFormatOptions): string;
93
+ toLocaleTimeString(locales?: Intl.LocalesArgument, options?: Intl.DateTimeFormatOptions): string;
94
+ valueOf(): number;
95
+ getTime(): number;
96
+ getFullYear(): number;
97
+ getYear(): number;
98
+ getUTCFullYear(): number;
99
+ getMonth(): number;
100
+ getUTCMonth(): number;
101
+ getDate(): number;
102
+ getUTCDate(): number;
103
+ getDay(): number;
104
+ getUTCDay(): number;
105
+ getHours(): number;
106
+ getUTCHours(): number;
107
+ getMinutes(): number;
108
+ getUTCMinutes(): number;
109
+ getSeconds(): number;
110
+ getUTCSeconds(): number;
111
+ getMilliseconds(): number;
112
+ getUTCMilliseconds(): number;
113
+ getTimezoneOffset(): number;
114
+ setYear(year: number): number;
115
+ setTime(...args: [time: number]): number;
116
+ setMilliseconds(...args: [milliseconds: number]): number;
117
+ setUTCMilliseconds(...args: [milliseconds: number]): number;
118
+ setSeconds(...args: [seconds: number, milliseconds?: number]): number;
119
+ setUTCSeconds(...args: [seconds: number, milliseconds?: number]): number;
120
+ setMinutes(...args: [minutes: number, seconds?: number, milliseconds?: number]): number;
121
+ setUTCMinutes(...args: [minutes: number, seconds?: number, milliseconds?: number]): number;
122
+ setHours(...args: [hours: number, minutes?: number, seconds?: number, milliseconds?: number]): number;
123
+ setUTCHours(...args: [hours: number, minutes?: number, seconds?: number, milliseconds?: number]): number;
124
+ setDate(...args: [dateValue: number]): number;
125
+ setUTCDate(...args: [dateValue: number]): number;
126
+ setMonth(...args: [month: number, dateValue?: number]): number;
127
+ setUTCMonth(...args: [month: number, dateValue?: number]): number;
128
+ setFullYear(...args: [year: number, month?: number, dateValue?: number]): number;
129
+ setUTCFullYear(...args: [year: number, month?: number, dateValue?: number]): number;
130
+ toUTCString(): string;
131
+ toGMTString(): string;
132
+ toISOString(): string;
133
+ [Symbol.toPrimitive](hint: "default" | "string" | "number"): string | number;
134
+ readonly [Symbol.toStringTag]: "TrackedDate";
135
+ }
136
+
137
+ declare class TrackedMap<K, V> {
138
+ private slots;
139
+ private index;
140
+ private count;
141
+ constructor(entries?: Iterable<readonly [K, V]>);
142
+ get size(): number;
143
+ has(key: K): boolean;
144
+ get(key: K): V | undefined;
145
+ set(key: K, value: V): this;
146
+ delete(key: K): boolean;
147
+ clear(): void;
148
+ entries(): IterableIterator<[K, V]>;
149
+ keys(): IterableIterator<K>;
150
+ values(): IterableIterator<V>;
151
+ forEach(callback: (value: V, key: K, map: TrackedMap<K, V>) => void): void;
152
+ [Symbol.iterator](): IterableIterator<[K, V]>;
153
+ readonly [Symbol.toStringTag]: "TrackedMap";
154
+ }
155
+
156
+ declare class TrackedSet<T> {
157
+ private slots;
158
+ private index;
159
+ private count;
160
+ constructor(values?: Iterable<T>);
161
+ get size(): number;
162
+ has(value: T): boolean;
163
+ add(value: T): this;
164
+ delete(value: T): boolean;
165
+ clear(): void;
166
+ entries(): IterableIterator<[T, T]>;
167
+ keys(): IterableIterator<T>;
168
+ values(): IterableIterator<T>;
169
+ forEach(callback: (value: T, key: T, set: TrackedSet<T>) => void): void;
170
+ [Symbol.iterator](): IterableIterator<T>;
171
+ readonly [Symbol.toStringTag]: "TrackedSet";
172
+ }
173
+
174
+ declare function transact(state: object, mutate: () => void, meta?: unknown): void;
175
+
176
+ interface ScopeOptions {
177
+ readonly maxDepth?: number;
178
+ }
179
+ declare function scope<P extends object>(Component: ComponentType<P>, options?: ScopeOptions): FC<P>;
180
+
181
+ declare function useGroup(): Group;
182
+
183
+ declare function useMutableState<T extends object>(properties: T, group?: Group): T;
184
+
185
+ export { type AddOperation, type Channel, type Context, type Group, type GroupListener, type Ignored, type Op, type Operation, type OperationPath, type RemoveOperation, type ReplaceOperation, type ScopeOptions, type StateListener, TrackedDate, TrackedMap, TrackedSet, type UnsafeTracked, applyOps, createChannel, createGroup, createMutableState, diffSnapshots, identify, ignore, isSameIdentity, isState, scope, subscribe, transact, unsafeTrack, useGroup, useMutableState };