@cyysummer/projector 0.0.12 → 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.12
2
+ * @cyysummer/projector v0.0.13
3
3
  * (c) 2021-PRESENT Chris Liu
4
4
  * @license MIT
5
5
  **/
6
- import"on-change";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:c}=t,o=this._resolveEffect(r);o&&(this._ensureScheduler(),this._scheduler.enqueue({path:r.join("."),effect:o,ctx:{source:e,path:r,value:c}}))}_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{cloneDeep as F,debounce as j,set as E}from"lodash-es";import{withEvents as L}from"@cyysummer/core";var p=class extends L(){constructor(t){super();this._paused=!1;this._shadow=F(t)}sendTo(t){this._projectorFactory=typeof t=="function"?t:()=>t}pause(){this._paused=!0}resume(){this._paused=!1}},y=class extends p{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})}},S=class extends p{constructor(t,r=500){super(t);this._wait=r;this._buffer=new Map;this.receiveCore=j(()=>{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{debounce as P}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:c}=t,o=r(this.ensureStrategy(),c);o instanceof Promise&&await o}}},T=class extends g{constructor(t,r=500){super(t);this.wait=r;this.flushCore=P(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 k}from"lodash-es";import C from"on-change";function I(a,e=500){let t=e>0?new S(a,e):new y(a);return[C(a,(c,o)=>t.receive(c,o),{pathAsArray:!0}),t]}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 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._strategyFactory)throw new Error("Builder: no strategy was provided. Call `withStrategy()` to provide.");this._scheduler||(this._scheduler=new T),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._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}}onFlush(e){return this._flushCallback=e,this}withLazyScheduler(){return this._scheduler=new T(void 0,this._waitTime),this}withTotalScheduler(){return this._scheduler=new _,this}withStrategy(e){return this._strategyFactory=typeof e=="function"?e:()=>e,this}track(){this._recorder&&this._tracked||([this._tracked,this._recorder]=I(this._initial,this._waitTime))}};import{get as m,keys as w}from"lodash-es";var x=a=>{},v=class{at(e){return(t,r)=>{let c=i(r);return t.execute(e,c)}}atWith(e,t){return(c,o)=>{let u=i(o),n=t(u);return c.execute(e,n)}}arrayAt(e,t){return(r,c)=>{let o=c.path?m(c.source,c.path):c.value;if(o&&Array.isArray(o)&&o.length>0){let u={keys:typeof o[0]=="object"?w(o[0]):[],resolveHeader:(n,s)=>String(n),...t};return r.executeArray(e,o,u)}}}arrayAtWith(e,t,r){return(c,o)=>{let u=o.path?m(o.source,o.path):o.value;if(u&&Array.isArray(u)&&u.length>0){let n=u.map(t),s={keys:typeof u[0]=="object"?w(u[0]):[],resolveHeader:(h,R)=>String(h),...r};return c.executeArray(e,n,s)}}}loop(e){return async(t,r)=>{let c=i(r);if(Array.isArray(c)){let o=c;for(let u=0;u<o.length;u++){let n=o[u],h=e(n,u)(t,{value:n});h instanceof Promise&&await h}}else throw new Error(`Effect: Value at path '${r.path}' is not an array.`)}}sourceWith(e,t){return(r,c)=>{let o=t(c.source);return r.execute(e,o)}}raw(e,t){return(r,c)=>r.execute(e,t)}sequence(e){return async(t,r)=>{let c=i(r);for(let o of e)await o(t,{value:c})}}sequenceWith(e,t){return async(c,o)=>{let u=i(o),n=t(u);for(let s of e)await s(c,{value:n})}}when(e,t,r=x){return(c,o)=>{let u=i(o);return(e(u)?t:r)(c,{value:u})}}whenFromSource(e,t,r=x){return(c,o)=>(e(o.source)?t:r)(c,o)}},i=a=>a.source&&a.path?m(a.source,a.path):a.value;export{b as Builder,d as DynamicProjector,v as EffectFactory,S as LazyRecorder,T as LazyScheduler,f as Projector,l as ProjectorBase,y as Recorder,p as RecorderBase,g as Scheduler,_ as TotalScheduler,I 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.12",
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
@@ -121,16 +121,22 @@ declare class DynamicProjector<TSource> extends ProjectorBase<TSource> {
121
121
  protected _ensureScheduler(): void;
122
122
  }
123
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
+ */
124
130
  declare class Builder<TSource extends object, TTarget, TLocation> {
125
131
  private _initial;
126
- private _schema;
127
- private _waitTime;
128
132
  private _flushCallback?;
129
133
  private _recorder?;
134
+ private _schema?;
130
135
  private _scheduler?;
131
136
  private _strategyFactory?;
132
137
  private _tracked?;
133
- constructor(_initial: TSource, _schema: Schema<TSource>, _waitTime?: number);
138
+ private _waitTime;
139
+ constructor(_initial: TSource);
134
140
  buildDynamic(): {
135
141
  tracked: TSource;
136
142
  recorder: IRecorder<TSource>;
@@ -142,6 +148,13 @@ declare class Builder<TSource extends object, TTarget, TLocation> {
142
148
  recorder: IRecorder<TSource>;
143
149
  scheduler: IScheduler<TTarget>;
144
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>;
145
158
  /**
146
159
  * Provide a callback to execute after scheduler flushed.
147
160
  * @param callback
@@ -149,6 +162,7 @@ declare class Builder<TSource extends object, TTarget, TLocation> {
149
162
  */
150
163
  onFlush(callback: Action): Builder<TSource, TTarget, TLocation>;
151
164
  withLazyScheduler(): Builder<TSource, TTarget, TLocation>;
165
+ withScheduler(): Builder<TSource, TTarget, TLocation>;
152
166
  withTotalScheduler(): Builder<TSource, TTarget, TLocation>;
153
167
  withStrategy(strategy: ITargetExecutionStrategy<TTarget, TLocation>): Builder<TSource, TTarget, TLocation>;
154
168
  withStrategy(factory: Func<ITargetExecutionStrategy<TTarget, TLocation>>): Builder<TSource, TTarget, TLocation>;
@@ -180,6 +194,14 @@ declare class EffectFactory<TSource extends object, TLocation = any> {
180
194
  * @returns
181
195
  */
182
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>;
183
205
  /**
184
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.
185
207
  * @param location Identifies the location on the effect target where this effect will be executed.
@@ -209,18 +231,25 @@ declare class EffectFactory<TSource extends object, TLocation = any> {
209
231
  */
210
232
  raw<T>(location: TLocation, rawValue: T): Effect<TSource, T>;
211
233
  /**
212
- * Call multiple effects in sequence.
234
+ * Execute multiple effects in sequence.
213
235
  * @param effects Effects to call.
214
236
  * @returns
215
237
  */
216
- sequence<T>(effects: Effect<TSource, T>[]): Effect<TSource, T>;
238
+ sequence<T>(...effects: Effect<TSource, T>[]): Effect<TSource, T>;
217
239
  /**
218
- * 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.
219
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`.
220
248
  * @param mapper Function to transform the value.
249
+ * @param effects Effects to call.
221
250
  * @returns
222
251
  */
223
- 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>;
224
253
  /**
225
254
  * Execute effect based on condition. The condition is based on the value of the current property path.
226
255
  * @param condition Condition delegate that takes the value of the property and returns true or false.
@@ -294,7 +323,7 @@ declare class LazyRecorder<TSource extends object> extends RecorderBase<TSource>
294
323
  /**
295
324
  * Scheduler abstract class, used to schedule projector effects.
296
325
  */
297
- declare abstract class Scheduler<TTarget> implements IScheduler<TTarget> {
326
+ declare abstract class SchedulerBase<TTarget> implements IScheduler<TTarget> {
298
327
  protected readonly _queue: Map<string, IScheduleItem<any, any>>;
299
328
  protected _strategyFactory?: Func<ITargetExecutionStrategy<TTarget, any>>;
300
329
  constructor(strategy?: ITargetExecutionStrategy<TTarget, any>);
@@ -307,6 +336,12 @@ declare abstract class Scheduler<TTarget> implements IScheduler<TTarget> {
307
336
  protected abstract flushCore(): MaybePromise<void>;
308
337
  protected run(list: IScheduleItem<any, any>[]): Promise<void>;
309
338
  }
339
+ /**
340
+ * Run all queued effects at once.
341
+ */
342
+ declare class Scheduler<TTarget> extends SchedulerBase<TTarget> {
343
+ protected flushCore(): Promise<void>;
344
+ }
310
345
  /**
311
346
  * A scheduler that **delays and batches** effect executions using debounce.
312
347
  *
@@ -319,15 +354,15 @@ declare abstract class Scheduler<TTarget> implements IScheduler<TTarget> {
319
354
  * `LazyScheduler` is more conservative: it waits for a quiet period before flushing the accumulated
320
355
  * effects in batch.
321
356
  */
322
- declare class LazyScheduler<TTarget> extends Scheduler<TTarget> {
357
+ declare class LazyScheduler<TTarget> extends SchedulerBase<TTarget> {
323
358
  protected wait: number;
324
359
  constructor(strategy?: ITargetExecutionStrategy<TTarget, any>, wait?: number);
325
360
  protected readonly flushCore: lodash_es.DebouncedFunc<() => Promise<void>>;
326
361
  }
327
362
  /**
328
- * 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.
329
364
  */
330
- declare class TotalScheduler<TTarget> extends Scheduler<TTarget> {
365
+ declare class TotalScheduler<TTarget> extends SchedulerBase<TTarget> {
331
366
  protected flushCore(): Promise<void>;
332
367
  }
333
368
 
@@ -339,4 +374,4 @@ declare class TotalScheduler<TTarget> extends Scheduler<TTarget> {
339
374
  */
340
375
  declare function track<TSource extends object>(initial: TSource, wait?: number): [TSource, IRecorder<TSource>];
341
376
 
342
- 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 };