skandha 0.11.0 → 0.12.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/README.md +36 -21
- package/index.d.ts +2 -1
- package/index.js +2 -1
- package/internal/logging.js +19 -32
- package/internal/options.d.ts +0 -3
- package/internal/options.js +4 -6
- package/internal/symbols.js +1 -1
- package/internal/utils.js +53 -20
- package/lib/cleanUp.d.ts +2 -0
- package/lib/cleanUp.js +14 -0
- package/lib/data.js +6 -8
- package/lib/facet.js +4 -4
- package/lib/install.js +1 -1
- package/lib/operation.js +17 -64
- package/lib/patch.js +5 -5
- package/lib/registerCtr.d.ts +7 -2
- package/lib/registerCtr.js +13 -10
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,22 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
## Introduction
|
|
4
4
|
|
|
5
|
-
SkandhaJS is a library that provides data containers with reusable behaviors, or "facets," that are implemented as classes. Examples of facets are selection, highlighting, filtering, drag-and-drop, etc. These facets provide interfaces for specific behaviors and rely on callback functions provided by the programmer to specify the details of those behaviors. The goal of SkandhaJS is to provide a way to create interactions between different behaviors in a consistent and modular way.
|
|
5
|
+
SkandhaJS is a library that provides data containers with generic (reusable) behaviors, or "facets," that are implemented as classes. Examples of facets are selection, highlighting, filtering, drag-and-drop, etc. These facets provide interfaces for specific behaviors and rely on callback functions provided by the programmer to specify the details of those behaviors. The goal of SkandhaJS is to provide a way to create interactions between different behaviors in a consistent and modular way.
|
|
6
6
|
|
|
7
7
|
As a brief illustration, consider the example of selecting a todo in a list of todos:
|
|
8
8
|
|
|
9
9
|
1. The application has a local state with a list of todos. The state contains a `todoSelection` facet so that clients can make a selection.
|
|
10
|
-
2. When the user clicks on a todo, the todo-list component calls `todoSelection.selectItem(todo.id, shift, ctrl)`.
|
|
10
|
+
2. When the user clicks on a todo, the todo-list component calls `todoSelection.selectItem({id: todo.id, shift, ctrl})`.
|
|
11
11
|
3. The `todoSelection.selectItem` function runs the `select` function from its `callbackMap`.
|
|
12
12
|
4. The `select` callback performs the selection and a side-effect: it highlights the item as well.
|
|
13
13
|
|
|
14
14
|
## Benefits
|
|
15
15
|
|
|
16
|
-
There are three ways in which facets help you to write
|
|
16
|
+
There are three ways in which facets help you to write reusable code:
|
|
17
17
|
|
|
18
18
|
1. They can be reused in any container, regardless of what data-type is stored in the container. This means that selection, filtering and drag-and-drop work the same everywhere in the application.
|
|
19
19
|
2. They allow you to capture the interaction between behaviours in reusable functions.
|
|
20
|
-
3. They allow you to handle user-interaction in UI components in a uniform way. All UI components that receive a selection facet will use the same interface to make a selection (and in theory the component can remain agnostic of how selection works, or even of the type of items that are being selected).
|
|
20
|
+
3. They allow you to handle user-interaction in UI components in a uniform way. All UI components that receive a selection facet will use the same interface to make a selection (and in theory the component can remain agnostic of how selection works, or even of the type of the items that are being selected).
|
|
21
21
|
|
|
22
22
|
## Links
|
|
23
23
|
|
|
@@ -30,9 +30,10 @@ There are three ways in which facets help you to write generic code:
|
|
|
30
30
|
## Glossary (conceptual)
|
|
31
31
|
|
|
32
32
|
- callback function: a function that is called by an operation to help implement that operation
|
|
33
|
-
-
|
|
33
|
+
- callbacks object: a collection of callback functions that is used by an operation.
|
|
34
|
+
- callbackMap: a dictionary that contains a callbacks object for every operation of a facet
|
|
34
35
|
- container: an object that contains data and facets. Typically, these facets are made to work together using callback functions.
|
|
35
|
-
- facet: a class that implements a
|
|
36
|
+
- facet: a class that implements a generic behaviour, such as selection.
|
|
36
37
|
- operation: a member function of a facet that changes the data of that facet.
|
|
37
38
|
|
|
38
39
|
## Glossary (technical)
|
|
@@ -40,11 +41,11 @@ There are three ways in which facets help you to write generic code:
|
|
|
40
41
|
- aspiration: a library that implements a form of Aspect Oriented Programming using callback functions.
|
|
41
42
|
- mapDataToProps: a helper function that maps data from one object to another. It's used to map data between facets.
|
|
42
43
|
- registerCtr: the function that tells SkandhaJS which containers and facets exist.
|
|
43
|
-
- setCallbackMap: the function that installs the callbackMap
|
|
44
|
+
- setCallbackMap: the function that installs the callbackMap for a facet.
|
|
44
45
|
|
|
45
46
|
## Example: a Selection facet
|
|
46
47
|
|
|
47
|
-
In Skandha, a behaviour such as selection is implemented in a "facet" class. The purpose of the selection facet is to store (and manipulate) the
|
|
48
|
+
In Skandha, a behaviour such as selection is implemented in a "facet" class. The purpose of the selection facet is to store (and manipulate) the selection state. In this example, we will show a simplified `Selection` facet class.
|
|
48
49
|
|
|
49
50
|
```
|
|
50
51
|
// file: Selection.ts
|
|
@@ -76,7 +77,7 @@ export type SelectionCbs<T = any> = DefineCbs<Selection<T>, Cbs<T>>;
|
|
|
76
77
|
|
|
77
78
|
## Facets have @input, @data and @output members
|
|
78
79
|
|
|
79
|
-
In `Selection.ts` we see the Selection facet class. It has a `selectableIds` field that stores the list of ids from which we can select. The `@input` decorator marks this field as an input. The `ids`, `anchorId` and `items` fields store the result of calling `selectItem()` and are marked as `@output` fields. Fields that are decorated with `@data` are used both as output and input. These decorators help with logging, and they also make it possible to bind the facets to state-management solutions such as MobX.
|
|
80
|
+
In `Selection.ts` we see the `Selection` facet class. It has a `selectableIds` field that stores the list of ids from which we can select. The `@input` decorator marks this field as an input. The `ids`, `anchorId` and `items` fields store the result of calling `selectItem()` and are marked as `@output` fields. Fields that are decorated with `@data` are used both as output and input. These decorators help with logging, and they also make it possible to bind the facets to state-management solutions such as MobX (this is explained later).
|
|
80
81
|
|
|
81
82
|
## Operations are decorated with `@operation`.
|
|
82
83
|
|
|
@@ -84,24 +85,38 @@ The `Selection` facet has a `selectItem()` operation that allows you to select f
|
|
|
84
85
|
|
|
85
86
|
## Operations are logged
|
|
86
87
|
|
|
87
|
-
As a side effect of calling an operation, the entire container is logged to the console (if logging is enabled); this includes the Selection facet, and any other facets that the container may have (such as filtering, or highlight). The log message contains all
|
|
88
|
+
As a side effect of calling an operation, the entire container is logged to the console (if logging is enabled); this includes the `Selection` facet, and any other facets that the container may have (such as filtering, or highlight). The log message contains all `@input`, `@data` and `@output` fields of all facets in the container.
|
|
88
89
|
|
|
89
90
|
## The `@withCbs` decorator is used to enable callbacks in an operation.
|
|
90
91
|
|
|
91
92
|
The `@withCbs` decorator comes from the `Aspiration` library. When you call a function `f` that is decorated with `@withCbs` then the following happens:
|
|
92
93
|
|
|
93
|
-
- Aspiration looks up the
|
|
94
|
-
-
|
|
95
|
-
-
|
|
96
|
-
-
|
|
94
|
+
- Aspiration looks up the callbacks object (that was installed with `setCallbackMap()`) for the given operation;
|
|
95
|
+
- It copies the operation arguments to the callbacks object, so that callback functions can access these arguments;
|
|
96
|
+
- It calls `f(arguments)`, which internally will use the installed callback functions to do its job;
|
|
97
|
+
- It restores the callbacks object to its previous state. This ensures that nested calls work correctly.
|
|
97
98
|
|
|
98
|
-
If the original function `f` wants to access the
|
|
99
|
+
If the original function `f` wants to access the callbacks object then it uses the `getCallbacks` helper function. Note that the client code calls `f` in the usual way; it remains agnostic of the fact that callbacks are used to help implement `f`.
|
|
99
100
|
|
|
100
101
|
## Facets have a static `className()` function
|
|
101
102
|
|
|
102
|
-
To allow SkandhaJS to log the container, you need to add the `className()` function to each facet class, so that Skandha knows the name of the facet.
|
|
103
|
-
|
|
104
|
-
not override the `className()`. By keeping the original `className()` function, you will be able to replace facets with their subclassed versions without affecting the facet lookup mechanism.
|
|
103
|
+
To allow SkandhaJS to log the container, you need to add the `className()` function to each facet class, so that Skandha knows the name of the facet. The `className()` function is also used to look up facets in containers with the `getf` function (e.g. `getf(Selection, ctr)` returns the selection facet in `ctr`).
|
|
104
|
+
|
|
105
|
+
Note that if you subclass a facet class then you should (in most cases) not override the `className()`. By keeping the original `className()` function, you will be able to replace facets with their subclassed versions without affecting the facet lookup mechanism. However, if you subclass the facet then you must add the `skandhaSymbol` member, as explained below.
|
|
106
|
+
|
|
107
|
+
## Facets that use inheritance must have a static `skandhaSymbol` member
|
|
108
|
+
|
|
109
|
+
Skandha will store class-level information about a facet class in the class itself. If you subclass a facet class then you must add a `skandhaSymbol` member so that Skandha will keep the information for the subclass separate from the information for the parent class:
|
|
110
|
+
|
|
111
|
+
```
|
|
112
|
+
// file: Selection.ts
|
|
113
|
+
import { Selection } from 'skandha-facets';
|
|
114
|
+
|
|
115
|
+
export class MySelection extends Selection {
|
|
116
|
+
static className = () => 'Selection';
|
|
117
|
+
static skandhaSymbol = Symbol('MySelection');
|
|
118
|
+
}
|
|
119
|
+
```
|
|
105
120
|
|
|
106
121
|
## Example: a container with Selection, Highlight and Filtering
|
|
107
122
|
|
|
@@ -188,7 +203,7 @@ We call `setCallbackMap()` to install a callbackMap object for each operation of
|
|
|
188
203
|
|
|
189
204
|
## The callback function can access the callbackMap object through `this`.
|
|
190
205
|
|
|
191
|
-
When the clients calls `todosCtr.selection.selectItem()` and `select()` is called back, then all arguments of the operation are available through `this`. In our case, we see that the `select()` callback function is accessing `this.args`. See the `aspiration` library documentation for details.
|
|
206
|
+
When the clients calls `todosCtr.selection.selectItem()` and `select()` is called back, then all arguments of the `selectItem` operation are available through `this`. In our case, we see that the `select()` callback function is accessing `this.args`. See the `aspiration` library documentation for details.
|
|
192
207
|
|
|
193
208
|
## Callbacks can take care of side effects
|
|
194
209
|
|
|
@@ -200,7 +215,7 @@ In `createContainer.ts` we also have a `Filtering` facet that has an `apply(f)`
|
|
|
200
215
|
|
|
201
216
|
## The `registerCtr()` allows SkandhaJS to introspect the container
|
|
202
217
|
|
|
203
|
-
At the end of `createContainer.ts` we call `registerCtr()` so that SkandhaJS can introspect the container. This is necessary for two reasons. First, it makes it possible to look up a facet by its name or class, e.g. `getf(
|
|
218
|
+
At the end of `createContainer.ts` we call `registerCtr()` so that SkandhaJS can introspect the container. This is necessary for two reasons. First, it makes it possible to look up a facet by its name or class, e.g. `getf(Selection, todosCtr)` returns `todosCtr.selection`. Second, it informs SkandhaJS how to log all facets of the container.
|
|
204
219
|
|
|
205
220
|
## Example: `mapDataToProps()` maps data onto each facet
|
|
206
221
|
|
|
@@ -269,7 +284,7 @@ If you want to render the facets with React then we need to let React know when
|
|
|
269
284
|
## Conclusion
|
|
270
285
|
|
|
271
286
|
SkandhaJS introduces a lot of new patterns and ideas (that are of course not in fact new) to allow you to
|
|
272
|
-
put
|
|
287
|
+
put generic behaviours on top of your existing data-structures. The difference to a more standard approach
|
|
273
288
|
can feel overwhelming, but it's important to point out the beneficial side effect of
|
|
274
289
|
separating concerns in a clear and understandable way. We could add a `Editing` facet to our code and install
|
|
275
290
|
a policy that says that changing the highlight disables editing. By using facets, we will not be tempted to put
|
package/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { getCtrState } from './internal/logging';
|
|
2
|
-
export {
|
|
2
|
+
export { getOptions, setOptions } from './internal/options';
|
|
3
|
+
export { addCleanUpFunctionToCtr, cleanUpCtr } from './lib/cleanUp';
|
|
3
4
|
export { getFacetMemberNames, getc } from './lib/ctr';
|
|
4
5
|
export { data, getDataMemberNames, getLoggedMemberNames, input, log, output, } from './lib/data';
|
|
5
6
|
export { getf } from './lib/facet';
|
package/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { getCtrState } from './internal/logging';
|
|
2
|
-
export {
|
|
2
|
+
export { getOptions, setOptions } from './internal/options';
|
|
3
|
+
export { addCleanUpFunctionToCtr, cleanUpCtr } from './lib/cleanUp';
|
|
3
4
|
export { getFacetMemberNames, getc } from './lib/ctr';
|
|
4
5
|
export { data, getDataMemberNames, getLoggedMemberNames, input, log, output, } from './lib/data';
|
|
5
6
|
export { getf } from './lib/facet';
|
package/internal/logging.js
CHANGED
|
@@ -1,21 +1,10 @@
|
|
|
1
|
-
var __assign = (this && this.__assign) || function () {
|
|
2
|
-
__assign = Object.assign || function(t) {
|
|
3
|
-
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
4
|
-
s = arguments[i];
|
|
5
|
-
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
6
|
-
t[p] = s[p];
|
|
7
|
-
}
|
|
8
|
-
return t;
|
|
9
|
-
};
|
|
10
|
-
return __assign.apply(this, arguments);
|
|
11
|
-
};
|
|
12
1
|
import { getCtrAdmin, getFacetAdmin } from '../internal/utils';
|
|
13
|
-
import {
|
|
2
|
+
import { getFacetMemberNames, getc } from '../lib/ctr';
|
|
14
3
|
import { getLoggedMemberNames } from '../lib/data';
|
|
15
4
|
import { options } from './options';
|
|
16
5
|
export function _defaultFacetLogName(facet) {
|
|
17
|
-
|
|
18
|
-
|
|
6
|
+
const ctr = getc(facet);
|
|
7
|
+
const prefix = ctr ? ctr.constructor.name + '/' : '';
|
|
19
8
|
return prefix + facet.constructor.name;
|
|
20
9
|
}
|
|
21
10
|
export function facetLogName(facet) {
|
|
@@ -29,45 +18,43 @@ function camelToSnake(string) {
|
|
|
29
18
|
})
|
|
30
19
|
.toLowerCase();
|
|
31
20
|
}
|
|
32
|
-
|
|
21
|
+
const opName = (operationMember) => camelToSnake(operationMember).toUpperCase();
|
|
33
22
|
export function log(facet, operationMember, args, start) {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
23
|
+
const ctr = getc(facet);
|
|
24
|
+
const getState = ctr ? () => getCtrState(ctr) : () => facetState(facet);
|
|
25
|
+
const operationName = opName(operationMember);
|
|
26
|
+
const label = facetLogName(facet) + '.' + operationName;
|
|
38
27
|
if (start) {
|
|
39
28
|
console.group(label);
|
|
40
|
-
console.log('%c
|
|
41
|
-
console.log('%c
|
|
29
|
+
console.log('%c args: ', 'color: gray', args);
|
|
30
|
+
console.log('%c state', 'color: gray', getState());
|
|
42
31
|
}
|
|
43
32
|
else {
|
|
44
|
-
console.log('%c
|
|
33
|
+
console.log('%c next', 'color: gray', getState());
|
|
45
34
|
// @ts-ignore
|
|
46
35
|
console.groupEnd(label);
|
|
47
36
|
}
|
|
48
37
|
}
|
|
49
38
|
export function facetState(facet) {
|
|
50
|
-
return getLoggedMemberNames(facet).reduce(
|
|
51
|
-
var _a;
|
|
39
|
+
return getLoggedMemberNames(facet).reduce((acc, loggedMember) => {
|
|
52
40
|
try {
|
|
53
|
-
|
|
54
|
-
return
|
|
41
|
+
const data = options.formatObject(facet[loggedMember]);
|
|
42
|
+
return Object.assign(Object.assign({}, acc), { [loggedMember]: data });
|
|
55
43
|
}
|
|
56
|
-
catch (
|
|
44
|
+
catch (_a) {
|
|
57
45
|
return acc;
|
|
58
46
|
}
|
|
59
47
|
}, {});
|
|
60
48
|
}
|
|
61
49
|
export function defaultGetCtrState(ctr) {
|
|
62
50
|
if (ctr) {
|
|
63
|
-
return getFacetMemberNames(ctr).reduce(
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
return __assign(__assign({}, acc), (_a = {}, _a[facetMemberName] = facetState(facet), _a));
|
|
51
|
+
return getFacetMemberNames(ctr).reduce((acc, facetMemberName) => {
|
|
52
|
+
const facet = ctr[facetMemberName];
|
|
53
|
+
return Object.assign(Object.assign({}, acc), { [facetMemberName]: facetState(facet) });
|
|
67
54
|
}, {});
|
|
68
55
|
}
|
|
69
56
|
}
|
|
70
57
|
export function getCtrState(ctr) {
|
|
71
58
|
var _a, _b;
|
|
72
|
-
return ((_b = (_a = getCtrAdmin(ctr)) === null || _a === void 0 ? void 0 : _a.
|
|
59
|
+
return ((_b = (_a = getCtrAdmin(ctr)) === null || _a === void 0 ? void 0 : _a.getCtrState) !== null && _b !== void 0 ? _b : defaultGetCtrState)(ctr);
|
|
73
60
|
}
|
package/internal/options.d.ts
CHANGED
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
export declare const options: {
|
|
2
2
|
logging: boolean;
|
|
3
3
|
formatObject: (x: any) => any;
|
|
4
|
-
decorateCb: (x: any) => any;
|
|
5
4
|
};
|
|
6
5
|
export declare const getOptions: () => {
|
|
7
6
|
logging: boolean;
|
|
8
7
|
formatObject: (x: any) => any;
|
|
9
|
-
decorateCb: (x: any) => any;
|
|
10
8
|
};
|
|
11
9
|
export declare const setOptions: (moreOptions: any) => void;
|
|
12
|
-
export declare const decorateCb: (x: any) => any;
|
package/internal/options.js
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
export
|
|
1
|
+
export const options = {
|
|
2
2
|
logging: false,
|
|
3
|
-
formatObject:
|
|
4
|
-
decorateCb: function (x) { return x; },
|
|
3
|
+
formatObject: (x) => x,
|
|
5
4
|
};
|
|
6
|
-
export
|
|
7
|
-
export
|
|
5
|
+
export const getOptions = () => options;
|
|
6
|
+
export const setOptions = (moreOptions) => {
|
|
8
7
|
Object.assign(options, moreOptions);
|
|
9
8
|
};
|
|
10
|
-
export var decorateCb = function (x) { return options.decorateCb(x); };
|
package/internal/symbols.js
CHANGED
package/internal/utils.js
CHANGED
|
@@ -1,34 +1,67 @@
|
|
|
1
|
-
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
|
2
|
-
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
3
|
-
if (ar || !(i in from)) {
|
|
4
|
-
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
5
|
-
ar[i] = from[i];
|
|
6
|
-
}
|
|
7
|
-
}
|
|
8
|
-
return to.concat(ar || Array.prototype.slice.call(from));
|
|
9
|
-
};
|
|
10
1
|
import { symbols } from './symbols';
|
|
2
|
+
// This class is a workaround for a problem that occurs when you
|
|
3
|
+
// try to store information using facet.constructor[symbols.admin] = ...
|
|
4
|
+
// For some reason, when you inherit from a Facet class then the original
|
|
5
|
+
// class and the enherited class share the same facet.constructor[symbols.admin]
|
|
6
|
+
// instance.
|
|
7
|
+
const facetClassAdminMap = new WeakMap();
|
|
11
8
|
export function getOrCreate(obj, key, fn) {
|
|
12
9
|
if (!obj[key]) {
|
|
13
10
|
obj[key] = fn();
|
|
14
11
|
}
|
|
15
12
|
return obj[key];
|
|
16
13
|
}
|
|
17
|
-
export
|
|
18
|
-
|
|
19
|
-
for (var _i = 1; _i < arguments.length; _i++) {
|
|
20
|
-
arrs[_i - 1] = arguments[_i];
|
|
21
|
-
}
|
|
22
|
-
return arr.map(function (val, i) {
|
|
23
|
-
return arrs.reduce(function (a, arr) { return __spreadArray(__spreadArray([], a, true), [arr[i]], false); }, [val]);
|
|
24
|
-
});
|
|
14
|
+
export const zip = (arr, ...arrs) => {
|
|
15
|
+
return arr.map((val, i) => arrs.reduce((a, arr) => [...a, arr[i]], [val]));
|
|
25
16
|
};
|
|
26
17
|
export function getCtrAdmin(ctr) {
|
|
27
|
-
return getOrCreate(ctr, symbols.admin,
|
|
18
|
+
return getOrCreate(ctr, symbols.admin, () => ({}));
|
|
28
19
|
}
|
|
29
20
|
export function getFacetClassAdmin(facetClass) {
|
|
30
|
-
|
|
21
|
+
if (!!facetClass.skandhaSymbol) {
|
|
22
|
+
return getOrCreate(facetClassAdminMap, facetClass.skandhaSymbol, () => {
|
|
23
|
+
// This function returns the initial value for the facetClassAdminMap.
|
|
24
|
+
// If the facetClass has a superClass, then we return a copy of the
|
|
25
|
+
// facetClassAdmin of the superClass.
|
|
26
|
+
const superClass = Object.getPrototypeOf(facetClass);
|
|
27
|
+
if (superClass !== Function.prototype && superClass !== null) {
|
|
28
|
+
return deepCopy(getFacetClassAdmin(superClass));
|
|
29
|
+
}
|
|
30
|
+
return {};
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
return getOrCreate(facetClass, symbols.admin, () => ({}));
|
|
31
34
|
}
|
|
32
35
|
export function getFacetAdmin(facet) {
|
|
33
|
-
return getOrCreate(facet, symbols.admin,
|
|
36
|
+
return getOrCreate(facet, symbols.admin, () => ({}));
|
|
37
|
+
}
|
|
38
|
+
function deepCopy(obj) {
|
|
39
|
+
var copy;
|
|
40
|
+
// Handle the 3 simple types, and null or undefined
|
|
41
|
+
if (null == obj || 'object' != typeof obj)
|
|
42
|
+
return obj;
|
|
43
|
+
// Handle Date
|
|
44
|
+
if (obj instanceof Date) {
|
|
45
|
+
copy = new Date();
|
|
46
|
+
copy.setTime(obj.getTime());
|
|
47
|
+
return copy;
|
|
48
|
+
}
|
|
49
|
+
// Handle Array
|
|
50
|
+
if (obj instanceof Array) {
|
|
51
|
+
copy = [];
|
|
52
|
+
for (var i = 0, len = obj.length; i < len; i++) {
|
|
53
|
+
copy[i] = deepCopy(obj[i]);
|
|
54
|
+
}
|
|
55
|
+
return copy;
|
|
56
|
+
}
|
|
57
|
+
// Handle Object
|
|
58
|
+
if (obj instanceof Object) {
|
|
59
|
+
copy = {};
|
|
60
|
+
for (var attr in obj) {
|
|
61
|
+
if (obj.hasOwnProperty(attr))
|
|
62
|
+
copy[attr] = deepCopy(obj[attr]);
|
|
63
|
+
}
|
|
64
|
+
return copy;
|
|
65
|
+
}
|
|
66
|
+
throw new Error("Unable to copy obj! Its type isn't supported.");
|
|
34
67
|
}
|
package/lib/cleanUp.d.ts
ADDED
package/lib/cleanUp.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { getCtrAdmin } from '../internal/utils';
|
|
2
|
+
export function addCleanUpFunctionToCtr(ctr, f) {
|
|
3
|
+
var _a;
|
|
4
|
+
const ctrAdmin = getCtrAdmin(ctr);
|
|
5
|
+
ctrAdmin.cleanUpFunctions = (_a = ctrAdmin.cleanUpFunctions) !== null && _a !== void 0 ? _a : [];
|
|
6
|
+
ctrAdmin.cleanUpFunctions.push(f);
|
|
7
|
+
}
|
|
8
|
+
export function cleanUpCtr(ctr) {
|
|
9
|
+
var _a;
|
|
10
|
+
const ctrAdmin = getCtrAdmin(ctr);
|
|
11
|
+
ctrAdmin.cleanUpFunctions = (_a = ctrAdmin.cleanUpFunctions) !== null && _a !== void 0 ? _a : [];
|
|
12
|
+
ctrAdmin.cleanUpFunctions.forEach((x) => x());
|
|
13
|
+
ctrAdmin.cleanUpFunctions = [];
|
|
14
|
+
}
|
package/lib/data.js
CHANGED
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
import { getFacetClassAdmin } from '../internal/utils';
|
|
2
|
-
export function log(dataHost, dataMember, descriptor) {
|
|
2
|
+
export function log(dataHost, dataMember, descriptor = undefined) {
|
|
3
3
|
var _a;
|
|
4
|
-
|
|
5
|
-
var facetClassAdmin = getFacetClassAdmin(dataHost.constructor);
|
|
4
|
+
const facetClassAdmin = getFacetClassAdmin(dataHost.constructor);
|
|
6
5
|
facetClassAdmin.loggedMembers = (_a = facetClassAdmin.loggedMembers) !== null && _a !== void 0 ? _a : {};
|
|
7
6
|
facetClassAdmin.loggedMembers[dataMember] = true;
|
|
8
7
|
return descriptor;
|
|
9
8
|
}
|
|
10
|
-
export function data(dataHost, dataMember, descriptor) {
|
|
9
|
+
export function data(dataHost, dataMember, descriptor = undefined) {
|
|
11
10
|
var _a;
|
|
12
|
-
|
|
13
|
-
var facetClassAdmin = getFacetClassAdmin(dataHost.constructor);
|
|
11
|
+
const facetClassAdmin = getFacetClassAdmin(dataHost.constructor);
|
|
14
12
|
facetClassAdmin.dataMembers = (_a = facetClassAdmin.dataMembers) !== null && _a !== void 0 ? _a : {};
|
|
15
13
|
facetClassAdmin.dataMembers[dataMember] = true;
|
|
16
14
|
return log(dataHost, dataMember, descriptor);
|
|
@@ -23,5 +21,5 @@ export function getDataMemberNames(facet) {
|
|
|
23
21
|
var _a;
|
|
24
22
|
return Object.keys((_a = getFacetClassAdmin(facet.constructor).dataMembers) !== null && _a !== void 0 ? _a : {});
|
|
25
23
|
}
|
|
26
|
-
export
|
|
27
|
-
export
|
|
24
|
+
export const input = data;
|
|
25
|
+
export const output = data;
|
package/lib/facet.js
CHANGED
|
@@ -5,13 +5,13 @@ export function facetClassName(facetClass) {
|
|
|
5
5
|
export function getf(facetClass, ctr) {
|
|
6
6
|
var _a;
|
|
7
7
|
if (!ctr)
|
|
8
|
-
return
|
|
9
|
-
|
|
8
|
+
return (ctr) => getf(facetClass, ctr);
|
|
9
|
+
const facet = typeof facetClass === 'string'
|
|
10
10
|
? ctr[facetClass]
|
|
11
11
|
: (_a = getCtrAdmin(ctr).facetByFacetClassName) === null || _a === void 0 ? void 0 : _a[facetClassName(facetClass)];
|
|
12
12
|
if (!facet) {
|
|
13
|
-
|
|
14
|
-
console.error(
|
|
13
|
+
const facetName = facetClass === 'string' ? facetClass : facetClassName(facetClass);
|
|
14
|
+
console.error(`No facet ${facetName} in container ${getCtrAdmin(ctr).logName}`);
|
|
15
15
|
}
|
|
16
16
|
return facet;
|
|
17
17
|
}
|
package/lib/install.js
CHANGED
package/lib/operation.js
CHANGED
|
@@ -7,78 +7,35 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
7
7
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
|
-
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
11
|
-
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
12
|
-
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
13
|
-
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
14
|
-
function step(op) {
|
|
15
|
-
if (f) throw new TypeError("Generator is already executing.");
|
|
16
|
-
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
17
|
-
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
18
|
-
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
19
|
-
switch (op[0]) {
|
|
20
|
-
case 0: case 1: t = op; break;
|
|
21
|
-
case 4: _.label++; return { value: op[1], done: false };
|
|
22
|
-
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
23
|
-
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
24
|
-
default:
|
|
25
|
-
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
26
|
-
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
27
|
-
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
28
|
-
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
29
|
-
if (t[2]) _.ops.pop();
|
|
30
|
-
_.trys.pop(); continue;
|
|
31
|
-
}
|
|
32
|
-
op = body.call(thisArg, _);
|
|
33
|
-
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
34
|
-
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
35
|
-
}
|
|
36
|
-
};
|
|
37
10
|
import { log } from '../internal/logging';
|
|
38
11
|
import { options } from '../internal/options';
|
|
39
12
|
import { getFacetClassAdmin } from '../internal/utils';
|
|
40
13
|
function _operation(target, propertyName, descriptor, punctualOptions) {
|
|
41
14
|
var _a;
|
|
42
|
-
|
|
43
|
-
|
|
15
|
+
const f = descriptor.value;
|
|
16
|
+
const facetClassAdmin = getFacetClassAdmin(target.constructor);
|
|
44
17
|
facetClassAdmin.operationMemberNames =
|
|
45
18
|
(_a = facetClassAdmin.operationMemberNames) !== null && _a !== void 0 ? _a : [];
|
|
46
19
|
facetClassAdmin.operationMemberNames.push(propertyName);
|
|
47
20
|
if (typeof descriptor.value === 'function') {
|
|
48
21
|
descriptor.value = !!punctualOptions.async
|
|
49
|
-
? function () {
|
|
22
|
+
? function (...args) {
|
|
50
23
|
var _a;
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
return
|
|
58
|
-
switch (_b.label) {
|
|
59
|
-
case 0:
|
|
60
|
-
facet = this;
|
|
61
|
-
isLogging = options.logging && ((_a = punctualOptions.log) !== null && _a !== void 0 ? _a : true);
|
|
62
|
-
isLogging && log(facet, propertyName, args, true);
|
|
63
|
-
return [4 /*yield*/, f.bind(this).apply(void 0, args)];
|
|
64
|
-
case 1:
|
|
65
|
-
returnValue = _b.sent();
|
|
66
|
-
isLogging && log(facet, propertyName, args, false);
|
|
67
|
-
return [2 /*return*/, Promise.resolve(returnValue)];
|
|
68
|
-
}
|
|
69
|
-
});
|
|
24
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
25
|
+
const facet = this;
|
|
26
|
+
const isLogging = options.logging && ((_a = punctualOptions.log) !== null && _a !== void 0 ? _a : true);
|
|
27
|
+
isLogging && log(facet, propertyName, args, true);
|
|
28
|
+
const returnValue = yield f.bind(this)(...args);
|
|
29
|
+
isLogging && log(facet, propertyName, args, false);
|
|
30
|
+
return Promise.resolve(returnValue);
|
|
70
31
|
});
|
|
71
32
|
}
|
|
72
|
-
: function () {
|
|
33
|
+
: function (...args) {
|
|
73
34
|
var _a;
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
args[_i] = arguments[_i];
|
|
77
|
-
}
|
|
78
|
-
var facet = this;
|
|
79
|
-
var isLogging = options.logging && ((_a = punctualOptions.log) !== null && _a !== void 0 ? _a : true);
|
|
35
|
+
const facet = this;
|
|
36
|
+
const isLogging = options.logging && ((_a = punctualOptions.log) !== null && _a !== void 0 ? _a : true);
|
|
80
37
|
isLogging && log(facet, propertyName, args, true);
|
|
81
|
-
|
|
38
|
+
const returnValue = f.bind(this)(...args);
|
|
82
39
|
isLogging && log(facet, propertyName, args, false);
|
|
83
40
|
return returnValue;
|
|
84
41
|
};
|
|
@@ -89,17 +46,13 @@ export function getOperationMemberNames(facet) {
|
|
|
89
46
|
var _a;
|
|
90
47
|
return (_a = getFacetClassAdmin(facet.constructor).operationMemberNames) !== null && _a !== void 0 ? _a : [];
|
|
91
48
|
}
|
|
92
|
-
export function operation() {
|
|
93
|
-
var args = [];
|
|
94
|
-
for (var _i = 0; _i < arguments.length; _i++) {
|
|
95
|
-
args[_i] = arguments[_i];
|
|
96
|
-
}
|
|
49
|
+
export function operation(...args) {
|
|
97
50
|
if (args.length === 1) {
|
|
98
|
-
|
|
51
|
+
const wrapped = (target, propertyName, descriptor) => {
|
|
99
52
|
return _operation(target, propertyName, descriptor, args[0]);
|
|
100
53
|
};
|
|
101
54
|
return wrapped;
|
|
102
55
|
}
|
|
103
|
-
|
|
56
|
+
const [target, propertyName, descriptor] = args;
|
|
104
57
|
return _operation(target, propertyName, descriptor, {});
|
|
105
58
|
}
|
package/lib/patch.js
CHANGED
|
@@ -7,14 +7,14 @@ export function mapDataToProp(facet, prop, getter) {
|
|
|
7
7
|
});
|
|
8
8
|
}
|
|
9
9
|
export function mapDataToProps(target, mappings) {
|
|
10
|
-
for (
|
|
10
|
+
for (const key in mappings) {
|
|
11
11
|
if (mappings.hasOwnProperty(key)) {
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
const facet = target[key];
|
|
13
|
+
const propMappings = mappings[key];
|
|
14
14
|
if (facet && propMappings) {
|
|
15
|
-
for (
|
|
15
|
+
for (const prop in propMappings) {
|
|
16
16
|
if (propMappings.hasOwnProperty(prop)) {
|
|
17
|
-
|
|
17
|
+
const getter = propMappings[prop];
|
|
18
18
|
if (getter) {
|
|
19
19
|
mapDataToProp(facet, prop, getter);
|
|
20
20
|
}
|
package/lib/registerCtr.d.ts
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
export type OptionsT = {
|
|
2
2
|
name: string;
|
|
3
3
|
facets?: string[];
|
|
4
|
-
|
|
4
|
+
getCtrState?: Function;
|
|
5
5
|
};
|
|
6
|
-
export
|
|
6
|
+
export type ArgsT = {
|
|
7
|
+
ctr: any;
|
|
8
|
+
options: OptionsT;
|
|
9
|
+
initCtr?: Function;
|
|
10
|
+
};
|
|
11
|
+
export declare function registerCtr({ ctr, options, initCtr }: ArgsT): void;
|
package/lib/registerCtr.js
CHANGED
|
@@ -3,29 +3,32 @@ import { options as skandhaOptions } from '../internal/options';
|
|
|
3
3
|
import { getCtrAdmin, getFacetAdmin } from '../internal/utils';
|
|
4
4
|
import { setCtr } from './ctr';
|
|
5
5
|
import { facetClassName } from './facet';
|
|
6
|
-
export function registerCtr(ctr, options) {
|
|
6
|
+
export function registerCtr({ ctr, options, initCtr }) {
|
|
7
7
|
var _a, _b, _c;
|
|
8
|
-
|
|
8
|
+
const ctrAdmin = getCtrAdmin(ctr);
|
|
9
9
|
ctrAdmin.logName = options.name;
|
|
10
10
|
ctrAdmin.facetMembers = (_a = ctrAdmin.facetMembers) !== null && _a !== void 0 ? _a : [];
|
|
11
11
|
ctrAdmin.facetByFacetClassName = (_b = ctrAdmin.facetByFacetClassName) !== null && _b !== void 0 ? _b : {};
|
|
12
|
-
ctrAdmin.
|
|
13
|
-
((_c = options.facets) !== null && _c !== void 0 ? _c : Object.getOwnPropertyNames(ctr)).forEach(
|
|
14
|
-
|
|
12
|
+
ctrAdmin.getCtrState = options.getCtrState;
|
|
13
|
+
((_c = options.facets) !== null && _c !== void 0 ? _c : Object.getOwnPropertyNames(ctr)).forEach((facetName) => {
|
|
14
|
+
const facet = ctr[facetName];
|
|
15
15
|
setCtr(facet, ctr);
|
|
16
16
|
ctrAdmin.facetMembers.push(facetName);
|
|
17
17
|
if (!facet.constructor.className) {
|
|
18
|
-
console.error(
|
|
19
|
-
|
|
18
|
+
console.error(`Facet ${facetName} of container ${ctrAdmin.logName} ` +
|
|
19
|
+
`does not have a static className function`);
|
|
20
20
|
}
|
|
21
|
-
|
|
22
|
-
getFacetAdmin(facet).logName =
|
|
21
|
+
const className = facetClassName(facet.constructor);
|
|
22
|
+
getFacetAdmin(facet).logName = `${ctrAdmin.logName}/${className}`;
|
|
23
23
|
if (ctrAdmin.facetByFacetClassName[className] !== undefined) {
|
|
24
|
-
console.error(
|
|
24
|
+
console.error(`Two facets of same type ${className} in container ${ctrAdmin.logName}`);
|
|
25
25
|
}
|
|
26
26
|
ctrAdmin.facetByFacetClassName[className] = facet;
|
|
27
27
|
});
|
|
28
28
|
if (skandhaOptions.logging) {
|
|
29
29
|
console.log('%c %s ctr initialized', 'color: gray', ctrAdmin.logName, getCtrState(ctr));
|
|
30
30
|
}
|
|
31
|
+
if (initCtr) {
|
|
32
|
+
initCtr(ctr);
|
|
33
|
+
}
|
|
31
34
|
}
|