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

WolferyScripting / resclient-ts / #30

25 Aug 2025 07:17PM UTC coverage: 50.87% (-0.3%) from 51.153%
#30

push

DonovanDMC
1.1.2

228 of 286 branches covered (79.72%)

Branch coverage included in aggregate %.

1584 of 3276 relevant lines covered (48.35%)

10.54 hits per line

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

72.17
/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 ResModelOptions {
1✔
9
    definition?: Record<string, PropertyDefinition>;
1✔
10
}
1✔
11
export default class ResModel {
1✔
12
    protected _definition?: Record<string, PropertyDefinition>;
38✔
13
    protected _props!: AnyObject;
38✔
14
    protected api!: ResClient;
38✔
15
    rid!: string;
38✔
16
    constructor(api: ResClient, rid: string, options?: ResModelOptions) {
38✔
17
        update(this, options ?? {}, {
38✔
18
            definition: { type: "?object", property: "_definition" }
38✔
19
        });
38✔
20
        this.p
38✔
21
            .writable("_definition")
38✔
22
            .readOnly("_props", {})
38✔
23
            .readOnly("api", api)
38✔
24
            .define("rid", false, true, true, rid);
38✔
25
    }
38✔
26

38✔
27
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
38✔
28
    protected async _listen(on: boolean): Promise<void> {
38✔
29
        // empty
11✔
30
    }
11✔
31

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

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

38✔
41
    protected get p(): Properties {
38✔
42
        return Properties.of(this);
38✔
43
    }
38✔
44

38✔
45
    get props(): AnyObject {
38✔
46
        return this._props;
×
47
    }
×
48

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

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

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

38✔
62
    getClient(): ResClient {
38✔
63
        return this.api;
×
64
    }
×
65

38✔
66
    async init(data?: AnyObject): Promise<this> {
38✔
67
        if (data) {
11✔
68
            this.update(data);
11✔
69
        }
11✔
70

11✔
71
        await this._listen(true);
11✔
72
        return this;
11✔
73
    }
11✔
74

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

38✔
80
    off(events: string | Array<string> | null, handler: AnyFunction): this {
38✔
81
        this.api.eventBus.off(this, events, handler);
×
82
        return this;
×
83
    }
×
84

38✔
85
    on(events: string | Array<string> | null, handler: AnyFunction): this {
38✔
86
        this.api.eventBus.on(this, events, handler);
×
87
        return this;
×
88
    }
×
89

38✔
90
    resourceOff(events: string | Array<string> | null, handler: AnyFunction): this {
38✔
91
        this.api.resourceOff(this.rid, events, handler);
×
92
        return this;
×
93
    }
×
94

38✔
95
    resourceOn(events: string | Array<string> | null, handler: AnyFunction): this {
38✔
96
        this.api.resourceOn(this.rid, events, handler);
×
97
        return this;
×
98
    }
×
99

38✔
100
    // TODO: needs better typing
38✔
101
    setModel(props: AnyObject): Promise<unknown> {
38✔
102
        return this.api.setModel(this.rid, props);
×
103
    }
×
104

38✔
105
    toJSON(): AnyObject {
38✔
106
        const o = this._definition
4✔
107
            ? copy(this._props, this._definition)
4!
108
            : ({ ...this._props });
4✔
109
        // eslint-disable-next-line guard-for-in
4✔
110
        for (const k in o) {
4✔
111
            const v = o[k];
6✔
112
            if (typeof v === "object" && v !== null && "toJSON" in v) {
6✔
113
                o[k] = (v as { toJSON(): object; }).toJSON();
1✔
114
            }
1✔
115
        }
6✔
116
        return o;
4✔
117
    }
4✔
118

38✔
119
    /** Undo preventing this model from being unsubscribed. */
38✔
120
    unkeep(): void {
38✔
121
        this.cacheItem.unkeep();
×
122
    }
×
123

38✔
124
    update(props: AnyObject, reset = false): AnyObject | null {
38✔
125
        if (!props) {
15✔
126
            return null;
1✔
127
        }
1✔
128

14✔
129
        let changed: AnyObject | null = null, v: unknown, promote: boolean;
14✔
130

14✔
131
        try {
14✔
132
            const p = this._props;
14✔
133

14✔
134
            if (reset) {
15!
135
                props = { ...props };
×
136
                for (const k in p) {
×
137
                    if (!Object.hasOwn(props, k)) {
×
138
                        props[k] = undefined;
×
139
                    }
×
140
                }
×
141
            }
×
142

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

14✔
181
        return changed;
14✔
182
    }
14✔
183
}
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