@cyysummer/projector 0.0.11 → 0.0.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.
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
- * @cyysummer/projector v0.0.11
2
+ * @cyysummer/projector v0.0.13
3
3
  * (c) 2021-PRESENT Chris Liu
4
4
  * @license MIT
5
5
  **/
6
- import k from"on-change";import{cloneDeep as v,debounce as F,set as E}from"lodash-es";import{withEvents as j}from"@cyysummer/core";var f=class extends j(){constructor(t){super();this._paused=!1;this._shadow=v(t)}sendTo(t){this._projectorFactory=typeof t=="function"?t:()=>t}pause(){this._paused=!0}resume(){this._paused=!1}},i=class extends f{receive(e,t){if(this._paused)return;E(this._shadow,e,t);let r=[{path:e,value:t}];this._projectorFactory?.()?.project(this._shadow,...r),this.emit("record",{next:this._shadow,patches:r})}},d=class extends f{constructor(t,r=500){super(t);this._wait=r;this._buffer=new Map;this.receiveCore=F(()=>{let t=[...this._buffer.values()];log.trace("LazyRecorder.receiveCore,",t),this._buffer.clear();for(let r of t)E(this._shadow,r.path,r.value);this._projectorFactory?.()?.project(this._shadow,...t),this.emit("record",{next:this._shadow,patches:t})},this._wait,{trailing:!0})}receive(t,r){this._paused||(this._buffer.set(t.join("."),{path:t,value:r}),this.receiveCore())}};import P from"on-change";var y=class{constructor(e,t){this._schema=e;this._scheduler=t;if(!e)throw new Error("Projector: Schema is required.")}project(e,...t){for(let r of t)this._dispatch(e,r)}_dispatch(e,t){let{path:r,value:o}=t,c=this._resolveEffect(r);c&&(this._ensureScheduler(),this._scheduler.enqueue({path:r.join("."),effect:c,ctx:{source:e,path:r,value:o}}))}_ensureScheduler(){if(!this._scheduler)throw new Error("Projector: no scheduler.")}_resolveEffect(e){let t=this._schema;for(let r of e){if(!t)return null;t=t[r]}return typeof t=="function"?t:null}},p=class extends y{constructor(e,t){super(e,t)}},S=class extends y{withScheduler(e){return typeof e=="function"?this._schedulerFactory=e:this._scheduler=e,this}_ensureScheduler(){if(!this._scheduler&&this._schedulerFactory&&(this._scheduler=this._schedulerFactory()),!this._scheduler)throw new Error("Projector: no scheduler.")}};import{debounce as L}from"lodash-es";var g=class{constructor(e){this._queue=new Map;e&&(this._strategyFactory=()=>e)}enqueue(e){return this._queue.set(e.path,e),this}flush(){return log.trace("Scheduler.flush()",[...this._queue.keys()]),this.flushCore()}reset(){this._queue.clear()}withStrategy(e){return this._strategyFactory=typeof e=="function"?e:()=>e,this}ensureStrategy(){let e=this._strategyFactory?.();if(!e)throw new Error("Scheduler: no strategy.");return e}async run(e){for(let t of e){let{effect:r,ctx:o}=t,c=r(this.ensureStrategy(),o);c instanceof Promise&&await c}}},h=class extends g{constructor(t,r=500){super(t);this.wait=r;this.flushCore=L(async()=>{let t=[...this._queue.values()];this._queue.clear(),this.run(t)},this.wait)}},_=class extends g{async flushCore(){this.ensureStrategy().reset(),this.run([...this._queue.values()])}};import{debounce as C}from"lodash-es";var b=class{constructor(e,t,r=500){this._initial=e;this._schema=t;this._waitTime=r;if(!e||!t||!r)throw new Error("Builder: all params are required.");if(Number(r)<0)throw new Error("Builder: wait time must be >= 0.")}buildDynamic(){this.track();let e=new S(this._schema);return this._scheduler&&e.withScheduler(this._scheduler),this._recorder.sendTo(e),{tracked:this._tracked,recorder:this._recorder,projector:e,scheduler:this._scheduler}}buildStatic(){if(!this._strategy&&!this._strategyFactory)throw new Error("Builder: no strategy was provided. Call `withStrategy()` to provide.");this._scheduler||(this._scheduler=new h),this._flushCallback||log.warn("Builder: no flush callback was provided.");let e=async()=>{await this._scheduler.flush(),this._flushCallback?.()};this.track(),this._scheduler.withStrategy(this._strategy??this._strategyFactory);let t=new p(this._schema,this._scheduler);return this._recorder.sendTo(t),this._recorder.on("record",this._waitTime?C(e,this._waitTime):e),{tracked:this._tracked,recorder:this._recorder,scheduler:this._scheduler}}onFlush(e){return this._flushCallback=e,this}withLazyScheduler(e=500){return this._scheduler=new h(void 0,e),this}withTotalScheduler(){return this._scheduler=new _,this}withStrategy(e){return typeof e=="function"?this._strategyFactory=e:this._strategy=e,this}track(){this._recorder&&this._tracked||(this._recorder=this._waitTime>0?new d(this._initial,this._waitTime):new i(this._initial),this._tracked=P(this._initial,(e,t)=>this._recorder.receive(e,t),{pathAsArray:!0}))}};import{get as m,keys as w}from"lodash-es";var I=u=>{},x=class{at(e){return(t,r)=>{let o=T(r);return t.execute(e,o)}}atWith(e,t){return(o,c)=>{let a=T(c),s=t(a);return o.execute(e,s)}}arrayAt(e,t){return(r,o)=>{let c=o.path?m(o.source,o.path):o.value;if(c&&Array.isArray(c)&&c.length>0){let a={keys:typeof c[0]=="object"?w(c[0]):[],resolveHeader:(s,n)=>String(s),...t};return r.executeArray(e,c,a)}}}arrayAtWith(e,t,r){return(o,c)=>{let a=c.path?m(c.source,c.path):c.value;if(a&&Array.isArray(a)&&a.length>0){let s=a.map(t),n={keys:typeof a[0]=="object"?w(a[0]):[],resolveHeader:(l,A)=>String(l),...r};return o.executeArray(e,s,n)}}}loop(e){return async(t,r)=>{let o=T(r);if(Array.isArray(o)){let c=o;for(let a=0;a<c.length;a++){let s=c[a],l=e(s,a)(t,{value:s});l instanceof Promise&&await l}}else throw new Error(`Effect: Value at path '${r.path}' is not an array.`)}}sourceWith(e,t){return(r,o)=>{let c=t(o.source);return r.execute(e,c)}}raw(e,t){return(r,o)=>r.execute(e,t)}sequence(e){return async(t,r)=>{let o=T(r);for(let c of e)await c(t,{value:o})}}sequenceWith(e,t){return async(o,c)=>{let a=T(c),s=t(a);for(let n of e)await n(o,{value:s})}}when(e,t,r=I){return(o,c)=>{let a=T(c);return(e(a)?t:r)(o,{value:a})}}whenFromSource(e,t,r=I){return(o,c)=>(e(c.source)?t:r)(o,c)}},T=u=>u.source&&u.path?m(u.source,u.path):u.value;function X(u){let e=new i(u);return[k(u,(r,o)=>e.receive(r,o),{pathAsArray:!0}),e]}export{b as Builder,S as DynamicProjector,x as EffectFactory,d as LazyRecorder,h as LazyScheduler,p as Projector,y as ProjectorBase,i as Recorder,f as RecorderBase,g as Scheduler,_ as TotalScheduler,X as track};
6
+ var l=class{constructor(e,t){this._schema=e;this._scheduler=t;if(!e)throw new Error("Projector: Schema is required.")}project(e,...t){for(let r of t)this._dispatch(e,r)}_dispatch(e,t){let{path:r,value:o}=t,c=this._resolveEffect(r);c&&(this._ensureScheduler(),this._scheduler.enqueue({path:r.join("."),effect:c,ctx:{source:e,path:r,value:o}}))}_ensureScheduler(){if(!this._scheduler)throw new Error("Projector: no scheduler.")}_resolveEffect(e){let t=this._schema;for(let r of e){if(!t)return null;t=t[r]}return typeof t=="function"?t:null}},f=class extends l{constructor(e,t){super(e,t)}},d=class extends l{withScheduler(e){return typeof e=="function"?this._schedulerFactory=e:this._scheduler=e,this}_ensureScheduler(){if(!this._scheduler&&this._schedulerFactory&&(this._scheduler=this._schedulerFactory()),!this._scheduler)throw new Error("Projector: no scheduler.")}};import{debounce as L}from"lodash-es";var T=class{constructor(e){this._queue=new Map;e&&(this._strategyFactory=()=>e)}enqueue(e){return this._queue.set(e.path,e),this}flush(){return log.trace("Scheduler.flush()",[...this._queue.keys()]),this.flushCore()}reset(){this._queue.clear()}withStrategy(e){return this._strategyFactory=typeof e=="function"?e:()=>e,this}ensureStrategy(){let e=this._strategyFactory?.();if(!e)throw new Error("Scheduler: no strategy.");return e}async run(e){for(let t of e){let{effect:r,ctx:o}=t,c=r(this.ensureStrategy(),o);c instanceof Promise&&await c}}},y=class extends T{async flushCore(){let e=[...this._queue.values()];this._queue.clear(),this.run(e)}},h=class extends T{constructor(t,r=500){super(t);this.wait=r;this.flushCore=L(async()=>{let t=[...this._queue.values()];this._queue.clear(),this.run(t)},this.wait)}},p=class extends T{async flushCore(){this.ensureStrategy().reset(),this.run([...this._queue.values()])}};import{debounce as k}from"lodash-es";import A from"on-change";import{cloneDeep as j,debounce as P,set as b}from"lodash-es";import{withEvents as C}from"@cyysummer/core";var S=class extends C(){constructor(t){super();this._paused=!1;this._shadow=j(t)}sendTo(t){this._projectorFactory=typeof t=="function"?t:()=>t}pause(){this._paused=!0}resume(){this._paused=!1}},g=class extends S{receive(e,t){if(this._paused)return;b(this._shadow,e,t);let r=[{path:e,value:t}];this._projectorFactory?.()?.project(this._shadow,...r),this.emit("record",{next:this._shadow,patches:r})}},_=class extends S{constructor(t,r=500){super(t);this._wait=r;this._buffer=new Map;this.receiveCore=P(()=>{let t=[...this._buffer.values()];log.trace("LazyRecorder.receiveCore,",t),this._buffer.clear();for(let r of t)b(this._shadow,r.path,r.value);this._projectorFactory?.()?.project(this._shadow,...t),this.emit("record",{next:this._shadow,patches:t})},this._wait,{trailing:!0})}receive(t,r){this._paused||(this._buffer.set(t.join("."),{path:t,value:r}),this.receiveCore())}};function w(a,e=500){let t=e>0?new _(a,e):new g(a);return[A(a,(o,c)=>t.receive(o,c),{pathAsArray:!0}),t]}var I=class{constructor(e){this._initial=e;this._waitTime=500;if(!e)throw new Error("Builder: initial object is required.")}buildDynamic(){if(!this._schema)throw new Error("Builder: no schema. Call `loadSchema()` before build.");this.track();let e=new d(this._schema);return this._scheduler&&e.withScheduler(this._scheduler),this._recorder.sendTo(e),{tracked:this._tracked,recorder:this._recorder,projector:e,scheduler:this._scheduler}}build(){if(!this._schema)throw new Error("Builder: no schema. Call `loadSchema()` before build.");if(!this._strategyFactory)throw new Error("Builder: no strategy. Call `withStrategy()` before build.");this._scheduler||(this._scheduler=new h);let e=async()=>{await this._scheduler.flush(),this._flushCallback?.()};this.track(),this._scheduler.withStrategy(this._strategyFactory);let t=new f(this._schema,this._scheduler);return this._recorder.sendTo(t),this._recorder.on("record",this._waitTime?k(e,this._waitTime):e),{tracked:this._tracked,recorder:this._recorder,scheduler:this._scheduler}}lazy(e=500){if(Number(e)<0)throw new Error("Builder: wait time must be >= 0.");return this._waitTime=e,this}loadSchema(e){return this._schema=e,this}onFlush(e){return this._flushCallback=e,this}withLazyScheduler(){return this._scheduler=new h(void 0,this._waitTime),this}withScheduler(){return this._scheduler=new y(void 0),this}withTotalScheduler(){return this._scheduler=new p,this}withStrategy(e){return this._strategyFactory=typeof e=="function"?e:()=>e,this}track(){this._recorder&&this._tracked||([this._tracked,this._recorder]=w(this._initial,this._waitTime))}};import{get as E,keys as R}from"lodash-es";var x=a=>{},v=class{at(e){return(t,r)=>{let o=i(r);return t.execute(e,o)}}atWith(e,t){return async(o,c)=>{let u=i(c),n=await t(u);return o.execute(e,n)}}arrayAt(e,t){return(r,o)=>{let c=o.path?E(o.source,o.path):o.value;if(c&&Array.isArray(c)&&c.length>0){let u=m(c,t);return r.executeArray(e,c,u)}}}arrayFrom(e,t,r){return async(c,u)=>{let n=i(u),s=await t(n),F=m(s,r);return c.executeArray(e,s,F)}}arrayAtWith(e,t,r){return(o,c)=>{let u=c.path?E(c.source,c.path):c.value;if(u&&Array.isArray(u)&&u.length>0){let n=u.map(t),s=m(u,r);return o.executeArray(e,n,s)}}}loop(e){return async(t,r)=>{let o=i(r);if(Array.isArray(o)){let c=o;for(let u=0;u<c.length;u++){let n=c[u];await e(n,u)(t,{value:n})}}else throw new Error(`Effect: Value at path '${r.path}' is not an array.`)}}sourceWith(e,t){return async(r,o)=>{let c=await t(o.source);return r.execute(e,c)}}raw(e,t){return(r,o)=>r.execute(e,t)}sequence(...e){return async(t,r)=>{let o=i(r);for(let c of e)await c(t,{value:o})}}sequenceFromSource(e,...t){return async(r,o)=>{let c=await e(o.source);for(let u of t)await u(r,{value:c})}}sequenceWith(e,...t){return async(o,c)=>{let u=i(c),n=await e(u);for(let s of t)await s(o,{value:n})}}when(e,t,r=x){return(o,c)=>{let u=i(c);return(e(u)?t:r)(o,{value:u})}}whenFromSource(e,t,r=x){return(o,c)=>(e(c.source)?t:r)(o,c)}},m=(a,e)=>({keys:typeof a[0]=="object"?R(a[0]):[],resolveHeader:(t,r)=>String(t),...e}),i=a=>a.source&&a.path?E(a.source,a.path):a.value;export{I as Builder,d as DynamicProjector,v as EffectFactory,_ as LazyRecorder,h as LazyScheduler,f as Projector,l as ProjectorBase,g as Recorder,S as RecorderBase,y as Scheduler,T as SchedulerBase,p as TotalScheduler,w as track};
7
7
  //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cyysummer/projector",
