reactronic 0.22.312 → 0.22.315

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,4 +1,5 @@
1
1
  export { all, pause } from './util/Utils';
2
+ export { Collection, Item, CollectionReader } from './util/Collection';
2
3
  export { SealedArray } from './util/SealedArray';
3
4
  export { SealedMap } from './util/SealedMap';
4
5
  export { SealedSet } from './util/SealedSet';
@@ -6,7 +7,9 @@ export { MemberOptions, SnapshotOptions, Kind, Reentrance, LoggingOptions, Profi
6
7
  export { Worker } from './Worker';
7
8
  export { Controller } from './Controller';
8
9
  export { Ref, ToggleRef, BoolOnly, GivenTypeOnly } from './Ref';
9
- export { ReactiveObject, ReactiveArray, ReactiveMap } from './impl/Hooks';
10
+ export { ReactiveObject } from './impl/Hooks';
11
+ export { ReactiveArray } from './impl/ReactiveArray';
12
+ export { ReactiveMap } from './impl/ReactiveMap';
10
13
  export { Changeset } from './impl/Changeset';
11
14
  export { Transaction } from './impl/Transaction';
12
15
  export { Monitor } from './impl/Monitor';
@@ -1,11 +1,14 @@
1
1
  export { all, pause } from './util/Utils';
2
+ export { Collection } from './util/Collection';
2
3
  export { SealedArray } from './util/SealedArray';
3
4
  export { SealedMap } from './util/SealedMap';
4
5
  export { SealedSet } from './util/SealedSet';
5
6
  export { Kind, Reentrance, LoggingLevel } from './Options';
6
7
  export { Controller } from './Controller';
7
8
  export { Ref, ToggleRef } from './Ref';
8
- export { ReactiveObject, ReactiveArray, ReactiveMap } from './impl/Hooks';
9
+ export { ReactiveObject } from './impl/Hooks';
10
+ export { ReactiveArray } from './impl/ReactiveArray';
11
+ export { ReactiveMap } from './impl/ReactiveMap';
9
12
  export { Changeset } from './impl/Changeset';
10
13
  export { Transaction } from './impl/Transaction';
11
14
  export { Monitor } from './impl/Monitor';
@@ -4,55 +4,12 @@ import { LoggingOptions, ProfilingOptions } from '../Logging';
4
4
  import { MemberName, ObjectHandle, StandaloneMode } from './Data';
5
5
  import { Journal } from './Journal';
6
6
  import { Monitor } from './Monitor';
7
- export declare abstract class ReactiveObject {
8
- constructor();
7
+ export declare abstract class HookedObject {
8
+ protected constructor(reactive: boolean);
9
9
  [Symbol.toStringTag](): string;
10
10
  }
11
- export declare class ReactiveArray<T> extends ReactiveObject {
12
- private a;
13
- get length(): number;
14
- set length(n: number);
15
- get(n: number): T;
16
- set(n: number, item: T): void;
17
- toString(): string;
18
- toLocaleString(): string;
19
- pop(): T | undefined;
20
- push(...items: T[]): number;
21
- concat(...items: (T | ConcatArray<T>)[]): T[];
22
- join(separator?: string): string;
23
- reverse(): T[];
24
- shift(): T | undefined;
25
- slice(start?: number, end?: number): T[];
26
- sort(compareFn?: (a: T, b: T) => number): this;
27
- splice(start: number, deleteCount?: number): T[];
28
- unshift(...items: T[]): number;
29
- indexOf(searchElement: T, fromIndex?: number): number;
30
- lastIndexOf(searchElement: T, fromIndex?: number): number;
31
- every(predicate: (value: T, index: number, array: T[]) => unknown, thisArg?: any): boolean;
32
- some(predicate: (value: T, index: number, array: T[]) => unknown, thisArg?: any): boolean;
33
- forEach(callbackfn: (value: T, index: number, array: T[]) => void, thisArg?: any): void;
34
- map<U>(callbackfn: (value: T, index: number, array: T[]) => U, thisArg?: any): U[];
35
- filter(predicate: (value: T, index: number, array: T[]) => unknown, thisArg?: any): T[];
36
- reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue: T): T;
37
- reduceRight(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue: T): T;
38
- entries(): IterableIterator<[number, T]>;
39
- keys(): IterableIterator<number>;
40
- values(): IterableIterator<T>;
41
- private get mutable();
42
- }
43
- export declare class ReactiveMap<K, V> extends ReactiveObject {
44
- private m;
45
- clear(): void;
46
- delete(key: K): boolean;
47
- forEach(callbackfn: (value: V, key: K, map: Map<K, V>) => void, thisArg?: any): void;
48
- get(key: K): V | undefined;
49
- has(key: K): boolean;
50
- set(key: K, value: V): this;
51
- get size(): number;
52
- entries(): IterableIterator<[K, V]>;
53
- keys(): IterableIterator<K>;
54
- values(): IterableIterator<V>;
55
- private get mutable();
11
+ export declare abstract class ReactiveObject extends HookedObject {
12
+ constructor();
56
13
  }
57
14
  export declare class OptionsImpl implements MemberOptions {
58
15
  readonly getter: Function;
@@ -76,7 +33,10 @@ export declare class Hooks implements ProxyHandler<ObjectHandle> {
76
33
  static mainThreadBlockingWarningThreshold: number;
77
34
  static asyncActionDurationWarningThreshold: number;
78
35
  static sensitivity: boolean;
79
- static readonly handler: Hooks;
36
+ static readonly transactional: Hooks;
37
+ static readonly reactive: Hooks;
38
+ readonly isReactive: boolean;
39
+ constructor(isReactive: boolean);
80
40
  getPrototypeOf(h: ObjectHandle): object | null;
81
41
  get(h: ObjectHandle, m: MemberName, receiver: any): any;
82
42
  set(h: ObjectHandle, m: MemberName, value: any, receiver: any): boolean;
@@ -87,10 +47,11 @@ export declare class Hooks implements ProxyHandler<ObjectHandle> {
87
47
  static decorateOperation(implicit: boolean, decorator: Function, options: Partial<MemberOptions>, proto: any, member: MemberName, pd: PropertyDescriptor | undefined): any;
88
48
  static decorateOperationParametrized(decorator: Function, options: Partial<MemberOptions>): F<any>;
89
49
  static acquireHandle(obj: any): ObjectHandle;
90
- static createHandleForReactiveObject(proto: any, data: any, blank: any, hint: string): ObjectHandle;
50
+ static createHandleForReactronicObject(proto: any, data: any, blank: any, hint: string, reactive: boolean): ObjectHandle;
91
51
  static setProfilingMode(isOn: boolean, options?: Partial<ProfilingOptions>): void;
92
52
  static sensitive<T>(sensitivity: boolean, func: F<T>, ...args: any[]): T;
93
53
  static setHint<T>(obj: T, hint: string | undefined): T;
54
+ static getHint<T>(obj: T): string;
94
55
  static createOperation: (h: ObjectHandle, m: MemberName, options: OptionsImpl) => F<any>;
95
56
  static rememberOperationOptions: (proto: any, m: MemberName, getter: Function | undefined, setter: Function | undefined, enumerable: boolean, configurable: boolean, options: Partial<MemberOptions>, implicit: boolean) => OptionsImpl;
96
57
  }
@@ -1,14 +1,13 @@
1
1
  import { UNDEF } from '../util/Utils';
2
- import { Sealant } from '../util/Sealant';
3
2
  import { Log, misuse } from '../util/Dbg';
4
3
  import { Kind, Reentrance } from '../Options';
5
4
  import { ObjectSnapshot, ObjectHandle, Subscription, Meta } from './Data';
6
5
  import { Changeset, Dump, EMPTY_SNAPSHOT } from './Changeset';
7
- export class ReactiveObject {
8
- constructor() {
6
+ export class HookedObject {
7
+ constructor(reactive) {
9
8
  const proto = new.target.prototype;
10
9
  const initial = Meta.getFrom(proto, Meta.Initial);
11
- const h = Hooks.createHandleForReactiveObject(proto, this, initial, new.target.name);
10
+ const h = Hooks.createHandleForReactronicObject(proto, this, initial, new.target.name, reactive);
12
11
  return h.proxy;
13
12
  }
14
13
  [Symbol.toStringTag]() {
@@ -16,66 +15,9 @@ export class ReactiveObject {
16
15
  return Dump.obj(h);
17
16
  }
18
17
  }
19
- export class ReactiveArray extends ReactiveObject {
20
- constructor() {
21
- super(...arguments);
22
- this.a = new Array();
23
- }
24
- get length() { return this.a.length; }
25
- set length(n) { this.a.length = n; }
26
- get(n) { return this.a[n]; }
27
- set(n, item) { this.mutable[n] = item; }
28
- toString() { return this.a.toString(); }
29
- toLocaleString() { return this.a.toLocaleString(); }
30
- pop() { return this.mutable.pop(); }
31
- push(...items) { return this.mutable.push(...items); }
32
- concat(...items) { return this.a.concat(...items); }
33
- join(separator) { return this.a.join(separator); }
34
- reverse() { return this.mutable.reverse(); }
35
- shift() { return this.mutable.shift(); }
36
- slice(start, end) { return this.a.slice(start, end); }
37
- sort(compareFn) { this.mutable.sort(compareFn); return this; }
38
- splice(start, deleteCount, ...items) { return this.mutable.splice(start, deleteCount, ...items); }
39
- unshift(...items) { return this.mutable.unshift(...items); }
40
- indexOf(searchElement, fromIndex) { return this.a.indexOf(searchElement, fromIndex); }
41
- lastIndexOf(searchElement, fromIndex) { return this.a.lastIndexOf(searchElement, fromIndex); }
42
- every(predicate, thisArg) { return this.a.every(predicate, thisArg); }
43
- some(predicate, thisArg) { return this.a.some(predicate, thisArg); }
44
- forEach(callbackfn, thisArg) { return this.a.forEach(callbackfn, thisArg); }
45
- map(callbackfn, thisArg) { return this.a.map(callbackfn, thisArg); }
46
- filter(predicate, thisArg) { return this.a.filter(predicate, thisArg); }
47
- reduce(callbackfn, initialValue) { return this.a.reduce(callbackfn, initialValue); }
48
- reduceRight(callbackfn, initialValue) { return this.a.reduceRight(callbackfn, initialValue); }
49
- entries() { return this.a.entries(); }
50
- keys() { return this.a.keys(); }
51
- values() { return this.a.values(); }
52
- get mutable() {
53
- const createCopy = this.a[Sealant.CreateCopy];
54
- if (createCopy)
55
- return this.a = createCopy.call(this.a);
56
- return this.a;
57
- }
58
- }
59
- export class ReactiveMap extends ReactiveObject {
18
+ export class ReactiveObject extends HookedObject {
60
19
  constructor() {
61
- super(...arguments);
62
- this.m = new Map();
63
- }
64
- clear() { this.mutable.clear(); }
65
- delete(key) { return this.mutable.delete(key); }
66
- forEach(callbackfn, thisArg) { this.m.forEach(callbackfn, thisArg); }
67
- get(key) { return this.m.get(key); }
68
- has(key) { return this.m.has(key); }
69
- set(key, value) { this.mutable.set(key, value); return this; }
70
- get size() { return this.m.size; }
71
- entries() { return this.m.entries(); }
72
- keys() { return this.m.keys(); }
73
- values() { return this.m.values(); }
74
- get mutable() {
75
- const createCopy = this.m[Sealant.CreateCopy];
76
- if (createCopy)
77
- return this.m = createCopy.call(this.m);
78
- return this.m;
20
+ super(true);
79
21
  }
80
22
  }
81
23
  const DEFAULT_OPTIONS = Object.freeze({
@@ -113,6 +55,9 @@ function merge(def, existing, patch, implicit) {
113
55
  return patch !== undefined && (existing === def || !implicit) ? patch : existing;
114
56
  }
115
57
  export class Hooks {
58
+ constructor(isReactive) {
59
+ this.isReactive = isReactive;
60
+ }
116
61
  getPrototypeOf(h) {
117
62
  return Reflect.getPrototypeOf(h.data);
118
63
  }
@@ -123,7 +68,8 @@ export class Hooks {
123
68
  const os = cs.getObjectSnapshot(h, m);
124
69
  result = os.data[m];
125
70
  if (result instanceof Subscription && !result.isOperation) {
126
- Changeset.markUsed(result, os, m, h, Kind.Plain, false);
71
+ if (this.isReactive)
72
+ Changeset.markUsed(result, os, m, h, Kind.Plain, false);
127
73
  result = result.content;
128
74
  }
129
75
  else
@@ -182,11 +128,11 @@ export class Hooks {
182
128
  if (reactive) {
183
129
  const get = function () {
184
130
  const h = Hooks.acquireHandle(this);
185
- return Hooks.handler.get(h, m, this);
131
+ return Hooks.reactive.get(h, m, this);
186
132
  };
187
133
  const set = function (value) {
188
134
  const h = Hooks.acquireHandle(this);
189
- return Hooks.handler.set(h, m, value, this);
135
+ return Hooks.reactive.set(h, m, value, this);
190
136
  };
191
137
  const enumerable = true;
192
138
  const configurable = false;
@@ -235,16 +181,17 @@ export class Hooks {
235
181
  throw misuse('only objects can be reactive');
236
182
  const initial = Meta.getFrom(Object.getPrototypeOf(obj), Meta.Initial);
237
183
  const os = new ObjectSnapshot(EMPTY_SNAPSHOT.changeset, EMPTY_SNAPSHOT, Object.assign({}, initial));
238
- h = new ObjectHandle(obj, obj, Hooks.handler, os, obj.constructor.name);
184
+ h = new ObjectHandle(obj, obj, Hooks.reactive, os, obj.constructor.name);
239
185
  Meta.set(os.data, Meta.Handle, h);
240
186
  Meta.set(obj, Meta.Handle, h);
241
187
  Meta.set(os.data, Meta.Revision, new Subscription(1));
242
188
  }
243
189
  return h;
244
190
  }
245
- static createHandleForReactiveObject(proto, data, blank, hint) {
191
+ static createHandleForReactronicObject(proto, data, blank, hint, reactive) {
246
192
  const ctx = Changeset.edit();
247
- const h = new ObjectHandle(data, undefined, Hooks.handler, EMPTY_SNAPSHOT, hint);
193
+ const hooks = reactive ? Hooks.reactive : Hooks.transactional;
194
+ const h = new ObjectHandle(data, undefined, hooks, EMPTY_SNAPSHOT, hint);
248
195
  ctx.getEditableObjectSnapshot(h, Meta.Handle, blank);
249
196
  if (!Hooks.reactionsAutoStartDisabled)
250
197
  for (const m in Meta.getFrom(proto, Meta.Reactions))
@@ -282,13 +229,18 @@ export class Hooks {
282
229
  }
283
230
  return obj;
284
231
  }
232
+ static getHint(obj) {
233
+ const h = Hooks.acquireHandle(obj);
234
+ return h.hint;
235
+ }
285
236
  }
286
237
  Hooks.reactionsAutoStartDisabled = false;
287
238
  Hooks.repetitiveUsageWarningThreshold = Number.MAX_SAFE_INTEGER;
288
239
  Hooks.mainThreadBlockingWarningThreshold = Number.MAX_SAFE_INTEGER;
289
240
  Hooks.asyncActionDurationWarningThreshold = Number.MAX_SAFE_INTEGER;
290
241
  Hooks.sensitivity = false;
291
- Hooks.handler = new Hooks();
242
+ Hooks.transactional = new Hooks(false);
243
+ Hooks.reactive = new Hooks(true);
292
244
  Hooks.createOperation = function (h, m, options) {
293
245
  throw misuse('createOperation should never be called');
294
246
  };
@@ -12,7 +12,7 @@ export declare class MonitorImpl extends Monitor {
12
12
  counter: number;
13
13
  workers: Set<Worker>;
14
14
  duration: number;
15
- internals: {
15
+ readonly internals: {
16
16
  started: number;
17
17
  activationDelay: number;
18
18
  activationTimeout: undefined;
@@ -51,7 +51,8 @@ export class MonitorImpl extends Monitor {
51
51
  return m;
52
52
  }
53
53
  static activate(mon, delay) {
54
- if (mon.internals.started === 0) {
54
+ const active = mon.counter > 0;
55
+ if (mon.internals.started === 0 && active) {
55
56
  mon.duration = 0;
56
57
  mon.internals.started = performance.now();
57
58
  MonitorImpl.tick(mon);
@@ -60,7 +61,7 @@ export class MonitorImpl extends Monitor {
60
61
  if (mon.internals.activationTimeout === undefined)
61
62
  mon.internals.activationTimeout = setTimeout(() => Transaction.run({ hint: 'Monitor.activate', standalone: 'isolated' }, MonitorImpl.activate, mon, -1), delay);
62
63
  }
63
- else if (mon.counter > 0)
64
+ else if (active)
64
65
  mon.isActive = true;
65
66
  }
66
67
  static deactivate(mon, delay) {
@@ -80,7 +81,7 @@ export class MonitorImpl extends Monitor {
80
81
  }
81
82
  static tick(mon) {
82
83
  if (mon.internals.started !== 0) {
83
- Transaction.run(null, () => {
84
+ Transaction.run(MONITOR_TICK_OPTIONS, () => {
84
85
  const resolution = mon.internals.durationResolution;
85
86
  mon.duration = Math.round(resolution * (performance.now() - mon.internals.started)) / resolution;
86
87
  });
@@ -90,3 +91,6 @@ export class MonitorImpl extends Monitor {
90
91
  }
91
92
  }
92
93
  }
94
+ const MONITOR_TICK_OPTIONS = Object.freeze({
95
+ hint: 'Monitor.tick',
96
+ });
@@ -9,7 +9,7 @@ import { Hooks, OptionsImpl } from './Hooks';
9
9
  import { JournalImpl } from './Journal';
10
10
  const BOOT_ARGS = [];
11
11
  const BOOT_CAUSE = '<boot>';
12
- const EMPTY_HANDLE = new ObjectHandle(undefined, undefined, Hooks.handler, EMPTY_SNAPSHOT, '<empty>');
12
+ const EMPTY_HANDLE = new ObjectHandle(undefined, undefined, Hooks.reactive, EMPTY_SNAPSHOT, '<empty>');
13
13
  export class OperationController extends Controller {
14
14
  constructor(h, m) {
15
15
  super();
@@ -0,0 +1,33 @@
1
+ import { ReactiveObject } from './Hooks';
2
+ export declare class ReactiveArray<T> extends ReactiveObject {
3
+ private a;
4
+ get length(): number;
5
+ set length(n: number);
6
+ get(n: number): T;
7
+ set(n: number, item: T): void;
8
+ toString(): string;
9
+ toLocaleString(): string;
10
+ pop(): T | undefined;
11
+ push(...items: T[]): number;
12
+ concat(...items: (T | ConcatArray<T>)[]): T[];
13
+ join(separator?: string): string;
14
+ reverse(): T[];
15
+ shift(): T | undefined;
16
+ slice(start?: number, end?: number): T[];
17
+ sort(compareFn?: (a: T, b: T) => number): this;
18
+ splice(start: number, deleteCount?: number): T[];
19
+ unshift(...items: T[]): number;
20
+ indexOf(searchElement: T, fromIndex?: number): number;
21
+ lastIndexOf(searchElement: T, fromIndex?: number): number;
22
+ every(predicate: (value: T, index: number, array: T[]) => unknown, thisArg?: any): boolean;
23
+ some(predicate: (value: T, index: number, array: T[]) => unknown, thisArg?: any): boolean;
24
+ forEach(callbackfn: (value: T, index: number, array: T[]) => void, thisArg?: any): void;
25
+ map<U>(callbackfn: (value: T, index: number, array: T[]) => U, thisArg?: any): U[];
26
+ filter(predicate: (value: T, index: number, array: T[]) => unknown, thisArg?: any): T[];
27
+ reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue: T): T;
28
+ reduceRight(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue: T): T;
29
+ entries(): IterableIterator<[number, T]>;
30
+ keys(): IterableIterator<number>;
31
+ values(): IterableIterator<T>;
32
+ private get mutable();
33
+ }
@@ -0,0 +1,42 @@
1
+ import { Sealant } from '../util/Sealant';
2
+ import { ReactiveObject } from './Hooks';
3
+ export class ReactiveArray extends ReactiveObject {
4
+ constructor() {
5
+ super(...arguments);
6
+ this.a = new Array();
7
+ }
8
+ get length() { return this.a.length; }
9
+ set length(n) { this.a.length = n; }
10
+ get(n) { return this.a[n]; }
11
+ set(n, item) { this.mutable[n] = item; }
12
+ toString() { return this.a.toString(); }
13
+ toLocaleString() { return this.a.toLocaleString(); }
14
+ pop() { return this.mutable.pop(); }
15
+ push(...items) { return this.mutable.push(...items); }
16
+ concat(...items) { return this.a.concat(...items); }
17
+ join(separator) { return this.a.join(separator); }
18
+ reverse() { return this.mutable.reverse(); }
19
+ shift() { return this.mutable.shift(); }
20
+ slice(start, end) { return this.a.slice(start, end); }
21
+ sort(compareFn) { this.mutable.sort(compareFn); return this; }
22
+ splice(start, deleteCount, ...items) { return this.mutable.splice(start, deleteCount, ...items); }
23
+ unshift(...items) { return this.mutable.unshift(...items); }
24
+ indexOf(searchElement, fromIndex) { return this.a.indexOf(searchElement, fromIndex); }
25
+ lastIndexOf(searchElement, fromIndex) { return this.a.lastIndexOf(searchElement, fromIndex); }
26
+ every(predicate, thisArg) { return this.a.every(predicate, thisArg); }
27
+ some(predicate, thisArg) { return this.a.some(predicate, thisArg); }
28
+ forEach(callbackfn, thisArg) { return this.a.forEach(callbackfn, thisArg); }
29
+ map(callbackfn, thisArg) { return this.a.map(callbackfn, thisArg); }
30
+ filter(predicate, thisArg) { return this.a.filter(predicate, thisArg); }
31
+ reduce(callbackfn, initialValue) { return this.a.reduce(callbackfn, initialValue); }
32
+ reduceRight(callbackfn, initialValue) { return this.a.reduceRight(callbackfn, initialValue); }
33
+ entries() { return this.a.entries(); }
34
+ keys() { return this.a.keys(); }
35
+ values() { return this.a.values(); }
36
+ get mutable() {
37
+ const createCopy = this.a[Sealant.CreateCopy];
38
+ if (createCopy)
39
+ return this.a = createCopy.call(this.a);
40
+ return this.a;
41
+ }
42
+ }
@@ -0,0 +1,15 @@
1
+ import { ReactiveObject } from './Hooks';
2
+ export declare class ReactiveMap<K, V> extends ReactiveObject {
3
+ private m;
4
+ clear(): void;
5
+ delete(key: K): boolean;
6
+ forEach(callbackfn: (value: V, key: K, map: Map<K, V>) => void, thisArg?: any): void;
7
+ get(key: K): V | undefined;
8
+ has(key: K): boolean;
9
+ set(key: K, value: V): this;
10
+ get size(): number;
11
+ entries(): IterableIterator<[K, V]>;
12
+ keys(): IterableIterator<K>;
13
+ values(): IterableIterator<V>;
14
+ private get mutable();
15
+ }
@@ -0,0 +1,24 @@
1
+ import { Sealant } from '../util/Sealant';
2
+ import { ReactiveObject } from './Hooks';
3
+ export class ReactiveMap extends ReactiveObject {
4
+ constructor() {
5
+ super(...arguments);
6
+ this.m = new Map();
7
+ }
8
+ clear() { this.mutable.clear(); }
9
+ delete(key) { return this.mutable.delete(key); }
10
+ forEach(callbackfn, thisArg) { this.m.forEach(callbackfn, thisArg); }
11
+ get(key) { return this.m.get(key); }
12
+ has(key) { return this.m.has(key); }
13
+ set(key, value) { this.mutable.set(key, value); return this; }
14
+ get size() { return this.m.size; }
15
+ entries() { return this.m.entries(); }
16
+ keys() { return this.m.keys(); }
17
+ values() { return this.m.values(); }
18
+ get mutable() {
19
+ const createCopy = this.m[Sealant.CreateCopy];
20
+ if (createCopy)
21
+ return this.m = createCopy.call(this.m);
22
+ return this.m;
23
+ }
24
+ }
@@ -0,0 +1,61 @@
1
+ export declare type GetItemKey<T = unknown> = (item: T) => string | undefined;
2
+ export interface CollectionReader<T> {
3
+ readonly strict: boolean;
4
+ readonly count: number;
5
+ readonly addedCount: number;
6
+ readonly removedCount: number;
7
+ readonly isMergeInProgress: boolean;
8
+ lookup(key: string): Item<T> | undefined;
9
+ claim(key: string): Item<T> | undefined;
10
+ add(self: T): Item<T>;
11
+ remove(item: Item<T>): void;
12
+ move(item: Item<T>, after: Item<T>): void;
13
+ beginMerge(): void;
14
+ endMerge(error?: unknown): void;
15
+ resetAddedAndRemovedLists(): void;
16
+ items(): Generator<Item<T>>;
17
+ addedItems(reset?: boolean): Generator<Item<T>>;
18
+ removedItems(reset?: boolean): Generator<Item<T>>;
19
+ isAdded(item: Item<T>): boolean;
20
+ isMoved(item: Item<T>): boolean;
21
+ isRemoved(item: Item<T>): boolean;
22
+ isCurrent(item: Item<T>): boolean;
23
+ }
24
+ export interface Item<T> {
25
+ readonly self: T;
26
+ readonly prev?: Item<T>;
27
+ aux?: Item<T>;
28
+ }
29
+ export declare class Collection<T> implements CollectionReader<T> {
30
+ readonly strict: boolean;
31
+ readonly getKey: GetItemKey<T>;
32
+ private map;
33
+ private tag;
34
+ private current;
35
+ private added;
36
+ private removed;
37
+ private lastNotFoundKey;
38
+ private strictNextItem?;
39
+ constructor(strict: boolean, getKey: GetItemKey<T>);
40
+ get count(): number;
41
+ get addedCount(): number;
42
+ get removedCount(): number;
43
+ get isMergeInProgress(): boolean;
44
+ lookup(key: string | undefined): Item<T> | undefined;
45
+ claim(key: string): Item<T> | undefined;
46
+ add(self: T): Item<T>;
47
+ remove(item: Item<T>): void;
48
+ move(item: Item<T>, after: Item<T>): void;
49
+ beginMerge(): void;
50
+ endMerge(error?: unknown): void;
51
+ resetAddedAndRemovedLists(): void;
52
+ items(): Generator<Item<T>>;
53
+ addedItems(reset?: boolean): Generator<Item<T>>;
54
+ removedItems(reset?: boolean): Generator<Item<T>>;
55
+ isAdded(item: Item<T>): boolean;
56
+ isMoved(item: Item<T>): boolean;
57
+ isRemoved(item: Item<T>): boolean;
58
+ isCurrent(item: Item<T>): boolean;
59
+ markAsMoved(item: Item<T>): void;
60
+ static createItem<T>(self: T): Item<T>;
61
+ }
@@ -0,0 +1,275 @@
1
+ export class Collection {
2
+ constructor(strict, getKey) {
3
+ this.strict = strict;
4
+ this.getKey = getKey;
5
+ this.map = new Map();
6
+ this.tag = ~0;
7
+ this.current = new ItemChain();
8
+ this.added = new ItemChain();
9
+ this.removed = new ItemChain();
10
+ this.lastNotFoundKey = undefined;
11
+ this.strictNextItem = undefined;
12
+ }
13
+ get count() {
14
+ return this.current.count;
15
+ }
16
+ get addedCount() {
17
+ return this.added.count;
18
+ }
19
+ get removedCount() {
20
+ return this.removed.count;
21
+ }
22
+ get isMergeInProgress() {
23
+ return this.tag > 0;
24
+ }
25
+ lookup(key) {
26
+ let result = undefined;
27
+ if (key !== undefined && key !== this.lastNotFoundKey) {
28
+ result = this.map.get(key);
29
+ if (result) {
30
+ if (this.getKey(result.self) !== key) {
31
+ this.lastNotFoundKey = key;
32
+ result = undefined;
33
+ }
34
+ }
35
+ else
36
+ this.lastNotFoundKey = key;
37
+ }
38
+ return result;
39
+ }
40
+ claim(key) {
41
+ const tag = this.tag;
42
+ if (tag < 0)
43
+ throw new Error('merge is not in progress');
44
+ let item = this.strictNextItem;
45
+ if (key !== (item ? this.getKey(item.self) : undefined))
46
+ item = this.lookup(key);
47
+ if (item) {
48
+ if (item.tag === tag)
49
+ throw new Error(`duplicate item: ${key}`);
50
+ item.tag = tag;
51
+ if (this.strict && item !== this.strictNextItem)
52
+ item.status = tag;
53
+ this.strictNextItem = item.next;
54
+ this.removed.exclude(item);
55
+ this.current.include(item);
56
+ }
57
+ return item;
58
+ }
59
+ add(self) {
60
+ const key = this.getKey(self);
61
+ if (this.lookup(key) !== undefined)
62
+ throw new Error(`key is already in use: ${key}`);
63
+ let tag = this.tag;
64
+ if (tag < 0) {
65
+ tag = ~this.tag + 1;
66
+ this.tag = ~tag;
67
+ }
68
+ const item = new ItemImpl(self, tag);
69
+ this.map.set(key, item);
70
+ this.lastNotFoundKey = undefined;
71
+ this.strictNextItem = undefined;
72
+ this.current.include(item);
73
+ this.added.aux(item);
74
+ return item;
75
+ }
76
+ remove(item) {
77
+ const t = item;
78
+ if (!this.isRemoved(t)) {
79
+ this.current.exclude(t);
80
+ this.removed.include(t);
81
+ t.tag--;
82
+ }
83
+ }
84
+ move(item, after) {
85
+ throw new Error('not implemented');
86
+ }
87
+ beginMerge() {
88
+ if (this.isMergeInProgress)
89
+ throw new Error('merge is in progress already');
90
+ this.tag = ~this.tag + 1;
91
+ this.strictNextItem = this.current.first;
92
+ this.removed.grab(this.current, false);
93
+ this.added.reset();
94
+ }
95
+ endMerge(error) {
96
+ if (!this.isMergeInProgress)
97
+ throw new Error('merge is ended already');
98
+ this.tag = ~this.tag;
99
+ if (error === undefined) {
100
+ const currentCount = this.current.count;
101
+ if (currentCount > 0) {
102
+ const getKey = this.getKey;
103
+ if (currentCount > this.removed.count) {
104
+ const map = this.map;
105
+ for (const x of this.removed.items())
106
+ map.delete(getKey(x.self));
107
+ }
108
+ else {
109
+ const map = this.map = new Map();
110
+ for (const x of this.current.items())
111
+ map.set(getKey(x.self), x);
112
+ }
113
+ }
114
+ else
115
+ this.map = new Map();
116
+ }
117
+ else {
118
+ this.current.grab(this.removed, true);
119
+ const getKey = this.getKey;
120
+ for (const x of this.added.itemsViaAux()) {
121
+ this.map.delete(getKey(x.self));
122
+ this.current.exclude(x);
123
+ }
124
+ this.added.reset();
125
+ }
126
+ }
127
+ resetAddedAndRemovedLists() {
128
+ this.removed.reset();
129
+ this.added.reset();
130
+ }
131
+ *items() {
132
+ let x = this.current.first;
133
+ while (x !== undefined) {
134
+ const next = x.next;
135
+ yield x;
136
+ x = next;
137
+ }
138
+ }
139
+ *addedItems(reset) {
140
+ let x = this.added.first;
141
+ while (x !== undefined) {
142
+ const next = x.aux;
143
+ if (!this.isRemoved(x))
144
+ yield x;
145
+ x = next;
146
+ }
147
+ if (reset)
148
+ this.added.reset();
149
+ }
150
+ *removedItems(reset) {
151
+ let x = this.removed.first;
152
+ while (x !== undefined) {
153
+ const next = x.next;
154
+ yield x;
155
+ x = next;
156
+ }
157
+ if (reset)
158
+ this.removed.reset();
159
+ }
160
+ isAdded(item) {
161
+ const t = item;
162
+ let tag = this.tag;
163
+ if (tag < 0)
164
+ tag = ~tag;
165
+ return t.status === ~tag && t.tag > 0;
166
+ }
167
+ isMoved(item) {
168
+ const t = item;
169
+ let tag = this.tag;
170
+ if (tag < 0)
171
+ tag = ~tag;
172
+ return t.status === tag && t.tag > 0;
173
+ }
174
+ isRemoved(item) {
175
+ const t = item;
176
+ const tag = this.tag;
177
+ return tag > 0 ? t.tag < tag : t.tag < tag - 1;
178
+ }
179
+ isCurrent(item) {
180
+ const t = item;
181
+ return t.tag === this.tag;
182
+ }
183
+ markAsMoved(item) {
184
+ const t = item;
185
+ if (t.tag > 0)
186
+ t.status = t.tag;
187
+ }
188
+ static createItem(self) {
189
+ return new ItemImpl(self, 0);
190
+ }
191
+ }
192
+ class ItemImpl {
193
+ constructor(self, tag) {
194
+ this.self = self;
195
+ this.tag = tag;
196
+ this.status = ~tag;
197
+ this.next = undefined;
198
+ this.prev = undefined;
199
+ this.aux = undefined;
200
+ }
201
+ }
202
+ class ItemChain {
203
+ constructor() {
204
+ this.count = 0;
205
+ this.first = undefined;
206
+ this.last = undefined;
207
+ }
208
+ *items() {
209
+ let x = this.first;
210
+ while (x !== undefined) {
211
+ const next = x.next;
212
+ yield x;
213
+ x = next;
214
+ }
215
+ }
216
+ *itemsViaAux() {
217
+ let x = this.first;
218
+ while (x !== undefined) {
219
+ const next = x.aux;
220
+ yield x;
221
+ x = next;
222
+ }
223
+ }
224
+ reset() {
225
+ this.count = 0;
226
+ this.first = undefined;
227
+ this.last = undefined;
228
+ }
229
+ grab(from, join) {
230
+ const head = from.first;
231
+ if (join && head) {
232
+ const last = this.last;
233
+ head.prev = last;
234
+ if (last)
235
+ this.last = last.next = head;
236
+ else
237
+ this.first = this.last = head;
238
+ this.count += from.count;
239
+ }
240
+ else {
241
+ this.count = from.count;
242
+ this.first = head;
243
+ this.last = from.last;
244
+ }
245
+ from.reset();
246
+ }
247
+ include(item) {
248
+ const last = this.last;
249
+ item.prev = last;
250
+ item.next = undefined;
251
+ if (last)
252
+ this.last = last.next = item;
253
+ else
254
+ this.first = this.last = item;
255
+ this.count++;
256
+ }
257
+ exclude(item) {
258
+ if (item.prev !== undefined)
259
+ item.prev.next = item.next;
260
+ if (item.next !== undefined)
261
+ item.next.prev = item.prev;
262
+ if (item === this.first)
263
+ this.first = item.next;
264
+ this.count--;
265
+ }
266
+ aux(item) {
267
+ item.aux = undefined;
268
+ const last = this.last;
269
+ if (last)
270
+ this.last = last.aux = item;
271
+ else
272
+ this.first = this.last = item;
273
+ this.count++;
274
+ }
275
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "reactronic",
3
- "version": "0.22.312",
3
+ "version": "0.22.315",
4
4
  "description": "Reactronic - Transactional Reactive State Management",
5
5
  "type": "module",
6
6
  "main": "build/dist/source/api.js",
@@ -30,15 +30,15 @@
30
30
  },
31
31
  "homepage": "https://github.com/nezaboodka/reactronic/blob/master/README.md#readme",
32
32
  "devDependencies": {
33
- "@types/node": "18.0.0",
34
- "@types/react": "18.0.14",
35
- "@typescript-eslint/eslint-plugin": "5.29.0",
36
- "@typescript-eslint/parser": "5.29.0",
37
- "ava": "4.3.0",
33
+ "@types/node": "18.0.5",
34
+ "@types/react": "18.0.15",
35
+ "@typescript-eslint/eslint-plugin": "5.30.6",
36
+ "@typescript-eslint/parser": "5.30.6",
37
+ "ava": "4.3.1",
38
38
  "c8": "7.11.3",
39
- "eslint": "8.18.0",
39
+ "eslint": "8.19.0",
40
40
  "react": "18.2.0",
41
- "ts-node": "10.8.1",
41
+ "ts-node": "10.9.1",
42
42
  "typescript": "4.7.3"
43
43
  },
44
44
  "scripts": {