@journeyapps-labs/lib-reactor-data-layer 1.0.0
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 +201 -0
- package/dist/@types/Collection.d.ts +17 -0
- package/dist/@types/LifecycleCollection.d.ts +26 -0
- package/dist/@types/PaginatedCollection.d.ts +38 -0
- package/dist/@types/index.d.ts +3 -0
- package/dist/Collection.js +89 -0
- package/dist/Collection.js.map +1 -0
- package/dist/LifecycleCollection.js +84 -0
- package/dist/LifecycleCollection.js.map +1 -0
- package/dist/PaginatedCollection.js +130 -0
- package/dist/PaginatedCollection.js.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/package.json +29 -0
- package/src/Collection.ts +68 -0
- package/src/LifecycleCollection.ts +85 -0
- package/src/PaginatedCollection.ts +150 -0
- package/src/index.ts +3 -0
- package/tests/collections.test.ts +92 -0
- package/tests/lifecycle.test.ts +114 -0
- package/tsconfig.json +21 -0
- package/tsconfig.tsbuildinfo +1 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { IObservableArray, observable } from 'mobx';
|
|
2
|
+
import { BaseObserver } from '@journeyapps-labs/lib-reactor-utils';
|
|
3
|
+
|
|
4
|
+
export interface CollectionListener {
|
|
5
|
+
cleared: () => any;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface LoadEvent {
|
|
9
|
+
aborted: boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export class Collection<T> extends BaseObserver<CollectionListener> {
|
|
13
|
+
@observable
|
|
14
|
+
accessor items: IObservableArray<T>;
|
|
15
|
+
|
|
16
|
+
@observable
|
|
17
|
+
accessor loading: boolean;
|
|
18
|
+
|
|
19
|
+
@observable
|
|
20
|
+
accessor failed: boolean;
|
|
21
|
+
|
|
22
|
+
protected promise: Promise<T[]>;
|
|
23
|
+
|
|
24
|
+
constructor() {
|
|
25
|
+
super();
|
|
26
|
+
this.items = [] as IObservableArray<T>;
|
|
27
|
+
this.loading = false;
|
|
28
|
+
this.failed = false;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
clear() {
|
|
32
|
+
this.loading = false;
|
|
33
|
+
this.failed = false;
|
|
34
|
+
this.items.clear();
|
|
35
|
+
this.iterateListeners((cb) => cb.cleared?.());
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async load(loader: (event: LoadEvent) => Promise<T[]>) {
|
|
39
|
+
if (this.promise) {
|
|
40
|
+
return this.promise;
|
|
41
|
+
}
|
|
42
|
+
const loadEvent: LoadEvent = {
|
|
43
|
+
aborted: false
|
|
44
|
+
};
|
|
45
|
+
this.loading = true;
|
|
46
|
+
this.promise = loader(loadEvent);
|
|
47
|
+
const l = this.registerListener({
|
|
48
|
+
cleared: () => {
|
|
49
|
+
loadEvent.aborted = true;
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
try {
|
|
53
|
+
const items = await this.promise;
|
|
54
|
+
if (!items || loadEvent.aborted) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
this.items.replace(items);
|
|
58
|
+
} catch (ex) {
|
|
59
|
+
this.failed = true;
|
|
60
|
+
throw ex;
|
|
61
|
+
} finally {
|
|
62
|
+
l();
|
|
63
|
+
this.loading = false;
|
|
64
|
+
this.promise = null;
|
|
65
|
+
}
|
|
66
|
+
return this.items;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { Collection } from './Collection';
|
|
2
|
+
import { autorun, computed, IReactionDisposer, makeObservable, observable } from 'mobx';
|
|
3
|
+
import * as _ from 'lodash';
|
|
4
|
+
|
|
5
|
+
export interface LifecycleModel<Serialized> {
|
|
6
|
+
key: string;
|
|
7
|
+
|
|
8
|
+
dispose();
|
|
9
|
+
|
|
10
|
+
patch(data: Serialized);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface LifecycleCollectionOptions<Serialized, Model extends LifecycleModel<Serialized>> {
|
|
14
|
+
collection: Collection<Serialized>;
|
|
15
|
+
generateModel: (d: Serialized) => Model;
|
|
16
|
+
getKeyForSerialized: (s: Serialized) => string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Wraps an existing collection with data-to-model conversion, and is smart about creating / patching and deleting
|
|
21
|
+
* models based on how the wrapping collection loads items
|
|
22
|
+
*/
|
|
23
|
+
export class LifecycleCollection<Serialized, Model extends LifecycleModel<Serialized>> {
|
|
24
|
+
@observable
|
|
25
|
+
accessor _items: Map<string, Model>;
|
|
26
|
+
|
|
27
|
+
reaction: IReactionDisposer;
|
|
28
|
+
|
|
29
|
+
constructor(protected options: LifecycleCollectionOptions<Serialized, Model>) {
|
|
30
|
+
this._items = new Map();
|
|
31
|
+
this.reaction = autorun(
|
|
32
|
+
() => {
|
|
33
|
+
const items = options.collection.items.reduce((prev, cur) => {
|
|
34
|
+
const key = options.getKeyForSerialized(cur);
|
|
35
|
+
if (!key) {
|
|
36
|
+
console.error(`Missing key for lifecycle item, check that the 'get key()' accessor is implemented.`);
|
|
37
|
+
return prev;
|
|
38
|
+
}
|
|
39
|
+
prev[options.getKeyForSerialized(cur)] = cur;
|
|
40
|
+
return prev;
|
|
41
|
+
}, {});
|
|
42
|
+
const incomingKeys = Object.keys(items);
|
|
43
|
+
let existingKeys = Array.from(this._items.keys());
|
|
44
|
+
|
|
45
|
+
_.difference(existingKeys, incomingKeys).forEach((m) => {
|
|
46
|
+
if (this._items.has(m)) {
|
|
47
|
+
this._items.get(m).dispose();
|
|
48
|
+
this._items.delete(m);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
_.intersection(incomingKeys, existingKeys).forEach((key: string) => {
|
|
53
|
+
this._items.get(key)?.patch(items[key]);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
_.difference(incomingKeys, existingKeys).map((k) => {
|
|
57
|
+
const model = options.generateModel(items[k]);
|
|
58
|
+
this._items.set(model.key, model);
|
|
59
|
+
});
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
name: 'LifecycleCollection'
|
|
63
|
+
}
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
get collection() {
|
|
68
|
+
return this.options.collection;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
@computed get items(): Model[] {
|
|
72
|
+
return Array.from(this._items.values());
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
@computed get loading() {
|
|
76
|
+
return this.options.collection.loading;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
dispose() {
|
|
80
|
+
this.reaction();
|
|
81
|
+
this.items.forEach((i) => {
|
|
82
|
+
i.dispose();
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { autorun, IReactionDisposer, observable } from 'mobx';
|
|
2
|
+
import { Collection } from './Collection';
|
|
3
|
+
import { SearchResult, SearchResultEntry } from '@journeyapps-labs/lib-reactor-search';
|
|
4
|
+
|
|
5
|
+
export interface PaginatedCollectionOptions<T, R> {
|
|
6
|
+
transformer: (res: R) => T[];
|
|
7
|
+
loaderIterator?: () => Promise<AsyncIterator<R>> | AsyncIterator<R>;
|
|
8
|
+
hasMore?: (res: R) => boolean;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface AsSearchResultOptions<T> {
|
|
12
|
+
idTransformer: (item: T) => string;
|
|
13
|
+
match: (item: T) => boolean;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface PaginatedSearchResultEntry<T = any> extends SearchResultEntry {
|
|
17
|
+
item: T;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export class PaginatedSearchResult<T extends any> extends SearchResult<PaginatedSearchResultEntry<T>> {
|
|
21
|
+
observer: PaginatedCollection;
|
|
22
|
+
disposer: IReactionDisposer;
|
|
23
|
+
|
|
24
|
+
constructor(observer: PaginatedCollection) {
|
|
25
|
+
super();
|
|
26
|
+
this.observer = observer;
|
|
27
|
+
this.disposer = autorun(() => {
|
|
28
|
+
this.loading = observer ? observer.loading : false;
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
dispose() {
|
|
33
|
+
super.dispose();
|
|
34
|
+
this.disposer?.();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
hasMore() {
|
|
38
|
+
if (!this.observer) {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
return this.observer.hasMore;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
loadMore() {
|
|
45
|
+
if (!this.observer) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
return this.observer.loadMore();
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export class PaginatedCollection<T = any, R = any> extends Collection<T> {
|
|
53
|
+
@observable
|
|
54
|
+
protected accessor lastResponse: R;
|
|
55
|
+
|
|
56
|
+
protected iterator: AsyncIterator<R>;
|
|
57
|
+
protected options: PaginatedCollectionOptions<T, R>;
|
|
58
|
+
|
|
59
|
+
public hasMore: boolean;
|
|
60
|
+
|
|
61
|
+
constructor(options: PaginatedCollectionOptions<T, R>) {
|
|
62
|
+
super();
|
|
63
|
+
this.lastResponse = null;
|
|
64
|
+
this.iterator = null;
|
|
65
|
+
this.options = options;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
getData(res: R) {
|
|
69
|
+
return this.options.transformer(res);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async loadAll(
|
|
73
|
+
load?: () => Promise<AsyncIterator<R>> | AsyncIterator<R>,
|
|
74
|
+
options?: { abort: AbortController }
|
|
75
|
+
): Promise<any> {
|
|
76
|
+
const loader = load ?? this.options.loaderIterator;
|
|
77
|
+
this.hasMore = await this.loadInitialData(loader);
|
|
78
|
+
while (!options?.abort.signal.aborted && this.hasMore) {
|
|
79
|
+
this.hasMore = await this.loadMore();
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
clear() {
|
|
84
|
+
this.lastResponse = null;
|
|
85
|
+
this.hasMore = false;
|
|
86
|
+
this.iterator = null;
|
|
87
|
+
super.clear();
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async loadMore(): Promise<boolean> {
|
|
91
|
+
if (!this.iterator) {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
this.hasMore = false;
|
|
95
|
+
await this.load(async (event) => {
|
|
96
|
+
const res = await this.iterator.next();
|
|
97
|
+
if (event.aborted || res.done || !res.value) {
|
|
98
|
+
return this.items;
|
|
99
|
+
}
|
|
100
|
+
this.hasMore = res.value.more;
|
|
101
|
+
this.lastResponse = res.value;
|
|
102
|
+
return this.items.concat(this.getData(res.value));
|
|
103
|
+
});
|
|
104
|
+
return this.hasMore;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async loadInitialData(load?: () => Promise<AsyncIterator<R>> | AsyncIterator<R>): Promise<boolean> {
|
|
108
|
+
const loader = load ?? this.options.loaderIterator;
|
|
109
|
+
|
|
110
|
+
await this.load(async (event) => {
|
|
111
|
+
this.iterator = await loader();
|
|
112
|
+
const next = await this.iterator.next();
|
|
113
|
+
if (event.aborted || next.done || !next.value) {
|
|
114
|
+
return [];
|
|
115
|
+
}
|
|
116
|
+
this.lastResponse = next.value;
|
|
117
|
+
this.hasMore = this.options.hasMore(this.lastResponse);
|
|
118
|
+
return this.getData(this.lastResponse);
|
|
119
|
+
});
|
|
120
|
+
return this.hasMore;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
asSearchResult(options: AsSearchResultOptions<T>): PaginatedSearchResult<T> {
|
|
124
|
+
const result = new PaginatedSearchResult<T>(this);
|
|
125
|
+
const runner = autorun(() => {
|
|
126
|
+
if (!this.lastResponse) {
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
result.setValues(
|
|
130
|
+
this.items
|
|
131
|
+
.filter((d) => options.match(d))
|
|
132
|
+
.map((d) => {
|
|
133
|
+
return {
|
|
134
|
+
item: d,
|
|
135
|
+
key: options.idTransformer(d)
|
|
136
|
+
};
|
|
137
|
+
})
|
|
138
|
+
);
|
|
139
|
+
});
|
|
140
|
+
result.registerListener({
|
|
141
|
+
dispose: () => {
|
|
142
|
+
runner();
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
// also load the entries
|
|
147
|
+
this.loadMore();
|
|
148
|
+
return result;
|
|
149
|
+
}
|
|
150
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { expect, it } from 'vitest';
|
|
2
|
+
import { Collection } from '../src/Collection';
|
|
3
|
+
import { configure } from 'mobx';
|
|
4
|
+
|
|
5
|
+
configure({
|
|
6
|
+
enforceActions: 'never'
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
const sleep = (s: number) =>
|
|
10
|
+
new Promise<void>((resolve) => {
|
|
11
|
+
setTimeout(resolve, s);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('Should load and clear collections', async () => {
|
|
15
|
+
let collection = new Collection<string>();
|
|
16
|
+
|
|
17
|
+
expect(collection.loading).toEqual(false);
|
|
18
|
+
expect(collection.failed).toEqual(false);
|
|
19
|
+
expect(collection.items).toEqual([]);
|
|
20
|
+
|
|
21
|
+
collection.load(async (event) => {
|
|
22
|
+
await sleep(200);
|
|
23
|
+
expect(event.aborted).toEqual(false);
|
|
24
|
+
return ['test1'];
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
expect(collection.loading).toEqual(true);
|
|
28
|
+
expect(collection.failed).toEqual(false);
|
|
29
|
+
expect(collection.items).toEqual([]);
|
|
30
|
+
|
|
31
|
+
await sleep(500);
|
|
32
|
+
expect(collection.loading).toEqual(false);
|
|
33
|
+
expect(collection.failed).toEqual(false);
|
|
34
|
+
expect(collection.items).toEqual(['test1']);
|
|
35
|
+
|
|
36
|
+
collection.clear();
|
|
37
|
+
expect(collection.loading).toEqual(false);
|
|
38
|
+
expect(collection.failed).toEqual(false);
|
|
39
|
+
expect(collection.items).toEqual([]);
|
|
40
|
+
|
|
41
|
+
expect(collection._listenerSize).toEqual(0);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('Should abort collections', async () => {
|
|
45
|
+
let collection = new Collection<string>();
|
|
46
|
+
|
|
47
|
+
collection.load(async (event) => {
|
|
48
|
+
await sleep(200);
|
|
49
|
+
expect(event.aborted).toEqual(true);
|
|
50
|
+
return ['test1'];
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
expect(collection.loading).toEqual(true);
|
|
54
|
+
expect(collection.failed).toEqual(false);
|
|
55
|
+
expect(collection.items).toEqual([]);
|
|
56
|
+
|
|
57
|
+
collection.clear();
|
|
58
|
+
await sleep(500);
|
|
59
|
+
expect(collection.loading).toEqual(false);
|
|
60
|
+
expect(collection.failed).toEqual(false);
|
|
61
|
+
expect(collection.items).toEqual([]);
|
|
62
|
+
|
|
63
|
+
expect(collection._listenerSize).toEqual(0);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('Should rethrow errors when loading data after updating state', async () => {
|
|
67
|
+
let collection = new Collection<string>();
|
|
68
|
+
let ex: Error;
|
|
69
|
+
|
|
70
|
+
collection
|
|
71
|
+
.load(async (event) => {
|
|
72
|
+
await sleep(200);
|
|
73
|
+
throw new Error('test');
|
|
74
|
+
})
|
|
75
|
+
.catch((_ex) => {
|
|
76
|
+
ex = _ex;
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
expect(collection.loading).toEqual(true);
|
|
80
|
+
expect(collection.failed).toEqual(false);
|
|
81
|
+
expect(collection.items).toEqual([]);
|
|
82
|
+
|
|
83
|
+
collection.clear();
|
|
84
|
+
await sleep(500);
|
|
85
|
+
expect(collection.loading).toEqual(false);
|
|
86
|
+
expect(collection.failed).toEqual(true);
|
|
87
|
+
expect(collection.items).toEqual([]);
|
|
88
|
+
|
|
89
|
+
expect(collection._listenerSize).toEqual(0);
|
|
90
|
+
expect(ex).toBeTruthy();
|
|
91
|
+
expect(ex.message).toEqual('test');
|
|
92
|
+
});
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { expect, it } from 'vitest';
|
|
2
|
+
import { Collection } from '../src/Collection';
|
|
3
|
+
import { LifecycleCollection, LifecycleModel } from '../src';
|
|
4
|
+
import { autorun, configure, observable } from 'mobx';
|
|
5
|
+
|
|
6
|
+
configure({
|
|
7
|
+
enforceActions: 'never'
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
export interface DummySerialized {
|
|
11
|
+
id: string;
|
|
12
|
+
label: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
class DummyModel implements LifecycleModel<DummySerialized> {
|
|
16
|
+
@observable
|
|
17
|
+
accessor ser: DummySerialized;
|
|
18
|
+
|
|
19
|
+
disposed: boolean;
|
|
20
|
+
|
|
21
|
+
constructor(ser: DummySerialized) {
|
|
22
|
+
this.ser = ser;
|
|
23
|
+
this.disposed = false;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
get key() {
|
|
27
|
+
return this.ser.id;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
dispose() {
|
|
31
|
+
this.disposed = true;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
patch(data: DummySerialized) {
|
|
35
|
+
this.ser = data;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
it('Should correctly manage lifecycle', async () => {
|
|
40
|
+
let generated: DummyModel[] = [];
|
|
41
|
+
|
|
42
|
+
let collection = new Collection<DummySerialized>();
|
|
43
|
+
let lifecycle_collection = new LifecycleCollection<DummySerialized, DummyModel>({
|
|
44
|
+
collection: collection,
|
|
45
|
+
generateModel: (ser: DummySerialized) => {
|
|
46
|
+
const c = new DummyModel(ser);
|
|
47
|
+
generated.push(c);
|
|
48
|
+
return c;
|
|
49
|
+
},
|
|
50
|
+
getKeyForSerialized: (ser) => {
|
|
51
|
+
return ser.id;
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
expect(collection.items.length).toEqual(0);
|
|
56
|
+
expect(lifecycle_collection.items.length).toEqual(0);
|
|
57
|
+
|
|
58
|
+
let items = [];
|
|
59
|
+
autorun(() => {
|
|
60
|
+
items = [...lifecycle_collection.items];
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
await collection.load(async () => {
|
|
64
|
+
return [
|
|
65
|
+
{
|
|
66
|
+
id: '1',
|
|
67
|
+
label: 'a'
|
|
68
|
+
}
|
|
69
|
+
];
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
expect(collection.items.length).toEqual(1);
|
|
73
|
+
expect(lifecycle_collection.items.length).toEqual(1);
|
|
74
|
+
expect(items.length).toEqual(1);
|
|
75
|
+
expect(lifecycle_collection.items[0].disposed).toEqual(false);
|
|
76
|
+
expect(generated.length).toEqual(1);
|
|
77
|
+
|
|
78
|
+
// test patching
|
|
79
|
+
await collection.load(async () => {
|
|
80
|
+
return [
|
|
81
|
+
{
|
|
82
|
+
id: '1',
|
|
83
|
+
label: 'b'
|
|
84
|
+
}
|
|
85
|
+
];
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
expect(collection.items.length).toEqual(1);
|
|
89
|
+
expect(lifecycle_collection.items.length).toEqual(1);
|
|
90
|
+
expect(items.length).toEqual(1);
|
|
91
|
+
expect(items[0].ser.label).toEqual('b');
|
|
92
|
+
expect(lifecycle_collection.items[0].ser.label).toEqual('b');
|
|
93
|
+
expect(lifecycle_collection.items[0].disposed).toEqual(false);
|
|
94
|
+
expect(generated.length).toEqual(1);
|
|
95
|
+
|
|
96
|
+
// create new item, old item should be disposed
|
|
97
|
+
await collection.load(async () => {
|
|
98
|
+
return [
|
|
99
|
+
{
|
|
100
|
+
id: '2',
|
|
101
|
+
label: 'c'
|
|
102
|
+
}
|
|
103
|
+
];
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
expect(collection.items.length).toEqual(1);
|
|
107
|
+
expect(lifecycle_collection.items.length).toEqual(1);
|
|
108
|
+
expect(items.length).toEqual(1);
|
|
109
|
+
expect(lifecycle_collection.items[0].ser.label).toEqual('c');
|
|
110
|
+
expect(items[0].ser.label).toEqual('c');
|
|
111
|
+
expect(lifecycle_collection.items[0].disposed).toEqual(false);
|
|
112
|
+
expect(generated[0].disposed).toEqual(true);
|
|
113
|
+
expect(generated.length).toEqual(2);
|
|
114
|
+
});
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../../tsconfig.base.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"declarationDir": "dist/@types",
|
|
5
|
+
"rootDir": "src",
|
|
6
|
+
"outDir": "dist"
|
|
7
|
+
},
|
|
8
|
+
"include": [
|
|
9
|
+
"src/**/*.ts",
|
|
10
|
+
"src/**/*.tsx",
|
|
11
|
+
"src/**/*.json"
|
|
12
|
+
],
|
|
13
|
+
"references": [
|
|
14
|
+
{
|
|
15
|
+
"path": "../lib-reactor-search"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"path": "../lib-reactor-utils"
|
|
19
|
+
}
|
|
20
|
+
]
|
|
21
|
+
}
|