ogi-addon 1.0.0 → 1.1.0

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.
@@ -24,6 +24,12 @@ export function isBooleanOption(option: ConfigurationOption): option is BooleanO
24
24
 
25
25
  export class ConfigurationBuilder {
26
26
  private options: ConfigurationOption[] = [];
27
+
28
+ /**
29
+ * 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.
30
+ * @param option { (option: NumberOption) => NumberOption }
31
+ * @returns
32
+ */
27
33
  public addNumberOption(option: (option: NumberOption) => NumberOption): ConfigurationBuilder {
28
34
  let newOption = new NumberOption();
29
35
  newOption = option(newOption);
@@ -31,6 +37,10 @@ export class ConfigurationBuilder {
31
37
  return this;
32
38
  }
33
39
 
40
+ /**
41
+ * 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.
42
+ * @param option { (option: StringOption) => StringOption }
43
+ */
34
44
  public addStringOption(option: (option: StringOption) => StringOption) {
35
45
  let newOption = new StringOption();
36
46
  newOption = option(newOption);
@@ -38,6 +48,10 @@ export class ConfigurationBuilder {
38
48
  return this;
39
49
  }
40
50
 
51
+ /**
52
+ * 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.
53
+ * @param option { (option: BooleanOption) => BooleanOption }
54
+ */
41
55
  public addBooleanOption(option: (option: BooleanOption) => BooleanOption) {
42
56
  let newOption = new BooleanOption();
43
57
  newOption = option(newOption);
@@ -74,22 +88,39 @@ export class ConfigurationOption {
74
88
  public description: string = '';
75
89
  public type: ConfigurationOptionType = 'unset'
76
90
 
91
+ /**
92
+ * Set the name of the option. **REQUIRED**
93
+ * @param name {string} The name of the option. This is used to reference the option in the configuration file.
94
+ */
77
95
  setName(name: string) {
78
96
  this.name = name;
79
97
  return this;
80
98
  }
81
99
 
100
+ /**
101
+ * Set the display name of the option. This is used to show the user a human readable version of what the option is. **REQUIRED**
102
+ * @param displayName {string} The display name of the option.
103
+ * @returns
104
+ */
82
105
  setDisplayName(displayName: string) {
83
106
  this.displayName = displayName;
84
107
  return this;
85
108
  }
86
109
 
110
+ /**
111
+ * Set the description of the option. This is to show the user a brief description of what this option does. **REQUIRED**
112
+ * @param description {string} The description of the option.
113
+ * @returns
114
+ */
87
115
  setDescription(description: string) {
88
116
  this.description = description;
89
117
  return this;
90
118
  }
91
119
 
92
-
120
+ /**
121
+ * 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.
122
+ * @param input {unknown} The input to validate
123
+ */
93
124
  validate(input: unknown): [ boolean, string ] {
94
125
  throw new Error('Validation code not implemented. Value: ' + input)
95
126
  };
@@ -103,26 +134,46 @@ export class StringOption extends ConfigurationOption {
103
134
  public inputType: 'text' | 'file' | 'password' | 'folder' = 'text';
104
135
  public type: ConfigurationOptionType = 'string'
105
136
 
137
+ /**
138
+ * 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.
139
+ * @param allowedValues {string[]} An array of allowed values for the string. If the array is empty, any value is allowed.
140
+ */
106
141
  setAllowedValues(allowedValues: string[]): this {
107
142
  this.allowedValues = allowedValues;
108
143
  return this;
109
144
  }
110
145
 
146
+ /**
147
+ * Set the default value for the string. This value will be used if the user does not provide a value. **HIGHLY RECOMMENDED**
148
+ * @param defaultValue {string} The default value for the string.
149
+ */
111
150
  setDefaultValue(defaultValue: string): this {
112
151
  this.defaultValue = defaultValue;
113
152
  return this;
114
153
  }
115
154
 
155
+ /**
156
+ * Set the minimum text length for the string. If the user provides a string that is less than this value, the validation will fail.
157
+ * @param minTextLength {number} The minimum text length for the string.
158
+ */
116
159
  setMinTextLength(minTextLength: number): this {
117
160
  this.minTextLength = minTextLength;
118
161
  return this;
119
162
  }
120
163
 
164
+ /**
165
+ * Set the maximum text length for the string. If the user provides a string that is greater than this value, the validation will fail.
166
+ * @param maxTextLength {number} The maximum text length for the string.
167
+ */
121
168
  setMaxTextLength(maxTextLength: number): this {
122
169
  this.maxTextLength = maxTextLength;
123
170
  return this;
124
171
  }
125
172
 
173
+ /**
174
+ * Set the input type for the string. This will change how the client renders the input.
175
+ * @param inputType {'text' | 'file' | 'password' | 'folder'} The input type for the string.
176
+ */
126
177
  setInputType(inputType: 'text' | 'file' | 'password' | 'folder'): this {
127
178
  this.inputType = inputType;
128
179
  return this;
@@ -148,21 +199,38 @@ export class NumberOption extends ConfigurationOption {
148
199
  public defaultValue: number = 0;
149
200
  public type: ConfigurationOptionType = 'number'
150
201
  public inputType: 'range' | 'number' = 'number';
202
+
203
+ /**
204
+ * Set the minimum value for the number. If the user provides a number that is less than this value, the validation will fail.
205
+ * @param min {number} The minimum value for the number.
206
+ */
151
207
  setMin(min: number): this {
152
208
  this.min = min;
153
209
  return this;
154
210
  }
155
211
 
212
+ /**
213
+ * Set the input type for the number. This will change how the client renders the input.
214
+ * @param type {'range' | 'number'} The input type for the number.
215
+ */
156
216
  setInputType(type: 'range' | 'number'): this {
157
217
  this.inputType = type;
158
218
  return this;
159
219
  }
160
220
 
221
+ /**
222
+ * Set the maximum value for the number. If the user provides a number that is greater than this value, the validation will fail.
223
+ * @param max {number} The maximum value for the number.
224
+ */
161
225
  setMax(max: number): this {
162
226
  this.max = max;
163
227
  return this
164
228
  }
165
229
 
230
+ /**
231
+ * Set the default value for the number. This value will be used if the user does not provide a value. **HIGHLY RECOMMENDED**
232
+ * @param defaultValue {number} The default value for the number.
233
+ */
166
234
  setDefaultValue(defaultValue: number): this {
167
235
  this.defaultValue = defaultValue;
168
236
  return this;
@@ -184,6 +252,10 @@ export class BooleanOption extends ConfigurationOption {
184
252
  public type: ConfigurationOptionType = 'boolean'
185
253
  public defaultValue: boolean = false;
186
254
 
255
+ /**
256
+ * Set the default value for the boolean. This value will be used if the user does not provide a value. **HIGHLY RECOMMENDED**
257
+ * @param defaultValue {boolean} The default value for the boolean.
258
+ */
187
259
  setDefaultValue(defaultValue: boolean): this {
188
260
  this.defaultValue = defaultValue;
189
261
  return this;
package/src/main.ts CHANGED
@@ -5,14 +5,14 @@ import { Configuration } from './config/Configuration';
5
5
  import EventResponse from './EventResponse';
6
6
  import { SearchResult } from './SearchEngine';
7
7
 
8
- export type OGIAddonEvent = 'connect' | 'disconnect' | 'configure' | 'authenticate' | 'search' | 'setup' | 'library-search' | 'game-details' | 'exit';
8
+ export type OGIAddonEvent = 'connect' | 'disconnect' | 'configure' | 'authenticate' | 'search' | 'setup' | 'library-search' | 'game-details' | 'exit' | 'request-dl';
9
9
  export type OGIAddonClientSentEvent = 'response' | 'authenticate' | 'configure' | 'defer-update' | 'notification' | 'input-asked';
10
10
 
11
- export type OGIAddonServerSentEvent = 'authenticate' | 'configure' | 'config-update' | 'search' | 'setup' | 'response' | 'library-search' | 'game-details';
11
+ export type OGIAddonServerSentEvent = 'authenticate' | 'configure' | 'config-update' | 'search' | 'setup' | 'response' | 'library-search' | 'game-details' | 'request-dl';
12
12
  export { ConfigurationBuilder, Configuration, EventResponse, SearchResult };
13
13
  const defaultPort = 7654;
14
14
  import pjson from '../package.json';
15
- const version = pjson.version;
15
+ export const version = pjson.version;
16
16
 
17
17
  export interface ClientSentEventTypes {
18
18
  response: any;
@@ -33,12 +33,51 @@ export type BasicLibraryInfo = {
33
33
  }
34
34
 
35
35
  export interface EventListenerTypes {
36
+ /**
37
+ * This event is emitted when the addon connects to the OGI Addon Server. Addon does not need to resolve anything.
38
+ * @param socket
39
+ * @returns
40
+ */
36
41
  connect: (socket: ws) => void;
42
+
43
+ /**
44
+ * 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.
45
+ * @param reason
46
+ * @returns
47
+ */
37
48
  disconnect: (reason: string) => void;
49
+ /**
50
+ * 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)
51
+ * @param config
52
+ * @returns
53
+ */
38
54
  configure: (config: ConfigurationBuilder) => ConfigurationBuilder;
55
+ /**
56
+ * This event is called when the client provides a response to any event. This should be treated as middleware.
57
+ * @param response
58
+ * @returns
59
+ */
39
60
  response: (response: any) => void;
61
+
62
+ /**
63
+ * This event is called when the client requests for the addon to authenticate itself. You don't need to provide any info.
64
+ * @param config
65
+ * @returns
66
+ */
40
67
  authenticate: (config: any) => void;
68
+ /**
69
+ * 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)
70
+ * @param query
71
+ * @param event
72
+ * @returns
73
+ */
41
74
  search: (query: { type: 'steamapp' | 'internal', text: string }, event: EventResponse<SearchResult[]>) => void;
75
+ /**
76
+ * 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)
77
+ * @param data
78
+ * @param event
79
+ * @returns
80
+ */
42
81
  setup: (
43
82
  data: {
44
83
  path: string,
@@ -53,9 +92,17 @@ export interface EventListenerTypes {
53
92
  storefront: 'steam' | 'internal'
54
93
  }, event: EventResponse<LibraryInfo>
55
94
  ) => void;
95
+
96
+ /**
97
+ * This event is emitted when the client requires for a search to be performed. Input is the search query.
98
+ * @param query
99
+ * @param event
100
+ * @returns
101
+ */
56
102
  'library-search': (query: string, event: EventResponse<BasicLibraryInfo[]>) => void;
57
103
  'game-details': (appID: number, event: EventResponse<StoreData>) => void;
58
104
  exit: () => void;
105
+ 'request-dl': (appID: number, info: SearchResult, event: EventResponse<SearchResult>) => void;
59
106
  }
60
107
 
61
108
  export interface StoreData {
@@ -89,6 +136,22 @@ export interface OGIAddonConfiguration {
89
136
  author: string;
90
137
  repository: string;
91
138
  }
139
+
140
+ /**
141
+ * 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.
142
+ * @example
143
+ * ```typescript
144
+ * const addon = new OGIAddon({
145
+ * name: 'Test Addon',
146
+ * id: 'test-addon',
147
+ * description: 'A test addon',
148
+ * version: '1.0.0',
149
+ * author: 'OGI Developers',
150
+ * repository: ''
151
+ * });
152
+ * ```
153
+ *
154
+ */
92
155
  export default class OGIAddon {
93
156
  public eventEmitter = new events.EventEmitter();
94
157
  public addonWSListener: OGIAddonWSListener;
@@ -100,6 +163,11 @@ export default class OGIAddon {
100
163
  this.addonWSListener = new OGIAddonWSListener(this, this.eventEmitter);
101
164
  }
102
165
 
166
+ /**
167
+ * Register an event listener for the addon. (See EventListenerTypes)
168
+ * @param event {OGIAddonEvent}
169
+ * @param listener {EventListenerTypes[OGIAddonEvent]}
170
+ */
103
171
  public on<T extends OGIAddonEvent>(event: T, listener: EventListenerTypes[T]) {
104
172
  this.eventEmitter.on(event, listener);
105
173
  }
@@ -108,11 +176,18 @@ export default class OGIAddon {
108
176
  this.eventEmitter.emit(event, ...args);
109
177
  }
110
178
 
179
+ /**
180
+ * Notify the client using a notification. Provide the type of notification, the message, and an ID.
181
+ * @param notification {Notification}
182
+ */
111
183
  public notify(notification: Notification) {
112
184
  this.addonWSListener.send('notification', [ notification ]);
113
185
  }
114
186
  }
115
187
 
188
+ /**
189
+ * Library Info is the metadata for a library entry after setting up a game.
190
+ */
116
191
  export interface LibraryInfo {
117
192
  name: string;
118
193
  version: string;
@@ -266,8 +341,21 @@ class OGIAddonWSListener {
266
341
  const gameDetailsResult = await this.waitForEventToRespond(gameDetailsEvent);
267
342
  this.respondToMessage(message.id!!, gameDetailsResult.data);
268
343
  break
269
- }
270
- });
344
+ case 'request-dl':
345
+ let requestDLEvent = new EventResponse<SearchResult>((screen, name, description) => this.userInputAsked(screen, name, description, this.socket));
346
+ if (this.eventEmitter.listenerCount('request-dl') === 0) {
347
+ this.respondToMessage(message.id!!, { error: 'No event listener for request-dl' });
348
+ break;
349
+ }
350
+ this.eventEmitter.emit('request-dl', message.args.appID, message.args.info, requestDLEvent);
351
+ const requestDLResult = await this.waitForEventToRespond(requestDLEvent);
352
+ if (requestDLEvent.data === null || requestDLEvent.data?.downloadType === 'request') {
353
+ 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.');
354
+ }
355
+ this.respondToMessage(message.id!!, requestDLResult.data);
356
+ break
357
+ }
358
+ });
271
359
  }
272
360
 
273
361
  private waitForEventToRespond<T>(event: EventResponse<T>): Promise<EventResponse<T>> {
package/tsconfig.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "ESNext",
6
6
  "DOM"
7
7
  ], // specifies which default set of type definitions to use ("DOM", "ES6", etc)
8
- "removeComments": true, // Strips all comments from TypeScript files when converting into JavaScript- you rarely read compiled code so this saves space
8
+ "removeComments": false, // Strips all comments from TypeScript files when converting into JavaScript- you rarely read compiled code so this saves space
9
9
  "target": "ESNext", // Target environment. Most modern browsers support ES6, but you may want to set it to newer or older. (defaults to ES3)
10
10
  "declaration": true,
11
11