opshot 0.3.4 → 0.4.0

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.
Files changed (4) hide show
  1. package/README.md +33 -39
  2. package/dist/index.d.ts +435 -42
  3. package/dist/index.js +2993 -1241
  4. package/package.json +20 -10
package/dist/index.d.ts CHANGED
@@ -1,75 +1,424 @@
1
1
  import { ComponentType, FC } from 'react';
2
2
 
3
+ declare const ignoreMarker: unique symbol;
4
+ /**
5
+ * A factory-argument marker wrapping `T`.
6
+ *
7
+ * @typeParam T - Value type.
8
+ */
9
+ interface Ignored<T> {
10
+ readonly [ignoreMarker]: T;
11
+ }
12
+ /**
13
+ * Marks a factory-argument value so the edge at that path is untracked in that state.
14
+ *
15
+ * @typeParam T - Value type.
16
+ * @param value - Value to ignore.
17
+ * @returns A marker consumed at create.
18
+ */
19
+ declare function ignore<T>(value: T): Ignored<T>;
20
+
21
+ declare const unsafeMarker: unique symbol;
22
+ /**
23
+ * A factory-argument marker wrapping `T`.
24
+ *
25
+ * @typeParam T - Value type.
26
+ */
27
+ interface UnsafeTracked<T> {
28
+ readonly [unsafeMarker]: T;
29
+ }
30
+ /**
31
+ * Marks a factory-argument value so strict is disabled at and under that path.
32
+ *
33
+ * @typeParam T - Value type.
34
+ * @param value - Value to track without strict.
35
+ * @returns A marker consumed at create.
36
+ */
37
+ declare function unsafeTrack<T>(value: T): UnsafeTracked<T>;
38
+
39
+ /**
40
+ * Schedules when bare writes notify listeners. Call `flush` once.
41
+ *
42
+ * @param flush - Delivers pending ops.
43
+ * @returns Nothing.
44
+ */
45
+ type EmissionScheduler = (flush: () => void) => void;
46
+ /**
47
+ * State creation options.
48
+ *
49
+ * @example
50
+ * createMutableState({ x: 0 }, { emitOn: (flush) => requestAnimationFrame(flush), strict: false })
51
+ */
52
+ interface MutableNodeOptions {
53
+ /**
54
+ * When bare writes notify listeners. Defaults to a microtask.
55
+ */
56
+ readonly emitOn?: EmissionScheduler;
57
+ /**
58
+ * When true, throws at a dangerous edge, at the cause. Defaults to true.
59
+ */
60
+ readonly strict?: boolean;
61
+ }
62
+
63
+ /**
64
+ * Options for `createMutableState`.
65
+ *
66
+ * @example
67
+ * createMutableState({ count: 0 }, { group, emitOn, strict: false })
68
+ */
69
+ interface MutableStateOptions extends MutableNodeOptions {
70
+ /**
71
+ * Group that receives this state's changes.
72
+ */
73
+ readonly group?: Group;
74
+ }
75
+ /**
76
+ * The live state shape after factory-argument markers are collapsed.
77
+ *
78
+ * @typeParam T - Factory argument type.
79
+ */
80
+ type Unmarked<T> = T extends Ignored<infer Inner> ? Unmarked<Inner> : T extends UnsafeTracked<infer Inner> ? Unmarked<Inner> : T extends (...args: never) => unknown ? T : T extends ReadonlyArray<infer Element> ? Array<Unmarked<Element>> : T extends object ? {
81
+ [Key in keyof T]: Unmarked<T[Key]>;
82
+ } : T;
83
+ /**
84
+ * Creates a mutable state object.
85
+ *
86
+ * `ignore()` on a value in the factory argument makes the edge at that path untracked in that state.
87
+ * `unsafeTrack()` on a value in the factory argument disables strict at and under that path.
88
+ *
89
+ * @typeParam T - State shape.
90
+ * @param properties - Initial fields.
91
+ * @param options - Creation options.
92
+ * @returns The state, with factory-argument markers collapsed.
93
+ */
94
+ declare function createMutableState<T extends object>(properties: T, options?: MutableStateOptions): Unmarked<T>;
95
+
96
+ /**
97
+ * Path segments to a value in state.
98
+ *
99
+ * @example
100
+ * ["document", "items", 0, "title"]
101
+ */
3
102
  type OperationPath = ReadonlyArray<string | number>;
