@vue-modeler/model 1.0.7 → 2.0.1-beta.1
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.cjs +1 -1
- package/dist/index.d.ts +73 -13
- package/dist/index.js +169 -161
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var g=Object.defineProperty;var m=(r,t,e)=>t in r?g(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e;var n=(r,t,e)=>m(r,typeof t!="symbol"?t+"":t,e);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const l=require("vue");class S extends Error{constructor(t,e){super(e.cause.message),this.actionName=t,this.options=e,this.name=this.constructor.name}get cause(){return this.options.cause}throwCause(){throw this.cause}toString(){return this.options.cause.message}}class u extends Error{constructor(t,e){super(t,e),this.message=t,this.options=e,this.name=this.constructor.name}}class p extends u{constructor(t,e,o){super(`Trying to update state of ${t} from ${e} to ${o}`),this.name=this.constructor.name}}class w extends u{constructor(t,e){super(`Unexpected AbortError for the action ${t} in state ${e}`),this.name=this.constructor.name}}const A=r=>r instanceof DOMException&&r.name==="AbortError"||typeof r=="object"&&r!==null&&"message"in r&&r.message==="canceled",s=class s{constructor(t,e){n(this,"name");n(this,"_state",s.possibleState.ready);n(this,"_value",null);n(this,"_args",null);this.model=t,this.actionFunction=e;const o=e.name;if(!(o in t&&typeof t[o]=="function"))throw new u(`Model does not contain method ${o}`);if(typeof e[s.actionFlag]!="function")throw new u(`Method ${o} is not action`);this.name=o}toString(){return this.name}get possibleStates(){return Object.values(s.possibleState)}get state(){return this._state}set state(t){this._state=t,this.model.setActionState(this)}get abortController(){return this.isPending?this._value.abortController:null}get args(){return this._args||[]}get promise(){return this.isPending?this._value.promise:null}get error(){return this.isError?this._value:null}get abortReason(){return this.isAbort?this._value.abortController.signal.reason:null}get isPending(){return this.state===s.possibleState.pending}get isError(){return this.state===s.possibleState.error}get isReady(){return this.state===s.possibleState.ready}get isLock(){return this.state===s.possibleState.lock}get isAbort(){return this.state===s.possibleState.abort}is(...t){return!!t.find(e=>this.state===e)}exec(...t){if(this.is(s.possibleState.lock,s.possibleState.pending))throw new p(this.name,this.state,s.possibleState.pending);const e=[...t];let o=t.length&&t[t.length-1];o instanceof AbortController||(o=new AbortController,e.push(o)),this.state=s.possibleState.pending,this._args=t;const i=this.actionFunction[s.actionFlag].apply(this.model,e);if(!(i instanceof Promise))return this.state=s.possibleState.ready,Promise.resolve();const d=i.then(()=>{this.ready()}).catch(a=>{if(a instanceof u||a instanceof RangeError||a instanceof ReferenceError||a instanceof SyntaxError||a instanceof TypeError||a instanceof URIError||a instanceof EvalError)throw a;const b=A(a);if(b&&!this.is(s.possibleState.pending,s.possibleState.lock))throw new w(this.name,this.state);if(b&&this._value.abortController instanceof AbortController&&this._value.abortController.signal.reason===s.abortedByLock){this.state=s.possibleState.lock,this._value=null;return}if(b){this.state=s.possibleState.abort;return}this.setError(new S(this.name,{cause:a}))});return this._value={promise:d,abortController:o},d}abort(t){return this.isPending?(this._value.abortController.abort(t),this._value.promise):Promise.resolve()}lock(){return this.isPending?this.abort(s.abortedByLock):(this.state=s.possibleState.lock,this._value=null,Promise.resolve())}unlock(){if(!this.isLock)throw new p(this.name,this.state,s.possibleState.ready);return this.ready()}setError(t){if(!this.isPending)throw new p(this.name,this.state,s.possibleState.error);return this.state=s.possibleState.error,this._value=t,this}ready(){return this.state=s.possibleState.ready,this}resetError(){if(!this.error)throw new p(this.name,this.state,s.possibleState.ready);return this.ready()}};n(s,"actionFlag",Symbol("__action_original_method__")),n(s,"possibleState",{pending:"pending",error:"error",lock:"lock",ready:"ready",abort:"abort"}),n(s,"abortedByLock",Symbol("lock"));let c=s;const y=Proxy;function v(r){if(!(r instanceof f))throw new Error("ProtoModel instance is required");return new y(l.shallowReactive(r),{get(e,o,h){const i=Reflect.get(e,o,h),d=typeof i=="function";return d&&c.actionFlag in i&&typeof i[c.actionFlag]=="function"?e.action(i):d?i.bind(e):i}})}class f{constructor(){n(this,"_effectScope",l.effectScope(!0));n(this,"_actions",new WeakMap);n(this,"_actionIds",new WeakMap);n(this,"_actionStates",new Map);n(this,"_actionsSize",0);n(this,"_watchStopHandlers",new Set)}static model(...t){if(this.prototype===f.prototype)throw new Error("ProtoModel is abstract class and can not be instantiated");const e=new this(...t);return v(e)}get hasPendingActions(){return!!this.getActionStatesRef(c.possibleState.pending).value}get hasActionWithError(){return!!this.getActionStatesRef(c.possibleState.error).value}watch(...t){if(t.length===0)throw new Error("watch requires at least one argument");const e=t.length===1?this._effectScope.run(()=>l.watchEffect(t[0])):this._effectScope.run(()=>l.watch(...t));if(!e)throw new Error("watchStopHandler is undefined");return this._watchStopHandlers.add(e),e}computed(t,e){return this._effectScope.run(()=>l.computed(t,e))}updateBit(t,e,o){const h=o?1:0,i=~(1<<e);return t&i|h<<e}createAction(t){const e=l.shallowReactive(new c(this,t));return this._actions.set(t,e),this._actionIds.set(e,++this._actionsSize),this.setActionState(e),e}getActionStatesRef(t){const e=this._actionStates.get(t)||l.ref(0);return this._actionStates.get(t)===void 0&&this._actionStates.set(t,e),e}action(t){if(!(c.actionFlag in t&&typeof t[c.actionFlag]=="function"))throw new u("Action decorator is not applied to the method");return this._actions.get(t)||this.createAction(t)}setActionState(t){const e=this._actionIds.get(t);if(!e)throw new Error("Action not found");for(const o of t.possibleStates)o!==t.state&&(this.getActionStatesRef(o).value=this.updateBit(this.getActionStatesRef(o).value,e,!1));this.getActionStatesRef(t.state).value=this.updateBit(this.getActionStatesRef(t.state).value,e,!0)}isModelOf(t){return this instanceof t}destructor(){this._watchStopHandlers.forEach(t=>{t()}),this._watchStopHandlers=new Set,this._effectScope.stop()}}function E(r,t){if(t.static)throw new Error("Action decorator is not supported for static methods");if(t.private)throw new Error("Action decorator is not supported for private methods");const e=t.name.toString(),o={[e]:function(...h){return this.action(o[e]).exec(...h)}};return o[e][c.actionFlag]=r,o[e]}exports.ActionError=S;exports.ActionInternalError=u;exports.ActionStatusConflictError=p;exports.ActionUnexpectedAbortError=w;exports.ProtoModel=f;exports.action=E;
|
package/dist/index.d.ts
CHANGED
|
@@ -2,8 +2,6 @@ import { ComputedGetter } from 'vue';
|
|
|
2
2
|
import { ComputedRef } from 'vue';
|
|
3
3
|
import { DebuggerOptions } from 'vue';
|
|
4
4
|
import { EffectScope } from 'vue';
|
|
5
|
-
import { Provider } from '@vue-modeler/dc';
|
|
6
|
-
import { ProviderOptions } from '@vue-modeler/dc';
|
|
7
5
|
import { Ref } from 'vue';
|
|
8
6
|
import { ShallowReactive } from 'vue';
|
|
9
7
|
import { WatchStopHandle } from 'vue';
|
|
@@ -53,7 +51,7 @@ declare class Action<Args extends any[] = unknown[]> {
|
|
|
53
51
|
resetError(): this;
|
|
54
52
|
}
|
|
55
53
|
|
|
56
|
-
export declare
|
|
54
|
+
export declare function action<T extends ProtoModel, Args extends unknown[]>(originalMethod: (this: T, ...args: Args) => Promise<void>, context: ClassMethodDecoratorContext<T, (this: T, ...args: Args) => Promise<void>>): OriginalMethodWrapper<Args>;
|
|
57
55
|
|
|
58
56
|
/**
|
|
59
57
|
* IMPORTANT: this class looks like error,
|
|
@@ -112,24 +110,20 @@ export declare class ActionUnexpectedAbortError extends ActionInternalError {
|
|
|
112
110
|
|
|
113
111
|
declare type ActionValue = ActionPendingValue | ActionError | null;
|
|
114
112
|
|
|
115
|
-
export declare function createModel<Target extends ProtoModel>(protoModel: Target): Model<Target>;
|
|
116
|
-
|
|
117
113
|
export declare type Model<T> = {
|
|
118
114
|
[K in keyof T]: T[K] extends ((...args: infer Args) => Promise<void>) ? Action<Args> : K extends ProtectedMethodInModel ? never : T[K];
|
|
119
115
|
};
|
|
120
116
|
|
|
121
|
-
export declare function model<T extends ProtoModel>(modelFactory: () => T, options?: ProviderOptions): Provider<Model<T>>;
|
|
122
|
-
|
|
123
117
|
export declare type ModelAdapterProxyConstructor = new <Target extends ProtoModel>(target: Target, handler: ProxyHandler<Target>) => Model<Target>;
|
|
124
118
|
|
|
125
119
|
export declare type ModelConstructor<T extends new (...args: unknown[]) => unknown> = T extends new (...args: unknown[]) => infer R ? R extends ProtoModel ? T : never : never;
|
|
126
120
|
|
|
127
121
|
declare type ModelConstructor_2 = new (...args: any[]) => ProtoModel;
|
|
128
122
|
|
|
129
|
-
export declare type OriginalMethod
|
|
123
|
+
export declare type OriginalMethod = (...args: any[]) => Promise<void>;
|
|
130
124
|
|
|
131
125
|
export declare interface OriginalMethodWrapper<Args extends unknown[] = unknown[]> {
|
|
132
|
-
(...args: Args):
|
|
126
|
+
(...args: Args): Promise<void>;
|
|
133
127
|
[Action.actionFlag]: OriginalMethod;
|
|
134
128
|
}
|
|
135
129
|
|
|
@@ -139,11 +133,75 @@ export declare abstract class ProtoModel {
|
|
|
139
133
|
protected _effectScope: EffectScope;
|
|
140
134
|
protected _actions: WeakMap<OriginalMethodWrapper<unknown[]>, ShallowReactive<ActionPublic>>;
|
|
141
135
|
protected _actionIds: WeakMap<ShallowReactive<ActionPublic>, number>;
|
|
142
|
-
protected _actionStates: Map<"
|
|
136
|
+
protected _actionStates: Map<"error" | "abort" | "lock" | "pending" | "ready", Ref<number>>;
|
|
143
137
|
protected _actionsSize: number;
|
|
144
|
-
protected
|
|
138
|
+
protected _watchStopHandlers: Set<WatchStopHandle>;
|
|
139
|
+
/**
|
|
140
|
+
* Creates a model instance.
|
|
141
|
+
*
|
|
142
|
+
* @param args - arguments for the model constructor.
|
|
143
|
+
* @returns model instance.
|
|
144
|
+
* @see src/create-model.ts
|
|
145
|
+
*/
|
|
146
|
+
static model<T extends ProtoModel, Args extends unknown[]>(this: new (...args: Args) => T, ...args: Args): Model<T>;
|
|
145
147
|
get hasPendingActions(): boolean;
|
|
146
148
|
get hasActionWithError(): boolean;
|
|
149
|
+
/**
|
|
150
|
+
* Registers watcher in the model effect scope.
|
|
151
|
+
* It uses "watch" or "watchEffect" function from Vue.
|
|
152
|
+
*
|
|
153
|
+
* It has restrictions. It can't be used with own not reactive properties.
|
|
154
|
+
* This is because `this` in inner context is not shallow reactive.
|
|
155
|
+
*
|
|
156
|
+
* Example with error:
|
|
157
|
+
*
|
|
158
|
+
* ```ts
|
|
159
|
+
* class TestModel extends ProtoModel {
|
|
160
|
+
* protected _someProperty: string = ''
|
|
161
|
+
*
|
|
162
|
+
* constructor () {
|
|
163
|
+
* super()
|
|
164
|
+
*
|
|
165
|
+
* // This watcher will not work because
|
|
166
|
+
* // `this` and `this.someProperty`
|
|
167
|
+
* // are not reactive
|
|
168
|
+
* this.watch(
|
|
169
|
+
* () => this.someProperty,
|
|
170
|
+
* (value: string) => {
|
|
171
|
+
* console.log(value)
|
|
172
|
+
* }
|
|
173
|
+
* )
|
|
174
|
+
* }
|
|
175
|
+
*
|
|
176
|
+
* get someProperty(): string {
|
|
177
|
+
* return this._someProperty
|
|
178
|
+
* }
|
|
179
|
+
* }
|
|
180
|
+
*
|
|
181
|
+
* To watch for own properties you need to define it as reactive property
|
|
182
|
+
* with ref or this.computed.
|
|
183
|
+
*
|
|
184
|
+
* ```ts
|
|
185
|
+
* class TestModel extends ProtoModel {
|
|
186
|
+
* protected _someProperty = ref('')
|
|
187
|
+
*
|
|
188
|
+
* constructor () {
|
|
189
|
+
* super()
|
|
190
|
+
*
|
|
191
|
+
* // This watcher will work because `this._someProperty` is reactive
|
|
192
|
+
* this.watch(
|
|
193
|
+
* () => this._someProperty.value,
|
|
194
|
+
* (value: string) => {
|
|
195
|
+
* console.log(value)
|
|
196
|
+
* }
|
|
197
|
+
* )
|
|
198
|
+
* }
|
|
199
|
+
* }
|
|
200
|
+
* ```
|
|
201
|
+
*
|
|
202
|
+
* @param args - arguments for "watch" or "watchEffect" function.
|
|
203
|
+
* @see src/create-model.ts
|
|
204
|
+
*/
|
|
147
205
|
protected watch(...args: unknown[]): WatchStopHandle;
|
|
148
206
|
protected computed<T>(getter: ComputedGetter<T>, debugOptions?: DebuggerOptions): ComputedRef<T>;
|
|
149
207
|
protected updateBit(number: number, bitPosition: number, bitValue: boolean): number;
|
|
@@ -154,6 +212,7 @@ export declare abstract class ProtoModel {
|
|
|
154
212
|
*
|
|
155
213
|
* From external context of model this method is implicitly used to get or create an action.
|
|
156
214
|
* It called inside get hook proxy of action.
|
|
215
|
+
* @see src/create-model.ts
|
|
157
216
|
*
|
|
158
217
|
* ```ts
|
|
159
218
|
* class TestModel extends ProtoModel {
|
|
@@ -167,9 +226,10 @@ export declare abstract class ProtoModel {
|
|
|
167
226
|
*
|
|
168
227
|
* In internal context of model this method is used to get Action instance.
|
|
169
228
|
* You can`t call it as `testModel.testAction.exec()` or `testModel.testAction.isPending` etc.
|
|
170
|
-
* because TypeScript sees it as
|
|
229
|
+
* because TypeScript sees it as standard method of the class and you will get error about type.
|
|
171
230
|
*
|
|
172
|
-
* To get Action instance in internal context of model
|
|
231
|
+
* To get Action instance in internal context of model
|
|
232
|
+
* you need to call it as `testModel.action(testModel.testAction)`
|
|
173
233
|
*
|
|
174
234
|
* ```ts
|
|
175
235
|
* class TestModel extends ProtoModel {
|
package/dist/index.js
CHANGED
|
@@ -1,66 +1,8 @@
|
|
|
1
|
-
var
|
|
2
|
-
var
|
|
3
|
-
var n = (
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
function k() {
|
|
7
|
-
var s;
|
|
8
|
-
const t = (s = y()) == null ? void 0 : s.proxy;
|
|
9
|
-
if (!t)
|
|
10
|
-
throw new Error("Current instance is undefined");
|
|
11
|
-
if (!t.$vueModelerDc)
|
|
12
|
-
throw new Error("Dependency container undefined. Check plugin setup");
|
|
13
|
-
return t.$vueModelerDc;
|
|
14
|
-
}
|
|
15
|
-
function g(s, t = { persistentInstance: !1 }) {
|
|
16
|
-
const e = Symbol("provider"), o = () => {
|
|
17
|
-
const a = k(), i = a.get(e) || a.add(e, s);
|
|
18
|
-
return t.persistentInstance || (i.subscribeOnParentScopeDispose(S), S(() => {
|
|
19
|
-
i.parentScopeCount > 0 || a.delete(e);
|
|
20
|
-
})), i.instance;
|
|
21
|
-
};
|
|
22
|
-
return o.__isProvider__ = !0, Object.defineProperty(
|
|
23
|
-
o,
|
|
24
|
-
"asKey",
|
|
25
|
-
{
|
|
26
|
-
value: e,
|
|
27
|
-
writable: !1
|
|
28
|
-
}
|
|
29
|
-
), o;
|
|
30
|
-
}
|
|
31
|
-
class M {
|
|
32
|
-
constructor() {
|
|
33
|
-
f(this, "isServer", !0), f(this, "stateKey", "__SSR_STATE__"), f(this, "serializers", /* @__PURE__ */ new Set()), f(this, "stateFromServer", {}), this.isServer = typeof window > "u", this.initStateFromServer();
|
|
34
|
-
}
|
|
35
|
-
initStateFromServer() {
|
|
36
|
-
if (this.isServer || !globalThis.__INITIAL_STATE__)
|
|
37
|
-
return;
|
|
38
|
-
const t = typeof globalThis.__INITIAL_STATE__ == "string" ? JSON.parse(globalThis.__INITIAL_STATE__) : globalThis.__INITIAL_STATE__;
|
|
39
|
-
typeof t[this.stateKey] == "object" && (this.stateFromServer = t[this.stateKey]);
|
|
40
|
-
}
|
|
41
|
-
extractState(t) {
|
|
42
|
-
return this.stateFromServer[t];
|
|
43
|
-
}
|
|
44
|
-
addSerializer(t) {
|
|
45
|
-
return this.isServer && this.serializers.add(t), t;
|
|
46
|
-
}
|
|
47
|
-
removeSerializer(t) {
|
|
48
|
-
return this.isServer ? this.serializers.delete(t) : !1;
|
|
49
|
-
}
|
|
50
|
-
serialize() {
|
|
51
|
-
const t = {};
|
|
52
|
-
for (const e of this.serializers) {
|
|
53
|
-
const o = e();
|
|
54
|
-
o.value !== void 0 && (t[o.extractionKey] = o.value);
|
|
55
|
-
}
|
|
56
|
-
return t;
|
|
57
|
-
}
|
|
58
|
-
injectState(t) {
|
|
59
|
-
this.isServer && (t[this.stateKey] = this.serialize());
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
g(() => new M());
|
|
63
|
-
class x extends Error {
|
|
1
|
+
var S = Object.defineProperty;
|
|
2
|
+
var w = (r, t, e) => t in r ? S(r, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : r[t] = e;
|
|
3
|
+
var n = (r, t, e) => w(r, typeof t != "symbol" ? t + "" : t, e);
|
|
4
|
+
import { shallowReactive as b, effectScope as m, watchEffect as g, watch as A, computed as y, ref as v } from "vue";
|
|
5
|
+
class k extends Error {
|
|
64
6
|
constructor(t, e) {
|
|
65
7
|
super(e.cause.message), this.actionName = t, this.options = e, this.name = this.constructor.name;
|
|
66
8
|
}
|
|
@@ -74,40 +16,40 @@ class x extends Error {
|
|
|
74
16
|
return this.options.cause.message;
|
|
75
17
|
}
|
|
76
18
|
}
|
|
77
|
-
class
|
|
19
|
+
class l extends Error {
|
|
78
20
|
constructor(t, e) {
|
|
79
21
|
super(t, e), this.message = t, this.options = e, this.name = this.constructor.name;
|
|
80
22
|
}
|
|
81
23
|
}
|
|
82
|
-
class
|
|
24
|
+
class p extends l {
|
|
83
25
|
constructor(t, e, o) {
|
|
84
26
|
super(`Trying to update state of ${t} from ${e} to ${o}`), this.name = this.constructor.name;
|
|
85
27
|
}
|
|
86
28
|
}
|
|
87
|
-
class
|
|
29
|
+
class E extends l {
|
|
88
30
|
constructor(t, e) {
|
|
89
31
|
super(`Unexpected AbortError for the action ${t} in state ${e}`), this.name = this.constructor.name;
|
|
90
32
|
}
|
|
91
33
|
}
|
|
92
|
-
const
|
|
34
|
+
const P = (r) => r instanceof DOMException && r.name === "AbortError" || typeof r == "object" && r !== null && "message" in r && r.message === "canceled", s = class s {
|
|
93
35
|
constructor(t, e) {
|
|
94
36
|
n(this, "name");
|
|
95
|
-
n(this, "_state",
|
|
37
|
+
n(this, "_state", s.possibleState.ready);
|
|
96
38
|
n(this, "_value", null);
|
|
97
39
|
n(this, "_args", null);
|
|
98
40
|
this.model = t, this.actionFunction = e;
|
|
99
41
|
const o = e.name;
|
|
100
42
|
if (!(o in t && typeof t[o] == "function"))
|
|
101
|
-
throw new
|
|
102
|
-
if (typeof e[
|
|
103
|
-
throw new
|
|
43
|
+
throw new l(`Model does not contain method ${o}`);
|
|
44
|
+
if (typeof e[s.actionFlag] != "function")
|
|
45
|
+
throw new l(`Method ${o} is not action`);
|
|
104
46
|
this.name = o;
|
|
105
47
|
}
|
|
106
48
|
toString() {
|
|
107
49
|
return this.name;
|
|
108
50
|
}
|
|
109
51
|
get possibleStates() {
|
|
110
|
-
return Object.values(
|
|
52
|
+
return Object.values(s.possibleState);
|
|
111
53
|
}
|
|
112
54
|
get state() {
|
|
113
55
|
return this._state;
|
|
@@ -132,19 +74,19 @@ const F = (s) => s instanceof DOMException && s.name === "AbortError" || typeof
|
|
|
132
74
|
return this.isAbort ? this._value.abortController.signal.reason : null;
|
|
133
75
|
}
|
|
134
76
|
get isPending() {
|
|
135
|
-
return this.state ===
|
|
77
|
+
return this.state === s.possibleState.pending;
|
|
136
78
|
}
|
|
137
79
|
get isError() {
|
|
138
|
-
return this.state ===
|
|
80
|
+
return this.state === s.possibleState.error;
|
|
139
81
|
}
|
|
140
82
|
get isReady() {
|
|
141
|
-
return this.state ===
|
|
83
|
+
return this.state === s.possibleState.ready;
|
|
142
84
|
}
|
|
143
85
|
get isLock() {
|
|
144
|
-
return this.state ===
|
|
86
|
+
return this.state === s.possibleState.lock;
|
|
145
87
|
}
|
|
146
88
|
get isAbort() {
|
|
147
|
-
return this.state ===
|
|
89
|
+
return this.state === s.possibleState.abort;
|
|
148
90
|
}
|
|
149
91
|
is(...t) {
|
|
150
92
|
return !!t.find((e) => this.state === e);
|
|
@@ -153,35 +95,35 @@ const F = (s) => s instanceof DOMException && s.name === "AbortError" || typeof
|
|
|
153
95
|
* Put into action in PENDING state
|
|
154
96
|
*/
|
|
155
97
|
exec(...t) {
|
|
156
|
-
if (this.is(
|
|
157
|
-
throw new
|
|
98
|
+
if (this.is(s.possibleState.lock, s.possibleState.pending))
|
|
99
|
+
throw new p(
|
|
158
100
|
this.name,
|
|
159
101
|
this.state,
|
|
160
|
-
|
|
102
|
+
s.possibleState.pending
|
|
161
103
|
);
|
|
162
104
|
const e = [...t];
|
|
163
105
|
let o = t.length && t[t.length - 1];
|
|
164
|
-
o instanceof AbortController || (o = new AbortController(), e.push(o)), this.state =
|
|
165
|
-
const i = this.actionFunction[
|
|
106
|
+
o instanceof AbortController || (o = new AbortController(), e.push(o)), this.state = s.possibleState.pending, this._args = t;
|
|
107
|
+
const i = this.actionFunction[s.actionFlag].apply(this.model, e);
|
|
166
108
|
if (!(i instanceof Promise))
|
|
167
|
-
return this.state =
|
|
109
|
+
return this.state = s.possibleState.ready, Promise.resolve();
|
|
168
110
|
const u = i.then(() => {
|
|
169
111
|
this.ready();
|
|
170
|
-
}).catch((
|
|
171
|
-
if (
|
|
172
|
-
throw
|
|
173
|
-
const
|
|
174
|
-
if (
|
|
175
|
-
throw new
|
|
176
|
-
if (
|
|
177
|
-
this.state =
|
|
112
|
+
}).catch((a) => {
|
|
113
|
+
if (a instanceof l || a instanceof RangeError || a instanceof ReferenceError || a instanceof SyntaxError || a instanceof TypeError || a instanceof URIError || a instanceof EvalError)
|
|
114
|
+
throw a;
|
|
115
|
+
const d = P(a);
|
|
116
|
+
if (d && !this.is(s.possibleState.pending, s.possibleState.lock))
|
|
117
|
+
throw new E(this.name, this.state);
|
|
118
|
+
if (d && this._value.abortController instanceof AbortController && this._value.abortController.signal.reason === s.abortedByLock) {
|
|
119
|
+
this.state = s.possibleState.lock, this._value = null;
|
|
178
120
|
return;
|
|
179
121
|
}
|
|
180
|
-
if (
|
|
181
|
-
this.state =
|
|
122
|
+
if (d) {
|
|
123
|
+
this.state = s.possibleState.abort;
|
|
182
124
|
return;
|
|
183
125
|
}
|
|
184
|
-
this.setError(new
|
|
126
|
+
this.setError(new k(this.name, { cause: a }));
|
|
185
127
|
});
|
|
186
128
|
return this._value = {
|
|
187
129
|
promise: u,
|
|
@@ -195,51 +137,65 @@ const F = (s) => s instanceof DOMException && s.name === "AbortError" || typeof
|
|
|
195
137
|
return this.isPending ? (this._value.abortController.abort(t), this._value.promise) : Promise.resolve();
|
|
196
138
|
}
|
|
197
139
|
lock() {
|
|
198
|
-
return this.isPending ? this.abort(
|
|
140
|
+
return this.isPending ? this.abort(s.abortedByLock) : (this.state = s.possibleState.lock, this._value = null, Promise.resolve());
|
|
199
141
|
}
|
|
200
142
|
unlock() {
|
|
201
143
|
if (!this.isLock)
|
|
202
|
-
throw new
|
|
144
|
+
throw new p(
|
|
203
145
|
this.name,
|
|
204
146
|
this.state,
|
|
205
|
-
|
|
147
|
+
s.possibleState.ready
|
|
206
148
|
);
|
|
207
149
|
return this.ready();
|
|
208
150
|
}
|
|
209
151
|
setError(t) {
|
|
210
152
|
if (!this.isPending)
|
|
211
|
-
throw new
|
|
153
|
+
throw new p(
|
|
212
154
|
this.name,
|
|
213
155
|
this.state,
|
|
214
|
-
|
|
156
|
+
s.possibleState.error
|
|
215
157
|
);
|
|
216
|
-
return this.state =
|
|
158
|
+
return this.state = s.possibleState.error, this._value = t, this;
|
|
217
159
|
}
|
|
218
160
|
ready() {
|
|
219
|
-
return this.state =
|
|
161
|
+
return this.state = s.possibleState.ready, this;
|
|
220
162
|
}
|
|
221
163
|
resetError() {
|
|
222
164
|
if (!this.error)
|
|
223
|
-
throw new
|
|
165
|
+
throw new p(
|
|
224
166
|
this.name,
|
|
225
167
|
this.state,
|
|
226
|
-
|
|
168
|
+
s.possibleState.ready
|
|
227
169
|
);
|
|
228
170
|
return this.ready();
|
|
229
171
|
}
|
|
230
172
|
};
|
|
231
|
-
n(
|
|
173
|
+
n(s, "actionFlag", Symbol("__action_original_method__")), n(s, "possibleState", {
|
|
232
174
|
pending: "pending",
|
|
233
175
|
error: "error",
|
|
234
176
|
lock: "lock",
|
|
235
177
|
ready: "ready",
|
|
236
178
|
abort: "abort"
|
|
237
|
-
}), n(
|
|
238
|
-
let
|
|
239
|
-
|
|
179
|
+
}), n(s, "abortedByLock", Symbol("lock"));
|
|
180
|
+
let c = s;
|
|
181
|
+
const _ = Proxy;
|
|
182
|
+
function M(r) {
|
|
183
|
+
if (!(r instanceof f))
|
|
184
|
+
throw new Error("ProtoModel instance is required");
|
|
185
|
+
return new _(
|
|
186
|
+
b(r),
|
|
187
|
+
{
|
|
188
|
+
get(e, o, h) {
|
|
189
|
+
const i = Reflect.get(e, o, h), u = typeof i == "function";
|
|
190
|
+
return u && c.actionFlag in i && typeof i[c.actionFlag] == "function" ? e.action(i) : u ? i.bind(e) : i;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
class f {
|
|
240
196
|
constructor() {
|
|
241
197
|
// each model has its own effect scope to avoid memory leaks
|
|
242
|
-
n(this, "_effectScope",
|
|
198
|
+
n(this, "_effectScope", m(!0));
|
|
243
199
|
// we use WeakMap to store actions as keys to avoid memory leaks
|
|
244
200
|
n(this, "_actions", /* @__PURE__ */ new WeakMap());
|
|
245
201
|
n(this, "_actionIds", /* @__PURE__ */ new WeakMap());
|
|
@@ -247,34 +203,105 @@ class w {
|
|
|
247
203
|
// WeakMap doesn't have a size property, so we need to store the size of the map
|
|
248
204
|
n(this, "_actionsSize", 0);
|
|
249
205
|
// watchers are stored in a set to avoid memory leaks
|
|
250
|
-
n(this, "
|
|
206
|
+
n(this, "_watchStopHandlers", /* @__PURE__ */ new Set());
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Creates a model instance.
|
|
210
|
+
*
|
|
211
|
+
* @param args - arguments for the model constructor.
|
|
212
|
+
* @returns model instance.
|
|
213
|
+
* @see src/create-model.ts
|
|
214
|
+
*/
|
|
215
|
+
static model(...t) {
|
|
216
|
+
if (this.prototype === f.prototype)
|
|
217
|
+
throw new Error("ProtoModel is abstract class and can not be instantiated");
|
|
218
|
+
const e = new this(...t);
|
|
219
|
+
return M(e);
|
|
251
220
|
}
|
|
252
221
|
get hasPendingActions() {
|
|
253
|
-
return !!this.getActionStatesRef(
|
|
222
|
+
return !!this.getActionStatesRef(c.possibleState.pending).value;
|
|
254
223
|
}
|
|
255
224
|
get hasActionWithError() {
|
|
256
|
-
return !!this.getActionStatesRef(
|
|
225
|
+
return !!this.getActionStatesRef(c.possibleState.error).value;
|
|
257
226
|
}
|
|
227
|
+
/**
|
|
228
|
+
* Registers watcher in the model effect scope.
|
|
229
|
+
* It uses "watch" or "watchEffect" function from Vue.
|
|
230
|
+
*
|
|
231
|
+
* It has restrictions. It can't be used with own not reactive properties.
|
|
232
|
+
* This is because `this` in inner context is not shallow reactive.
|
|
233
|
+
*
|
|
234
|
+
* Example with error:
|
|
235
|
+
*
|
|
236
|
+
* ```ts
|
|
237
|
+
* class TestModel extends ProtoModel {
|
|
238
|
+
* protected _someProperty: string = ''
|
|
239
|
+
*
|
|
240
|
+
* constructor () {
|
|
241
|
+
* super()
|
|
242
|
+
*
|
|
243
|
+
* // This watcher will not work because
|
|
244
|
+
* // `this` and `this.someProperty`
|
|
245
|
+
* // are not reactive
|
|
246
|
+
* this.watch(
|
|
247
|
+
* () => this.someProperty,
|
|
248
|
+
* (value: string) => {
|
|
249
|
+
* console.log(value)
|
|
250
|
+
* }
|
|
251
|
+
* )
|
|
252
|
+
* }
|
|
253
|
+
*
|
|
254
|
+
* get someProperty(): string {
|
|
255
|
+
* return this._someProperty
|
|
256
|
+
* }
|
|
257
|
+
* }
|
|
258
|
+
*
|
|
259
|
+
* To watch for own properties you need to define it as reactive property
|
|
260
|
+
* with ref or this.computed.
|
|
261
|
+
*
|
|
262
|
+
* ```ts
|
|
263
|
+
* class TestModel extends ProtoModel {
|
|
264
|
+
* protected _someProperty = ref('')
|
|
265
|
+
*
|
|
266
|
+
* constructor () {
|
|
267
|
+
* super()
|
|
268
|
+
*
|
|
269
|
+
* // This watcher will work because `this._someProperty` is reactive
|
|
270
|
+
* this.watch(
|
|
271
|
+
* () => this._someProperty.value,
|
|
272
|
+
* (value: string) => {
|
|
273
|
+
* console.log(value)
|
|
274
|
+
* }
|
|
275
|
+
* )
|
|
276
|
+
* }
|
|
277
|
+
* }
|
|
278
|
+
* ```
|
|
279
|
+
*
|
|
280
|
+
* @param args - arguments for "watch" or "watchEffect" function.
|
|
281
|
+
* @see src/create-model.ts
|
|
282
|
+
*/
|
|
258
283
|
watch(...t) {
|
|
259
|
-
|
|
284
|
+
if (t.length === 0)
|
|
285
|
+
throw new Error("watch requires at least one argument");
|
|
286
|
+
const e = t.length === 1 ? this._effectScope.run(() => g(t[0])) : this._effectScope.run(() => A(...t));
|
|
260
287
|
if (!e)
|
|
261
288
|
throw new Error("watchStopHandler is undefined");
|
|
262
|
-
return this.
|
|
289
|
+
return this._watchStopHandlers.add(e), e;
|
|
263
290
|
}
|
|
264
291
|
computed(t, e) {
|
|
265
|
-
return this._effectScope.run(() =>
|
|
292
|
+
return this._effectScope.run(() => y(t, e));
|
|
266
293
|
}
|
|
267
294
|
// @see https://github.com/trekhleb/javascript-algorithms/blob/master/src/algorithms/math/bits/updateBit.js
|
|
268
295
|
updateBit(t, e, o) {
|
|
269
|
-
const
|
|
270
|
-
return t & i |
|
|
296
|
+
const h = o ? 1 : 0, i = ~(1 << e);
|
|
297
|
+
return t & i | h << e;
|
|
271
298
|
}
|
|
272
299
|
createAction(t) {
|
|
273
|
-
const e = b(new
|
|
300
|
+
const e = b(new c(this, t));
|
|
274
301
|
return this._actions.set(t, e), this._actionIds.set(e, ++this._actionsSize), this.setActionState(e), e;
|
|
275
302
|
}
|
|
276
303
|
getActionStatesRef(t) {
|
|
277
|
-
const e = this._actionStates.get(t) ||
|
|
304
|
+
const e = this._actionStates.get(t) || v(0);
|
|
278
305
|
return this._actionStates.get(t) === void 0 && this._actionStates.set(t, e), e;
|
|
279
306
|
}
|
|
280
307
|
/**
|
|
@@ -282,6 +309,7 @@ class w {
|
|
|
282
309
|
*
|
|
283
310
|
* From external context of model this method is implicitly used to get or create an action.
|
|
284
311
|
* It called inside get hook proxy of action.
|
|
312
|
+
* @see src/create-model.ts
|
|
285
313
|
*
|
|
286
314
|
* ```ts
|
|
287
315
|
* class TestModel extends ProtoModel {
|
|
@@ -295,9 +323,10 @@ class w {
|
|
|
295
323
|
*
|
|
296
324
|
* In internal context of model this method is used to get Action instance.
|
|
297
325
|
* You can`t call it as `testModel.testAction.exec()` or `testModel.testAction.isPending` etc.
|
|
298
|
-
* because TypeScript sees it as
|
|
326
|
+
* because TypeScript sees it as standard method of the class and you will get error about type.
|
|
299
327
|
*
|
|
300
|
-
* To get Action instance in internal context of model
|
|
328
|
+
* To get Action instance in internal context of model
|
|
329
|
+
* you need to call it as `testModel.action(testModel.testAction)`
|
|
301
330
|
*
|
|
302
331
|
* ```ts
|
|
303
332
|
* class TestModel extends ProtoModel {
|
|
@@ -319,8 +348,8 @@ class w {
|
|
|
319
348
|
* @returns action
|
|
320
349
|
*/
|
|
321
350
|
action(t) {
|
|
322
|
-
if (!(
|
|
323
|
-
throw new
|
|
351
|
+
if (!(c.actionFlag in t && typeof t[c.actionFlag] == "function"))
|
|
352
|
+
throw new l("Action decorator is not applied to the method");
|
|
324
353
|
return this._actions.get(t) || this.createAction(t);
|
|
325
354
|
}
|
|
326
355
|
/**
|
|
@@ -341,52 +370,31 @@ class w {
|
|
|
341
370
|
return this instanceof t;
|
|
342
371
|
}
|
|
343
372
|
destructor() {
|
|
344
|
-
this.
|
|
373
|
+
this._watchStopHandlers.forEach((t) => {
|
|
345
374
|
t();
|
|
346
|
-
}), this.
|
|
375
|
+
}), this._watchStopHandlers = /* @__PURE__ */ new Set(), this._effectScope.stop();
|
|
347
376
|
}
|
|
348
377
|
}
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
{
|
|
356
|
-
get(e, o, a) {
|
|
357
|
-
const i = Reflect.get(e, o, a), u = typeof i == "function";
|
|
358
|
-
return u && l.actionFlag in i && typeof i[l.actionFlag] == "function" ? e.action(i) : u ? i.bind(e) : i;
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
);
|
|
362
|
-
}
|
|
363
|
-
function j(s, t) {
|
|
364
|
-
return g(() => z(s()), t);
|
|
365
|
-
}
|
|
366
|
-
const B = (s, t, e) => {
|
|
367
|
-
if (typeof t != "string")
|
|
368
|
-
throw new Error("Action name is not a string");
|
|
369
|
-
if (typeof e.value != "function")
|
|
370
|
-
throw new Error("Property value is not a function");
|
|
371
|
-
if (!(s instanceof w))
|
|
372
|
-
throw new Error("Target is not instance of ProtoModel");
|
|
373
|
-
const o = e.value, a = {
|
|
378
|
+
function I(r, t) {
|
|
379
|
+
if (t.static)
|
|
380
|
+
throw new Error("Action decorator is not supported for static methods");
|
|
381
|
+
if (t.private)
|
|
382
|
+
throw new Error("Action decorator is not supported for private methods");
|
|
383
|
+
const e = t.name.toString(), o = {
|
|
374
384
|
// Action constructor checks that model has method with the same name.
|
|
375
385
|
// We can`t define anonymous function and change name because Function.name is readonly property.
|
|
376
386
|
// So we need to create stub object to save original method name
|
|
377
|
-
[
|
|
378
|
-
return this.action(
|
|
387
|
+
[e]: function(...h) {
|
|
388
|
+
return this.action(o[e]).exec(...h);
|
|
379
389
|
}
|
|
380
390
|
};
|
|
381
|
-
|
|
382
|
-
}
|
|
391
|
+
return o[e][c.actionFlag] = r, o[e];
|
|
392
|
+
}
|
|
383
393
|
export {
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
z as createModel,
|
|
391
|
-
j as model
|
|
394
|
+
k as ActionError,
|
|
395
|
+
l as ActionInternalError,
|
|
396
|
+
p as ActionStatusConflictError,
|
|
397
|
+
E as ActionUnexpectedAbortError,
|
|
398
|
+
f as ProtoModel,
|
|
399
|
+
I as action
|
|
392
400
|
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@vue-modeler/model",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "2.0.1-beta.1",
|
|
5
5
|
"description": "A state management library based on models for Vue.js",
|
|
6
6
|
"author": "abratko",
|
|
7
7
|
"license": "MIT",
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
"@types/node": "^22.14.0",
|
|
29
29
|
"@vitest/coverage-v8": "2.1.3",
|
|
30
30
|
"@vue/test-utils": "^1.3.6",
|
|
31
|
+
"conventional-changelog-conventionalcommits": "^9.1.0",
|
|
31
32
|
"eslint": "^9.23.0",
|
|
32
33
|
"eslint-scope": "^8.3.0",
|
|
33
34
|
"eslint-visitor-keys": "^4.2.0",
|
|
@@ -40,7 +41,6 @@
|
|
|
40
41
|
"vitest": "^2.1.9"
|
|
41
42
|
},
|
|
42
43
|
"peerDependencies": {
|
|
43
|
-
"@vue-modeler/dc": ">=2.1.0 <3.0.0-beta.999",
|
|
44
44
|
"vue": ">=2.7 <4.0.0"
|
|
45
45
|
},
|
|
46
46
|
"scripts": {
|