• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

WolferyScripting / resclient-ts / #19

08 Aug 2025 02:44PM UTC coverage: 50.25% (-5.2%) from 55.405%
#19

push

DonovanDMC
1.0.5

218 of 274 branches covered (79.56%)

Branch coverage included in aggregate %.

1489 of 3123 relevant lines covered (47.68%)

10.79 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

63.7
/lib/models/ResCollection.ts
1
import type ResClient from "./ResClient.js";
1✔
2
import type CacheItem from "./CacheItem.js";
1✔
3
import { copy } from "../includes/utils/obj.js";
1✔
4
import Properties from "../util/Properties.js";
1✔
5
import { type AnyObject, type AnyFunction } from "../util/types.js";
1✔
6

1✔
7
export default class ResCollection<V = unknown> {
1✔
8
    private _idCallback?: (item: V) => string;
12✔
9
    private _list: Array<V> = [];
12✔
10
    private _map!: Record<string, V> | null;
12✔
11
    protected api!: ResClient;
12✔
12
    rid!: string;
12✔
13
    constructor(api: ResClient, rid: string, options?: { idCallback?(item: V): string; }) {
12✔
14
        options = copy(options ?? {}, {
12✔
15
            idCallback: { type: "?function" }
12✔
16
        });
12✔
17
        this.p
12✔
18
            .writableBulk(["_idCallback", options?.idCallback?.bind(this)], "_list", ["_map", options.idCallback ? {} : null])
12✔
19
            .readOnly("api", api)
12✔
20
            .define("rid", false, true, true, rid);
12✔
21
    }
12✔
22

12✔
23
    private _hasID(): void {
12✔
24
        if (!this._idCallback) {
14!
25
            throw new Error("No id callback defined");
×
26
        }
×
27
    }
14✔
28

12✔
29
    protected get p(): Properties {
12✔
30
        return Properties.of(this);
12✔
31
    }
12✔
32

12✔
33
    get cacheItem(): CacheItem<ResCollection> {
12✔
34
        return this.getClient().cache[this.rid] as CacheItem<ResCollection>;
×
35
    }
×
36

12✔
37
    /** If this collection is empty. */
12✔
38
    get empty(): boolean {
12✔
39
        return this.length === 0;
×
40
    }
×
41

12✔
42
    get length(): number {
12✔
43
        return this._list.length;
×
44
    }
×
45

12✔
46
    get list(): Array<V> {
12✔
47
        return this._list;
×
48
    }
×
49

12✔
50
    [Symbol.iterator](): Iterator<V, undefined> {
12✔
51
        return this._list[Symbol.iterator]();
×
52
    }
×
53

12✔
54
    add(item: V, index: number): void {
12✔
55
        this._list.splice(index, 0, item);
3✔
56

3✔
57
        if (this._idCallback) {
3✔
58
            const id = String(this._idCallback(item));
2✔
59
            if (["", "undefined", "null"].includes(id) || id.replaceAll(/\W/g, "") === "") {
2!
60
                console.debug(item);
×
61
                throw new Error("No id for item");
×
62
            }
×
63
            if (this._map![id]) {
2✔
64
                throw new Error(`Duplicate id - ${id}`);
1✔
65
            }
1✔
66
            this._map![id] = item;
1✔
67
        }
1✔
68
    }
3✔
69

12✔
70
    at(index: number): V | undefined {
12✔
71
        return this._list[index];
14✔
72
    }
14✔
73

12✔
74
    auth<T = unknown>(method: string, params: unknown): Promise<T> {
12✔
75
        return this.api.authenticate<T>(this.rid, method, params);
×
76
    }
×
77

12✔
78
    call<T = unknown>(method: string, params: unknown): Promise<T> {
12✔
79
        return this.api.call<T>(this.rid, method, params);
×
80
    }
×
81

12✔
82
    /** Called when the collection is deleted. */
12✔
83
    async dispose(): Promise<void> {
12✔
84
        // noop
×
85
    }
×
86

12✔
87
    /** See: {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/every | Array#every } */
12✔
88
    every<T extends V, ThisArg = ResCollection<V>>(predicate: (value: V, index: number, array: Array<V>) => value is T, thisArg?: ThisArg): this is Array<T>;
12✔
89
    every<ThisArg = ResCollection<V>>(predicate: (value: V, index: number, array: Array<V>) => unknown, thisArg?: ThisArg): boolean;
12✔
90
    every(predicate: (value: V, index: number, array: Array<V>) => unknown, thisArg?: unknown): boolean {
12✔
91
        return this.toArray().every(predicate, thisArg);
×
92

×
93
    }
×
94

12✔
95
    /** See: {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter | Array#filter } */
12✔
96
    filter<S extends V, ThisArg = ResCollection<V>>(predicate: (this: ThisArg, value: V, index: number, array: Array<V>) => value is S, thisArg?: ThisArg): Array<S>;
12✔
97
    filter<ThisArg = ResCollection<V>>(predicate: (this: ThisArg, value: V, index: number, array: Array<V>) => unknown, thisArg?: ThisArg): Array<V>;
12✔
98
    filter(predicate: (value: V, index: number, array: Array<V>) => unknown, thisArg?: unknown): Array<V> {
12✔
99
        return this.toArray().filter(predicate, thisArg) ;
×
100
    }
×
101

12✔
102
    /** See: {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find | Array#find } */
12✔
103
    find<S extends V, ThisArg = ResCollection<V>>(predicate: (this: ThisArg, value: V, index: number, obj: Array<V>) => value is S, thisArg?: ThisArg): S | undefined;
12✔
104
    find<ThisArg = ResCollection<V>>(predicate: (this: ThisArg, value: V, index: number, obj: Array<V>) => unknown, thisArg?: ThisArg): V | undefined;
12✔
105
    find(predicate: (value: V, index: number, obj: Array<V>) => unknown, thisArg?: unknown): V | undefined {
12✔
106
        return this.toArray().find(predicate, thisArg);
×
107
    }
×
108

12✔
109
    /** See: {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findIndex | Array#findIndex } */
12✔
110
    findIndex(predicate: (value: V, index: number, obj: Array<V>) => unknown, thisArg?: unknown): number {
12✔
111
        return this.toArray().findIndex(predicate, thisArg);
×
112
    }
×
113

12✔
114
    /**
12✔
115
     * Get the first element, or first X elements if a number is provided.
12✔
116
     * @param amount The amount of elements to get.
12✔
117
     */
12✔
118
    first(): V | undefined;
12✔
119
    first(amount: number): Array<V>;
12✔
120
    first(amount?: number): V | Array<V> | undefined {
12✔
121
        if (amount === undefined) {
×
122
            const iterable = this[Symbol.iterator]();
×
123
            return iterable.next().value;
×
124
        }
×
125

×
126
        if (amount < 0) {
×
127
            return this.last(amount * -1);
×
128
        }
×
129
        amount = Math.min(amount, this.length);
×
130

×
131
        const iterable = this[Symbol.iterator]();
×
132
        return Array.from({ length: amount }, () => iterable.next().value!);
×
133
    }
×
134

12✔
135
    get(id: string | number): V | undefined {
12✔
136
        this._hasID();
14✔
137
        return this._map![id];
14✔
138
    }
14✔
139

12✔
140
    getClient(): ResClient {
12✔
141
        return this.api;
×
142
    }
×
143

12✔
144
    getOrThrow(id: string | number): V {
12✔
145
        const item = this.get(id);
×
146
        if (item === undefined) {
×
147
            throw new TypeError(`${id} not found in ${this.rid}`);
×
148
        }
×
149

×
150
        return item;
×
151
    }
×
152

12✔
153
    indexOf(item: V): number {
12✔
154
        return this._list.indexOf(item);
×
155
    }
×
156

12✔
157
    async init(data: Array<V> = []): Promise<this> {
12✔
158
        this._list = data;
12✔
159

12✔
160
        if (this._idCallback) {
12✔
161
            this._map = {};
6✔
162
            for (const v of this._list) {
6✔
163
                const id = String(this._idCallback(v));
18✔
164
                if (["", "undefined", "null"].includes(id) || id.replaceAll(/\W/g, "") === "") {
18!
165
                    throw new Error("No id for item");
×
166
                }
×
167
                if (this._map[id]) {
18!
168
                    throw new Error(`Duplicate id - ${id}`);
×
169
                }
×
170
                this._map[id] = v;
18✔
171
            }
18✔
172
        }
6✔
173

12✔
174
        return this;
12✔
175
    }
12✔
176

12✔
177
    /**
12✔
178
     * Get the last element, or last X elements if a number is provided.
12✔
179
     * @param amount The amount of elements to get.
12✔
180
     */
12✔
181
    last(): V | undefined;
12✔
182
    last(amount: number): Array<V>;
12✔
183
    last(amount?: number): V | Array<V> | undefined {
12✔
184
        const iterator = Array.from(this._list);
×
185
        if (amount === undefined) {
×
186
            return iterator.at(-1);
×
187
        }
×
188
        if (amount < 0) {
×
189
            return this.first(amount * -1);
×
190
        }
×
191
        if (!amount) {
×
192
            return [];
×
193
        }
×
194

×
195
        return iterator.slice(-amount);
×
196
    }
×
197

12✔
198
    /** See: {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map | Array#map } */
12✔
199
    map<T>(predicate: (value: V, index: number, obj: Array<V>) => T, thisArg?: unknown): Array<T> {
12✔
200
        return this.toArray().map(predicate, thisArg);
×
201
    }
×
202

12✔
203
    off(events: string | Array<string> | null, handler: AnyFunction): this {
12✔
204
        this.api.eventBus.off(this, events, handler);
×
205
        return this;
×
206
    }
×
207

12✔
208
    on(events: string | Array<string> | null, handler: AnyFunction): this {
12✔
209
        this.api.eventBus.on(this, events, handler);
×
210
        return this;
×
211
    }
×
212

12✔
213
    /**
12✔
214
     * Pick a random element from the collection, or undefined if the collection is empty.
12✔
215
     */
12✔
216
    random(): V | undefined {
12✔
217
        if (this.empty) {
×
218
            return undefined;
×
219
        }
×
220
        const iterable = Array.from(this._list);
×
221

×
222
        return iterable[Math.floor(Math.random() * iterable.length)];
×
223
    }
×
224

12✔
225
    /** See: {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce | Array#reduce } */
12✔
226
    reduce(predicate: (previousValue: V, currentValue: V, currentIndex: number, array: Array<V>) => V): V;
12✔
227
    reduce(predicate: (previousValue: V, currentValue: V, currentIndex: number, array: Array<V>) => V, initialValue: V): V;
12✔
228
    reduce<T>(predicate: (previousValue: T, currentValue: V, currentIndex: number, array: Array<V>) => T, initialValue: T): T;
12✔
229
    reduce<T>(predicate: (previousValue: T, currentValue: V, currentIndex: number, array: Array<V>) => T, initialValue?: T): T {
12✔
230
        return this.toArray().reduce(predicate, initialValue!);
×
231
    }
×
232

12✔
233
    /** See: {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduceRight | Array#reduceRight } */
12✔
234
    reduceRight(predicate: (previousValue: V, currentValue: V, currentIndex: number, array: Array<V>) => V): V;
12✔
235
    reduceRight(predicate: (previousValue: V, currentValue: V, currentIndex: number, array: Array<V>) => V, initialValue: V): V;
12✔
236
    reduceRight<T>(predicate: (previousValue: T, currentValue: V, currentIndex: number, array: Array<V>) => T, initialValue: T): T;
12✔
237
    reduceRight<T>(predicate: (previousValue: T, currentValue: V, currentIndex: number, array: Array<V>) => T, initialValue?: T): T {
12✔
238
        return this.toArray().reduceRight(predicate, initialValue!);
×
239
    }
×
240

12✔
241
    remove(index: number): V | undefined {
12✔
242
        const item = this._list[index];
2✔
243
        if (item !== undefined) {
2✔
244
            this._list.splice(index, 1);
2✔
245

2✔
246
            if (this._idCallback) {
2✔
247
                delete this._map![this._idCallback(item)];
1✔
248
            }
1✔
249
        }
2✔
250

2✔
251
        return item;
2✔
252
    }
2✔
253

12✔
254
    resourceOff(events: string | Array<string> | null, handler: AnyFunction): this {
12✔
255
        this.api.resourceOff(this.rid, events, handler);
×
256
        return this;
×
257
    }
×
258

12✔
259
    resourceOn(events: string | Array<string> | null, handler: AnyFunction): this {
12✔
260
        this.api.resourceOn(this.rid, events, handler);
×
261
        return this;
×
262
    }
×
263

12✔
264
    /** See: {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some | Array#some } */
12✔
265
    some<ThisArg = ResCollection<V>>(predicate: (value: V, index: number, array: Array<V>) => unknown, thisArg?: ThisArg): boolean {
12✔
266
        return this.toArray().some(predicate, thisArg);
×
267
    }
×
268

12✔
269
    /** Get the values of this collection as an array. */
12✔
270
    toArray(): Array<V> {
12✔
271
        return Array.from(this._list);
×
272
    }
×
273

12✔
274
    toJSON(): Array<unknown> {
12✔
275
        return this._list.map(v => (
×
276
            v !== null && typeof v === "object" && "toJSON" in v
×
277
                ? (v as { toJSON(): AnyObject; }).toJSON()
×
278
                : v
×
279
        ));
×
280
    }
×
281
}
12✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc