ogi-addon 1.0.0 → 1.1.5

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.
package/build/main.cjs CHANGED
@@ -33,6 +33,8 @@ __export(main_exports, {
33
33
  Configuration: () => Configuration,
34
34
  ConfigurationBuilder: () => ConfigurationBuilder,
35
35
  EventResponse: () => EventResponse,
36
+ SearchTool: () => SearchTool,
37
+ VERSION: () => VERSION,
36
38
  default: () => OGIAddon
37
39
  });
38
40
  module.exports = __toCommonJS(main_exports);
@@ -48,18 +50,31 @@ var configValidation = import_zod.default.object({
48
50
  });
49
51
  var ConfigurationBuilder = class {
50
52
  options = [];
53
+ /**
54
+ * Add a number option to the configuration builder and return the builder for chaining. You must provide a name, display name, and description for the option.
55
+ * @param option { (option: NumberOption) => NumberOption }
56
+ * @returns
57
+ */
51
58
  addNumberOption(option) {
52
59
  let newOption = new NumberOption();
53
60
  newOption = option(newOption);
54
61
  this.options.push(newOption);
55
62
  return this;
56
63
  }
64
+ /**
65
+ * Add a string option to the configuration builder and return the builder for chaining. You must provide a name, display name, and description for the option.
66
+ * @param option { (option: StringOption) => StringOption }
67
+ */
57
68
  addStringOption(option) {
58
69
  let newOption = new StringOption();
59
70
  newOption = option(newOption);
60
71
  this.options.push(newOption);
61
72
  return this;
62
73
  }
74
+ /**
75
+ * Add a boolean option to the configuration builder and return the builder for chaining. You must provide a name, display name, and description for the option.
76
+ * @param option { (option: BooleanOption) => BooleanOption }
77
+ */
63
78
  addBooleanOption(option) {
64
79
  let newOption = new BooleanOption();
65
80
  newOption = option(newOption);
@@ -89,18 +104,36 @@ var ConfigurationOption = class {
89
104
  displayName = "";
90
105
  description = "";
91
106
  type = "unset";
107
+ /**
108
+ * Set the name of the option. **REQUIRED**
109
+ * @param name {string} The name of the option. This is used to reference the option in the configuration file.
110
+ */
92
111
  setName(name) {
93
112
  this.name = name;
94
113
  return this;
95
114
  }
115
+ /**
116
+ * Set the display name of the option. This is used to show the user a human readable version of what the option is. **REQUIRED**
117
+ * @param displayName {string} The display name of the option.
118
+ * @returns
119
+ */
96
120
  setDisplayName(displayName) {
97
121
  this.displayName = displayName;
98
122
  return this;
99
123
  }
124
+ /**
125
+ * Set the description of the option. This is to show the user a brief description of what this option does. **REQUIRED**
126
+ * @param description {string} The description of the option.
127
+ * @returns
128
+ */
100
129
  setDescription(description) {
101
130
  this.description = description;
102
131
  return this;
103
132
  }
133
+ /**
134
+ * Validation code for the option. This is called when the user provides input to the option. If the validation fails, the user will be prompted to provide input again.
135
+ * @param input {unknown} The input to validate
136
+ */
104
137
  validate(input) {
105
138
  throw new Error("Validation code not implemented. Value: " + input);
106
139
  }
@@ -112,22 +145,42 @@ var StringOption = class extends ConfigurationOption {
112
145
  defaultValue = "";
113
146
  inputType = "text";
114
147
  type = "string";
148
+ /**
149
+ * Set the allowed values for the string. If the array is empty, any value is allowed. When provided, the client will act like this option is a dropdown.
150
+ * @param allowedValues {string[]} An array of allowed values for the string. If the array is empty, any value is allowed.
151
+ */
115
152
  setAllowedValues(allowedValues) {
116
153
  this.allowedValues = allowedValues;
117
154
  return this;
118
155
  }
156
+ /**
157
+ * Set the default value for the string. This value will be used if the user does not provide a value. **HIGHLY RECOMMENDED**
158
+ * @param defaultValue {string} The default value for the string.
159
+ */
119
160
  setDefaultValue(defaultValue) {
120
161
  this.defaultValue = defaultValue;
121
162
  return this;
122
163
  }
164
+ /**
165
+ * Set the minimum text length for the string. If the user provides a string that is less than this value, the validation will fail.
166
+ * @param minTextLength {number} The minimum text length for the string.
167
+ */
123
168
  setMinTextLength(minTextLength) {
124
169
  this.minTextLength = minTextLength;
125
170
  return this;
126
171
  }
172
+ /**
173
+ * Set the maximum text length for the string. If the user provides a string that is greater than this value, the validation will fail.
174
+ * @param maxTextLength {number} The maximum text length for the string.
175
+ */
127
176
  setMaxTextLength(maxTextLength) {
128
177
  this.maxTextLength = maxTextLength;
129
178
  return this;
130
179
  }
180
+ /**
181
+ * Set the input type for the string. This will change how the client renders the input.
182
+ * @param inputType {'text' | 'file' | 'password' | 'folder'} The input type for the string.
183
+ */
131
184
  setInputType(inputType) {
132
185
  this.inputType = inputType;
133
186
  return this;
@@ -150,18 +203,34 @@ var NumberOption = class extends ConfigurationOption {
150
203
  defaultValue = 0;
151
204
  type = "number";
152
205
  inputType = "number";
206
+ /**
207
+ * Set the minimum value for the number. If the user provides a number that is less than this value, the validation will fail.
208
+ * @param min {number} The minimum value for the number.
209
+ */
153
210
  setMin(min) {
154
211
  this.min = min;
155
212
  return this;
156
213
  }
214
+ /**
215
+ * Set the input type for the number. This will change how the client renders the input.
216
+ * @param type {'range' | 'number'} The input type for the number.
217
+ */
157
218
  setInputType(type) {
158
219
  this.inputType = type;
159
220
  return this;
160
221
  }
222
+ /**
223
+ * Set the maximum value for the number. If the user provides a number that is greater than this value, the validation will fail.
224
+ * @param max {number} The maximum value for the number.
225
+ */
161
226
  setMax(max) {
162
227
  this.max = max;
163
228
  return this;
164
229
  }
230
+ /**
231
+ * Set the default value for the number. This value will be used if the user does not provide a value. **HIGHLY RECOMMENDED**
232
+ * @param defaultValue {number} The default value for the number.
233
+ */
165
234
  setDefaultValue(defaultValue) {
166
235
  this.defaultValue = defaultValue;
167
236
  return this;
@@ -179,6 +248,10 @@ var NumberOption = class extends ConfigurationOption {
179
248
  var BooleanOption = class extends ConfigurationOption {
180
249
  type = "boolean";
181
250
  defaultValue = false;
251
+ /**
252
+ * Set the default value for the boolean. This value will be used if the user does not provide a value. **HIGHLY RECOMMENDED**
253
+ * @param defaultValue {boolean} The default value for the boolean.
254
+ */
182
255
  setDefaultValue(defaultValue) {
183
256
  this.defaultValue = defaultValue;
184
257
  return this;
@@ -275,16 +348,35 @@ var EventResponse = class {
275
348
  defer() {
276
349
  this.deffered = true;
277
350
  }
351
+ /**
352
+ * Resolve the event with data. This acts like a promise resolve, and will stop the event from being processed further. **You must always call this method when you are done with the event.**
353
+ * @param data {T}
354
+ */
278
355
  resolve(data) {
279
356
  this.resolved = true;
280
357
  this.data = data;
281
358
  }
359
+ /**
360
+ * Completes the event and resolves it, but does not return any data. **You must always call this method when you are done with the event.**
361
+ */
282
362
  complete() {
283
363
  this.resolved = true;
284
364
  }
365
+ /**
366
+ * Logs a message to the event. This is useful for debugging and logging information to the user.
367
+ * @param message {string}
368
+ */
285
369
  log(message) {
286
370
  this.logs.push(message);
287
371
  }
372
+ /**
373
+ * Send a screen to the client to ask for input. Use the `ConfigurationBuilder` system to build the screen. Once sent to the user, the addon cannot change the screen.
374
+ * @async
375
+ * @param name {string}
376
+ * @param description {string}
377
+ * @param screen {ConfigurationBuilder}
378
+ * @returns {Promise<{ [key: string]: boolean | string | number }>}
379
+ */
288
380
  async askForInput(name, description, screen) {
289
381
  if (!this.onInputAsked) {
290
382
  throw new Error("No input asked callback");
@@ -293,12 +385,16 @@ var EventResponse = class {
293
385
  }
294
386
  };
295
387
 
388
+ // src/main.ts
389
+ var import_fuse = __toESM(require("fuse.js"), 1);
390
+
296
391
  // package.json
297
392
  var package_default = {
298
393
  name: "ogi-addon",
299
394
  module: "./build/main.js",
300
395
  type: "module",
301
396
  main: "./build/main.cjs",
397
+ version: "1.1.5",
302
398
  exports: {
303
399
  ".": {
304
400
  import: {
@@ -327,8 +423,8 @@ var package_default = {
327
423
  email: "me@nat3z.com",
328
424
  url: "https://nat3z.com/"
329
425
  },
330
- version: "1.0.0",
331
426
  dependencies: {
427
+ "fuse.js": "^7.1.0",
332
428
  ws: "^8.4.0",
333
429
  zod: "^3.23.8"
334
430
  },
@@ -347,7 +443,7 @@ var package_default = {
347
443
 
348
444
  // src/main.ts
349
445
  var defaultPort = 7654;
350
- var version = package_default.version;
446
+ var VERSION = package_default.version;
351
447
  var OGIAddon = class {
352
448
  eventEmitter = new import_node_events.default.EventEmitter();
353
449
  addonWSListener;
@@ -357,15 +453,91 @@ var OGIAddon = class {
357
453
  this.addonInfo = addonInfo;
358
454
  this.addonWSListener = new OGIAddonWSListener(this, this.eventEmitter);
359
455
  }
456
+ /**
457
+ * Register an event listener for the addon. (See EventListenerTypes)
458
+ * @param event {OGIAddonEvent}
459
+ * @param listener {EventListenerTypes[OGIAddonEvent]}
460
+ */
360
461
  on(event, listener) {
361
462
  this.eventEmitter.on(event, listener);
362
463
  }
363
464
  emit(event, ...args) {
364
465
  this.eventEmitter.emit(event, ...args);
365
466
  }
467
+ /**
468
+ * Notify the client using a notification. Provide the type of notification, the message, and an ID.
469
+ * @param notification {Notification}
470
+ */
366
471
  notify(notification) {
367
472
  this.addonWSListener.send("notification", [notification]);
368
473
  }
474
+ /**
475
+ * Search for items in the OGI Steam-Synced Library. Query can either be a Steam AppID or a Steam Game Name.
476
+ * @param query {string}
477
+ * @param event {EventResponse<BasicLibraryInfo[]>}
478
+ */
479
+ async steamSearch(query, strict = false) {
480
+ const id = this.addonWSListener.send("steam-search", { query, strict });
481
+ return await this.addonWSListener.waitForResponseFromServer(id);
482
+ }
483
+ /**
484
+ * Notify the OGI Addon Server that you are performing a background task. This can be used to help users understand what is happening in the background.
485
+ * @param id {string}
486
+ * @param progress {number}
487
+ * @param logs {string[]}
488
+ */
489
+ async task() {
490
+ const id = Math.random().toString(36).substring(7);
491
+ const progress = 0;
492
+ const logs = [];
493
+ const task = new CustomTask(this.addonWSListener, id, progress, logs);
494
+ this.addonWSListener.send("task-update", { id, progress, logs, finished: false });
495
+ return task;
496
+ }
497
+ };
498
+ var CustomTask = class {
499
+ id;
500
+ progress;
501
+ logs;
502
+ finished = false;
503
+ ws;
504
+ constructor(ws2, id, progress, logs) {
505
+ this.id = id;
506
+ this.progress = progress;
507
+ this.logs = logs;
508
+ this.ws = ws2;
509
+ }
510
+ log(log) {
511
+ this.logs.push(log);
512
+ this.update();
513
+ }
514
+ finish() {
515
+ this.finished = true;
516
+ this.update();
517
+ }
518
+ setProgress(progress) {
519
+ this.progress = progress;
520
+ this.update();
521
+ }
522
+ update() {
523
+ this.ws.send("task-update", { id: this.id, progress: this.progress, logs: this.logs, finished: this.finished });
524
+ }
525
+ };
526
+ var SearchTool = class {
527
+ fuse;
528
+ constructor(items, keys) {
529
+ this.fuse = new import_fuse.default(items, {
530
+ keys,
531
+ threshold: 0.3,
532
+ includeScore: true
533
+ });
534
+ }
535
+ search(query, limit = 10) {
536
+ return this.fuse.search(query).slice(0, limit).map((result) => result.item);
537
+ }
538
+ addItems(items) {
539
+ items.map((item) => this.fuse.add(item));
540
+ }
369
541
  };
370
542
  var OGIAddonWSListener = class {
371
543
  socket;
@@ -380,22 +552,16 @@ var OGIAddonWSListener = class {
380
552
  this.socket = new import_ws.default("ws://localhost:" + defaultPort);
381
553
  this.socket.on("open", () => {
382
554
  console.log("Connected to OGI Addon Server");
383
- console.log("OGI Addon Server Version:", version);
384
- this.socket.send(JSON.stringify({
385
- event: "authenticate",
386
- args: {
387
- ...this.addon.addonInfo,
388
- secret: process.argv[process.argv.length - 1].split("=")[1],
389
- ogiVersion: version
390
- }
391
- }));
555
+ console.log("OGI Addon Server Version:", VERSION);
556
+ this.send("authenticate", {
557
+ ...this.addon.addonInfo,
558
+ secret: process.argv[process.argv.length - 1].split("=")[1],
559
+ ogiVersion: VERSION
560
+ });
392
561
  this.eventEmitter.emit("connect");
393
562
  let configBuilder = new ConfigurationBuilder();
394
563
  this.eventEmitter.emit("configure", configBuilder);
395
- this.socket.send(JSON.stringify({
396
- event: "configure",
397
- args: configBuilder.build(false)
398
- }));
564
+ this.send("configure", configBuilder.build(false));
399
565
  this.addon.config = new Configuration(configBuilder.build(true));
400
566
  });
401
567
  this.socket.on("error", (error) => {
@@ -489,6 +655,19 @@ var OGIAddonWSListener = class {
489
655
  const gameDetailsResult = await this.waitForEventToRespond(gameDetailsEvent);
490
656
  this.respondToMessage(message.id, gameDetailsResult.data);
491
657
  break;
658
+ case "request-dl":
659
+ let requestDLEvent = new EventResponse((screen, name, description) => this.userInputAsked(screen, name, description, this.socket));
660
+ if (this.eventEmitter.listenerCount("request-dl") === 0) {
661
+ this.respondToMessage(message.id, { error: "No event listener for request-dl" });
662
+ break;
663
+ }
664
+ this.eventEmitter.emit("request-dl", message.args.appID, message.args.info, requestDLEvent);
665
+ const requestDLResult = await this.waitForEventToRespond(requestDLEvent);
666
+ if (requestDLEvent.data === null || requestDLEvent.data?.downloadType === "request") {
667
+ throw new Error("Request DL event did not return a valid result. Please ensure that the event does not resolve with another `request` download type.");
668
+ }
669
+ this.respondToMessage(message.id, requestDLResult.data);
670
+ break;
492
671
  }
493
672
  });
494
673
  }
@@ -542,10 +721,13 @@ var OGIAddonWSListener = class {
542
721
  });
543
722
  }
544
723
  send(event, args) {
724
+ const id = Math.random().toString(36).substring(7);
545
725
  this.socket.send(JSON.stringify({
546
726
  event,
547
- args
727
+ args,
728
+ id
548
729
  }));
730
+ return id;
549
731
  }
550
732
  close() {
551
733
  this.socket.close();
@@ -555,6 +737,8 @@ var OGIAddonWSListener = class {
555
737
  0 && (module.exports = {
556
738
  Configuration,
557
739
  ConfigurationBuilder,
558
- EventResponse
740
+ EventResponse,
741
+ SearchTool,
742
+ VERSION
559
743
  });
560
744
  //# sourceMappingURL=main.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/main.ts","../src/config/ConfigurationBuilder.ts","../src/config/Configuration.ts","../src/EventResponse.ts","../package.json"],"sourcesContent":["import ws, { WebSocket } from 'ws';\r\nimport events from 'node:events';\r\nimport { ConfigurationBuilder, ConfigurationFile } from './config/ConfigurationBuilder';\r\nimport { Configuration } from './config/Configuration';\r\nimport EventResponse from './EventResponse';\r\nimport { SearchResult } from './SearchEngine';\r\n\r\nexport type OGIAddonEvent = 'connect' | 'disconnect' | 'configure' | 'authenticate' | 'search' | 'setup' | 'library-search' | 'game-details' | 'exit';\r\nexport type OGIAddonClientSentEvent = 'response' | 'authenticate' | 'configure' | 'defer-update' | 'notification' | 'input-asked';\r\n\r\nexport type OGIAddonServerSentEvent = 'authenticate' | 'configure' | 'config-update' | 'search' | 'setup' | 'response' | 'library-search' | 'game-details';\r\nexport { ConfigurationBuilder, Configuration, EventResponse, SearchResult };\r\nconst defaultPort = 7654;\r\nimport pjson from '../package.json';\r\nconst version = pjson.version;\r\n\r\nexport interface ClientSentEventTypes {\r\n response: any;\r\n authenticate: any;\r\n configure: ConfigurationFile;\r\n 'defer-update': {\r\n logs: string[], \r\n progress: number\r\n };\r\n notification: Notification;\r\n 'input-asked': ConfigurationBuilder;\r\n}\r\n\r\nexport type BasicLibraryInfo = {\r\n name: string;\r\n capsuleImage: string;\r\n appID: number;\r\n}\r\n\r\nexport interface EventListenerTypes {\r\n connect: (socket: ws) => void;\r\n disconnect: (reason: string) => void;\r\n configure: (config: ConfigurationBuilder) => ConfigurationBuilder;\r\n response: (response: any) => void;\r\n authenticate: (config: any) => void;\r\n search: (query: { type: 'steamapp' | 'internal', text: string }, event: EventResponse<SearchResult[]>) => void;\r\n setup: (\r\n data: { \r\n path: string, \r\n type: 'direct' | 'torrent' | 'magnet',\r\n name: string,\r\n usedRealDebrid: boolean, \r\n multiPartFiles?: {\r\n name: string,\r\n downloadURL: string\r\n }[],\r\n appID: number,\r\n storefront: 'steam' | 'internal'\r\n }, event: EventResponse<LibraryInfo>\r\n ) => void;\r\n 'library-search': (query: string, event: EventResponse<BasicLibraryInfo[]>) => void;\r\n 'game-details': (appID: number, event: EventResponse<StoreData>) => void;\r\n exit: () => void;\r\n}\r\n\r\nexport interface StoreData {\r\n name: string;\r\n publishers: string[];\r\n developers: string[];\r\n appID: number;\r\n releaseDate: string;\r\n capsuleImage: string;\r\n coverImage: string;\r\n basicDescription: string;\r\n description: string;\r\n headerImage: string;\r\n}\r\nexport interface WebsocketMessageClient {\r\n event: OGIAddonClientSentEvent;\r\n id?: string;\r\n args: any;\r\n}\r\nexport interface WebsocketMessageServer {\r\n event: OGIAddonServerSentEvent;\r\n id?: string;\r\n args: any;\r\n}\r\nexport interface OGIAddonConfiguration {\r\n name: string;\r\n id: string;\r\n description: string;\r\n version: string;\r\n\r\n author: string;\r\n repository: string;\r\n}\r\nexport default class OGIAddon {\r\n public eventEmitter = new events.EventEmitter();\r\n public addonWSListener: OGIAddonWSListener;\r\n public addonInfo: OGIAddonConfiguration;\r\n public config: Configuration = new Configuration({});\r\n\r\n constructor(addonInfo: OGIAddonConfiguration) {\r\n this.addonInfo = addonInfo;\r\n this.addonWSListener = new OGIAddonWSListener(this, this.eventEmitter);\r\n }\r\n \r\n public on<T extends OGIAddonEvent>(event: T, listener: EventListenerTypes[T]) {\r\n this.eventEmitter.on(event, listener);\r\n }\r\n\r\n public emit<T extends OGIAddonEvent>(event: T, ...args: Parameters<EventListenerTypes[T]>) {\r\n this.eventEmitter.emit(event, ...args);\r\n }\r\n\r\n public notify(notification: Notification) {\r\n this.addonWSListener.send('notification', [ notification ]);\r\n }\r\n}\r\n\r\nexport interface LibraryInfo {\r\n name: string;\r\n version: string;\r\n cwd: string;\r\n appID: number;\r\n launchExecutable: string;\r\n launchArguments?: string;\r\n capsuleImage: string;\r\n storefront: 'steam' | 'internal';\r\n addonsource: string;\r\n coverImage: string;\r\n titleImage?: string;\r\n}\r\ninterface Notification {\r\n type: 'warning' | 'error' | 'info' | 'success';\r\n message: string;\r\n id: string\r\n}\r\nclass OGIAddonWSListener {\r\n private socket: WebSocket;\r\n public eventEmitter: events.EventEmitter;\r\n public addon: OGIAddon;\r\n\r\n constructor(ogiAddon: OGIAddon, eventEmitter: events.EventEmitter) {\r\n if (process.argv[process.argv.length - 1].split('=')[0] !== '--addonSecret') {\r\n throw new Error('No secret provided. This usually happens because the addon was not started by the OGI Addon Server.');\r\n }\r\n this.addon = ogiAddon;\r\n this.eventEmitter = eventEmitter;\r\n this.socket = new ws('ws://localhost:' + defaultPort);\r\n this.socket.on('open', () => {\r\n console.log('Connected to OGI Addon Server');\r\n console.log('OGI Addon Server Version:', version);\r\n\r\n // Authenticate with OGI Addon Server\r\n this.socket.send(JSON.stringify({\r\n event: 'authenticate',\r\n args: {\r\n ...this.addon.addonInfo,\r\n secret: process.argv[process.argv.length - 1].split('=')[1],\r\n ogiVersion: version\r\n }\r\n }));\r\n\r\n this.eventEmitter.emit('connect');\r\n\r\n // send a configuration request\r\n let configBuilder = new ConfigurationBuilder();\r\n this.eventEmitter.emit('configure', configBuilder);\r\n \r\n this.socket.send(JSON.stringify({\r\n event: 'configure',\r\n args: configBuilder.build(false) \r\n }));\r\n this.addon.config = new Configuration(configBuilder.build(true));\r\n });\r\n\r\n this.socket.on('error', (error) => {\r\n if (error.message.includes('Failed to connect')) {\r\n throw new Error('OGI Addon Server is not running/is unreachable. Please start the server and try again.');\r\n }\r\n console.error('An error occurred:', error);\r\n })\r\n\r\n this.socket.on('close', (code, reason) => {\r\n if (code === 1008) {\r\n console.error('Authentication failed:', reason);\r\n return;\r\n }\r\n this.eventEmitter.emit('disconnect', reason);\r\n console.log(\"Disconnected from OGI Addon Server\")\r\n console.error(reason.toString())\r\n this.eventEmitter.emit('exit');\r\n this.socket.close();\r\n });\r\n\r\n this.registerMessageReceiver();\r\n }\r\n\r\n private async userInputAsked(configBuilt: ConfigurationBuilder, name: string, description: string, socket: WebSocket): Promise<{ [key: string]: number | boolean | string }> {\r\n const config = configBuilt.build(false);\r\n const id = Math.random().toString(36).substring(7);\r\n if (!socket) {\r\n return {};\r\n }\r\n socket.send(JSON.stringify({\r\n event: 'input-asked',\r\n args: {\r\n config,\r\n name,\r\n description\r\n },\r\n id: id\r\n }));\r\n return await this.waitForResponseFromServer(id);\r\n }\r\n\r\n private registerMessageReceiver() {\r\n this.socket.on('message', async (data: string) => {\r\n const message: WebsocketMessageServer = JSON.parse(data);\r\n switch (message.event) {\r\n case 'config-update':\r\n const result = this.addon.config.updateConfig(message.args);\r\n if (!result[0]) {\r\n this.respondToMessage(message.id!!, { success: false, error: result[1] });\r\n }\r\n else {\r\n this.respondToMessage(message.id!!, { success: true });\r\n }\r\n break \r\n case 'search':\r\n let searchResultEvent = new EventResponse<SearchResult[]>((screen, name, description) => this.userInputAsked(screen, name, description, this.socket));\r\n this.eventEmitter.emit('search', message.args, searchResultEvent);\r\n const searchResult = await this.waitForEventToRespond(searchResultEvent); \r\n this.respondToMessage(message.id!!, searchResult.data);\r\n break\r\n case 'setup':\r\n let setupEvent = new EventResponse<LibraryInfo>((screen, name, description) => this.userInputAsked(screen, name, description, this.socket));\r\n this.eventEmitter.emit('setup', { path: message.args.path, appID: message.args.appID, storefront: message.args.storefront, type: message.args.type, name: message.args.name, usedRealDebrid: message.args.usedRealDebrid, multiPartFiles: message.args.multiPartFiles }, setupEvent);\r\n const interval = setInterval(() => {\r\n if (setupEvent.resolved) {\r\n clearInterval(interval);\r\n return;\r\n }\r\n this.send('defer-update', { \r\n logs: setupEvent.logs,\r\n deferID: message.args.deferID,\r\n progress: setupEvent.progress\r\n } as any);\r\n }, 100);\r\n const setupResult = await this.waitForEventToRespond(setupEvent);\r\n this.respondToMessage(message.id!!, setupResult.data);\r\n break\r\n case 'library-search':\r\n let librarySearchEvent = new EventResponse<BasicLibraryInfo[]>((screen, name, description) => this.userInputAsked(screen, name, description, this.socket));\r\n if (this.eventEmitter.listenerCount('game-details') === 0) {\r\n this.respondToMessage(message.id!!, []);\r\n break;\r\n }\r\n this.eventEmitter.emit('library-search', message.args, librarySearchEvent);\r\n const librarySearchResult = await this.waitForEventToRespond(librarySearchEvent);\r\n this.respondToMessage(message.id!!, librarySearchResult.data);\r\n break\r\n case 'game-details':\r\n let gameDetailsEvent = new EventResponse<StoreData>((screen, name, description) => this.userInputAsked(screen, name, description, this.socket));\r\n if (this.eventEmitter.listenerCount('game-details') === 0) {\r\n this.respondToMessage(message.id!!, { error: 'No event listener for game-details' });\r\n break;\r\n }\r\n this.eventEmitter.emit('game-details', message.args, gameDetailsEvent);\r\n const gameDetailsResult = await this.waitForEventToRespond(gameDetailsEvent);\r\n this.respondToMessage(message.id!!, gameDetailsResult.data);\r\n break\r\n }\r\n });\r\n }\r\n\r\n private waitForEventToRespond<T>(event: EventResponse<T>): Promise<EventResponse<T>> {\r\n // check the handlers to see if there even is any\r\n return new Promise((resolve, reject) => {\r\n const dataGet = setInterval(() => {\r\n if (event.resolved) {\r\n resolve(event);\r\n clearTimeout(timeout);\r\n }\r\n }, 5); \r\n\r\n const timeout = setTimeout(() => {\r\n if (event.deffered) {\r\n clearInterval(dataGet);\r\n const interval = setInterval(() => {\r\n if (event.resolved) {\r\n clearInterval(interval);\r\n resolve(event);\r\n }\r\n }, 100);\r\n }\r\n else {\r\n reject('Event did not respond in time');\r\n }\r\n }, 5000)\r\n });\r\n }\r\n\r\n public respondToMessage(messageID: string, response: any) {\r\n this.socket.send(JSON.stringify({\r\n event: 'response',\r\n id: messageID,\r\n args: response\r\n }));\r\n console.log(\"dispatched response to \" + messageID)\r\n }\r\n\r\n public waitForResponseFromServer<T>(messageID: string): Promise<T> {\r\n return new Promise((resolve) => {\r\n const waiter = (data: string) => {\r\n const message: WebsocketMessageClient = JSON.parse(data);\r\n if (message.event !== 'response') {\r\n this.socket.once('message', waiter);\r\n return;\r\n }\r\n console.log(\"received response from \" + messageID)\r\n\r\n if (message.id === messageID) {\r\n resolve(message.args);\r\n }\r\n else {\r\n this.socket.once('message', waiter);\r\n }\r\n }\r\n this.socket.once('message', waiter);\r\n });\r\n }\r\n\r\n public send(event: OGIAddonClientSentEvent, args: Parameters<ClientSentEventTypes[OGIAddonClientSentEvent]>) {\r\n this.socket.send(JSON.stringify({\r\n event,\r\n args\r\n }));\r\n }\r\n\r\n public close() {\r\n this.socket.close();\r\n }\r\n\r\n \r\n}","import z, { ZodError } from \"zod\"\r\n\r\nexport interface ConfigurationFile {\r\n [key: string]: ConfigurationOption\r\n}\r\n\r\nconst configValidation = z.object({\r\n name: z.string().min(1),\r\n displayName: z.string().min(1),\r\n description: z.string().min(1),\r\n})\r\n\r\nexport function isStringOption(option: ConfigurationOption): option is StringOption {\r\n return option.type === 'string';\r\n }\r\n\r\nexport function isNumberOption(option: ConfigurationOption): option is NumberOption {\r\n return option.type === 'number';\r\n}\r\n\r\nexport function isBooleanOption(option: ConfigurationOption): option is BooleanOption {\r\n return option.type === 'boolean';\r\n}\r\n\r\nexport class ConfigurationBuilder {\r\n private options: ConfigurationOption[] = [];\r\n public addNumberOption(option: (option: NumberOption) => NumberOption): ConfigurationBuilder {\r\n let newOption = new NumberOption();\r\n newOption = option(newOption);\r\n this.options.push(newOption);\r\n return this;\r\n }\r\n\r\n public addStringOption(option: (option: StringOption) => StringOption) {\r\n let newOption = new StringOption();\r\n newOption = option(newOption);\r\n this.options.push(newOption);\r\n return this;\r\n }\r\n\r\n public addBooleanOption(option: (option: BooleanOption) => BooleanOption) {\r\n let newOption = new BooleanOption();\r\n newOption = option(newOption);\r\n this.options.push(newOption);\r\n return this;\r\n }\r\n\r\n public build(includeFunctions: boolean): ConfigurationFile {\r\n let config: ConfigurationFile = {};\r\n this.options.forEach(option => {\r\n // remove all functions from the option object\r\n if (!includeFunctions) {\r\n option = JSON.parse(JSON.stringify(option));\r\n const optionData = configValidation.safeParse(option)\r\n if (!optionData.success) {\r\n throw new ZodError(optionData.error.errors)\r\n }\r\n\r\n config[option.name] = option;\r\n }\r\n else {\r\n config[option.name] = option;\r\n }\r\n });\r\n return config;\r\n }\r\n}\r\n\r\nexport type ConfigurationOptionType = 'string' | 'number' | 'boolean' | 'unset'\r\nexport class ConfigurationOption {\r\n public name: string = '';\r\n public defaultValue: unknown = '';\r\n public displayName: string = '';\r\n public description: string = '';\r\n public type: ConfigurationOptionType = 'unset'\r\n \r\n setName(name: string) {\r\n this.name = name;\r\n return this;\r\n }\r\n\r\n setDisplayName(displayName: string) {\r\n this.displayName = displayName;\r\n return this;\r\n }\r\n\r\n setDescription(description: string) {\r\n this.description = description;\r\n return this;\r\n }\r\n\r\n\r\n validate(input: unknown): [ boolean, string ] {\r\n throw new Error('Validation code not implemented. Value: ' + input)\r\n };\r\n}\r\n\r\nexport class StringOption extends ConfigurationOption {\r\n public allowedValues: string[] = [];\r\n public minTextLength: number = 0;\r\n public maxTextLength: number = Number.MAX_SAFE_INTEGER;\r\n public defaultValue: string = '';\r\n public inputType: 'text' | 'file' | 'password' | 'folder' = 'text';\r\n public type: ConfigurationOptionType = 'string'\r\n\r\n setAllowedValues(allowedValues: string[]): this {\r\n this.allowedValues = allowedValues;\r\n return this;\r\n }\r\n\r\n setDefaultValue(defaultValue: string): this {\r\n this.defaultValue = defaultValue;\r\n return this;\r\n }\r\n\r\n setMinTextLength(minTextLength: number): this {\r\n this.minTextLength = minTextLength;\r\n return this;\r\n }\r\n\r\n setMaxTextLength(maxTextLength: number): this {\r\n this.maxTextLength = maxTextLength;\r\n return this;\r\n }\r\n\r\n setInputType(inputType: 'text' | 'file' | 'password' | 'folder'): this {\r\n this.inputType = inputType;\r\n return this;\r\n }\r\n\r\n override validate(input: unknown): [ boolean, string ] {\r\n if (typeof input !== 'string') {\r\n return [ false, 'Input is not a string' ];\r\n }\r\n if (this.allowedValues.length === 0 && input.length !== 0)\r\n return [ true, '' ];\r\n if (input.length < this.minTextLength || input.length > this.maxTextLength) {\r\n return [ false, 'Input is not within the text length ' + this.minTextLength + ' and ' + this.maxTextLength + ' characters (currently ' + input.length + ' characters)' ];\r\n }\r\n\r\n return [ this.allowedValues.includes(input), 'Input is not an allowed value' ];\r\n }\r\n}\r\n\r\nexport class NumberOption extends ConfigurationOption {\r\n public min: number = 0;\r\n public max: number = Number.MAX_SAFE_INTEGER;\r\n public defaultValue: number = 0;\r\n public type: ConfigurationOptionType = 'number'\r\n public inputType: 'range' | 'number' = 'number';\r\n setMin(min: number): this {\r\n this.min = min;\r\n return this;\r\n }\r\n\r\n setInputType(type: 'range' | 'number'): this {\r\n this.inputType = type;\r\n return this;\r\n }\r\n\r\n setMax(max: number): this {\r\n this.max = max;\r\n return this\r\n }\r\n\r\n setDefaultValue(defaultValue: number): this {\r\n this.defaultValue = defaultValue;\r\n return this;\r\n }\r\n\r\n override validate(input: unknown): [ boolean, string ] {\r\n if (isNaN(Number(input))) {\r\n return [ false, 'Input is not a number' ];\r\n }\r\n if (Number(input) < this.min || Number(input) > this.max) {\r\n return [ false, 'Input is not within the range of ' + this.min + ' and ' + this.max ];\r\n }\r\n return [ true, '' ];\r\n }\r\n\r\n}\r\n\r\nexport class BooleanOption extends ConfigurationOption {\r\n public type: ConfigurationOptionType = 'boolean'\r\n public defaultValue: boolean = false;\r\n\r\n setDefaultValue(defaultValue: boolean): this {\r\n this.defaultValue = defaultValue;\r\n return this;\r\n }\r\n\r\n override validate(input: unknown): [ boolean, string ] {\r\n if (typeof input !== 'boolean') {\r\n return [ false, 'Input is not a boolean' ];\r\n }\r\n return [ true, '' ];\r\n }\r\n\r\n}","import { ConfigurationFile, ConfigurationBuilder, BooleanOption, ConfigurationOption, ConfigurationOptionType, NumberOption, StringOption, isBooleanOption, isNumberOption, isStringOption } from \"./ConfigurationBuilder\";\r\n\r\ninterface DefiniteConfig {\r\n [key: string]: string | number | boolean;\r\n}\r\nexport class Configuration {\r\n readonly storedConfigTemplate: ConfigurationFile;\r\n definiteConfig: DefiniteConfig = {};\r\n constructor(configTemplate: ConfigurationFile) {\r\n this.storedConfigTemplate = configTemplate;\r\n }\r\n\r\n updateConfig(config: DefiniteConfig, validate: boolean = true): [ boolean, { [key: string]: string } ] {\r\n this.definiteConfig = config;\r\n if (validate) {\r\n const result = this.validateConfig();\r\n return result;\r\n }\r\n return [ true, {} ];\r\n }\r\n // provides falsey or truthy value, and an error message if falsey\r\n private validateConfig(): [ boolean, { [key: string]: string } ] {\r\n const erroredKeys = new Map<string, string>();\r\n for (const key in this.storedConfigTemplate) {\r\n if (this.definiteConfig[key] === null || this.definiteConfig[key] === undefined) {\r\n console.warn('Option ' + key + ' is not defined. Using default value Value: ' + this.definiteConfig[key]);\r\n this.definiteConfig[key] = this.storedConfigTemplate[key].defaultValue as string | number | boolean;\r\n }\r\n if (this.storedConfigTemplate[key].type !== typeof this.definiteConfig[key]) {\r\n throw new Error('Option ' + key + ' is not of the correct type');\r\n }\r\n\r\n const result = this.storedConfigTemplate[key].validate(this.definiteConfig[key]);\r\n if (!result[0]) {\r\n erroredKeys.set(key, result[1]);\r\n }\r\n }\r\n\r\n for (const key in this.definiteConfig) {\r\n if (!this.storedConfigTemplate[key]) {\r\n throw new Error('Option ' + key + ' is not defined in the configuration template');\r\n }\r\n }\r\n\r\n if (erroredKeys.size > 0) {\r\n return [ false, Object.fromEntries(erroredKeys) ];\r\n }\r\n\r\n return [ true, Object.fromEntries(erroredKeys) ];\r\n }\r\n\r\n getStringValue(optionName: string): string {\r\n if (!this.definiteConfig[optionName] === null) {\r\n throw new Error('Option ' + optionName + ' is not defined');\r\n }\r\n if (typeof this.definiteConfig[optionName] !== 'string') {\r\n throw new Error('Option ' + optionName + ' is not a string');\r\n }\r\n return this.definiteConfig[optionName];\r\n }\r\n\r\n getNumberValue(optionName: string): number {\r\n if (!this.definiteConfig[optionName] === null) {\r\n throw new Error('Option ' + optionName + ' is not defined');\r\n }\r\n if (typeof this.definiteConfig[optionName] !== 'number') {\r\n throw new Error('Option ' + optionName + ' is not a number');\r\n }\r\n return this.definiteConfig[optionName];\r\n }\r\n\r\n getBooleanValue(optionName: string): boolean {\r\n if (this.definiteConfig[optionName] === null) {\r\n throw new Error('Option ' + optionName + ' is not defined');\r\n }\r\n if (typeof this.definiteConfig[optionName] !== 'boolean') {\r\n throw new Error('Option ' + optionName + ' is not a boolean');\r\n }\r\n return this.definiteConfig[optionName];\r\n }\r\n}\r\n\r\nexport { ConfigurationFile, ConfigurationBuilder, BooleanOption, ConfigurationOption, ConfigurationOptionType, NumberOption, StringOption, isBooleanOption, isNumberOption, isStringOption };","import { ConfigurationBuilder } from \"./main\";\r\n\r\nexport default class EventResponse<T> {\r\n data: T | undefined = undefined;\r\n deffered: boolean = false;\r\n resolved: boolean = false;\r\n progress: number = 0;\r\n logs: string[] = [];\r\n onInputAsked?: (screen: ConfigurationBuilder, name: string, description: string) => Promise<{ [key: string]: boolean | string | number }>;\r\n\r\n constructor(onInputAsked?: (screen: ConfigurationBuilder, name: string, description: string) => Promise<{ [key: string]: boolean | string | number }>) {\r\n this.onInputAsked = onInputAsked;\r\n }\r\n \r\n\r\n public defer() {\r\n this.deffered = true;\r\n }\r\n\r\n public resolve(data: T) {\r\n this.resolved = true;\r\n this.data = data;\r\n }\r\n\r\n public complete() {\r\n this.resolved = true;\r\n }\r\n\r\n public log(message: string) {\r\n this.logs.push(message);\r\n }\r\n\r\n public async askForInput(name: string, description: string, screen: ConfigurationBuilder) {\r\n if (!this.onInputAsked) {\r\n throw new Error('No input asked callback');\r\n }\r\n return await this.onInputAsked(screen, name, description);\r\n }\r\n\r\n \r\n}","{\r\n \"name\": \"ogi-addon\",\r\n \"module\": \"./build/main.js\",\r\n \"type\": \"module\",\r\n \"main\": \"./build/main.cjs\",\r\n \"exports\": {\r\n \".\": {\r\n \"import\": {\r\n \"default\": \"./build/main.js\",\r\n \"types\": \"./build/main.d.ts\"\r\n },\r\n \"require\": {\r\n \"default\": \"./build/main.cjs\",\r\n \"types\": \"./build/main.d.cts\"\r\n }\r\n },\r\n \"./config\": {\r\n \"import\": {\r\n \"default\": \"./build/config/Configuration.js\",\r\n \"types\": \"./build/config/Configuration.d.ts\"\r\n },\r\n \"require\": {\r\n \"default\": \"./build/config/Configuration.cjs\",\r\n \"types\": \"./build/config/Configuration.d.cts\"\r\n }\r\n }\r\n },\r\n \"typings\": \"./build/main.d.ts\",\r\n \"author\": {\r\n \"name\": \"Nat3z\",\r\n \"email\": \"me@nat3z.com\",\r\n \"url\": \"https://nat3z.com/\"\r\n },\r\n \"version\": \"1.0.0\",\r\n \"dependencies\": {\r\n \"ws\": \"^8.4.0\",\r\n \"zod\": \"^3.23.8\"\r\n },\r\n \"scripts\": {\r\n \"auto-build\": \"tsc -w\",\r\n \"build\": \"tsup --config tsup.config.js\",\r\n \"release\": \"bun run build && npm publish\"\r\n },\r\n \"devDependencies\": {\r\n \"@types/node\": \"^20.14.12\",\r\n \"@types/ws\": \"^8.4.0\",\r\n \"tsup\": \"^8.2.3\",\r\n \"typescript\": \"^5.0.0\"\r\n }\r\n}"],"mappings":"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","names":["z","events","ws"]}
1
+ {"version":3,"sources":["../src/main.ts","../src/config/ConfigurationBuilder.ts","../src/config/Configuration.ts","../src/EventResponse.ts","../package.json"],"sourcesContent":["import ws, { WebSocket } from 'ws';\nimport events from 'node:events';\nimport { ConfigurationBuilder, ConfigurationFile } from './config/ConfigurationBuilder';\nimport { Configuration } from './config/Configuration';\nimport EventResponse from './EventResponse';\nimport { SearchResult } from './SearchEngine';\nimport Fuse from 'fuse.js';\n\nexport type OGIAddonEvent = 'connect' | 'disconnect' | 'configure' | 'authenticate' | 'search' | 'setup' | 'library-search' | 'game-details' | 'exit' | 'request-dl';\nexport type OGIAddonClientSentEvent = 'response' | 'authenticate' | 'configure' | 'defer-update' | 'notification' | 'input-asked' | 'steam-search' | 'task-update';\n\nexport type OGIAddonServerSentEvent = 'authenticate' | 'configure' | 'config-update' | 'search' | 'setup' | 'response' | 'library-search' | 'game-details' | 'request-dl';\nexport { ConfigurationBuilder, Configuration, EventResponse, SearchResult };\nconst defaultPort = 7654;\nimport pjson from '../package.json';\nexport const VERSION = pjson.version;\n\nexport interface ClientSentEventTypes {\n response: any;\n authenticate: {\n name: string;\n id: string;\n description: string;\n version: string;\n author: string;\n };\n configure: ConfigurationFile;\n 'defer-update': {\n logs: string[],\n progress: number\n };\n notification: Notification;\n 'input-asked': ConfigurationBuilder;\n 'steam-search': {\n query: string;\n strict: boolean;\n };\n 'task-update': {\n id: string;\n progress: number;\n logs: string[];\n finished: boolean;\n };\n}\n\nexport type BasicLibraryInfo = {\n name: string;\n capsuleImage: string;\n appID: number;\n}\n\nexport interface EventListenerTypes {\n /**\n * This event is emitted when the addon connects to the OGI Addon Server. Addon does not need to resolve anything.\n * @param socket \n * @returns \n */\n connect: (socket: ws) => void;\n\n /**\n * This event is emitted when the client requests for the addon to disconnect. Addon does not need to resolve this event, but we recommend `process.exit(0)` so the addon can exit gracefully instead of by force by the addon server.\n * @param reason \n * @returns \n */\n disconnect: (reason: string) => void;\n /**\n * This event is emitted when the client requests for the addon to configure itself. Addon should resolve the event with the internal configuration. (See ConfigurationBuilder) \n * @param config \n * @returns \n */\n configure: (config: ConfigurationBuilder) => ConfigurationBuilder;\n /**\n * This event is called when the client provides a response to any event. This should be treated as middleware. \n * @param response \n * @returns \n */\n response: (response: any) => void;\n\n /**\n * This event is called when the client requests for the addon to authenticate itself. You don't need to provide any info. \n * @param config \n * @returns \n */\n authenticate: (config: any) => void;\n /**\n * This event is emitted when the client requests for a torrent/direct download search to be performed. Addon is given the gameID (could be a steam appID or custom store appID), along with the storefront type. Addon should resolve the event with the search results. (See SearchResult) \n * @param query \n * @param event \n * @returns \n */\n search: (query: { type: 'steamapp' | 'internal', text: string }, event: EventResponse<SearchResult[]>) => void;\n /**\n * This event is emitted when the client requests for app setup to be performed. Addon should resolve the event with the metadata for the library entry. (See LibraryInfo)\n * @param data \n * @param event \n * @returns \n */\n setup: (\n data: {\n path: string,\n type: 'direct' | 'torrent' | 'magnet',\n name: string,\n usedRealDebrid: boolean,\n multiPartFiles?: {\n name: string,\n downloadURL: string\n }[],\n appID: number,\n storefront: 'steam' | 'internal'\n }, event: EventResponse<LibraryInfo>\n ) => void;\n\n /**\n * This event is emitted when the client requires for a search to be performed. Input is the search query. \n * @param query \n * @param event \n * @returns \n */\n 'library-search': (query: string, event: EventResponse<BasicLibraryInfo[]>) => void;\n 'game-details': (appID: number, event: EventResponse<StoreData>) => void;\n exit: () => void;\n 'request-dl': (appID: number, info: SearchResult, event: EventResponse<SearchResult>) => void;\n}\n\nexport interface StoreData {\n name: string;\n publishers: string[];\n developers: string[];\n appID: number;\n releaseDate: string;\n capsuleImage: string;\n coverImage: string;\n basicDescription: string;\n description: string;\n headerImage: string;\n}\nexport interface WebsocketMessageClient {\n event: OGIAddonClientSentEvent;\n id?: string;\n args: any;\n}\nexport interface WebsocketMessageServer {\n event: OGIAddonServerSentEvent;\n id?: string;\n args: any;\n}\nexport interface OGIAddonConfiguration {\n name: string;\n id: string;\n description: string;\n version: string;\n\n author: string;\n repository: string;\n}\n\n/**\n * The main class for the OGI Addon. This class is used to interact with the OGI Addon Server. The OGI Addon Server provides a `--addonSecret` to the addon so it can securely connect.\n * @example \n * ```typescript\n * const addon = new OGIAddon({\n * name: 'Test Addon',\n* id: 'test-addon',\n * description: 'A test addon',\n * version: '1.0.0',\n * author: 'OGI Developers',\n * repository: ''\n * });\n * ```\n * \n */\nexport default class OGIAddon {\n public eventEmitter = new events.EventEmitter();\n public addonWSListener: OGIAddonWSListener;\n public addonInfo: OGIAddonConfiguration;\n public config: Configuration = new Configuration({});\n\n constructor(addonInfo: OGIAddonConfiguration) {\n this.addonInfo = addonInfo;\n this.addonWSListener = new OGIAddonWSListener(this, this.eventEmitter);\n }\n\n /**\n * Register an event listener for the addon. (See EventListenerTypes) \n * @param event {OGIAddonEvent}\n * @param listener {EventListenerTypes[OGIAddonEvent]} \n */\n public on<T extends OGIAddonEvent>(event: T, listener: EventListenerTypes[T]) {\n this.eventEmitter.on(event, listener);\n }\n\n public emit<T extends OGIAddonEvent>(event: T, ...args: Parameters<EventListenerTypes[T]>) {\n this.eventEmitter.emit(event, ...args);\n }\n\n /**\n * Notify the client using a notification. Provide the type of notification, the message, and an ID. \n * @param notification {Notification}\n */\n public notify(notification: Notification) {\n this.addonWSListener.send('notification', [notification]);\n }\n\n /**\n * Search for items in the OGI Steam-Synced Library. Query can either be a Steam AppID or a Steam Game Name.\n * @param query {string}\n * @param event {EventResponse<BasicLibraryInfo[]>}\n */\n public async steamSearch(query: string, strict: boolean = false) {\n const id = this.addonWSListener.send('steam-search', { query, strict });\n return await this.addonWSListener.waitForResponseFromServer<Omit<BasicLibraryInfo, 'capsuleImage'>[]>(id);\n }\n\n /**\n * Notify the OGI Addon Server that you are performing a background task. This can be used to help users understand what is happening in the background.\n * @param id {string}\n * @param progress {number}\n * @param logs {string[]}\n */\n public async task() {\n const id = Math.random().toString(36).substring(7);\n const progress = 0;\n const logs: string[] = [];\n const task = new CustomTask(this.addonWSListener, id, progress, logs);\n this.addonWSListener.send('task-update', { id, progress, logs, finished: false });\n return task;\n }\n}\n\nclass CustomTask {\n public readonly id: string;\n public progress: number;\n public logs: string[];\n public finished: boolean = false;\n public ws: OGIAddonWSListener;\n constructor(ws: OGIAddonWSListener, id: string, progress: number, logs: string[]) {\n this.id = id;\n this.progress = progress;\n this.logs = logs;\n this.ws = ws;\n }\n public log(log: string) {\n this.logs.push(log);\n this.update();\n }\n public finish() {\n this.finished = true;\n this.update();\n }\n public setProgress(progress: number) {\n this.progress = progress;\n this.update();\n }\n public update() {\n this.ws.send('task-update', { id: this.id, progress: this.progress, logs: this.logs, finished: this.finished });\n }\n}\n/**\n * A search tool for the OGI Addon. This tool is used to search for items in the library. Powered by Fuse.js, bundled into OGI.\n * @example\n * ```typescript\n * const searchTool = new SearchTool<LibraryInfo>([], ['name']);\n * const results = searchTool.search('test', 10);\n * ```\n */\nexport class SearchTool<T> {\n private fuse: Fuse<T>;\n constructor(items: T[], keys: string[]) {\n this.fuse = new Fuse(items, {\n keys,\n threshold: 0.3,\n includeScore: true\n });\n }\n public search(query: string, limit: number = 10): T[] {\n return this.fuse.search(query).slice(0, limit).map(result => result.item);\n }\n public addItems(items: T[]) {\n items.map(item => this.fuse.add(item));\n }\n}\n/**\n * Library Info is the metadata for a library entry after setting up a game.\n */\nexport interface LibraryInfo {\n name: string;\n version: string;\n cwd: string;\n appID: number;\n launchExecutable: string;\n launchArguments?: string;\n capsuleImage: string;\n storefront: 'steam' | 'internal';\n addonsource: string;\n coverImage: string;\n titleImage?: string;\n}\ninterface Notification {\n type: 'warning' | 'error' | 'info' | 'success';\n message: string;\n id: string\n}\nclass OGIAddonWSListener {\n private socket: WebSocket;\n public eventEmitter: events.EventEmitter;\n public addon: OGIAddon;\n\n constructor(ogiAddon: OGIAddon, eventEmitter: events.EventEmitter) {\n if (process.argv[process.argv.length - 1].split('=')[0] !== '--addonSecret') {\n throw new Error('No secret provided. This usually happens because the addon was not started by the OGI Addon Server.');\n }\n this.addon = ogiAddon;\n this.eventEmitter = eventEmitter;\n this.socket = new ws('ws://localhost:' + defaultPort);\n this.socket.on('open', () => {\n console.log('Connected to OGI Addon Server');\n console.log('OGI Addon Server Version:', VERSION);\n\n // Authenticate with OGI Addon Server\n this.send('authenticate', {\n ...this.addon.addonInfo,\n secret: process.argv[process.argv.length - 1].split('=')[1],\n ogiVersion: VERSION\n });\n\n this.eventEmitter.emit('connect');\n\n // send a configuration request\n let configBuilder = new ConfigurationBuilder();\n this.eventEmitter.emit('configure', configBuilder);\n this.send('configure', configBuilder.build(false));\n this.addon.config = new Configuration(configBuilder.build(true));\n });\n\n this.socket.on('error', (error) => {\n if (error.message.includes('Failed to connect')) {\n throw new Error('OGI Addon Server is not running/is unreachable. Please start the server and try again.');\n }\n console.error('An error occurred:', error);\n })\n\n this.socket.on('close', (code, reason) => {\n if (code === 1008) {\n console.error('Authentication failed:', reason);\n return;\n }\n this.eventEmitter.emit('disconnect', reason);\n console.log(\"Disconnected from OGI Addon Server\")\n console.error(reason.toString())\n this.eventEmitter.emit('exit');\n this.socket.close();\n });\n\n this.registerMessageReceiver();\n }\n\n private async userInputAsked(configBuilt: ConfigurationBuilder, name: string, description: string, socket: WebSocket): Promise<{ [key: string]: number | boolean | string }> {\n const config = configBuilt.build(false);\n const id = Math.random().toString(36).substring(7);\n if (!socket) {\n return {};\n }\n socket.send(JSON.stringify({\n event: 'input-asked',\n args: {\n config,\n name,\n description\n },\n id: id\n }));\n return await this.waitForResponseFromServer(id);\n }\n\n private registerMessageReceiver() {\n this.socket.on('message', async (data: string) => {\n const message: WebsocketMessageServer = JSON.parse(data);\n switch (message.event) {\n case 'config-update':\n const result = this.addon.config.updateConfig(message.args);\n if (!result[0]) {\n this.respondToMessage(message.id!!, { success: false, error: result[1] });\n }\n else {\n this.respondToMessage(message.id!!, { success: true });\n }\n break\n case 'search':\n let searchResultEvent = new EventResponse<SearchResult[]>((screen, name, description) => this.userInputAsked(screen, name, description, this.socket));\n this.eventEmitter.emit('search', message.args, searchResultEvent);\n const searchResult = await this.waitForEventToRespond(searchResultEvent);\n this.respondToMessage(message.id!!, searchResult.data);\n break\n case 'setup':\n let setupEvent = new EventResponse<LibraryInfo>((screen, name, description) => this.userInputAsked(screen, name, description, this.socket));\n this.eventEmitter.emit('setup', { path: message.args.path, appID: message.args.appID, storefront: message.args.storefront, type: message.args.type, name: message.args.name, usedRealDebrid: message.args.usedRealDebrid, multiPartFiles: message.args.multiPartFiles }, setupEvent);\n const interval = setInterval(() => {\n if (setupEvent.resolved) {\n clearInterval(interval);\n return;\n }\n this.send('defer-update', {\n logs: setupEvent.logs,\n deferID: message.args.deferID,\n progress: setupEvent.progress\n } as any);\n }, 100);\n const setupResult = await this.waitForEventToRespond(setupEvent);\n this.respondToMessage(message.id!!, setupResult.data);\n break\n case 'library-search':\n let librarySearchEvent = new EventResponse<BasicLibraryInfo[]>((screen, name, description) => this.userInputAsked(screen, name, description, this.socket));\n if (this.eventEmitter.listenerCount('game-details') === 0) {\n this.respondToMessage(message.id!!, []);\n break;\n }\n this.eventEmitter.emit('library-search', message.args, librarySearchEvent);\n const librarySearchResult = await this.waitForEventToRespond(librarySearchEvent);\n this.respondToMessage(message.id!!, librarySearchResult.data);\n break\n case 'game-details':\n let gameDetailsEvent = new EventResponse<StoreData>((screen, name, description) => this.userInputAsked(screen, name, description, this.socket));\n if (this.eventEmitter.listenerCount('game-details') === 0) {\n this.respondToMessage(message.id!!, { error: 'No event listener for game-details' });\n break;\n }\n this.eventEmitter.emit('game-details', message.args, gameDetailsEvent);\n const gameDetailsResult = await this.waitForEventToRespond(gameDetailsEvent);\n this.respondToMessage(message.id!!, gameDetailsResult.data);\n break\n case 'request-dl':\n let requestDLEvent = new EventResponse<SearchResult>((screen, name, description) => this.userInputAsked(screen, name, description, this.socket));\n if (this.eventEmitter.listenerCount('request-dl') === 0) {\n this.respondToMessage(message.id!!, { error: 'No event listener for request-dl' });\n break;\n }\n this.eventEmitter.emit('request-dl', message.args.appID, message.args.info, requestDLEvent);\n const requestDLResult = await this.waitForEventToRespond(requestDLEvent);\n if (requestDLEvent.data === null || requestDLEvent.data?.downloadType === 'request') {\n throw new Error('Request DL event did not return a valid result. Please ensure that the event does not resolve with another `request` download type.');\n }\n this.respondToMessage(message.id!!, requestDLResult.data);\n break\n }\n });\n }\n\n private waitForEventToRespond<T>(event: EventResponse<T>): Promise<EventResponse<T>> {\n // check the handlers to see if there even is any\n return new Promise((resolve, reject) => {\n const dataGet = setInterval(() => {\n if (event.resolved) {\n resolve(event);\n clearTimeout(timeout);\n }\n }, 5);\n\n const timeout = setTimeout(() => {\n if (event.deffered) {\n clearInterval(dataGet);\n const interval = setInterval(() => {\n if (event.resolved) {\n clearInterval(interval);\n resolve(event);\n }\n }, 100);\n }\n else {\n reject('Event did not respond in time');\n }\n }, 5000)\n });\n }\n\n public respondToMessage(messageID: string, response: any) {\n this.socket.send(JSON.stringify({\n event: 'response',\n id: messageID,\n args: response\n }));\n console.log(\"dispatched response to \" + messageID)\n }\n\n public waitForResponseFromServer<T>(messageID: string): Promise<T> {\n return new Promise((resolve) => {\n const waiter = (data: string) => {\n const message: WebsocketMessageClient = JSON.parse(data);\n if (message.event !== 'response') {\n this.socket.once('message', waiter);\n return;\n }\n console.log(\"received response from \" + messageID)\n\n if (message.id === messageID) {\n resolve(message.args);\n }\n else {\n this.socket.once('message', waiter);\n }\n }\n this.socket.once('message', waiter);\n });\n }\n\n public send(event: OGIAddonClientSentEvent, args: ClientSentEventTypes[OGIAddonClientSentEvent]): string {\n // generate a random id\n const id = Math.random().toString(36).substring(7);\n this.socket.send(JSON.stringify({\n event,\n args,\n id\n }));\n return id;\n }\n\n public close() {\n this.socket.close();\n }\n\n\n}\n","import z, { ZodError } from \"zod\"\n\nexport interface ConfigurationFile {\n [key: string]: ConfigurationOption\n}\n\nconst configValidation = z.object({\n name: z.string().min(1),\n displayName: z.string().min(1),\n description: z.string().min(1),\n})\n\nexport function isStringOption(option: ConfigurationOption): option is StringOption {\n return option.type === 'string';\n }\n\nexport function isNumberOption(option: ConfigurationOption): option is NumberOption {\n return option.type === 'number';\n}\n\nexport function isBooleanOption(option: ConfigurationOption): option is BooleanOption {\n return option.type === 'boolean';\n}\n\nexport class ConfigurationBuilder {\n private options: ConfigurationOption[] = [];\n\n /**\n * Add a number option to the configuration builder and return the builder for chaining. You must provide a name, display name, and description for the option.\n * @param option { (option: NumberOption) => NumberOption }\n * @returns \n */\n public addNumberOption(option: (option: NumberOption) => NumberOption): ConfigurationBuilder {\n let newOption = new NumberOption();\n newOption = option(newOption);\n this.options.push(newOption);\n return this;\n }\n\n /**\n * Add a string option to the configuration builder and return the builder for chaining. You must provide a name, display name, and description for the option.\n * @param option { (option: StringOption) => StringOption }\n */\n public addStringOption(option: (option: StringOption) => StringOption) {\n let newOption = new StringOption();\n newOption = option(newOption);\n this.options.push(newOption);\n return this;\n }\n\n /**\n * Add a boolean option to the configuration builder and return the builder for chaining. You must provide a name, display name, and description for the option.\n * @param option { (option: BooleanOption) => BooleanOption }\n */\n public addBooleanOption(option: (option: BooleanOption) => BooleanOption) {\n let newOption = new BooleanOption();\n newOption = option(newOption);\n this.options.push(newOption);\n return this;\n }\n\n public build(includeFunctions: boolean): ConfigurationFile {\n let config: ConfigurationFile = {};\n this.options.forEach(option => {\n // remove all functions from the option object\n if (!includeFunctions) {\n option = JSON.parse(JSON.stringify(option));\n const optionData = configValidation.safeParse(option)\n if (!optionData.success) {\n throw new ZodError(optionData.error.errors)\n }\n\n config[option.name] = option;\n }\n else {\n config[option.name] = option;\n }\n });\n return config;\n }\n}\n\nexport type ConfigurationOptionType = 'string' | 'number' | 'boolean' | 'unset'\nexport class ConfigurationOption {\n public name: string = '';\n public defaultValue: unknown = '';\n public displayName: string = '';\n public description: string = '';\n public type: ConfigurationOptionType = 'unset'\n \n /**\n * Set the name of the option. **REQUIRED**\n * @param name {string} The name of the option. This is used to reference the option in the configuration file.\n */\n setName(name: string) {\n this.name = name;\n return this;\n }\n\n /**\n * Set the display name of the option. This is used to show the user a human readable version of what the option is. **REQUIRED** \n * @param displayName {string} The display name of the option. \n * @returns \n */\n setDisplayName(displayName: string) {\n this.displayName = displayName;\n return this;\n }\n\n /**\n * Set the description of the option. This is to show the user a brief description of what this option does. **REQUIRED**\n * @param description {string} The description of the option. \n * @returns \n */\n setDescription(description: string) {\n this.description = description;\n return this;\n }\n\n /**\n * Validation code for the option. This is called when the user provides input to the option. If the validation fails, the user will be prompted to provide input again.\n * @param input {unknown} The input to validate\n */\n validate(input: unknown): [ boolean, string ] {\n throw new Error('Validation code not implemented. Value: ' + input)\n };\n}\n\nexport class StringOption extends ConfigurationOption {\n public allowedValues: string[] = [];\n public minTextLength: number = 0;\n public maxTextLength: number = Number.MAX_SAFE_INTEGER;\n public defaultValue: string = '';\n public inputType: 'text' | 'file' | 'password' | 'folder' = 'text';\n public type: ConfigurationOptionType = 'string'\n\n /**\n * Set the allowed values for the string. If the array is empty, any value is allowed. When provided, the client will act like this option is a dropdown. \n * @param allowedValues {string[]} An array of allowed values for the string. If the array is empty, any value is allowed.\n */\n setAllowedValues(allowedValues: string[]): this {\n this.allowedValues = allowedValues;\n return this;\n }\n\n /**\n * Set the default value for the string. This value will be used if the user does not provide a value. **HIGHLY RECOMMENDED**\n * @param defaultValue {string} The default value for the string.\n */\n setDefaultValue(defaultValue: string): this {\n this.defaultValue = defaultValue;\n return this;\n }\n\n /**\n * Set the minimum text length for the string. If the user provides a string that is less than this value, the validation will fail. \n * @param minTextLength {number} The minimum text length for the string. \n */\n setMinTextLength(minTextLength: number): this {\n this.minTextLength = minTextLength;\n return this;\n }\n\n /**\n * Set the maximum text length for the string. If the user provides a string that is greater than this value, the validation will fail. \n * @param maxTextLength {number} The maximum text length for the string.\n */\n setMaxTextLength(maxTextLength: number): this {\n this.maxTextLength = maxTextLength;\n return this;\n }\n\n /**\n * Set the input type for the string. This will change how the client renders the input. \n * @param inputType {'text' | 'file' | 'password' | 'folder'} The input type for the string. \n */\n setInputType(inputType: 'text' | 'file' | 'password' | 'folder'): this {\n this.inputType = inputType;\n return this;\n }\n\n override validate(input: unknown): [ boolean, string ] {\n if (typeof input !== 'string') {\n return [ false, 'Input is not a string' ];\n }\n if (this.allowedValues.length === 0 && input.length !== 0)\n return [ true, '' ];\n if (input.length < this.minTextLength || input.length > this.maxTextLength) {\n return [ false, 'Input is not within the text length ' + this.minTextLength + ' and ' + this.maxTextLength + ' characters (currently ' + input.length + ' characters)' ];\n }\n\n return [ this.allowedValues.includes(input), 'Input is not an allowed value' ];\n }\n}\n\nexport class NumberOption extends ConfigurationOption {\n public min: number = 0;\n public max: number = Number.MAX_SAFE_INTEGER;\n public defaultValue: number = 0;\n public type: ConfigurationOptionType = 'number'\n public inputType: 'range' | 'number' = 'number';\n\n /**\n * Set the minimum value for the number. If the user provides a number that is less than this value, the validation will fail.\n * @param min {number} The minimum value for the number.\n */\n setMin(min: number): this {\n this.min = min;\n return this;\n }\n\n /**\n * Set the input type for the number. This will change how the client renders the input. \n * @param type {'range' | 'number'} The input type for the number. \n */\n setInputType(type: 'range' | 'number'): this {\n this.inputType = type;\n return this;\n }\n\n /**\n * Set the maximum value for the number. If the user provides a number that is greater than this value, the validation will fail.\n * @param max {number} The maximum value for the number.\n */\n setMax(max: number): this {\n this.max = max;\n return this\n }\n\n /**\n * Set the default value for the number. This value will be used if the user does not provide a value. **HIGHLY RECOMMENDED**\n * @param defaultValue {number} The default value for the number.\n */\n setDefaultValue(defaultValue: number): this {\n this.defaultValue = defaultValue;\n return this;\n }\n\n override validate(input: unknown): [ boolean, string ] {\n if (isNaN(Number(input))) {\n return [ false, 'Input is not a number' ];\n }\n if (Number(input) < this.min || Number(input) > this.max) {\n return [ false, 'Input is not within the range of ' + this.min + ' and ' + this.max ];\n }\n return [ true, '' ];\n }\n\n}\n\nexport class BooleanOption extends ConfigurationOption {\n public type: ConfigurationOptionType = 'boolean'\n public defaultValue: boolean = false;\n\n /**\n * Set the default value for the boolean. This value will be used if the user does not provide a value. **HIGHLY RECOMMENDED**\n * @param defaultValue {boolean} The default value for the boolean.\n */\n setDefaultValue(defaultValue: boolean): this {\n this.defaultValue = defaultValue;\n return this;\n }\n\n override validate(input: unknown): [ boolean, string ] {\n if (typeof input !== 'boolean') {\n return [ false, 'Input is not a boolean' ];\n }\n return [ true, '' ];\n }\n\n}","import { ConfigurationFile, ConfigurationBuilder, BooleanOption, ConfigurationOption, ConfigurationOptionType, NumberOption, StringOption, isBooleanOption, isNumberOption, isStringOption } from \"./ConfigurationBuilder\";\n\ninterface DefiniteConfig {\n [key: string]: string | number | boolean;\n}\nexport class Configuration {\n readonly storedConfigTemplate: ConfigurationFile;\n definiteConfig: DefiniteConfig = {};\n constructor(configTemplate: ConfigurationFile) {\n this.storedConfigTemplate = configTemplate;\n }\n\n updateConfig(config: DefiniteConfig, validate: boolean = true): [ boolean, { [key: string]: string } ] {\n this.definiteConfig = config;\n if (validate) {\n const result = this.validateConfig();\n return result;\n }\n return [ true, {} ];\n }\n // provides falsey or truthy value, and an error message if falsey\n private validateConfig(): [ boolean, { [key: string]: string } ] {\n const erroredKeys = new Map<string, string>();\n for (const key in this.storedConfigTemplate) {\n if (this.definiteConfig[key] === null || this.definiteConfig[key] === undefined) {\n console.warn('Option ' + key + ' is not defined. Using default value Value: ' + this.definiteConfig[key]);\n this.definiteConfig[key] = this.storedConfigTemplate[key].defaultValue as string | number | boolean;\n }\n if (this.storedConfigTemplate[key].type !== typeof this.definiteConfig[key]) {\n throw new Error('Option ' + key + ' is not of the correct type');\n }\n\n const result = this.storedConfigTemplate[key].validate(this.definiteConfig[key]);\n if (!result[0]) {\n erroredKeys.set(key, result[1]);\n }\n }\n\n for (const key in this.definiteConfig) {\n if (!this.storedConfigTemplate[key]) {\n throw new Error('Option ' + key + ' is not defined in the configuration template');\n }\n }\n\n if (erroredKeys.size > 0) {\n return [ false, Object.fromEntries(erroredKeys) ];\n }\n\n return [ true, Object.fromEntries(erroredKeys) ];\n }\n\n getStringValue(optionName: string): string {\n if (!this.definiteConfig[optionName] === null) {\n throw new Error('Option ' + optionName + ' is not defined');\n }\n if (typeof this.definiteConfig[optionName] !== 'string') {\n throw new Error('Option ' + optionName + ' is not a string');\n }\n return this.definiteConfig[optionName];\n }\n\n getNumberValue(optionName: string): number {\n if (!this.definiteConfig[optionName] === null) {\n throw new Error('Option ' + optionName + ' is not defined');\n }\n if (typeof this.definiteConfig[optionName] !== 'number') {\n throw new Error('Option ' + optionName + ' is not a number');\n }\n return this.definiteConfig[optionName];\n }\n\n getBooleanValue(optionName: string): boolean {\n if (this.definiteConfig[optionName] === null) {\n throw new Error('Option ' + optionName + ' is not defined');\n }\n if (typeof this.definiteConfig[optionName] !== 'boolean') {\n throw new Error('Option ' + optionName + ' is not a boolean');\n }\n return this.definiteConfig[optionName];\n }\n}\n\nexport { ConfigurationFile, ConfigurationBuilder, BooleanOption, ConfigurationOption, ConfigurationOptionType, NumberOption, StringOption, isBooleanOption, isNumberOption, isStringOption };","import { ConfigurationBuilder } from \"./main\";\n\nexport default class EventResponse<T> {\n data: T | undefined = undefined;\n deffered: boolean = false;\n resolved: boolean = false;\n progress: number = 0;\n logs: string[] = [];\n onInputAsked?: (screen: ConfigurationBuilder, name: string, description: string) => Promise<{ [key: string]: boolean | string | number }>;\n\n constructor(onInputAsked?: (screen: ConfigurationBuilder, name: string, description: string) => Promise<{ [key: string]: boolean | string | number }>) {\n this.onInputAsked = onInputAsked;\n }\n \n\n public defer() {\n this.deffered = true;\n }\n\n /**\n * Resolve the event with data. This acts like a promise resolve, and will stop the event from being processed further. **You must always call this method when you are done with the event.** \n * @param data {T}\n */\n public resolve(data: T) {\n this.resolved = true;\n this.data = data;\n }\n\n /**\n * Completes the event and resolves it, but does not return any data. **You must always call this method when you are done with the event.** \n */\n public complete() {\n this.resolved = true;\n }\n\n /**\n * Logs a message to the event. This is useful for debugging and logging information to the user. \n * @param message {string}\n */\n public log(message: string) {\n this.logs.push(message);\n }\n\n /**\n * Send a screen to the client to ask for input. Use the `ConfigurationBuilder` system to build the screen. Once sent to the user, the addon cannot change the screen.\n * @async\n * @param name {string}\n * @param description {string}\n * @param screen {ConfigurationBuilder}\n * @returns {Promise<{ [key: string]: boolean | string | number }>}\n */\n public async askForInput(name: string, description: string, screen: ConfigurationBuilder): Promise<{ [key: string]: boolean | string | number; }> {\n if (!this.onInputAsked) {\n throw new Error('No input asked callback');\n }\n return await this.onInputAsked(screen, name, description);\n }\n\n \n}","{\n \"name\": \"ogi-addon\",\n \"module\": \"./build/main.js\",\n \"type\": \"module\",\n \"main\": \"./build/main.cjs\",\n \"version\": \"1.1.5\",\n \"exports\": {\n \".\": {\n \"import\": {\n \"default\": \"./build/main.js\",\n \"types\": \"./build/main.d.ts\"\n },\n \"require\": {\n \"default\": \"./build/main.cjs\",\n \"types\": \"./build/main.d.cts\"\n }\n },\n \"./config\": {\n \"import\": {\n \"default\": \"./build/config/Configuration.js\",\n \"types\": \"./build/config/Configuration.d.ts\"\n },\n \"require\": {\n \"default\": \"./build/config/Configuration.cjs\",\n \"types\": \"./build/config/Configuration.d.cts\"\n }\n }\n },\n \"typings\": \"./build/main.d.ts\",\n \"author\": {\n \"name\": \"Nat3z\",\n \"email\": \"me@nat3z.com\",\n \"url\": \"https://nat3z.com/\"\n },\n \"dependencies\": {\n \"fuse.js\": \"^7.1.0\",\n \"ws\": \"^8.4.0\",\n \"zod\": \"^3.23.8\"\n },\n \"scripts\": {\n \"auto-build\": \"tsc -w\",\n \"build\": \"tsup --config tsup.config.js\",\n \"release\": \"bun run build && npm publish\"\n },\n \"devDependencies\": {\n \"@types/node\": \"^20.14.12\",\n \"@types/ws\": \"^8.4.0\",\n \"tsup\": \"^8.2.3\",\n \"typescript\": \"^5.0.0\"\n }\n}"],"mappings":"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","names":["z","events","ws","Fuse"]}