opshot 0.5.0 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -206,6 +206,8 @@ const Chart = () => {
206
206
  };
207
207
  ```
208
208
 
209
+ Separate from that callback, the `flush(state)` export ends the window from outside.
210
+
209
211
  ## Batches
210
212
 
211
213
  `batch` runs a callback and tags every write inside it with your `meta`, so a listener can tell its own writes from everyone else's.
package/dist/index.d.ts CHANGED
@@ -250,6 +250,13 @@ declare function subscribe(state: object, listener: StateListener): () => void;
250
250
  */
251
251
  declare function batch(callback: () => void, meta?: unknown): void;
252
252
 
253
+ /**
254
+ * Ends the state's window now, delivering the operations gathered so far.
255
+ *
256
+ * @param state - State to flush.
257
+ */
258
+ declare function flush(state: object): void;
259
+
253
260
  /**
254
261
  * Wraps a component so it re-renders only when fields it read change.
255
262
  *
@@ -269,4 +276,4 @@ declare function scope<P extends object>(Component: ComponentType<P>): FC<P>;
269
276
  */
270
277
  declare function useMutableState<T extends object>(properties: (() => T) | T, options?: MutableStateOptions): T;
271
278
 
272
- export { type EmissionScheduler, type MutableStateOptions, type Operation, type StateListener, TrackedDate, TrackedMap, TrackedSet, batch, createMutableState, identify, ignore, isSameIdentity, isState, scope, subscribe, unsafeTrack, useMutableState };
279
+ export { type EmissionScheduler, type MutableStateOptions, type Operation, type StateListener, TrackedDate, TrackedMap, TrackedSet, batch, createMutableState, flush, identify, ignore, isSameIdentity, isState, scope, subscribe, unsafeTrack, useMutableState };
package/dist/index.js CHANGED
@@ -257,9 +257,83 @@ function evict(handle, node) {
257
257
  cascade(handle, rawNode, membership.keys);
258
258
  }
259
259
 
260
+ // src/utils/predicates.ts
261
+ var isObjectLike = (value) => value !== null && (typeof value === "object" || typeof value === "function");
262
+
263
+ // src/identity.ts
264
+ var readProxyTargets = /* @__PURE__ */ new WeakMap();
265
+ var identityRecords = /* @__PURE__ */ new WeakMap();
266
+ var nextInternId = 0;
267
+ var registerReadProxyTarget = (readProxy, target) => {
268
+ readProxyTargets.set(readProxy, target);
269
+ };
270
+ var getRegisteredReadProxyTarget = (readProxy) => readProxyTargets.get(readProxy);
271
+ function resolveIdentity(value) {
272
+ let current = value;
273
+ while (isObjectLike(current)) {
274
+ const readTarget = getRegisteredReadProxyTarget(current);
275
+ if (readTarget !== void 0 && readTarget !== current) {
276
+ current = readTarget;
277
+ continue;
278
+ }
279
+ const raw = rawOf(current);
280
+ if (raw !== current) {
281
+ current = raw;
282
+ continue;
283
+ }
284
+ break;
285
+ }
286
+ return current;
287
+ }
288
+ var recordFor = (key) => {
289
+ let record = identityRecords.get(key);
290
+ if (record === void 0) {
291
+ record = {};
292
+ identityRecords.set(key, record);
293
+ }
294
+ return record;
295
+ };
296
+ function identify(value) {
297
+ const target = resolveIdentity(value);
298
+ if (!isObjectLike(target)) return value;
299
+ const record = recordFor(target);
300
+ if (record.token !== void 0) return record.token;
301
+ const token = Object.freeze({});
302
+ record.token = token;
303
+ return token;
304
+ }
305
+ var internIdentity = (key) => {
306
+ const resolved = resolveIdentity(key);
307
+ const record = recordFor(resolved);
308
+ if (record.id !== void 0) return record.id;
309
+ const id = nextInternId;
310
+ nextInternId += 1;
311
+ record.id = id;
312
+ return id;
313
+ };
314
+ function isSameIdentity(first, second) {
315
+ const resolvedFirst = resolveIdentity(first);
316
+ const resolvedSecond = resolveIdentity(second);
317
+ return resolvedFirst === resolvedSecond;
318
+ }
319
+
320
+ // src/peelReadProxy.ts
321
+ function peelReadProxy(value) {
322
+ if (!isObjectLike(value)) return value;
323
+ let current = value;
324
+ while (isObjectLike(current)) {
325
+ const registeredTarget = getRegisteredReadProxyTarget(current);
326
+ if (registeredTarget === void 0) break;
327
+ current = registeredTarget;
328
+ }
329
+ return current;
330
+ }
331
+
260
332
  // src/handle.ts
261
333
  function handleOf(state) {
262
- const raw = rawOf(state);
334
+ const peeled = peelReadProxy(state);
335
+ if (!isObjectLike(peeled)) return void 0;
336
+ const raw = rawOf(peeled);
263
337
  const record = recordOf(raw);
264
338
  if (record === void 0) return void 0;
265
339
  for (const handle of record.memberships.keys()) {
@@ -289,6 +363,7 @@ function batch(callback, meta) {
289
363
 
290
364
  // src/emit/emitterDeliver.ts
291
365
  var runDelivery = (pending, failures) => {
366
+ pending.handle.lastDirty = pending.dirty;
292
367
  for (const deliver of pending.deliveries) {
293
368
  try {
294
369
  deliver(pending.operations);
@@ -305,9 +380,11 @@ var raiseFailures = (failures) => {
305
380
  var queuedDeliveries = [];
306
381
  var deliveryFailures = [];
307
382
  var isDraining = false;
308
- var prepareDelivery = (handle, operations) => ({
383
+ var prepareDelivery = (handle, operations, dirty) => ({
309
384
  deliveries: [...handle.subscribers.values()],
310
- operations
385
+ operations,
386
+ handle,
387
+ dirty
311
388
  });
312
389
  var enqueueDelivery = (pending) => {
313
390
  queuedDeliveries.push(pending);
@@ -345,19 +422,21 @@ function recordOperation(handle, raw, pending) {
345
422
  }
346
423
  handle.pending.push(pending);
347
424
  byKey.set(pending.key, handle.pending.length - 1);
348
- if (handle.isFlushScheduled) return;
349
- handle.isFlushScheduled = true;
425
+ if (handle.scheduledFlush !== void 0) return;
350
426
  const run = () => {
351
- flush(handle);
427
+ if (handle.scheduledFlush !== run) return;
428
+ flushWindow(handle);
352
429
  };
430
+ handle.scheduledFlush = run;
353
431
  void Promise.resolve().then(() => {
432
+ if (handle.scheduledFlush !== run) return;
354
433
  const emitOn = handle.emitOn;
355
434
  if (emitOn === void 0) run();
356
435
  else emitOn(run);
357
436
  });
358
437
  }
359
- function flush(handle) {
360
- handle.isFlushScheduled = false;
438
+ function flushWindow(handle) {
439
+ handle.scheduledFlush = void 0;
361
440
  const pending = handle.pending.splice(0);
362
441
  handle.pendingIndex.clear();
363
442
  const operations = new Array();
@@ -372,6 +451,7 @@ function flush(handle) {
372
451
  };
373
452
  operations.push(Object.freeze(operation));
374
453
  }
454
+ if (operations.length === 0) return;
375
455
  const edges = /* @__PURE__ */ new Map();
376
456
  const nodes = /* @__PURE__ */ new Set();
377
457
  for (const operation of operations) {
@@ -385,15 +465,10 @@ function flush(handle) {
385
465
  nodes.add(raw);
386
466
  }
387
467
  const dirty = { edges, nodes };
388
- handle.lastDirty = dirty;
389
- if (operations.length === 0) return;
390
- enqueueDelivery(prepareDelivery(handle, operations));
468
+ enqueueDelivery(prepareDelivery(handle, operations, dirty));
391
469
  drainDeliveries();
392
470
  }
393
471
 
394
- // src/utils/predicates.ts
395
- var isObjectLike = (value) => value !== null && (typeof value === "object" || typeof value === "function");
396
-
397
472
  // src/proxy.ts
398
473
  var prototypeFunctionOf = (target, key) => {
399
474
  for (let holder = Reflect.getPrototypeOf(target); holder !== null; holder = Reflect.getPrototypeOf(holder)) {
@@ -587,7 +662,7 @@ var handler = {
587
662
  // src/createMutableState.ts
588
663
  installProxyHandler(handler);
589
664
  function createMutableState(properties, options) {
590
- const incoming = properties;
665
+ const incoming = peelReadProxy(properties);
591
666
  if (typeof incoming !== "object" || incoming === null) return properties;
592
667
  if (isIgnored(incoming)) return properties;
593
668
  if (Object.isFrozen(incoming)) return properties;
@@ -601,83 +676,13 @@ function createMutableState(properties, options) {
601
676
  emitOn: options?.emitOn,
602
677
  subscribers: /* @__PURE__ */ new Map(),
603
678
  pending: [],
604
- pendingIndex: /* @__PURE__ */ new Map(),
605
- isFlushScheduled: false
679
+ pendingIndex: /* @__PURE__ */ new Map()
606
680
  };
607
681
  const proxy = proxyOf(root);
608
682
  attachRoot(handle, root, exempt);
609
683
  return proxy;
610
684
  }
611
685
 
612
- // src/identity.ts
613
- var readProxyTargets = /* @__PURE__ */ new WeakMap();
614
- var identityRecords = /* @__PURE__ */ new WeakMap();
615
- var nextInternId = 0;
616
- var registerReadProxyTarget = (readProxy, target) => {
617
- readProxyTargets.set(readProxy, target);
618
- };
619
- var getRegisteredReadProxyTarget = (readProxy) => readProxyTargets.get(readProxy);
620
- function resolveIdentity(value) {
621
- let current = value;
622
- while (isObjectLike(current)) {
623
- const readTarget = getRegisteredReadProxyTarget(current);
624
- if (readTarget !== void 0 && readTarget !== current) {
625
- current = readTarget;
626
- continue;
627
- }
628
- const raw = rawOf(current);
629
- if (raw !== current) {
630
- current = raw;
631
- continue;
632
- }
633
- break;
634
- }
635
- return current;
636
- }
637
- var recordFor = (key) => {
638
- let record = identityRecords.get(key);
639
- if (record === void 0) {
640
- record = {};
641
- identityRecords.set(key, record);
642
- }
643
- return record;
644
- };
645
- function identify(value) {
646
- const target = resolveIdentity(value);
647
- if (!isObjectLike(target)) return value;
648
- const record = recordFor(target);
649
- if (record.token !== void 0) return record.token;
650
- const token = Object.freeze({});
651
- record.token = token;
652
- return token;
653
- }
654
- var internIdentity = (key) => {
655
- const resolved = resolveIdentity(key);
656
- const record = recordFor(resolved);
657
- if (record.id !== void 0) return record.id;
658
- const id = nextInternId;
659
- nextInternId += 1;
660
- record.id = id;
661
- return id;
662
- };
663
- function isSameIdentity(first, second) {
664
- const resolvedFirst = resolveIdentity(first);
665
- const resolvedSecond = resolveIdentity(second);
666
- return resolvedFirst === resolvedSecond;
667
- }
668
-
669
- // src/peelReadProxy.ts
670
- function peelReadProxy(value) {
671
- if (!isObjectLike(value)) return value;
672
- let current = value;
673
- while (isObjectLike(current)) {
674
- const registeredTarget = getRegisteredReadProxyTarget(current);
675
- if (registeredTarget === void 0) break;
676
- current = registeredTarget;
677
- }
678
- return current;
679
- }
680
-
681
686
  // src/isState.ts
682
687
  function isState(value) {
683
688
  const resolved = peelReadProxy(value);
@@ -1172,6 +1177,11 @@ function subscribe(state, listener) {
1172
1177
  return addStateListener(state, listener, listener);
1173
1178
  }
1174
1179
 
1180
+ // src/flush.ts
1181
+ function flush(state) {
1182
+ flushWindow(requireHandle(state, "opshot: flush requires a state"));
1183
+ }
1184
+
1175
1185
  // src/react/propWalk.ts
1176
1186
  var stateKeysByContainer = /* @__PURE__ */ new WeakMap();
1177
1187
  var noStateKeys = /* @__PURE__ */ new Set();
@@ -1678,4 +1688,4 @@ function useMutableState(properties, options) {
1678
1688
  return readProxy;
1679
1689
  }
1680
1690
 
1681
- export { TrackedDate, TrackedMap, TrackedSet, batch, createMutableState, identify, ignore, isSameIdentity, isState, scope, subscribe, unsafeTrack, useMutableState };
1691
+ export { TrackedDate, TrackedMap, TrackedSet, batch, createMutableState, flush, identify, ignore, isSameIdentity, isState, scope, subscribe, unsafeTrack, useMutableState };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opshot",
3
- "version": "0.5.0",
3
+ "version": "0.5.2",
4
4
  "description": "Plain-object state for React: mutate it directly, re-render only the components that read what changed, and track every change operation",
5
5
  "type": "module",
6
6
  "exports": {