@cyysummer/projector 0.0.10 → 0.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +48 -0
- package/dist/index.js +2 -2
- package/package.json +1 -1
- package/types/lib.d.ts +45 -18
- package/types/types.d.ts +2 -0
package/README.md
CHANGED
|
@@ -1,3 +1,51 @@
|
|
|
1
1
|
# Structural Data Projector
|
|
2
2
|
|
|
3
3
|
结构化对象映射引擎
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
### Common
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
```typescript
|
|
11
|
+
interface IMyData {
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const schema: Schema<IMyData> = {
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
class OutputTarget {
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
type TargetLocation = string;
|
|
21
|
+
|
|
22
|
+
const strategy = new DemoStrategy(container);
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### Svelte 5 with runes
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
let initialState: IMyData = $state({});
|
|
29
|
+
const builder = new Builder<IMyData, OutputTarget, TargetLocation>(initialState, schema)
|
|
30
|
+
.onFlush(() => {
|
|
31
|
+
// Do something when scheduler flushed. For example, update UI.
|
|
32
|
+
})
|
|
33
|
+
.withStrategy(strategy);
|
|
34
|
+
const { tracked } = builder.buildStatic();
|
|
35
|
+
let formData: IMyData = $derived(tracked);
|
|
36
|
+
// bind `formData` to UI.
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Vue
|
|
40
|
+
|
|
41
|
+
```typescript
|
|
42
|
+
const initialState: IMyData = {};
|
|
43
|
+
const builder = new Builder<IMyData, OutputTarget, TargetLocation>(initialState, schema)
|
|
44
|
+
.onFlush(() => {
|
|
45
|
+
// Do something when scheduler flushed. For example, update UI.
|
|
46
|
+
})
|
|
47
|
+
.withStrategy(strategy);
|
|
48
|
+
const { tracked } = builder.buildStatic();
|
|
49
|
+
const formData: IMyData = reactive(tracked);
|
|
50
|
+
// bind `formData` to UI.
|
|
51
|
+
```
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @cyysummer/projector v0.0.
|
|
2
|
+
* @cyysummer/projector v0.0.11
|
|
3
3
|
* (c) 2021-PRESENT Chris Liu
|
|
4
4
|
* @license MIT
|
|
5
5
|
**/
|
|
6
|
-
import
|
|
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};
|
|
7
7
|
//# sourceMappingURL=index.js.map
|
package/package.json
CHANGED
package/types/lib.d.ts
CHANGED
|
@@ -63,6 +63,7 @@ interface ITargetExecutionStrategy<TTarget, TLocation> {
|
|
|
63
63
|
execute<T extends any>(location: TLocation, value: T): MaybePromise<void>;
|
|
64
64
|
executeArray<T extends any>(location: TLocation, rows: T[], options: ArrayEffectOptions): MaybePromise<void>;
|
|
65
65
|
reset(): void;
|
|
66
|
+
get target(): TTarget;
|
|
66
67
|
}
|
|
67
68
|
|
|
68
69
|
interface IProjector<TSource> {
|
|
@@ -72,6 +73,7 @@ interface IProjector<TSource> {
|
|
|
72
73
|
interface IScheduler<TTarget> {
|
|
73
74
|
enqueue(effect: IScheduleItem): void;
|
|
74
75
|
flush(): MaybePromise<void>;
|
|
76
|
+
reset(): void;
|
|
75
77
|
withStrategy(strategy: ITargetExecutionStrategy<TTarget, any>): IScheduler<TTarget>;
|
|
76
78
|
withStrategy(factory: Func<ITargetExecutionStrategy<TTarget, any>>): IScheduler<TTarget>;
|
|
77
79
|
}
|
|
@@ -93,15 +95,14 @@ type RecorderEvents = {
|
|
|
93
95
|
patches: Patch[];
|
|
94
96
|
};
|
|
95
97
|
};
|
|
96
|
-
declare const
|
|
98
|
+
declare const RecorderBase_base: Constructor<object> & (new (...args: any[]) => object & IEventful<RecorderEvents>);
|
|
97
99
|
/**
|
|
98
100
|
* Record changes to an object.
|
|
99
101
|
*/
|
|
100
|
-
declare class
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
private _shadow;
|
|
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;
|
|
105
106
|
constructor(initial: TSource);
|
|
106
107
|
/**
|
|
107
108
|
* Receive changes from the tracked object.
|
|
@@ -109,7 +110,7 @@ declare class Recorder<TSource extends object> extends Recorder_base {
|
|
|
109
110
|
* @param value
|
|
110
111
|
* @returns
|
|
111
112
|
*/
|
|
112
|
-
receive(path: any[], value: any): void;
|
|
113
|
+
abstract receive(path: any[], value: any): void;
|
|
113
114
|
/**
|
|
114
115
|
* Send changes to a projector.
|
|
115
116
|
* @param projector
|
|
@@ -122,7 +123,28 @@ declare class Recorder<TSource extends object> extends Recorder_base {
|
|
|
122
123
|
sendTo(factory: Func<IProjector<TSource>>): void;
|
|
123
124
|
pause(): void;
|
|
124
125
|
resume(): void;
|
|
125
|
-
|
|
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>;
|
|
126
148
|
}
|
|
127
149
|
|
|
128
150
|
declare abstract class ProjectorBase<TSource> implements IProjector<TSource> {
|
|
@@ -147,26 +169,31 @@ declare class DynamicProjector<TSource> extends ProjectorBase<TSource> {
|
|
|
147
169
|
declare class Builder<TSource extends object, TTarget, TLocation> {
|
|
148
170
|
private _initial;
|
|
149
171
|
private _schema;
|
|
150
|
-
private
|
|
151
|
-
private
|
|
172
|
+
private _waitTime;
|
|
173
|
+
private _flushCallback?;
|
|
152
174
|
private _recorder?;
|
|
153
175
|
private _scheduler?;
|
|
154
176
|
private _strategy?;
|
|
155
177
|
private _strategyFactory?;
|
|
156
178
|
private _tracked?;
|
|
157
|
-
constructor(_initial: TSource, _schema: Schema<TSource
|
|
179
|
+
constructor(_initial: TSource, _schema: Schema<TSource>, _waitTime?: number);
|
|
158
180
|
buildDynamic(): {
|
|
159
181
|
tracked: TSource;
|
|
160
|
-
recorder:
|
|
182
|
+
recorder: RecorderBase<TSource>;
|
|
161
183
|
projector: DynamicProjector<TSource>;
|
|
162
184
|
scheduler: IScheduler<TTarget> | undefined;
|
|
163
185
|
};
|
|
164
186
|
buildStatic(): {
|
|
165
187
|
tracked: TSource;
|
|
166
|
-
recorder:
|
|
188
|
+
recorder: RecorderBase<TSource>;
|
|
167
189
|
scheduler: IScheduler<TTarget>;
|
|
168
190
|
};
|
|
169
|
-
|
|
191
|
+
/**
|
|
192
|
+
* Provide a callback to execute after scheduler flushed.
|
|
193
|
+
* @param callback
|
|
194
|
+
* @returns
|
|
195
|
+
*/
|
|
196
|
+
onFlush(callback: Action): Builder<TSource, TTarget, TLocation>;
|
|
170
197
|
withLazyScheduler(wait?: number): Builder<TSource, TTarget, TLocation>;
|
|
171
198
|
withTotalScheduler(): Builder<TSource, TTarget, TLocation>;
|
|
172
199
|
withStrategy(strategy: ITargetExecutionStrategy<TTarget, TLocation>): Builder<TSource, TTarget, TLocation>;
|
|
@@ -262,15 +289,15 @@ declare class EffectFactory<TSource extends object, TLocation = any> {
|
|
|
262
289
|
* Scheduler abstract class, used to schedule projector effects.
|
|
263
290
|
*/
|
|
264
291
|
declare abstract class Scheduler<TTarget> implements IScheduler<TTarget> {
|
|
265
|
-
protected _strategy?: ITargetExecutionStrategy<TTarget, any> | undefined;
|
|
266
292
|
protected readonly _queue: Map<string, IScheduleItem<any, any>>;
|
|
267
293
|
protected _strategyFactory?: Func<ITargetExecutionStrategy<TTarget, any>>;
|
|
268
|
-
constructor(
|
|
294
|
+
constructor(strategy?: ITargetExecutionStrategy<TTarget, any>);
|
|
269
295
|
enqueue(item: IScheduleItem<any, any>): IScheduler<TTarget>;
|
|
270
296
|
flush(): MaybePromise<void>;
|
|
297
|
+
reset(): void;
|
|
271
298
|
withStrategy(strategy: ITargetExecutionStrategy<TTarget, any>): IScheduler<TTarget>;
|
|
272
299
|
withStrategy(factory: Func<ITargetExecutionStrategy<TTarget, any>>): IScheduler<TTarget>;
|
|
273
|
-
protected
|
|
300
|
+
protected ensureStrategy(): ITargetExecutionStrategy<TTarget, any>;
|
|
274
301
|
protected abstract flushCore(): MaybePromise<void>;
|
|
275
302
|
protected run(list: IScheduleItem<any, any>[]): Promise<void>;
|
|
276
303
|
}
|
|
@@ -305,4 +332,4 @@ declare class TotalScheduler<TTarget> extends Scheduler<TTarget> {
|
|
|
305
332
|
*/
|
|
306
333
|
declare function track<TSource extends object>(initial: TSource): [TSource, Recorder<TSource>];
|
|
307
334
|
|
|
308
|
-
export { Builder, DynamicProjector, EffectFactory, LazyScheduler, Projector, ProjectorBase, Recorder, Scheduler, TotalScheduler, track };
|
|
335
|
+
export { Builder, DynamicProjector, EffectFactory, LazyRecorder, LazyScheduler, Projector, ProjectorBase, Recorder, RecorderBase, Scheduler, TotalScheduler, track };
|
package/types/types.d.ts
CHANGED
|
@@ -61,6 +61,7 @@ export interface ITargetExecutionStrategy<TTarget, TLocation> {
|
|
|
61
61
|
execute<T extends any>(location: TLocation, value: T): MaybePromise<void>;
|
|
62
62
|
executeArray<T extends any>(location: TLocation, rows: T[], options: ArrayEffectOptions): MaybePromise<void>;
|
|
63
63
|
reset(): void;
|
|
64
|
+
get target(): TTarget;
|
|
64
65
|
}
|
|
65
66
|
|
|
66
67
|
export interface IProjector<TSource> {
|
|
@@ -70,6 +71,7 @@ export interface IProjector<TSource> {
|
|
|
70
71
|
export interface IScheduler<TTarget> {
|
|
71
72
|
enqueue(effect: IScheduleItem): void;
|
|
72
73
|
flush(): MaybePromise<void>;
|
|
74
|
+
reset(): void;
|
|
73
75
|
withStrategy(strategy: ITargetExecutionStrategy<TTarget, any>): IScheduler<TTarget>;
|
|
74
76
|
withStrategy(factory: Func<ITargetExecutionStrategy<TTarget, any>>): IScheduler<TTarget>;
|
|
75
77
|
}
|