@cyysummer/projector 0.0.2
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/LICENSE +21 -0
- package/README.md +3 -0
- package/dist/index.js +6 -0
- package/package.json +47 -0
- package/types/index.d.ts +8 -0
- package/types/lib.d.ts +124 -0
- package/types/types.d.ts +60 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2021-PRESENT Chris Liu
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @cyysummer/projector v0.0.2
|
|
3
|
+
* (c) 2021-PRESENT Chris Liu
|
|
4
|
+
* @license MIT
|
|
5
|
+
**/
|
|
6
|
+
import _ from"on-change";import{set as g}from"lodash-es";import{withEvents as S}from"@cyysummer/core";var T=class extends S(){constructor(e){super();this._paused=!1;this._shadow=structuredClone(e)}record(e,r){this._paused||(g(this._shadow,e,r),this.emit("record",{next:this._shadow,path:e,value:r}))}pause(){this._paused=!0}resume(){this._paused=!1}};import{get as p}from"lodash-es";var l=a=>{},h=class{constructor(t){this._strategy=t}chain(t){return async(e,r)=>{let c=s(r);for(let o of t)await o(e,{value:c})}}chainWith(t,e){return async(c,o)=>{let u=s(o),n=e(u);for(let f of t)await f(c,{value:n})}}create(t){return(e,r)=>{let c=s(r);return this._strategy.execute(e,t,c)}}createIf(t,e,r=l){return(c,o)=>{let u=s(o);return(t(u)?e:r)(c,{value:u})}}createIfRoot(t,e,r=l){return(c,o)=>(t(o.source)?e:r)(c,o)}createRoot(t,e){return(r,c)=>{let o=e(c.source);return this._strategy.execute(r,t,o)}}createForArray(t){return(e,r)=>{let c=r.path?p(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)}}}createForRaw(t,e){return(r,c)=>this._strategy.execute(r,t,e)}createWith(t,e){return(c,o)=>{let u=s(o),n=e(u);return this._strategy.execute(c,t,n)}}};function s(a){return a.source&&a.path?p(a.source,a.path):a.value}var y=class{constructor(t,e){this._schema=t;this._scheduler=e}project(t,...e){if(!this._schema)throw new Error("Schema not loaded");for(let r of e)this._dispatch(t,r)}_dispatch(t,e){let{path:r,value:c}=e,o=this._resolveEffect(r);o&&this._scheduler.enqueue({path:r.join("."),effect:o,ctx:{source:t,path:r,value:c}})}_resolveEffect(t){let e=this._schema;for(let r of t){if(!e)return null;e=e[r]}return typeof e=="function"?e:null}};var i=class{constructor(t=null){this._target=t;this._targetFactory=null}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")}},d=class extends i{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,u=c(this._target,o);u instanceof Promise&&await u}}};function F(a){let t=new T(a);return[_(a,t.record.bind(t),{pathAsArray:!0}),t]}export{d as BufferedScheduler,h as EffectBuilder,y as Projector,T as Recorder,i as Scheduler,F as track};
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@cyysummer/projector",
|
|
3
|
+
"version": "0.0.2",
|
|
4
|
+
"description": "A powerful state projection library for creating anything.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Chris Liu",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist/**",
|
|
9
|
+
"types/**"
|
|
10
|
+
],
|
|
11
|
+
"type": "module",
|
|
12
|
+
"module": "./dist/index.js",
|
|
13
|
+
"types": "./types",
|
|
14
|
+
"exports": {
|
|
15
|
+
".": {
|
|
16
|
+
"import": "./dist/index.js",
|
|
17
|
+
"types": "./types/index.d.ts"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"lodash-es": "^4.17.22",
|
|
22
|
+
"on-change": "^6.0.1",
|
|
23
|
+
"@cyysummer/core": "^0.0.12"
|
|
24
|
+
},
|
|
25
|
+
"peerDependencies": {
|
|
26
|
+
"@cyysummer/core": "^0.0.12"
|
|
27
|
+
},
|
|
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"
|
|
34
|
+
},
|
|
35
|
+
"publishConfig": {
|
|
36
|
+
"access": "public"
|
|
37
|
+
},
|
|
38
|
+
"scripts": {
|
|
39
|
+
"dev": "tsup --watch",
|
|
40
|
+
"clean": "rm -rf ./dist",
|
|
41
|
+
"prebuild": "tsup --dts-only",
|
|
42
|
+
"build": "tsup --no-dts",
|
|
43
|
+
"postbuild": "mv -f dist/index.d.ts types/lib.d.ts",
|
|
44
|
+
"release": "pnpm run clean && pnpm run build",
|
|
45
|
+
"test": "vitest"
|
|
46
|
+
}
|
|
47
|
+
}
|
package/types/index.d.ts
ADDED
package/types/lib.d.ts
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
type RecorderEvents = {
|
|
2
|
+
record: {
|
|
3
|
+
next: any;
|
|
4
|
+
path: any[];
|
|
5
|
+
value: any;
|
|
6
|
+
};
|
|
7
|
+
};
|
|
8
|
+
declare const Recorder_base: Constructor<object> & (new (...args: any[]) => object & IEventful<RecorderEvents>);
|
|
9
|
+
/**
|
|
10
|
+
* Record changes to an object.
|
|
11
|
+
*/
|
|
12
|
+
declare class Recorder<TSource extends object> extends Recorder_base {
|
|
13
|
+
private _paused;
|
|
14
|
+
private _shadow;
|
|
15
|
+
constructor(initial: TSource);
|
|
16
|
+
record(path: any[], value: any): void;
|
|
17
|
+
pause(): void;
|
|
18
|
+
resume(): void;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Build effects for a target.
|
|
23
|
+
*/
|
|
24
|
+
declare class EffectBuilder<TTarget, TSource extends object> {
|
|
25
|
+
private _strategy;
|
|
26
|
+
constructor(_strategy: ITargetStrategy<TTarget>);
|
|
27
|
+
/**
|
|
28
|
+
* Call multiple effects in sequence.
|
|
29
|
+
* @param effects Effects to call.
|
|
30
|
+
* @returns
|
|
31
|
+
*/
|
|
32
|
+
chain<T>(effects: Effect<TSource, T>[]): Effect<TSource, T>;
|
|
33
|
+
/**
|
|
34
|
+
* Call multiple effects in sequence. Use `customizer` to transform the original value.
|
|
35
|
+
* @param effects Effects to call.
|
|
36
|
+
* @param customizer Function to transform the original value.
|
|
37
|
+
* @returns
|
|
38
|
+
*/
|
|
39
|
+
chainWith<T, R>(effects: Effect<TSource, R>[], customizer: Func1<T, R>): Effect<TSource, R>;
|
|
40
|
+
/**
|
|
41
|
+
* Create an effect for the specified location.
|
|
42
|
+
* @param at The location in `TTarget` where the effect is to be executed.
|
|
43
|
+
* @returns
|
|
44
|
+
*/
|
|
45
|
+
create<T>(at: string): Effect<TSource, T>;
|
|
46
|
+
/**
|
|
47
|
+
* Execute effect based on condition. The condition is based on the value of the current property path.
|
|
48
|
+
* @param condition Condition delegate that takes the original value of the property and returns true or false.
|
|
49
|
+
* @param whenTrue When condition is true, this effect is executed.
|
|
50
|
+
* @param whenFalse When condition is false, this effect is executed.
|
|
51
|
+
* @returns
|
|
52
|
+
*/
|
|
53
|
+
createIf<T>(condition: Predicate<T>, whenTrue: Effect<TSource, any>, whenFalse?: Effect<TSource, any>): Effect<TSource, T>;
|
|
54
|
+
/**
|
|
55
|
+
* Execute effect based on condition. The condition is based on the source object.
|
|
56
|
+
* @param condition Condition delegate that takes the source object and returns true or false.
|
|
57
|
+
* @param whenTrue When condition is true, this effect is executed.
|
|
58
|
+
* @param whenFalse When condition is false, this effect is executed.
|
|
59
|
+
* @returns
|
|
60
|
+
*/
|
|
61
|
+
createIfRoot<T>(condition: Predicate<TSource>, whenTrue: Effect<TSource, any>, whenFalse?: Effect<TSource, any>): Effect<TSource, T>;
|
|
62
|
+
/**
|
|
63
|
+
* Create an effect for the specified location. Use customizer to transform the source object.
|
|
64
|
+
* @param at The location in `TTarget` where the effect is to be executed.
|
|
65
|
+
* @param customizer Function to transform the source object.
|
|
66
|
+
* @returns
|
|
67
|
+
*/
|
|
68
|
+
createRoot<T>(at: string, customizer: Func1<TSource, string>): Effect<TSource, T>;
|
|
69
|
+
/**
|
|
70
|
+
* Create an effect for the specified location for array data.
|
|
71
|
+
* @param at The location in `TTarget` where the effect is to be executed.
|
|
72
|
+
* @returns
|
|
73
|
+
*/
|
|
74
|
+
createForArray<T extends Array<U>, U extends object = any>(at: string): Effect<TSource, T>;
|
|
75
|
+
/**
|
|
76
|
+
* Create an effect for the specified location for raw value. This will not read from source object.
|
|
77
|
+
* @param at The location in `TTarget` where the effect is to be executed.
|
|
78
|
+
* @param raw Raw value to use.
|
|
79
|
+
* @returns
|
|
80
|
+
*/
|
|
81
|
+
createForRaw<T>(at: string, raw: T): Effect<TSource, T>;
|
|
82
|
+
/**
|
|
83
|
+
* Create an effect for the specified location. Use `customizer` to transform the source object.
|
|
84
|
+
* @param at The location in `TTarget` where the effect is to be executed.
|
|
85
|
+
* @param customizer Function to transform the source object.
|
|
86
|
+
* @returns
|
|
87
|
+
*/
|
|
88
|
+
createWith<T, R>(at: string, customizer: Func1<T, R>): Effect<TSource, R>;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
declare class Projector<TSource> implements IProjector<TSource> {
|
|
92
|
+
private _schema;
|
|
93
|
+
private _scheduler;
|
|
94
|
+
constructor(_schema: Schema<TSource>, _scheduler: IScheduler<any>);
|
|
95
|
+
project(next: TSource, ...patches: Patch[]): void;
|
|
96
|
+
private _dispatch;
|
|
97
|
+
private _resolveEffect;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Scheduler abstract class, used to schedule projector effects.
|
|
102
|
+
*/
|
|
103
|
+
declare abstract class Scheduler<TTarget> implements IScheduler<TTarget> {
|
|
104
|
+
protected _target: TTarget | null;
|
|
105
|
+
protected _targetFactory: Func<TTarget> | null;
|
|
106
|
+
constructor(_target?: TTarget | null);
|
|
107
|
+
abstract enqueue(item: IScheduleItem<TTarget, any>): IScheduler<TTarget>;
|
|
108
|
+
abstract flush(): MaybePromise<void>;
|
|
109
|
+
withTarget(target: TTarget): IScheduler<TTarget>;
|
|
110
|
+
withTarget(target: Func<TTarget>): IScheduler<TTarget>;
|
|
111
|
+
protected checkTarget(): void;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Buffered scheduler, will buffer effects and execute them in batch.
|
|
115
|
+
*/
|
|
116
|
+
declare class BufferedScheduler<TTarget> extends Scheduler<TTarget> {
|
|
117
|
+
protected readonly _queue: Map<string, IScheduleItem<TTarget, any>>;
|
|
118
|
+
enqueue(item: IScheduleItem<TTarget, any>): IScheduler<TTarget>;
|
|
119
|
+
flush(): Promise<void>;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
declare function track<TSource extends object>(initial: TSource): [TSource, Recorder<TSource>];
|
|
123
|
+
|
|
124
|
+
export { BufferedScheduler, EffectBuilder, Projector, Recorder, Scheduler, track };
|
package/types/types.d.ts
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// prettier-ignore
|
|
2
|
+
type Schema<TSource, T = any> = TSource extends object ?
|
|
3
|
+
Partial<{
|
|
4
|
+
// If the property is an array, map whole array to effect.
|
|
5
|
+
// Else map property to schema.
|
|
6
|
+
[K in keyof T]:
|
|
7
|
+
T[K] extends Array<infer U> ?
|
|
8
|
+
Effect<TSource, U>
|
|
9
|
+
// If TSource is an object, map each property to schema, or write effect for the property.
|
|
10
|
+
: Schema<TSource, T[K]> | Effect<TSource, T[K]>;
|
|
11
|
+
}>
|
|
12
|
+
// Else map T to effect.
|
|
13
|
+
: Effect<TSource, T>;
|
|
14
|
+
|
|
15
|
+
// todo: Solve this `any`
|
|
16
|
+
type Effect<TSource, TValue> = (target: any, ctx: IEffectContext<TSource, TValue>) => MaybePromise<void>;
|
|
17
|
+
|
|
18
|
+
interface IEffectContext<TSource, TValue> {
|
|
19
|
+
/**
|
|
20
|
+
* The source object.
|
|
21
|
+
*/
|
|
22
|
+
source?: TSource;
|
|
23
|
+
/**
|
|
24
|
+
* The path of the property.
|
|
25
|
+
*/
|
|
26
|
+
path?: string;
|
|
27
|
+
/**
|
|
28
|
+
* The value of the property.
|
|
29
|
+
*
|
|
30
|
+
* If `source` or `path` is not specified, the value is passed from another effect.
|
|
31
|
+
*/
|
|
32
|
+
value: TValue;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
interface ITargetStrategy<TTarget> {
|
|
36
|
+
execute<T>(target: TTarget, at: string, value: T): MaybePromise<void>;
|
|
37
|
+
executeArray<T>(target: TTarget, at: string, keys: Partial<keyof T>[], rows: T[]): MaybePromise<void>;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
interface IProjector<TSource> {
|
|
41
|
+
project(next: TSource, ...patches: Patch[]): void;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
interface IScheduler<TTarget> {
|
|
45
|
+
enqueue(effect: IScheduleItem): void;
|
|
46
|
+
flush(): MaybePromise<void>;
|
|
47
|
+
withTarget(target: TTarget): IScheduler<TTarget>;
|
|
48
|
+
withTarget(target: Func<TTarget>): IScheduler<TTarget>;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
interface IScheduleItem<TSource, TValue> {
|
|
52
|
+
path: string;
|
|
53
|
+
effect: Effect<TSource, TValue>;
|
|
54
|
+
ctx: IEffectContext<TSource, TValue>;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
interface Patch {
|
|
58
|
+
path: (string | symbol)[];
|
|
59
|
+
value: any;
|
|
60
|
+
}
|