ogi-addon 1.8.1 → 1.9.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/main.cjs +39 -2
- package/build/main.cjs.map +1 -1
- package/build/main.d.cts +57 -7
- package/build/main.d.ts +57 -7
- package/build/main.js +37 -1
- package/build/main.js.map +1 -1
- package/package.json +6 -1
- package/src/main.ts +77 -14
package/build/main.cjs
CHANGED
|
@@ -36,6 +36,7 @@ __export(main_exports, {
|
|
|
36
36
|
EventResponse: () => EventResponse,
|
|
37
37
|
SearchTool: () => SearchTool,
|
|
38
38
|
VERSION: () => VERSION,
|
|
39
|
+
ZodLibraryInfo: () => ZodLibraryInfo,
|
|
39
40
|
default: () => OGIAddon
|
|
40
41
|
});
|
|
41
42
|
module.exports = __toCommonJS(main_exports);
|
|
@@ -419,7 +420,7 @@ var package_default = {
|
|
|
419
420
|
module: "./build/main.js",
|
|
420
421
|
type: "module",
|
|
421
422
|
main: "./build/main.cjs",
|
|
422
|
-
version: "1.
|
|
423
|
+
version: "1.9.2",
|
|
423
424
|
exports: {
|
|
424
425
|
".": {
|
|
425
426
|
import: {
|
|
@@ -448,6 +449,11 @@ var package_default = {
|
|
|
448
449
|
email: "me@nat3z.com",
|
|
449
450
|
url: "https://nat3z.com/"
|
|
450
451
|
},
|
|
452
|
+
repository: {
|
|
453
|
+
type: "git",
|
|
454
|
+
url: "https://github.com/Nat3z/OpenGameInstaller",
|
|
455
|
+
directory: "packages/ogi-addon"
|
|
456
|
+
},
|
|
451
457
|
dependencies: {
|
|
452
458
|
"fuse.js": "^7.1.0",
|
|
453
459
|
ws: "^8.4.0",
|
|
@@ -472,6 +478,7 @@ var package_default = {
|
|
|
472
478
|
// src/main.ts
|
|
473
479
|
var import_node_child_process = require("child_process");
|
|
474
480
|
var import_node_fs = __toESM(require("fs"), 1);
|
|
481
|
+
var import_zod2 = require("zod");
|
|
475
482
|
var defaultPort = 7654;
|
|
476
483
|
var VERSION = package_default.version;
|
|
477
484
|
var OGIAddon = class {
|
|
@@ -712,6 +719,19 @@ var SearchTool = class {
|
|
|
712
719
|
items.map((item) => this.fuse.add(item));
|
|
713
720
|
}
|
|
714
721
|
};
|
|
722
|
+
var ZodLibraryInfo = import_zod2.z.object({
|
|
723
|
+
name: import_zod2.z.string(),
|
|
724
|
+
version: import_zod2.z.string(),
|
|
725
|
+
cwd: import_zod2.z.string(),
|
|
726
|
+
appID: import_zod2.z.number(),
|
|
727
|
+
launchExecutable: import_zod2.z.string(),
|
|
728
|
+
launchArguments: import_zod2.z.string().optional(),
|
|
729
|
+
capsuleImage: import_zod2.z.string(),
|
|
730
|
+
storefront: import_zod2.z.string(),
|
|
731
|
+
addonsource: import_zod2.z.string(),
|
|
732
|
+
coverImage: import_zod2.z.string(),
|
|
733
|
+
titleImage: import_zod2.z.string().optional()
|
|
734
|
+
});
|
|
715
735
|
var OGIAddonWSListener = class {
|
|
716
736
|
socket;
|
|
717
737
|
eventEmitter;
|
|
@@ -904,6 +924,22 @@ var OGIAddonWSListener = class {
|
|
|
904
924
|
gameDetailsEvent
|
|
905
925
|
);
|
|
906
926
|
break;
|
|
927
|
+
case "check-for-updates":
|
|
928
|
+
let checkForUpdatesEvent = new EventResponse(
|
|
929
|
+
(screen, name, description) => this.userInputAsked(screen, name, description, this.socket)
|
|
930
|
+
);
|
|
931
|
+
this.eventEmitter.emit(
|
|
932
|
+
"check-for-updates",
|
|
933
|
+
message.args,
|
|
934
|
+
checkForUpdatesEvent
|
|
935
|
+
);
|
|
936
|
+
const checkForUpdatesResult = await this.waitForEventToRespond(checkForUpdatesEvent);
|
|
937
|
+
this.respondToMessage(
|
|
938
|
+
message.id,
|
|
939
|
+
checkForUpdatesResult.data,
|
|
940
|
+
checkForUpdatesEvent
|
|
941
|
+
);
|
|
942
|
+
break;
|
|
907
943
|
case "request-dl":
|
|
908
944
|
let requestDLEvent = new EventResponse(
|
|
909
945
|
(screen, name, description) => this.userInputAsked(screen, name, description, this.socket)
|
|
@@ -1044,6 +1080,7 @@ var OGIAddonWSListener = class {
|
|
|
1044
1080
|
CustomTask,
|
|
1045
1081
|
EventResponse,
|
|
1046
1082
|
SearchTool,
|
|
1047
|
-
VERSION
|
|
1083
|
+
VERSION,
|
|
1084
|
+
ZodLibraryInfo
|
|
1048
1085
|
});
|
|
1049
1086
|
//# sourceMappingURL=main.cjs.map
|
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 {\n ConfigurationBuilder,\n ConfigurationFile,\n} 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 =\n | 'connect'\n | 'disconnect'\n | 'configure'\n | 'authenticate'\n | 'search'\n | 'setup'\n | 'library-search'\n | 'game-details'\n | 'exit'\n | 'task-run'\n | 'request-dl'\n | 'catalog';\n\nexport type OGIAddonClientSentEvent =\n | 'response'\n | 'authenticate'\n | 'configure'\n | 'defer-update'\n | 'notification'\n | 'input-asked'\n | 'get-app-details'\n | 'search-app-name'\n | 'flag'\n | 'task-update';\n\nexport type OGIAddonServerSentEvent =\n | 'authenticate'\n | 'configure'\n | 'config-update'\n | 'search'\n | 'setup'\n | 'response'\n | 'library-search'\n | 'task-run'\n | 'game-details'\n | 'request-dl'\n | 'catalog';\nexport { ConfigurationBuilder, Configuration, EventResponse, SearchResult };\nconst defaultPort = 7654;\nimport pjson from '../package.json';\nimport { exec, spawn } from 'node:child_process';\nimport fs from 'node:fs';\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 'task-update': {\n id: string;\n progress: number;\n logs: string[];\n finished: boolean;\n failed: string | undefined;\n };\n 'get-app-details': {\n appID: number;\n storefront: string;\n };\n 'search-app-name': {\n query: string;\n storefront: string;\n };\n flag: {\n flag: string;\n value: string | string[];\n };\n}\n\nexport type BasicLibraryInfo = {\n name: string;\n capsuleImage: string;\n appID: number;\n storefront: string;\n};\n\nexport type SetupEventResponse = Omit<\n LibraryInfo,\n | 'capsuleImage'\n | 'coverImage'\n | 'name'\n | 'appID'\n | 'storefront'\n | 'addonsource'\n | 'titleImage'\n> & {\n redistributables?: {\n name: string;\n path: string;\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 event\n * @returns\n */\n connect: (event: EventResponse<void>) => 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: (\n query: { storefront: string; appID: number; for: 'game' | 'task' | 'all' },\n event: EventResponse<SearchResult[]>\n ) => 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' | 'empty';\n name: string;\n usedRealDebrid: boolean;\n multiPartFiles?: {\n name: string;\n downloadURL: string;\n }[];\n appID: number;\n storefront: string;\n manifest?: Record<string, unknown>;\n },\n event: EventResponse<SetupEventResponse>\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': (\n query: string,\n event: EventResponse<BasicLibraryInfo[]>\n ) => void;\n\n /**\n * This event is emitted when the client requests for a task to be run. Addon should resolve the event with the task.\n * @param task\n * @param event\n * @returns\n */\n 'task-run': (\n task: {\n manifest: Record<string, unknown>;\n downloadPath: string;\n name: string;\n },\n event: EventResponse<void>\n ) => void;\n\n /**\n * This event is emitted when the client requests for a game details to be fetched. Addon should resolve the event with the game details. This is used to generate a store page for the game.\n * @param appID\n * @param event\n * @returns\n */\n 'game-details': (\n details: { appID: number; storefront: string },\n event: EventResponse<StoreData | undefined>\n ) => void;\n\n /**\n * This event is emitted when the client requests for the addon to exit. Use this to perform any cleanup tasks, ending with a `process.exit(0)`.\n * @returns\n */\n exit: () => void;\n\n /**\n * This event is emitted when the client requests for a download to be performed with the 'request' type. Addon should resolve the event with a SearchResult containing the actual download info.\n * @param appID\n * @param info\n * @param event\n * @returns\n */\n 'request-dl': (\n appID: number,\n info: SearchResult,\n event: EventResponse<SearchResult>\n ) => void;\n\n /**\n * This event is emitted when the client requests for a catalog to be fetched. Addon should resolve the event with the catalog.\n * @param event\n * @returns\n */\n catalog: (\n event: Omit<\n EventResponse<{\n [key: string]: {\n name: string;\n description: string;\n listings: BasicLibraryInfo[];\n };\n }>,\n 'askForInput'\n >\n ) => 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 statusError?: string;\n}\nexport interface WebsocketMessageServer {\n event: OGIAddonServerSentEvent;\n id?: string;\n args: any;\n statusError?: string;\n}\n\n/**\n * The configuration for the addon. This is used to identify the addon and provide information about it.\n * Storefronts is an array of names of stores that the addon supports.\n */\nexport interface OGIAddonConfiguration {\n name: string;\n id: string;\n description: string;\n version: string;\n\n author: string;\n repository: string;\n storefronts: 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 private eventsAvailable: OGIAddonEvent[] = [];\n private registeredConnectEvent: boolean = false;\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>(\n event: T,\n listener: EventListenerTypes[T]\n ) {\n this.eventEmitter.on(event, listener);\n this.eventsAvailable.push(event);\n // wait for the addon to be connected\n if (!this.registeredConnectEvent) {\n this.addonWSListener.eventEmitter.once('connect', () => {\n this.addonWSListener.send('flag', {\n flag: 'events-available',\n value: this.eventsAvailable,\n });\n });\n this.registeredConnectEvent = true;\n }\n }\n\n public emit<T extends OGIAddonEvent>(\n event: T,\n ...args: Parameters<EventListenerTypes[T]>\n ) {\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 * Get the app details for a given appID and storefront.\n * @param appID {number}\n * @param storefront {string}\n * @returns {Promise<StoreData>}\n */\n public async getAppDetails(appID: number, storefront: string) {\n const id = this.addonWSListener.send('get-app-details', {\n appID,\n storefront,\n });\n return await this.addonWSListener.waitForResponseFromServer<\n StoreData | undefined\n >(id);\n }\n\n public async searchGame(query: string, storefront: string) {\n const id = this.addonWSListener.send('search-app-name', {\n query,\n storefront,\n });\n return await this.addonWSListener.waitForResponseFromServer<\n BasicLibraryInfo[]\n >(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', {\n id,\n progress,\n logs,\n finished: false,\n failed: undefined,\n });\n return task;\n }\n\n /**\n * Extract a file using 7-Zip on Windows, unzip on Linux/Mac.\n * @param path {string}\n * @param outputPath {string}\n * @param type {'unrar' | 'unzip'}\n * @returns {Promise<void>}\n */\n public async extractFile(\n path: string,\n outputPath: string,\n type: 'unrar' | 'unzip'\n ) {\n return new Promise<void>((resolve, reject) => {\n // Ensure outputPath exists\n if (!fs.existsSync(outputPath)) {\n fs.mkdirSync(outputPath, { recursive: true });\n }\n\n if (type === 'unzip') {\n // Prefer 7-Zip on Windows, unzip on Linux/Mac\n if (process.platform === 'win32') {\n // 7-Zip path (default install location)\n const s7ZipPath = '\"C:\\\\Program Files\\\\7-Zip\\\\7z.exe\"';\n exec(\n `${s7ZipPath} x \"${path}\" -o\"${outputPath}\"`,\n (err: any, stdout: any, stderr: any) => {\n if (err) {\n console.error(err);\n console.log(stderr);\n reject(new Error('Failed to extract ZIP file'));\n return;\n }\n console.log(stdout);\n console.log(stderr);\n resolve();\n }\n );\n } else {\n // Use unzip on Linux/Mac\n const unzipProcess = spawn(\n 'unzip',\n [\n '-o', // overwrite files without prompting\n path,\n '-d', // specify output directory\n outputPath,\n ],\n {\n env: {\n ...process.env,\n UNZIP_DISABLE_ZIPBOMB_DETECTION: 'TRUE',\n },\n }\n );\n\n unzipProcess.stdout.on('data', (data: Buffer) => {\n console.log(`[unzip stdout]: ${data}`);\n });\n\n unzipProcess.stderr.on('data', (data: Buffer) => {\n console.error(`[unzip stderr]: ${data}`);\n });\n\n unzipProcess.on('close', (code: number) => {\n if (code !== 0) {\n console.error(`unzip process exited with code ${code}`);\n reject(new Error('Failed to extract ZIP file'));\n return;\n }\n resolve();\n });\n }\n } else if (type === 'unrar') {\n if (process.platform === 'win32') {\n // 7-Zip path (default install location)\n const s7ZipPath = '\"C:\\\\Program Files\\\\7-Zip\\\\7z.exe\"';\n exec(\n `${s7ZipPath} x \"${path}\" -o\"${outputPath}\"`,\n (err: any, stdout: any, stderr: any) => {\n if (err) {\n console.error(err);\n console.log(stderr);\n reject(new Error('Failed to extract RAR file'));\n return;\n }\n console.log(stdout);\n console.log(stderr);\n resolve();\n }\n );\n } else {\n // Use unrar on Linux/Mac\n const unrarProcess = spawn('unrar', ['x', '-y', path, outputPath]);\n\n unrarProcess.stdout.on('data', (data: Buffer) => {\n console.log(`[unrar stdout]: ${data}`);\n });\n\n unrarProcess.stderr.on('data', (data: Buffer) => {\n console.error(`[unrar stderr]: ${data}`);\n });\n\n unrarProcess.on('close', (code: number) => {\n if (code !== 0) {\n console.error(`unrar process exited with code ${code}`);\n reject(new Error('Failed to extract RAR file'));\n return;\n }\n resolve();\n });\n }\n } else {\n reject(new Error('Unknown extraction type'));\n }\n });\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(\n ws: OGIAddonWSListener,\n id: string,\n progress: number,\n logs: string[]\n ) {\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', {\n id: this.id,\n progress: this.progress,\n logs: this.logs,\n finished: this.finished,\n failed: this.failed,\n });\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(\n items: T[],\n keys: string[],\n options: Omit<IFuseOptions<T>, 'keys'> = {\n threshold: 0.3,\n includeScore: true,\n }\n ) {\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\n .search(query)\n .slice(0, limit)\n .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: string;\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 (\n process.argv[process.argv.length - 1].split('=')[0] !== '--addonSecret'\n ) {\n throw new Error(\n 'No secret provided. This usually happens because the addon was not started by the OGI Addon Server.'\n );\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 // 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 // wait for the config-update to be received then send connect\n const configListener = (event: ws.MessageEvent) => {\n if (event === undefined) return;\n // event can be a Buffer, string, ArrayBuffer, or Buffer[]\n let data: string;\n if (typeof event === 'string') {\n data = event;\n } else if (event instanceof Buffer) {\n data = event.toString();\n } else if (event && typeof (event as any).data === 'string') {\n data = (event as any).data;\n } else if (event && (event as any).data instanceof Buffer) {\n data = (event as any).data.toString();\n } else {\n // fallback for other types\n data = event.toString();\n }\n const message: WebsocketMessageServer = JSON.parse(data);\n if (message.event === 'config-update') {\n console.log('Config update received');\n this.socket.off('message', configListener);\n this.eventEmitter.emit(\n 'connect',\n new EventResponse<void>((screen, name, description) => {\n return this.userInputAsked(\n screen,\n name,\n description,\n this.socket\n );\n })\n );\n }\n };\n this.socket.on('message', configListener);\n });\n\n this.socket.on('error', (error) => {\n if (error.message.includes('Failed to connect')) {\n throw new Error(\n 'OGI Addon Server is not running/is unreachable. Please start the server and try again.'\n );\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(\n configBuilt: ConfigurationBuilder,\n name: string,\n description: string,\n socket: WebSocket\n ): 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(\n JSON.stringify({\n event: 'input-asked',\n args: {\n config,\n name,\n description,\n },\n id: id,\n })\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(\n message.id!!,\n {\n success: false,\n error: result[1],\n },\n undefined\n );\n } else {\n this.respondToMessage(message.id!!, { success: true }, undefined);\n }\n break;\n case 'search':\n let searchResultEvent = new EventResponse<SearchResult[]>(\n (screen, name, description) =>\n this.userInputAsked(screen, name, description, this.socket)\n );\n this.eventEmitter.emit('search', message.args, searchResultEvent);\n const searchResult =\n await this.waitForEventToRespond(searchResultEvent);\n this.respondToMessage(\n message.id!!,\n searchResult.data,\n searchResultEvent\n );\n break;\n case 'setup': {\n let setupEvent = new EventResponse<SetupEventResponse>(\n (screen, name, description) =>\n this.userInputAsked(screen, name, description, this.socket)\n );\n this.eventEmitter.emit('setup', message.args, 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, setupEvent);\n break;\n }\n case 'library-search':\n let librarySearchEvent = new EventResponse<BasicLibraryInfo[]>(\n (screen, name, description) =>\n this.userInputAsked(screen, name, description, this.socket)\n );\n this.eventEmitter.emit(\n 'library-search',\n message.args,\n librarySearchEvent\n );\n const librarySearchResult =\n await this.waitForEventToRespond(librarySearchEvent);\n this.respondToMessage(\n message.id!!,\n librarySearchResult.data,\n librarySearchEvent\n );\n break;\n case 'game-details':\n let gameDetailsEvent = new EventResponse<StoreData | undefined>(\n (screen, name, description) =>\n this.userInputAsked(screen, name, description, this.socket)\n );\n if (this.eventEmitter.listenerCount('game-details') === 0) {\n this.respondToMessage(\n message.id!!,\n {\n error: 'No event listener for game-details',\n },\n gameDetailsEvent\n );\n break;\n }\n this.eventEmitter.emit(\n 'game-details',\n message.args,\n gameDetailsEvent\n );\n const gameDetailsResult =\n await this.waitForEventToRespond(gameDetailsEvent);\n this.respondToMessage(\n message.id!!,\n gameDetailsResult.data,\n gameDetailsEvent\n );\n break;\n case 'request-dl':\n let requestDLEvent = new EventResponse<SearchResult>(\n (screen, name, description) =>\n this.userInputAsked(screen, name, description, this.socket)\n );\n if (this.eventEmitter.listenerCount('request-dl') === 0) {\n this.respondToMessage(\n message.id!!,\n {\n error: 'No event listener for request-dl',\n },\n requestDLEvent\n );\n break;\n }\n this.eventEmitter.emit(\n 'request-dl',\n message.args.appID,\n message.args.info,\n requestDLEvent\n );\n const requestDLResult =\n await this.waitForEventToRespond(requestDLEvent);\n if (requestDLEvent.failed) {\n this.respondToMessage(message.id!!, undefined, requestDLEvent);\n break;\n }\n if (\n requestDLEvent.data === undefined ||\n requestDLEvent.data?.downloadType === 'request'\n ) {\n throw new Error(\n 'Request DL event did not return a valid result. Please ensure that the event does not resolve with another `request` download type.'\n );\n }\n this.respondToMessage(\n message.id!!,\n requestDLResult.data,\n requestDLEvent\n );\n break;\n case 'catalog':\n let catalogEvent = new EventResponse<{\n [key: string]: {\n name: string;\n description: string;\n listings: BasicLibraryInfo[];\n };\n }>();\n this.eventEmitter.emit('catalog', catalogEvent);\n const catalogResult = await this.waitForEventToRespond(catalogEvent);\n this.respondToMessage(message.id!!, catalogResult.data, catalogEvent);\n break;\n case 'task-run': {\n let taskRunEvent = new EventResponse<void>(\n (screen, name, description) =>\n this.userInputAsked(screen, name, description, this.socket)\n );\n this.eventEmitter.emit('task-run', message.args, taskRunEvent);\n const interval = setInterval(() => {\n if (taskRunEvent.resolved) {\n clearInterval(interval);\n return;\n }\n this.send('defer-update', {\n logs: taskRunEvent.logs,\n deferID: message.args.deferID,\n progress: taskRunEvent.progress,\n failed: taskRunEvent.failed,\n } as ClientSentEventTypes['defer-update']);\n }, 100);\n const taskRunResult = await this.waitForEventToRespond(taskRunEvent);\n this.respondToMessage(message.id!!, taskRunResult.data, taskRunEvent);\n break;\n }\n }\n });\n }\n\n private waitForEventToRespond<T>(\n event: EventResponse<T>\n ): 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 } else {\n reject('Event did not respond in time');\n }\n }, 5000);\n });\n }\n\n public respondToMessage(\n messageID: string,\n response: any,\n originalEvent: EventResponse<any> | undefined\n ) {\n this.socket.send(\n JSON.stringify({\n event: 'response',\n id: messageID,\n args: response,\n statusError: originalEvent ? originalEvent.failed : undefined,\n })\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 } else {\n this.socket.once('message', waiter);\n }\n };\n this.socket.once('message', waiter);\n });\n }\n\n public send(\n event: OGIAddonClientSentEvent,\n args: ClientSentEventTypes[OGIAddonClientSentEvent]\n ): string {\n // generate a random id\n const id = Math.random().toString(36).substring(7);\n this.socket.send(\n JSON.stringify({\n event,\n args,\n id,\n })\n );\n return id;\n }\n\n public close() {\n this.socket.close();\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(\n option: ConfigurationOption\n): option is StringOption {\n return option.type === 'string';\n}\n\nexport function isNumberOption(\n option: ConfigurationOption\n): option is NumberOption {\n return option.type === 'number';\n}\n\nexport function isBooleanOption(\n option: ConfigurationOption\n): 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(\n option: (option: NumberOption) => NumberOption\n ): 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 } 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 (\n input.length < this.minTextLength ||\n input.length > this.maxTextLength\n ) {\n return [\n false,\n 'Input is not within the text length ' +\n this.minTextLength +\n ' and ' +\n this.maxTextLength +\n ' characters (currently ' +\n input.length +\n ' characters)',\n ];\n }\n\n return [\n this.allowedValues.includes(input),\n 'Input is not an allowed value',\n ];\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 [\n false,\n 'Input is not within the range of ' + this.min + ' and ' + this.max,\n ];\n }\n return [true, ''];\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 {\n ConfigurationFile,\n ConfigurationBuilder,\n BooleanOption,\n ConfigurationOption,\n ConfigurationOptionType,\n NumberOption,\n StringOption,\n isBooleanOption,\n isNumberOption,\n isStringOption,\n} 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(\n config: DefiniteConfig,\n validate: boolean = true\n ): [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 (\n this.definiteConfig[key] === null ||\n this.definiteConfig[key] === undefined\n ) {\n console.warn(\n 'Option ' +\n key +\n ' is not defined. Using default value Value: ' +\n this.storedConfigTemplate[key].defaultValue\n );\n this.definiteConfig[key] = this.storedConfigTemplate[key]\n .defaultValue as string | number | boolean;\n }\n if (\n this.storedConfigTemplate[key].type !== typeof this.definiteConfig[key]\n ) {\n throw new Error('Option ' + key + ' is not of the correct type');\n }\n\n const result = this.storedConfigTemplate[key].validate(\n this.definiteConfig[key]\n );\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] === undefined) {\n // remove the key from the definite config\n delete this.definiteConfig[key];\n console.warn(\n 'Option ' +\n key +\n ' is not defined in the configuration template. Removing from config.'\n );\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 {\n ConfigurationFile,\n ConfigurationBuilder,\n BooleanOption,\n ConfigurationOption,\n ConfigurationOptionType,\n NumberOption,\n StringOption,\n isBooleanOption,\n isNumberOption,\n isStringOption,\n};\n","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?: (\n screen: ConfigurationBuilder,\n name: string,\n description: string\n ) => Promise<{ [key: string]: boolean | string | number }>;\n\n constructor(\n onInputAsked?: (\n screen: ConfigurationBuilder,\n name: string,\n description: string\n ) => Promise<{ [key: string]: boolean | string | number }>\n ) {\n this.onInputAsked = onInputAsked;\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(\n name: string,\n description: string,\n screen: ConfigurationBuilder\n ): 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 \"name\": \"ogi-addon\",\n \"module\": \"./build/main.js\",\n \"type\": \"module\",\n \"main\": \"./build/main.cjs\",\n \"version\": \"1.8.1\",\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/minimatch\": \"^6.0.0\",\n \"@types/node\": \"^20.14.12\",\n \"@types/ws\": \"^8.4.0\",\n \"prettier\": \"^3.6.0\",\n \"tsup\": \"^8.2.3\",\n \"typescript\": \"^5.0.0\"\n }\n}\n"],"mappings":"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","names":["z","events","fs","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 {\n ConfigurationBuilder,\n ConfigurationFile,\n} 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 =\n | 'connect'\n | 'disconnect'\n | 'configure'\n | 'authenticate'\n | 'search'\n | 'setup'\n | 'library-search'\n | 'game-details'\n | 'exit'\n | 'check-for-updates'\n | 'task-run'\n | 'request-dl'\n | 'catalog';\n\nexport type OGIAddonClientSentEvent =\n | 'response'\n | 'authenticate'\n | 'configure'\n | 'defer-update'\n | 'notification'\n | 'input-asked'\n | 'get-app-details'\n | 'search-app-name'\n | 'flag'\n | 'task-update';\n\nexport type OGIAddonServerSentEvent =\n | 'authenticate'\n | 'configure'\n | 'config-update'\n | 'search'\n | 'setup'\n | 'response'\n | 'library-search'\n | 'check-for-updates'\n | 'task-run'\n | 'game-details'\n | 'request-dl'\n | 'catalog';\nexport { ConfigurationBuilder, Configuration, EventResponse, SearchResult };\nconst defaultPort = 7654;\nimport pjson from '../package.json';\nimport { exec, spawn } from 'node:child_process';\nimport fs from 'node:fs';\nimport { z } from 'zod';\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 'task-update': {\n id: string;\n progress: number;\n logs: string[];\n finished: boolean;\n failed: string | undefined;\n };\n 'get-app-details': {\n appID: number;\n storefront: string;\n };\n 'search-app-name': {\n query: string;\n storefront: string;\n };\n flag: {\n flag: string;\n value: string | string[];\n };\n}\n\nexport type BasicLibraryInfo = {\n name: string;\n capsuleImage: string;\n appID: number;\n storefront: string;\n};\n\nexport type SetupEventResponse = Omit<\n LibraryInfo,\n | 'capsuleImage'\n | 'coverImage'\n | 'name'\n | 'appID'\n | 'storefront'\n | 'addonsource'\n | 'titleImage'\n> & {\n redistributables?: {\n name: string;\n path: string;\n }[];\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 event\n * @returns\n */\n connect: (event: EventResponse<void>) => 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: (\n query:\n | {\n storefront: string;\n appID: number;\n for: 'game' | 'task' | 'all';\n }\n | {\n storefront: string;\n appID: number;\n for: 'update';\n libraryInfo: LibraryInfo;\n },\n event: EventResponse<SearchResult[]>\n ) => 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 for: 'game' | 'update';\n currentLibraryInfo: LibraryInfo | undefined;\n path: string;\n type: 'direct' | 'torrent' | 'magnet' | 'empty';\n name: string;\n usedRealDebrid: boolean;\n multiPartFiles?: {\n name: string;\n downloadURL: string;\n }[];\n appID: number;\n storefront: string;\n manifest?: Record<string, unknown>;\n },\n event: EventResponse<SetupEventResponse>\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': (\n query: string,\n event: EventResponse<BasicLibraryInfo[]>\n ) => void;\n\n /**\n * This event is emitted when the client requests for a task to be run. Addon should resolve the event with the task.\n * @param task\n * @param event\n * @returns\n */\n 'task-run': (\n task: {\n manifest: Record<string, unknown>;\n downloadPath: string;\n name: string;\n },\n event: EventResponse<void>\n ) => void;\n\n /**\n * This event is emitted when the client requests for a game details to be fetched. Addon should resolve the event with the game details. This is used to generate a store page for the game.\n * @param appID\n * @param event\n * @returns\n */\n 'game-details': (\n details: { appID: number; storefront: string },\n event: EventResponse<StoreData | undefined>\n ) => void;\n\n /**\n * This event is emitted when the client requests for the addon to exit. Use this to perform any cleanup tasks, ending with a `process.exit(0)`.\n * @returns\n */\n exit: () => void;\n\n /**\n * This event is emitted when the client requests for a download to be performed with the 'request' type. Addon should resolve the event with a SearchResult containing the actual download info.\n * @param appID\n * @param info\n * @param event\n * @returns\n */\n 'request-dl': (\n appID: number,\n info: SearchResult,\n event: EventResponse<SearchResult>\n ) => void;\n\n /**\n * This event is emitted when the client requests for a catalog to be fetched. Addon should resolve the event with the catalog.\n * @param event\n * @returns\n */\n catalog: (\n event: Omit<\n EventResponse<{\n [key: string]: {\n name: string;\n description: string;\n listings: BasicLibraryInfo[];\n };\n }>,\n 'askForInput'\n >\n ) => void;\n\n /**\n * This event is emitted when the client requests for an addon to check for updates. Addon should resolve the event with the update information.\n * @param data\n * @param event\n * @returns\n */\n 'check-for-updates': (\n data: { appID: number; storefront: string; currentVersion: string },\n event: EventResponse<\n | {\n available: true;\n version: string;\n }\n | {\n available: false;\n }\n >\n ) => 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 latestVersion: string;\n}\nexport interface WebsocketMessageClient {\n event: OGIAddonClientSentEvent;\n id?: string;\n args: any;\n statusError?: string;\n}\nexport interface WebsocketMessageServer {\n event: OGIAddonServerSentEvent;\n id?: string;\n args: any;\n statusError?: string;\n}\n\n/**\n * The configuration for the addon. This is used to identify the addon and provide information about it.\n * Storefronts is an array of names of stores that the addon supports.\n */\nexport interface OGIAddonConfiguration {\n name: string;\n id: string;\n description: string;\n version: string;\n\n author: string;\n repository: string;\n storefronts: 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 private eventsAvailable: OGIAddonEvent[] = [];\n private registeredConnectEvent: boolean = false;\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>(\n event: T,\n listener: EventListenerTypes[T]\n ) {\n this.eventEmitter.on(event, listener);\n this.eventsAvailable.push(event);\n // wait for the addon to be connected\n if (!this.registeredConnectEvent) {\n this.addonWSListener.eventEmitter.once('connect', () => {\n this.addonWSListener.send('flag', {\n flag: 'events-available',\n value: this.eventsAvailable,\n });\n });\n this.registeredConnectEvent = true;\n }\n }\n\n public emit<T extends OGIAddonEvent>(\n event: T,\n ...args: Parameters<EventListenerTypes[T]>\n ) {\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 * Get the app details for a given appID and storefront.\n * @param appID {number}\n * @param storefront {string}\n * @returns {Promise<StoreData>}\n */\n public async getAppDetails(appID: number, storefront: string) {\n const id = this.addonWSListener.send('get-app-details', {\n appID,\n storefront,\n });\n return await this.addonWSListener.waitForResponseFromServer<\n StoreData | undefined\n >(id);\n }\n\n public async searchGame(query: string, storefront: string) {\n const id = this.addonWSListener.send('search-app-name', {\n query,\n storefront,\n });\n return await this.addonWSListener.waitForResponseFromServer<\n BasicLibraryInfo[]\n >(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', {\n id,\n progress,\n logs,\n finished: false,\n failed: undefined,\n });\n return task;\n }\n\n /**\n * Extract a file using 7-Zip on Windows, unzip on Linux/Mac.\n * @param path {string}\n * @param outputPath {string}\n * @param type {'unrar' | 'unzip'}\n * @returns {Promise<void>}\n */\n public async extractFile(\n path: string,\n outputPath: string,\n type: 'unrar' | 'unzip'\n ) {\n return new Promise<void>((resolve, reject) => {\n // Ensure outputPath exists\n if (!fs.existsSync(outputPath)) {\n fs.mkdirSync(outputPath, { recursive: true });\n }\n\n if (type === 'unzip') {\n // Prefer 7-Zip on Windows, unzip on Linux/Mac\n if (process.platform === 'win32') {\n // 7-Zip path (default install location)\n const s7ZipPath = '\"C:\\\\Program Files\\\\7-Zip\\\\7z.exe\"';\n exec(\n `${s7ZipPath} x \"${path}\" -o\"${outputPath}\"`,\n (err: any, stdout: any, stderr: any) => {\n if (err) {\n console.error(err);\n console.log(stderr);\n reject(new Error('Failed to extract ZIP file'));\n return;\n }\n console.log(stdout);\n console.log(stderr);\n resolve();\n }\n );\n } else {\n // Use unzip on Linux/Mac\n const unzipProcess = spawn(\n 'unzip',\n [\n '-o', // overwrite files without prompting\n path,\n '-d', // specify output directory\n outputPath,\n ],\n {\n env: {\n ...process.env,\n UNZIP_DISABLE_ZIPBOMB_DETECTION: 'TRUE',\n },\n }\n );\n\n unzipProcess.stdout.on('data', (data: Buffer) => {\n console.log(`[unzip stdout]: ${data}`);\n });\n\n unzipProcess.stderr.on('data', (data: Buffer) => {\n console.error(`[unzip stderr]: ${data}`);\n });\n\n unzipProcess.on('close', (code: number) => {\n if (code !== 0) {\n console.error(`unzip process exited with code ${code}`);\n reject(new Error('Failed to extract ZIP file'));\n return;\n }\n resolve();\n });\n }\n } else if (type === 'unrar') {\n if (process.platform === 'win32') {\n // 7-Zip path (default install location)\n const s7ZipPath = '\"C:\\\\Program Files\\\\7-Zip\\\\7z.exe\"';\n exec(\n `${s7ZipPath} x \"${path}\" -o\"${outputPath}\"`,\n (err: any, stdout: any, stderr: any) => {\n if (err) {\n console.error(err);\n console.log(stderr);\n reject(new Error('Failed to extract RAR file'));\n return;\n }\n console.log(stdout);\n console.log(stderr);\n resolve();\n }\n );\n } else {\n // Use unrar on Linux/Mac\n const unrarProcess = spawn('unrar', ['x', '-y', path, outputPath]);\n\n unrarProcess.stdout.on('data', (data: Buffer) => {\n console.log(`[unrar stdout]: ${data}`);\n });\n\n unrarProcess.stderr.on('data', (data: Buffer) => {\n console.error(`[unrar stderr]: ${data}`);\n });\n\n unrarProcess.on('close', (code: number) => {\n if (code !== 0) {\n console.error(`unrar process exited with code ${code}`);\n reject(new Error('Failed to extract RAR file'));\n return;\n }\n resolve();\n });\n }\n } else {\n reject(new Error('Unknown extraction type'));\n }\n });\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(\n ws: OGIAddonWSListener,\n id: string,\n progress: number,\n logs: string[]\n ) {\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', {\n id: this.id,\n progress: this.progress,\n logs: this.logs,\n finished: this.finished,\n failed: this.failed,\n });\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(\n items: T[],\n keys: string[],\n options: Omit<IFuseOptions<T>, 'keys'> = {\n threshold: 0.3,\n includeScore: true,\n }\n ) {\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\n .search(query)\n .slice(0, limit)\n .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 const ZodLibraryInfo = z.object({\n name: z.string(),\n version: z.string(),\n cwd: z.string(),\n appID: z.number(),\n launchExecutable: z.string(),\n launchArguments: z.string().optional(),\n capsuleImage: z.string(),\n storefront: z.string(),\n addonsource: z.string(),\n coverImage: z.string(),\n titleImage: z.string().optional(),\n});\nexport type LibraryInfo = z.infer<typeof ZodLibraryInfo>;\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 (\n process.argv[process.argv.length - 1].split('=')[0] !== '--addonSecret'\n ) {\n throw new Error(\n 'No secret provided. This usually happens because the addon was not started by the OGI Addon Server.'\n );\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 // 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 // wait for the config-update to be received then send connect\n const configListener = (event: ws.MessageEvent) => {\n if (event === undefined) return;\n // event can be a Buffer, string, ArrayBuffer, or Buffer[]\n let data: string;\n if (typeof event === 'string') {\n data = event;\n } else if (event instanceof Buffer) {\n data = event.toString();\n } else if (event && typeof (event as any).data === 'string') {\n data = (event as any).data;\n } else if (event && (event as any).data instanceof Buffer) {\n data = (event as any).data.toString();\n } else {\n // fallback for other types\n data = event.toString();\n }\n const message: WebsocketMessageServer = JSON.parse(data);\n if (message.event === 'config-update') {\n console.log('Config update received');\n this.socket.off('message', configListener);\n this.eventEmitter.emit(\n 'connect',\n new EventResponse<void>((screen, name, description) => {\n return this.userInputAsked(\n screen,\n name,\n description,\n this.socket\n );\n })\n );\n }\n };\n this.socket.on('message', configListener);\n });\n\n this.socket.on('error', (error) => {\n if (error.message.includes('Failed to connect')) {\n throw new Error(\n 'OGI Addon Server is not running/is unreachable. Please start the server and try again.'\n );\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(\n configBuilt: ConfigurationBuilder,\n name: string,\n description: string,\n socket: WebSocket\n ): 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(\n JSON.stringify({\n event: 'input-asked',\n args: {\n config,\n name,\n description,\n },\n id: id,\n })\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(\n message.id!!,\n {\n success: false,\n error: result[1],\n },\n undefined\n );\n } else {\n this.respondToMessage(message.id!!, { success: true }, undefined);\n }\n break;\n case 'search':\n let searchResultEvent = new EventResponse<SearchResult[]>(\n (screen, name, description) =>\n this.userInputAsked(screen, name, description, this.socket)\n );\n this.eventEmitter.emit('search', message.args, searchResultEvent);\n const searchResult =\n await this.waitForEventToRespond(searchResultEvent);\n this.respondToMessage(\n message.id!!,\n searchResult.data,\n searchResultEvent\n );\n break;\n case 'setup': {\n let setupEvent = new EventResponse<SetupEventResponse>(\n (screen, name, description) =>\n this.userInputAsked(screen, name, description, this.socket)\n );\n this.eventEmitter.emit('setup', message.args, 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, setupEvent);\n break;\n }\n case 'library-search':\n let librarySearchEvent = new EventResponse<BasicLibraryInfo[]>(\n (screen, name, description) =>\n this.userInputAsked(screen, name, description, this.socket)\n );\n this.eventEmitter.emit(\n 'library-search',\n message.args,\n librarySearchEvent\n );\n const librarySearchResult =\n await this.waitForEventToRespond(librarySearchEvent);\n this.respondToMessage(\n message.id!!,\n librarySearchResult.data,\n librarySearchEvent\n );\n break;\n case 'game-details':\n let gameDetailsEvent = new EventResponse<StoreData | undefined>(\n (screen, name, description) =>\n this.userInputAsked(screen, name, description, this.socket)\n );\n if (this.eventEmitter.listenerCount('game-details') === 0) {\n this.respondToMessage(\n message.id!!,\n {\n error: 'No event listener for game-details',\n },\n gameDetailsEvent\n );\n break;\n }\n this.eventEmitter.emit(\n 'game-details',\n message.args,\n gameDetailsEvent\n );\n const gameDetailsResult =\n await this.waitForEventToRespond(gameDetailsEvent);\n this.respondToMessage(\n message.id!!,\n gameDetailsResult.data,\n gameDetailsEvent\n );\n break;\n case 'check-for-updates':\n let checkForUpdatesEvent = new EventResponse<\n | {\n available: true;\n version: string;\n }\n | {\n available: false;\n }\n >((screen, name, description) =>\n this.userInputAsked(screen, name, description, this.socket)\n );\n this.eventEmitter.emit(\n 'check-for-updates',\n message.args,\n checkForUpdatesEvent\n );\n const checkForUpdatesResult =\n await this.waitForEventToRespond(checkForUpdatesEvent);\n this.respondToMessage(\n message.id!!,\n checkForUpdatesResult.data,\n checkForUpdatesEvent\n );\n break;\n case 'request-dl':\n let requestDLEvent = new EventResponse<SearchResult>(\n (screen, name, description) =>\n this.userInputAsked(screen, name, description, this.socket)\n );\n if (this.eventEmitter.listenerCount('request-dl') === 0) {\n this.respondToMessage(\n message.id!!,\n {\n error: 'No event listener for request-dl',\n },\n requestDLEvent\n );\n break;\n }\n this.eventEmitter.emit(\n 'request-dl',\n message.args.appID,\n message.args.info,\n requestDLEvent\n );\n const requestDLResult =\n await this.waitForEventToRespond(requestDLEvent);\n if (requestDLEvent.failed) {\n this.respondToMessage(message.id!!, undefined, requestDLEvent);\n break;\n }\n if (\n requestDLEvent.data === undefined ||\n requestDLEvent.data?.downloadType === 'request'\n ) {\n throw new Error(\n 'Request DL event did not return a valid result. Please ensure that the event does not resolve with another `request` download type.'\n );\n }\n this.respondToMessage(\n message.id!!,\n requestDLResult.data,\n requestDLEvent\n );\n break;\n case 'catalog':\n let catalogEvent = new EventResponse<{\n [key: string]: {\n name: string;\n description: string;\n listings: BasicLibraryInfo[];\n };\n }>();\n this.eventEmitter.emit('catalog', catalogEvent);\n const catalogResult = await this.waitForEventToRespond(catalogEvent);\n this.respondToMessage(message.id!!, catalogResult.data, catalogEvent);\n break;\n case 'task-run': {\n let taskRunEvent = new EventResponse<void>(\n (screen, name, description) =>\n this.userInputAsked(screen, name, description, this.socket)\n );\n this.eventEmitter.emit('task-run', message.args, taskRunEvent);\n const interval = setInterval(() => {\n if (taskRunEvent.resolved) {\n clearInterval(interval);\n return;\n }\n this.send('defer-update', {\n logs: taskRunEvent.logs,\n deferID: message.args.deferID,\n progress: taskRunEvent.progress,\n failed: taskRunEvent.failed,\n } as ClientSentEventTypes['defer-update']);\n }, 100);\n const taskRunResult = await this.waitForEventToRespond(taskRunEvent);\n this.respondToMessage(message.id!!, taskRunResult.data, taskRunEvent);\n break;\n }\n }\n });\n }\n\n private waitForEventToRespond<T>(\n event: EventResponse<T>\n ): 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 } else {\n reject('Event did not respond in time');\n }\n }, 5000);\n });\n }\n\n public respondToMessage(\n messageID: string,\n response: any,\n originalEvent: EventResponse<any> | undefined\n ) {\n this.socket.send(\n JSON.stringify({\n event: 'response',\n id: messageID,\n args: response,\n statusError: originalEvent ? originalEvent.failed : undefined,\n })\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 } else {\n this.socket.once('message', waiter);\n }\n };\n this.socket.once('message', waiter);\n });\n }\n\n public send(\n event: OGIAddonClientSentEvent,\n args: ClientSentEventTypes[OGIAddonClientSentEvent]\n ): string {\n // generate a random id\n const id = Math.random().toString(36).substring(7);\n this.socket.send(\n JSON.stringify({\n event,\n args,\n id,\n })\n );\n return id;\n }\n\n public close() {\n this.socket.close();\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(\n option: ConfigurationOption\n): option is StringOption {\n return option.type === 'string';\n}\n\nexport function isNumberOption(\n option: ConfigurationOption\n): option is NumberOption {\n return option.type === 'number';\n}\n\nexport function isBooleanOption(\n option: ConfigurationOption\n): 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(\n option: (option: NumberOption) => NumberOption\n ): 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 } 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 (\n input.length < this.minTextLength ||\n input.length > this.maxTextLength\n ) {\n return [\n false,\n 'Input is not within the text length ' +\n this.minTextLength +\n ' and ' +\n this.maxTextLength +\n ' characters (currently ' +\n input.length +\n ' characters)',\n ];\n }\n\n return [\n this.allowedValues.includes(input),\n 'Input is not an allowed value',\n ];\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 [\n false,\n 'Input is not within the range of ' + this.min + ' and ' + this.max,\n ];\n }\n return [true, ''];\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 {\n ConfigurationFile,\n ConfigurationBuilder,\n BooleanOption,\n ConfigurationOption,\n ConfigurationOptionType,\n NumberOption,\n StringOption,\n isBooleanOption,\n isNumberOption,\n isStringOption,\n} 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(\n config: DefiniteConfig,\n validate: boolean = true\n ): [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 (\n this.definiteConfig[key] === null ||\n this.definiteConfig[key] === undefined\n ) {\n console.warn(\n 'Option ' +\n key +\n ' is not defined. Using default value Value: ' +\n this.storedConfigTemplate[key].defaultValue\n );\n this.definiteConfig[key] = this.storedConfigTemplate[key]\n .defaultValue as string | number | boolean;\n }\n if (\n this.storedConfigTemplate[key].type !== typeof this.definiteConfig[key]\n ) {\n throw new Error('Option ' + key + ' is not of the correct type');\n }\n\n const result = this.storedConfigTemplate[key].validate(\n this.definiteConfig[key]\n );\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] === undefined) {\n // remove the key from the definite config\n delete this.definiteConfig[key];\n console.warn(\n 'Option ' +\n key +\n ' is not defined in the configuration template. Removing from config.'\n );\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 {\n ConfigurationFile,\n ConfigurationBuilder,\n BooleanOption,\n ConfigurationOption,\n ConfigurationOptionType,\n NumberOption,\n StringOption,\n isBooleanOption,\n isNumberOption,\n isStringOption,\n};\n","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?: (\n screen: ConfigurationBuilder,\n name: string,\n description: string\n ) => Promise<{ [key: string]: boolean | string | number }>;\n\n constructor(\n onInputAsked?: (\n screen: ConfigurationBuilder,\n name: string,\n description: string\n ) => Promise<{ [key: string]: boolean | string | number }>\n ) {\n this.onInputAsked = onInputAsked;\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(\n name: string,\n description: string,\n screen: ConfigurationBuilder\n ): 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 \"name\": \"ogi-addon\",\n \"module\": \"./build/main.js\",\n \"type\": \"module\",\n \"main\": \"./build/main.cjs\",\n \"version\": \"1.9.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 \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/Nat3z/OpenGameInstaller\",\n \"directory\": \"packages/ogi-addon\"\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/minimatch\": \"^6.0.0\",\n \"@types/node\": \"^20.14.12\",\n \"@types/ws\": \"^8.4.0\",\n \"prettier\": \"^3.6.0\",\n \"tsup\": \"^8.2.3\",\n \"typescript\": \"^5.0.0\"\n }\n}\n"],"mappings":"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","names":["z","import_zod","events","fs","ws","Fuse"]}
|
package/build/main.d.cts
CHANGED
|
@@ -4,10 +4,11 @@ import { Configuration } from './config/Configuration.cjs';
|
|
|
4
4
|
import EventResponse from './EventResponse.cjs';
|
|
5
5
|
import { SearchResult } from './SearchEngine.cjs';
|
|
6
6
|
import { IFuseOptions } from 'fuse.js';
|
|
7
|
+
import { z } from 'zod';
|
|
7
8
|
|
|
8
|
-
type OGIAddonEvent = 'connect' | 'disconnect' | 'configure' | 'authenticate' | 'search' | 'setup' | 'library-search' | 'game-details' | 'exit' | 'task-run' | 'request-dl' | 'catalog';
|
|
9
|
+
type OGIAddonEvent = 'connect' | 'disconnect' | 'configure' | 'authenticate' | 'search' | 'setup' | 'library-search' | 'game-details' | 'exit' | 'check-for-updates' | 'task-run' | 'request-dl' | 'catalog';
|
|
9
10
|
type OGIAddonClientSentEvent = 'response' | 'authenticate' | 'configure' | 'defer-update' | 'notification' | 'input-asked' | 'get-app-details' | 'search-app-name' | 'flag' | 'task-update';
|
|
10
|
-
type OGIAddonServerSentEvent = 'authenticate' | 'configure' | 'config-update' | 'search' | 'setup' | 'response' | 'library-search' | 'task-run' | 'game-details' | 'request-dl' | 'catalog';
|
|
11
|
+
type OGIAddonServerSentEvent = 'authenticate' | 'configure' | 'config-update' | 'search' | 'setup' | 'response' | 'library-search' | 'check-for-updates' | 'task-run' | 'game-details' | 'request-dl' | 'catalog';
|
|
11
12
|
|
|
12
13
|
declare const VERSION: string;
|
|
13
14
|
interface ClientSentEventTypes {
|
|
@@ -99,6 +100,11 @@ interface EventListenerTypes {
|
|
|
99
100
|
storefront: string;
|
|
100
101
|
appID: number;
|
|
101
102
|
for: 'game' | 'task' | 'all';
|
|
103
|
+
} | {
|
|
104
|
+
storefront: string;
|
|
105
|
+
appID: number;
|
|
106
|
+
for: 'update';
|
|
107
|
+
libraryInfo: LibraryInfo;
|
|
102
108
|
}, event: EventResponse<SearchResult[]>) => void;
|
|
103
109
|
/**
|
|
104
110
|
* 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)
|
|
@@ -107,6 +113,8 @@ interface EventListenerTypes {
|
|
|
107
113
|
* @returns
|
|
108
114
|
*/
|
|
109
115
|
setup: (data: {
|
|
116
|
+
for: 'game' | 'update';
|
|
117
|
+
currentLibraryInfo: LibraryInfo | undefined;
|
|
110
118
|
path: string;
|
|
111
119
|
type: 'direct' | 'torrent' | 'magnet' | 'empty';
|
|
112
120
|
name: string;
|
|
@@ -172,6 +180,22 @@ interface EventListenerTypes {
|
|
|
172
180
|
listings: BasicLibraryInfo[];
|
|
173
181
|
};
|
|
174
182
|
}>, 'askForInput'>) => void;
|
|
183
|
+
/**
|
|
184
|
+
* This event is emitted when the client requests for an addon to check for updates. Addon should resolve the event with the update information.
|
|
185
|
+
* @param data
|
|
186
|
+
* @param event
|
|
187
|
+
* @returns
|
|
188
|
+
*/
|
|
189
|
+
'check-for-updates': (data: {
|
|
190
|
+
appID: number;
|
|
191
|
+
storefront: string;
|
|
192
|
+
currentVersion: string;
|
|
193
|
+
}, event: EventResponse<{
|
|
194
|
+
available: true;
|
|
195
|
+
version: string;
|
|
196
|
+
} | {
|
|
197
|
+
available: false;
|
|
198
|
+
}>) => void;
|
|
175
199
|
}
|
|
176
200
|
interface StoreData {
|
|
177
201
|
name: string;
|
|
@@ -184,6 +208,7 @@ interface StoreData {
|
|
|
184
208
|
basicDescription: string;
|
|
185
209
|
description: string;
|
|
186
210
|
headerImage: string;
|
|
211
|
+
latestVersion: string;
|
|
187
212
|
}
|
|
188
213
|
interface WebsocketMessageClient {
|
|
189
214
|
event: OGIAddonClientSentEvent;
|
|
@@ -300,19 +325,44 @@ declare class SearchTool<T> {
|
|
|
300
325
|
/**
|
|
301
326
|
* Library Info is the metadata for a library entry after setting up a game.
|
|
302
327
|
*/
|
|
303
|
-
|
|
328
|
+
declare const ZodLibraryInfo: z.ZodObject<{
|
|
329
|
+
name: z.ZodString;
|
|
330
|
+
version: z.ZodString;
|
|
331
|
+
cwd: z.ZodString;
|
|
332
|
+
appID: z.ZodNumber;
|
|
333
|
+
launchExecutable: z.ZodString;
|
|
334
|
+
launchArguments: z.ZodOptional<z.ZodString>;
|
|
335
|
+
capsuleImage: z.ZodString;
|
|
336
|
+
storefront: z.ZodString;
|
|
337
|
+
addonsource: z.ZodString;
|
|
338
|
+
coverImage: z.ZodString;
|
|
339
|
+
titleImage: z.ZodOptional<z.ZodString>;
|
|
340
|
+
}, "strip", z.ZodTypeAny, {
|
|
304
341
|
name: string;
|
|
305
342
|
version: string;
|
|
306
343
|
cwd: string;
|
|
307
344
|
appID: number;
|
|
308
345
|
launchExecutable: string;
|
|
309
|
-
launchArguments?: string;
|
|
310
346
|
capsuleImage: string;
|
|
311
347
|
storefront: string;
|
|
312
348
|
addonsource: string;
|
|
313
349
|
coverImage: string;
|
|
314
|
-
|
|
315
|
-
|
|
350
|
+
launchArguments?: string | undefined;
|
|
351
|
+
titleImage?: string | undefined;
|
|
352
|
+
}, {
|
|
353
|
+
name: string;
|
|
354
|
+
version: string;
|
|
355
|
+
cwd: string;
|
|
356
|
+
appID: number;
|
|
357
|
+
launchExecutable: string;
|
|
358
|
+
capsuleImage: string;
|
|
359
|
+
storefront: string;
|
|
360
|
+
addonsource: string;
|
|
361
|
+
coverImage: string;
|
|
362
|
+
launchArguments?: string | undefined;
|
|
363
|
+
titleImage?: string | undefined;
|
|
364
|
+
}>;
|
|
365
|
+
type LibraryInfo = z.infer<typeof ZodLibraryInfo>;
|
|
316
366
|
interface Notification {
|
|
317
367
|
type: 'warning' | 'error' | 'info' | 'success';
|
|
318
368
|
message: string;
|
|
@@ -332,4 +382,4 @@ declare class OGIAddonWSListener {
|
|
|
332
382
|
close(): void;
|
|
333
383
|
}
|
|
334
384
|
|
|
335
|
-
export { type BasicLibraryInfo, type ClientSentEventTypes, Configuration, ConfigurationBuilder, CustomTask, type EventListenerTypes, EventResponse, type LibraryInfo, type OGIAddonClientSentEvent, type OGIAddonConfiguration, type OGIAddonEvent, type OGIAddonServerSentEvent, SearchResult, SearchTool, type SetupEventResponse, type StoreData, VERSION, type WebsocketMessageClient, type WebsocketMessageServer, OGIAddon as default };
|
|
385
|
+
export { type BasicLibraryInfo, type ClientSentEventTypes, Configuration, ConfigurationBuilder, CustomTask, type EventListenerTypes, EventResponse, type LibraryInfo, type OGIAddonClientSentEvent, type OGIAddonConfiguration, type OGIAddonEvent, type OGIAddonServerSentEvent, SearchResult, SearchTool, type SetupEventResponse, type StoreData, VERSION, type WebsocketMessageClient, type WebsocketMessageServer, ZodLibraryInfo, OGIAddon as default };
|
package/build/main.d.ts
CHANGED
|
@@ -4,10 +4,11 @@ import { Configuration } from './config/Configuration.js';
|
|
|
4
4
|
import EventResponse from './EventResponse.js';
|
|
5
5
|
import { SearchResult } from './SearchEngine.js';
|
|
6
6
|
import { IFuseOptions } from 'fuse.js';
|
|
7
|
+
import { z } from 'zod';
|
|
7
8
|
|
|
8
|
-
type OGIAddonEvent = 'connect' | 'disconnect' | 'configure' | 'authenticate' | 'search' | 'setup' | 'library-search' | 'game-details' | 'exit' | 'task-run' | 'request-dl' | 'catalog';
|
|
9
|
+
type OGIAddonEvent = 'connect' | 'disconnect' | 'configure' | 'authenticate' | 'search' | 'setup' | 'library-search' | 'game-details' | 'exit' | 'check-for-updates' | 'task-run' | 'request-dl' | 'catalog';
|
|
9
10
|
type OGIAddonClientSentEvent = 'response' | 'authenticate' | 'configure' | 'defer-update' | 'notification' | 'input-asked' | 'get-app-details' | 'search-app-name' | 'flag' | 'task-update';
|
|
10
|
-
type OGIAddonServerSentEvent = 'authenticate' | 'configure' | 'config-update' | 'search' | 'setup' | 'response' | 'library-search' | 'task-run' | 'game-details' | 'request-dl' | 'catalog';
|
|
11
|
+
type OGIAddonServerSentEvent = 'authenticate' | 'configure' | 'config-update' | 'search' | 'setup' | 'response' | 'library-search' | 'check-for-updates' | 'task-run' | 'game-details' | 'request-dl' | 'catalog';
|
|
11
12
|
|
|
12
13
|
declare const VERSION: string;
|
|
13
14
|
interface ClientSentEventTypes {
|
|
@@ -99,6 +100,11 @@ interface EventListenerTypes {
|
|
|
99
100
|
storefront: string;
|
|
100
101
|
appID: number;
|
|
101
102
|
for: 'game' | 'task' | 'all';
|
|
103
|
+
} | {
|
|
104
|
+
storefront: string;
|
|
105
|
+
appID: number;
|
|
106
|
+
for: 'update';
|
|
107
|
+
libraryInfo: LibraryInfo;
|
|
102
108
|
}, event: EventResponse<SearchResult[]>) => void;
|
|
103
109
|
/**
|
|
104
110
|
* 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)
|
|
@@ -107,6 +113,8 @@ interface EventListenerTypes {
|
|
|
107
113
|
* @returns
|
|
108
114
|
*/
|
|
109
115
|
setup: (data: {
|
|
116
|
+
for: 'game' | 'update';
|
|
117
|
+
currentLibraryInfo: LibraryInfo | undefined;
|
|
110
118
|
path: string;
|
|
111
119
|
type: 'direct' | 'torrent' | 'magnet' | 'empty';
|
|
112
120
|
name: string;
|
|
@@ -172,6 +180,22 @@ interface EventListenerTypes {
|
|
|
172
180
|
listings: BasicLibraryInfo[];
|
|
173
181
|
};
|
|
174
182
|
}>, 'askForInput'>) => void;
|
|
183
|
+
/**
|
|
184
|
+
* This event is emitted when the client requests for an addon to check for updates. Addon should resolve the event with the update information.
|
|
185
|
+
* @param data
|
|
186
|
+
* @param event
|
|
187
|
+
* @returns
|
|
188
|
+
*/
|
|
189
|
+
'check-for-updates': (data: {
|
|
190
|
+
appID: number;
|
|
191
|
+
storefront: string;
|
|
192
|
+
currentVersion: string;
|
|
193
|
+
}, event: EventResponse<{
|
|
194
|
+
available: true;
|
|
195
|
+
version: string;
|
|
196
|
+
} | {
|
|
197
|
+
available: false;
|
|
198
|
+
}>) => void;
|
|
175
199
|
}
|
|
176
200
|
interface StoreData {
|
|
177
201
|
name: string;
|
|
@@ -184,6 +208,7 @@ interface StoreData {
|
|
|
184
208
|
basicDescription: string;
|
|
185
209
|
description: string;
|
|
186
210
|
headerImage: string;
|
|
211
|
+
latestVersion: string;
|
|
187
212
|
}
|
|
188
213
|
interface WebsocketMessageClient {
|
|
189
214
|
event: OGIAddonClientSentEvent;
|
|
@@ -300,19 +325,44 @@ declare class SearchTool<T> {
|
|
|
300
325
|
/**
|
|
301
326
|
* Library Info is the metadata for a library entry after setting up a game.
|
|
302
327
|
*/
|
|
303
|
-
|
|
328
|
+
declare const ZodLibraryInfo: z.ZodObject<{
|
|
329
|
+
name: z.ZodString;
|
|
330
|
+
version: z.ZodString;
|
|
331
|
+
cwd: z.ZodString;
|
|
332
|
+
appID: z.ZodNumber;
|
|
333
|
+
launchExecutable: z.ZodString;
|
|
334
|
+
launchArguments: z.ZodOptional<z.ZodString>;
|
|
335
|
+
capsuleImage: z.ZodString;
|
|
336
|
+
storefront: z.ZodString;
|
|
337
|
+
addonsource: z.ZodString;
|
|
338
|
+
coverImage: z.ZodString;
|
|
339
|
+
titleImage: z.ZodOptional<z.ZodString>;
|
|
340
|
+
}, "strip", z.ZodTypeAny, {
|
|
304
341
|
name: string;
|
|
305
342
|
version: string;
|
|
306
343
|
cwd: string;
|
|
307
344
|
appID: number;
|
|
308
345
|
launchExecutable: string;
|
|
309
|
-
launchArguments?: string;
|
|
310
346
|
capsuleImage: string;
|
|
311
347
|
storefront: string;
|
|
312
348
|
addonsource: string;
|
|
313
349
|
coverImage: string;
|
|
314
|
-
|
|
315
|
-
|
|
350
|
+
launchArguments?: string | undefined;
|
|
351
|
+
titleImage?: string | undefined;
|
|
352
|
+
}, {
|
|
353
|
+
name: string;
|
|
354
|
+
version: string;
|
|
355
|
+
cwd: string;
|
|
356
|
+
appID: number;
|
|
357
|
+
launchExecutable: string;
|
|
358
|
+
capsuleImage: string;
|
|
359
|
+
storefront: string;
|
|
360
|
+
addonsource: string;
|
|
361
|
+
coverImage: string;
|
|
362
|
+
launchArguments?: string | undefined;
|
|
363
|
+
titleImage?: string | undefined;
|
|
364
|
+
}>;
|
|
365
|
+
type LibraryInfo = z.infer<typeof ZodLibraryInfo>;
|
|
316
366
|
interface Notification {
|
|
317
367
|
type: 'warning' | 'error' | 'info' | 'success';
|
|
318
368
|
message: string;
|
|
@@ -332,4 +382,4 @@ declare class OGIAddonWSListener {
|
|
|
332
382
|
close(): void;
|
|
333
383
|
}
|
|
334
384
|
|
|
335
|
-
export { type BasicLibraryInfo, type ClientSentEventTypes, Configuration, ConfigurationBuilder, CustomTask, type EventListenerTypes, EventResponse, type LibraryInfo, type OGIAddonClientSentEvent, type OGIAddonConfiguration, type OGIAddonEvent, type OGIAddonServerSentEvent, SearchResult, SearchTool, type SetupEventResponse, type StoreData, VERSION, type WebsocketMessageClient, type WebsocketMessageServer, OGIAddon as default };
|
|
385
|
+
export { type BasicLibraryInfo, type ClientSentEventTypes, Configuration, ConfigurationBuilder, CustomTask, type EventListenerTypes, EventResponse, type LibraryInfo, type OGIAddonClientSentEvent, type OGIAddonConfiguration, type OGIAddonEvent, type OGIAddonServerSentEvent, SearchResult, SearchTool, type SetupEventResponse, type StoreData, VERSION, type WebsocketMessageClient, type WebsocketMessageServer, ZodLibraryInfo, OGIAddon as default };
|
package/build/main.js
CHANGED
|
@@ -379,7 +379,7 @@ var package_default = {
|
|
|
379
379
|
module: "./build/main.js",
|
|
380
380
|
type: "module",
|
|
381
381
|
main: "./build/main.cjs",
|
|
382
|
-
version: "1.
|
|
382
|
+
version: "1.9.2",
|
|
383
383
|
exports: {
|
|
384
384
|
".": {
|
|
385
385
|
import: {
|
|
@@ -408,6 +408,11 @@ var package_default = {
|
|
|
408
408
|
email: "me@nat3z.com",
|
|
409
409
|
url: "https://nat3z.com/"
|
|
410
410
|
},
|
|
411
|
+
repository: {
|
|
412
|
+
type: "git",
|
|
413
|
+
url: "https://github.com/Nat3z/OpenGameInstaller",
|
|
414
|
+
directory: "packages/ogi-addon"
|
|
415
|
+
},
|
|
411
416
|
dependencies: {
|
|
412
417
|
"fuse.js": "^7.1.0",
|
|
413
418
|
ws: "^8.4.0",
|
|
@@ -432,6 +437,7 @@ var package_default = {
|
|
|
432
437
|
// src/main.ts
|
|
433
438
|
import { exec, spawn } from "node:child_process";
|
|
434
439
|
import fs from "node:fs";
|
|
440
|
+
import { z as z2 } from "zod";
|
|
435
441
|
var defaultPort = 7654;
|
|
436
442
|
var VERSION = package_default.version;
|
|
437
443
|
var OGIAddon = class {
|
|
@@ -672,6 +678,19 @@ var SearchTool = class {
|
|
|
672
678
|
items.map((item) => this.fuse.add(item));
|
|
673
679
|
}
|
|
674
680
|
};
|
|
681
|
+
var ZodLibraryInfo = z2.object({
|
|
682
|
+
name: z2.string(),
|
|
683
|
+
version: z2.string(),
|
|
684
|
+
cwd: z2.string(),
|
|
685
|
+
appID: z2.number(),
|
|
686
|
+
launchExecutable: z2.string(),
|
|
687
|
+
launchArguments: z2.string().optional(),
|
|
688
|
+
capsuleImage: z2.string(),
|
|
689
|
+
storefront: z2.string(),
|
|
690
|
+
addonsource: z2.string(),
|
|
691
|
+
coverImage: z2.string(),
|
|
692
|
+
titleImage: z2.string().optional()
|
|
693
|
+
});
|
|
675
694
|
var OGIAddonWSListener = class {
|
|
676
695
|
socket;
|
|
677
696
|
eventEmitter;
|
|
@@ -864,6 +883,22 @@ var OGIAddonWSListener = class {
|
|
|
864
883
|
gameDetailsEvent
|
|
865
884
|
);
|
|
866
885
|
break;
|
|
886
|
+
case "check-for-updates":
|
|
887
|
+
let checkForUpdatesEvent = new EventResponse(
|
|
888
|
+
(screen, name, description) => this.userInputAsked(screen, name, description, this.socket)
|
|
889
|
+
);
|
|
890
|
+
this.eventEmitter.emit(
|
|
891
|
+
"check-for-updates",
|
|
892
|
+
message.args,
|
|
893
|
+
checkForUpdatesEvent
|
|
894
|
+
);
|
|
895
|
+
const checkForUpdatesResult = await this.waitForEventToRespond(checkForUpdatesEvent);
|
|
896
|
+
this.respondToMessage(
|
|
897
|
+
message.id,
|
|
898
|
+
checkForUpdatesResult.data,
|
|
899
|
+
checkForUpdatesEvent
|
|
900
|
+
);
|
|
901
|
+
break;
|
|
867
902
|
case "request-dl":
|
|
868
903
|
let requestDLEvent = new EventResponse(
|
|
869
904
|
(screen, name, description) => this.userInputAsked(screen, name, description, this.socket)
|
|
@@ -1004,6 +1039,7 @@ export {
|
|
|
1004
1039
|
EventResponse,
|
|
1005
1040
|
SearchTool,
|
|
1006
1041
|
VERSION,
|
|
1042
|
+
ZodLibraryInfo,
|
|
1007
1043
|
OGIAddon as default
|
|
1008
1044
|
};
|
|
1009
1045
|
//# sourceMappingURL=main.js.map
|
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 {\n ConfigurationBuilder,\n ConfigurationFile,\n} 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 =\n | 'connect'\n | 'disconnect'\n | 'configure'\n | 'authenticate'\n | 'search'\n | 'setup'\n | 'library-search'\n | 'game-details'\n | 'exit'\n | 'task-run'\n | 'request-dl'\n | 'catalog';\n\nexport type OGIAddonClientSentEvent =\n | 'response'\n | 'authenticate'\n | 'configure'\n | 'defer-update'\n | 'notification'\n | 'input-asked'\n | 'get-app-details'\n | 'search-app-name'\n | 'flag'\n | 'task-update';\n\nexport type OGIAddonServerSentEvent =\n | 'authenticate'\n | 'configure'\n | 'config-update'\n | 'search'\n | 'setup'\n | 'response'\n | 'library-search'\n | 'task-run'\n | 'game-details'\n | 'request-dl'\n | 'catalog';\nexport { ConfigurationBuilder, Configuration, EventResponse, SearchResult };\nconst defaultPort = 7654;\nimport pjson from '../package.json';\nimport { exec, spawn } from 'node:child_process';\nimport fs from 'node:fs';\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 'task-update': {\n id: string;\n progress: number;\n logs: string[];\n finished: boolean;\n failed: string | undefined;\n };\n 'get-app-details': {\n appID: number;\n storefront: string;\n };\n 'search-app-name': {\n query: string;\n storefront: string;\n };\n flag: {\n flag: string;\n value: string | string[];\n };\n}\n\nexport type BasicLibraryInfo = {\n name: string;\n capsuleImage: string;\n appID: number;\n storefront: string;\n};\n\nexport type SetupEventResponse = Omit<\n LibraryInfo,\n | 'capsuleImage'\n | 'coverImage'\n | 'name'\n | 'appID'\n | 'storefront'\n | 'addonsource'\n | 'titleImage'\n> & {\n redistributables?: {\n name: string;\n path: string;\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 event\n * @returns\n */\n connect: (event: EventResponse<void>) => 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: (\n query: { storefront: string; appID: number; for: 'game' | 'task' | 'all' },\n event: EventResponse<SearchResult[]>\n ) => 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' | 'empty';\n name: string;\n usedRealDebrid: boolean;\n multiPartFiles?: {\n name: string;\n downloadURL: string;\n }[];\n appID: number;\n storefront: string;\n manifest?: Record<string, unknown>;\n },\n event: EventResponse<SetupEventResponse>\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': (\n query: string,\n event: EventResponse<BasicLibraryInfo[]>\n ) => void;\n\n /**\n * This event is emitted when the client requests for a task to be run. Addon should resolve the event with the task.\n * @param task\n * @param event\n * @returns\n */\n 'task-run': (\n task: {\n manifest: Record<string, unknown>;\n downloadPath: string;\n name: string;\n },\n event: EventResponse<void>\n ) => void;\n\n /**\n * This event is emitted when the client requests for a game details to be fetched. Addon should resolve the event with the game details. This is used to generate a store page for the game.\n * @param appID\n * @param event\n * @returns\n */\n 'game-details': (\n details: { appID: number; storefront: string },\n event: EventResponse<StoreData | undefined>\n ) => void;\n\n /**\n * This event is emitted when the client requests for the addon to exit. Use this to perform any cleanup tasks, ending with a `process.exit(0)`.\n * @returns\n */\n exit: () => void;\n\n /**\n * This event is emitted when the client requests for a download to be performed with the 'request' type. Addon should resolve the event with a SearchResult containing the actual download info.\n * @param appID\n * @param info\n * @param event\n * @returns\n */\n 'request-dl': (\n appID: number,\n info: SearchResult,\n event: EventResponse<SearchResult>\n ) => void;\n\n /**\n * This event is emitted when the client requests for a catalog to be fetched. Addon should resolve the event with the catalog.\n * @param event\n * @returns\n */\n catalog: (\n event: Omit<\n EventResponse<{\n [key: string]: {\n name: string;\n description: string;\n listings: BasicLibraryInfo[];\n };\n }>,\n 'askForInput'\n >\n ) => 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 statusError?: string;\n}\nexport interface WebsocketMessageServer {\n event: OGIAddonServerSentEvent;\n id?: string;\n args: any;\n statusError?: string;\n}\n\n/**\n * The configuration for the addon. This is used to identify the addon and provide information about it.\n * Storefronts is an array of names of stores that the addon supports.\n */\nexport interface OGIAddonConfiguration {\n name: string;\n id: string;\n description: string;\n version: string;\n\n author: string;\n repository: string;\n storefronts: 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 private eventsAvailable: OGIAddonEvent[] = [];\n private registeredConnectEvent: boolean = false;\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>(\n event: T,\n listener: EventListenerTypes[T]\n ) {\n this.eventEmitter.on(event, listener);\n this.eventsAvailable.push(event);\n // wait for the addon to be connected\n if (!this.registeredConnectEvent) {\n this.addonWSListener.eventEmitter.once('connect', () => {\n this.addonWSListener.send('flag', {\n flag: 'events-available',\n value: this.eventsAvailable,\n });\n });\n this.registeredConnectEvent = true;\n }\n }\n\n public emit<T extends OGIAddonEvent>(\n event: T,\n ...args: Parameters<EventListenerTypes[T]>\n ) {\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 * Get the app details for a given appID and storefront.\n * @param appID {number}\n * @param storefront {string}\n * @returns {Promise<StoreData>}\n */\n public async getAppDetails(appID: number, storefront: string) {\n const id = this.addonWSListener.send('get-app-details', {\n appID,\n storefront,\n });\n return await this.addonWSListener.waitForResponseFromServer<\n StoreData | undefined\n >(id);\n }\n\n public async searchGame(query: string, storefront: string) {\n const id = this.addonWSListener.send('search-app-name', {\n query,\n storefront,\n });\n return await this.addonWSListener.waitForResponseFromServer<\n BasicLibraryInfo[]\n >(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', {\n id,\n progress,\n logs,\n finished: false,\n failed: undefined,\n });\n return task;\n }\n\n /**\n * Extract a file using 7-Zip on Windows, unzip on Linux/Mac.\n * @param path {string}\n * @param outputPath {string}\n * @param type {'unrar' | 'unzip'}\n * @returns {Promise<void>}\n */\n public async extractFile(\n path: string,\n outputPath: string,\n type: 'unrar' | 'unzip'\n ) {\n return new Promise<void>((resolve, reject) => {\n // Ensure outputPath exists\n if (!fs.existsSync(outputPath)) {\n fs.mkdirSync(outputPath, { recursive: true });\n }\n\n if (type === 'unzip') {\n // Prefer 7-Zip on Windows, unzip on Linux/Mac\n if (process.platform === 'win32') {\n // 7-Zip path (default install location)\n const s7ZipPath = '\"C:\\\\Program Files\\\\7-Zip\\\\7z.exe\"';\n exec(\n `${s7ZipPath} x \"${path}\" -o\"${outputPath}\"`,\n (err: any, stdout: any, stderr: any) => {\n if (err) {\n console.error(err);\n console.log(stderr);\n reject(new Error('Failed to extract ZIP file'));\n return;\n }\n console.log(stdout);\n console.log(stderr);\n resolve();\n }\n );\n } else {\n // Use unzip on Linux/Mac\n const unzipProcess = spawn(\n 'unzip',\n [\n '-o', // overwrite files without prompting\n path,\n '-d', // specify output directory\n outputPath,\n ],\n {\n env: {\n ...process.env,\n UNZIP_DISABLE_ZIPBOMB_DETECTION: 'TRUE',\n },\n }\n );\n\n unzipProcess.stdout.on('data', (data: Buffer) => {\n console.log(`[unzip stdout]: ${data}`);\n });\n\n unzipProcess.stderr.on('data', (data: Buffer) => {\n console.error(`[unzip stderr]: ${data}`);\n });\n\n unzipProcess.on('close', (code: number) => {\n if (code !== 0) {\n console.error(`unzip process exited with code ${code}`);\n reject(new Error('Failed to extract ZIP file'));\n return;\n }\n resolve();\n });\n }\n } else if (type === 'unrar') {\n if (process.platform === 'win32') {\n // 7-Zip path (default install location)\n const s7ZipPath = '\"C:\\\\Program Files\\\\7-Zip\\\\7z.exe\"';\n exec(\n `${s7ZipPath} x \"${path}\" -o\"${outputPath}\"`,\n (err: any, stdout: any, stderr: any) => {\n if (err) {\n console.error(err);\n console.log(stderr);\n reject(new Error('Failed to extract RAR file'));\n return;\n }\n console.log(stdout);\n console.log(stderr);\n resolve();\n }\n );\n } else {\n // Use unrar on Linux/Mac\n const unrarProcess = spawn('unrar', ['x', '-y', path, outputPath]);\n\n unrarProcess.stdout.on('data', (data: Buffer) => {\n console.log(`[unrar stdout]: ${data}`);\n });\n\n unrarProcess.stderr.on('data', (data: Buffer) => {\n console.error(`[unrar stderr]: ${data}`);\n });\n\n unrarProcess.on('close', (code: number) => {\n if (code !== 0) {\n console.error(`unrar process exited with code ${code}`);\n reject(new Error('Failed to extract RAR file'));\n return;\n }\n resolve();\n });\n }\n } else {\n reject(new Error('Unknown extraction type'));\n }\n });\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(\n ws: OGIAddonWSListener,\n id: string,\n progress: number,\n logs: string[]\n ) {\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', {\n id: this.id,\n progress: this.progress,\n logs: this.logs,\n finished: this.finished,\n failed: this.failed,\n });\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(\n items: T[],\n keys: string[],\n options: Omit<IFuseOptions<T>, 'keys'> = {\n threshold: 0.3,\n includeScore: true,\n }\n ) {\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\n .search(query)\n .slice(0, limit)\n .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: string;\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 (\n process.argv[process.argv.length - 1].split('=')[0] !== '--addonSecret'\n ) {\n throw new Error(\n 'No secret provided. This usually happens because the addon was not started by the OGI Addon Server.'\n );\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 // 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 // wait for the config-update to be received then send connect\n const configListener = (event: ws.MessageEvent) => {\n if (event === undefined) return;\n // event can be a Buffer, string, ArrayBuffer, or Buffer[]\n let data: string;\n if (typeof event === 'string') {\n data = event;\n } else if (event instanceof Buffer) {\n data = event.toString();\n } else if (event && typeof (event as any).data === 'string') {\n data = (event as any).data;\n } else if (event && (event as any).data instanceof Buffer) {\n data = (event as any).data.toString();\n } else {\n // fallback for other types\n data = event.toString();\n }\n const message: WebsocketMessageServer = JSON.parse(data);\n if (message.event === 'config-update') {\n console.log('Config update received');\n this.socket.off('message', configListener);\n this.eventEmitter.emit(\n 'connect',\n new EventResponse<void>((screen, name, description) => {\n return this.userInputAsked(\n screen,\n name,\n description,\n this.socket\n );\n })\n );\n }\n };\n this.socket.on('message', configListener);\n });\n\n this.socket.on('error', (error) => {\n if (error.message.includes('Failed to connect')) {\n throw new Error(\n 'OGI Addon Server is not running/is unreachable. Please start the server and try again.'\n );\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(\n configBuilt: ConfigurationBuilder,\n name: string,\n description: string,\n socket: WebSocket\n ): 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(\n JSON.stringify({\n event: 'input-asked',\n args: {\n config,\n name,\n description,\n },\n id: id,\n })\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(\n message.id!!,\n {\n success: false,\n error: result[1],\n },\n undefined\n );\n } else {\n this.respondToMessage(message.id!!, { success: true }, undefined);\n }\n break;\n case 'search':\n let searchResultEvent = new EventResponse<SearchResult[]>(\n (screen, name, description) =>\n this.userInputAsked(screen, name, description, this.socket)\n );\n this.eventEmitter.emit('search', message.args, searchResultEvent);\n const searchResult =\n await this.waitForEventToRespond(searchResultEvent);\n this.respondToMessage(\n message.id!!,\n searchResult.data,\n searchResultEvent\n );\n break;\n case 'setup': {\n let setupEvent = new EventResponse<SetupEventResponse>(\n (screen, name, description) =>\n this.userInputAsked(screen, name, description, this.socket)\n );\n this.eventEmitter.emit('setup', message.args, 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, setupEvent);\n break;\n }\n case 'library-search':\n let librarySearchEvent = new EventResponse<BasicLibraryInfo[]>(\n (screen, name, description) =>\n this.userInputAsked(screen, name, description, this.socket)\n );\n this.eventEmitter.emit(\n 'library-search',\n message.args,\n librarySearchEvent\n );\n const librarySearchResult =\n await this.waitForEventToRespond(librarySearchEvent);\n this.respondToMessage(\n message.id!!,\n librarySearchResult.data,\n librarySearchEvent\n );\n break;\n case 'game-details':\n let gameDetailsEvent = new EventResponse<StoreData | undefined>(\n (screen, name, description) =>\n this.userInputAsked(screen, name, description, this.socket)\n );\n if (this.eventEmitter.listenerCount('game-details') === 0) {\n this.respondToMessage(\n message.id!!,\n {\n error: 'No event listener for game-details',\n },\n gameDetailsEvent\n );\n break;\n }\n this.eventEmitter.emit(\n 'game-details',\n message.args,\n gameDetailsEvent\n );\n const gameDetailsResult =\n await this.waitForEventToRespond(gameDetailsEvent);\n this.respondToMessage(\n message.id!!,\n gameDetailsResult.data,\n gameDetailsEvent\n );\n break;\n case 'request-dl':\n let requestDLEvent = new EventResponse<SearchResult>(\n (screen, name, description) =>\n this.userInputAsked(screen, name, description, this.socket)\n );\n if (this.eventEmitter.listenerCount('request-dl') === 0) {\n this.respondToMessage(\n message.id!!,\n {\n error: 'No event listener for request-dl',\n },\n requestDLEvent\n );\n break;\n }\n this.eventEmitter.emit(\n 'request-dl',\n message.args.appID,\n message.args.info,\n requestDLEvent\n );\n const requestDLResult =\n await this.waitForEventToRespond(requestDLEvent);\n if (requestDLEvent.failed) {\n this.respondToMessage(message.id!!, undefined, requestDLEvent);\n break;\n }\n if (\n requestDLEvent.data === undefined ||\n requestDLEvent.data?.downloadType === 'request'\n ) {\n throw new Error(\n 'Request DL event did not return a valid result. Please ensure that the event does not resolve with another `request` download type.'\n );\n }\n this.respondToMessage(\n message.id!!,\n requestDLResult.data,\n requestDLEvent\n );\n break;\n case 'catalog':\n let catalogEvent = new EventResponse<{\n [key: string]: {\n name: string;\n description: string;\n listings: BasicLibraryInfo[];\n };\n }>();\n this.eventEmitter.emit('catalog', catalogEvent);\n const catalogResult = await this.waitForEventToRespond(catalogEvent);\n this.respondToMessage(message.id!!, catalogResult.data, catalogEvent);\n break;\n case 'task-run': {\n let taskRunEvent = new EventResponse<void>(\n (screen, name, description) =>\n this.userInputAsked(screen, name, description, this.socket)\n );\n this.eventEmitter.emit('task-run', message.args, taskRunEvent);\n const interval = setInterval(() => {\n if (taskRunEvent.resolved) {\n clearInterval(interval);\n return;\n }\n this.send('defer-update', {\n logs: taskRunEvent.logs,\n deferID: message.args.deferID,\n progress: taskRunEvent.progress,\n failed: taskRunEvent.failed,\n } as ClientSentEventTypes['defer-update']);\n }, 100);\n const taskRunResult = await this.waitForEventToRespond(taskRunEvent);\n this.respondToMessage(message.id!!, taskRunResult.data, taskRunEvent);\n break;\n }\n }\n });\n }\n\n private waitForEventToRespond<T>(\n event: EventResponse<T>\n ): 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 } else {\n reject('Event did not respond in time');\n }\n }, 5000);\n });\n }\n\n public respondToMessage(\n messageID: string,\n response: any,\n originalEvent: EventResponse<any> | undefined\n ) {\n this.socket.send(\n JSON.stringify({\n event: 'response',\n id: messageID,\n args: response,\n statusError: originalEvent ? originalEvent.failed : undefined,\n })\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 } else {\n this.socket.once('message', waiter);\n }\n };\n this.socket.once('message', waiter);\n });\n }\n\n public send(\n event: OGIAddonClientSentEvent,\n args: ClientSentEventTypes[OGIAddonClientSentEvent]\n ): string {\n // generate a random id\n const id = Math.random().toString(36).substring(7);\n this.socket.send(\n JSON.stringify({\n event,\n args,\n id,\n })\n );\n return id;\n }\n\n public close() {\n this.socket.close();\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(\n option: ConfigurationOption\n): option is StringOption {\n return option.type === 'string';\n}\n\nexport function isNumberOption(\n option: ConfigurationOption\n): option is NumberOption {\n return option.type === 'number';\n}\n\nexport function isBooleanOption(\n option: ConfigurationOption\n): 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(\n option: (option: NumberOption) => NumberOption\n ): 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 } 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 (\n input.length < this.minTextLength ||\n input.length > this.maxTextLength\n ) {\n return [\n false,\n 'Input is not within the text length ' +\n this.minTextLength +\n ' and ' +\n this.maxTextLength +\n ' characters (currently ' +\n input.length +\n ' characters)',\n ];\n }\n\n return [\n this.allowedValues.includes(input),\n 'Input is not an allowed value',\n ];\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 [\n false,\n 'Input is not within the range of ' + this.min + ' and ' + this.max,\n ];\n }\n return [true, ''];\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 {\n ConfigurationFile,\n ConfigurationBuilder,\n BooleanOption,\n ConfigurationOption,\n ConfigurationOptionType,\n NumberOption,\n StringOption,\n isBooleanOption,\n isNumberOption,\n isStringOption,\n} 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(\n config: DefiniteConfig,\n validate: boolean = true\n ): [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 (\n this.definiteConfig[key] === null ||\n this.definiteConfig[key] === undefined\n ) {\n console.warn(\n 'Option ' +\n key +\n ' is not defined. Using default value Value: ' +\n this.storedConfigTemplate[key].defaultValue\n );\n this.definiteConfig[key] = this.storedConfigTemplate[key]\n .defaultValue as string | number | boolean;\n }\n if (\n this.storedConfigTemplate[key].type !== typeof this.definiteConfig[key]\n ) {\n throw new Error('Option ' + key + ' is not of the correct type');\n }\n\n const result = this.storedConfigTemplate[key].validate(\n this.definiteConfig[key]\n );\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] === undefined) {\n // remove the key from the definite config\n delete this.definiteConfig[key];\n console.warn(\n 'Option ' +\n key +\n ' is not defined in the configuration template. Removing from config.'\n );\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 {\n ConfigurationFile,\n ConfigurationBuilder,\n BooleanOption,\n ConfigurationOption,\n ConfigurationOptionType,\n NumberOption,\n StringOption,\n isBooleanOption,\n isNumberOption,\n isStringOption,\n};\n","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?: (\n screen: ConfigurationBuilder,\n name: string,\n description: string\n ) => Promise<{ [key: string]: boolean | string | number }>;\n\n constructor(\n onInputAsked?: (\n screen: ConfigurationBuilder,\n name: string,\n description: string\n ) => Promise<{ [key: string]: boolean | string | number }>\n ) {\n this.onInputAsked = onInputAsked;\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(\n name: string,\n description: string,\n screen: ConfigurationBuilder\n ): 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 \"name\": \"ogi-addon\",\n \"module\": \"./build/main.js\",\n \"type\": \"module\",\n \"main\": \"./build/main.cjs\",\n \"version\": \"1.8.1\",\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/minimatch\": \"^6.0.0\",\n \"@types/node\": \"^20.14.12\",\n \"@types/ws\": \"^8.4.0\",\n \"prettier\": \"^3.6.0\",\n \"tsup\": \"^8.2.3\",\n \"typescript\": \"^5.0.0\"\n }\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 {\n ConfigurationBuilder,\n ConfigurationFile,\n} 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 =\n | 'connect'\n | 'disconnect'\n | 'configure'\n | 'authenticate'\n | 'search'\n | 'setup'\n | 'library-search'\n | 'game-details'\n | 'exit'\n | 'check-for-updates'\n | 'task-run'\n | 'request-dl'\n | 'catalog';\n\nexport type OGIAddonClientSentEvent =\n | 'response'\n | 'authenticate'\n | 'configure'\n | 'defer-update'\n | 'notification'\n | 'input-asked'\n | 'get-app-details'\n | 'search-app-name'\n | 'flag'\n | 'task-update';\n\nexport type OGIAddonServerSentEvent =\n | 'authenticate'\n | 'configure'\n | 'config-update'\n | 'search'\n | 'setup'\n | 'response'\n | 'library-search'\n | 'check-for-updates'\n | 'task-run'\n | 'game-details'\n | 'request-dl'\n | 'catalog';\nexport { ConfigurationBuilder, Configuration, EventResponse, SearchResult };\nconst defaultPort = 7654;\nimport pjson from '../package.json';\nimport { exec, spawn } from 'node:child_process';\nimport fs from 'node:fs';\nimport { z } from 'zod';\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 'task-update': {\n id: string;\n progress: number;\n logs: string[];\n finished: boolean;\n failed: string | undefined;\n };\n 'get-app-details': {\n appID: number;\n storefront: string;\n };\n 'search-app-name': {\n query: string;\n storefront: string;\n };\n flag: {\n flag: string;\n value: string | string[];\n };\n}\n\nexport type BasicLibraryInfo = {\n name: string;\n capsuleImage: string;\n appID: number;\n storefront: string;\n};\n\nexport type SetupEventResponse = Omit<\n LibraryInfo,\n | 'capsuleImage'\n | 'coverImage'\n | 'name'\n | 'appID'\n | 'storefront'\n | 'addonsource'\n | 'titleImage'\n> & {\n redistributables?: {\n name: string;\n path: string;\n }[];\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 event\n * @returns\n */\n connect: (event: EventResponse<void>) => 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: (\n query:\n | {\n storefront: string;\n appID: number;\n for: 'game' | 'task' | 'all';\n }\n | {\n storefront: string;\n appID: number;\n for: 'update';\n libraryInfo: LibraryInfo;\n },\n event: EventResponse<SearchResult[]>\n ) => 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 for: 'game' | 'update';\n currentLibraryInfo: LibraryInfo | undefined;\n path: string;\n type: 'direct' | 'torrent' | 'magnet' | 'empty';\n name: string;\n usedRealDebrid: boolean;\n multiPartFiles?: {\n name: string;\n downloadURL: string;\n }[];\n appID: number;\n storefront: string;\n manifest?: Record<string, unknown>;\n },\n event: EventResponse<SetupEventResponse>\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': (\n query: string,\n event: EventResponse<BasicLibraryInfo[]>\n ) => void;\n\n /**\n * This event is emitted when the client requests for a task to be run. Addon should resolve the event with the task.\n * @param task\n * @param event\n * @returns\n */\n 'task-run': (\n task: {\n manifest: Record<string, unknown>;\n downloadPath: string;\n name: string;\n },\n event: EventResponse<void>\n ) => void;\n\n /**\n * This event is emitted when the client requests for a game details to be fetched. Addon should resolve the event with the game details. This is used to generate a store page for the game.\n * @param appID\n * @param event\n * @returns\n */\n 'game-details': (\n details: { appID: number; storefront: string },\n event: EventResponse<StoreData | undefined>\n ) => void;\n\n /**\n * This event is emitted when the client requests for the addon to exit. Use this to perform any cleanup tasks, ending with a `process.exit(0)`.\n * @returns\n */\n exit: () => void;\n\n /**\n * This event is emitted when the client requests for a download to be performed with the 'request' type. Addon should resolve the event with a SearchResult containing the actual download info.\n * @param appID\n * @param info\n * @param event\n * @returns\n */\n 'request-dl': (\n appID: number,\n info: SearchResult,\n event: EventResponse<SearchResult>\n ) => void;\n\n /**\n * This event is emitted when the client requests for a catalog to be fetched. Addon should resolve the event with the catalog.\n * @param event\n * @returns\n */\n catalog: (\n event: Omit<\n EventResponse<{\n [key: string]: {\n name: string;\n description: string;\n listings: BasicLibraryInfo[];\n };\n }>,\n 'askForInput'\n >\n ) => void;\n\n /**\n * This event is emitted when the client requests for an addon to check for updates. Addon should resolve the event with the update information.\n * @param data\n * @param event\n * @returns\n */\n 'check-for-updates': (\n data: { appID: number; storefront: string; currentVersion: string },\n event: EventResponse<\n | {\n available: true;\n version: string;\n }\n | {\n available: false;\n }\n >\n ) => 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 latestVersion: string;\n}\nexport interface WebsocketMessageClient {\n event: OGIAddonClientSentEvent;\n id?: string;\n args: any;\n statusError?: string;\n}\nexport interface WebsocketMessageServer {\n event: OGIAddonServerSentEvent;\n id?: string;\n args: any;\n statusError?: string;\n}\n\n/**\n * The configuration for the addon. This is used to identify the addon and provide information about it.\n * Storefronts is an array of names of stores that the addon supports.\n */\nexport interface OGIAddonConfiguration {\n name: string;\n id: string;\n description: string;\n version: string;\n\n author: string;\n repository: string;\n storefronts: 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 private eventsAvailable: OGIAddonEvent[] = [];\n private registeredConnectEvent: boolean = false;\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>(\n event: T,\n listener: EventListenerTypes[T]\n ) {\n this.eventEmitter.on(event, listener);\n this.eventsAvailable.push(event);\n // wait for the addon to be connected\n if (!this.registeredConnectEvent) {\n this.addonWSListener.eventEmitter.once('connect', () => {\n this.addonWSListener.send('flag', {\n flag: 'events-available',\n value: this.eventsAvailable,\n });\n });\n this.registeredConnectEvent = true;\n }\n }\n\n public emit<T extends OGIAddonEvent>(\n event: T,\n ...args: Parameters<EventListenerTypes[T]>\n ) {\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 * Get the app details for a given appID and storefront.\n * @param appID {number}\n * @param storefront {string}\n * @returns {Promise<StoreData>}\n */\n public async getAppDetails(appID: number, storefront: string) {\n const id = this.addonWSListener.send('get-app-details', {\n appID,\n storefront,\n });\n return await this.addonWSListener.waitForResponseFromServer<\n StoreData | undefined\n >(id);\n }\n\n public async searchGame(query: string, storefront: string) {\n const id = this.addonWSListener.send('search-app-name', {\n query,\n storefront,\n });\n return await this.addonWSListener.waitForResponseFromServer<\n BasicLibraryInfo[]\n >(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', {\n id,\n progress,\n logs,\n finished: false,\n failed: undefined,\n });\n return task;\n }\n\n /**\n * Extract a file using 7-Zip on Windows, unzip on Linux/Mac.\n * @param path {string}\n * @param outputPath {string}\n * @param type {'unrar' | 'unzip'}\n * @returns {Promise<void>}\n */\n public async extractFile(\n path: string,\n outputPath: string,\n type: 'unrar' | 'unzip'\n ) {\n return new Promise<void>((resolve, reject) => {\n // Ensure outputPath exists\n if (!fs.existsSync(outputPath)) {\n fs.mkdirSync(outputPath, { recursive: true });\n }\n\n if (type === 'unzip') {\n // Prefer 7-Zip on Windows, unzip on Linux/Mac\n if (process.platform === 'win32') {\n // 7-Zip path (default install location)\n const s7ZipPath = '\"C:\\\\Program Files\\\\7-Zip\\\\7z.exe\"';\n exec(\n `${s7ZipPath} x \"${path}\" -o\"${outputPath}\"`,\n (err: any, stdout: any, stderr: any) => {\n if (err) {\n console.error(err);\n console.log(stderr);\n reject(new Error('Failed to extract ZIP file'));\n return;\n }\n console.log(stdout);\n console.log(stderr);\n resolve();\n }\n );\n } else {\n // Use unzip on Linux/Mac\n const unzipProcess = spawn(\n 'unzip',\n [\n '-o', // overwrite files without prompting\n path,\n '-d', // specify output directory\n outputPath,\n ],\n {\n env: {\n ...process.env,\n UNZIP_DISABLE_ZIPBOMB_DETECTION: 'TRUE',\n },\n }\n );\n\n unzipProcess.stdout.on('data', (data: Buffer) => {\n console.log(`[unzip stdout]: ${data}`);\n });\n\n unzipProcess.stderr.on('data', (data: Buffer) => {\n console.error(`[unzip stderr]: ${data}`);\n });\n\n unzipProcess.on('close', (code: number) => {\n if (code !== 0) {\n console.error(`unzip process exited with code ${code}`);\n reject(new Error('Failed to extract ZIP file'));\n return;\n }\n resolve();\n });\n }\n } else if (type === 'unrar') {\n if (process.platform === 'win32') {\n // 7-Zip path (default install location)\n const s7ZipPath = '\"C:\\\\Program Files\\\\7-Zip\\\\7z.exe\"';\n exec(\n `${s7ZipPath} x \"${path}\" -o\"${outputPath}\"`,\n (err: any, stdout: any, stderr: any) => {\n if (err) {\n console.error(err);\n console.log(stderr);\n reject(new Error('Failed to extract RAR file'));\n return;\n }\n console.log(stdout);\n console.log(stderr);\n resolve();\n }\n );\n } else {\n // Use unrar on Linux/Mac\n const unrarProcess = spawn('unrar', ['x', '-y', path, outputPath]);\n\n unrarProcess.stdout.on('data', (data: Buffer) => {\n console.log(`[unrar stdout]: ${data}`);\n });\n\n unrarProcess.stderr.on('data', (data: Buffer) => {\n console.error(`[unrar stderr]: ${data}`);\n });\n\n unrarProcess.on('close', (code: number) => {\n if (code !== 0) {\n console.error(`unrar process exited with code ${code}`);\n reject(new Error('Failed to extract RAR file'));\n return;\n }\n resolve();\n });\n }\n } else {\n reject(new Error('Unknown extraction type'));\n }\n });\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(\n ws: OGIAddonWSListener,\n id: string,\n progress: number,\n logs: string[]\n ) {\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', {\n id: this.id,\n progress: this.progress,\n logs: this.logs,\n finished: this.finished,\n failed: this.failed,\n });\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(\n items: T[],\n keys: string[],\n options: Omit<IFuseOptions<T>, 'keys'> = {\n threshold: 0.3,\n includeScore: true,\n }\n ) {\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\n .search(query)\n .slice(0, limit)\n .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 const ZodLibraryInfo = z.object({\n name: z.string(),\n version: z.string(),\n cwd: z.string(),\n appID: z.number(),\n launchExecutable: z.string(),\n launchArguments: z.string().optional(),\n capsuleImage: z.string(),\n storefront: z.string(),\n addonsource: z.string(),\n coverImage: z.string(),\n titleImage: z.string().optional(),\n});\nexport type LibraryInfo = z.infer<typeof ZodLibraryInfo>;\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 (\n process.argv[process.argv.length - 1].split('=')[0] !== '--addonSecret'\n ) {\n throw new Error(\n 'No secret provided. This usually happens because the addon was not started by the OGI Addon Server.'\n );\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 // 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 // wait for the config-update to be received then send connect\n const configListener = (event: ws.MessageEvent) => {\n if (event === undefined) return;\n // event can be a Buffer, string, ArrayBuffer, or Buffer[]\n let data: string;\n if (typeof event === 'string') {\n data = event;\n } else if (event instanceof Buffer) {\n data = event.toString();\n } else if (event && typeof (event as any).data === 'string') {\n data = (event as any).data;\n } else if (event && (event as any).data instanceof Buffer) {\n data = (event as any).data.toString();\n } else {\n // fallback for other types\n data = event.toString();\n }\n const message: WebsocketMessageServer = JSON.parse(data);\n if (message.event === 'config-update') {\n console.log('Config update received');\n this.socket.off('message', configListener);\n this.eventEmitter.emit(\n 'connect',\n new EventResponse<void>((screen, name, description) => {\n return this.userInputAsked(\n screen,\n name,\n description,\n this.socket\n );\n })\n );\n }\n };\n this.socket.on('message', configListener);\n });\n\n this.socket.on('error', (error) => {\n if (error.message.includes('Failed to connect')) {\n throw new Error(\n 'OGI Addon Server is not running/is unreachable. Please start the server and try again.'\n );\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(\n configBuilt: ConfigurationBuilder,\n name: string,\n description: string,\n socket: WebSocket\n ): 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(\n JSON.stringify({\n event: 'input-asked',\n args: {\n config,\n name,\n description,\n },\n id: id,\n })\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(\n message.id!!,\n {\n success: false,\n error: result[1],\n },\n undefined\n );\n } else {\n this.respondToMessage(message.id!!, { success: true }, undefined);\n }\n break;\n case 'search':\n let searchResultEvent = new EventResponse<SearchResult[]>(\n (screen, name, description) =>\n this.userInputAsked(screen, name, description, this.socket)\n );\n this.eventEmitter.emit('search', message.args, searchResultEvent);\n const searchResult =\n await this.waitForEventToRespond(searchResultEvent);\n this.respondToMessage(\n message.id!!,\n searchResult.data,\n searchResultEvent\n );\n break;\n case 'setup': {\n let setupEvent = new EventResponse<SetupEventResponse>(\n (screen, name, description) =>\n this.userInputAsked(screen, name, description, this.socket)\n );\n this.eventEmitter.emit('setup', message.args, 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, setupEvent);\n break;\n }\n case 'library-search':\n let librarySearchEvent = new EventResponse<BasicLibraryInfo[]>(\n (screen, name, description) =>\n this.userInputAsked(screen, name, description, this.socket)\n );\n this.eventEmitter.emit(\n 'library-search',\n message.args,\n librarySearchEvent\n );\n const librarySearchResult =\n await this.waitForEventToRespond(librarySearchEvent);\n this.respondToMessage(\n message.id!!,\n librarySearchResult.data,\n librarySearchEvent\n );\n break;\n case 'game-details':\n let gameDetailsEvent = new EventResponse<StoreData | undefined>(\n (screen, name, description) =>\n this.userInputAsked(screen, name, description, this.socket)\n );\n if (this.eventEmitter.listenerCount('game-details') === 0) {\n this.respondToMessage(\n message.id!!,\n {\n error: 'No event listener for game-details',\n },\n gameDetailsEvent\n );\n break;\n }\n this.eventEmitter.emit(\n 'game-details',\n message.args,\n gameDetailsEvent\n );\n const gameDetailsResult =\n await this.waitForEventToRespond(gameDetailsEvent);\n this.respondToMessage(\n message.id!!,\n gameDetailsResult.data,\n gameDetailsEvent\n );\n break;\n case 'check-for-updates':\n let checkForUpdatesEvent = new EventResponse<\n | {\n available: true;\n version: string;\n }\n | {\n available: false;\n }\n >((screen, name, description) =>\n this.userInputAsked(screen, name, description, this.socket)\n );\n this.eventEmitter.emit(\n 'check-for-updates',\n message.args,\n checkForUpdatesEvent\n );\n const checkForUpdatesResult =\n await this.waitForEventToRespond(checkForUpdatesEvent);\n this.respondToMessage(\n message.id!!,\n checkForUpdatesResult.data,\n checkForUpdatesEvent\n );\n break;\n case 'request-dl':\n let requestDLEvent = new EventResponse<SearchResult>(\n (screen, name, description) =>\n this.userInputAsked(screen, name, description, this.socket)\n );\n if (this.eventEmitter.listenerCount('request-dl') === 0) {\n this.respondToMessage(\n message.id!!,\n {\n error: 'No event listener for request-dl',\n },\n requestDLEvent\n );\n break;\n }\n this.eventEmitter.emit(\n 'request-dl',\n message.args.appID,\n message.args.info,\n requestDLEvent\n );\n const requestDLResult =\n await this.waitForEventToRespond(requestDLEvent);\n if (requestDLEvent.failed) {\n this.respondToMessage(message.id!!, undefined, requestDLEvent);\n break;\n }\n if (\n requestDLEvent.data === undefined ||\n requestDLEvent.data?.downloadType === 'request'\n ) {\n throw new Error(\n 'Request DL event did not return a valid result. Please ensure that the event does not resolve with another `request` download type.'\n );\n }\n this.respondToMessage(\n message.id!!,\n requestDLResult.data,\n requestDLEvent\n );\n break;\n case 'catalog':\n let catalogEvent = new EventResponse<{\n [key: string]: {\n name: string;\n description: string;\n listings: BasicLibraryInfo[];\n };\n }>();\n this.eventEmitter.emit('catalog', catalogEvent);\n const catalogResult = await this.waitForEventToRespond(catalogEvent);\n this.respondToMessage(message.id!!, catalogResult.data, catalogEvent);\n break;\n case 'task-run': {\n let taskRunEvent = new EventResponse<void>(\n (screen, name, description) =>\n this.userInputAsked(screen, name, description, this.socket)\n );\n this.eventEmitter.emit('task-run', message.args, taskRunEvent);\n const interval = setInterval(() => {\n if (taskRunEvent.resolved) {\n clearInterval(interval);\n return;\n }\n this.send('defer-update', {\n logs: taskRunEvent.logs,\n deferID: message.args.deferID,\n progress: taskRunEvent.progress,\n failed: taskRunEvent.failed,\n } as ClientSentEventTypes['defer-update']);\n }, 100);\n const taskRunResult = await this.waitForEventToRespond(taskRunEvent);\n this.respondToMessage(message.id!!, taskRunResult.data, taskRunEvent);\n break;\n }\n }\n });\n }\n\n private waitForEventToRespond<T>(\n event: EventResponse<T>\n ): 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 } else {\n reject('Event did not respond in time');\n }\n }, 5000);\n });\n }\n\n public respondToMessage(\n messageID: string,\n response: any,\n originalEvent: EventResponse<any> | undefined\n ) {\n this.socket.send(\n JSON.stringify({\n event: 'response',\n id: messageID,\n args: response,\n statusError: originalEvent ? originalEvent.failed : undefined,\n })\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 } else {\n this.socket.once('message', waiter);\n }\n };\n this.socket.once('message', waiter);\n });\n }\n\n public send(\n event: OGIAddonClientSentEvent,\n args: ClientSentEventTypes[OGIAddonClientSentEvent]\n ): string {\n // generate a random id\n const id = Math.random().toString(36).substring(7);\n this.socket.send(\n JSON.stringify({\n event,\n args,\n id,\n })\n );\n return id;\n }\n\n public close() {\n this.socket.close();\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(\n option: ConfigurationOption\n): option is StringOption {\n return option.type === 'string';\n}\n\nexport function isNumberOption(\n option: ConfigurationOption\n): option is NumberOption {\n return option.type === 'number';\n}\n\nexport function isBooleanOption(\n option: ConfigurationOption\n): 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(\n option: (option: NumberOption) => NumberOption\n ): 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 } 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 (\n input.length < this.minTextLength ||\n input.length > this.maxTextLength\n ) {\n return [\n false,\n 'Input is not within the text length ' +\n this.minTextLength +\n ' and ' +\n this.maxTextLength +\n ' characters (currently ' +\n input.length +\n ' characters)',\n ];\n }\n\n return [\n this.allowedValues.includes(input),\n 'Input is not an allowed value',\n ];\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 [\n false,\n 'Input is not within the range of ' + this.min + ' and ' + this.max,\n ];\n }\n return [true, ''];\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 {\n ConfigurationFile,\n ConfigurationBuilder,\n BooleanOption,\n ConfigurationOption,\n ConfigurationOptionType,\n NumberOption,\n StringOption,\n isBooleanOption,\n isNumberOption,\n isStringOption,\n} 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(\n config: DefiniteConfig,\n validate: boolean = true\n ): [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 (\n this.definiteConfig[key] === null ||\n this.definiteConfig[key] === undefined\n ) {\n console.warn(\n 'Option ' +\n key +\n ' is not defined. Using default value Value: ' +\n this.storedConfigTemplate[key].defaultValue\n );\n this.definiteConfig[key] = this.storedConfigTemplate[key]\n .defaultValue as string | number | boolean;\n }\n if (\n this.storedConfigTemplate[key].type !== typeof this.definiteConfig[key]\n ) {\n throw new Error('Option ' + key + ' is not of the correct type');\n }\n\n const result = this.storedConfigTemplate[key].validate(\n this.definiteConfig[key]\n );\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] === undefined) {\n // remove the key from the definite config\n delete this.definiteConfig[key];\n console.warn(\n 'Option ' +\n key +\n ' is not defined in the configuration template. Removing from config.'\n );\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 {\n ConfigurationFile,\n ConfigurationBuilder,\n BooleanOption,\n ConfigurationOption,\n ConfigurationOptionType,\n NumberOption,\n StringOption,\n isBooleanOption,\n isNumberOption,\n isStringOption,\n};\n","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?: (\n screen: ConfigurationBuilder,\n name: string,\n description: string\n ) => Promise<{ [key: string]: boolean | string | number }>;\n\n constructor(\n onInputAsked?: (\n screen: ConfigurationBuilder,\n name: string,\n description: string\n ) => Promise<{ [key: string]: boolean | string | number }>\n ) {\n this.onInputAsked = onInputAsked;\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(\n name: string,\n description: string,\n screen: ConfigurationBuilder\n ): 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 \"name\": \"ogi-addon\",\n \"module\": \"./build/main.js\",\n \"type\": \"module\",\n \"main\": \"./build/main.cjs\",\n \"version\": \"1.9.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 \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/Nat3z/OpenGameInstaller\",\n \"directory\": \"packages/ogi-addon\"\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/minimatch\": \"^6.0.0\",\n \"@types/node\": \"^20.14.12\",\n \"@types/ws\": \"^8.4.0\",\n \"prettier\": \"^3.6.0\",\n \"tsup\": \"^8.2.3\",\n \"typescript\": \"^5.0.0\"\n }\n}\n"],"mappings":";AAAA,OAAO,QAAuB;AAC9B,OAAO,YAAY;;;ACDnB,OAAO,KAAK,gBAAgB;AAM5B,IAAM,mBAAmB,EAAE,OAAO;AAAA,EAChC,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACtB,aAAa,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAC7B,aAAa,EAAE,OAAO,EAAE,IAAI,CAAC;AAC/B,CAAC;AAoBM,IAAM,uBAAN,MAA2B;AAAA,EACxB,UAAiC,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOnC,gBACL,QACsB;AACtB,QAAI,YAAY,IAAI,aAAa;AACjC,gBAAY,OAAO,SAAS;AAC5B,SAAK,QAAQ,KAAK,SAAS;AAC3B,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMO,gBAAgB,QAAgD;AACrE,QAAI,YAAY,IAAI,aAAa;AACjC,gBAAY,OAAO,SAAS;AAC5B,SAAK,QAAQ,KAAK,SAAS;AAC3B,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMO,iBAAiB,QAAkD;AACxE,QAAI,YAAY,IAAI,cAAc;AAClC,gBAAY,OAAO,SAAS;AAC5B,SAAK,QAAQ,KAAK,SAAS;AAC3B,WAAO;AAAA,EACT;AAAA,EAEO,MAAM,kBAA8C;AACzD,QAAI,SAA4B,CAAC;AACjC,SAAK,QAAQ,QAAQ,CAAC,WAAW;AAE/B,UAAI,CAAC,kBAAkB;AACrB,iBAAS,KAAK,MAAM,KAAK,UAAU,MAAM,CAAC;AAC1C,cAAM,aAAa,iBAAiB,UAAU,MAAM;AACpD,YAAI,CAAC,WAAW,SAAS;AACvB,gBAAM,IAAI,SAAS,WAAW,MAAM,MAAM;AAAA,QAC5C;AAEA,eAAO,OAAO,IAAI,IAAI;AAAA,MACxB,OAAO;AACL,eAAO,OAAO,IAAI,IAAI;AAAA,MACxB;AAAA,IACF,CAAC;AACD,WAAO;AAAA,EACT;AACF;AAGO,IAAM,sBAAN,MAA0B;AAAA,EACxB,OAAe;AAAA,EACf,eAAwB;AAAA,EACxB,cAAsB;AAAA,EACtB,cAAsB;AAAA,EACtB,OAAgC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMvC,QAAQ,MAAc;AACpB,SAAK,OAAO;AACZ,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,eAAe,aAAqB;AAClC,SAAK,cAAc;AACnB,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,eAAe,aAAqB;AAClC,SAAK,cAAc;AACnB,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,SAAS,OAAmC;AAC1C,UAAM,IAAI,MAAM,6CAA6C,KAAK;AAAA,EACpE;AACF;AAEO,IAAM,eAAN,cAA2B,oBAAoB;AAAA,EAC7C,gBAA0B,CAAC;AAAA,EAC3B,gBAAwB;AAAA,EACxB,gBAAwB,OAAO;AAAA,EAC/B,eAAuB;AAAA,EACvB,YAAqD;AAAA,EACrD,OAAgC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMvC,iBAAiB,eAA+B;AAC9C,SAAK,gBAAgB;AACrB,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,gBAAgB,cAA4B;AAC1C,SAAK,eAAe;AACpB,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,iBAAiB,eAA6B;AAC5C,SAAK,gBAAgB;AACrB,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,iBAAiB,eAA6B;AAC5C,SAAK,gBAAgB;AACrB,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,aAAa,WAA0D;AACrE,SAAK,YAAY;AACjB,WAAO;AAAA,EACT;AAAA,EAES,SAAS,OAAmC;AACnD,QAAI,OAAO,UAAU,UAAU;AAC7B,aAAO,CAAC,OAAO,uBAAuB;AAAA,IACxC;AACA,QAAI,KAAK,cAAc,WAAW,KAAK,MAAM,WAAW;AACtD,aAAO,CAAC,MAAM,EAAE;AAClB,QACE,MAAM,SAAS,KAAK,iBACpB,MAAM,SAAS,KAAK,eACpB;AACA,aAAO;AAAA,QACL;AAAA,QACA,yCACE,KAAK,gBACL,UACA,KAAK,gBACL,4BACA,MAAM,SACN;AAAA,MACJ;AAAA,IACF;AAEA,WAAO;AAAA,MACL,KAAK,cAAc,SAAS,KAAK;AAAA,MACjC;AAAA,IACF;AAAA,EACF;AACF;AAEO,IAAM,eAAN,cAA2B,oBAAoB;AAAA,EAC7C,MAAc;AAAA,EACd,MAAc,OAAO;AAAA,EACrB,eAAuB;AAAA,EACvB,OAAgC;AAAA,EAChC,YAAgC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMvC,OAAO,KAAmB;AACxB,SAAK,MAAM;AACX,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,aAAa,MAAgC;AAC3C,SAAK,YAAY;AACjB,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,OAAO,KAAmB;AACxB,SAAK,MAAM;AACX,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,gBAAgB,cAA4B;AAC1C,SAAK,eAAe;AACpB,WAAO;AAAA,EACT;AAAA,EAES,SAAS,OAAmC;AACnD,QAAI,MAAM,OAAO,KAAK,CAAC,GAAG;AACxB,aAAO,CAAC,OAAO,uBAAuB;AAAA,IACxC;AACA,QAAI,OAAO,KAAK,IAAI,KAAK,OAAO,OAAO,KAAK,IAAI,KAAK,KAAK;AACxD,aAAO;AAAA,QACL;AAAA,QACA,sCAAsC,KAAK,MAAM,UAAU,KAAK;AAAA,MAClE;AAAA,IACF;AACA,WAAO,CAAC,MAAM,EAAE;AAAA,EAClB;AACF;AAEO,IAAM,gBAAN,cAA4B,oBAAoB;AAAA,EAC9C,OAAgC;AAAA,EAChC,eAAwB;AAAA;AAAA;AAAA;AAAA;AAAA,EAM/B,gBAAgB,cAA6B;AAC3C,SAAK,eAAe;AACpB,WAAO;AAAA,EACT;AAAA,EAES,SAAS,OAAmC;AACnD,QAAI,OAAO,UAAU,WAAW;AAC9B,aAAO,CAAC,OAAO,wBAAwB;AAAA,IACzC;AACA,WAAO,CAAC,MAAM,EAAE;AAAA,EAClB;AACF;;;ACrRO,IAAM,gBAAN,MAAoB;AAAA,EAChB;AAAA,EACT,iBAAiC,CAAC;AAAA,EAClC,YAAY,gBAAmC;AAC7C,SAAK,uBAAuB;AAAA,EAC9B;AAAA,EAEA,aACE,QACA,WAAoB,MACkB;AACtC,SAAK,iBAAiB;AACtB,QAAI,UAAU;AACZ,YAAM,SAAS,KAAK,eAAe;AACnC,aAAO;AAAA,IACT;AACA,WAAO,CAAC,MAAM,CAAC,CAAC;AAAA,EAClB;AAAA;AAAA,EAEQ,iBAAuD;AAC7D,UAAM,cAAc,oBAAI,IAAoB;AAC5C,eAAW,OAAO,KAAK,sBAAsB;AAC3C,UACE,KAAK,eAAe,GAAG,MAAM,QAC7B,KAAK,eAAe,GAAG,MAAM,QAC7B;AACA,gBAAQ;AAAA,UACN,YACE,MACA,iDACA,KAAK,qBAAqB,GAAG,EAAE;AAAA,QACnC;AACA,aAAK,eAAe,GAAG,IAAI,KAAK,qBAAqB,GAAG,EACrD;AAAA,MACL;AACA,UACE,KAAK,qBAAqB,GAAG,EAAE,SAAS,OAAO,KAAK,eAAe,GAAG,GACtE;AACA,cAAM,IAAI,MAAM,YAAY,MAAM,6BAA6B;AAAA,MACjE;AAEA,YAAM,SAAS,KAAK,qBAAqB,GAAG,EAAE;AAAA,QAC5C,KAAK,eAAe,GAAG;AAAA,MACzB;AACA,UAAI,CAAC,OAAO,CAAC,GAAG;AACd,oBAAY,IAAI,KAAK,OAAO,CAAC,CAAC;AAAA,MAChC;AAAA,IACF;AAEA,eAAW,OAAO,KAAK,gBAAgB;AACrC,UAAI,KAAK,qBAAqB,GAAG,MAAM,QAAW;AAEhD,eAAO,KAAK,eAAe,GAAG;AAC9B,gBAAQ;AAAA,UACN,YACE,MACA;AAAA,QACJ;AAAA,MACF;AAAA,IACF;AAEA,QAAI,YAAY,OAAO,GAAG;AACxB,aAAO,CAAC,OAAO,OAAO,YAAY,WAAW,CAAC;AAAA,IAChD;AAEA,WAAO,CAAC,MAAM,OAAO,YAAY,WAAW,CAAC;AAAA,EAC/C;AAAA,EAEA,eAAe,YAA4B;AACzC,QAAI,CAAC,KAAK,eAAe,UAAU,MAAM,MAAM;AAC7C,YAAM,IAAI,MAAM,YAAY,aAAa,iBAAiB;AAAA,IAC5D;AACA,QAAI,OAAO,KAAK,eAAe,UAAU,MAAM,UAAU;AACvD,YAAM,IAAI,MAAM,YAAY,aAAa,kBAAkB;AAAA,IAC7D;AACA,WAAO,KAAK,eAAe,UAAU;AAAA,EACvC;AAAA,EAEA,eAAe,YAA4B;AACzC,QAAI,CAAC,KAAK,eAAe,UAAU,MAAM,MAAM;AAC7C,YAAM,IAAI,MAAM,YAAY,aAAa,iBAAiB;AAAA,IAC5D;AACA,QAAI,OAAO,KAAK,eAAe,UAAU,MAAM,UAAU;AACvD,YAAM,IAAI,MAAM,YAAY,aAAa,kBAAkB;AAAA,IAC7D;AACA,WAAO,KAAK,eAAe,UAAU;AAAA,EACvC;AAAA,EAEA,gBAAgB,YAA6B;AAC3C,QAAI,KAAK,eAAe,UAAU,MAAM,MAAM;AAC5C,YAAM,IAAI,MAAM,YAAY,aAAa,iBAAiB;AAAA,IAC5D;AACA,QAAI,OAAO,KAAK,eAAe,UAAU,MAAM,WAAW;AACxD,YAAM,IAAI,MAAM,YAAY,aAAa,mBAAmB;AAAA,IAC9D;AACA,WAAO,KAAK,eAAe,UAAU;AAAA,EACvC;AACF;;;AC/GA,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,EAMA,YACE,cAKA;AACA,SAAK,eAAe;AAAA,EACtB;AAAA,EAEO,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,YACX,MACA,aACA,QACuD;AACvD,QAAI,CAAC,KAAK,cAAc;AACtB,YAAM,IAAI,MAAM,yBAAyB;AAAA,IAC3C;AACA,WAAO,MAAM,KAAK,aAAa,QAAQ,MAAM,WAAW;AAAA,EAC1D;AACF;;;AHvEA,OAAO,UAA4B;;;AITnC;AAAA,EACE,MAAQ;AAAA,EACR,QAAU;AAAA,EACV,MAAQ;AAAA,EACR,MAAQ;AAAA,EACR,SAAW;AAAA,EACX,SAAW;AAAA,IACT,KAAK;AAAA,MACH,QAAU;AAAA,QACR,SAAW;AAAA,QACX,OAAS;AAAA,MACX;AAAA,MACA,SAAW;AAAA,QACT,SAAW;AAAA,QACX,OAAS;AAAA,MACX;AAAA,IACF;AAAA,IACA,YAAY;AAAA,MACV,QAAU;AAAA,QACR,SAAW;AAAA,QACX,OAAS;AAAA,MACX;AAAA,MACA,SAAW;AAAA,QACT,SAAW;AAAA,QACX,OAAS;AAAA,MACX;AAAA,IACF;AAAA,EACF;AAAA,EACA,SAAW;AAAA,EACX,QAAU;AAAA,IACR,MAAQ;AAAA,IACR,OAAS;AAAA,IACT,KAAO;AAAA,EACT;AAAA,EACA,YAAc;AAAA,IACZ,MAAQ;AAAA,IACR,KAAO;AAAA,IACP,WAAa;AAAA,EACf;AAAA,EACA,cAAgB;AAAA,IACd,WAAW;AAAA,IACX,IAAM;AAAA,IACN,KAAO;AAAA,EACT;AAAA,EACA,SAAW;AAAA,IACT,cAAc;AAAA,IACd,OAAS;AAAA,IACT,SAAW;AAAA,IACX,gBAAgB;AAAA,EAClB;AAAA,EACA,iBAAmB;AAAA,IACjB,oBAAoB;AAAA,IACpB,eAAe;AAAA,IACf,aAAa;AAAA,IACb,UAAY;AAAA,IACZ,MAAQ;AAAA,IACR,YAAc;AAAA,EAChB;AACF;;;AJJA,SAAS,MAAM,aAAa;AAC5B,OAAO,QAAQ;AACf,SAAS,KAAAA,UAAS;AAJlB,IAAM,cAAc;AAKb,IAAM,UAAU,gBAAM;AAmS7B,IAAqB,WAArB,MAA8B;AAAA,EACrB,eAAe,IAAI,OAAO,aAAa;AAAA,EACvC;AAAA,EACA;AAAA,EACA,SAAwB,IAAI,cAAc,CAAC,CAAC;AAAA,EAC3C,kBAAmC,CAAC;AAAA,EACpC,yBAAkC;AAAA,EAE1C,YAAY,WAAkC;AAC5C,SAAK,YAAY;AACjB,SAAK,kBAAkB,IAAI,mBAAmB,MAAM,KAAK,YAAY;AAAA,EACvE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOO,GACL,OACA,UACA;AACA,SAAK,aAAa,GAAG,OAAO,QAAQ;AACpC,SAAK,gBAAgB,KAAK,KAAK;AAE/B,QAAI,CAAC,KAAK,wBAAwB;AAChC,WAAK,gBAAgB,aAAa,KAAK,WAAW,MAAM;AACtD,aAAK,gBAAgB,KAAK,QAAQ;AAAA,UAChC,MAAM;AAAA,UACN,OAAO,KAAK;AAAA,QACd,CAAC;AAAA,MACH,CAAC;AACD,WAAK,yBAAyB;AAAA,IAChC;AAAA,EACF;AAAA,EAEO,KACL,UACG,MACH;AACA,SAAK,aAAa,KAAK,OAAO,GAAG,IAAI;AAAA,EACvC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMO,OAAO,cAA4B;AACxC,SAAK,gBAAgB,KAAK,gBAAgB,CAAC,YAAY,CAAC;AAAA,EAC1D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAa,cAAc,OAAe,YAAoB;AAC5D,UAAM,KAAK,KAAK,gBAAgB,KAAK,mBAAmB;AAAA,MACtD;AAAA,MACA;AAAA,IACF,CAAC;AACD,WAAO,MAAM,KAAK,gBAAgB,0BAEhC,EAAE;AAAA,EACN;AAAA,EAEA,MAAa,WAAW,OAAe,YAAoB;AACzD,UAAM,KAAK,KAAK,gBAAgB,KAAK,mBAAmB;AAAA,MACtD;AAAA,MACA;AAAA,IACF,CAAC;AACD,WAAO,MAAM,KAAK,gBAAgB,0BAEhC,EAAE;AAAA,EACN;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAa,OAAO;AAClB,UAAM,KAAK,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,UAAU,CAAC;AACjD,UAAM,WAAW;AACjB,UAAM,OAAiB,CAAC;AACxB,UAAM,OAAO,IAAI,WAAW,KAAK,iBAAiB,IAAI,UAAU,IAAI;AACpE,SAAK,gBAAgB,KAAK,eAAe;AAAA,MACvC;AAAA,MACA;AAAA,MACA;AAAA,MACA,UAAU;AAAA,MACV,QAAQ;AAAA,IACV,CAAC;AACD,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAa,YACX,MACA,YACA,MACA;AACA,WAAO,IAAI,QAAc,CAAC,SAAS,WAAW;AAE5C,UAAI,CAAC,GAAG,WAAW,UAAU,GAAG;AAC9B,WAAG,UAAU,YAAY,EAAE,WAAW,KAAK,CAAC;AAAA,MAC9C;AAEA,UAAI,SAAS,SAAS;AAEpB,YAAI,QAAQ,aAAa,SAAS;AAEhC,gBAAM,YAAY;AAClB;AAAA,YACE,GAAG,SAAS,OAAO,IAAI,QAAQ,UAAU;AAAA,YACzC,CAAC,KAAU,QAAa,WAAgB;AACtC,kBAAI,KAAK;AACP,wBAAQ,MAAM,GAAG;AACjB,wBAAQ,IAAI,MAAM;AAClB,uBAAO,IAAI,MAAM,4BAA4B,CAAC;AAC9C;AAAA,cACF;AACA,sBAAQ,IAAI,MAAM;AAClB,sBAAQ,IAAI,MAAM;AAClB,sBAAQ;AAAA,YACV;AAAA,UACF;AAAA,QACF,OAAO;AAEL,gBAAM,eAAe;AAAA,YACnB;AAAA,YACA;AAAA,cACE;AAAA;AAAA,cACA;AAAA,cACA;AAAA;AAAA,cACA;AAAA,YACF;AAAA,YACA;AAAA,cACE,KAAK;AAAA,gBACH,GAAG,QAAQ;AAAA,gBACX,iCAAiC;AAAA,cACnC;AAAA,YACF;AAAA,UACF;AAEA,uBAAa,OAAO,GAAG,QAAQ,CAAC,SAAiB;AAC/C,oBAAQ,IAAI,mBAAmB,IAAI,EAAE;AAAA,UACvC,CAAC;AAED,uBAAa,OAAO,GAAG,QAAQ,CAAC,SAAiB;AAC/C,oBAAQ,MAAM,mBAAmB,IAAI,EAAE;AAAA,UACzC,CAAC;AAED,uBAAa,GAAG,SAAS,CAAC,SAAiB;AACzC,gBAAI,SAAS,GAAG;AACd,sBAAQ,MAAM,kCAAkC,IAAI,EAAE;AACtD,qBAAO,IAAI,MAAM,4BAA4B,CAAC;AAC9C;AAAA,YACF;AACA,oBAAQ;AAAA,UACV,CAAC;AAAA,QACH;AAAA,MACF,WAAW,SAAS,SAAS;AAC3B,YAAI,QAAQ,aAAa,SAAS;AAEhC,gBAAM,YAAY;AAClB;AAAA,YACE,GAAG,SAAS,OAAO,IAAI,QAAQ,UAAU;AAAA,YACzC,CAAC,KAAU,QAAa,WAAgB;AACtC,kBAAI,KAAK;AACP,wBAAQ,MAAM,GAAG;AACjB,wBAAQ,IAAI,MAAM;AAClB,uBAAO,IAAI,MAAM,4BAA4B,CAAC;AAC9C;AAAA,cACF;AACA,sBAAQ,IAAI,MAAM;AAClB,sBAAQ,IAAI,MAAM;AAClB,sBAAQ;AAAA,YACV;AAAA,UACF;AAAA,QACF,OAAO;AAEL,gBAAM,eAAe,MAAM,SAAS,CAAC,KAAK,MAAM,MAAM,UAAU,CAAC;AAEjE,uBAAa,OAAO,GAAG,QAAQ,CAAC,SAAiB;AAC/C,oBAAQ,IAAI,mBAAmB,IAAI,EAAE;AAAA,UACvC,CAAC;AAED,uBAAa,OAAO,GAAG,QAAQ,CAAC,SAAiB;AAC/C,oBAAQ,MAAM,mBAAmB,IAAI,EAAE;AAAA,UACzC,CAAC;AAED,uBAAa,GAAG,SAAS,CAAC,SAAiB;AACzC,gBAAI,SAAS,GAAG;AACd,sBAAQ,MAAM,kCAAkC,IAAI,EAAE;AACtD,qBAAO,IAAI,MAAM,4BAA4B,CAAC;AAC9C;AAAA,YACF;AACA,oBAAQ;AAAA,UACV,CAAC;AAAA,QACH;AAAA,MACF,OAAO;AACL,eAAO,IAAI,MAAM,yBAAyB,CAAC;AAAA,MAC7C;AAAA,IACF,CAAC;AAAA,EACH;AACF;AAEO,IAAM,aAAN,MAAiB;AAAA,EACN;AAAA,EACT;AAAA,EACA;AAAA,EACA,WAAoB;AAAA,EACpB;AAAA,EACA,SAA6B;AAAA,EACpC,YACEC,KACA,IACA,UACA,MACA;AACA,SAAK,KAAK;AACV,SAAK,WAAW;AAChB,SAAK,OAAO;AACZ,SAAK,KAAKA;AAAA,EACZ;AAAA,EACO,IAAI,KAAa;AACtB,SAAK,KAAK,KAAK,GAAG;AAClB,SAAK,OAAO;AAAA,EACd;AAAA,EACO,SAAS;AACd,SAAK,WAAW;AAChB,SAAK,OAAO;AAAA,EACd;AAAA,EACO,KAAK,SAAiB;AAC3B,SAAK,SAAS;AACd,SAAK,OAAO;AAAA,EACd;AAAA,EACO,YAAY,UAAkB;AACnC,SAAK,WAAW;AAChB,SAAK,OAAO;AAAA,EACd;AAAA,EACO,SAAS;AACd,SAAK,GAAG,KAAK,eAAe;AAAA,MAC1B,IAAI,KAAK;AAAA,MACT,UAAU,KAAK;AAAA,MACf,MAAM,KAAK;AAAA,MACX,UAAU,KAAK;AAAA,MACf,QAAQ,KAAK;AAAA,IACf,CAAC;AAAA,EACH;AACF;AASO,IAAM,aAAN,MAAoB;AAAA,EACjB;AAAA,EACR,YACE,OACA,MACA,UAAyC;AAAA,IACvC,WAAW;AAAA,IACX,cAAc;AAAA,EAChB,GACA;AACA,SAAK,OAAO,IAAI,KAAK,OAAO;AAAA,MAC1B;AAAA,MACA,GAAG;AAAA,IACL,CAAC;AAAA,EACH;AAAA,EACO,OAAO,OAAe,QAAgB,IAAS;AACpD,WAAO,KAAK,KACT,OAAO,KAAK,EACZ,MAAM,GAAG,KAAK,EACd,IAAI,CAAC,WAAW,OAAO,IAAI;AAAA,EAChC;AAAA,EACO,SAAS,OAAY;AAC1B,UAAM,IAAI,CAAC,SAAS,KAAK,KAAK,IAAI,IAAI,CAAC;AAAA,EACzC;AACF;AAIO,IAAM,iBAAiBC,GAAE,OAAO;AAAA,EACrC,MAAMA,GAAE,OAAO;AAAA,EACf,SAASA,GAAE,OAAO;AAAA,EAClB,KAAKA,GAAE,OAAO;AAAA,EACd,OAAOA,GAAE,OAAO;AAAA,EAChB,kBAAkBA,GAAE,OAAO;AAAA,EAC3B,iBAAiBA,GAAE,OAAO,EAAE,SAAS;AAAA,EACrC,cAAcA,GAAE,OAAO;AAAA,EACvB,YAAYA,GAAE,OAAO;AAAA,EACrB,aAAaA,GAAE,OAAO;AAAA,EACtB,YAAYA,GAAE,OAAO;AAAA,EACrB,YAAYA,GAAE,OAAO,EAAE,SAAS;AAClC,CAAC;AAOD,IAAM,qBAAN,MAAyB;AAAA,EACf;AAAA,EACD;AAAA,EACA;AAAA,EAEP,YAAY,UAAoB,cAAmC;AACjE,QACE,QAAQ,KAAK,QAAQ,KAAK,SAAS,CAAC,EAAE,MAAM,GAAG,EAAE,CAAC,MAAM,iBACxD;AACA,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AACA,SAAK,QAAQ;AACb,SAAK,eAAe;AACpB,SAAK,SAAS,IAAI,GAAG,oBAAoB,WAAW;AACpD,SAAK,OAAO,GAAG,QAAQ,MAAM;AAC3B,cAAQ,IAAI,+BAA+B;AAC3C,cAAQ,IAAI,6BAA6B,OAAO;AAGhD,WAAK,KAAK,gBAAgB;AAAA,QACxB,GAAG,KAAK,MAAM;AAAA,QACd,QAAQ,QAAQ,KAAK,QAAQ,KAAK,SAAS,CAAC,EAAE,MAAM,GAAG,EAAE,CAAC;AAAA,QAC1D,YAAY;AAAA,MACd,CAAC;AAGD,UAAI,gBAAgB,IAAI,qBAAqB;AAC7C,WAAK,aAAa,KAAK,aAAa,aAAa;AACjD,WAAK,KAAK,aAAa,cAAc,MAAM,KAAK,CAAC;AACjD,WAAK,MAAM,SAAS,IAAI,cAAc,cAAc,MAAM,IAAI,CAAC;AAG/D,YAAM,iBAAiB,CAAC,UAA2B;AACjD,YAAI,UAAU,OAAW;AAEzB,YAAI;AACJ,YAAI,OAAO,UAAU,UAAU;AAC7B,iBAAO;AAAA,QACT,WAAW,iBAAiB,QAAQ;AAClC,iBAAO,MAAM,SAAS;AAAA,QACxB,WAAW,SAAS,OAAQ,MAAc,SAAS,UAAU;AAC3D,iBAAQ,MAAc;AAAA,QACxB,WAAW,SAAU,MAAc,gBAAgB,QAAQ;AACzD,iBAAQ,MAAc,KAAK,SAAS;AAAA,QACtC,OAAO;AAEL,iBAAO,MAAM,SAAS;AAAA,QACxB;AACA,cAAM,UAAkC,KAAK,MAAM,IAAI;AACvD,YAAI,QAAQ,UAAU,iBAAiB;AACrC,kBAAQ,IAAI,wBAAwB;AACpC,eAAK,OAAO,IAAI,WAAW,cAAc;AACzC,eAAK,aAAa;AAAA,YAChB;AAAA,YACA,IAAI,cAAoB,CAAC,QAAQ,MAAM,gBAAgB;AACrD,qBAAO,KAAK;AAAA,gBACV;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA,KAAK;AAAA,cACP;AAAA,YACF,CAAC;AAAA,UACH;AAAA,QACF;AAAA,MACF;AACA,WAAK,OAAO,GAAG,WAAW,cAAc;AAAA,IAC1C,CAAC;AAED,SAAK,OAAO,GAAG,SAAS,CAAC,UAAU;AACjC,UAAI,MAAM,QAAQ,SAAS,mBAAmB,GAAG;AAC/C,cAAM,IAAI;AAAA,UACR;AAAA,QACF;AAAA,MACF;AACA,cAAQ,MAAM,sBAAsB,KAAK;AAAA,IAC3C,CAAC;AAED,SAAK,OAAO,GAAG,SAAS,CAAC,MAAM,WAAW;AACxC,UAAI,SAAS,MAAM;AACjB,gBAAQ,MAAM,0BAA0B,MAAM;AAC9C;AAAA,MACF;AACA,WAAK,aAAa,KAAK,cAAc,MAAM;AAC3C,cAAQ,IAAI,oCAAoC;AAChD,cAAQ,MAAM,OAAO,SAAS,CAAC;AAC/B,WAAK,aAAa,KAAK,MAAM;AAC7B,WAAK,OAAO,MAAM;AAAA,IACpB,CAAC;AAED,SAAK,wBAAwB;AAAA,EAC/B;AAAA,EAEA,MAAc,eACZ,aACA,MACA,aACA,QACuD;AACvD,UAAM,SAAS,YAAY,MAAM,KAAK;AACtC,UAAM,KAAK,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,UAAU,CAAC;AACjD,QAAI,CAAC,QAAQ;AACX,aAAO,CAAC;AAAA,IACV;AACA,WAAO;AAAA,MACL,KAAK,UAAU;AAAA,QACb,OAAO;AAAA,QACP,MAAM;AAAA,UACJ;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,QACA;AAAA,MACF,CAAC;AAAA,IACH;AACA,WAAO,MAAM,KAAK,0BAA0B,EAAE;AAAA,EAChD;AAAA,EAEQ,0BAA0B;AAChC,SAAK,OAAO,GAAG,WAAW,OAAO,SAAiB;AAChD,YAAM,UAAkC,KAAK,MAAM,IAAI;AACvD,cAAQ,QAAQ,OAAO;AAAA,QACrB,KAAK;AACH,gBAAM,SAAS,KAAK,MAAM,OAAO,aAAa,QAAQ,IAAI;AAC1D,cAAI,CAAC,OAAO,CAAC,GAAG;AACd,iBAAK;AAAA,cACH,QAAQ;AAAA,cACR;AAAA,gBACE,SAAS;AAAA,gBACT,OAAO,OAAO,CAAC;AAAA,cACjB;AAAA,cACA;AAAA,YACF;AAAA,UACF,OAAO;AACL,iBAAK,iBAAiB,QAAQ,IAAM,EAAE,SAAS,KAAK,GAAG,MAAS;AAAA,UAClE;AACA;AAAA,QACF,KAAK;AACH,cAAI,oBAAoB,IAAI;AAAA,YAC1B,CAAC,QAAQ,MAAM,gBACb,KAAK,eAAe,QAAQ,MAAM,aAAa,KAAK,MAAM;AAAA,UAC9D;AACA,eAAK,aAAa,KAAK,UAAU,QAAQ,MAAM,iBAAiB;AAChE,gBAAM,eACJ,MAAM,KAAK,sBAAsB,iBAAiB;AACpD,eAAK;AAAA,YACH,QAAQ;AAAA,YACR,aAAa;AAAA,YACb;AAAA,UACF;AACA;AAAA,QACF,KAAK,SAAS;AACZ,cAAI,aAAa,IAAI;AAAA,YACnB,CAAC,QAAQ,MAAM,gBACb,KAAK,eAAe,QAAQ,MAAM,aAAa,KAAK,MAAM;AAAA,UAC9D;AACA,eAAK,aAAa,KAAK,SAAS,QAAQ,MAAM,UAAU;AACxD,gBAAM,WAAW,YAAY,MAAM;AACjC,gBAAI,WAAW,UAAU;AACvB,4BAAc,QAAQ;AACtB;AAAA,YACF;AACA,iBAAK,KAAK,gBAAgB;AAAA,cACxB,MAAM,WAAW;AAAA,cACjB,SAAS,QAAQ,KAAK;AAAA,cACtB,UAAU,WAAW;AAAA,cACrB,QAAQ,WAAW;AAAA,YACrB,CAAyC;AAAA,UAC3C,GAAG,GAAG;AACN,gBAAM,cAAc,MAAM,KAAK,sBAAsB,UAAU;AAC/D,eAAK,iBAAiB,QAAQ,IAAM,YAAY,MAAM,UAAU;AAChE;AAAA,QACF;AAAA,QACA,KAAK;AACH,cAAI,qBAAqB,IAAI;AAAA,YAC3B,CAAC,QAAQ,MAAM,gBACb,KAAK,eAAe,QAAQ,MAAM,aAAa,KAAK,MAAM;AAAA,UAC9D;AACA,eAAK,aAAa;AAAA,YAChB;AAAA,YACA,QAAQ;AAAA,YACR;AAAA,UACF;AACA,gBAAM,sBACJ,MAAM,KAAK,sBAAsB,kBAAkB;AACrD,eAAK;AAAA,YACH,QAAQ;AAAA,YACR,oBAAoB;AAAA,YACpB;AAAA,UACF;AACA;AAAA,QACF,KAAK;AACH,cAAI,mBAAmB,IAAI;AAAA,YACzB,CAAC,QAAQ,MAAM,gBACb,KAAK,eAAe,QAAQ,MAAM,aAAa,KAAK,MAAM;AAAA,UAC9D;AACA,cAAI,KAAK,aAAa,cAAc,cAAc,MAAM,GAAG;AACzD,iBAAK;AAAA,cACH,QAAQ;AAAA,cACR;AAAA,gBACE,OAAO;AAAA,cACT;AAAA,cACA;AAAA,YACF;AACA;AAAA,UACF;AACA,eAAK,aAAa;AAAA,YAChB;AAAA,YACA,QAAQ;AAAA,YACR;AAAA,UACF;AACA,gBAAM,oBACJ,MAAM,KAAK,sBAAsB,gBAAgB;AACnD,eAAK;AAAA,YACH,QAAQ;AAAA,YACR,kBAAkB;AAAA,YAClB;AAAA,UACF;AACA;AAAA,QACF,KAAK;AACH,cAAI,uBAAuB,IAAI;AAAA,YAQ7B,CAAC,QAAQ,MAAM,gBACf,KAAK,eAAe,QAAQ,MAAM,aAAa,KAAK,MAAM;AAAA,UAC5D;AACA,eAAK,aAAa;AAAA,YAChB;AAAA,YACA,QAAQ;AAAA,YACR;AAAA,UACF;AACA,gBAAM,wBACJ,MAAM,KAAK,sBAAsB,oBAAoB;AACvD,eAAK;AAAA,YACH,QAAQ;AAAA,YACR,sBAAsB;AAAA,YACtB;AAAA,UACF;AACA;AAAA,QACF,KAAK;AACH,cAAI,iBAAiB,IAAI;AAAA,YACvB,CAAC,QAAQ,MAAM,gBACb,KAAK,eAAe,QAAQ,MAAM,aAAa,KAAK,MAAM;AAAA,UAC9D;AACA,cAAI,KAAK,aAAa,cAAc,YAAY,MAAM,GAAG;AACvD,iBAAK;AAAA,cACH,QAAQ;AAAA,cACR;AAAA,gBACE,OAAO;AAAA,cACT;AAAA,cACA;AAAA,YACF;AACA;AAAA,UACF;AACA,eAAK,aAAa;AAAA,YAChB;AAAA,YACA,QAAQ,KAAK;AAAA,YACb,QAAQ,KAAK;AAAA,YACb;AAAA,UACF;AACA,gBAAM,kBACJ,MAAM,KAAK,sBAAsB,cAAc;AACjD,cAAI,eAAe,QAAQ;AACzB,iBAAK,iBAAiB,QAAQ,IAAM,QAAW,cAAc;AAC7D;AAAA,UACF;AACA,cACE,eAAe,SAAS,UACxB,eAAe,MAAM,iBAAiB,WACtC;AACA,kBAAM,IAAI;AAAA,cACR;AAAA,YACF;AAAA,UACF;AACA,eAAK;AAAA,YACH,QAAQ;AAAA,YACR,gBAAgB;AAAA,YAChB;AAAA,UACF;AACA;AAAA,QACF,KAAK;AACH,cAAI,eAAe,IAAI,cAMpB;AACH,eAAK,aAAa,KAAK,WAAW,YAAY;AAC9C,gBAAM,gBAAgB,MAAM,KAAK,sBAAsB,YAAY;AACnE,eAAK,iBAAiB,QAAQ,IAAM,cAAc,MAAM,YAAY;AACpE;AAAA,QACF,KAAK,YAAY;AACf,cAAI,eAAe,IAAI;AAAA,YACrB,CAAC,QAAQ,MAAM,gBACb,KAAK,eAAe,QAAQ,MAAM,aAAa,KAAK,MAAM;AAAA,UAC9D;AACA,eAAK,aAAa,KAAK,YAAY,QAAQ,MAAM,YAAY;AAC7D,gBAAM,WAAW,YAAY,MAAM;AACjC,gBAAI,aAAa,UAAU;AACzB,4BAAc,QAAQ;AACtB;AAAA,YACF;AACA,iBAAK,KAAK,gBAAgB;AAAA,cACxB,MAAM,aAAa;AAAA,cACnB,SAAS,QAAQ,KAAK;AAAA,cACtB,UAAU,aAAa;AAAA,cACvB,QAAQ,aAAa;AAAA,YACvB,CAAyC;AAAA,UAC3C,GAAG,GAAG;AACN,gBAAM,gBAAgB,MAAM,KAAK,sBAAsB,YAAY;AACnE,eAAK,iBAAiB,QAAQ,IAAM,cAAc,MAAM,YAAY;AACpE;AAAA,QACF;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEQ,sBACN,OAC2B;AAE3B,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,YAAM,UAAU,YAAY,MAAM;AAChC,YAAI,MAAM,UAAU;AAClB,kBAAQ,KAAK;AACb,uBAAa,OAAO;AAAA,QACtB;AAAA,MACF,GAAG,CAAC;AAEJ,YAAM,UAAU,WAAW,MAAM;AAC/B,YAAI,MAAM,UAAU;AAClB,wBAAc,OAAO;AACrB,gBAAM,WAAW,YAAY,MAAM;AACjC,gBAAI,MAAM,UAAU;AAClB,4BAAc,QAAQ;AACtB,sBAAQ,KAAK;AAAA,YACf;AAAA,UACF,GAAG,GAAG;AAAA,QACR,OAAO;AACL,iBAAO,+BAA+B;AAAA,QACxC;AAAA,MACF,GAAG,GAAI;AAAA,IACT,CAAC;AAAA,EACH;AAAA,EAEO,iBACL,WACA,UACA,eACA;AACA,SAAK,OAAO;AAAA,MACV,KAAK,UAAU;AAAA,QACb,OAAO;AAAA,QACP,IAAI;AAAA,QACJ,MAAM;AAAA,QACN,aAAa,gBAAgB,cAAc,SAAS;AAAA,MACtD,CAAC;AAAA,IACH;AACA,YAAQ,IAAI,4BAA4B,SAAS;AAAA,EACnD;AAAA,EAEO,0BAA6B,WAA+B;AACjE,WAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,YAAM,SAAS,CAAC,SAAiB;AAC/B,cAAM,UAAkC,KAAK,MAAM,IAAI;AACvD,YAAI,QAAQ,UAAU,YAAY;AAChC,eAAK,OAAO,KAAK,WAAW,MAAM;AAClC;AAAA,QACF;AACA,gBAAQ,IAAI,4BAA4B,SAAS;AAEjD,YAAI,QAAQ,OAAO,WAAW;AAC5B,kBAAQ,QAAQ,IAAI;AAAA,QACtB,OAAO;AACL,eAAK,OAAO,KAAK,WAAW,MAAM;AAAA,QACpC;AAAA,MACF;AACA,WAAK,OAAO,KAAK,WAAW,MAAM;AAAA,IACpC,CAAC;AAAA,EACH;AAAA,EAEO,KACL,OACA,MACQ;AAER,UAAM,KAAK,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,UAAU,CAAC;AACjD,SAAK,OAAO;AAAA,MACV,KAAK,UAAU;AAAA,QACb;AAAA,QACA;AAAA,QACA;AAAA,MACF,CAAC;AAAA,IACH;AACA,WAAO;AAAA,EACT;AAAA,EAEO,QAAQ;AACb,SAAK,OAAO,MAAM;AAAA,EACpB;AACF;","names":["z","ws","z"]}
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"module": "./build/main.js",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./build/main.cjs",
|
|
6
|
-
"version": "1.
|
|
6
|
+
"version": "1.9.2",
|
|
7
7
|
"exports": {
|
|
8
8
|
".": {
|
|
9
9
|
"import": {
|
|
@@ -32,6 +32,11 @@
|
|
|
32
32
|
"email": "me@nat3z.com",
|
|
33
33
|
"url": "https://nat3z.com/"
|
|
34
34
|
},
|
|
35
|
+
"repository": {
|
|
36
|
+
"type": "git",
|
|
37
|
+
"url": "https://github.com/Nat3z/OpenGameInstaller",
|
|
38
|
+
"directory": "packages/ogi-addon"
|
|
39
|
+
},
|
|
35
40
|
"dependencies": {
|
|
36
41
|
"fuse.js": "^7.1.0",
|
|
37
42
|
"ws": "^8.4.0",
|
package/src/main.ts
CHANGED
|
@@ -19,6 +19,7 @@ export type OGIAddonEvent =
|
|
|
19
19
|
| 'library-search'
|
|
20
20
|
| 'game-details'
|
|
21
21
|
| 'exit'
|
|
22
|
+
| 'check-for-updates'
|
|
22
23
|
| 'task-run'
|
|
23
24
|
| 'request-dl'
|
|
24
25
|
| 'catalog';
|
|
@@ -43,6 +44,7 @@ export type OGIAddonServerSentEvent =
|
|
|
43
44
|
| 'setup'
|
|
44
45
|
| 'response'
|
|
45
46
|
| 'library-search'
|
|
47
|
+
| 'check-for-updates'
|
|
46
48
|
| 'task-run'
|
|
47
49
|
| 'game-details'
|
|
48
50
|
| 'request-dl'
|
|
@@ -52,6 +54,7 @@ const defaultPort = 7654;
|
|
|
52
54
|
import pjson from '../package.json';
|
|
53
55
|
import { exec, spawn } from 'node:child_process';
|
|
54
56
|
import fs from 'node:fs';
|
|
57
|
+
import { z } from 'zod';
|
|
55
58
|
export const VERSION = pjson.version;
|
|
56
59
|
|
|
57
60
|
export interface ClientSentEventTypes {
|
|
@@ -113,6 +116,7 @@ export type SetupEventResponse = Omit<
|
|
|
113
116
|
path: string;
|
|
114
117
|
}[];
|
|
115
118
|
};
|
|
119
|
+
|
|
116
120
|
export interface EventListenerTypes {
|
|
117
121
|
/**
|
|
118
122
|
* This event is emitted when the addon connects to the OGI Addon Server. Addon does not need to resolve anything.
|
|
@@ -153,7 +157,18 @@ export interface EventListenerTypes {
|
|
|
153
157
|
* @returns
|
|
154
158
|
*/
|
|
155
159
|
search: (
|
|
156
|
-
query:
|
|
160
|
+
query:
|
|
161
|
+
| {
|
|
162
|
+
storefront: string;
|
|
163
|
+
appID: number;
|
|
164
|
+
for: 'game' | 'task' | 'all';
|
|
165
|
+
}
|
|
166
|
+
| {
|
|
167
|
+
storefront: string;
|
|
168
|
+
appID: number;
|
|
169
|
+
for: 'update';
|
|
170
|
+
libraryInfo: LibraryInfo;
|
|
171
|
+
},
|
|
157
172
|
event: EventResponse<SearchResult[]>
|
|
158
173
|
) => void;
|
|
159
174
|
/**
|
|
@@ -164,6 +179,8 @@ export interface EventListenerTypes {
|
|
|
164
179
|
*/
|
|
165
180
|
setup: (
|
|
166
181
|
data: {
|
|
182
|
+
for: 'game' | 'update';
|
|
183
|
+
currentLibraryInfo: LibraryInfo | undefined;
|
|
167
184
|
path: string;
|
|
168
185
|
type: 'direct' | 'torrent' | 'magnet' | 'empty';
|
|
169
186
|
name: string;
|
|
@@ -252,6 +269,25 @@ export interface EventListenerTypes {
|
|
|
252
269
|
'askForInput'
|
|
253
270
|
>
|
|
254
271
|
) => void;
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* This event is emitted when the client requests for an addon to check for updates. Addon should resolve the event with the update information.
|
|
275
|
+
* @param data
|
|
276
|
+
* @param event
|
|
277
|
+
* @returns
|
|
278
|
+
*/
|
|
279
|
+
'check-for-updates': (
|
|
280
|
+
data: { appID: number; storefront: string; currentVersion: string },
|
|
281
|
+
event: EventResponse<
|
|
282
|
+
| {
|
|
283
|
+
available: true;
|
|
284
|
+
version: string;
|
|
285
|
+
}
|
|
286
|
+
| {
|
|
287
|
+
available: false;
|
|
288
|
+
}
|
|
289
|
+
>
|
|
290
|
+
) => void;
|
|
255
291
|
}
|
|
256
292
|
|
|
257
293
|
export interface StoreData {
|
|
@@ -265,6 +301,7 @@ export interface StoreData {
|
|
|
265
301
|
basicDescription: string;
|
|
266
302
|
description: string;
|
|
267
303
|
headerImage: string;
|
|
304
|
+
latestVersion: string;
|
|
268
305
|
}
|
|
269
306
|
export interface WebsocketMessageClient {
|
|
270
307
|
event: OGIAddonClientSentEvent;
|
|
@@ -605,19 +642,20 @@ export class SearchTool<T> {
|
|
|
605
642
|
/**
|
|
606
643
|
* Library Info is the metadata for a library entry after setting up a game.
|
|
607
644
|
*/
|
|
608
|
-
export
|
|
609
|
-
name: string
|
|
610
|
-
version: string
|
|
611
|
-
cwd: string
|
|
612
|
-
appID: number
|
|
613
|
-
launchExecutable: string
|
|
614
|
-
launchArguments
|
|
615
|
-
capsuleImage: string
|
|
616
|
-
storefront: string
|
|
617
|
-
addonsource: string
|
|
618
|
-
coverImage: string
|
|
619
|
-
titleImage
|
|
620
|
-
}
|
|
645
|
+
export const ZodLibraryInfo = z.object({
|
|
646
|
+
name: z.string(),
|
|
647
|
+
version: z.string(),
|
|
648
|
+
cwd: z.string(),
|
|
649
|
+
appID: z.number(),
|
|
650
|
+
launchExecutable: z.string(),
|
|
651
|
+
launchArguments: z.string().optional(),
|
|
652
|
+
capsuleImage: z.string(),
|
|
653
|
+
storefront: z.string(),
|
|
654
|
+
addonsource: z.string(),
|
|
655
|
+
coverImage: z.string(),
|
|
656
|
+
titleImage: z.string().optional(),
|
|
657
|
+
});
|
|
658
|
+
export type LibraryInfo = z.infer<typeof ZodLibraryInfo>;
|
|
621
659
|
interface Notification {
|
|
622
660
|
type: 'warning' | 'error' | 'info' | 'success';
|
|
623
661
|
message: string;
|
|
@@ -843,6 +881,31 @@ class OGIAddonWSListener {
|
|
|
843
881
|
gameDetailsEvent
|
|
844
882
|
);
|
|
845
883
|
break;
|
|
884
|
+
case 'check-for-updates':
|
|
885
|
+
let checkForUpdatesEvent = new EventResponse<
|
|
886
|
+
| {
|
|
887
|
+
available: true;
|
|
888
|
+
version: string;
|
|
889
|
+
}
|
|
890
|
+
| {
|
|
891
|
+
available: false;
|
|
892
|
+
}
|
|
893
|
+
>((screen, name, description) =>
|
|
894
|
+
this.userInputAsked(screen, name, description, this.socket)
|
|
895
|
+
);
|
|
896
|
+
this.eventEmitter.emit(
|
|
897
|
+
'check-for-updates',
|
|
898
|
+
message.args,
|
|
899
|
+
checkForUpdatesEvent
|
|
900
|
+
);
|
|
901
|
+
const checkForUpdatesResult =
|
|
902
|
+
await this.waitForEventToRespond(checkForUpdatesEvent);
|
|
903
|
+
this.respondToMessage(
|
|
904
|
+
message.id!!,
|
|
905
|
+
checkForUpdatesResult.data,
|
|
906
|
+
checkForUpdatesEvent
|
|
907
|
+
);
|
|
908
|
+
break;
|
|
846
909
|
case 'request-dl':
|
|
847
910
|
let requestDLEvent = new EventResponse<SearchResult>(
|
|
848
911
|
(screen, name, description) =>
|