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

WolferyScripting / resclient-ts / #33

27 Aug 2025 09:08PM UTC coverage: 51.192% (+0.3%) from 50.87%
#33

push

DonovanDMC
1.1.4

230 of 292 branches covered (78.77%)

Branch coverage included in aggregate %.

1660 of 3400 relevant lines covered (48.82%)

10.5 hits per line

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

72.4
/lib/models/ResModel.ts
1
import type ResClient from "./ResClient.js";
1✔
2
import type CacheItem from "./CacheItem.js";
1✔
3
import { copy, equal, update, type PropertyDefinition } from "../includes/utils/obj.js";
1✔
4
import Properties from "../util/Properties.js";
1✔
5
import type { AnyFunction, AnyObject } from "../util/types.js";
1✔
6
import { UpdateError } from "../util/errors.js";
1✔
7

1✔
8
export interface ResModelResourceEvents<T extends AnyObject= AnyObject> {
1✔
9
    change: [data: Partial<T>];
1✔
10
}
1✔
11
export interface ResModelOptions {
1✔
12
    definition?: Record<string, PropertyDefinition>;
1✔
13
}
1✔
14
export default class ResModel<P extends AnyObject = AnyObject, ResourceEvents extends { [K in keyof ResourceEvents]: Array<unknown> } = ResModelResourceEvents<P>, ModelEvents extends { [K in keyof ModelEvents]: Array<unknown> } = Record<string, Array<unknown>>> {
1✔
15
    protected _definition?: Record<string, PropertyDefinition>;
38✔
16
    protected _props!: P;
38✔
17
    protected api!: ResClient;
38✔
18
    rid!: string;
38✔
19
    constructor(api: ResClient, rid: string, options: ResModelOptions = {}) {
38✔
20
        update<ResModelOptions, this>(this, options, {
38✔
21
            definition: { type: "?object", property: "_definition" }
38✔
22
        });
38✔
23
        this.p
38✔
24
            .writable("_definition")
38✔
25
            .readOnly("_props", {})
38✔
26
            .readOnly("api", api)
38✔
27
            .define("rid", false, true, true, rid);
38✔
28
    }
38✔
29

38✔
30
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
38✔
31
    protected async _listen(on: boolean): Promise<void> {
38✔
32
        // empty
×
33
    }
×
34

38✔
35
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
38✔
36
    protected _shouldPromoteKey(key: string, value: unknown): boolean {
38✔
37
        return true;
17✔
38
    }
17✔
39

38✔
40
    protected get cacheItem(): CacheItem<ResModel> {
38✔
41
        return this.getClient().cache[this.rid] as CacheItem<ResModel>;
×
42
    }
×
43

38✔
44
    protected get p(): Properties {
38✔
45
        return Properties.of(this);
38✔
46
    }
38✔
47

38✔
48
    get props(): P {
38✔
49
        return this._props;
×
50
    }
×
51

38✔
52
    auth<T = unknown>(method: string, params: unknown): Promise<T> {
38✔
53
        return this.api.authenticate<T>(this.rid, method, params);
×
54
    }
×
55

38✔
56
    call<T = unknown>(method: string, params?: unknown): Promise<T> {
38✔
57
        return this.api.call<T>(this.rid, method, params);
×
58
    }
×
59

38✔
60
    /** Called when the model is unsubscribed. */
38✔
61
    async dispose(): Promise<void> {
38✔
62
        await this._listen(false);
×
63
    }
×
64

38✔
65
    getClient(): ResClient {
38✔
66
        return this.api;
×
67
    }
×
68

38✔
69
    async init(data?: P): Promise<this> {
38✔
70
        if (data) {
11✔
71
            this.update(data);
11✔
72
        }
11✔
73

11✔
74
        return this;
11✔
75
    }
11✔
76

38✔
77
    /** Prevent this model from being unsubscribed. */
38✔
78
    keep(): void {
38✔
79
        this.cacheItem.keep();
×
80
    }
×
81

38✔
82
    off<K extends keyof ModelEvents>(event: K, handler: (...args: ModelEvents[K]) => void): this;
38✔
83
    off(events: string | Array<string> | null, handler: AnyFunction): this;
38✔
84
    off(events: string | Array<string> | null, handler: AnyFunction): this {
38✔
85
        this.api.eventBus.off(this, events, handler);
×
86
        return this;
×
87
    }
×
88

38✔
89
    on<K extends keyof ModelEvents>(event: K, handler: (...args: ModelEvents[K]) => void): this;
38✔
90
    on(events: string | Array<string> | null, handler: AnyFunction): this;
38✔
91
    on(events: string | Array<string> | null, handler: AnyFunction): this {
38✔
92
        this.api.eventBus.on(this, events, handler);
×
93
        return this;
×
94
    }
×
95

38✔
96
    resourceOff<K extends keyof ResourceEvents>(event: K, handler: (...args: ResourceEvents[K]) => void): this;
38✔
97
    resourceOff(events: string | Array<string> | null, handler: AnyFunction): this;
38✔
98
    resourceOff(events: string | Array<string> | null, handler: AnyFunction): this {
38✔
99
        this.api.resourceOff(this.rid, events, handler);
×
100
        return this;
×
101
    }
×
102

38✔
103
    resourceOn<K extends keyof ResourceEvents>(event: K, handler: (...args: ResourceEvents[K]) => void): this;
38✔
104
    resourceOn(events: string | Array<string> | null, handler: AnyFunction): this;
38✔
105
    resourceOn(events: string | Array<string> | null, handler: AnyFunction): this {
38✔
106
        this.api.resourceOn(this.rid, events, handler);
×
107
        return this;
×
108
    }
×
109

38✔
110
    // TODO: needs better typing
38✔
111
    setModel(props: AnyObject): Promise<unknown> {
38✔
112
        return this.api.setModel(this.rid, props);
×
113
    }
×
114

38✔
115
    toJSON(): AnyObject {
38✔
116
        const o = this._definition
4✔
117
            ? copy(this._props, this._definition)
4!
118
            : ({ ...this._props });
4✔
119
        // eslint-disable-next-line guard-for-in
4✔
120
        for (const k in o) {
4✔
121
            const v = o[k];
6✔
122
            if (typeof v === "object" && v !== null && "toJSON" in v) {
6✔
123
                o[k] = (v as { toJSON(): object; }).toJSON() as never;
1✔
124
            }
1✔
125
        }
6✔
126
        return o;
4✔
127
    }
4✔
128

38✔
129
    /** Undo preventing this model from being unsubscribed. */
38✔
130
    unkeep(): void {
38✔
131
        this.cacheItem.unkeep();
×
132
    }
×
133

38✔
134
    update(props: Partial<P>, reset = false): Partial<P> | null {
38✔
135
        if (!props) {
15✔
136
            return null;
1✔
137
        }
1✔
138

14✔
139
        let changed: Partial<P> | null = null, v: unknown, promote: boolean;
14✔
140

14✔
141
        try {
14✔
142
            const p = this._props;
14✔
143

14✔
144
            if (reset) {
15!
145
                props = { ...props };
×
146
                for (const k in p) {
×
147
                    if (!Object.hasOwn(props, k)) {
×
148
                        props[k] = undefined;
×
149
                    }
×
150
                }
×
151
            }
×
152

14✔
153
            if (this._definition) {
15!
154
                changed = update(p, props, this._definition);
×
155
                for (const key in changed) {
×
156
                    if ((Object.hasOwn(this, key) || !(this as AnyObject)[key]) && key[0] !== "_" && Object.getOwnPropertyDescriptor(this, key)?.writable !== false) {
×
157
                        v = p[key];
×
158
                        if (v === undefined) {
×
159
                            delete (this as AnyObject)[key];
×
160
                        } else {
×
161
                            (this as AnyObject)[key] = v;
×
162
                        }
×
163
                    }
×
164
                }
×
165
            } else {
15✔
166
            // eslint-disable-next-line guard-for-in
14✔
167
                for (const key in props) {
14✔
168
                    v = props[key];
24✔
169
                    promote = (Object.hasOwn(this, key) || !(this as AnyObject)[key]) && key[0] !== "_" && Object.getOwnPropertyDescriptor(this, key)?.writable !== false && this._shouldPromoteKey(key, v);
24✔
170
                    if (!equal(p[key], v)) {
24✔
171
                        changed ||= {};
24✔
172
                        changed[key] = p[key];
24✔
173
                        if (v === undefined) {
24!
174
                            delete p[key];
×
175
                            if (promote) {
×
176
                                delete (this as AnyObject)[key];
×
177
                            }
×
178
                        } else {
24✔
179
                            p[key] = v as never;
24✔
180
                            if (promote) {
24✔
181
                                (this as AnyObject)[key] = v;
17✔
182
                            }
17✔
183
                        }
24✔
184
                    }
24✔
185
                }
24✔
186
            }
14✔
187
        } catch (error) {
15!
188
            throw new UpdateError(this.rid, error as Error);
×
189
        }
×
190

14✔
191
        return changed;
14✔
192
    }
14✔
193
}
38✔
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