@sonoransoftware/sonoran.js 1.0.34 → 1.0.36

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.
Files changed (44) hide show
  1. package/.github/workflows/auto-pr-on-branch-push.yml +89 -0
  2. package/.github/workflows/codex_instructions.md +24 -0
  3. package/.github/workflows/push-pr-nudge-codex.yml +50 -0
  4. package/dist/constants.d.ts +242 -1
  5. package/dist/constants.js +1 -0
  6. package/dist/index.d.ts +1 -1
  7. package/dist/instance/Instance.d.ts +6 -0
  8. package/dist/instance/Instance.js +27 -0
  9. package/dist/instance/instance.types.d.ts +3 -0
  10. package/dist/libs/rest/src/lib/REST.d.ts +2 -1
  11. package/dist/libs/rest/src/lib/REST.js +118 -0
  12. package/dist/libs/rest/src/lib/RequestManager.d.ts +2 -0
  13. package/dist/libs/rest/src/lib/RequestManager.js +209 -0
  14. package/dist/libs/rest/src/lib/errors/RateLimitError.js +19 -1
  15. package/dist/libs/rest/src/lib/utils/constants.d.ts +108 -24
  16. package/dist/libs/rest/src/lib/utils/constants.js +118 -2
  17. package/dist/managers/CADActiveUnitsManager.d.ts +3 -0
  18. package/dist/managers/CADActiveUnitsManager.js +16 -6
  19. package/dist/managers/CADManager.d.ts +223 -0
  20. package/dist/managers/CADManager.js +513 -4
  21. package/dist/managers/CADServerManager.d.ts +11 -0
  22. package/dist/managers/CADServerManager.js +56 -13
  23. package/dist/managers/CMSManager.d.ts +78 -0
  24. package/dist/managers/CMSManager.js +213 -3
  25. package/dist/managers/CMSServerManager.d.ts +8 -0
  26. package/dist/managers/CMSServerManager.js +61 -18
  27. package/dist/managers/RadioManager.d.ts +55 -0
  28. package/dist/managers/RadioManager.js +224 -0
  29. package/package.json +1 -1
  30. package/readme.md +294 -12
  31. package/src/constants.ts +281 -1
  32. package/src/index.ts +42 -1
  33. package/src/instance/Instance.ts +30 -1
  34. package/src/instance/instance.types.ts +4 -1
  35. package/src/libs/rest/src/lib/REST.ts +117 -1
  36. package/src/libs/rest/src/lib/RequestManager.ts +229 -10
  37. package/src/libs/rest/src/lib/errors/RateLimitError.ts +20 -2
  38. package/src/libs/rest/src/lib/utils/constants.ts +223 -26
  39. package/src/managers/CADActiveUnitsManager.ts +19 -6
  40. package/src/managers/CADManager.ts +574 -4
  41. package/src/managers/CADServerManager.ts +59 -15
  42. package/src/managers/CMSManager.ts +196 -2
  43. package/src/managers/CMSServerManager.ts +65 -21
  44. package/src/managers/RadioManager.ts +187 -0
@@ -1,6 +1,26 @@
1
1
  import { Instance } from '../instance/Instance';
2
2
  import { CADSubscriptionVersionEnum } from '../constants';
3
3
  import { APIError, DefaultCADRestOptions, REST } from '../libs/rest/src';
4
+ import type {
5
+ RESTTypedAPIDataStructs,
6
+ CADPenalCodeStruct,
7
+ CADSetAPIIDStruct,
8
+ CADNewEditRecordOptionOneStruct,
9
+ CADNewEditRecordOptionTwoStruct,
10
+ CADLookupByIntStruct,
11
+ CADLookupStruct,
12
+ CADModifyAccountPermsStruct,
13
+ CADKickBanUserStruct,
14
+ CADSetPostalStruct,
15
+ CADModifyIdentifierStruct,
16
+ CADAddBlipStruct,
17
+ CADModifyBlipStruct,
18
+ CADGetCallsStruct,
19
+ CADGetActiveUnitsStruct,
20
+ CADNewDispatchStruct,
21
+ CADStreetSignStruct,
22
+ CADUnitLocationStruct
23
+ } from '../libs/rest/src';
4
24
  import { BaseManager } from './BaseManager';
