@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
@@ -44,6 +44,11 @@ class REST extends events_1.EventEmitter {
44
44
  apiKey = this.instance.cmsApiKey;
45
45
  break;
46
46
  }
47
+ case constants_2.productEnums.RADIO: {
48
+ communityId = this.instance.radioCommunityId;
49
+ apiKey = this.instance.radioApiKey;
50
+ break;
51
+ }
47
52
  }
48
53
  if (!communityId || !apiKey)
49
54
  throw new Error(`Community ID or API Key could not be found for request. P${apiType.product}`);
@@ -59,6 +64,7 @@ class REST extends events_1.EventEmitter {
59
64
  return this.requestManager.queueRequest(options);
60
65
  }
61
66
  formatDataArguments(type, args) {
67
+ var _a, _b, _c;
62
68
  switch (type) {
63
69
  case 'VERIFY_WHITELIST': {
64
70
  return {
@@ -73,6 +79,9 @@ class REST extends events_1.EventEmitter {
73
79
  serverId: args[0]
74
80
  };
75
81
  }
82
+ case 'SET_GAME_SERVERS': {
83
+ return (_a = args[0]) !== null && _a !== void 0 ? _a : [];
84
+ }
76
85
  case 'RSVP': {
77
86
  return {
78
87
  eventId: args[0],
@@ -100,6 +109,64 @@ class REST extends events_1.EventEmitter {
100
109
  uniqueId: args[4]
101
110
  };
102
111
  }
112
+ case 'GET_CURRENT_CLOCK_IN': {
113
+ return {
114
+ apiId: args[0],
115
+ username: args[1],
116
+ accId: args[2],
117
+ discord: args[3],
118
+ uniqueId: args[4]
119
+ };
120
+ }
121
+ case 'GET_ACCOUNTS': {
122
+ return (_b = args[0]) !== null && _b !== void 0 ? _b : {};
123
+ }
124
+ case 'GET_PROFILE_FIELDS': {
125
+ return {};
126
+ }
127
+ case 'SET_CLOCK': {
128
+ if (args[0] && typeof args[0] === 'object' && !Array.isArray(args[0])) {
129
+ return args[0];
130
+ }
131
+ return {
132
+ serverId: args[0],
133
+ currentUtc: args[1],
134
+ currentGame: args[2],
135
+ secondsPerHour: args[3]
136
+ };
137
+ }
138
+ case 'JOIN_COMMUNITY':
139
+ case 'LEAVE_COMMUNITY': {
140
+ const payload = args[0] && typeof args[0] === 'object' && !Array.isArray(args[0]) && 'internalKey' in args[0]
141
+ ? args[0]
142
+ : null;
143
+ const internalKey = payload ? payload.internalKey : args[0];
144
+ const accountsInput = payload ? payload.accounts : args[1];
145
+ let accounts = [];
146
+ if (Array.isArray(accountsInput)) {
147
+ accounts = accountsInput.map((entry) => {
148
+ if (typeof entry === 'string') {
149
+ return { account: entry };
150
+ }
151
+ if (entry && typeof entry === 'object' && 'account' in entry) {
152
+ return entry;
153
+ }
154
+ return { account: String(entry) };
155
+ });
156
+ }
157
+ else if (accountsInput) {
158
+ if (typeof accountsInput === 'string') {
159
+ accounts = [{ account: accountsInput }];
160
+ }
161
+ else if (typeof accountsInput === 'object' && 'account' in accountsInput) {
162
+ accounts = [accountsInput];
163
+ }
164
+ }
165
+ return {
166
+ internalKey,
167
+ accounts
168
+ };
169
+ }
103
170
  case 'CLOCK_IN_OUT': {
104
171
  return {
105
172
  apiId: args[0],
@@ -131,6 +198,13 @@ class REST extends events_1.EventEmitter {
131
198
  secret: args[0],
132
199
  };
133
200
  }
201
+ case 'GET_FORM_TEMPLATE_SUBMISSIONS': {
202
+ return {
203
+ templateId: args[0],
204
+ skip: args[1],
205
+ take: args[2],
206
+ };
207
+ }
134
208
  case 'CHANGE_FORM_STAGE': {
135
209
  return {
136
210
  accId: args[0],
@@ -195,6 +269,16 @@ class REST extends events_1.EventEmitter {
195
269
  uniqueId: args[4],
196
270
  };
197
271
  }
272
+ case 'TRIGGER_PROMOTION_FLOWS': {
273
+ const payload = args[0];
274
+ if (!Array.isArray(payload)) {
275
+ throw new Error('TRIGGER_PROMOTION_FLOWS requires an array of promotion flow payloads.');
276
+ }
277
+ return payload;
278
+ }
279
+ case 'GET_PROMOTION_FLOWS': {
280
+ return [];
281
+ }
198
282
  case 'ERLC_GET_ONLINE_PLAYERS': {
199
283
  return {
200
284
  robloxJoinCode: args[0]
@@ -216,6 +300,40 @@ class REST extends events_1.EventEmitter {
216
300
  points: args[6],
217
301
  };
218
302
  }
303
+ case 'RADIO_GET_COMMUNITY_CHANNELS':
304
+ case 'RADIO_GET_CONNECTED_USERS':
305
+ case 'RADIO_GET_SERVER_SUBSCRIPTION_FROM_IP': {
306
+ return undefined;
307
+ }
308
+ case 'RADIO_GET_CONNECTED_USER': {
309
+ return {
310
+ roomId: args[0],
311
+ identity: args[1]
312
+ };
313
+ }
314
+ case 'RADIO_SET_USER_CHANNELS': {
315
+ return {
316
+ identity: args[0],
317
+ options: (_c = args[1]) !== null && _c !== void 0 ? _c : {}
318
+ };
319
+ }
320
+ case 'RADIO_SET_USER_DISPLAY_NAME': {
321
+ return {
322
+ accId: args[0],
323
+ displayName: args[1]
324
+ };
325
+ }
326
+ case 'RADIO_SET_SERVER_IP': {
327
+ return {
328
+ pushUrl: args[0]
329
+ };
330
+ }
331
+ case 'RADIO_SET_IN_GAME_SPEAKER_LOCATIONS': {
332
+ return {
333
+ locations: args[0],
334
+ token: args[1]
335
+ };
336
+ }
219
337
  default: {
220
338
  return args;
221
339
  }
@@ -19,6 +19,7 @@ export interface RequestData {
19
19
  key: string;
20
20
  type: string;
21
21
  data: any;
22
+ internalKey?: string;
22
23
  }
23
24
  export interface InternalRequestData extends RequestData {
24
25
  product: productEnums;
@@ -55,5 +56,6 @@ export declare class RequestManager extends EventEmitter {
55
56
  removeRateLimit(id: string): void;
56
57
  private createHandler;
57
58
  private static resolveRequestData;
59
+ private static resolveRadioRequest;
58
60
  debug(log: string): void;
59
61
  }
@@ -24,6 +24,10 @@ class RequestManager extends events_1.EventEmitter {
24
24
  this.options = { ...constants_1.DefaultCMSRestOptions, ...options };
25
25
  break;
26
26
  }
27
+ case constants_2.productEnums.RADIO: {
28
+ this.options = { ...constants_1.DefaultRadioRestOptions, ...options };
29
+ break;
30
+ }
27
31
  default: {
28
32
  throw new Error('No Product provided for RequestManager initialization');
29
33
  }
@@ -66,9 +70,15 @@ class RequestManager extends events_1.EventEmitter {
66
70
  case constants_2.productEnums.CMS:
67
71
  apiURL = instance.cmsApiUrl;
68
72
  break;
73
+ case constants_2.productEnums.RADIO:
74
+ apiURL = instance.radioApiUrl;
75
+ break;
69
76
  }
70
77
  const findType = constants_1.AllAPITypes.find((_type) => _type.type === type);
71
78
  if (findType) {
79
+ if (product === constants_2.productEnums.RADIO) {
80
+ return RequestManager.resolveRadioRequest(instance, apiURL, findType, data, apiData);
81
+ }
72
82
  apiData.fullUrl = `${apiURL}/${findType.path}`;
73
83
  apiData.method = findType.method;
74
84
  apiData.fetchOptions.method = findType.method;
@@ -79,6 +89,18 @@ class RequestManager extends events_1.EventEmitter {
79
89
  apiData.data.data = clonedData;
80
90
  break;
81
91
  }
92
+ case 'SET_GAME_SERVERS': {
93
+ apiData.data.data = clonedData;
94
+ break;
95
+ }
96
+ case 'TRIGGER_PROMOTION_FLOWS': {
97
+ apiData.data.data = clonedData;
98
+ break;
99
+ }
100
+ case 'GET_PROMOTION_FLOWS': {
101
+ apiData.data.data = [];
102
+ break;
103
+ }
82
104
  case 'SET_PENAL_CODES': {
83
105
  apiData.data.data = [clonedData[0]];
84
106
  break;
@@ -119,6 +141,29 @@ class RequestManager extends events_1.EventEmitter {
119
141
  apiData.data.data = [clonedData[0]];
120
142
  break;
121
143
  }
144
+ case 'SET_CLOCK': {
145
+ apiData.data.data = Array.isArray(clonedData) ? clonedData : [clonedData];
146
+ break;
147
+ }
148
+ case 'JOIN_COMMUNITY':
149
+ case 'LEAVE_COMMUNITY': {
150
+ const internalKey = clonedData === null || clonedData === void 0 ? void 0 : clonedData.internalKey;
151
+ if (internalKey !== undefined) {
152
+ apiData.data.internalKey = internalKey;
153
+ }
154
+ const accountsSource = clonedData === null || clonedData === void 0 ? void 0 : clonedData.accounts;
155
+ const accountsArray = Array.isArray(accountsSource) ? accountsSource : accountsSource ? [accountsSource] : [];
156
+ apiData.data.data = accountsArray.map((entry) => {
157
+ if (typeof entry === 'string') {
158
+ return { account: entry };
159
+ }
160
+ if (entry && typeof entry === 'object' && 'account' in entry) {
161
+ return entry;
162
+ }
163
+ return { account: String(entry) };
164
+ });
165
+ break;
166
+ }
122
167
  case 'NEW_CHARACTER': {
123
168
  apiData.data.data = [clonedData[0]];
124
169
  break;
@@ -184,6 +229,170 @@ class RequestManager extends events_1.EventEmitter {
184
229
  };
185
230
  return apiData;
186
231
  }
232
+ static resolveRadioRequest(instance, apiURL, apiType, request, apiData) {
233
+ var _a, _b, _c;
234
+ if (!apiURL || typeof apiURL !== 'string') {
235
+ throw new Error('Radio API URL could not be resolved for request.');
236
+ }
237
+ const rawData = request.data;
238
+ const payload = rawData == null ? {} : (typeof rawData === 'object' ? (0, utils_1.cloneObject)(rawData) : rawData);
239
+ const headers = {
240
+ Accept: 'application/json'
241
+ };
242
+ const applyHeaders = (source) => {
243
+ if (!source)
244
+ return;
245
+ if (Array.isArray(source)) {
246
+ for (const [key, value] of source) {
247
+ headers[key] = value;
248
+ }
249
+ return;
250
+ }
251
+ if (typeof source === 'object' && source !== null && 'forEach' in source && typeof source.forEach === 'function') {
252
+ source.forEach((value, key) => {
253
+ headers[key] = value;
254
+ });
255
+ return;
256
+ }
257
+ Object.assign(headers, source);
258
+ };
259
+ applyHeaders(instance.apiHeaders);
260
+ let method = apiType.method;
261
+ let path = apiType.path;
262
+ let body;
263
+ const ensureAuth = () => {
264
+ if (!request.id || !request.key) {
265
+ throw new Error('Community ID or API Key could not be found for request.');
266
+ }
267
+ return {
268
+ id: request.id,
269
+ key: request.key,
270
+ encodedId: encodeURIComponent(request.id),
271
+ encodedKey: encodeURIComponent(request.key)
272
+ };
273
+ };
274
+ const encodeSegment = (value) => encodeURIComponent(String(value));
275
+ switch (apiType.type) {
276
+ case 'RADIO_GET_COMMUNITY_CHANNELS': {
277
+ const auth = ensureAuth();
278
+ path = `${apiType.path}/${auth.encodedId}/${auth.encodedKey}`;
279
+ method = 'GET';
280
+ break;
281
+ }
282
+ case 'RADIO_GET_CONNECTED_USERS': {
283
+ const auth = ensureAuth();
284
+ path = `${apiType.path}/${auth.encodedId}/${auth.encodedKey}`;
285
+ method = 'GET';
286
+ break;
287
+ }
288
+ case 'RADIO_GET_CONNECTED_USER': {
289
+ const auth = ensureAuth();
290
+ const roomIdRaw = (_a = payload === null || payload === void 0 ? void 0 : payload.roomId) !== null && _a !== void 0 ? _a : payload === null || payload === void 0 ? void 0 : payload.roomID;
291
+ if (roomIdRaw === undefined) {
292
+ throw new Error('roomId is required for RADIO_GET_CONNECTED_USER requests.');
293
+ }
294
+ const roomIdNumeric = typeof roomIdRaw === 'number' ? roomIdRaw : Number(roomIdRaw);
295
+ if (Number.isNaN(roomIdNumeric)) {
296
+ throw new Error('roomId must be a number for RADIO_GET_CONNECTED_USER requests.');
297
+ }
298
+ const identity = payload === null || payload === void 0 ? void 0 : payload.identity;
299
+ if (!identity) {
300
+ throw new Error('identity is required for RADIO_GET_CONNECTED_USER requests.');
301
+ }
302
+ path = `${apiType.path}/${auth.encodedId}/${auth.encodedKey}/${encodeSegment(roomIdNumeric)}/${encodeSegment(identity)}`;
303
+ method = 'GET';
304
+ break;
305
+ }
306
+ case 'RADIO_SET_USER_CHANNELS': {
307
+ const auth = ensureAuth();
308
+ const identity = payload === null || payload === void 0 ? void 0 : payload.identity;
309
+ if (!identity) {
310
+ throw new Error('identity is required for RADIO_SET_USER_CHANNELS requests.');
311
+ }
312
+ const options = (_b = payload === null || payload === void 0 ? void 0 : payload.options) !== null && _b !== void 0 ? _b : {};
313
+ path = `${apiType.path}/${auth.encodedId}/${auth.encodedKey}/${encodeSegment(identity)}`;
314
+ method = 'POST';
315
+ const requestBody = {};
316
+ if ((options === null || options === void 0 ? void 0 : options.transmit) !== undefined) {
317
+ requestBody.transmit = options.transmit;
318
+ }
319
+ if ((options === null || options === void 0 ? void 0 : options.scan) !== undefined) {
320
+ requestBody.scan = options.scan;
321
+ }
322
+ body = requestBody;
323
+ break;
324
+ }
325
+ case 'RADIO_SET_USER_DISPLAY_NAME': {
326
+ const auth = ensureAuth();
327
+ const accId = payload === null || payload === void 0 ? void 0 : payload.accId;
328
+ const displayName = payload === null || payload === void 0 ? void 0 : payload.displayName;
329
+ if (!accId || !displayName) {
330
+ throw new Error('accId and displayName are required for RADIO_SET_USER_DISPLAY_NAME requests.');
331
+ }
332
+ method = 'POST';
333
+ body = {
334
+ id: auth.id,
335
+ key: auth.key,
336
+ accId,
337
+ displayName
338
+ };
339
+ path = apiType.path;
340
+ break;
341
+ }
342
+ case 'RADIO_GET_SERVER_SUBSCRIPTION_FROM_IP': {
343
+ method = 'GET';
344
+ path = apiType.path;
345
+ break;
346
+ }
347
+ case 'RADIO_SET_SERVER_IP': {
348
+ const auth = ensureAuth();
349
+ const pushUrl = payload === null || payload === void 0 ? void 0 : payload.pushUrl;
350
+ if (!pushUrl) {
351
+ throw new Error('pushUrl is required for RADIO_SET_SERVER_IP requests.');
352
+ }
353
+ method = 'POST';
354
+ body = {
355
+ id: auth.id,
356
+ key: auth.key,
357
+ pushUrl
358
+ };
359
+ path = apiType.path;
360
+ break;
361
+ }
362
+ case 'RADIO_SET_IN_GAME_SPEAKER_LOCATIONS': {
363
+ const auth = ensureAuth();
364
+ const locations = payload === null || payload === void 0 ? void 0 : payload.locations;
365
+ if (!Array.isArray(locations)) {
366
+ throw new Error('locations array is required for RADIO_SET_IN_GAME_SPEAKER_LOCATIONS requests.');
367
+ }
368
+ method = 'POST';
369
+ body = {
370
+ id: auth.id,
371
+ key: auth.key,
372
+ locations
373
+ };
374
+ const token = (_c = payload === null || payload === void 0 ? void 0 : payload.token) !== null && _c !== void 0 ? _c : auth.key;
375
+ if (token) {
376
+ headers.Authorization = `Bearer ${token}`;
377
+ }
378
+ path = apiType.path;
379
+ break;
380
+ }
381
+ default: {
382
+ throw new Error(`Unsupported radio API type received: ${apiType.type}`);
383
+ }
384
+ }
385
+ apiData.typePath = path;
386
+ apiData.fullUrl = `${apiURL}/${path}`;
387
+ apiData.method = method;
388
+ apiData.fetchOptions.method = method;
389
+ if (body !== undefined) {
390
+ headers['Content-Type'] = 'application/json';
391
+ apiData.fetchOptions.body = JSON.stringify(body);
392
+ }
393
+ apiData.fetchOptions.headers = headers;
394
+ return apiData;
395
+ }
187
396
  debug(log) {
188
397
  return this.instance._debugLog(log);
189
398
  }
@@ -13,7 +13,25 @@ class RateLimitError extends Error {
13
13
  * The name of the error
14
14
  */
15
15
  get name() {
16
- return `Ratelimit Hit - [${this.product === constants_1.productEnums.CAD ? 'Sonoran CAD' : this.product === constants_1.productEnums.CMS ? 'Sonoran CMS' : 'Invalid Product'} '${this.type}']`;
16
+ let productName;
17
+ switch (this.product) {
18
+ case constants_1.productEnums.CAD: {
19
+ productName = 'Sonoran CAD';
20
+ break;
21
+ }
22
+ case constants_1.productEnums.CMS: {
23
+ productName = 'Sonoran CMS';
24
+ break;
25
+ }
26
+ case constants_1.productEnums.RADIO: {
27
+ productName = 'Sonoran Radio';
28
+ break;
29
+ }
30
+ default: {
31
+ productName = 'Invalid Product';
32
+ }
33
+ }
34
+ return `Ratelimit Hit - [${productName} '${this.type}']`;
17
35
  }
18
36
  }
19
37
  exports.RateLimitError = RateLimitError;