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

WolferyScripting / resclient-ts / #13

28 Jul 2025 04:45PM UTC coverage: 51.039% (+0.3%) from 50.726%
#13

push

DonovanDMC
stricter types

187 of 240 branches covered (77.92%)

Branch coverage included in aggregate %.

239 of 290 new or added lines in 12 files covered. (82.41%)

8 existing lines in 5 files now uncovered.

1188 of 2454 relevant lines covered (48.41%)

9.96 hits per line

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

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

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

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

12✔
28
    /** If this collection is empty. */
12✔
29
    get empty(): boolean {
12✔
30
        return this.length === 0;
×
31
    }
×
32

12✔
33
    get length(): number {
12✔
34
        return this._list.length;
×
35
    }
×
36

12✔
37
    get list(): Array<V> {
12✔
38
        return this._list;
×
39
    }
×
40

12✔
41
    [Symbol.iterator](): Iterator<V, undefined> {
12✔
NEW
42
        return this._list[Symbol.iterator]();
×
UNCOV
43
    }
×
44

12✔
45
    add(item: V, index: number): void {
12✔
46
        this._list.splice(index, 0, item);
3✔
47

3✔
48
        if (this._idCallback) {
3✔
49
            const id = String(this._idCallback(item));
2✔
50
            if (["", "undefined", "null"].includes(id) || id.replaceAll(/\W/g, "") === "") {
2!
51
                console.debug(item);
×
52
                throw new Error("No id for item");
×
53
            }
×
54
            if (this._map![id]) {
2✔
55
                throw new Error(`Duplicate id - ${id}`);
1✔
56
            }
1✔
57
            this._map![id] = item;
1✔
58
        }
1✔
59
    }
3✔
60

12✔
61
    at(index: number): V | undefined {
12✔
62
        return this._list[index];
14✔
63
    }
14✔
64

12✔
65
    auth<T = unknown>(method: string, params: unknown): Promise<T> {
12✔
NEW
66
        return this.api.authenticate<T>(this.rid, method, params);
×
UNCOV
67
    }
×
68

12✔
69
    call<T = unknown>(method: string, params: unknown): Promise<T> {
12✔
NEW
70
        return this.api.call<T>(this.rid, method, params);
×
UNCOV
71
    }
×
72

12✔
73
    /** See: {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/every | Array#every } */
12✔
74
    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✔
75
    every<ThisArg = ResCollection<V>>(predicate: (value: V, index: number, array: Array<V>) => unknown, thisArg?: ThisArg): boolean;
12✔
76
    every(predicate: (value: V, index: number, array: Array<V>) => unknown, thisArg?: unknown): boolean {
12✔
77
        return this.toArray().every(predicate, thisArg);
×
78

×
79
    }
×
80

12✔
81
    /** See: {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter | Array#filter } */
12✔
82
    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✔
83
    filter<ThisArg = ResCollection<V>>(predicate: (this: ThisArg, value: V, index: number, array: Array<V>) => unknown, thisArg?: ThisArg): Array<V>;
12✔
84
    filter(predicate: (value: V, index: number, array: Array<V>) => unknown, thisArg?: unknown): Array<V> {
12✔
85
        return this.toArray().filter(predicate, thisArg) ;
×
86
    }
×
87

12✔
88
    /** See: {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find | Array#find } */
12✔
89
    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✔
90
    find<ThisArg = ResCollection<V>>(predicate: (this: ThisArg, value: V, index: number, obj: Array<V>) => unknown, thisArg?: ThisArg): V | undefined;
12✔
91
    find(predicate: (value: V, index: number, obj: Array<V>) => unknown, thisArg?: unknown): V | undefined {
12✔
92
        return this.toArray().find(predicate, thisArg);
×
93
    }
×
94

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

12✔
100
    /**
12✔
101
     * Get the first element, or first X elements if a number is provided.
12✔
102
     * @param amount The amount of elements to get.
12✔
103
     */
12✔
104
    first(): V | undefined;
12✔
105
    first(amount: number): Array<V>;
12✔
106
    first(amount?: number): V | Array<V> | undefined {
12✔
107
        if (amount === undefined) {
×
108
            const iterable = this[Symbol.iterator]();
×
109
            return iterable.next().value;
×
110
        }
×
111

×
112
        if (amount < 0) {
×
113
            return this.last(amount * -1);
×
114
        }
×
115
        amount = Math.min(amount, this.length);
×
116

×
117
        const iterable = this[Symbol.iterator]();
×
NEW
118
        return Array.from({ length: amount }, () => iterable.next().value!);
×
119
    }
×
120

12✔
121
    get(id: string | number): V | undefined {
12✔
122
        this._hasID();
14✔
123
        return this._map![id];
14✔
124
    }
14✔
125

12✔
126
    getClient(): C {
12✔
127
        return this.api;
×
128
    }
×
129

12✔
130
    indexOf(item: V): number {
12✔
131
        return this._list.indexOf(item);
×
132
    }
