as-model 0.0.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/LICENSE.md +21 -0
- package/README.md +413 -0
- package/dist/index.js +1025 -0
- package/esm/index.js +49 -0
- package/esm/key/index.js +73 -0
- package/esm/key/type.js +0 -0
- package/esm/model/index.js +28 -0
- package/esm/model/type.js +0 -0
- package/esm/store/index.js +153 -0
- package/esm/store/instance.js +197 -0
- package/esm/store/type.js +0 -0
- package/esm/tools.js +78 -0
- package/esm/updater/index.js +129 -0
- package/esm/updater/notifier.js +121 -0
- package/esm/updater/tunnel.js +138 -0
- package/esm/updater/type.js +0 -0
- package/esm/validation/index.js +25 -0
- package/index.d.ts +163 -0
- package/package.json +69 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) Jimmy.Harding
|
|
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
|
@@ -0,0 +1,413 @@
|
|
|
1
|
+
[![npm][npm-image]][npm-url]
|
|
2
|
+
[![NPM downloads][npm-downloads-image]][npm-url]
|
|
3
|
+
[![standard][standard-image]][standard-url]
|
|
4
|
+
|
|
5
|
+
[npm-image]: https://img.shields.io/npm/v/a-model.svg?style=flat-square
|
|
6
|
+
[npm-url]: https://www.npmjs.com/package/a-model
|
|
7
|
+
[standard-image]: https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat-square
|
|
8
|
+
[standard-url]: http://npm.im/standard
|
|
9
|
+
[npm-downloads-image]: https://img.shields.io/npm/dm/a-model.svg?style=flat-square
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# a-model
|
|
13
|
+
|
|
14
|
+
This is a simple state management library with model coding style for javascript/typescript.
|
|
15
|
+
|
|
16
|
+
## Code first
|
|
17
|
+
|
|
18
|
+
Create a model function:
|
|
19
|
+
|
|
20
|
+
```js
|
|
21
|
+
// model.js
|
|
22
|
+
// A parameter for model state.
|
|
23
|
+
export function counting(state){
|
|
24
|
+
// Define instance object for outside usage.
|
|
25
|
+
return {
|
|
26
|
+
// Define properties for instance.
|
|
27
|
+
count: state,
|
|
28
|
+
symbol: !state? '': (state > 0? '+' : '-'),
|
|
29
|
+
// Create action methods for changing state.
|
|
30
|
+
increase:()=> state + 1,
|
|
31
|
+
decrease:()=> state - 1,
|
|
32
|
+
add(...additions){
|
|
33
|
+
return additions.reduce((result, current)=>{
|
|
34
|
+
return result + current;
|
|
35
|
+
}, state);
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Create store:
|
|
42
|
+
|
|
43
|
+
```js
|
|
44
|
+
// store.js
|
|
45
|
+
import {counting} from './model';
|
|
46
|
+
import {createStore} from 'a-model';
|
|
47
|
+
|
|
48
|
+
// Create and initialize a model store.
|
|
49
|
+
const store = createStore(counting, 0);
|
|
50
|
+
// Get instance and call action methods to change state.
|
|
51
|
+
store.getInstance().increase();
|
|
52
|
+
// Get new properties from instance.
|
|
53
|
+
console.log(store.getInstance().count); // 1
|
|
54
|
+
store.getInstance().add(2,3);
|
|
55
|
+
console.log(store.getInstance().count); // 6
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Create multiple stores:
|
|
59
|
+
|
|
60
|
+
```js
|
|
61
|
+
import {counting} from './model';
|
|
62
|
+
import {createKey, createStores} from 'a-model';
|
|
63
|
+
|
|
64
|
+
// Create model key with initial state.
|
|
65
|
+
const countingKey0 = createKey(counting, 0);
|
|
66
|
+
const countingKey1 = createKey(counting, 1);
|
|
67
|
+
|
|
68
|
+
// Use model keys as templates to create multiple stores.
|
|
69
|
+
const stores = createStores(countingKey0, countingKey1);
|
|
70
|
+
// Find store by model key
|
|
71
|
+
const store0 = stores.find(countingKey0);
|
|
72
|
+
store0?.getInstance().increase();
|
|
73
|
+
console.log(store0?.getInstance().count); // 1
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Model key is a template for creating multiple stores, and it is also an identifier to find the rightstore from multiple stores.
|
|
77
|
+
|
|
78
|
+
Use **model** API to create store or key.
|
|
79
|
+
|
|
80
|
+
```js
|
|
81
|
+
import {counting} from './model';
|
|
82
|
+
import {model} from 'a-model';
|
|
83
|
+
|
|
84
|
+
const store = model(counting).createStore(0);
|
|
85
|
+
const key = model(counting).createKey(0);
|
|
86
|
+
......
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
In typescript develop environment, `model` API can do a type check for making sure the model action method returns a correct type.
|
|
90
|
+
|
|
91
|
+
```js
|
|
92
|
+
// ts
|
|
93
|
+
import {model} from 'a-model';
|
|
94
|
+
|
|
95
|
+
// The model api ensures every action method returns a same type value with model state.
|
|
96
|
+
const counting = model((state: number)=>{
|
|
97
|
+
return {
|
|
98
|
+
count: state,
|
|
99
|
+
increase:()=>state + 1 + '', // type error, should be number, but returns string.
|
|
100
|
+
decrease:()=>state - 1,
|
|
101
|
+
add(...additions: number[]){
|
|
102
|
+
return additions.reduce((result, current)=>{
|
|
103
|
+
return result + current;
|
|
104
|
+
}, state);
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
const store = counting.createStore(0);
|
|
110
|
+
const key = counting.createKey(0);
|
|
111
|
+
......
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Sync store
|
|
115
|
+
|
|
116
|
+
```js
|
|
117
|
+
import {counting} from './model';
|
|
118
|
+
import {model} from 'a-model';
|
|
119
|
+
|
|
120
|
+
const store = model(counting).createStore(0);
|
|
121
|
+
const {getInstance} = store;
|
|
122
|
+
// Subscribe the state changes.
|
|
123
|
+
const unsubscribe = store.subscribe((action)=>{
|
|
124
|
+
console.log(store.getInstance());
|
|
125
|
+
});
|
|
126
|
+
getInstance().increase(); // output: {count: 1}
|
|
127
|
+
// Destroy subscription.
|
|
128
|
+
unsubscribe();
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Sync store with state in react hooks:
|
|
132
|
+
|
|
133
|
+
```js
|
|
134
|
+
import {model, createStores} from 'a-model';
|
|
135
|
+
import {
|
|
136
|
+
createContext,
|
|
137
|
+
useRef,
|
|
138
|
+
useState,
|
|
139
|
+
useEffect,
|
|
140
|
+
useContext
|
|
141
|
+
} from 'react';
|
|
142
|
+
|
|
143
|
+
// Local state management
|
|
144
|
+
function useModel(modelFn, defaultState){
|
|
145
|
+
// Use ref to persist the store object.
|
|
146
|
+
const storeRef = useRef(model(modelFn).createStore(defaultState));
|
|
147
|
+
const store = storeRef.current;
|
|
148
|
+
// Set store instance as an initial state for useState.
|
|
149
|
+
const [state, setState] = useState(store.getInstance());
|
|
150
|
+
|
|
151
|
+
useEffect(()=>{
|
|
152
|
+
// Subscribe the state changes.
|
|
153
|
+
const unsubscribe = store.subscribe((action)=>{
|
|
154
|
+
setState(store.getInstance());
|
|
155
|
+
});
|
|
156
|
+
// Destroy subscription when component unmounts.
|
|
157
|
+
return unsubscribe;
|
|
158
|
+
}, []);
|
|
159
|
+
|
|
160
|
+
return state;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function App(){
|
|
164
|
+
const {
|
|
165
|
+
count,
|
|
166
|
+
increase
|
|
167
|
+
} = useModel(counting, 0);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// global static state management
|
|
171
|
+
function useModelStore(store){
|
|
172
|
+
const [state, setState] = useState(store.getInstance());
|
|
173
|
+
|
|
174
|
+
useEffect(()=>{
|
|
175
|
+
const unsubscribe = store.subscribe((action)=>{
|
|
176
|
+
setState(store.getInstance());
|
|
177
|
+
});
|
|
178
|
+
return unsubscribe;
|
|
179
|
+
}, []);
|
|
180
|
+
|
|
181
|
+
return state;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const store = model(counting).createStore({state: 0});
|
|
185
|
+
|
|
186
|
+
function App(){
|
|
187
|
+
const {
|
|
188
|
+
count,
|
|
189
|
+
increase
|
|
190
|
+
} = useModelStore(store);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// global dynamic state management
|
|
194
|
+
const ModelContext = createContext(null);
|
|
195
|
+
|
|
196
|
+
function create(...keys){
|
|
197
|
+
return {
|
|
198
|
+
provide(Component){
|
|
199
|
+
return function Provider(props){
|
|
200
|
+
// Create and persist multiple stores in Component, that makes different elements from this Component carry different stores.
|
|
201
|
+
const storesRef = useRef(createStores(...keys));
|
|
202
|
+
const stores = storesRef.current;
|
|
203
|
+
// Use Context to provide multiple stores to all the children.
|
|
204
|
+
return (
|
|
205
|
+
<ModelContext.Provider value={stores}>
|
|
206
|
+
<Component {...props} />
|
|
207
|
+
</ModelContext.Provider>
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function useModelKey(key){
|
|
215
|
+
const stores = useContext(ModelContext);
|
|
216
|
+
if(stores==null){
|
|
217
|
+
throw new Error('ModelContext is not provided');
|
|
218
|
+
}
|
|
219
|
+
// Use model key to find the right store.
|
|
220
|
+
const store = stores.find(key);
|
|
221
|
+
if(store==null){
|
|
222
|
+
throw new Error('Can not find store by model key');
|
|
223
|
+
}
|
|
224
|
+
const [state, setState] = useState(store.getInstance());
|
|
225
|
+
|
|
226
|
+
useEffect(()=>{
|
|
227
|
+
const unsubscribe = store.subscribe((action)=>{
|
|
228
|
+
setState(store.getInstance());
|
|
229
|
+
});
|
|
230
|
+
return unsubscribe;
|
|
231
|
+
}, []);
|
|
232
|
+
|
|
233
|
+
return state;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const countingKey = model(counting).createKey(0);
|
|
237
|
+
|
|
238
|
+
const App = create(countingKey).provide(function App(){
|
|
239
|
+
const {
|
|
240
|
+
count,
|
|
241
|
+
increase
|
|
242
|
+
} = useModelKey(countingKey);
|
|
243
|
+
});
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
## Install
|
|
247
|
+
|
|
248
|
+
```
|
|
249
|
+
npm install as-model
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
## Simplify API
|
|
253
|
+
|
|
254
|
+
### createStore
|
|
255
|
+
|
|
256
|
+
```js
|
|
257
|
+
function createStore(modelFnOrKey, initialState?):store
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
#### parameters
|
|
261
|
+
|
|
262
|
+
* modelFnOrKey - a model function accepts a state parameter and returns an object with action methods, or a model key of model function.
|
|
263
|
+
* initialState - the initial state of the model.
|
|
264
|
+
|
|
265
|
+
#### return
|
|
266
|
+
|
|
267
|
+
A store object with model key and methods. The store object has `getInstance` method to get the instance of the model; `subscribe` method to subscribe the state changes; `update` method to update the store with new model function and initial state; `destroy` method to destroy the store.
|
|
268
|
+
|
|
269
|
+
**store** structure:
|
|
270
|
+
|
|
271
|
+
```js
|
|
272
|
+
{
|
|
273
|
+
getInstance: ()=>instance,
|
|
274
|
+
subscribe: (listener)=>unsubscribe,
|
|
275
|
+
key: modelKey,
|
|
276
|
+
destroy: ()=>void
|
|
277
|
+
update: (
|
|
278
|
+
data:{
|
|
279
|
+
model?:modelFn,
|
|
280
|
+
initialState?: any
|
|
281
|
+
}
|
|
282
|
+
)=>void
|
|
283
|
+
}
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
### createKey
|
|
287
|
+
|
|
288
|
+
```js
|
|
289
|
+
function createKey(modelFn, initialState?):key
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
#### parameters
|
|
293
|
+
|
|
294
|
+
* modelFn - a model function accepts a state parameter and returns an object with action methods.
|
|
295
|
+
* initialState - the initial state of the model.
|
|
296
|
+
|
|
297
|
+
#### return
|
|
298
|
+
|
|
299
|
+
A model key function with `createStore` method to create a store with the model key.
|
|
300
|
+
|
|
301
|
+
**key** structure:
|
|
302
|
+
|
|
303
|
+
```js
|
|
304
|
+
{
|
|
305
|
+
createStore: (initialState?)=>store
|
|
306
|
+
}
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
### createStores
|
|
310
|
+
|
|
311
|
+
```js
|
|
312
|
+
function createStores(...keys):stores
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
#### parameters
|
|
316
|
+
|
|
317
|
+
* keys - multiple model keys of model functions.
|
|
318
|
+
|
|
319
|
+
#### return
|
|
320
|
+
|
|
321
|
+
Multiple stores created by the model keys.
|
|
322
|
+
|
|
323
|
+
**stores** structure:
|
|
324
|
+
|
|
325
|
+
```js
|
|
326
|
+
{
|
|
327
|
+
find: (key)=>store,
|
|
328
|
+
destroy: ()=>void
|
|
329
|
+
}
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
### createSignal
|
|
333
|
+
|
|
334
|
+
```js
|
|
335
|
+
function createSignal(store):signalAPI
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
#### parameters
|
|
339
|
+
|
|
340
|
+
* store - a store object created by `createStore` method.
|
|
341
|
+
|
|
342
|
+
#### return
|
|
343
|
+
|
|
344
|
+
Signal api object with `subscribe` method to subscribe the state changes, and `getSignal` method to get the signal callback function.
|
|
345
|
+
|
|
346
|
+
**signalAPI** structure:
|
|
347
|
+
|
|
348
|
+
```js
|
|
349
|
+
{
|
|
350
|
+
subscribe: (listener)=>unsubscribe,
|
|
351
|
+
getSignal: ()=>signal,
|
|
352
|
+
key: modelKey,
|
|
353
|
+
}
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
The signal function returns a real time instance from store. Only when the properties picked from real time instance are changed, the subscribed listener can receive an action notification.
|
|
357
|
+
|
|
358
|
+
### model
|
|
359
|
+
|
|
360
|
+
```js
|
|
361
|
+
function model(modelFn):modelAPI
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
#### parameters
|
|
365
|
+
|
|
366
|
+
* modelFn - a model function accepts a state parameter and returns an object with action methods.
|
|
367
|
+
|
|
368
|
+
#### return
|
|
369
|
+
|
|
370
|
+
Model api object with `createStore`and `createKey` methods to create store and key for the model function.
|
|
371
|
+
|
|
372
|
+
**modelAPI** structure:
|
|
373
|
+
|
|
374
|
+
```js
|
|
375
|
+
{
|
|
376
|
+
createStore: (initialState?)=>store,
|
|
377
|
+
createKey: (initialState?)=>key
|
|
378
|
+
}
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
### config
|
|
382
|
+
|
|
383
|
+
```js
|
|
384
|
+
function config(options):configAPI
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
#### parameters
|
|
388
|
+
|
|
389
|
+
* options - an object with the following properties:
|
|
390
|
+
* batchNotify - a callback function to batch notify the listeners, for example: `unstable_batchedUpdates` from react-dom.
|
|
391
|
+
|
|
392
|
+
#### return
|
|
393
|
+
|
|
394
|
+
All apis above except `createSignal` API.
|
|
395
|
+
|
|
396
|
+
```js
|
|
397
|
+
{
|
|
398
|
+
createStore: (modelFnOrKey, initialState?)=>store,
|
|
399
|
+
createKey: (modelFn, initialState?)=>key,
|
|
400
|
+
createStores: (...keys)=>stores,
|
|
401
|
+
model: (modelFn)=>modelAPI
|
|
402
|
+
}
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
## Browser Support
|
|
406
|
+
|
|
407
|
+
```
|
|
408
|
+
chrome: '>=91',
|
|
409
|
+
edge: '>=91',
|
|
410
|
+
firefox: '=>90',
|
|
411
|
+
safari: '>=15'
|
|
412
|
+
```
|
|
413
|
+
|