3
- "version": "0.0.11",
3
+ "version": "0.0.13",
4
4
  "description": "A powerful state projection library for creating anything.",
5
5
  "license": "MIT",
6
6
  "author": "Chris Liu",
package/types/lib.d.ts CHANGED
@@ -56,6 +56,19 @@ type ArrayEffectOptions = {
56
56
  resolveHeader: (key: string | symbol, index?: number) => string;
57
57
  }
58
58
 
59
+
60
+ type RecorderEvents = {
61
+ record: { next: any; patches: Patch[]; };
62
+ };
63
+
64
+ interface IRecorder<TSource extends object> extends IEventful<RecorderEvents> {
65
+ pause(): void;
66
+ receive(path: any[], value: any): void;
67
+ resume(): void;
68
+ sendTo(projector: IProjector<TSource>): void;
69
+ sendTo(factory: Func<IProjector<TSource>>): void;
70
+ }
71
+
59
72
  /**
60
73
  * Strategy defines actual execution methods for projecting values to target.
61
74
  */
@@ -89,64 +102,6 @@ interface Patch {
89
102
  value: any;
90
103
  }
91
104
 
92
- type RecorderEvents = {
93
- record: {
94
- next: any;
95
- patches: Patch[];
96
- };
97
- };
98
- declare const RecorderBase_base: Constructor<object> & (new (...args: any[]) => object & IEventful<RecorderEvents>);
99
- /**
100
- * Record changes to an object.
101
- */
102
- declare abstract class RecorderBase<TSource extends object> extends RecorderBase_base {
103
- protected _paused: boolean;
104
- protected _projectorFactory?: Func<IProjector<TSource>>;
105
- protected _shadow: TSource;
106
- constructor(initial: TSource);
107
- /**
108
- * Receive changes from the tracked object.
109
- * @param path
110
- * @param value
111
- * @returns
112
- */
113
- abstract receive(path: any[], value: any): void;
114
- /**
115
- * Send changes to a projector.
116
- * @param projector
117
- */
118
- sendTo(projector: IProjector<TSource>): void;
119
- /**
120
- * Send changes to a projector.
121
- * @param factory
122
- */
123
- sendTo(factory: Func<IProjector<TSource>>): void;
124
- pause(): void;
125
- resume(): void;
126
- }
127
- declare class Recorder<TSource extends object> extends RecorderBase<TSource> {
128
- /**
129
- * Receive changes from the tracked object.
130
- * @param path
131
- * @param value
132
- * @returns
133
- */
134
- receive(path: any[], value: any): void;
135
- }
136
- declare class LazyRecorder<TSource extends object> extends RecorderBase<TSource> {
137
- protected _wait: number;
138
- protected _buffer: Map<string, Patch>;
139
- constructor(initial: TSource, _wait?: number);
140
- /**
141
- * Receive changes from the tracked object.
142
- * @param path
143
- * @param value
144
- * @returns
145
- */
146
- receive(path: any[], value: any): void;
147
- protected readonly receiveCore: lodash_es.DebouncedFunc<() => void>;
148
- }
149
-
150
105
  declare abstract class ProjectorBase<TSource> implements IProjector<TSource> {
151
106
  protected _schema: Schema<TSource>;
152
107
  protected _scheduler?: IScheduler<any> | undefined;
@@ -166,35 +121,48 @@ declare class DynamicProjector<TSource> extends ProjectorBase<TSource> {
166
121
  protected _ensureScheduler(): void;
167
122
  }
168
123
 
124
+ /**
125
+ * Builder pattern for projector.
126
+ * @template TSource Source object structure.
127
+ * @template TTarget Projection target.
128
+ * @template TLocation A descriptor of location in `TTarget` where projection effects are executed.
129
+ */
169
130
  declare class Builder<TSource extends object, TTarget, TLocation> {
170
131
  private _initial;
171
- private _schema;
172
- private _waitTime;
173
132
  private _flushCallback?;
174
133
  private _recorder?;
134
+ private _schema?;
175
135
  private _scheduler?;
176
- private _strategy?;
177
136
  private _strategyFactory?;
178
137
  private _tracked?;
179
- constructor(_initial: TSource, _schema: Schema<TSource>, _waitTime?: number);
138
+ private _waitTime;
139
+ constructor(_initial: TSource);
180
140
  buildDynamic(): {
181
141
  tracked: TSource;
182
- recorder: RecorderBase<TSource>;
142
+ recorder: IRecorder<TSource>;
183
143
  projector: DynamicProjector<TSource>;
184
144
  scheduler: IScheduler<TTarget> | undefined;
185
145
  };
186
- buildStatic(): {
146
+ build(): {
187
147
  tracked: TSource;
188
- recorder: RecorderBase<TSource>;
148
+ recorder: IRecorder<TSource>;
189
149
  scheduler: IScheduler<TTarget>;
190
150
  };
151
+ /**
152
+ * Use debounced Recorder/Scheduler.
153
+ * @param wait Debounce time in milliseconds. Default: 500.
154
+ * @returns
155
+ */
156
+ lazy(wait?: number): Builder<TSource, TTarget, TLocation>;
157
+ loadSchema(schema: Schema<TSource>): Builder<TSource, TTarget, TLocation>;
191
158
  /**
192
159
  * Provide a callback to execute after scheduler flushed.
193
160
  * @param callback
194
161
  * @returns
195
162
  */
196
163
  onFlush(callback: Action): Builder<TSource, TTarget, TLocation>;
197
- withLazyScheduler(wait?: number): Builder<TSource, TTarget, TLocation>;
164
+ withLazyScheduler(): Builder<TSource, TTarget, TLocation>;
165
+ withScheduler(): Builder<TSource, TTarget, TLocation>;
198
166
  withTotalScheduler(): Builder<TSource, TTarget, TLocation>;
199
167
  withStrategy(strategy: ITargetExecutionStrategy<TTarget, TLocation>): Builder<TSource, TTarget, TLocation>;
200
168
  withStrategy(factory: Func<ITargetExecutionStrategy<TTarget, TLocation>>): Builder<TSource, TTarget, TLocation>;
@@ -226,6 +194,14 @@ declare class EffectFactory<TSource extends object, TLocation = any> {
226
194
  * @returns
227
195
  */
228
196
  arrayAt<T, U = UnwrapArray<T>>(location: TLocation, options?: Partial<ArrayEffectOptions>): Effect<TSource, T>;
197
+ /**
198
+ * Create an effect bound to a specific location on the target. Source value is converted to array using mapper.
199
+ * @param location Identifies the location on the effect target where this effect will be executed.
200
+ * @param mapper Function to transform the element of the array.
201
+ * @param options
202
+ * @returns
203
+ */
204
+ arrayFrom<T, U extends any[]>(location: TLocation, mapper: Func1<T, U>, options?: Partial<ArrayEffectOptions>): Effect<TSource, U>;
229
205
  /**
230
206
  * Create an effect bound to a specific location on the target. When it executs, source value will be transformed with `mapper`. Source value should be an array.
231
207
  * @param location Identifies the location on the effect target where this effect will be executed.
@@ -255,18 +231,25 @@ declare class EffectFactory<TSource extends object, TLocation = any> {
255
231
  */
256
232
  raw<T>(location: TLocation, rawValue: T): Effect<TSource, T>;
257
233
  /**
258
- * Call multiple effects in sequence.
234
+ * Execute multiple effects in sequence.
259
235
  * @param effects Effects to call.
260
236
  * @returns
261
237
  */
262
- sequence<T>(effects: Effect<TSource, T>[]): Effect<TSource, T>;
238
+ sequence<T>(...effects: Effect<TSource, T>[]): Effect<TSource, T>;
263
239
  /**
264
- * Call multiple effects in sequence. When it executs, source value will be transformed with `mapper` first, then pass to `effects`.
240
+ * Execute multiple effects in sequence. When it executs, `TSource` object will be transformed with `mapper` first, then pass to `effects`.
241
+ * @param mapper Function to transform the value.
265
242
  * @param effects Effects to call.
243
+ * @returns
244
+ */
245
+ sequenceFromSource<R>(mapper: Func1<TSource, R>, ...effects: Effect<TSource, R>[]): Effect<TSource, R>;
246
+ /**
247
+ * Execute multiple effects in sequence. When it executs, source value will be transformed with `mapper` first, then pass to `effects`.
266
248
  * @param mapper Function to transform the value.
249
+ * @param effects Effects to call.
267
250
  * @returns
268
251
  */
269
- sequenceWith<T, R>(effects: Effect<TSource, R>[], mapper: Func1<T, R>): Effect<TSource, R>;
252
+ sequenceWith<T, R>(mapper: Func1<T, R>, ...effects: Effect<TSource, any>[]): Effect<TSource, R>;
270
253
  /**
271
254
  * Execute effect based on condition. The condition is based on the value of the current property path.
272
255
  * @param condition Condition delegate that takes the value of the property and returns true or false.
@@ -285,10 +268,62 @@ declare class EffectFactory<TSource extends object, TLocation = any> {
285
268
  whenFromSource<T>(condition: Predicate<TSource>, ifTrue: Effect<TSource, any>, ifFalse?: Effect<TSource, any>): Effect<TSource, T>;
286
269
  }
287
270
 
271
+ declare const RecorderBase_base: Constructor<object> & (new (...args: any[]) => object & IEventful<RecorderEvents>);
272
+ /**
273
+ * Record changes to an object.
274
+ */
275
+ declare abstract class RecorderBase<TSource extends object> extends RecorderBase_base implements IRecorder<TSource> {
276
+ protected _paused: boolean;
277
+ protected _projectorFactory?: Func<IProjector<TSource>>;
278
+ protected _shadow: TSource;
279
+ constructor(initial: TSource);
280
+ /**
281
+ * Receive changes from the tracked object.
282
+ * @param path
283
+ * @param value
284
+ * @returns
285
+ */
286
+ abstract receive(path: any[], value: any): void;
287
+ /**
288
+ * Send changes to a projector.
289
+ * @param projector
290
+ */
291
+ sendTo(projector: IProjector<TSource>): void;
292
+ /**
293
+ * Send changes to a projector.
294
+ * @param factory
295
+ */
296
+ sendTo(factory: Func<IProjector<TSource>>): void;
297
+ pause(): void;
298
+ resume(): void;
299
+ }
300
+ declare class Recorder<TSource extends object> extends RecorderBase<TSource> {
301
+ /**
302
+ * Receive changes from the tracked object.
303
+ * @param path
304
+ * @param value
305
+ * @returns
306
+ */
307
+ receive(path: any[], value: any): void;
308
+ }
309
+ declare class LazyRecorder<TSource extends object> extends RecorderBase<TSource> {
310
+ protected _wait: number;
311
+ protected _buffer: Map<string, Patch>;
312
+ constructor(initial: TSource, _wait?: number);
313
+ /**
314
+ * Receive changes from the tracked object.
315
+ * @param path
316
+ * @param value
317
+ * @returns
318
+ */
319
+ receive(path: any[], value: any): void;
320
+ protected readonly receiveCore: lodash_es.DebouncedFunc<() => void>;
321
+ }
322
+
288
323
  /**
289
324
  * Scheduler abstract class, used to schedule projector effects.
290
325
  */
291
- declare abstract class Scheduler<TTarget> implements IScheduler<TTarget> {
326
+ declare abstract class SchedulerBase<TTarget> implements IScheduler<TTarget> {
292
327
  protected readonly _queue: Map<string, IScheduleItem<any, any>>;
293
328
  protected _strategyFactory?: Func<ITargetExecutionStrategy<TTarget, any>>;
294
329
  constructor(strategy?: ITargetExecutionStrategy<TTarget, any>);
@@ -301,6 +336,12 @@ declare abstract class Scheduler<TTarget> implements IScheduler<TTarget> {
301
336
  protected abstract flushCore(): MaybePromise<void>;
302
337
  protected run(list: IScheduleItem<any, any>[]): Promise<void>;
303
338
  }
339
+ /**
340
+ * Run all queued effects at once.
341
+ */
342
+ declare class Scheduler<TTarget> extends SchedulerBase<TTarget> {
343
+ protected flushCore(): Promise<void>;
344
+ }
304
345
  /**
305
346
  * A scheduler that **delays and batches** effect executions using debounce.
306
347
  *
@@ -313,23 +354,24 @@ declare abstract class Scheduler<TTarget> implements IScheduler<TTarget> {
313
354
  * `LazyScheduler` is more conservative: it waits for a quiet period before flushing the accumulated
314
355
  * effects in batch.
315
356
  */
316
- declare class LazyScheduler<TTarget> extends Scheduler<TTarget> {
357
+ declare class LazyScheduler<TTarget> extends SchedulerBase<TTarget> {
317
358
  protected wait: number;
318
359
  constructor(strategy?: ITargetExecutionStrategy<TTarget, any>, wait?: number);
319
360
  protected readonly flushCore: lodash_es.DebouncedFunc<() => Promise<void>>;
320
361
  }
321
362
  /**
322
- * Run all queued effects at once. The queue will NOT be cleared. The strategy will `reset()` before running any effects.
363
+ * Run all queued effects at once. The queue will NOT be cleared. The strategy will `reset()` before running any effects, in order to make sure strategy is in initial state.
323
364
  */
324
- declare class TotalScheduler<TTarget> extends Scheduler<TTarget> {
365
+ declare class TotalScheduler<TTarget> extends SchedulerBase<TTarget> {
325
366
  protected flushCore(): Promise<void>;
326
367
  }
327
368
 
328
369
  /**
329
370
  * Track changes to an object.
330
371
  * @param initial Source object to track. This must the raw object, not reactive-enabled object.
372
+ * @param [wait=500] Wait before change notification. Useful when the object changes at high frequency.
331
373
  * @returns A tuple of the tracked object and a recorder.
332
374
  */
333
- declare function track<TSource extends object>(initial: TSource): [TSource, Recorder<TSource>];
375
+ declare function track<TSource extends object>(initial: TSource, wait?: number): [TSource, IRecorder<TSource>];
334
376
 
335
- export { Builder, DynamicProjector, EffectFactory, LazyRecorder, LazyScheduler, Projector, ProjectorBase, Recorder, RecorderBase, Scheduler, TotalScheduler, track };
377
+ export { Builder, DynamicProjector, EffectFactory, LazyRecorder, LazyScheduler, Projector, ProjectorBase, Recorder, RecorderBase, Scheduler, SchedulerBase, TotalScheduler, track };
package/types/types.d.ts CHANGED
@@ -54,6 +54,19 @@ export type ArrayEffectOptions = {
54
54
  resolveHeader: (key: string | symbol, index?: number) => string;
55
55
  }
56
56
 
57
+
58
+ export type RecorderEvents = {
59
+ record: { next: any; patches: Patch[]; };
60
+ };
61
+
62
+ export interface IRecorder<TSource extends object> extends IEventful<RecorderEvents> {
63
+ pause(): void;
64
+ receive(path: any[], value: any): void;
65
+ resume(): void;
66
+ sendTo(projector: IProjector<TSource>): void;
67
+ sendTo(factory: Func<IProjector<TSource>>): void;
68
+ }
69
+
57
70
  /**
58
71
  * Strategy defines actual execution methods for projecting values to target.
59
72
  */