@cyysummer/projector 0.0.7 → 0.0.9

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.7
2
+ * @cyysummer/projector v0.0.9
3
3
  * (c) 2021-PRESENT Chris Liu
4
4
  * @license MIT
5
5
  **/
6
- import I from"on-change";import{set as E}from"lodash-es";import{withEvents as m}from"@cyysummer/core";var i=class extends m(){constructor(e){super();this._paused=!1;this._shadow=structuredClone(e)}receive(e,r){if(this._paused)return;E(this._shadow,e,r);let c=[{path:e,value:r}];this.emit("record",{next:this._shadow,patches:c}),this._ensureProjector(),this._projector?.project(this._shadow,...c)}sendTo(e){typeof e=="function"?this._projectorFactory=e:this._projector=e}pause(){this._paused=!0}resume(){this._paused=!1}_ensureProjector(){!this._projector&&this._projectorFactory&&(this._projector=this._projectorFactory())}};import{get as S}from"lodash-es";var p=u=>{},d=class{constructor(t){this._strategy=t}create(t){return(e,r)=>{let c=a(r);return this._strategy.execute(e,t,c)}}createWith(t,e){return(c,o)=>{let s=a(o),n=e(s);return this._strategy.execute(c,t,n)}}forEach(t){return async(e,r)=>{let c=a(r);if(Array.isArray(c)){let o=c;for(let s=0;s<o.length;s++){let n=o[s],l=t(n,s)(e,{value:n});l instanceof Promise&&await l}}else throw new Error(`Effect: Value at path '${r.path}' is not an array.`)}}fromArray(t){return(e,r)=>{let c=r.path?S(r.source,r.path):r.value;if(c&&Array.isArray(c)&&c.length>0){let o=Object.keys(c[0]);return this._strategy.executeArray(e,t,o,c)}}}fromPrimitive(t){return(e,r)=>{let c=a(r);return this._strategy.execute(e,t,c)}}fromSource(t,e){return(r,c)=>{let o=e(c.source);return this._strategy.execute(r,t,o)}}fromValue(t,e){return(r,c)=>this._strategy.execute(r,t,e)}sequence(t){return async(e,r)=>{let c=a(r);for(let o of t)await o(e,{value:c})}}sequenceWith(t,e){return async(c,o)=>{let s=a(o),n=e(s);for(let T of t)await T(c,{value:n})}}when(t,e,r=p){return(c,o)=>{let s=a(o);return(t(s)?e:r)(c,{value:s})}}whenFromSource(t,e,r=p){return(c,o)=>(t(o.source)?e:r)(c,o)}};function a(u){return u.source&&u.path?S(u.source,u.path):u.value}var f=class{constructor(t){this._schema=t;if(!t)throw new Error("Projector: Schema is required.")}project(t,...e){for(let r of e)this._dispatch(t,r)}_dispatch(t,e){let{path:r,value:c}=e,o=this._resolveEffect(r);o&&(this._ensureScheduler(),this._scheduler.enqueue({path:r.join("."),effect:o,ctx:{source:t,path:r,value:c}}))}_ensureScheduler(){if(!this._scheduler)throw new Error("Projector: no scheduler.")}_resolveEffect(t){let e=this._schema;for(let r of t){if(!e)return null;e=e[r]}return typeof e=="function"?e:null}},y=class extends f{constructor(t,e){super(t),this._scheduler=e}},_=class extends f{scheduleWith(t){return typeof t=="function"?this._schedulerFactory=t:this._scheduler=t,this}_ensureScheduler(){if(!this._scheduler&&this._schedulerFactory&&(this._scheduler=this._schedulerFactory()),!this._scheduler)throw new Error("Projector: no scheduler.")}};var h=class{constructor(t){this._target=t}withTarget(t){return typeof t=="function"?this._targetFactory=t:this._target=t,this}checkTarget(){if(!this._target&&this._targetFactory&&(this._target=this._targetFactory()),!this._target)throw new Error("Scheduler: target is not set")}},g=class extends h{constructor(){super(...arguments);this._queue=new Map}enqueue(e){return this._queue.set(e.path,e),this}async flush(){this.checkTarget(),log.trace("BufferedScheduler: flushing");let e=[...this._queue.values()];this._queue.clear();for(let r of e){let{effect:c,ctx:o}=r,s=c(this._target,o);s instanceof Promise&&await s}}};function k(u){let t=new i(u);return[I(u,t.receive.bind(t),{pathAsArray:!0}),t]}export{g as BufferedScheduler,_ as DynamicProjector,d as EffectFactory,y as Projector,f as ProjectorBase,i as Recorder,h as Scheduler,k as track};
6
+ import w from"on-change";import{cloneDeep as m,set as x}from"lodash-es";import{withEvents as v}from"@cyysummer/core";var f=class extends v(){constructor(t){super();this._paused=!1;this._shadow=m(t)}receive(t,r){if(this._paused)return;x(this._shadow,t,r);let o=[{path:t,value:r}];this.emit("record",{next:this._shadow,patches:o}),this._ensureProjector(),this._projector?.project(this._shadow,...o)}sendTo(t){typeof t=="function"?this._projectorFactory=t:this._projector=t}pause(){this._paused=!0}resume(){this._paused=!1}_ensureProjector(){!this._projector&&this._projectorFactory&&(this._projector=this._projectorFactory())}};import{get as p,keys as l}from"lodash-es";var S=n=>{},d=class{at(e){return(t,r)=>{let o=i(r);return t.execute(e,o)}}atWith(e,t){return(o,c)=>{let a=i(c),u=t(a);return o.execute(e,u)}}arrayAt(e,t){return(r,o)=>{let c=o.path?p(o.source,o.path):o.value;if(c&&Array.isArray(c)&&c.length>0){let a={keys:typeof c[0]=="object"?l(c[0]):[],resolveHeader:(u,s)=>String(u),...t};return r.executeArray(e,c,a)}}}arrayAtWith(e,t,r){return(o,c)=>{let a=c.path?p(c.source,c.path):c.value;if(a&&Array.isArray(a)&&a.length>0){let u=a.map(t),s={keys:typeof a[0]=="object"?l(a[0]):[],resolveHeader:(T,j)=>String(T),...r};return o.executeArray(e,u,s)}}}loop(e){return async(t,r)=>{let o=i(r);if(Array.isArray(o)){let c=o;for(let a=0;a<c.length;a++){let u=c[a],T=e(u,a)(t,{value:u});T instanceof Promise&&await T}}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=i(r);for(let c of e)await c(t,{value:o})}}sequenceWith(e,t){return async(o,c)=>{let a=i(c),u=t(a);for(let s of e)await s(o,{value:u})}}when(e,t,r=S){return(o,c)=>{let a=i(c);return(e(a)?t:r)(o,{value:a})}}whenFromSource(e,t,r=S){return(o,c)=>(e(c.source)?t:r)(o,c)}},i=n=>n.source&&n.path?p(n.source,n.path):n.value;var h=class{constructor(e){this._schema=e;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}},g=class extends h{constructor(e,t){super(e),this._scheduler=t}},E=class extends h{scheduleWith(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 b}from"lodash-es";var y=class{constructor(e){this._strategy=e;this._queue=new Map}enqueue(e){return this._queue.set(e.path,e),this}flush(){return this.checkStrategy(),log.trace("Scheduler.flush()",[...this._queue.keys()]),this.flushCore()}withStrategy(e){return typeof e=="function"?this._strategyFactory=e:this._strategy=e,this}checkStrategy(){if(!this._strategy&&this._strategyFactory&&(this._strategy=this._strategyFactory()),!this._strategy)throw new Error("Scheduler: target is not set")}async run(e){for(let t of e){let{effect:r,ctx:o}=t,c=r(this._strategy,o);c instanceof Promise&&await c}}},I=class extends y{constructor(t,r=500){super(t);this.timeout=r;this.flushCore=b(async()=>{let t=[...this._queue.values()];this._queue.clear(),this.run(t)},this.timeout)}},_=class extends y{async flushCore(){this._strategy.reset(),this.run([...this._queue.values()])}};function O(n){let e=new f(n);return[w(n,(r,o)=>e.receive(r,o),{pathAsArray:!0}),e]}export{E as DynamicProjector,d as EffectFactory,I as LazyScheduler,g as Projector,h as ProjectorBase,f as Recorder,y as Scheduler,_ as TotalScheduler,O 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.7",
3
+ "version": "0.0.9",
4
4
  "description": "A powerful state projection library for creating anything.",
5
5
  "license": "MIT",
6
6
  "author": "Chris Liu",
@@ -18,7 +18,7 @@
18
18
  }
19
19
  },