5
25
  import * as globalTypes from '../constants';
6
26
  import type { Mutable } from '../constants';
@@ -26,9 +46,9 @@ export class CADManager extends BaseManager {
26
46
  protected async buildManager(instance: Instance) {
27
47
  const mutableThis = this as Mutable<CADManager>;
28
48
  try {
29
- const versionResp: any = await this.rest?.request('GET_VERSION');
30
- mutableThis.version = Number.parseInt(versionResp.replace(/(^\d+)(.+$)/i,'$1'));
31
- if (this.version >= globalTypes.CADSubscriptionVersionEnum.STANDARD) {
49
+ const version = await this.getVersion();
50
+ mutableThis.version = version;
51
+ if (version >= globalTypes.CADSubscriptionVersionEnum.STANDARD) {
32
52
  this.servers = new CADServerManager(instance, this);
33
53
  }
34
54
  instance.isCADSuccessful = true;
@@ -40,6 +60,34 @@ export class CADManager extends BaseManager {
40
60
  }
41
61
  }
42
62
 
63
+ private getRest(): REST {
64
+ if (!this.rest) {
65
+ throw new Error('CAD REST client is not initialized.');
66
+ }
67
+ return this.rest;
68
+ }
69
+
70
+ private async executeCadRequest<K extends keyof RESTTypedAPIDataStructs, T = unknown>(type: K, ...args: RESTTypedAPIDataStructs[K]): Promise<globalTypes.CADStandardResponse<T>> {
71
+ try {
72
+ const response = await this.getRest().request(type, ...args);
73
+ return { success: true, data: response as T };
74
+ } catch (err) {
75
+ if (err instanceof APIError) {
76
+ return { success: false, reason: err.response };
77
+ }
78
+ throw err;
79
+ }
80
+ }
81
+
82
+ /**
83
+ * Retrieves the community's CAD subscription version.
84
+ */
85
+ public async getVersion(): Promise<number> {
86
+ const versionResp: any = await this.rest?.request('GET_VERSION');
87
+ const versionString = String(versionResp);
88
+ return Number.parseInt(versionString.replace(/(^\d+)(.+$)/i, '$1'), 10);
89
+ }
90
+
43
91
  /**
44
92
  * Gets a community account by `accId` or `apiId`.
45
93
  * @param {Object} params The object that contains parameters to get a community account.
@@ -61,4 +109,526 @@ export class CADManager extends BaseManager {
61
109
  }
62
110
  });
63
111
  }
64
- }
112
+
113
+ /**
114
+ * Updates the CAD clock to match in-game time.
115
+ */
116
+ public async setClockTime(data: { serverId: number; currentUtc: string; currentGame: string; secondsPerHour: number }): Promise<globalTypes.CADSetClockTimePromiseResult> {
117
+ return new Promise(async (resolve, reject) => {
118
+ try {
119
+ const response: any = await this.rest?.request('SET_CLOCK', data);
120
+ resolve({ success: true, data: response });
121
+ } catch (err) {
122
+ if (err instanceof APIError) {
123
+ resolve({ success: false, reason: err.response });
124
+ } else {
125
+ reject(err);
126
+ }
127
+ }
128
+ });
129
+ }
130
+
131
+ /**
132
+ * Adds accounts to the community via the join community endpoint.
133
+ * NOTE: This endpoint is intended for internal CMS use and requires an internal key.
134
+ */
135
+ public async joinCommunity(internalKey: string, accounts: string | { account: string } | Array<string | { account: string }>): Promise<globalTypes.CADJoinCommunityPromiseResult> {
136
+ if (!internalKey) {
137
+ throw new Error('internalKey is required to join a community.');
138
+ }
139
+ const normalizedAccounts = this.normalizeAccountEntries(accounts);
140
+ if (normalizedAccounts.length === 0) {
141
+ throw new Error('At least one account must be provided to join the community.');
142
+ }
143
+ return new Promise(async (resolve, reject) => {
144
+ try {
145
+ const response: any = await this.rest?.request('JOIN_COMMUNITY', { internalKey, accounts: normalizedAccounts });
146
+ resolve({ success: true, data: response });
147
+ } catch (err) {
148
+ if (err instanceof APIError) {
149
+ resolve({ success: false, reason: err.response });
150
+ } else {
151
+ reject(err);
152
+ }
153
+ }
154
+ });
155
+ }
156
+
157
+ /**
158
+ * Removes accounts from the community via the leave community endpoint.
159
+ * NOTE: This endpoint is intended for internal CMS use and requires an internal key.
160
+ */
161
+ public async leaveCommunity(internalKey: string, accounts: string | { account: string } | Array<string | { account: string }>): Promise<globalTypes.CADLeaveCommunityPromiseResult> {
162
+ if (!internalKey) {
163
+ throw new Error('internalKey is required to leave a community.');
164
+ }
165
+ const normalizedAccounts = this.normalizeAccountEntries(accounts);
166
+ if (normalizedAccounts.length === 0) {
167
+ throw new Error('At least one account must be provided to leave the community.');
168
+ }
169
+ return new Promise(async (resolve, reject) => {
170
+ try {
171
+ const response: any = await this.rest?.request('LEAVE_COMMUNITY', { internalKey, accounts: normalizedAccounts });
172
+ resolve({ success: true, data: response });
173
+ } catch (err) {
174
+ if (err instanceof APIError) {
175
+ resolve({ success: false, reason: err.response });
176
+ } else {
177
+ reject(err);
178
+ }
179
+ }
180
+ });
181
+ }
182
+
183
+ /**
184
+ * Updates the penal code configuration for the community.
185
+ */
186
+ public async setPenalCodes(codes: CADPenalCodeStruct[]): Promise<globalTypes.CADStandardResponse> {
187
+ if (!Array.isArray(codes) || codes.length === 0) {
188
+ throw new Error('codes array must include at least one penal code entry.');
189
+ }
190
+ return this.executeCadRequest('SET_PENAL_CODES', codes);
191
+ }
192
+
193
+ /**
194
+ * Assigns API IDs to a community account.
195
+ */
196
+ public async setAccountApiIds(data: CADSetAPIIDStruct): Promise<globalTypes.CADStandardResponse> {
197
+ if (!data?.username || !Array.isArray(data.apiIds)) {
198
+ throw new Error('username and apiIds are required to set account API IDs.');
199
+ }
200
+ return this.executeCadRequest('SET_API_ID', data);
201
+ }
202
+
203
+ /**
204
+ * Retrieves record templates filtered by a specific record type.
205
+ */
206
+ public async getRecordTemplates(recordTypeId?: number): Promise<globalTypes.CADStandardResponse> {
207
+ if (typeof recordTypeId === 'number') {
208
+ return this.executeCadRequest('GET_TEMPLATES', recordTypeId);
209
+ }
210
+ return this.executeCadRequest('GET_TEMPLATES');
211
+ }
212
+
213
+ /**
214
+ * Creates a new record entry.
215
+ */
216
+ public async createRecord(data: CADNewEditRecordOptionOneStruct | CADNewEditRecordOptionTwoStruct): Promise<globalTypes.CADStandardResponse> {
217
+ return this.executeCadRequest('NEW_RECORD', data);
218
+ }
219
+
220
+ /**
221
+ * Updates an existing record entry.
222
+ */
223
+ public async updateRecord(data: CADNewEditRecordOptionOneStruct | CADNewEditRecordOptionTwoStruct): Promise<globalTypes.CADStandardResponse> {
224
+ return this.executeCadRequest('EDIT_RECORD', data);
225
+ }
226
+
227
+ /**
228
+ * Removes a record by its identifier.
229
+ */
230
+ public async removeRecord(id: number): Promise<globalTypes.CADStandardResponse> {
231
+ if (!Number.isInteger(id)) {
232
+ throw new Error('id must be an integer when removing a record.');
233
+ }
234
+ return this.executeCadRequest('REMOVE_RECORD', id);
235
+ }
236
+
237
+ /**
238
+ * Performs an internal lookup using the CAD lookup by identifier endpoint.
239
+ */
240
+ public async lookupByInt(data: CADLookupByIntStruct): Promise<globalTypes.CADStandardResponse> {
241
+ if (!data || !('searchType' in data) || !('value' in data)) {
242
+ throw new Error('searchType and value are required for lookupByInt.');
243
+ }
244
+ return this.executeCadRequest('LOOKUP_INT', data);
245
+ }
246
+
247
+ /**
248
+ * Executes a CAD lookup using first/last name, plate, or other values.
249
+ */
250
+ public async lookupRecords(data: CADLookupStruct): Promise<globalTypes.CADStandardResponse> {
251
+ return this.executeCadRequest('LOOKUP', data);
252
+ }
253
+
254
+ /**
255
+ * Checks whether the provided API ID exists.
256
+ */
257
+ public async checkApiId(apiId: string): Promise<globalTypes.CADStandardResponse> {
258
+ if (!apiId) {
259
+ throw new Error('apiId is required to check for existence.');
260
+ }
261
+ return this.executeCadRequest('CHECK_APIID', apiId);
262
+ }
263
+
264
+ /**
265
+ * Applies a permission key to an account.
266
+ */
267
+ public async applyPermissionKey(apiId: string | undefined, permissionKey: string): Promise<globalTypes.CADStandardResponse> {
268
+ if (!permissionKey) {
269
+ throw new Error('permissionKey is required when applying a permission key.');
270
+ }
271
+ return this.executeCadRequest('APPLY_PERMISSION_KEY', apiId, permissionKey);
272
+ }
273
+
274
+ /**
275
+ * Sets account permissions for a CAD user.
276
+ */
277
+ public async setAccountPermissions(data: CADModifyAccountPermsStruct): Promise<globalTypes.CADStandardResponse> {
278
+ if (!data || (!Array.isArray(data.add) && !Array.isArray(data.remove))) {
279
+ throw new Error('At least one permission change is required.');
280
+ }
281
+ return this.executeCadRequest('SET_ACCOUNT_PERMISSIONS', data);
282
+ }
283
+
284
+ /**
285
+ * Bans or kicks a CAD user.
286
+ */
287
+ public async banUser(data: CADKickBanUserStruct): Promise<globalTypes.CADStandardResponse> {
288
+ if (!data?.apiId) {
289
+ throw new Error('apiId is required when banning or kicking a user.');
290
+ }
291
+ return this.executeCadRequest('BAN_USER', data);
292
+ }
293
+
294
+ /**
295
+ * Verifies a secret string against CAD configuration.
296
+ */
297
+ public async verifySecret(secret: string): Promise<globalTypes.CADStandardResponse> {
298
+ if (!secret) {
299
+ throw new Error('secret is required to verify.');
300
+ }
301
+ return this.executeCadRequest('VERIFY_SECRET', secret);
302
+ }
303
+
304
+ /**
305
+ * Authorizes street sign changes for a CAD server.
306
+ */
307
+ public async authorizeStreetSigns(serverId: number): Promise<globalTypes.CADStandardResponse> {
308
+ if (!Number.isInteger(serverId)) {
309
+ throw new Error('serverId must be an integer when authorizing street signs.');
310
+ }
311
+ return this.executeCadRequest('AUTH_STREETSIGNS', serverId);
312
+ }
313
+
314
+ /**
315
+ * Replaces the community postal list.
316
+ */
317
+ public async setPostals(data: CADSetPostalStruct[]): Promise<globalTypes.CADStandardResponse> {
318
+ if (!Array.isArray(data) || data.length === 0) {
319
+ throw new Error('data must include at least one postal entry.');
320
+ }
321
+ return this.executeCadRequest('SET_POSTALS', data);
322
+ }
323
+
324
+ /**
325
+ * Sends a photo to CAD for the provided API ID.
326
+ */
327
+ public async sendPhoto(apiId: string | undefined, url: string): Promise<globalTypes.CADStandardResponse> {
328
+ if (!url) {
329
+ throw new Error('url is required to send a CAD photo.');
330
+ }
331
+ return this.executeCadRequest('SEND_PHOTO', apiId, url);
332
+ }
333
+
334
+ /**
335
+ * Retrieves characters belonging to an API ID.
336
+ */
337
+ public async getCharacters(apiId: string): Promise<globalTypes.CADStandardResponse> {
338
+ if (!apiId) {
339
+ throw new Error('apiId is required to fetch characters.');
340
+ }
341
+ return this.executeCadRequest('GET_CHARACTERS', apiId);
342
+ }
343
+
344
+ /**
345
+ * Creates a new civilian character.
346
+ */
347
+ public async createCharacter(data: CADNewEditRecordOptionOneStruct | CADNewEditRecordOptionTwoStruct): Promise<globalTypes.CADStandardResponse> {
348
+ return this.executeCadRequest('NEW_CHARACTER', data);
349
+ }
350
+
351
+ /**
352
+ * Updates an existing civilian character.
353
+ */
354
+ public async updateCharacter(data: CADNewEditRecordOptionOneStruct | CADNewEditRecordOptionTwoStruct): Promise<globalTypes.CADStandardResponse> {
355
+ return this.executeCadRequest('EDIT_CHARACTER', data);
356
+ }
357
+
358
+ /**
359
+ * Removes a civilian character by identifier.
360
+ */
361
+ public async removeCharacter(id: number): Promise<globalTypes.CADStandardResponse> {
362
+ if (!Number.isInteger(id)) {
363
+ throw new Error('id must be an integer when removing a character.');
364
+ }
365
+ return this.executeCadRequest('REMOVE_CHARACTER', id);
366
+ }
367
+
368
+ /**
369
+ * Retrieves identifiers assigned to an API ID.
370
+ */
371
+ public async getIdentifiers(apiId: string): Promise<globalTypes.CADStandardResponse> {
372
+ if (!apiId) {
373
+ throw new Error('apiId is required to fetch identifiers.');
374
+ }
375
+ return this.executeCadRequest('GET_IDENTIFIERS', apiId);
376
+ }
377
+
378
+ /**
379
+ * Modifies identifiers for an account.
380
+ */
381
+ public async modifyIdentifier(data: CADModifyIdentifierStruct): Promise<globalTypes.CADStandardResponse> {
382
+ if (!data?.apiId) {
383
+ throw new Error('apiId is required when modifying an identifier.');
384
+ }
385
+ return this.executeCadRequest('MODIFY_IDENTIFIER', data);
386
+ }
387
+
388
+ /**
389
+ * Sets the active identifier by identifier ID.
390
+ */
391
+ public async setIdentifier(apiId: string | undefined, identId: number): Promise<globalTypes.CADStandardResponse> {
392
+ if (!Number.isInteger(identId)) {
393
+ throw new Error('identId must be an integer when setting an identifier.');
394
+ }
395
+ return this.executeCadRequest('SET_IDENTIFIER', apiId, identId);
396
+ }
397
+
398
+ /**
399
+ * Toggles panic state for a unit.
400
+ */
401
+ public async setUnitPanic(apiId: string | undefined, isPanic: boolean): Promise<globalTypes.CADStandardResponse> {
402
+ return this.executeCadRequest('UNIT_PANIC', apiId, isPanic);
403
+ }
404
+
405
+ /**
406
+ * Updates a unit's status.
407
+ */
408
+ public async setUnitStatus(apiId: string | undefined, status: number, serverId: number): Promise<globalTypes.CADStandardResponse> {
409
+ if (!Number.isInteger(serverId)) {
410
+ throw new Error('serverId must be an integer when updating unit status.');
411
+ }
412
+ return this.executeCadRequest('UNIT_STATUS', apiId, status, serverId);
413
+ }
414
+
415
+ /**
416
+ * Retrieves live map blips for a CAD server.
417
+ */
418
+ public async getBlips(serverId: number): Promise<globalTypes.CADStandardResponse> {
419
+ if (!Number.isInteger(serverId)) {
420
+ throw new Error('serverId must be an integer when retrieving blips.');
421
+ }
422
+ return this.executeCadRequest('GET_BLIPS', serverId);
423
+ }
424
+
425
+ /**
426
+ * Adds blips to the CAD map.
427
+ */
428
+ public async addBlips(blips: CADAddBlipStruct | CADAddBlipStruct[]): Promise<globalTypes.CADStandardResponse> {
429
+ const payload = Array.isArray(blips) ? blips : [blips];
430
+ if (payload.length === 0) {
431
+ throw new Error('At least one blip must be provided when adding blips.');
432
+ }
433
+ return this.executeCadRequest('ADD_BLIP', payload);
434
+ }
435
+
436
+ /**
437
+ * Updates existing CAD map blips.
438
+ */
439
+ public async updateBlips(blips: CADModifyBlipStruct | CADModifyBlipStruct[]): Promise<globalTypes.CADStandardResponse> {
440
+ const payload = Array.isArray(blips) ? blips : [blips];
441
+ if (payload.length === 0) {
442
+ throw new Error('At least one blip must be provided when updating blips.');
443
+ }
444
+ return this.executeCadRequest('MODIFY_BLIP', payload);
445
+ }
446
+
447
+ /**
448
+ * Removes a CAD map blip.
449
+ */
450
+ public async removeBlip(id: number): Promise<globalTypes.CADStandardResponse> {
451
+ if (!Number.isInteger(id)) {
452
+ throw new Error('id must be an integer when removing a blip.');
453
+ }
454
+ return this.executeCadRequest('REMOVE_BLIP', id);
455
+ }
456
+
457
+ /**
458
+ * Creates a new 911 call entry.
459
+ */
460
+ public async create911Call(params: { serverId: number; isEmergency: boolean; caller: string; location: string; description: string; metaData?: Record<string, string> }): Promise<globalTypes.CADStandardResponse> {
461
+ const { serverId, isEmergency, caller, location, description, metaData = {} } = params;
462
+ if (!Number.isInteger(serverId)) {
463
+ throw new Error('serverId must be an integer when creating a 911 call.');
464
+ }
465
+ return this.executeCadRequest('911_CALL', serverId, isEmergency, caller, location, description, metaData);
466
+ }
467
+
468
+ /**
469
+ * Removes a 911 call by its identifier.
470
+ */
471
+ public async remove911Call(callId: number): Promise<globalTypes.CADStandardResponse> {
472
+ if (!Number.isInteger(callId)) {
473
+ throw new Error('callId must be an integer when removing a 911 call.');
474
+ }
475
+ return this.executeCadRequest('REMOVE_911', callId);
476
+ }
477
+
478
+ /**
479
+ * Retrieves dispatch calls with optional pagination.
480
+ */
481
+ public async getCalls(options: CADGetCallsStruct): Promise<globalTypes.CADStandardResponse> {
482
+ return this.executeCadRequest('GET_CALLS', options);
483
+ }
484
+
485
+ /**
486
+ * Retrieves active units for the provided filters.
487
+ */
488
+ public async getActiveUnits(options: CADGetActiveUnitsStruct): Promise<globalTypes.CADStandardResponse> {
489
+ return this.executeCadRequest('GET_ACTIVE_UNITS', options);
490
+ }
491
+
492
+ /**
493
+ * Kicks an active unit from the CAD.
494
+ */
495
+ public async kickUnit(apiId: string | undefined, reason: string, serverId: number): Promise<globalTypes.CADStandardResponse> {
496
+ if (!reason) {
497
+ throw new Error('reason is required when kicking a unit.');
498
+ }
499
+ if (!Number.isInteger(serverId)) {
500
+ throw new Error('serverId must be an integer when kicking a unit.');
501
+ }
502
+ return this.executeCadRequest('KICK_UNIT', apiId, reason, serverId);
503
+ }
504
+
505
+ /**
506
+ * Creates a new dispatch call.
507
+ */
508
+ public async createDispatch(data: CADNewDispatchStruct): Promise<globalTypes.CADStandardResponse> {
509
+ return this.executeCadRequest('NEW_DISPATCH', data);
510
+ }
511
+
512
+ /**
513
+ * Attaches units to an existing dispatch call.
514
+ */
515
+ public async attachUnits(serverId: number, callId: number, units: string[]): Promise<globalTypes.CADStandardResponse> {
516
+ if (!Number.isInteger(serverId) || !Number.isInteger(callId)) {
517
+ throw new Error('serverId and callId must be integers when attaching units.');
518
+ }
519
+ if (!Array.isArray(units) || units.length === 0) {
520
+ throw new Error('units must include at least one entry when attaching.');
521
+ }
522
+ return this.executeCadRequest('ATTACH_UNIT', serverId, callId, units);
523
+ }
524
+
525
+ /**
526
+ * Detaches units from dispatch calls.
527
+ */
528
+ public async detachUnits(serverId: number, units: string[]): Promise<globalTypes.CADStandardResponse> {
529
+ if (!Number.isInteger(serverId)) {
530
+ throw new Error('serverId must be an integer when detaching units.');
531
+ }
532
+ if (!Array.isArray(units) || units.length === 0) {
533
+ throw new Error('units must include at least one entry when detaching.');
534
+ }
535
+ return this.executeCadRequest('DETACH_UNIT', serverId, units);
536
+ }
537
+
538
+ /**
539
+ * Updates the postal code on a call.
540
+ */
541
+ public async setCallPostal(serverId: number, callId: number, postal: string): Promise<globalTypes.CADStandardResponse> {
542
+ if (!Number.isInteger(serverId) || !Number.isInteger(callId)) {
543
+ throw new Error('serverId and callId must be integers when setting a call postal.');
544
+ }
545
+ if (!postal) {
546
+ throw new Error('postal is required when setting a call postal.');
547
+ }
548
+ return this.executeCadRequest('SET_CALL_POSTAL', serverId, callId, postal);
549
+ }
550
+
551
+ /**
552
+ * Updates a call's primary unit.
553
+ */
554
+ public async setCallPrimary(serverId: number, callId: number, primary: number, trackPrimary: boolean): Promise<globalTypes.CADStandardResponse> {
555
+ if (!Number.isInteger(serverId) || !Number.isInteger(callId)) {
556
+ throw new Error('serverId and callId must be integers when setting a call primary.');
557
+ }
558
+ return this.executeCadRequest('SET_CALL_PRIMARY', serverId, callId, primary, trackPrimary);
559
+ }
560
+
561
+ /**
562
+ * Adds a note to an active call.
563
+ */
564
+ public async addCallNote(serverId: number, callId: number, note: string): Promise<globalTypes.CADStandardResponse> {
565
+ if (!Number.isInteger(serverId) || !Number.isInteger(callId)) {
566
+ throw new Error('serverId and callId must be integers when adding a call note.');
567
+ }
568
+ if (!note) {
569
+ throw new Error('note is required when adding a call note.');
570
+ }
571
+ return this.executeCadRequest('ADD_CALL_NOTE', serverId, callId, note);
572
+ }
573
+
574
+ /**
575
+ * Closes a CAD call.
576
+ */
577
+ public async closeCall(serverId: number, callId: number): Promise<globalTypes.CADStandardResponse> {
578
+ if (!Number.isInteger(serverId) || !Number.isInteger(callId)) {
579
+ throw new Error('serverId and callId must be integers when closing a call.');
580
+ }
581
+ return this.executeCadRequest('CLOSE_CALL', serverId, callId);
582
+ }
583
+
584
+ /**
585
+ * Updates live unit locations on the CAD map.
586
+ */
587
+ public async updateUnitLocations(locations: CADUnitLocationStruct[]): Promise<globalTypes.CADStandardResponse> {
588
+ if (!Array.isArray(locations) || locations.length === 0) {
589
+ throw new Error('locations must include at least one entry when updating unit locations.');
590
+ }
591
+ return this.executeCadRequest('UNIT_LOCATION', locations);
592
+ }
593
+
594
+ /**
595
+ * Replaces the street sign configuration for a server.
596
+ */
597
+ public async setStreetSignConfig(serverId: number, signConfig: CADStreetSignStruct[]): Promise<globalTypes.CADStandardResponse> {
598
+ if (!Number.isInteger(serverId)) {
599
+ throw new Error('serverId must be an integer when setting street sign config.');
600
+ }
601
+ if (!Array.isArray(signConfig) || signConfig.length === 0) {
602
+ throw new Error('signConfig must include at least one entry when setting street sign config.');
603
+ }
604
+ return this.executeCadRequest('SET_STREETSIGN_CONFIG', serverId, signConfig);
605
+ }
606
+
607
+ /**
608
+ * Updates an existing street sign's text.
609
+ */
610
+ public async updateStreetSign(serverId: number, signData: { ids: number[]; text1: string; text2: string; text3: string }): Promise<globalTypes.CADStandardResponse> {
611
+ if (!Number.isInteger(serverId)) {
612
+ throw new Error('serverId must be an integer when updating a street sign.');
613
+ }
614
+ if (!signData || !Array.isArray(signData.ids) || signData.ids.length === 0) {
615
+ throw new Error('signData.ids must include at least one identifier when updating a street sign.');
616
+ }
617
+ return this.executeCadRequest('UPDATE_STREETSIGN', serverId, signData);
618
+ }
619
+
620
+ private normalizeAccountEntries(input: string | { account: string } | Array<string | { account: string }>): { account: string }[] {
621
+ const entries = Array.isArray(input) ? input : [input];
622
+ return entries
623
+ .filter((entry): entry is string | { account: string } => entry !== undefined && entry !== null)
624
+ .map((entry) => {
625
+ if (typeof entry === 'string') {
626
+ return { account: entry };
627
+ }
628
+ if ('account' in entry) {
629
+ return { account: entry.account };
630
+ }
631
+ throw new Error('Invalid account entry provided.');
632
+ });
633
+ }
634
+ }
@@ -1,27 +1,71 @@
1
1
  import { Instance } from '../instance/Instance';
2
- import { CADServerAPIStruct } from '../libs/rest/src';
2
+ import * as globalTypes from '../constants';
3
+ import { APIError, CADServerAPIStruct } from '../libs/rest/src';
3
4
  import { CADServer } from '../structures/CADServer';
4
5
  import { CacheManager } from './CacheManager';
5
6
  import { CADManager } from './CADManager';
6
7
 
7
8
  export class CADServerManager extends CacheManager<number, CADServer, CADServerAPIStruct> {
8
- constructor(instance: Instance, manager: CADManager) {
9
+ constructor(instance: Instance, private readonly manager: CADManager) {
9
10
  super(instance, CADServer, []);
11
+ void this.initialize();
12
+ }
13
+
14
+ /**
15
+ * Retrieves the CAD servers belonging to the community.
16
+ */
17
+ public async getServers(): Promise<CADServerAPIStruct[]> {
18
+ if (!this.manager.rest) {
19
+ throw new Error('CAD REST client is not initialized.');
20
+ }
21
+ const serversRes: any = await this.manager.rest.request('GET_SERVERS');
22
+ const parsed = typeof serversRes === 'string' ? JSON.parse(serversRes) : serversRes;
23
+ const servers = Array.isArray(parsed?.servers) ? parsed.servers : [];
24
+ return servers;
25
+ }
10
26
 
11
- (async () => {
12
- try {
13
- const serversRes: any = await manager.rest?.request('GET_SERVERS');
14
- const servers = JSON.parse(serversRes).servers;
15
- servers.forEach((server: CADServerAPIStruct) => {
16
- const serverStruct = {
17
- id: server.id,
18
- config: server
19
- };
27
+ /**
28
+ * Updates the CAD server configuration.
29
+ */
30
+ public async setServers(servers: CADServerAPIStruct[], deployMap = false): Promise<globalTypes.CADStandardResponse<CADServerAPIStruct[]>> {
31
+ if (!Array.isArray(servers) || servers.length === 0) {
32
+ throw new Error('servers array must include at least one server configuration.');
33
+ }
34
+ if (!this.manager.rest) {
35
+ throw new Error('CAD REST client is not initialized.');
36
+ }
37
+ try {
38
+ const response: any = await this.manager.rest.request('SET_SERVERS', servers, deployMap);
39
+ const parsed = typeof response === 'string' ? JSON.parse(response) : response;
40
+ const updated = Array.isArray(parsed?.servers) ? parsed.servers : Array.isArray(parsed) ? parsed : [];
41
+ if (updated.length > 0) {
42
+ this.cache.clear();
43
+ updated.forEach((server: CADServerAPIStruct) => {
44
+ const serverStruct = { id: server.id, config: server };
20
45
  this._add(serverStruct, true, server.id);
21
46
  });
22
- } catch (err) {
23
- throw new Error(String(err));
24
47
  }
25
- })();
48
+ return { success: true, data: updated };
49
+ } catch (err) {
50
+ if (err instanceof APIError) {
51
+ return { success: false, reason: err.response };
52
+ }
53
+ throw err;
54
+ }
55
+ }
56
+
57
+ private async initialize(): Promise<void> {
58
+ try {
59
+ const servers = await this.getServers();
60
+ servers.forEach((server: CADServerAPIStruct) => {
61
+ const serverStruct = {
62
+ id: server.id,
63
+ config: server
64
+ };
65
+ this._add(serverStruct, true, server.id);
66
+ });
67
+ } catch (err) {
68
+ throw new Error(String(err));
69
+ }
26
70
  }
27
- }
71
+ }