@wszerad/items 0.1.2 → 0.3.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/README.md +377 -547
- package/dist/index.d.mts +78 -55
- package/dist/index.mjs +159 -112
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -1,30 +1,37 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
//#region src/diff.d.ts
|
|
8
|
-
interface ItemDiff<I> {
|
|
9
|
-
id: I;
|
|
10
|
-
changes: ReturnType<typeof diff>;
|
|
1
|
+
//#region src/select.d.ts
|
|
2
|
+
declare class BaseSelect<E, SE> {
|
|
3
|
+
items: E[];
|
|
4
|
+
context: Map<SE, E>;
|
|
5
|
+
constructor(items: E[], context?: Map<SE, E>);
|
|
6
|
+
private cleanContext;
|
|
11
7
|
}
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
8
|
+
declare class SingleSelect<E, SE> extends BaseSelect<E, SE> {}
|
|
9
|
+
declare class Select<E, SE> extends BaseSelect<E, SE> {
|
|
10
|
+
take(len: number): Select<E, SE>;
|
|
11
|
+
skip(len: number): Select<E, SE>;
|
|
12
|
+
filter(testFn: TestFn<E>): Select<E, SE>;
|
|
13
|
+
revert(): Select<E, SE>;
|
|
14
|
+
sort(sortFn: (x: E, y: E) => number): Select<E, SE>;
|
|
15
|
+
at(index: number): SingleSelect<E | undefined, SE>;
|
|
16
|
+
find(testFn: TestFn<E>): SingleSelect<E | undefined, SE>;
|
|
17
|
+
from<T>(entities: Iterable<E>): Select<E, SE>;
|
|
18
|
+
from<T>(entities: Iterable<T>, matcher: MatchFn<E, T>): Select<E | undefined, T>;
|
|
19
|
+
on<T>(entry: E): SingleSelect<E, SE>;
|
|
20
|
+
on<T>(entry: T, matcher: MatchFn<E, T>): SingleSelect<E | undefined, T>;
|
|
16
21
|
}
|
|
17
22
|
//#endregion
|
|
18
|
-
//#region src/
|
|
19
|
-
type
|
|
20
|
-
type
|
|
21
|
-
type
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
type
|
|
25
|
-
type
|
|
26
|
-
|
|
27
|
-
|
|
23
|
+
//#region src/types.d.ts
|
|
24
|
+
type ItemId = string | number;
|
|
25
|
+
type CheckFn<E> = (entity: E) => boolean;
|
|
26
|
+
type TestFn<E> = (entry: E) => boolean;
|
|
27
|
+
type MatchFn<E, T> = (entity: T, existing: E) => boolean;
|
|
28
|
+
type SelectorSelect<E, EE, SE> = (selector: Select<E, any>) => Select<EE, SE>;
|
|
29
|
+
type SelectorSelectSingle<E, EE, SE> = (selector: Select<E, any>) => SingleSelect<EE, SE>;
|
|
30
|
+
type SelectorChain<E, EE, SE> = SelectorSelect<E, EE, SE> | SelectorSelectSingle<E, EE, SE>;
|
|
31
|
+
type Selector<E, EE, I extends ItemId, SE = E> = SelectorChain<E, EE, SE> | I | Iterable<I>;
|
|
32
|
+
type UpdateFn<E, EE, SE> = (entity: EE, match?: SE) => E;
|
|
33
|
+
type Updater<E, EE, SE = E> = UpdateFn<E, EE, SE> | Partial<E>;
|
|
34
|
+
type SelectId<E> = (entity: E) => ItemId;
|
|
28
35
|
type ItemsOptions<E> = {
|
|
29
36
|
selectId?: SelectId<E>;
|
|
30
37
|
sortComparer?: false | ((a: E, b: E) => number);
|
|
@@ -33,45 +40,61 @@ interface ItemsState<E, I> {
|
|
|
33
40
|
ids: I[];
|
|
34
41
|
entities: Map<I, E>;
|
|
35
42
|
}
|
|
36
|
-
|
|
43
|
+
interface ItemDiff {
|
|
44
|
+
id: ItemId;
|
|
45
|
+
changes: any[];
|
|
46
|
+
}
|
|
47
|
+
interface ItemsDiff {
|
|
48
|
+
added: ItemId[];
|
|
49
|
+
removed: ItemId[];
|
|
50
|
+
updated: ItemDiff[];
|
|
51
|
+
}
|
|
52
|
+
//#endregion
|
|
53
|
+
//#region src/Items.d.ts
|
|
54
|
+
declare class Items<E extends Object, I extends ItemId = ItemId> {
|
|
37
55
|
private options;
|
|
38
56
|
private state;
|
|
39
57
|
constructor(items?: Iterable<E>, options?: ItemsOptions<E>);
|
|
58
|
+
add(items: Iterable<E>): Items<E, I>;
|
|
59
|
+
update<SE, EE>(select: Selector<E, EE, I, SE>, updater: Partial<E>): Items<E, I>;
|
|
60
|
+
update<SE, EE>(select: I | Iterable<I>, updater: Updater<E, E, E>): Items<E, I>;
|
|
61
|
+
update<SE, EE>(select: SelectorChain<E, EE, SE>, updater: Updater<E, EE, SE>): Items<E, I>;
|
|
62
|
+
merge(items: Iterable<E>): Items<E, I>;
|
|
63
|
+
remove<EE extends E = E>(select: Selector<E, EE, I>): Items<E, I>;
|
|
64
|
+
pick<EE extends E = E>(select: Selector<E, EE, I>): Items<E, I>;
|
|
65
|
+
select<EE, SE = never>(select: I): E | undefined;
|
|
66
|
+
select<EE, SE = never>(select: Iterable<I>): E[];
|
|
67
|
+
select<EE, SE = never>(select: SelectorSelect<E, EE, SE>): E[];
|
|
68
|
+
select<EE, SE = never>(select: SelectorSelectSingle<E, EE, SE>): E | undefined;
|
|
69
|
+
selectId<EE, SE = never>(select: I): I | undefined;
|
|
70
|
+
selectId<EE, SE = never>(select: Iterable<I>): I[];
|
|
71
|
+
selectId<EE, SE = never>(select: SelectorSelectSingle<E, EE, SE>): I | undefined;
|
|
72
|
+
selectId<EE, SE = never>(select: SelectorSelect<E, EE, SE>): I[];
|
|
73
|
+
clear(): Items<E, I>;
|
|
74
|
+
every(check: CheckFn<E>): boolean;
|
|
75
|
+
some(check: CheckFn<E>): boolean;
|
|
76
|
+
has(id: I): boolean;
|
|
77
|
+
get(id: I): E | undefined;
|
|
78
|
+
get(id: undefined): undefined;
|
|
40
79
|
getIds(): I[];
|
|
41
|
-
getEntities():
|
|
80
|
+
getEntities(): E[];
|
|
42
81
|
get length(): number;
|
|
43
|
-
|
|
44
|
-
insert(entity: E): Items<E, I>;
|
|
45
|
-
insertMany(entities: Iterable<E>): Items<E, I>;
|
|
46
|
-
upsert(entity: Partial<E>): Items<E, I>;
|
|
47
|
-
upsertMany(entities: Iterable<Partial<E>>): Items<E, I>;
|
|
48
|
-
set(entity: E): Items<E, I>;
|
|
49
|
-
setMany(entities: Iterable<E>): Items<E, I>;
|
|
50
|
-
every(check: SelectorFn<E>): boolean;
|
|
51
|
-
some(check: SelectorFn<E>): boolean;
|
|
52
|
-
has(id: I): boolean;
|
|
53
|
-
hasMany(select: Selector<E, I>): boolean;
|
|
54
|
-
update(id: I, updater: Updater<E>): Items<E, I>;
|
|
55
|
-
updateMany(select: Selector<E, I>, updater: Updater<E>): Items<E, I>;
|
|
56
|
-
remove(id: I): Items<E, I>;
|
|
57
|
-
removeMany(select: Selector<E, I>): Items<E, I>;
|
|
58
|
-
clear(): Items<E, I>;
|
|
59
|
-
filter(select: Selector<E, I>): Items<E, I>;
|
|
60
|
-
page(page: number, pageSize: number): {
|
|
61
|
-
items: NonNullable<E>[];
|
|
62
|
-
page: number;
|
|
63
|
-
pageSize: number;
|
|
64
|
-
hasNext: boolean;
|
|
65
|
-
hasPrevious: boolean;
|
|
66
|
-
total: number;
|
|
67
|
-
totalPages: number;
|
|
68
|
-
};
|
|
69
|
-
diff(base: Items<E, I>): ItemsDiff<I>;
|
|
82
|
+
private resolveSelector;
|
|
70
83
|
private sortIds;
|
|
71
|
-
|
|
84
|
+
extractId(entity: E | undefined): I | undefined;
|
|
72
85
|
private get sortComparer();
|
|
73
86
|
[Symbol.iterator](): Iterator<E>;
|
|
87
|
+
static compare<E extends Object, I extends ItemId>(base: Items<E, I>, to: Items<E, I>): {
|
|
88
|
+
added: ItemId[];
|
|
89
|
+
removed: I[];
|
|
90
|
+
updated: any[];
|
|
91
|
+
};
|
|
74
92
|
}
|
|
75
93
|
//#endregion
|
|
76
|
-
|
|
94
|
+
//#region src/utils.d.ts
|
|
95
|
+
declare function defaultSelectId<E extends {
|
|
96
|
+
id: ItemId;
|
|
97
|
+
}>(entity: E): ItemId;
|
|
98
|
+
//#endregion
|
|
99
|
+
export { BaseSelect, type CheckFn, type ItemDiff, type ItemId, Items, type ItemsDiff, type ItemsOptions, type ItemsState, Select, type SelectId, type Selector, SingleSelect, type UpdateFn, type Updater, defaultSelectId };
|
|
77
100
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.mjs
CHANGED
|
@@ -1,18 +1,69 @@
|
|
|
1
1
|
import { diff } from "ohash/utils";
|
|
2
2
|
|
|
3
|
-
//#region src/
|
|
3
|
+
//#region src/utils.ts
|
|
4
4
|
function defaultSelectId(entity) {
|
|
5
5
|
return entity.id;
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
//#endregion
|
|
9
|
-
//#region src/
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
}
|
|
9
|
+
//#region src/select.ts
|
|
10
|
+
var BaseSelect = class {
|
|
11
|
+
context;
|
|
12
|
+
constructor(items, context = /* @__PURE__ */ new Map()) {
|
|
13
|
+
this.items = items;
|
|
14
|
+
this.context = this.cleanContext(items, context);
|
|
15
|
+
}
|
|
16
|
+
cleanContext(items, context) {
|
|
17
|
+
if (context.size === 0) return /* @__PURE__ */ new Map();
|
|
18
|
+
const cleanedContext = /* @__PURE__ */ new Map();
|
|
19
|
+
for (const [key, value] of context.entries()) if (items.includes(value)) cleanedContext.set(key, value);
|
|
20
|
+
return cleanedContext;
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
var SingleSelect = class extends BaseSelect {};
|
|
24
|
+
var Select = class Select extends BaseSelect {
|
|
25
|
+
take(len) {
|
|
26
|
+
return new Select(this.items.slice(0, len), this.context);
|
|
27
|
+
}
|
|
28
|
+
skip(len) {
|
|
29
|
+
return new Select(this.items.slice(len), this.context);
|
|
30
|
+
}
|
|
31
|
+
filter(testFn) {
|
|
32
|
+
return new Select(this.items.filter((entry) => testFn(entry)), this.context);
|
|
33
|
+
}
|
|
34
|
+
revert() {
|
|
35
|
+
return new Select([...this.items].reverse(), this.context);
|
|
36
|
+
}
|
|
37
|
+
sort(sortFn) {
|
|
38
|
+
return new Select([...this.items].sort((a, b) => sortFn(a, b)), this.context);
|
|
39
|
+
}
|
|
40
|
+
at(index) {
|
|
41
|
+
const item = this.items[index];
|
|
42
|
+
return new SingleSelect([item], this.context);
|
|
43
|
+
}
|
|
44
|
+
find(testFn) {
|
|
45
|
+
const item = this.items.find((entry) => testFn(entry));
|
|
46
|
+
return new SingleSelect(item ? [item] : [], this.context);
|
|
47
|
+
}
|
|
48
|
+
from(entities, matcher) {
|
|
49
|
+
if (matcher) {
|
|
50
|
+
const items = Array.from(entities);
|
|
51
|
+
const pairs = new Map(items.map((entry) => {
|
|
52
|
+
return [entry, this.items.find((item) => matcher(entry, item))];
|
|
53
|
+
}));
|
|
54
|
+
return new Select(Array.from(pairs.values()), pairs);
|
|
55
|
+
} else return new Select(Array.from(entities));
|
|
56
|
+
}
|
|
57
|
+
on(entry, matcher) {
|
|
58
|
+
if (matcher) {
|
|
59
|
+
const select = this.from([entry], matcher);
|
|
60
|
+
return new SingleSelect(select.items, select.context);
|
|
61
|
+
} else {
|
|
62
|
+
const select = this.from([entry]);
|
|
63
|
+
return new SingleSelect(select.items, select.context);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
};
|
|
16
67
|
|
|
17
68
|
//#endregion
|
|
18
69
|
//#region src/diff.ts
|
|
@@ -39,15 +90,6 @@ function itemsDiff(fromItems, toItems) {
|
|
|
39
90
|
};
|
|
40
91
|
}
|
|
41
92
|
|
|
42
|
-
//#endregion
|
|
43
|
-
//#region src/updater.ts
|
|
44
|
-
function update(entity, updater) {
|
|
45
|
-
return {
|
|
46
|
-
...entity,
|
|
47
|
-
...typeof updater === "function" ? updater(entity) : updater
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
|
|
51
93
|
//#endregion
|
|
52
94
|
//#region src/Items.ts
|
|
53
95
|
var Items = class Items {
|
|
@@ -55,121 +97,122 @@ var Items = class Items {
|
|
|
55
97
|
constructor(items = [], options = {}) {
|
|
56
98
|
this.options = options;
|
|
57
99
|
const entities = new Map(Array.from(items).map((item) => {
|
|
58
|
-
return [this.
|
|
59
|
-
}));
|
|
100
|
+
return [this.extractId(item), item];
|
|
101
|
+
}).filter(([id]) => id !== void 0));
|
|
60
102
|
this.state = {
|
|
61
103
|
ids: this.sortIds([...entities.keys()], entities),
|
|
62
104
|
entities
|
|
63
105
|
};
|
|
64
106
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
return this.
|
|
73
|
-
}
|
|
74
|
-
select
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
107
|
+
add(items) {
|
|
108
|
+
const newEntities = new Map(this.state.entities);
|
|
109
|
+
Array.from(items).forEach((item) => {
|
|
110
|
+
const id = this.extractId(item);
|
|
111
|
+
if (id === void 0) return;
|
|
112
|
+
if (!newEntities.has(id)) newEntities.set(id, item);
|
|
113
|
+
});
|
|
114
|
+
return new Items(newEntities.values(), this.options);
|
|
115
|
+
}
|
|
116
|
+
update(select, updater) {
|
|
117
|
+
const [, entities, map] = this.resolveSelector(select);
|
|
118
|
+
const newEntities = new Map(this.state.entities);
|
|
119
|
+
const isFn = typeof updater === "function";
|
|
120
|
+
if (isFn && map.size) {
|
|
121
|
+
Array.from(map.entries()).forEach(([entity, pair]) => {
|
|
122
|
+
const updatedEntry = updater(pair, entity);
|
|
123
|
+
const id = this.extractId(updatedEntry);
|
|
124
|
+
newEntities.set(id, updatedEntry);
|
|
125
|
+
});
|
|
126
|
+
return new Items(newEntities.values(), this.options);
|
|
127
|
+
}
|
|
128
|
+
entities.forEach((entity) => {
|
|
129
|
+
if (isFn) {
|
|
130
|
+
const updatedEntry = updater(entity);
|
|
131
|
+
const id = this.extractId(updatedEntry);
|
|
132
|
+
newEntities.set(id, updatedEntry);
|
|
133
|
+
} else if (entity) {
|
|
134
|
+
const updatedEntry = {
|
|
135
|
+
...entity,
|
|
136
|
+
...updater
|
|
137
|
+
};
|
|
138
|
+
const id = this.extractId(updatedEntry);
|
|
139
|
+
newEntities.set(id, updatedEntry);
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
return new Items(newEntities.values(), this.options);
|
|
143
|
+
}
|
|
144
|
+
merge(items) {
|
|
145
|
+
const newEntities = new Map(this.state.entities);
|
|
146
|
+
Array.from(items).map((item) => [this.extractId(item), item]).filter(([id]) => id !== void 0).forEach(([id, item]) => {
|
|
147
|
+
const entry = this.get(id);
|
|
148
|
+
newEntities.set(id, entry ? {
|
|
149
|
+
...entry,
|
|
150
|
+
...item
|
|
151
|
+
} : item);
|
|
152
|
+
});
|
|
153
|
+
return new Items(newEntities.values(), this.options);
|
|
79
154
|
}
|
|
80
|
-
|
|
81
|
-
|
|
155
|
+
remove(select) {
|
|
156
|
+
const [, entities] = this.resolveSelector(select);
|
|
157
|
+
const selectedIds = entities.map((entity) => this.extractId(entity));
|
|
158
|
+
return new Items(this.state.ids.filter((id) => !selectedIds.includes(id)).map((id) => this.get(id)), this.options);
|
|
82
159
|
}
|
|
83
|
-
|
|
84
|
-
|
|
160
|
+
pick(select) {
|
|
161
|
+
const [, entities] = this.resolveSelector(select);
|
|
162
|
+
return new Items(entities, this.options);
|
|
85
163
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
const existing = clone.get(id);
|
|
91
|
-
if (existing) clone.set(id, {
|
|
92
|
-
...existing,
|
|
93
|
-
...entity
|
|
94
|
-
});
|
|
95
|
-
else clone.set(id, entity);
|
|
96
|
-
});
|
|
97
|
-
return new Items(clone.values(), this.options);
|
|
164
|
+
select(select) {
|
|
165
|
+
let [single, entities] = this.resolveSelector(select);
|
|
166
|
+
entities = entities.filter(Boolean);
|
|
167
|
+
return single ? entities[0] : entities;
|
|
98
168
|
}
|
|
99
|
-
|
|
100
|
-
|
|
169
|
+
selectId(select) {
|
|
170
|
+
let [single, entities] = this.resolveSelector(select);
|
|
171
|
+
entities = entities.filter(Boolean);
|
|
172
|
+
return single ? this.extractId(entities[0]) : entities.map((entity) => this.extractId(entity));
|
|
101
173
|
}
|
|
102
|
-
|
|
103
|
-
return new Items([
|
|
174
|
+
clear() {
|
|
175
|
+
return new Items([], this.options);
|
|
104
176
|
}
|
|
105
177
|
every(check) {
|
|
106
|
-
return this.getIds().every((id) => check(this.
|
|
178
|
+
return this.getIds().every((id) => check(this.get(id)));
|
|
107
179
|
}
|
|
108
180
|
some(check) {
|
|
109
|
-
return this.getIds().some((id) => check(this.
|
|
181
|
+
return this.getIds().some((id) => check(this.get(id)));
|
|
110
182
|
}
|
|
111
183
|
has(id) {
|
|
112
184
|
return this.state.ids.includes(id);
|
|
113
185
|
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
let result = false;
|
|
117
|
-
selector(this, select, (entity) => {
|
|
118
|
-
if (entity) result = true;
|
|
119
|
-
else failToFind = true;
|
|
120
|
-
});
|
|
121
|
-
return !failToFind && result;
|
|
122
|
-
}
|
|
123
|
-
update(id, updater) {
|
|
124
|
-
const entity = this.select(id);
|
|
125
|
-
if (!entity) return this;
|
|
126
|
-
const clone = this.getEntities();
|
|
127
|
-
clone.set(id, update(entity, updater));
|
|
128
|
-
return new Items(clone.values(), this.options);
|
|
129
|
-
}
|
|
130
|
-
updateMany(select, updater) {
|
|
131
|
-
const clone = this.getEntities();
|
|
132
|
-
selector(this, select, (entity, id) => {
|
|
133
|
-
if (entity) clone.set(id, update(entity, updater));
|
|
134
|
-
});
|
|
135
|
-
return new Items(clone.values(), this.options);
|
|
136
|
-
}
|
|
137
|
-
remove(id) {
|
|
138
|
-
const clone = this.getEntities();
|
|
139
|
-
clone.delete(id);
|
|
140
|
-
return new Items(clone.values(), this.options);
|
|
186
|
+
get(id) {
|
|
187
|
+
return id === void 0 ? void 0 : this.state.entities.get(id);
|
|
141
188
|
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
selector(this, select, (_, id) => {
|
|
145
|
-
clone.delete(id);
|
|
146
|
-
});
|
|
147
|
-
return new Items(clone.values(), this.options);
|
|
189
|
+
getIds() {
|
|
190
|
+
return [...this.state.ids];
|
|
148
191
|
}
|
|
149
|
-
|
|
150
|
-
return
|
|
192
|
+
getEntities() {
|
|
193
|
+
return this.state.ids.map((id) => this.get(id));
|
|
151
194
|
}
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
selector(this, select, (entity, id) => {
|
|
155
|
-
if (entity) clone.set(id, entity);
|
|
156
|
-
});
|
|
157
|
-
return new Items(clone.values(), this.options);
|
|
158
|
-
}
|
|
159
|
-
page(page, pageSize) {
|
|
160
|
-
const totalPages = Math.ceil(this.length / pageSize);
|
|
161
|
-
return {
|
|
162
|
-
items: this.getIds().slice(page * pageSize, (page + 1) * pageSize).map((id) => this.select(id)),
|
|
163
|
-
page,
|
|
164
|
-
pageSize,
|
|
165
|
-
hasNext: page < totalPages - 1,
|
|
166
|
-
hasPrevious: page > 0,
|
|
167
|
-
total: this.length,
|
|
168
|
-
totalPages
|
|
169
|
-
};
|
|
195
|
+
get length() {
|
|
196
|
+
return this.state.ids.length;
|
|
170
197
|
}
|
|
171
|
-
|
|
172
|
-
|
|
198
|
+
resolveSelector(select) {
|
|
199
|
+
if (typeof select === "function") {
|
|
200
|
+
const result = select(new Select(this.getEntities()));
|
|
201
|
+
return [
|
|
202
|
+
result instanceof SingleSelect,
|
|
203
|
+
result.items,
|
|
204
|
+
result.context
|
|
205
|
+
];
|
|
206
|
+
} else if (typeof select === "string" || typeof select === "number") return [
|
|
207
|
+
true,
|
|
208
|
+
[this.get(select)],
|
|
209
|
+
/* @__PURE__ */ new Map()
|
|
210
|
+
];
|
|
211
|
+
else return [
|
|
212
|
+
false,
|
|
213
|
+
Array.from(select).map((id) => this.get(id)),
|
|
214
|
+
/* @__PURE__ */ new Map()
|
|
215
|
+
];
|
|
173
216
|
}
|
|
174
217
|
sortIds(ids, entities) {
|
|
175
218
|
if (this.sortComparer === false) return ids;
|
|
@@ -178,7 +221,8 @@ var Items = class Items {
|
|
|
178
221
|
return sorter(entities.get(aId), entities.get(bId));
|
|
179
222
|
});
|
|
180
223
|
}
|
|
181
|
-
|
|
224
|
+
extractId(entity) {
|
|
225
|
+
if (!entity) return;
|
|
182
226
|
return this.options?.selectId?.(entity) || defaultSelectId(entity);
|
|
183
227
|
}
|
|
184
228
|
get sortComparer() {
|
|
@@ -187,8 +231,11 @@ var Items = class Items {
|
|
|
187
231
|
[Symbol.iterator]() {
|
|
188
232
|
return this.state.entities.values();
|
|
189
233
|
}
|
|
234
|
+
static compare(base, to) {
|
|
235
|
+
return itemsDiff(base, to);
|
|
236
|
+
}
|
|
190
237
|
};
|
|
191
238
|
|
|
192
239
|
//#endregion
|
|
193
|
-
export { Items };
|
|
240
|
+
export { BaseSelect, Items, Select, SingleSelect, defaultSelectId };
|
|
194
241
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["selector","added: I[]","updated: ItemDiff<I>[]","options: ItemsOptions<E>"],"sources":["../src/selectId.ts","../src/selector.ts","../src/diff.ts","../src/updater.ts","../src/Items.ts"],"sourcesContent":["export type StrOrNum = string | number\n\nexport type SelectId<E> = (entity: E) => StrOrNum\n\nexport function defaultSelectId<E extends { id: I }, I = E['id']>(entity: E) {\n return entity.id\n}\n","import { Items } from './Items'\nimport { StrOrNum } from './selectId'\n\nexport type SelectorFn<E> = (entity: E) => boolean\nexport type Selector<E, I> = Iterable<I> | SelectorFn<E>\nexport type Operation<E, I> = (entity: E | undefined, id: I) => void\n\nexport function selector<E, I extends StrOrNum>(items: Items<E, I>, selector: Selector<E, I>, operation: Operation<E, I>) {\n if (typeof selector === 'function') {\n items.getEntities().forEach((entity, id) => {\n if ((selector as SelectorFn<E>)(entity)) {\n operation(entity, id)\n }\n })\n } else {\n Array\n .from(selector as Iterable<I>)\n .forEach(id => operation(items.select(id), id))\n }\n}\n","import { diff } from 'ohash/utils'\nimport { Items } from './Items'\nimport { StrOrNum } from './selectId'\n\nexport interface ItemDiff<I> {\n id: I\n changes: ReturnType<typeof diff>\n}\n\nexport interface ItemsDiff<I> {\n added: I[]\n removed: I[]\n updated: ItemDiff<I>[]\n}\n\nexport function itemsDiff<E, I extends StrOrNum>(fromItems: Items<E, I>, toItems: Items<E, I>): ItemsDiff<I> {\n const ids = new Set(toItems.getIds())\n const baseIds = new Set(fromItems.getIds())\n\n const added: I[] = []\n const updated: ItemDiff<I>[] = []\n\n ids.forEach(id => {\n if (!baseIds.has(id)) {\n added.push(id)\n } else {\n const changes = diff(fromItems.select(id), toItems.select(id))\n\n if (changes.length > 0) {\n updated.push({ id, changes })\n }\n\n baseIds.delete(id)\n }\n })\n\n const removed = [...baseIds]\n\n return {\n added,\n removed,\n updated\n }\n}\n","export type UpdateFn<E> = (entity: E) => E\nexport type Updater<E> = UpdateFn<E> | Partial<E>\n\nexport function update<E>(entity: E, updater: Updater<E>) {\n return { ...entity, ...(typeof updater === 'function' ? updater(entity) : updater) }\n}\n","import { defaultSelectId, SelectId, StrOrNum } from './selectId'\nimport { selector, Selector, SelectorFn } from './selector'\nimport { Updater } from './updater'\nimport { itemsDiff } from './diff'\nimport { update } from './updater'\n\nexport type ItemsOptions<E> = {\n selectId?: SelectId<E>\n sortComparer?: false | ((a: E, b: E) => number)\n}\n\nexport interface ItemsState<E, I> {\n ids: I[]\n entities: Map<I, E>\n}\n\nexport class Items<E, I extends StrOrNum = StrOrNum> {\n private state: ItemsState<E, I>\n\n constructor(\n items: Iterable<E> = [],\n private options: ItemsOptions<E> = {}\n ) {\n const entities = new Map(\n Array\n .from(items)\n .map((item) => {\n const id = this.selectId(item)\n return [id, item]\n })\n )\n\n this.state = {\n ids: this.sortIds([...entities.keys()], entities),\n entities\n }\n }\n\n getIds(): I[] {\n return [...this.state.ids]\n }\n\n getEntities(): Map<I, E> {\n return new Map(this.state.entities)\n }\n\n get length(): number {\n return this.state.ids.length\n }\n\n select(id: I): E | undefined {\n return this.state.entities.get(id)\n }\n\n insert(entity: E) {\n return this.insertMany([entity])\n }\n\n insertMany(entities: Iterable<E>) {\n return new Items<E, I>([\n ...this,\n ...Array.from(entities).filter(entity => !this.has(this.selectId(entity)))\n ], this.options)\n }\n\n upsert(entity: Partial<E>) {\n return this.upsertMany([entity])\n }\n\n upsertMany(entities: Iterable<Partial<E>>) {\n const clone = this.getEntities()\n Array.from(entities).forEach(entity => {\n const id = this.selectId(entity as E)\n const existing = clone.get(id)\n if (existing) {\n clone.set(id, { ...existing, ...entity } as E)\n } else {\n clone.set(id, entity as E)\n }\n })\n return new Items<E, I>(clone.values(), this.options)\n }\n\n set(entity: E) {\n return this.setMany([entity])\n }\n\n setMany(entities: Iterable<E>) {\n return new Items<E, I>([\n ...this,\n ...entities\n ], this.options)\n }\n\n every(check: SelectorFn<E>) {\n return this.getIds().every(id => check(this.select(id)!))\n }\n\n some(check: SelectorFn<E>) {\n return this.getIds().some(id => check(this.select(id)!))\n }\n\n has(id: I) {\n return this.state.ids.includes(id)\n }\n\n hasMany(select: Selector<E, I>) {\n let failToFind = false\n let result = false\n selector(this, select, (entity) => {\n if (entity) {\n result = true\n } else {\n failToFind = true\n }\n })\n return !failToFind && result\n }\n\n update(id: I, updater: Updater<E>) {\n const entity = this.select(id)\n if (!entity) {\n return this\n }\n const clone = this.getEntities()\n clone.set(id, update(entity, updater))\n return new Items<E, I>(clone.values(), this.options)\n }\n\n updateMany(select: Selector<E, I>, updater: Updater<E>) {\n const clone = this.getEntities()\n selector(this, select, (entity, id) => {\n if (entity) {\n clone.set(id, update(entity, updater))\n }\n })\n return new Items<E, I>(clone.values(), this.options)\n }\n\n remove(id: I) {\n const clone = this.getEntities()\n clone.delete(id)\n return new Items<E, I>(clone.values(), this.options)\n }\n\n removeMany(select: Selector<E, I>) {\n const clone = this.getEntities()\n selector(this, select, (_, id) => {\n clone.delete(id)\n })\n return new Items<E, I>(clone.values(), this.options)\n }\n\n clear() {\n return new Items<E, I>([], this.options)\n }\n\n filter(select: Selector<E, I>) {\n const clone = new Map<I, E>()\n\n selector(this, select, (entity, id) => {\n if (entity) {\n clone.set(id, entity)\n }\n })\n\n return new Items<E, I>(clone.values(), this.options)\n }\n\n page(page: number, pageSize: number) {\n const totalPages = Math.ceil(this.length / pageSize)\n return {\n items: this.getIds()\n .slice(page * pageSize, (page + 1) * pageSize)\n .map(id => this.select(id)!),\n page,\n pageSize,\n hasNext: page < totalPages - 1,\n hasPrevious: page > 0,\n total: this.length,\n totalPages\n }\n }\n\n diff(base: Items<E, I>) {\n return itemsDiff(base, this)\n }\n\n private sortIds(\n ids: I[],\n entities: Map<I, E>\n ): Array<I> {\n if (this.sortComparer === false) {\n return ids\n }\n const sorter = this.sortComparer\n return [...ids].sort((aId, bId) => {\n const a = entities.get(aId)!\n const b = entities.get(bId)!\n return sorter(a, b)\n })\n }\n\n private selectId(entity: E): I {\n return this.options?.selectId?.(entity) as undefined || defaultSelectId(entity as E & { id: I })\n }\n\n private get sortComparer() {\n return this.options.sortComparer || false\n }\n\n [Symbol.iterator](): Iterator<E> {\n return this.state.entities.values()\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["items: E[]","added: ItemId[]","updated: any[]","options: ItemsOptions<E>"],"sources":["../src/utils.ts","../src/select.ts","../src/diff.ts","../src/Items.ts"],"sourcesContent":["import type { ItemId } from './types'\nexport function defaultSelectId<E extends { id: ItemId }>(entity: E): ItemId {\n return entity.id\n}\n","import { MatchFn, TestFn } from './types'\n\n// NOTE: E selected items, may be undefined as well\n// NOTE: SE content type\nexport class BaseSelect<E, SE> {\n public context: Map<SE, E>\n\n constructor(\n public items: E[],\n context: Map<SE, E> = new Map()\n ) {\n // Automatically clean context to only include items that exist in items array\n this.context = this.cleanContext(items, context)\n }\n\n private cleanContext(items: E[], context: Map<SE, E>): Map<SE, E> {\n if (context.size === 0) {\n return new Map()\n }\n\n const cleanedContext = new Map<SE, E>()\n for (const [key, value] of context.entries()) {\n if (items.includes(value)) {\n cleanedContext.set(key, value)\n }\n }\n return cleanedContext\n }\n}\n\nexport class SingleSelect<E, SE> extends BaseSelect<E, SE> {}\n\nexport class Select<E, SE> extends BaseSelect<E, SE> {\n take(len: number) {\n return new Select<E, SE>(this.items.slice(0, len), this.context)\n }\n\n skip(len: number) {\n return new Select<E, SE>(this.items.slice(len), this.context)\n }\n\n filter(testFn: TestFn<E>) {\n return new Select<E, SE>(this.items.filter((entry) => testFn(entry)), this.context)\n }\n\n revert(){\n return new Select<E, SE>([...this.items].reverse(), this.context)\n }\n\n sort(sortFn: (x: E, y: E) => number) {\n return new Select<E, SE>([...this.items].sort((a, b) => sortFn(a, b)), this.context)\n }\n\n at(index: number) {\n const item = this.items[index]\n return new SingleSelect<E | undefined, SE>([item], this.context)\n }\n\n find(testFn: TestFn<E>) {\n const item = this.items.find((entry) => testFn(entry))\n return new SingleSelect<E | undefined, SE>(item ? [item] : [], this.context)\n }\n\n from<T>(entities: Iterable<E>): Select<E, SE>\n from<T>(entities: Iterable<T>, matcher: MatchFn<E, T>): Select<E | undefined, T>\n from<T>(entities: Iterable<E> | Iterable<T>, matcher?: MatchFn<E, T>): Select<E, SE> | Select<E | undefined, T> {\n if (matcher) {\n const items = Array.from(entities as Iterable<T>)\n const pairs = new Map(\n items\n .map(entry => {\n const pair = this.items.find(item => matcher(entry, item))\n return [entry, pair]\n }) as [T, E | undefined][]\n )\n return new Select<E | undefined, T>(Array.from(pairs.values()), pairs)\n } else {\n return new Select<E, SE>(Array.from(entities as Iterable<E>))\n }\n }\n\n on<T>(entry: E): SingleSelect<E, SE>\n on<T>(entry: T, matcher: MatchFn<E, T>): SingleSelect<E | undefined, T>\n on<T>(entry: E, matcher?: MatchFn<E, T>): SingleSelect<E, SE> | SingleSelect<E | undefined, T> {\n if (matcher) {\n const select = this.from([entry as unknown as T], matcher)\n return new SingleSelect<E | undefined, T>(select.items, select.context)\n } else {\n const select = this.from([entry])\n return new SingleSelect<E, SE>(select.items, select.context)\n }\n }\n}\n","import type { ItemId } from './types'\nimport { Items } from './Items'\nimport { diff} from 'ohash/utils'\n\nexport function itemsDiff<E extends Object, I extends ItemId>(fromItems: Items<E, I>, toItems: Items<E, I>) {\n const ids = new Set(toItems.getIds())\n const baseIds = new Set(fromItems.getIds())\n\n const added: ItemId[] = []\n const updated: any[] = []\n\n ids.forEach(id => {\n if (!baseIds.has(id)) {\n added.push(id)\n } else {\n const changes = diff(fromItems.select(id), toItems.select(id))\n\n if (changes.length > 0) {\n updated.push({ id, changes })\n }\n\n baseIds.delete(id)\n }\n })\n\n const removed = [...baseIds]\n\n return {\n added,\n removed,\n updated\n }\n}\n","import {\n CheckFn, ItemId, ItemsOptions, ItemsState, Selector, SelectorChain, SelectorSelect,\n SelectorSelectSingle, Updater\n} from './types'\nimport { defaultSelectId } from './utils'\nimport { Select, SingleSelect } from './select'\nimport { itemsDiff } from './diff'\n\nexport class Items<E extends Object, I extends ItemId = ItemId> {\n private state: ItemsState<E, I>\n\n constructor(\n items: Iterable<E> = [],\n private options: ItemsOptions<E> = {}\n ) {\n const entities = new Map(\n Array\n .from(items)\n .map((item) => {\n const id = this.extractId(item)\n return [id, item]\n })\n .filter(([id]) => id !== undefined) as [I, E][]\n )\n\n this.state = {\n ids: this.sortIds([...entities.keys()], entities),\n entities\n }\n }\n\n // NOTE: Add item if id not exists\n add(items: Iterable<E>) {\n const newEntities = new Map(this.state.entities)\n\n Array.from(items).forEach((item) => {\n const id = this.extractId(item)\n if (id === undefined) {\n return\n }\n if (!newEntities.has(id)) {\n newEntities.set(id, item)\n }\n })\n\n return new Items<E, I>(newEntities.values(), this.options)\n }\n\n // NOTE: update selected ids with partial data or call function\n update<SE, EE>(select: Selector<E, EE, I, SE>, updater: Partial<E>): Items<E, I>\n update<SE, EE>(select: I | Iterable<I>, updater: Updater<E, E, E>): Items<E, I>\n update<SE, EE>(select: SelectorChain<E, EE, SE>, updater: Updater<E, EE, SE>): Items<E, I>\n update<SE, EE>(select: Selector<E, EE, I, SE>, updater: Updater<E, EE, SE>): Items<E, I> {\n const [, entities, map] = this.resolveSelector(select)\n const newEntities = new Map(this.state.entities)\n const isFn = typeof updater === 'function'\n\n if (isFn && map.size) {\n Array\n .from(map.entries())\n .forEach(([entity, pair]) => {\n const updatedEntry = updater(pair, entity)\n const id = this.extractId(updatedEntry)!\n newEntities.set(id, updatedEntry)\n })\n\n return new Items<E, I>(newEntities.values(), this.options)\n }\n\n entities\n .forEach((entity) => {\n if (isFn) {\n const updatedEntry = updater(entity)\n const id = this.extractId(updatedEntry)!\n newEntities.set(id, updatedEntry)\n } else if (entity) {\n const updatedEntry = { ...(entity as unknown as E), ...updater }\n const id = this.extractId(updatedEntry)!\n newEntities.set(id, updatedEntry)\n }\n })\n\n return new Items<E, I>(newEntities.values(), this.options)\n }\n\n // NOTE: add if not exists, overwrite if exists\n merge(items: Iterable<E>) {\n const newEntities = new Map(this.state.entities)\n\n Array\n .from(items)\n .map(item => [this.extractId(item), item] as [I, E])\n .filter(([id]) => id !== undefined)\n .forEach(([id, item]) => {\n const entry = this.get(id)\n newEntities.set(id, entry ? { ...entry, ...item } : item)\n })\n\n return new Items<E, I>(newEntities.values(), this.options)\n }\n\n remove<EE extends E = E>(select: Selector<E, EE, I>) {\n const [, entities] = this.resolveSelector(select)\n const selectedIds = entities.map(entity => this.extractId(entity as E))\n const idsToKeep = this.state.ids.filter(id => !selectedIds.includes(id))\n const items = idsToKeep.map(id => this.get(id)!)\n return new Items<E, I>(items, this.options)\n }\n\n pick<EE extends E = E>(select: Selector<E, EE, I>) {\n const [, entities] = this.resolveSelector(select)\n return new Items<E, I>(entities as E[], this.options)\n }\n\n select<EE, SE = never>(select: I): E | undefined\n select<EE, SE = never>(select: Iterable<I>): E[]\n select<EE, SE = never>(select: SelectorSelect<E, EE, SE>): E[]\n select<EE, SE = never>(select: SelectorSelectSingle<E, EE, SE>): E | undefined\n select<EE, SE = never>(select: Selector<E, EE, I, SE>): undefined | E | E[] {\n let [single, entities] = this.resolveSelector(select)\n entities = entities.filter(Boolean)\n return single ? entities[0] as unknown as E : entities as unknown as E[]\n }\n\n selectId<EE, SE = never>(select: I): I | undefined\n selectId<EE, SE = never>(select: Iterable<I>): I[]\n selectId<EE, SE = never>(select: SelectorSelectSingle<E, EE, SE>): I | undefined\n selectId<EE, SE = never>(select: SelectorSelect<E, EE, SE>): I[]\n selectId<EE, SE = never>(select: Selector<E, EE, I, SE>): undefined | I | I[] {\n let [single, entities] = this.resolveSelector(select)\n entities = entities.filter(Boolean)\n return single\n ? this.extractId(entities[0] as unknown as E)\n : entities.map(entity => this.extractId(entity as unknown as E)!)\n }\n\n clear() {\n return new Items<E, I>([], this.options)\n }\n\n every(check: CheckFn<E>) {\n return this.getIds().every(id => check(this.get(id)!))\n }\n\n some(check: CheckFn<E>) {\n return this.getIds().some(id => check(this.get(id)!))\n }\n\n has(id: I) {\n return this.state.ids.includes(id)\n }\n\n get(id: I ): E | undefined\n get(id: undefined): undefined\n get(id: I | undefined): E | undefined {\n return id === undefined ? undefined : this.state.entities.get(id)\n }\n\n getIds(): I[] {\n return [...this.state.ids]\n }\n\n getEntities(): E[] {\n return this.state.ids.map(id => this.get(id)!)\n }\n\n get length(): number {\n return this.state.ids.length\n }\n\n private resolveSelector<EE, SE>(select: Selector<E, EE, I, SE>): [boolean, EE[], Map<SE, EE>] {\n if (typeof select === 'function') {\n const newSelect = new Select<E, unknown>(this.getEntities())\n const result = select(newSelect)\n return [result instanceof SingleSelect, result.items as EE[], result.context as Map<SE, EE>]\n } else if (typeof select === 'string' || typeof select === 'number') {\n return [true, [this.get(select) as EE], new Map()]\n } else {\n return [false, Array.from(select).map(id => this.get(id) as EE), new Map()]\n }\n }\n\n private sortIds(\n ids: I[],\n entities: Map<I, E>\n ): Array<I> {\n if (this.sortComparer === false) {\n return ids\n }\n const sorter = this.sortComparer\n return [...ids].sort((aId, bId) => {\n const a = entities.get(aId)!\n const b = entities.get(bId)!\n return sorter(a, b)\n })\n }\n\n extractId(entity: E | undefined): I | undefined {\n if (!entity) {\n return undefined\n }\n\n return (this.options?.selectId?.(entity) || defaultSelectId(entity as E & { id: I })) as I\n }\n\n private get sortComparer() {\n return this.options.sortComparer || false\n }\n\n [Symbol.iterator](): Iterator<E> {\n return this.state.entities.values()\n }\n\n static compare<E extends Object, I extends ItemId>(base: Items<E, I>, to: Items<E, I>) {\n return itemsDiff(base, to)\n }\n}\n\n"],"mappings":"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"}
|