20
20
  "dependencies": {
21
- "lodash-es": "^4.17.22",
21
+ "lodash-es": "4.17.22",
22
22
  "on-change": "^6.0.1",
23
23
  "@cyysummer/core": "^0.0.12"
24
24
  },
@@ -26,11 +26,11 @@
26
26
  "@cyysummer/core": "^0.0.12"
27
27
  },
28
28
  "devDependencies": {
29
- "@types/lodash-es": "^4.17.12",
30
- "tsup": "^8.5.0",
31
- "typescript": "^5.9.2",
32
- "vite": "^7.1.5",
33
- "vitest": "^3.2.4"
29
+ "@types/lodash-es": "4.17.12",
30
+ "tsup": "^8.5.1",
31
+ "typescript": "^5.9.3",
32
+ "vite": "^7.3.1",
33
+ "vitest": "^4.0.17"
34
34
  },
35
35
  "publishConfig": {
36
36
  "access": "public"
package/types/lib.d.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import * as lodash_es from 'lodash-es';
2
+
1
3
  /**
2
4
  * A Schema defines how to project changes from a source object to a target object.
3
5
  */
@@ -16,8 +18,8 @@ type Schema<TSource, T = TSource> = {
16
18
  /**
17
19
  * An effect defines how to project a value from source to target.
18
20
  */
19
- // todo: Solve this `any`
20
- type Effect<TSource, TValue> = (target: any, ctx: IEffectContext<TSource, TValue>) => MaybePromise<void>;
21
+ // todo: Solve ITargetExecutionStrategy<any, any>. How to obtain TTarget and TLocation?
22
+ type Effect<TSource, TValue> = (strategy: ITargetExecutionStrategy<any, any>, ctx: IEffectContext<TSource, TValue>) => MaybePromise<void>;
21
23
 
22
24
  /**
23
25
  * The context for an effect. It contains information about changes.
@@ -39,12 +41,28 @@ interface IEffectContext<TSource, TValue> {
39
41
  value: TValue;
40
42
  }
41
43
 
44
+ /**
45
+ * Options for execute array effect.
46
+ */
47
+ type ArrayEffectOptions = {
48
+ /**
49
+ * The keys of the objects in the array. Use this only when element of array is object.
50
+ */
51
+ keys: (string | symbol)[];
52
+
53
+ /**
54
+ * Function to generate header for each column.
55
+ */
56
+ resolveHeader: (key: string | symbol, index?: number) => string;
57
+ }
58
+
42
59
  /**
43
60
  * Strategy defines actual execution methods for projecting values to target.
44
61
  */
45
62
  interface ITargetExecutionStrategy<TTarget, TLocation = any> {
46
- execute<T>(target: TTarget, location: TLocation, value: T): MaybePromise<void>;
47
- executeArray<T>(target: TTarget, location: TLocation, keys: Partial<keyof T>[], rows: T[]): MaybePromise<void>;
63
+ execute<T extends any>(location: TLocation, value: T): MaybePromise<void>;
64
+ executeArray<T extends any>(location: TLocation, rows: T[], options: ArrayEffectOptions): MaybePromise<void>;
65
+ reset(): void;
48
66
  }
49
67
 
50
68
  interface IProjector<TSource> {
@@ -54,8 +72,8 @@ interface IProjector<TSource> {
54
72
  interface IScheduler<TTarget> {
55
73
  enqueue(effect: IScheduleItem): void;
56
74
  flush(): MaybePromise<void>;
57
- withTarget(target: TTarget): IScheduler<TTarget>;
58
- withTarget(target: Func<TTarget>): IScheduler<TTarget>;
75
+ withStrategy(target: ITargetExecutionStrategy<TTarget, unknown>): IScheduler<TTarget>;
76
+ withStrategy(target: Func<ITargetExecutionStrategy<TTarget, unknown>>): IScheduler<TTarget>;
59
77
  }
60
78
 
61
79
  interface IScheduleItem<TSource, TValue> {
@@ -109,45 +127,57 @@ declare class Recorder<TSource extends object> extends Recorder_base {
109
127
 
110
128
  /**
111
129
  * Build effects for a target.
130
+ * @template TSource The type of the source object.
131
+ * @template TLocation The type used to describe a location within an effect target. This is interpreted by the execution strategy and may represent a bookmark, anchor, or address.
112
132
  */
113
- declare class EffectFactory<TTarget, TSource extends object, TLocation = any> {
114
- protected _strategy: ITargetExecutionStrategy<TTarget, TLocation>;
115
- constructor(_strategy: ITargetExecutionStrategy<TTarget, TLocation>);
133
+ declare class EffectFactory<TSource extends object, TLocation = any> {
116
134
  /**
117
- * Create an effect that will applied at the specified location.
118
- * @param location The location in `TTarget` where the effect is executed.
135
+ * Create an effect bound to a specific location on the target. Source value should be primitive type.
136
+ * @param location Identifies the location on the effect target where this effect will be executed.
119
137
  * @returns
120
138
  */
121
- create<T>(location: TLocation): Effect<TSource, T>;
139
+ at<T>(location: TLocation): Effect<TSource, T>;
122
140
  /**
123
- * Create an effect that will applied at the specified location. Use `mapper` to transform the source object.
124
- * @param location The location in `TTarget` where the effect is executed.
125
- * @param mapper Function to transform the source object.
141
+ * 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 primitive type.
142
+ * @param location Identifies the location on the effect target where this effect will be executed.
143
+ * @param mapper Function to transform the value.
126
144
  * @returns
127
145
  */
128
- createWith<T, R>(location: TLocation, mapper: Func1<T, R>): Effect<TSource, R>;
129
- forEach<T, U = T extends Array<infer K> ? K : never>(each: Func2<U, number, Effect<TSource, U>>): Effect<TSource, T>;
146
+ atWith<T, R>(location: TLocation, mapper: Func1<T, R>): Effect<TSource, R>;
130
147
  /**
131
- * Create an effect that will applied at the specified location for array data.
132
- * @param location The location in `TTarget` where the effect is executed.
148
+ * Create an effect bound to a specific location on the target. Source value should be an array.
149
+ * @param location Identifies the location on the effect target where this effect will be executed.
133
150
  * @returns
134
151
  */
135
- fromArray<T extends Array<U>, U extends object = any>(location: TLocation): Effect<TSource, T>;
136
- fromPrimitive<T>(location: TLocation): Effect<TSource, T>;
152
+ arrayAt<T, U = UnwrapArray<T>>(location: TLocation, options?: Partial<ArrayEffectOptions>): Effect<TSource, T>;
137
153
  /**
138
- * Create an effect that will applied at the specified location. Use `mapper` to transform the source object.
139
- * @param location The location in `TTarget` where the effect is executed.
140
- * @param mapper Function to transform the source object.
154
+ * 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.
155
+ * @param location Identifies the location on the effect target where this effect will be executed.
156
+ * @param mapper Function to transform the element of the array.
141
157
  * @returns
142
158
  */
143
- fromSource<T>(location: TLocation, mapper: Func1<TSource, string>): Effect<TSource, T>;
159
+ arrayAtWith<T, U = UnwrapArray<T>, R = any>(location: TLocation, mapper: Func1<U, R>, options?: Partial<ArrayEffectOptions>): Effect<TSource, T>;
144
160
  /**
145
- * Create an effect that will applied at the specified location for raw value. This effect will not read from source object.
146
- * @param location The location in `TTarget` where the effect is executed.
147
- * @param value The value to use.
161
+ *
162
+ * @param each
148
163
  * @returns
164
+ * @experimental
149
165
  */
150
- fromValue<T>(location: TLocation, value: T): Effect<TSource, T>;
166
+ loop<T, U = T extends Array<infer K> ? K : never>(each: Func2<U, number, Effect<TSource, U>>): Effect<TSource, T>;
167
+ /**
168
+ * Create an effect bound to a specific location on the target for `TSource` object. When it executs, `TSource` object will be transformed with `mapper`.
169
+ * @param location Identifies the location on the effect target where this effect will be executed.
170
+ * @param mapper Function to transform the `TSource` object.
171
+ * @returns
172
+ */
173
+ sourceWith<T>(location: TLocation, mapper: Func1<TSource, string>): Effect<TSource, T>;
174
+ /**
175
+ * Create an effect bound to a specific location on the target for raw value. This effect will not read from `TSource` object.
176
+ * @param location Identifies the location on the effect target where this effect will be executed.
177
+ * @param rawValue The value to use.
178
+ * @returns
179
+ */
180
+ raw<T>(location: TLocation, rawValue: T): Effect<TSource, T>;
151
181
  /**
152
182
  * Call multiple effects in sequence.
153
183
  * @param effects Effects to call.
@@ -155,23 +185,23 @@ declare class EffectFactory<TTarget, TSource extends object, TLocation = any> {
155
185
  */
156
186
  sequence<T>(effects: Effect<TSource, T>[]): Effect<TSource, T>;
157
187
  /**
158
- * Call multiple effects in sequence. Use `mapper` to transform the original value.
188
+ * Call multiple effects in sequence. When it executs, source value will be transformed with `mapper` first, then pass to `effects`.
159
189
  * @param effects Effects to call.
160
- * @param mapper Function to transform the original value.
190
+ * @param mapper Function to transform the value.
161
191
  * @returns
162
192
  */
163
193
  sequenceWith<T, R>(effects: Effect<TSource, R>[], mapper: Func1<T, R>): Effect<TSource, R>;
164
194
  /**
165
195
  * Execute effect based on condition. The condition is based on the value of the current property path.
166
- * @param condition Condition delegate that takes the original value of the property and returns true or false.
196
+ * @param condition Condition delegate that takes the value of the property and returns true or false.
167
197
  * @param ifTrue When condition is true, this effect is executed.
168
198
  * @param ifFalse When condition is false, this effect is executed.
169
199
  * @returns
170
200
  */
171
201
  when<T>(condition: Predicate<T>, ifTrue: Effect<TSource, any>, ifFalse?: Effect<TSource, any>): Effect<TSource, T>;
172
202
  /**
173
- * Execute effect based on condition. The condition is based on the source object.
174
- * @param condition Condition delegate that takes the source object and returns true or false.
203
+ * Execute effect based on condition. The condition is based on the `TSource` object.
204
+ * @param condition Condition delegate that takes the `TSource` object and returns true or false.
175
205
  * @param ifTrue When condition is true, this effect is executed.
176
206
  * @param ifFalse When condition is false, this effect is executed.
177
207
  * @returns
@@ -202,22 +232,40 @@ declare class DynamicProjector<TSource> extends ProjectorBase<TSource> {
202
232
  * Scheduler abstract class, used to schedule projector effects.
203
233
  */
204
234
  declare abstract class Scheduler<TTarget> implements IScheduler<TTarget> {
205
- protected _target?: TTarget | undefined;
206
- protected _targetFactory?: Func<TTarget>;
207
- constructor(_target?: TTarget | undefined);
208
- abstract enqueue(item: IScheduleItem<TTarget, any>): IScheduler<TTarget>;
209
- abstract flush(): MaybePromise<void>;
210
- withTarget(target: TTarget): IScheduler<TTarget>;
211
- withTarget(target: Func<TTarget>): IScheduler<TTarget>;
212
- protected checkTarget(): void;
235
+ protected _strategy?: ITargetExecutionStrategy<TTarget, any> | undefined;
236
+ protected readonly _queue: Map<string, IScheduleItem<any, any>>;
237
+ protected _strategyFactory?: Func<ITargetExecutionStrategy<TTarget, any>>;
238
+ constructor(_strategy?: ITargetExecutionStrategy<TTarget, any> | undefined);
239
+ enqueue(item: IScheduleItem<any, any>): IScheduler<TTarget>;
240
+ flush(): MaybePromise<void>;
241
+ withStrategy(strategy: ITargetExecutionStrategy<TTarget, any>): IScheduler<TTarget>;
242
+ withStrategy(strategy: Func<ITargetExecutionStrategy<TTarget, any>>): IScheduler<TTarget>;
243
+ protected checkStrategy(): void;
244
+ protected abstract flushCore(): MaybePromise<void>;
245
+ protected run(list: IScheduleItem<any, any>[]): Promise<void>;
246
+ }
247
+ /**
248
+ * A scheduler that **delays and batches** effect executions using debounce.
249
+ *
250
+ * Effects are collected in a queue and only executed after the specified `timeout`
251
+ * has passed without any new effects being enqueued. This helps reduce redundant
252
+ * executions (e.g. during rapid successive state updates) and is especially useful
253
+ * when projecting to expensive targets like UI rendering, DOM updates, or network requests.
254
+ *
255
+ * In contrast to `TotalScheduler` which eagerly executes all effects on every projection,
256
+ * `LazyScheduler` is more conservative: it waits for a quiet period before flushing the accumulated
257
+ * effects in batch.
258
+ */
259
+ declare class LazyScheduler<TTarget> extends Scheduler<TTarget> {
260
+ protected timeout: number;
261
+ constructor(strategy?: ITargetExecutionStrategy<TTarget, any>, timeout?: number);
262
+ protected readonly flushCore: lodash_es.DebouncedFunc<() => Promise<void>>;
213
263
  }
214
264
  /**
215
- * Buffered scheduler, will buffer effects and execute them in batch.
265
+ * Run all queued effects at once. The queue will NOT be cleared. The strategy will `reset()` before running any effects.
216
266
  */
217
- declare class BufferedScheduler<TTarget> extends Scheduler<TTarget> {
218
- protected readonly _queue: Map<string, IScheduleItem<TTarget, any>>;
219
- enqueue(item: IScheduleItem<TTarget, any>): IScheduler<TTarget>;
220
- flush(): Promise<void>;
267
+ declare class TotalScheduler<TTarget> extends Scheduler<TTarget> {
268
+ protected flushCore(): Promise<void>;
221
269
  }
222
270
 
223
271
  /**
@@ -227,4 +275,4 @@ declare class BufferedScheduler<TTarget> extends Scheduler<TTarget> {
227
275
  */
228
276
  declare function track<TSource extends object>(initial: TSource): [TSource, Recorder<TSource>];
229
277
 
230
- export { BufferedScheduler, DynamicProjector, EffectFactory, Projector, ProjectorBase, Recorder, Scheduler, track };
278
+ export { DynamicProjector, EffectFactory, LazyScheduler, Projector, ProjectorBase, Recorder, Scheduler, TotalScheduler, track };
package/types/types.d.ts CHANGED
@@ -16,8 +16,8 @@ export type Schema<TSource, T = TSource> = {
16
16
  /**
17
17
  * An effect defines how to project a value from source to target.
18
18
  */
19
- // todo: Solve this `any`
20
- export type Effect<TSource, TValue> = (target: any, ctx: IEffectContext<TSource, TValue>) => MaybePromise<void>;
19
+ // todo: Solve ITargetExecutionStrategy<any, any>. How to obtain TTarget and TLocation?
20
+ export type Effect<TSource, TValue> = (strategy: ITargetExecutionStrategy<any, any>, ctx: IEffectContext<TSource, TValue>) => MaybePromise<void>;
21
21
 
22
22
  /**
23
23
  * The context for an effect. It contains information about changes.
@@ -39,12 +39,28 @@ export interface IEffectContext<TSource, TValue> {
39
39
  value: TValue;
40
40
  }
41
41
 
42
+ /**
43
+ * Options for execute array effect.
44
+ */
45
+ export type ArrayEffectOptions = {
46
+ /**
47
+ * The keys of the objects in the array. Use this only when element of array is object.
48
+ */
49
+ keys: (string | symbol)[];
50
+
51
+ /**
52
+ * Function to generate header for each column.
53
+ */
54
+ resolveHeader: (key: string | symbol, index?: number) => string;
55
+ }
56
+
42
57
  /**
43
58
  * Strategy defines actual execution methods for projecting values to target.
44
59
  */
45
60
  export interface ITargetExecutionStrategy<TTarget, TLocation = any> {
46
- execute<T>(target: TTarget, location: TLocation, value: T): MaybePromise<void>;
47
- executeArray<T>(target: TTarget, location: TLocation, keys: Partial<keyof T>[], rows: T[]): MaybePromise<void>;
61
+ execute<T extends any>(location: TLocation, value: T): MaybePromise<void>;
62
+ executeArray<T extends any>(location: TLocation, rows: T[], options: ArrayEffectOptions): MaybePromise<void>;
63
+ reset(): void;
48
64
  }
49
65
 
50
66
  export interface IProjector<TSource> {
@@ -54,8 +70,8 @@ export interface IProjector<TSource> {
54
70
  export interface IScheduler<TTarget> {
55
71
  enqueue(effect: IScheduleItem): void;
56
72
  flush(): MaybePromise<void>;
57
- withTarget(target: TTarget): IScheduler<TTarget>;
58
- withTarget(target: Func<TTarget>): IScheduler<TTarget>;
73
+ withStrategy(target: ITargetExecutionStrategy<TTarget, unknown>): IScheduler<TTarget>;
74
+ withStrategy(target: Func<ITargetExecutionStrategy<TTarget, unknown>>): IScheduler<TTarget>;
59
75
  }
60
76
 
61
77
  export interface IScheduleItem<TSource, TValue> {