4
103
 
5
- interface AddOperation {
6
- readonly op: "add";
104
+ interface DeclarationTrie {
105
+ readonly ignored: boolean;
106
+ readonly unsafe: boolean;
107
+ readonly children: ReadonlyMap<string, DeclarationTrie>;
108
+ }
109
+
110
+ interface InEdge {
111
+ readonly parent: object;
112
+ readonly key: string | number;
113
+ }
114
+ interface NodeRecord {
115
+ edges: Array<InEdge>;
116
+ id: number | undefined;
117
+ }
118
+
119
+ interface CaptureTables {
120
+ mints: Array<{
121
+ readonly node: object;
122
+ readonly id: number;
123
+ }>;
124
+ binds: Array<{
125
+ readonly node: object;
126
+ readonly id: number;
127
+ }>;
128
+ bindIdByNode: Map<object, number>;
129
+ bindNodeById: Map<number, object>;
130
+ mintIdByNode: Map<object, number>;
131
+ mintNodeById: Map<number, object>;
132
+ nextStagedId: number;
133
+ }
134
+
135
+ interface DirtyIndex {
136
+ readonly edges: WeakMap<object, Set<string | symbol>>;
137
+ readonly nodes: WeakSet<object>;
138
+ }
139
+ interface Handle {
140
+ proxy: {
141
+ readonly root: object;
142
+ };
143
+ lastSnapshot: object;
144
+ hasPendingWrites: boolean;
145
+ isFlushScheduled: boolean;
146
+ isFlushHeld: boolean;
147
+ flushGeneration: number;
148
+ subscribers: StateListeners;
149
+ groups?: ReadonlyArray<GroupListeners>;
150
+ disarmWatch?: () => void;
151
+ emitOn?: EmissionScheduler;
152
+ strict: boolean;
153
+ declarations: DeclarationTrie | undefined;
154
+ nodes: WeakMap<object, NodeRecord>;
155
+ byId: Map<number, object>;
156
+ nextInternId: number;
157
+ lastDirty?: DirtyIndex;
158
+ stamp: object;
159
+ version: number;
160
+ replaying: boolean;
161
+ transactionCapture?: CaptureTables;
162
+ }
163
+
164
+ /**
165
+ * Assigns a value at a path.
166
+ *
167
+ * @example
168
+ * { verb: "assign", path: ["count"], value: 1 }
169
+ */
170
+ interface AssignMutation {
171
+ /**
172
+ * `"assign"`.
173
+ */
174
+ readonly verb: "assign";
175
+ /**
176
+ * Path to assign.
177
+ */
7
178
  readonly path: OperationPath;
179
+ /**
180
+ * Value to assign.
181
+ */
8
182
  readonly value: unknown;
183
+ /**
184
+ * Walk-ordered intern ids that override deterministic vending when this half re-admits departed material.
185
+ */
186
+ readonly ids?: ReadonlyArray<number>;
9
187
  }
10
- interface ReplaceOperation {
11
- readonly op: "replace";
188
+ /**
189
+ * Deletes the value at a path.
190
+ *
191
+ * @example
192
+ * { verb: "delete", path: ["temp"] }
193
+ */
194
+ interface DeleteMutation {
195
+ /**
196
+ * `"delete"`.
197
+ */
198
+ readonly verb: "delete";
199
+ /**
200
+ * Path to delete.
201
+ */
12
202
  readonly path: OperationPath;
13
- readonly value: unknown;
14
203
  }
15
- interface RemoveOperation {
16
- readonly op: "remove";
204
+ /**
205
+ * Links the interned node `ref` into `path`.
206
+ *
207
+ * @example
208
+ * { verb: "link", path: ["alias"], ref: 0 }
209
+ */
210
+ interface LinkMutation {
211
+ /**
212
+ * `"link"`.
213
+ */
214
+ readonly verb: "link";
215
+ /**
216
+ * Path to place the linked node.
217
+ */
17
218
  readonly path: OperationPath;
219
+ /**
220
+ * Intern id of the node to link.
221
+ */
222
+ readonly ref: number;
18
223
  }
19
- type Operation = AddOperation | ReplaceOperation | RemoveOperation;
20
- interface Op {
21
- readonly do: Operation;
22
- readonly undo: Operation;
224
+ /**
225
+ * An assign, a delete, or a link.
226
+ *
227
+ * @example
228
+ * { verb: "assign", path: ["profile"], value: { name: "Ada" } }
229
+ */
230
+ type Mutation = AssignMutation | DeleteMutation | LinkMutation;
231
+ /**
232
+ * A change with do and undo halves.
233
+ *
234
+ * @example
235
+ * { do: { verb: "assign", path: ["count"], value: 1 }, undo: { verb: "assign", path: ["count"], value: 0 } }
236
+ */
237
+ interface Operation {
238
+ /**
239
+ * Forward operation.
240
+ */
241
+ readonly do: Mutation;
242
+ /**
243
+ * Reverse operation.
244
+ */
245
+ readonly undo: Mutation;
23
246
  }
24
247
 
25
- type StateListener = (ops: ReadonlyArray<Op>, meta: unknown) => void;
26
- type GroupListener = (state: object, ops: ReadonlyArray<Op>, meta: unknown) => void;
248
+ /**
249
+ * Listener for one state's changes.
250
+ *
251
+ * @param ops - Ops for the change.
252
+ * @param meta - Writer meta, if any.
253
+ * @returns Nothing.
254
+ */
255
+ type StateListener = (ops: ReadonlyArray<Operation>, meta: unknown) => void;
256
+ /**
257
+ * Listener for a group's changes.
258
+ *
259
+ * @param state - State that changed.
260
+ * @param ops - Ops for the change.
261
+ * @param meta - Writer meta, if any.
262
+ * @returns Nothing.
263
+ */
264
+ type GroupListener = (state: object, ops: ReadonlyArray<Operation>, meta: unknown) => void;
265
+ type StateDeliver = (ops: ReadonlyArray<Operation>, meta: unknown, channelId: object | undefined) => void;
266
+ type GroupDeliver = (state: object, ops: ReadonlyArray<Operation>, meta: unknown, channelId: object | undefined) => void;
267
+ type StateListeners = Map<Function, Map<object | undefined, StateDeliver>>;
268
+ type GroupListeners = Map<Function, Map<object | undefined, GroupDeliver>>;
27
269
 
270
+ /**
271
+ * Creates states and receives their changes on one stream.
272
+ *
273
+ * @example
274
+ * const group = createGroup()
275
+ * const doc = group.createMutableState({ title: "" })
276
+ * subscribe(group, (state, ops, meta) => {})
277
+ */
28
278
  interface Group {
29
- createMutableState<T extends object>(properties: T): T;
279
+ /**
280
+ * Creates a state in this group.
281
+ *
282
+ * @typeParam T - State shape.
283
+ * @param properties - Initial fields.
284
+ * @param options - Creation options.
285
+ * @returns The state.
286
+ */
287
+ createMutableState<T extends object>(properties: T, options?: MutableNodeOptions): Unmarked<T>;
30
288
  }
31
- declare function createGroup(): Group;
289
+ /**
290
+ * Creates a group.
291
+ *
292
+ * @param parent - Optional parent group whose listeners hear this group's states.
293
+ * @returns A new group.
294
+ */
295
+ declare function createGroup(parent?: Group): Group;
32
296
 
33
- type Context<M> = {
297
+ /**
298
+ * Listener context from a channel subscription.
299
+ *
300
+ * @typeParam M - Meta type for this channel.
301
+ */
302
+ type EmissionContext<M> = {
34
303
  readonly isTransaction: true;
35
304
  readonly meta: M;
36
305
  } | {
37
306
  readonly isTransaction: false;
38
307
  readonly meta: unknown;
39
308
  };
309
+ /**
310
+ * Listens for changes from a group's states.
311
+ *
312
+ * @param group - Group to listen to.
313
+ * @param listener - Called on each change.
314
+ * @returns Unsubscribe function.
315
+ */
40
316
  declare function subscribe(group: Group, listener: GroupListener): () => void;
317
+ /**
318
+ * Listens for changes to a state.
319
+ *
320
+ * @param state - State to listen to.
321
+ * @param listener - Called on each change.
322
+ * @returns Unsubscribe function.
323
+ */
41
324
  declare function subscribe(state: object, listener: StateListener): () => void;
42
325
 
326
+ type ApplyDirection = "do" | "undo";
327
+
328
+ /**
329
+ * Channel-bound `transact`, `subscribe`, and `applyOperations`.
330
+ *
331
+ * @typeParam M - Meta type for this channel.
332
+ */
43
333
  interface Channel<M extends object> {
334
+ /**
335
+ * Runs a transaction with this channel's meta.
336
+ *
337
+ * @param state - State to change.
338
+ * @param mutate - Function that writes the state.
339
+ * @param meta - Meta for this write.
340
+ * @returns Nothing.
341
+ */
44
342
  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;
343
+ /**
344
+ * Listens for changes from a group's states.
345
+ *
346
+ * @param group - Group to listen to.
347
+ * @param listener - Called on each change.
348
+ * @returns Unsubscribe function.
349
+ */
350
+ subscribe(group: Group, listener: (state: object, ops: ReadonlyArray<Operation>, context: EmissionContext<M>) => void): () => void;
351
+ /**
352
+ * Listens for changes to a state.
353
+ *
354
+ * @param state - State to listen to.
355
+ * @param listener - Called on each change.
356
+ * @returns Unsubscribe function.
357
+ */
358
+ subscribe(state: object, listener: (ops: ReadonlyArray<Operation>, context: EmissionContext<M>) => void): () => void;
359
+ /**
360
+ * Applies operations with this channel's meta.
361
+ *
362
+ * @param state - State to change.
363
+ * @param operations - Operations to apply.
364
+ * @param direction - `"do"` or `"undo"`.
365
+ * @param meta - Meta for this write.
366
+ * @returns Nothing.
367
+ */
368
+ applyOperations(state: object, operations: ReadonlyArray<Operation>, direction: ApplyDirection, meta?: Partial<M>): void;
48
369
  }
370
+ /**
371
+ * Creates a channel with a typed meta convention.
372
+ *
373
+ * @typeParam M - Meta type for this channel.
374
+ * @param defaults - Default meta for this channel's writes.
375
+ * @returns The channel.
376
+ */
49
377
  declare function createChannel<M extends object>(defaults?: M): Channel<M>;
50
378
 
51
- declare function createMutableState<T extends object>(properties: T, group?: Group): T;
52
-
379
+ /**
380
+ * Returns a stable identity key for a value.
381
+ *
382
+ * @param value - Value to identify.
383
+ * @returns Identity key.
384
+ */
53
385
  declare function identify(value: object): object;
386
+ /**
387
+ * Returns whether two values share the same identity.
388
+ *
389
+ * @param first - First value.
390
+ * @param second - Second value.
391
+ * @returns True if they match.
392
+ */
54
393
  declare function isSameIdentity(first: object, second: object): boolean;
55
394
 
395
+ /**
396
+ * Returns whether a value is an opshot state.
397
+ *
398
+ * @param value - Value to test.
399
+ * @returns True if it is a state.
400
+ */
56
401
  declare function isState(value: unknown): value is object;
57
402
 
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>;
403
+ /**
404
+ * Applies operations to a state.
405
+ *
406
+ * @param state - State to change.
407
+ * @param operations - Operations to apply.
408
+ * @param direction - `"do"` or `"undo"`.
409
+ * @param meta - Passed to listeners.
410
+ * @returns Nothing.
411
+ */
412
+ declare function applyOperations(state: object, operations: ReadonlyArray<Operation>, direction: ApplyDirection, meta?: unknown): void;
67
413
 
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>;
414
+ /**
415
+ * Diffs two plain objects into ops.
416
+ *
417
+ * @param before - Earlier value.
418
+ * @param after - Later value.
419
+ * @returns Ops from before to after.
420
+ */
421
+ declare function diffObjects(before: object, after: object, handle?: Handle, dirty?: DirtyIndex, capture?: CaptureTables): Array<Operation>;
73
422
 
74
423
  type DateConstructorArgs = [] | [value: number | string] | [
75
424
  year: number,
@@ -80,10 +429,17 @@ type DateConstructorArgs = [] | [value: number | string] | [
80
429
  seconds?: number,
81
430
  milliseconds?: number
82
431
  ];
432
+ /**
433
+ * Tracked `Date` for use in state.
434
+ *
435
+ * @example
436
+ * createMutableState({ when: new TrackedDate() })
437
+ */
83
438
  declare class TrackedDate {
84
439
  private epochMs;
85
440
  constructor(...args: DateConstructorArgs);
86
441
  private readDate;
442
+ private readEpochMs;
87
443
  private write;
88
444
  toString(): string;
89
445
  toDateString(): string;
@@ -134,6 +490,12 @@ declare class TrackedDate {
134
490
  readonly [Symbol.toStringTag]: "TrackedDate";
135
491
  }
136
492
 
493
+ /**
494
+ * Tracked `Map` for use in state.
495
+ *
496
+ * @typeParam K - Key type.
497
+ * @typeParam V - Value type.
498
+ */
137
499
  declare class TrackedMap<K, V> {
138
500
  private slots;
139
501
  private index;
@@ -153,6 +515,11 @@ declare class TrackedMap<K, V> {
153
515
  readonly [Symbol.toStringTag]: "TrackedMap";
154
516
  }
155
517
 
518
+ /**
519
+ * Tracked `Set` for use in state.
520
+ *
521
+ * @typeParam T - Member type.
522
+ */
156
523
  declare class TrackedSet<T> {
157
524
  private slots;
158
525
  private index;
@@ -171,15 +538,41 @@ declare class TrackedSet<T> {
171
538
  readonly [Symbol.toStringTag]: "TrackedSet";
172
539
  }
173
540
 
541
+ /**
542
+ * Runs changes in one batch and notifies listeners with optional `meta`.
543
+ *
544
+ * @param state - State to change.
545
+ * @param mutate - Function that writes the state.
546
+ * @param meta - Passed to listeners.
547
+ * @returns Nothing.
548
+ */
174
549
  declare function transact(state: object, mutate: () => void, meta?: unknown): void;
175
550
 
176
- interface ScopeOptions {
177
- readonly maxDepth?: number;
178
- }
179
- declare function scope<P extends object>(Component: ComponentType<P>, options?: ScopeOptions): FC<P>;
551
+ /**
552
+ * Wraps a component so it re-renders only when fields it read change.
553
+ *
554
+ * @typeParam P - Props type.
555
+ * @param Component - Component to wrap.
556
+ * @returns The wrapped component.
557
+ */
558
+ declare function scope<P extends object>(Component: ComponentType<P>): FC<P>;
180
559
 
181
- declare function useGroup(): Group;
560
+ /**
561
+ * Creates a group for a component.
562
+ *
563
+ * @param parent - Optional parent group whose listeners hear this group's states.
564
+ * @returns The group.
565
+ */
566
+ declare function useGroup(parent?: Group): Group;
182
567
 
183
- declare function useMutableState<T extends object>(properties: (() => T) | T, group?: Group): T;
568
+ /**
569
+ * Creates mutable state for a component.
570
+ *
571
+ * @typeParam T - State shape.
572
+ * @param properties - Initial fields, or a function that returns them.
573
+ * @param options - Creation options.
574
+ * @returns The state.
575
+ */
576
+ declare function useMutableState<T extends object>(properties: (() => T) | T, options?: MutableStateOptions): Unmarked<T>;
184
577
 
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 };
578
+ export { type Channel, type EmissionContext, type EmissionScheduler, type Group, type GroupListener, type Ignored, type MutableNodeOptions, type MutableStateOptions, type Operation, type OperationPath, type StateListener, TrackedDate, TrackedMap, TrackedSet, type UnsafeTracked, applyOperations, createChannel, createGroup, createMutableState, diffObjects, identify, ignore, isSameIdentity, isState, scope, subscribe, transact, unsafeTrack, useGroup, useMutableState };