ogi-addon 1.2.0 → 1.2.2
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/EventResponse.cjs +9 -1
- package/build/EventResponse.cjs.map +1 -1
- package/build/EventResponse.d.cts +3 -1
- package/build/EventResponse.d.ts +3 -1
- package/build/EventResponse.js +9 -1
- package/build/EventResponse.js.map +1 -1
- package/build/main.cjs +24 -6
- package/build/main.cjs.map +1 -1
- package/build/main.d.cts +3 -0
- package/build/main.d.ts +3 -0
- package/build/main.js +24 -6
- package/build/main.js.map +1 -1
- package/package.json +1 -1
- package/src/EventResponse.ts +11 -1
- package/src/main.ts +16 -5
package/build/EventResponse.cjs
CHANGED
|
@@ -29,12 +29,16 @@ var EventResponse = class {
|
|
|
29
29
|
resolved = false;
|
|
30
30
|
progress = 0;
|
|
31
31
|
logs = [];
|
|
32
|
+
failed = void 0;
|
|
32
33
|
onInputAsked;
|
|
33
34
|
constructor(onInputAsked) {
|
|
34
35
|
this.onInputAsked = onInputAsked;
|
|
35
36
|
}
|
|
36
|
-
defer() {
|
|
37
|
+
defer(promise) {
|
|
37
38
|
this.deffered = true;
|
|
39
|
+
if (promise) {
|
|
40
|
+
promise();
|
|
41
|
+
}
|
|
38
42
|
}
|
|
39
43
|
/**
|
|
40
44
|
* 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.**
|
|
@@ -50,6 +54,10 @@ var EventResponse = class {
|
|
|
50
54
|
complete() {
|
|
51
55
|
this.resolved = true;
|
|
52
56
|
}
|
|
57
|
+
fail(message) {
|
|
58
|
+
this.resolved = true;
|
|
59
|
+
this.failed = message;
|
|
60
|
+
}
|
|
53
61
|
/**
|
|
54
62
|
* Logs a message to the event. This is useful for debugging and logging information to the user.
|
|
55
63
|
* @param message {string}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/EventResponse.ts"],"sourcesContent":["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}"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA,IAAqB,gBAArB,MAAsC;AAAA,EACpC,OAAsB;AAAA,EACtB,WAAoB;AAAA,EACpB,WAAoB;AAAA,EACpB,WAAmB;AAAA,EACnB,OAAiB,CAAC;AAAA,EAClB;AAAA,EAEA,YAAY,cAA2I;AACrJ,SAAK,eAAe;AAAA,EACtB;AAAA,EAGO,
|
|
1
|
+
{"version":3,"sources":["../src/EventResponse.ts"],"sourcesContent":["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 failed: string | undefined = undefined;\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(promise?: () => Promise<void>) {\n this.deffered = true;\n // include this to make it easier to use the defer method with async functions\n if (promise) {\n promise();\n }\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 public fail(message: string) {\n this.resolved = true;\n this.failed = message;\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}"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA,IAAqB,gBAArB,MAAsC;AAAA,EACpC,OAAsB;AAAA,EACtB,WAAoB;AAAA,EACpB,WAAoB;AAAA,EACpB,WAAmB;AAAA,EACnB,OAAiB,CAAC;AAAA,EAClB,SAA6B;AAAA,EAC7B;AAAA,EAEA,YAAY,cAA2I;AACrJ,SAAK,eAAe;AAAA,EACtB;AAAA,EAGO,MAAM,SAA+B;AAC1C,SAAK,WAAW;AAEhB,QAAI,SAAS;AACX,cAAQ;AAAA,IACV;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMO,QAAQ,MAAS;AACtB,SAAK,WAAW;AAChB,SAAK,OAAO;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKO,WAAW;AAChB,SAAK,WAAW;AAAA,EAClB;AAAA,EAEO,KAAK,SAAiB;AAC3B,SAAK,WAAW;AAChB,SAAK,SAAS;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMO,IAAI,SAAiB;AAC1B,SAAK,KAAK,KAAK,OAAO;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAa,YAAY,MAAc,aAAqB,QAAsF;AAChJ,QAAI,CAAC,KAAK,cAAc;AACtB,YAAM,IAAI,MAAM,yBAAyB;AAAA,IAC3C;AACA,WAAO,MAAM,KAAK,aAAa,QAAQ,MAAM,WAAW;AAAA,EAC1D;AAGF;","names":[]}
|
|
@@ -6,13 +6,14 @@ declare class EventResponse<T> {
|
|
|
6
6
|
resolved: boolean;
|
|
7
7
|
progress: number;
|
|
8
8
|
logs: string[];
|
|
9
|
+
failed: string | undefined;
|
|
9
10
|
onInputAsked?: (screen: ConfigurationBuilder, name: string, description: string) => Promise<{
|
|
10
11
|
[key: string]: boolean | string | number;
|
|
11
12
|
}>;
|
|
12
13
|
constructor(onInputAsked?: (screen: ConfigurationBuilder, name: string, description: string) => Promise<{
|
|
13
14
|
[key: string]: boolean | string | number;
|
|
14
15
|
}>);
|
|
15
|
-
defer(): void;
|
|
16
|
+
defer(promise?: () => Promise<void>): void;
|
|
16
17
|
/**
|
|
17
18
|
* 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.**
|
|
18
19
|
* @param data {T}
|
|
@@ -22,6 +23,7 @@ declare class EventResponse<T> {
|
|
|
22
23
|
* 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.**
|
|
23
24
|
*/
|
|
24
25
|
complete(): void;
|
|
26
|
+
fail(message: string): void;
|
|
25
27
|
/**
|
|
26
28
|
* Logs a message to the event. This is useful for debugging and logging information to the user.
|
|
27
29
|
* @param message {string}
|
package/build/EventResponse.d.ts
CHANGED
|
@@ -6,13 +6,14 @@ declare class EventResponse<T> {
|
|
|
6
6
|
resolved: boolean;
|
|
7
7
|
progress: number;
|
|
8
8
|
logs: string[];
|
|
9
|
+
failed: string | undefined;
|
|
9
10
|
onInputAsked?: (screen: ConfigurationBuilder, name: string, description: string) => Promise<{
|
|
10
11
|
[key: string]: boolean | string | number;
|
|
11
12
|
}>;
|
|
12
13
|
constructor(onInputAsked?: (screen: ConfigurationBuilder, name: string, description: string) => Promise<{
|
|
13
14
|
[key: string]: boolean | string | number;
|
|
14
15
|
}>);
|
|
15
|
-
defer(): void;
|
|
16
|
+
defer(promise?: () => Promise<void>): void;
|
|
16
17
|
/**
|
|
17
18
|
* 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.**
|
|
18
19
|
* @param data {T}
|
|
@@ -22,6 +23,7 @@ declare class EventResponse<T> {
|
|
|
22
23
|
* 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.**
|
|
23
24
|
*/
|
|
24
25
|
complete(): void;
|
|
26
|
+
fail(message: string): void;
|
|
25
27
|
/**
|
|
26
28
|
* Logs a message to the event. This is useful for debugging and logging information to the user.
|
|
27
29
|
* @param message {string}
|
package/build/EventResponse.js
CHANGED
|
@@ -5,12 +5,16 @@ var EventResponse = class {
|
|
|
5
5
|
resolved = false;
|
|
6
6
|
progress = 0;
|
|
7
7
|
logs = [];
|
|
8
|
+
failed = void 0;
|
|
8
9
|
onInputAsked;
|
|
9
10
|
constructor(onInputAsked) {
|
|
10
11
|
this.onInputAsked = onInputAsked;
|
|
11
12
|
}
|
|
12
|
-
defer() {
|
|
13
|
+
defer(promise) {
|
|
13
14
|
this.deffered = true;
|
|
15
|
+
if (promise) {
|
|
16
|
+
promise();
|
|
17
|
+
}
|
|
14
18
|
}
|
|
15
19
|
/**
|
|
16
20
|
* 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.**
|
|
@@ -26,6 +30,10 @@ var EventResponse = class {
|
|
|
26
30
|
complete() {
|
|
27
31
|
this.resolved = true;
|
|
28
32
|
}
|
|
33
|
+
fail(message) {
|
|
34
|
+
this.resolved = true;
|
|
35
|
+
this.failed = message;
|
|
36
|
+
}
|
|
29
37
|
/**
|
|
30
38
|
* Logs a message to the event. This is useful for debugging and logging information to the user.
|
|
31
39
|
* @param message {string}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/EventResponse.ts"],"sourcesContent":["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}"],"mappings":";AAEA,IAAqB,gBAArB,MAAsC;AAAA,EACpC,OAAsB;AAAA,EACtB,WAAoB;AAAA,EACpB,WAAoB;AAAA,EACpB,WAAmB;AAAA,EACnB,OAAiB,CAAC;AAAA,EAClB;AAAA,EAEA,YAAY,cAA2I;AACrJ,SAAK,eAAe;AAAA,EACtB;AAAA,EAGO,
|
|
1
|
+
{"version":3,"sources":["../src/EventResponse.ts"],"sourcesContent":["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 failed: string | undefined = undefined;\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(promise?: () => Promise<void>) {\n this.deffered = true;\n // include this to make it easier to use the defer method with async functions\n if (promise) {\n promise();\n }\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 public fail(message: string) {\n this.resolved = true;\n this.failed = message;\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}"],"mappings":";AAEA,IAAqB,gBAArB,MAAsC;AAAA,EACpC,OAAsB;AAAA,EACtB,WAAoB;AAAA,EACpB,WAAoB;AAAA,EACpB,WAAmB;AAAA,EACnB,OAAiB,CAAC;AAAA,EAClB,SAA6B;AAAA,EAC7B;AAAA,EAEA,YAAY,cAA2I;AACrJ,SAAK,eAAe;AAAA,EACtB;AAAA,EAGO,MAAM,SAA+B;AAC1C,SAAK,WAAW;AAEhB,QAAI,SAAS;AACX,cAAQ;AAAA,IACV;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMO,QAAQ,MAAS;AACtB,SAAK,WAAW;AAChB,SAAK,OAAO;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKO,WAAW;AAChB,SAAK,WAAW;AAAA,EAClB;AAAA,EAEO,KAAK,SAAiB;AAC3B,SAAK,WAAW;AAChB,SAAK,SAAS;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMO,IAAI,SAAiB;AAC1B,SAAK,KAAK,KAAK,OAAO;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAa,YAAY,MAAc,aAAqB,QAAsF;AAChJ,QAAI,CAAC,KAAK,cAAc;AACtB,YAAM,IAAI,MAAM,yBAAyB;AAAA,IAC3C;AACA,WAAO,MAAM,KAAK,aAAa,QAAQ,MAAM,WAAW;AAAA,EAC1D;AAGF;","names":[]}
|
package/build/main.cjs
CHANGED
|
@@ -342,12 +342,16 @@ var EventResponse = class {
|
|
|
342
342
|
resolved = false;
|
|
343
343
|
progress = 0;
|
|
344
344
|
logs = [];
|
|
345
|
+
failed = void 0;
|
|
345
346
|
onInputAsked;
|
|
346
347
|
constructor(onInputAsked) {
|
|
347
348
|
this.onInputAsked = onInputAsked;
|
|
348
349
|
}
|
|
349
|
-
defer() {
|
|
350
|
+
defer(promise) {
|
|
350
351
|
this.deffered = true;
|
|
352
|
+
if (promise) {
|
|
353
|
+
promise();
|
|
354
|
+
}
|
|
351
355
|
}
|
|
352
356
|
/**
|
|
353
357
|
* 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.**
|
|
@@ -363,6 +367,10 @@ var EventResponse = class {
|
|
|
363
367
|
complete() {
|
|
364
368
|
this.resolved = true;
|
|
365
369
|
}
|
|
370
|
+
fail(message) {
|
|
371
|
+
this.resolved = true;
|
|
372
|
+
this.failed = message;
|
|
373
|
+
}
|
|
366
374
|
/**
|
|
367
375
|
* Logs a message to the event. This is useful for debugging and logging information to the user.
|
|
368
376
|
* @param message {string}
|
|
@@ -395,7 +403,7 @@ var package_default = {
|
|
|
395
403
|
module: "./build/main.js",
|
|
396
404
|
type: "module",
|
|
397
405
|
main: "./build/main.cjs",
|
|
398
|
-
version: "1.2.
|
|
406
|
+
version: "1.2.2",
|
|
399
407
|
exports: {
|
|
400
408
|
".": {
|
|
401
409
|
import: {
|
|
@@ -493,7 +501,7 @@ var OGIAddon = class {
|
|
|
493
501
|
const progress = 0;
|
|
494
502
|
const logs = [];
|
|
495
503
|
const task = new CustomTask(this.addonWSListener, id, progress, logs);
|
|
496
|
-
this.addonWSListener.send("task-update", { id, progress, logs, finished: false });
|
|
504
|
+
this.addonWSListener.send("task-update", { id, progress, logs, finished: false, failed: void 0 });
|
|
497
505
|
return task;
|
|
498
506
|
}
|
|
499
507
|
};
|
|
@@ -503,6 +511,7 @@ var CustomTask = class {
|
|
|
503
511
|
logs;
|
|
504
512
|
finished = false;
|
|
505
513
|
ws;
|
|
514
|
+
failed = void 0;
|
|
506
515
|
constructor(ws2, id, progress, logs) {
|
|
507
516
|
this.id = id;
|
|
508
517
|
this.progress = progress;
|
|
@@ -517,12 +526,16 @@ var CustomTask = class {
|
|
|
517
526
|
this.finished = true;
|
|
518
527
|
this.update();
|
|
519
528
|
}
|
|
529
|
+
fail(message) {
|
|
530
|
+
this.failed = message;
|
|
531
|
+
this.update();
|
|
532
|
+
}
|
|
520
533
|
setProgress(progress) {
|
|
521
534
|
this.progress = progress;
|
|
522
535
|
this.update();
|
|
523
536
|
}
|
|
524
537
|
update() {
|
|
525
|
-
this.ws.send("task-update", { id: this.id, progress: this.progress, logs: this.logs, finished: this.finished });
|
|
538
|
+
this.ws.send("task-update", { id: this.id, progress: this.progress, logs: this.logs, finished: this.finished, failed: this.failed });
|
|
526
539
|
}
|
|
527
540
|
};
|
|
528
541
|
var SearchTool = class {
|
|
@@ -630,7 +643,8 @@ var OGIAddonWSListener = class {
|
|
|
630
643
|
this.send("defer-update", {
|
|
631
644
|
logs: setupEvent.logs,
|
|
632
645
|
deferID: message.args.deferID,
|
|
633
|
-
progress: setupEvent.progress
|
|
646
|
+
progress: setupEvent.progress,
|
|
647
|
+
failed: setupEvent.failed
|
|
634
648
|
});
|
|
635
649
|
}, 100);
|
|
636
650
|
const setupResult = await this.waitForEventToRespond(setupEvent);
|
|
@@ -664,7 +678,11 @@ var OGIAddonWSListener = class {
|
|
|
664
678
|
}
|
|
665
679
|
this.eventEmitter.emit("request-dl", message.args.appID, message.args.info, requestDLEvent);
|
|
666
680
|
const requestDLResult = await this.waitForEventToRespond(requestDLEvent);
|
|
667
|
-
if (requestDLEvent.
|
|
681
|
+
if (requestDLEvent.failed) {
|
|
682
|
+
this.respondToMessage(message.id, { statusError: requestDLEvent.failed });
|
|
683
|
+
break;
|
|
684
|
+
}
|
|
685
|
+
if (requestDLEvent.data === void 0 || requestDLEvent.data?.downloadType === "request") {
|
|
668
686
|
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.");
|
|
669
687
|
}
|
|
670
688
|
this.respondToMessage(message.id, requestDLResult.data);
|
package/build/main.cjs.map
CHANGED
|
@@ -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';\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, { IFuseOptions } 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\nexport class 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 wrapper over Fuse.js for the OGI Addon. This tool is used to search for items in the library.\n * @example\n * ```typescript\n * const searchTool = new SearchTool<LibraryInfo>([{ name: 'test', appID: 123 }, { name: 'test2', appID: 124 }], ['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[], options: Omit<IFuseOptions<T>, 'keys'> = { threshold: 0.3, includeScore: true }) {\n this.fuse = new Fuse(items, {\n keys,\n ...options\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.2.0\",\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 \"release-beta\": \"bun run build && npm publish --tag future\"\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"]}
|
|
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, { IFuseOptions } 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 failed: string | undefined;\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, failed: undefined });\n return task;\n }\n}\n\nexport class CustomTask {\n public readonly id: string;\n public progress: number;\n public logs: string[];\n public finished: boolean = false;\n public ws: OGIAddonWSListener;\n public failed: string | undefined = undefined;\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 fail(message: string) {\n this.failed = message;\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, failed: this.failed });\n }\n}\n/**\n * A search tool wrapper over Fuse.js for the OGI Addon. This tool is used to search for items in the library.\n * @example\n * ```typescript\n * const searchTool = new SearchTool<LibraryInfo>([{ name: 'test', appID: 123 }, { name: 'test2', appID: 124 }], ['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[], options: Omit<IFuseOptions<T>, 'keys'> = { threshold: 0.3, includeScore: true }) {\n this.fuse = new Fuse(items, {\n keys,\n ...options\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 failed: setupEvent.failed\n } as ClientSentEventTypes['defer-update']);\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.failed) {\n this.respondToMessage(message.id!!, { statusError: requestDLEvent.failed });\n break;\n }\n if (requestDLEvent.data === undefined || 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 failed: string | undefined = undefined;\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(promise?: () => Promise<void>) {\n this.deffered = true;\n // include this to make it easier to use the defer method with async functions\n if (promise) {\n promise();\n }\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 public fail(message: string) {\n this.resolved = true;\n this.failed = message;\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.2.2\",\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 \"release-beta\": \"bun run build && npm publish --tag future\"\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"]}
|
package/build/main.d.cts
CHANGED
|
@@ -36,6 +36,7 @@ interface ClientSentEventTypes {
|
|
|
36
36
|
progress: number;
|
|
37
37
|
logs: string[];
|
|
38
38
|
finished: boolean;
|
|
39
|
+
failed: string | undefined;
|
|
39
40
|
};
|
|
40
41
|
}
|
|
41
42
|
type BasicLibraryInfo = {
|
|
@@ -196,9 +197,11 @@ declare class CustomTask {
|
|
|
196
197
|
logs: string[];
|
|
197
198
|
finished: boolean;
|
|
198
199
|
ws: OGIAddonWSListener;
|
|
200
|
+
failed: string | undefined;
|
|
199
201
|
constructor(ws: OGIAddonWSListener, id: string, progress: number, logs: string[]);
|
|
200
202
|
log(log: string): void;
|
|
201
203
|
finish(): void;
|
|
204
|
+
fail(message: string): void;
|
|
202
205
|
setProgress(progress: number): void;
|
|
203
206
|
update(): void;
|
|
204
207
|
}
|
package/build/main.d.ts
CHANGED
|
@@ -36,6 +36,7 @@ interface ClientSentEventTypes {
|
|
|
36
36
|
progress: number;
|
|
37
37
|
logs: string[];
|
|
38
38
|
finished: boolean;
|
|
39
|
+
failed: string | undefined;
|
|
39
40
|
};
|
|
40
41
|
}
|
|
41
42
|
type BasicLibraryInfo = {
|
|
@@ -196,9 +197,11 @@ declare class CustomTask {
|
|
|
196
197
|
logs: string[];
|
|
197
198
|
finished: boolean;
|
|
198
199
|
ws: OGIAddonWSListener;
|
|
200
|
+
failed: string | undefined;
|
|
199
201
|
constructor(ws: OGIAddonWSListener, id: string, progress: number, logs: string[]);
|
|
200
202
|
log(log: string): void;
|
|
201
203
|
finish(): void;
|
|
204
|
+
fail(message: string): void;
|
|
202
205
|
setProgress(progress: number): void;
|
|
203
206
|
update(): void;
|
|
204
207
|
}
|
package/build/main.js
CHANGED
|
@@ -302,12 +302,16 @@ var EventResponse = class {
|
|
|
302
302
|
resolved = false;
|
|
303
303
|
progress = 0;
|
|
304
304
|
logs = [];
|
|
305
|
+
failed = void 0;
|
|
305
306
|
onInputAsked;
|
|
306
307
|
constructor(onInputAsked) {
|
|
307
308
|
this.onInputAsked = onInputAsked;
|
|
308
309
|
}
|
|
309
|
-
defer() {
|
|
310
|
+
defer(promise) {
|
|
310
311
|
this.deffered = true;
|
|
312
|
+
if (promise) {
|
|
313
|
+
promise();
|
|
314
|
+
}
|
|
311
315
|
}
|
|
312
316
|
/**
|
|
313
317
|
* 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.**
|
|
@@ -323,6 +327,10 @@ var EventResponse = class {
|
|
|
323
327
|
complete() {
|
|
324
328
|
this.resolved = true;
|
|
325
329
|
}
|
|
330
|
+
fail(message) {
|
|
331
|
+
this.resolved = true;
|
|
332
|
+
this.failed = message;
|
|
333
|
+
}
|
|
326
334
|
/**
|
|
327
335
|
* Logs a message to the event. This is useful for debugging and logging information to the user.
|
|
328
336
|
* @param message {string}
|
|
@@ -355,7 +363,7 @@ var package_default = {
|
|
|
355
363
|
module: "./build/main.js",
|
|
356
364
|
type: "module",
|
|
357
365
|
main: "./build/main.cjs",
|
|
358
|
-
version: "1.2.
|
|
366
|
+
version: "1.2.2",
|
|
359
367
|
exports: {
|
|
360
368
|
".": {
|
|
361
369
|
import: {
|
|
@@ -453,7 +461,7 @@ var OGIAddon = class {
|
|
|
453
461
|
const progress = 0;
|
|
454
462
|
const logs = [];
|
|
455
463
|
const task = new CustomTask(this.addonWSListener, id, progress, logs);
|
|
456
|
-
this.addonWSListener.send("task-update", { id, progress, logs, finished: false });
|
|
464
|
+
this.addonWSListener.send("task-update", { id, progress, logs, finished: false, failed: void 0 });
|
|
457
465
|
return task;
|
|
458
466
|
}
|
|
459
467
|
};
|
|
@@ -463,6 +471,7 @@ var CustomTask = class {
|
|
|
463
471
|
logs;
|
|
464
472
|
finished = false;
|
|
465
473
|
ws;
|
|
474
|
+
failed = void 0;
|
|
466
475
|
constructor(ws2, id, progress, logs) {
|
|
467
476
|
this.id = id;
|
|
468
477
|
this.progress = progress;
|
|
@@ -477,12 +486,16 @@ var CustomTask = class {
|
|
|
477
486
|
this.finished = true;
|
|
478
487
|
this.update();
|
|
479
488
|
}
|
|
489
|
+
fail(message) {
|
|
490
|
+
this.failed = message;
|
|
491
|
+
this.update();
|
|
492
|
+
}
|
|
480
493
|
setProgress(progress) {
|
|
481
494
|
this.progress = progress;
|
|
482
495
|
this.update();
|
|
483
496
|
}
|
|
484
497
|
update() {
|
|
485
|
-
this.ws.send("task-update", { id: this.id, progress: this.progress, logs: this.logs, finished: this.finished });
|
|
498
|
+
this.ws.send("task-update", { id: this.id, progress: this.progress, logs: this.logs, finished: this.finished, failed: this.failed });
|
|
486
499
|
}
|
|
487
500
|
};
|
|
488
501
|
var SearchTool = class {
|
|
@@ -590,7 +603,8 @@ var OGIAddonWSListener = class {
|
|
|
590
603
|
this.send("defer-update", {
|
|
591
604
|
logs: setupEvent.logs,
|
|
592
605
|
deferID: message.args.deferID,
|
|
593
|
-
progress: setupEvent.progress
|
|
606
|
+
progress: setupEvent.progress,
|
|
607
|
+
failed: setupEvent.failed
|
|
594
608
|
});
|
|
595
609
|
}, 100);
|
|
596
610
|
const setupResult = await this.waitForEventToRespond(setupEvent);
|
|
@@ -624,7 +638,11 @@ var OGIAddonWSListener = class {
|
|
|
624
638
|
}
|
|
625
639
|
this.eventEmitter.emit("request-dl", message.args.appID, message.args.info, requestDLEvent);
|
|
626
640
|
const requestDLResult = await this.waitForEventToRespond(requestDLEvent);
|
|
627
|
-
if (requestDLEvent.
|
|
641
|
+
if (requestDLEvent.failed) {
|
|
642
|
+
this.respondToMessage(message.id, { statusError: requestDLEvent.failed });
|
|
643
|
+
break;
|
|
644
|
+
}
|
|
645
|
+
if (requestDLEvent.data === void 0 || requestDLEvent.data?.downloadType === "request") {
|
|
628
646
|
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.");
|
|
629
647
|
}
|
|
630
648
|
this.respondToMessage(message.id, requestDLResult.data);
|
package/build/main.js.map
CHANGED
|
@@ -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';\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, { IFuseOptions } 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\nexport class 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 wrapper over Fuse.js for the OGI Addon. This tool is used to search for items in the library.\n * @example\n * ```typescript\n * const searchTool = new SearchTool<LibraryInfo>([{ name: 'test', appID: 123 }, { name: 'test2', appID: 124 }], ['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[], options: Omit<IFuseOptions<T>, 'keys'> = { threshold: 0.3, includeScore: true }) {\n this.fuse = new Fuse(items, {\n keys,\n ...options\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.2.0\",\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 \"release-beta\": \"bun run build && npm publish --tag future\"\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":["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, { IFuseOptions } 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 failed: string | undefined;\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, failed: undefined });\n return task;\n }\n}\n\nexport class CustomTask {\n public readonly id: string;\n public progress: number;\n public logs: string[];\n public finished: boolean = false;\n public ws: OGIAddonWSListener;\n public failed: string | undefined = undefined;\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 fail(message: string) {\n this.failed = message;\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, failed: this.failed });\n }\n}\n/**\n * A search tool wrapper over Fuse.js for the OGI Addon. This tool is used to search for items in the library.\n * @example\n * ```typescript\n * const searchTool = new SearchTool<LibraryInfo>([{ name: 'test', appID: 123 }, { name: 'test2', appID: 124 }], ['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[], options: Omit<IFuseOptions<T>, 'keys'> = { threshold: 0.3, includeScore: true }) {\n this.fuse = new Fuse(items, {\n keys,\n ...options\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 failed: setupEvent.failed\n } as ClientSentEventTypes['defer-update']);\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.failed) {\n this.respondToMessage(message.id!!, { statusError: requestDLEvent.failed });\n break;\n }\n if (requestDLEvent.data === undefined || 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 failed: string | undefined = undefined;\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(promise?: () => Promise<void>) {\n this.deffered = true;\n // include this to make it easier to use the defer method with async functions\n if (promise) {\n promise();\n }\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 public fail(message: string) {\n this.resolved = true;\n this.failed = message;\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.2.2\",\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 \"release-beta\": \"bun run build && npm publish --tag future\"\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":["ws"]}
|
package/package.json
CHANGED
package/src/EventResponse.ts
CHANGED
|
@@ -6,6 +6,7 @@ export default class EventResponse<T> {
|
|
|
6
6
|
resolved: boolean = false;
|
|
7
7
|
progress: number = 0;
|
|
8
8
|
logs: string[] = [];
|
|
9
|
+
failed: string | undefined = undefined;
|
|
9
10
|
onInputAsked?: (screen: ConfigurationBuilder, name: string, description: string) => Promise<{ [key: string]: boolean | string | number }>;
|
|
10
11
|
|
|
11
12
|
constructor(onInputAsked?: (screen: ConfigurationBuilder, name: string, description: string) => Promise<{ [key: string]: boolean | string | number }>) {
|
|
@@ -13,8 +14,12 @@ export default class EventResponse<T> {
|
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
|
|
16
|
-
public defer() {
|
|
17
|
+
public defer(promise?: () => Promise<void>) {
|
|
17
18
|
this.deffered = true;
|
|
19
|
+
// include this to make it easier to use the defer method with async functions
|
|
20
|
+
if (promise) {
|
|
21
|
+
promise();
|
|
22
|
+
}
|
|
18
23
|
}
|
|
19
24
|
|
|
20
25
|
/**
|
|
@@ -33,6 +38,11 @@ export default class EventResponse<T> {
|
|
|
33
38
|
this.resolved = true;
|
|
34
39
|
}
|
|
35
40
|
|
|
41
|
+
public fail(message: string) {
|
|
42
|
+
this.resolved = true;
|
|
43
|
+
this.failed = message;
|
|
44
|
+
}
|
|
45
|
+
|
|
36
46
|
/**
|
|
37
47
|
* Logs a message to the event. This is useful for debugging and logging information to the user.
|
|
38
48
|
* @param message {string}
|
package/src/main.ts
CHANGED
|
@@ -40,6 +40,7 @@ export interface ClientSentEventTypes {
|
|
|
40
40
|
progress: number;
|
|
41
41
|
logs: string[];
|
|
42
42
|
finished: boolean;
|
|
43
|
+
failed: string | undefined;
|
|
43
44
|
};
|
|
44
45
|
}
|
|
45
46
|
|
|
@@ -222,7 +223,7 @@ export default class OGIAddon {
|
|
|
222
223
|
const progress = 0;
|
|
223
224
|
const logs: string[] = [];
|
|
224
225
|
const task = new CustomTask(this.addonWSListener, id, progress, logs);
|
|
225
|
-
this.addonWSListener.send('task-update', { id, progress, logs, finished: false });
|
|
226
|
+
this.addonWSListener.send('task-update', { id, progress, logs, finished: false, failed: undefined });
|
|
226
227
|
return task;
|
|
227
228
|
}
|
|
228
229
|
}
|
|
@@ -233,6 +234,7 @@ export class CustomTask {
|
|
|
233
234
|
public logs: string[];
|
|
234
235
|
public finished: boolean = false;
|
|
235
236
|
public ws: OGIAddonWSListener;
|
|
237
|
+
public failed: string | undefined = undefined;
|
|
236
238
|
constructor(ws: OGIAddonWSListener, id: string, progress: number, logs: string[]) {
|
|
237
239
|
this.id = id;
|
|
238
240
|
this.progress = progress;
|
|
@@ -247,12 +249,16 @@ export class CustomTask {
|
|
|
247
249
|
this.finished = true;
|
|
248
250
|
this.update();
|
|
249
251
|
}
|
|
252
|
+
public fail(message: string) {
|
|
253
|
+
this.failed = message;
|
|
254
|
+
this.update();
|
|
255
|
+
}
|
|
250
256
|
public setProgress(progress: number) {
|
|
251
257
|
this.progress = progress;
|
|
252
258
|
this.update();
|
|
253
259
|
}
|
|
254
260
|
public update() {
|
|
255
|
-
this.ws.send('task-update', { id: this.id, progress: this.progress, logs: this.logs, finished: this.finished });
|
|
261
|
+
this.ws.send('task-update', { id: this.id, progress: this.progress, logs: this.logs, finished: this.finished, failed: this.failed });
|
|
256
262
|
}
|
|
257
263
|
}
|
|
258
264
|
/**
|
|
@@ -401,8 +407,9 @@ class OGIAddonWSListener {
|
|
|
401
407
|
this.send('defer-update', {
|
|
402
408
|
logs: setupEvent.logs,
|
|
403
409
|
deferID: message.args.deferID,
|
|
404
|
-
progress: setupEvent.progress
|
|
405
|
-
|
|
410
|
+
progress: setupEvent.progress,
|
|
411
|
+
failed: setupEvent.failed
|
|
412
|
+
} as ClientSentEventTypes['defer-update']);
|
|
406
413
|
}, 100);
|
|
407
414
|
const setupResult = await this.waitForEventToRespond(setupEvent);
|
|
408
415
|
this.respondToMessage(message.id!!, setupResult.data);
|
|
@@ -435,7 +442,11 @@ class OGIAddonWSListener {
|
|
|
435
442
|
}
|
|
436
443
|
this.eventEmitter.emit('request-dl', message.args.appID, message.args.info, requestDLEvent);
|
|
437
444
|
const requestDLResult = await this.waitForEventToRespond(requestDLEvent);
|
|
438
|
-
if (requestDLEvent.
|
|
445
|
+
if (requestDLEvent.failed) {
|
|
446
|
+
this.respondToMessage(message.id!!, { statusError: requestDLEvent.failed });
|
|
447
|
+
break;
|
|
448
|
+
}
|
|
449
|
+
if (requestDLEvent.data === undefined || requestDLEvent.data?.downloadType === 'request') {
|
|
439
450
|
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.');
|
|
440
451
|
}
|
|
441
452
|
this.respondToMessage(message.id!!, requestDLResult.data);
|