diff --git a/package-lock.json b/package-lock.json index 5baa75f..f0b63a2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,16 +1,17 @@ { "name": "@squeebot/core", - "version": "3.3.5", + "version": "3.4.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@squeebot/core", - "version": "3.3.5", + "version": "3.4.1", "license": "MIT", "dependencies": { "dateformat": "^4.5.1", "fs-extra": "^10.0.0", + "reflect-metadata": "^0.1.13", "semver": "^7.3.5", "tar": "^6.1.11" }, @@ -1410,6 +1411,11 @@ } ] }, + "node_modules/reflect-metadata": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", + "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==" + }, "node_modules/regexpp": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", @@ -2719,6 +2725,11 @@ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true }, + "reflect-metadata": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", + "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==" + }, "regexpp": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", diff --git a/package.json b/package.json index d611c05..6f9e943 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@squeebot/core", - "version": "3.4.1", + "version": "3.5.0", "description": "Squeebot v3 core for the execution environment", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -30,6 +30,7 @@ "dependencies": { "dateformat": "^4.5.1", "fs-extra": "^10.0.0", + "reflect-metadata": "^0.1.13", "semver": "^7.3.5", "tar": "^6.1.11" } diff --git a/src/common/http.ts b/src/common/http.ts index 7b1c568..4e69f12 100644 --- a/src/common/http.ts +++ b/src/common/http.ts @@ -1,7 +1,9 @@ import http, { RequestOptions } from 'http'; import https from 'https'; import fs from 'fs-extra'; +import { createGunzip, createInflate } from 'zlib'; import { URL } from 'url'; +import { Readable } from 'stream'; /** * Create an HTTP GET request. @@ -87,11 +89,27 @@ export function httpGET( reject(new Error('Request took too long!')); }, 5000); - res.on('data', (chunk) => { + let output: Readable = res; + + // Decompress GZip + if (res.headers['content-encoding'] === 'gzip') { + const gzip = createGunzip(); + res.pipe(gzip); + output = gzip; + } + + // Deflate + if (res.headers['content-encoding'] === 'deflate') { + const inflate = createInflate(); + res.pipe(inflate); + output = inflate; + } + + output.on('data', (chunk) => { data += chunk; }); - res.on('end', () => { + output.on('end', () => { clearTimeout(reqTimeOut); resolve(data || saveTo); diff --git a/src/npm/executor.ts b/src/npm/executor.ts index 0db5ccc..644dd7c 100644 --- a/src/npm/executor.ts +++ b/src/npm/executor.ts @@ -65,10 +65,10 @@ export class NPMExecutor { const pkgRefSplit = pkg.split('@'); const pkgFullName = (spi === 1 ? '@' : '') + pkgRefSplit[spi]; - const pkgVersion = this.removeVersionWildcard(pkgRefSplit[spi + 1]); + const pkgVersion = this.removeVersionWildcard(pkgRefSplit[spi + 1]) || 'latest'; const installedVersion = this.installed[pkgFullName]; if (installedVersion && installedVersion !== 'latest' && pkgVersion !== 'latest') { - const cardless = this.removeVersionWildcard(installedVersion); + const cardless = this.removeVersionWildcard(installedVersion) as string; if (semver.lte(pkgVersion, cardless)) { return; } @@ -101,7 +101,7 @@ export class NPMExecutor { await this.loadPackageFile(); } - private removeVersionWildcard(str: string): string { - return str.replace(/\^|>|=/, ''); + private removeVersionWildcard(str?: string): string | undefined { + return str?.replace(/\^|>|=/, ''); } } diff --git a/src/plugin/decorators/auto.ts b/src/plugin/decorators/auto.ts index 2196aa9..43e570f 100644 --- a/src/plugin/decorators/auto.ts +++ b/src/plugin/decorators/auto.ts @@ -1,14 +1,19 @@ +import 'reflect-metadata'; /** * Automatically execute method on plugin initialization */ -export function Auto(): (...args: any[]) => void { +export function Auto() { return ( target: any, propertyKey: string, descriptor: PropertyDescriptor, ) => { - descriptor.value.prototype.__autoexec = 1; + let autorunners: Array = Reflect.getMetadata('sb:autorunners', target); + if (!autorunners) { + Reflect.defineMetadata('sb:autorunners', autorunners = [], target); + } + autorunners.push(propertyKey); return descriptor; }; } diff --git a/src/plugin/decorators/configurable.ts b/src/plugin/decorators/configurable.ts index 67a57c5..b0c2982 100644 --- a/src/plugin/decorators/configurable.ts +++ b/src/plugin/decorators/configurable.ts @@ -1,10 +1,11 @@ +import 'reflect-metadata'; /** * Declare this plugin as one that the user can configure. * @param defconf Default configuration */ -export function Configurable(defconf: any): (...arg: any[]) => any { - return (constructor: (...arg: any[]) => any): void => { - constructor.prototype.__defconf = defconf; +export function Configurable(defconf: any) { + return (constructor: any): void => { + Reflect.defineMetadata('sb:defconf', defconf, constructor); }; } diff --git a/src/plugin/decorators/dependency.ts b/src/plugin/decorators/dependency.ts index 16e3aac..0359820 100644 --- a/src/plugin/decorators/dependency.ts +++ b/src/plugin/decorators/dependency.ts @@ -1,9 +1,10 @@ +import { Auto } from './auto'; /** * Fire the following method automatically when a dependency is (re)loaded. - * @param dep Name of the dependency + * @param dependency Name of the dependency */ -export function DependencyLoad(dep: string): (...args: any[]) => void { +export function DependencyLoad(dependency: string) { return ( target: any, propertyKey: string, @@ -17,23 +18,23 @@ export function DependencyLoad(dep: string): (...args: any[]) => void { return; } const nameof = plugin.manifest.name; - if (nameof !== dep) { + if (nameof !== dependency) { return; } originalMethod.call(self, plugin); }); }; // Set the function to be autoexecuted when the plugin is initialized. - descriptor.value.prototype.__autoexec = 1; + Auto()(target, propertyKey, descriptor); return descriptor; }; } /** * Fire the following method automatically when a dependency is unloaded. - * @param dep Name of the dependency + * @param dependency Name of the dependency */ -export function DependencyUnload(dep: string): (...args: any[]) => void { +export function DependencyUnload(dependency: string) { return ( target: any, propertyKey: string, @@ -47,13 +48,14 @@ export function DependencyUnload(dep: string): (...args: any[]) => void { if (typeof plugin !== 'string') { nameof = plugin.manifest.name; } - if (nameof !== dep) { + if (nameof !== dependency) { return; } originalMethod.call(self, plugin); }); }; - descriptor.value.prototype.__autoexec = 1; + // Set the function to be autoexecuted when the plugin is initialized. + Auto()(target, propertyKey, descriptor); return descriptor; }; } diff --git a/src/plugin/decorators/eventlistener.ts b/src/plugin/decorators/eventlistener.ts index 9c65659..7ed1023 100644 --- a/src/plugin/decorators/eventlistener.ts +++ b/src/plugin/decorators/eventlistener.ts @@ -1,9 +1,10 @@ +import { Auto } from './auto'; /** * Listen for an event targeted towards this plugin. * @param event Event name */ -export function EventListener(event: string): (...args: any[]) => void { +export function EventListener(event: string) { return ( target: any, propertyKey: string, @@ -19,7 +20,7 @@ export function EventListener(event: string): (...args: any[]) => void { ); }; // Set the function to be autoexecuted when the plugin is initialized. - descriptor.value.prototype.__autoexec = 1; + Auto()(target, propertyKey, descriptor); return descriptor; }; } diff --git a/src/plugin/decorators/service.ts b/src/plugin/decorators/service.ts index f877546..07d39f4 100644 --- a/src/plugin/decorators/service.ts +++ b/src/plugin/decorators/service.ts @@ -1,10 +1,11 @@ +import 'reflect-metadata'; /** * Declare this plugin as one that utilises services. - * @param servtype Service class, usually a Protocol + * @param injectedService Service class, usually a Protocol */ -export function InjectService(servtype: any): (...arg: any[]) => any { - return (constructor: (...arg: any[]) => any): void => { - constructor.prototype.__service = servtype; +export function InjectService(injectedService: any) { + return (constructor: any): void => { + Reflect.defineMetadata('sb:service', injectedService, constructor); }; } diff --git a/src/plugin/manager.ts b/src/plugin/manager.ts index b786f5b..eb281c1 100644 --- a/src/plugin/manager.ts +++ b/src/plugin/manager.ts @@ -1,3 +1,4 @@ +import 'reflect-metadata'; import * as path from 'path'; import { IEnvironment, Service } from '../types'; import { IPlugin, IPluginManifest } from './plugin'; @@ -232,8 +233,8 @@ export class PluginManager { } // Find default configuration, if it's configured, load the configuration - if (PluginModule.prototype.__defconf && config) { - config.setDefaults(PluginModule.prototype.__defconf); + if (Reflect.hasOwnMetadata('sb:defconf', PluginModule) && config) { + config.setDefaults(Reflect.getOwnMetadata('sb:defconf', PluginModule)); await config.load(); } @@ -242,8 +243,8 @@ export class PluginManager { const loaded = new PluginModule(plugin, this.stream, config); try { // Give the plugin a service - if (PluginModule.prototype.__service) { - loaded.service = new Service(PluginModule.prototype.__service); + if (Reflect.hasOwnMetadata('sb:service', PluginModule)) { + loaded.service = new Service(Reflect.getOwnMetadata('sb:service', PluginModule)); } // Call the initializer @@ -252,17 +253,12 @@ export class PluginManager { } // Call methods that are supposed to be executed automatically on load. - // This is really nasty and probably shouldn't be done, but I don't care! - for (const name of Object.getOwnPropertyNames(PluginModule.prototype)) { - // Prevent double initialization - if (name === 'initialize') { - continue; - } - - if (PluginModule.prototype[name] && - PluginModule.prototype[name].prototype && - PluginModule.prototype[name].prototype.__autoexec) { - loaded[name].call(loaded); + const autorunFunctions: Array = Reflect.getOwnMetadata('sb:autorunners', PluginModule.prototype); + if (autorunFunctions?.length) { + for (const autoFunction of autorunFunctions) { + if (!loaded[autoFunction]) continue; + if (autoFunction === 'initialize') continue; + loaded[autoFunction].call(loaded); } } } catch (e: any) { diff --git a/src/util/index.ts b/src/util/index.ts index e6ababa..a68c67e 100644 --- a/src/util/index.ts +++ b/src/util/index.ts @@ -11,7 +11,7 @@ export * from './checksum'; */ export function requireNoCache(file: string): object | undefined { const fullPath = path.resolve(file); - // TODO: maybe use new "import" function? + // TODO: maybe use new "import" function? <- currently has no cache invalidation // eslint-disable-next-line @typescript-eslint/no-var-requires const mod = require(fullPath); if (require.cache && require.cache[fullPath]) {