×
133

12✔
134
    init(data: Array<V> = []): this {
12✔
135
        this._list = data;
12✔
136

12✔
137
        if (this._idCallback) {
12✔
138
            this._map = {};
6✔
139
            for (const v of this._list) {
6✔
140
                const id = String(this._idCallback(v));
18✔
141
                if (["", "undefined", "null"].includes(id) || id.replaceAll(/\W/g, "") === "") {
18!
142
                    throw new Error("No id for item");
×
143
                }
×
144
                if (this._map[id]) {
18!
145
                    throw new Error(`Duplicate id - ${id}`);
×
146
                }
×
147
                this._map[id] = v;
18✔
148
            }
18✔
149
        }
6✔
150

12✔
151
        return this;
12✔
152
    }
12✔
153

12✔
154
    /**
12✔
155
     * Get the last element, or last X elements if a number is provided.
12✔
156
     * @param amount The amount of elements to get.
12✔
157
     */
12✔
158
    last(): V | undefined;
12✔
159
    last(amount: number): Array<V>;
12✔
160
    last(amount?: number): V | Array<V> | undefined {
12✔
161
        const iterator = Array.from(this._list);
×
162
        if (amount === undefined) {
×
163
            return iterator.at(-1);
×
164
        }
×
165
        if (amount < 0) {
×
166
            return this.first(amount * -1);
×
167
        }
×
168
        if (!amount) {
×
169
            return [];
×
170
        }
×
171

×
172
        return iterator.slice(-amount);
×
173
    }
×
174

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

12✔
180
    off(events: string | Array<string> | null, handler: AnyFunction): this {
12✔
181
        this.api.resourceOff(this.rid, events, handler);
×
182
        return this;
×
183
    }
×
184

12✔
185
    on(events: string | Array<string> | null, handler: AnyFunction): this {
12✔
186
        this.api.resourceOn(this.rid, events, handler);
×
187
        return this;
×
188
    }
×
189

12✔
190
    /**
12✔
191
     * Pick a random element from the collection, or undefined if the collection is empty.
12✔
192
     */
12✔
193
    random(): V | undefined {
12✔
194
        if (this.empty) {
×
195
            return undefined;
×
196
        }
×
197
        const iterable = Array.from(this._list);
×
198

×
199
        return iterable[Math.floor(Math.random() * iterable.length)];
×
200
    }
×
201

12✔
202
    /** See: {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce | Array#reduce } */
12✔
203
    reduce(predicate: (previousValue: V, currentValue: V, currentIndex: number, array: Array<V>) => V): V;
12✔
204
    reduce(predicate: (previousValue: V, currentValue: V, currentIndex: number, array: Array<V>) => V, initialValue: V): V;
12✔
205
    reduce<T>(predicate: (previousValue: T, currentValue: V, currentIndex: number, array: Array<V>) => T, initialValue: T): T;
12✔
206
    reduce<T>(predicate: (previousValue: T, currentValue: V, currentIndex: number, array: Array<V>) => T, initialValue?: T): T {
12✔
207
        return this.toArray().reduce(predicate, initialValue!);
×
208
    }
×
209

12✔
210
    /** See: {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduceRight | Array#reduceRight } */
12✔
211
    reduceRight(predicate: (previousValue: V, currentValue: V, currentIndex: number, array: Array<V>) => V): V;
12✔
212
    reduceRight(predicate: (previousValue: V, currentValue: V, currentIndex: number, array: Array<V>) => V, initialValue: V): V;
12✔
213
    reduceRight<T>(predicate: (previousValue: T, currentValue: V, currentIndex: number, array: Array<V>) => T, initialValue: T): T;
12✔
214
    reduceRight<T>(predicate: (previousValue: T, currentValue: V, currentIndex: number, array: Array<V>) => T, initialValue?: T): T {
12✔
215
        return this.toArray().reduceRight(predicate, initialValue!);
×
216
    }
×
217

12✔
218
    remove(index: number): V | undefined {
12✔
219
        const item = this._list[index];
2✔
220
        if (item !== undefined) {
2✔
221
            this._list.splice(index, 1);
2✔
222

2✔
223
            if (this._idCallback) {
2✔
224
                delete this._map![this._idCallback(item)];
1✔
225
            }
1✔
226
        }
2✔
227

2✔
228
        return item;
2✔
229
    }
2✔
230

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

12✔
236
    /** Get the values of this collection as an array. */
12✔
237
    toArray(): Array<V> {
12✔
238
        return Array.from(this._list);
×
239
    }
×
240

12✔
241
    toJSON(): Array<unknown> {
12✔
242
        return this._list.map(v => (
×
243
            v !== null && typeof v === "object" && "toJSON" in v
×
NEW
244
                ? (v as { toJSON(): AnyObject; }).toJSON()
×
245
                : v
×
246
        ));
×
247
    }
×
248
}
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