nodecg-io-core
Advanced tools
Comparing version 0.2.0 to 0.3.0
/// <reference types="node" /> | ||
import { NodeCG } from "nodecg-types/types/server"; | ||
import NodeCG from "@nodecg/types"; | ||
import { ObjectMap, Service, ServiceDependency, ServiceInstance } from "./service"; | ||
@@ -13,3 +13,3 @@ import { Result } from "./utils/result"; | ||
private readonly bundles; | ||
constructor(nodecg: NodeCG); | ||
constructor(nodecg: NodeCG.ServerAPI); | ||
/** | ||
@@ -16,0 +16,0 @@ * Gets all bundle dependencies |
@@ -67,3 +67,3 @@ "use strict"; | ||
} | ||
// Get the service dependency that manages dependance for this service type | ||
// Get the service dependency that manages dependence for this service type | ||
const svcDependency = bundle.find((svcDep) => svcDep.serviceType === instance.serviceType); | ||
@@ -95,3 +95,3 @@ if (svcDependency === undefined) { | ||
const oldInstance = svcDependency.serviceInstance; | ||
// Unset service instance and let the bundle know that it hasn't access to this service anymore. | ||
// Unset service instance and let the bundle know that it hasn't access to this service any more. | ||
svcDependency.serviceInstance = undefined; | ||
@@ -98,0 +98,0 @@ svcDependency.provider.updateClient(undefined); |
@@ -1,5 +0,15 @@ | ||
import { NodeCG } from "nodecg-types/types/server"; | ||
import NodeCG from "@nodecg/types"; | ||
import { Service } from "./service"; | ||
import { ServiceProvider } from "./serviceProvider"; | ||
/** | ||
* Config schema for the core bundle. | ||
* This is also defined in the configschema.json file. | ||
*/ | ||
export interface NodeCGBundleConfig { | ||
automaticLogin?: { | ||
enabled?: boolean; | ||
password?: string; | ||
}; | ||
} | ||
/** | ||
* Main type of NodeCG extension that the core bundle exposes. | ||
@@ -10,4 +20,4 @@ * Contains references to all internal modules. | ||
registerService<R, C>(service: Service<R, C>): void; | ||
requireService<C>(nodecg: NodeCG, serviceType: string): ServiceProvider<C> | undefined; | ||
requireService<C>(nodecg: NodeCG.ServerAPI, serviceType: string): ServiceProvider<C> | undefined; | ||
} | ||
//# sourceMappingURL=index.d.ts.map |
@@ -17,3 +17,3 @@ "use strict"; | ||
registerExitHandlers(nodecg, bundleManager, instanceManager, serviceManager, persistenceManager); | ||
// We use a extra object instead of returning a object containing all the managers and so on, because | ||
// We use an extra object instead of returning an object containing all the managers and so on, because | ||
// any loaded bundle would be able to call any (public or private) of the managers which is not intended. | ||
@@ -39,3 +39,3 @@ return { | ||
// Save everything | ||
// This is especially important if some services update some configs (e.g. updated tokens) and they haven't been saved yet. | ||
// This is especially important if some services update some configs (e.g., updated tokens) and they haven't been saved yet. | ||
persistenceManager.save(); | ||
@@ -42,0 +42,0 @@ // Unset all service instances in all bundles |
/// <reference types="node" /> | ||
import { NodeCG } from "nodecg-types/types/server"; | ||
import NodeCG from "@nodecg/types"; | ||
import { ObjectMap, Service, ServiceInstance } from "./service"; | ||
@@ -17,3 +17,3 @@ import { Result } from "./utils/result"; | ||
private ajv; | ||
constructor(nodecg: NodeCG, services: ServiceManager, bundles: BundleManager); | ||
constructor(nodecg: NodeCG.ServerAPI, services: ServiceManager, bundles: BundleManager); | ||
/** | ||
@@ -32,3 +32,3 @@ * Finds and returns the service instance with the passed name if it exists. | ||
* Creates a service instance of the passed service. | ||
* @param serviceType the type of the service of which a instance should be created | ||
* @param serviceType the type of the service of which an instance should be created | ||
* @param instanceName how the instance should be named | ||
@@ -46,3 +46,3 @@ * @return void if everything went fine and a string describing the issue if not | ||
* Updates the config of a service instance. | ||
* Before actually setting the new config, it validates it against the json schema of the service and | ||
* Before actually setting the new config, it validates it against the JSON schema of the service and | ||
* the validate function of the service. | ||
@@ -49,0 +49,0 @@ * @param instanceName the name of the service instance of which the config should be set. |
@@ -6,3 +6,3 @@ "use strict"; | ||
const result_1 = require("./utils/result"); | ||
const ajv_1 = (0, tslib_1.__importDefault)(require("ajv")); | ||
const ajv_1 = tslib_1.__importDefault(require("ajv")); | ||
const events_1 = require("events"); | ||
@@ -40,3 +40,3 @@ const logger_1 = require("./utils/logger"); | ||
* Creates a service instance of the passed service. | ||
* @param serviceType the type of the service of which a instance should be created | ||
* @param serviceType the type of the service of which an instance should be created | ||
* @param instanceName how the instance should be named | ||
@@ -49,3 +49,3 @@ * @return void if everything went fine and a string describing the issue if not | ||
} | ||
// Check if a instance with the same name already exists. | ||
// Check if an instance with the same name already exists. | ||
if (this.serviceInstances[instanceName] !== undefined) { | ||
@@ -114,3 +114,3 @@ return (0, result_1.error)("A service instance with the same name already exists."); | ||
* Updates the config of a service instance. | ||
* Before actually setting the new config, it validates it against the json schema of the service and | ||
* Before actually setting the new config, it validates it against the JSON schema of the service and | ||
* the validate function of the service. | ||
@@ -180,3 +180,3 @@ * @param instanceName the name of the service instance of which the config should be set. | ||
if (inst.config === undefined && !service.requiresNoConfig) { | ||
// No config has been set, therefore the service isn't ready and we can't create a client. | ||
// No config has been set, therefore the service isn't ready, and we can't create a client. | ||
inst.client = undefined; | ||
@@ -187,6 +187,6 @@ } | ||
// Create a client using the new config | ||
// If the service requires a config we make the undefined check above which ensures that undefined doesn't | ||
// get passed to the createClient function of the service. | ||
// If the service requires a config, we make the undefined check above, which ensures that undefined is not | ||
// passed to the createClient function of the service. | ||
// If the service does not require a config we can safely ignore the undefined error because in that case | ||
// passing undefined is the intended behavior. | ||
// passing undefined is the intended behaviour. | ||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
@@ -212,3 +212,3 @@ const client = await service.createClient(inst.config, new logger_1.Logger(instanceName, this.nodecg)); | ||
this.bundles.handleInstanceUpdate(inst, instanceName); | ||
// Stop old client, as it isn't used by any bundle anymore. | ||
// Stop old client, as it isn't used by any bundle any more. | ||
if (oldClient !== undefined) { | ||
@@ -215,0 +215,0 @@ this.nodecg.log.info(`Stopping old unused ${inst.serviceType} client...`); |
@@ -1,2 +0,2 @@ | ||
import { NodeCG } from "nodecg-types/types/server"; | ||
import NodeCG from "@nodecg/types"; | ||
import { InstanceManager } from "./instanceManager"; | ||
@@ -6,17 +6,17 @@ import { BundleManager } from "./bundleManager"; | ||
import { ServiceManager } from "./serviceManager"; | ||
export interface PasswordMessage { | ||
password: string; | ||
export interface AuthenticationMessage { | ||
encryptionKey: string; | ||
} | ||
export interface UpdateInstanceConfigMessage extends PasswordMessage { | ||
export interface UpdateInstanceConfigMessage extends AuthenticationMessage { | ||
instanceName: string; | ||
config: unknown; | ||
} | ||
export interface CreateServiceInstanceMessage extends PasswordMessage { | ||
export interface CreateServiceInstanceMessage extends AuthenticationMessage { | ||
serviceType: string; | ||
instanceName: string; | ||
} | ||
export interface DeleteServiceInstanceMessage extends PasswordMessage { | ||
export interface DeleteServiceInstanceMessage extends AuthenticationMessage { | ||
instanceName: string; | ||
} | ||
export interface SetServiceDependencyMessage extends PasswordMessage { | ||
export interface SetServiceDependencyMessage extends AuthenticationMessage { | ||
bundleName: string; | ||
@@ -27,3 +27,3 @@ instanceName: string | undefined; | ||
/** | ||
* MessageManager manages communication with the gui and handles NodeCG messages to control the framework. | ||
* MessageManager manages communication with the GUI and handles NodeCG messages to control the framework. | ||
* Also adds a small wrapper around the actual functions them to make some things easier. | ||
@@ -37,3 +37,3 @@ */ | ||
private persist; | ||
constructor(nodecg: NodeCG, services: ServiceManager, instances: InstanceManager, bundles: BundleManager, persist: PersistenceManager); | ||
constructor(nodecg: NodeCG.ServerAPI, services: ServiceManager, instances: InstanceManager, bundles: BundleManager, persist: PersistenceManager); | ||
registerMessageHandlers(): void; | ||
@@ -40,0 +40,0 @@ private listen; |
@@ -6,3 +6,3 @@ "use strict"; | ||
/** | ||
* MessageManager manages communication with the gui and handles NodeCG messages to control the framework. | ||
* MessageManager manages communication with the GUI and handles NodeCG messages to control the framework. | ||
* Also adds a small wrapper around the actual functions them to make some things easier. | ||
@@ -58,3 +58,3 @@ */ | ||
this.listen("load", async (msg) => { | ||
return this.persist.load(msg.password); | ||
return this.persist.load(msg.encryptionKey); | ||
}); | ||
@@ -88,3 +88,3 @@ this.listen("getServices", async () => { | ||
this.listen(messageName, async (msg) => { | ||
if (this.persist.checkPassword(msg.password)) { | ||
if (this.persist.checkEncryptionKey(msg.encryptionKey)) { | ||
return cb(msg); | ||
@@ -91,0 +91,0 @@ } |
@@ -1,7 +0,9 @@ | ||
import { NodeCG } from "nodecg-types/types/server"; | ||
import NodeCG from "@nodecg/types"; | ||
import { InstanceManager } from "./instanceManager"; | ||
import { BundleManager } from "./bundleManager"; | ||
import crypto from "crypto-js"; | ||
import { Result } from "./utils/result"; | ||
import { ObjectMap, ServiceDependency, ServiceInstance } from "./service"; | ||
import { ServiceManager } from "./serviceManager"; | ||
import { NodeCGBundleConfig } from "."; | ||
/** | ||
@@ -21,3 +23,17 @@ * Models all the data that needs to be persistent in a plain manner. | ||
/** | ||
* Models all the data that needs to be persistent in a encrypted manner. | ||
* Models all the data that needs to be persistent in an encrypted manner. | ||
* | ||
* For nodecg-io <= 0.2 configurations only the ciphertext value may be set | ||
* containing the encrypted data, iv and salt in the crypto.js format. | ||
* Salt and iv are managed by crypto.js and all AES defaults with a password are used (PBKDF1 using 1 MD5 iteration). | ||
* All this happens in the nodecg-io-core extension and the password is sent using NodeCG Messages. | ||
* | ||
* For nodecg-io >= 0.3 this was changed. A encryption key is derived using argon2id directly inside the browser when logging in. | ||
* Only the derived AES encryption key is sent to the extension using NodeCG messages. | ||
* That way analyzed network traffic and malicious bundles that listen for the same NodeCG message only allow getting | ||
* the encryption key and not the plain text password that may be used somewhere else. | ||
* | ||
* Still with this security upgrade you should only use trusted bundles with your NodeCG installation | ||
* and use https if you're using the dashboard over a untrusted network. | ||
* | ||
*/ | ||
@@ -29,12 +45,64 @@ export interface EncryptedData { | ||
cipherText?: string; | ||
/** | ||
* The salt that is used when deriving the encryption key from the password. | ||
* Only set for new format with nodecg-io >=0.3. | ||
*/ | ||
salt?: string; | ||
/** | ||
* The initialization vector used for encryption. | ||
* Only set for new format with nodecg-io >=0.3. | ||
*/ | ||
iv?: string; | ||
} | ||
/** | ||
* Decrypts the passed encrypted data using the passed password. | ||
* If the password is wrong an error will be returned. | ||
* Decrypts the passed encrypted data using the passed encryption key. | ||
* If the encryption key is wrong, an error will be returned. | ||
* | ||
* This function supports the <=0.2 format with the plain password as an | ||
* encryption key and no iv (read from ciphertext) and the >=0.3 format with the iv and derived key. | ||
* | ||
* @param cipherText the ciphertext that needs to be decrypted. | ||
* @param password the password for the encrypted data. | ||
* @param encryptionKey the encryption key for the encrypted data. | ||
* @param iv the initialization vector for the encrypted data. | ||
*/ | ||
export declare function decryptData(cipherText: string, password: string): Result<PersistentData>; | ||
export declare function decryptData(cipherText: string, encryptionKey: string | crypto.lib.WordArray, iv: string | undefined): Result<PersistentData>; | ||
/** | ||
* Encrypts the passed data object using the passed encryption key. | ||
* | ||
* @param data the data that needs to be encrypted. | ||
* @param encryptionKey the encryption key that should be used to encrypt the data. | ||
* @returns a tuple containing the encrypted data and the initialization vector as a hex string. | ||
*/ | ||
export declare function encryptData(data: PersistentData, encryptionKey: crypto.lib.WordArray): [string, string]; | ||
/** | ||
* Derives a key suitable for encrypting the config from the given password. | ||
* | ||
* @param password the password from which the encryption key will be derived. | ||
* @param salt the hex encoded salt that is used for key derivation. | ||
* @returns a hex encoded string of the derived key. | ||
*/ | ||
export declare function deriveEncryptionKey(password: string, salt: string): Promise<string>; | ||
/** | ||
* Re-encrypts the passed data to change the password/encryption key. | ||
* Currently only used to migrate from <=0.2 to >=0.3 config formats but | ||
* could be used to implement a change password feature in the future. | ||
* @param data the data that should be re-encrypted. | ||
* @param oldSecret the previous encryption key or password. | ||
* @param newSecret the new encryption key. | ||
*/ | ||
export declare function reEncryptData(data: EncryptedData, oldSecret: string | crypto.lib.WordArray, newSecret: crypto.lib.WordArray): Result<void>; | ||
/** | ||
* Ensures that the passed encrypted data has the salt attribute set. | ||
* The salt attribute is not set when either this is the first start of nodecg-io | ||
* or if this is a old config from nodecg-io <= 0.2. | ||
* | ||
* If this is a new configuration a new salt will be generated, set inside the EncryptedData object and returned. | ||
* If this is a old configuration from nodecg-io <= 0.2 it will be migrated to the new format as well. | ||
* | ||
* @param data the encrypted data where the salt should be ensured to be available | ||
* @param password the password of the encrypted data. Used if this config needs to be migrated | ||
* @return returns the either retrieved or generated salt | ||
*/ | ||
export declare function getEncryptionSalt(data: EncryptedData, password: string): Promise<string>; | ||
/** | ||
* Manages encrypted persistence of data that is held by the instance and bundle managers. | ||
@@ -47,12 +115,12 @@ */ | ||
private readonly bundles; | ||
private password; | ||
private encryptionKey; | ||
private encryptedData; | ||
constructor(nodecg: NodeCG, services: ServiceManager, instances: InstanceManager, bundles: BundleManager); | ||
constructor(nodecg: NodeCG.ServerAPI<NodeCGBundleConfig>, services: ServiceManager, instances: InstanceManager, bundles: BundleManager); | ||
/** | ||
* Checks whether the passed password is correct. Only works if already loaded and a password is already set. | ||
* @param password the password which should be checked for correctness | ||
* Checks whether the passed encryption key is correct. Only works if already loaded and a encryption key is already set. | ||
* @param encryptionKey the encryption key which should be checked for correctness | ||
*/ | ||
checkPassword(password: string): boolean; | ||
checkEncryptionKey(encryptionKey: string): boolean; | ||
/** | ||
* Returns if the locally stored configuration has been loaded and a password has been set. | ||
* Returns if the locally stored configuration has been loaded and a encryption key has been set. | ||
*/ | ||
@@ -62,11 +130,11 @@ isLoaded(): boolean; | ||
* Returns whether this is the first startup aka. whether any encrypted data has been saved. | ||
* If this returns true {{@link load}} will accept any password and use it to encrypt the configuration. | ||
* If this returns true {@link load} will accept any encryption key and use it to encrypt the configuration. | ||
*/ | ||
isFirstStartup(): boolean; | ||
/** | ||
* Decrypts and loads the locally stored configuration using the passed password. | ||
* @param password the password of the encrypted config. | ||
* @return success if the password was correct and loading has been successful and an error if the password is wrong. | ||
* Decrypts and loads the locally stored configuration using the passed encryption key. | ||
* @param encryptionKey the encryption key of the encrypted config. | ||
* @return success if the encryption key was correct and loading has been successful and an error if the encryption key is wrong. | ||
*/ | ||
load(password: string): Promise<Result<void>>; | ||
load(encryptionKey: string): Promise<Result<void>>; | ||
/** | ||
@@ -98,7 +166,7 @@ * Loads all passed instances into the framework by creating instances of the same type and name | ||
/** | ||
* Checks whether automatic login is setup and enabled. If yes it will do it using {@link PersistenceManager.setupAutomaticLogin}. | ||
* Checks whether automatic login is set up and enabled. If yes, it will do it using {@link PersistenceManager.setupAutomaticLogin}. | ||
*/ | ||
private checkAutomaticLogin; | ||
/** | ||
* Setups everything needed to automatically login using the provided password after nodecg has loaded. | ||
* Setups everything needed to automatically log in using the provided password after NodeCG has loaded. | ||
*/ | ||
@@ -105,0 +173,0 @@ private setupAutomaticLogin; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.PersistenceManager = exports.decryptData = void 0; | ||
exports.PersistenceManager = exports.getEncryptionSalt = exports.reEncryptData = exports.deriveEncryptionKey = exports.encryptData = exports.decryptData = void 0; | ||
const tslib_1 = require("tslib"); | ||
const crypto = (0, tslib_1.__importStar)(require("crypto-js")); | ||
const crypto_js_1 = tslib_1.__importDefault(require("crypto-js")); | ||
const hash_wasm_1 = require("hash-wasm"); | ||
const result_1 = require("./utils/result"); | ||
/** | ||
* Decrypts the passed encrypted data using the passed password. | ||
* If the password is wrong an error will be returned. | ||
* Decrypts the passed encrypted data using the passed encryption key. | ||
* If the encryption key is wrong, an error will be returned. | ||
* | ||
* This function supports the <=0.2 format with the plain password as an | ||
* encryption key and no iv (read from ciphertext) and the >=0.3 format with the iv and derived key. | ||
* | ||
* @param cipherText the ciphertext that needs to be decrypted. | ||
* @param password the password for the encrypted data. | ||
* @param encryptionKey the encryption key for the encrypted data. | ||
* @param iv the initialization vector for the encrypted data. | ||
*/ | ||
function decryptData(cipherText, password) { | ||
function decryptData(cipherText, encryptionKey, iv) { | ||
try { | ||
const decryptedBytes = crypto.AES.decrypt(cipherText, password); | ||
const decryptedText = decryptedBytes.toString(crypto.enc.Utf8); | ||
const ivWordArray = iv ? crypto_js_1.default.enc.Hex.parse(iv) : undefined; | ||
const decryptedBytes = crypto_js_1.default.AES.decrypt(cipherText, encryptionKey, { iv: ivWordArray }); | ||
const decryptedText = decryptedBytes.toString(crypto_js_1.default.enc.Utf8); | ||
const data = JSON.parse(decryptedText); | ||
@@ -27,2 +33,102 @@ return (0, result_1.success)(data); | ||
/** | ||
* Encrypts the passed data object using the passed encryption key. | ||
* | ||
* @param data the data that needs to be encrypted. | ||
* @param encryptionKey the encryption key that should be used to encrypt the data. | ||
* @returns a tuple containing the encrypted data and the initialization vector as a hex string. | ||
*/ | ||
function encryptData(data, encryptionKey) { | ||
const iv = crypto_js_1.default.lib.WordArray.random(16); | ||
const ivText = iv.toString(); | ||
const encrypted = crypto_js_1.default.AES.encrypt(JSON.stringify(data), encryptionKey, { iv }); | ||
return [encrypted.toString(), ivText]; | ||
} | ||
exports.encryptData = encryptData; | ||
/** | ||
* Derives a key suitable for encrypting the config from the given password. | ||
* | ||
* @param password the password from which the encryption key will be derived. | ||
* @param salt the hex encoded salt that is used for key derivation. | ||
* @returns a hex encoded string of the derived key. | ||
*/ | ||
async function deriveEncryptionKey(password, salt) { | ||
var _a, _b; | ||
const saltBytes = Uint8Array.from((_b = (_a = salt.match(/.{1,2}/g)) === null || _a === void 0 ? void 0 : _a.map((byte) => parseInt(byte, 16))) !== null && _b !== void 0 ? _b : []); | ||
return await (0, hash_wasm_1.argon2id)({ | ||
password, | ||
salt: saltBytes, | ||
// OWASP reccomends either t=1,m=37MiB or t=2,m=37MiB for argon2id: | ||
// https://www.owasp.org/index.php/Password_Storage_Cheat_Sheet#Argon2id | ||
// On a Ryzen 5 5500u a single iteration is about 220 ms. Two iterations would make that about 440 ms, which is still fine. | ||
// This is run inside the browser when logging in, therefore 37 MiB is acceptable too. | ||
// To future proof this we use 37 MiB ram and 2 iterations. | ||
iterations: 2, | ||
memorySize: 37, | ||
hashLength: 32, | ||
parallelism: 1, | ||
outputType: "hex", | ||
}); | ||
} | ||
exports.deriveEncryptionKey = deriveEncryptionKey; | ||
/** | ||
* Re-encrypts the passed data to change the password/encryption key. | ||
* Currently only used to migrate from <=0.2 to >=0.3 config formats but | ||
* could be used to implement a change password feature in the future. | ||
* @param data the data that should be re-encrypted. | ||
* @param oldSecret the previous encryption key or password. | ||
* @param newSecret the new encryption key. | ||
*/ | ||
function reEncryptData(data, oldSecret, newSecret) { | ||
if (data.cipherText === undefined) { | ||
return (0, result_1.error)("Cannot re-encrypt empty cipher text."); | ||
} | ||
const decryptedData = decryptData(data.cipherText, oldSecret, data.iv); | ||
if (decryptedData.failed) { | ||
return (0, result_1.error)(decryptedData.errorMessage); | ||
} | ||
const [newCipherText, iv] = encryptData(decryptedData.result, newSecret); | ||
data.cipherText = newCipherText; | ||
data.iv = iv; | ||
return (0, result_1.emptySuccess)(); | ||
} | ||
exports.reEncryptData = reEncryptData; | ||
/** | ||
* Ensures that the passed encrypted data has the salt attribute set. | ||
* The salt attribute is not set when either this is the first start of nodecg-io | ||
* or if this is a old config from nodecg-io <= 0.2. | ||
* | ||
* If this is a new configuration a new salt will be generated, set inside the EncryptedData object and returned. | ||
* If this is a old configuration from nodecg-io <= 0.2 it will be migrated to the new format as well. | ||
* | ||
* @param data the encrypted data where the salt should be ensured to be available | ||
* @param password the password of the encrypted data. Used if this config needs to be migrated | ||
* @return returns the either retrieved or generated salt | ||
*/ | ||
async function getEncryptionSalt(data, password) { | ||
if (data.salt !== undefined) { | ||
// We already have a salt, so we have the new (nodecg-io >=0.3) format too. | ||
// We don't need to do anything then. | ||
return data.salt; | ||
} | ||
// No salt is present, which is the case for the nodecg-io <=0.2 configs | ||
// where crypto-js derived the encryption key and managed the salt | ||
// or when nodecg-io is first started. | ||
// Generate a random salt. | ||
const salt = crypto_js_1.default.lib.WordArray.random(128 / 8).toString(); | ||
if (data.cipherText !== undefined) { | ||
// Salt is unset but we have some encrypted data. | ||
// This means that this is a old config (nodecg-io <=0.2), that we need to migrate to the new format. | ||
// Re-encrypt the configuration using our own derived key instead of the password. | ||
const newEncryptionKey = await deriveEncryptionKey(password, salt); | ||
const newEncryptionKeyArr = crypto_js_1.default.enc.Hex.parse(newEncryptionKey); | ||
const res = reEncryptData(data, password, newEncryptionKeyArr); | ||
if (res.failed) { | ||
throw new Error(`Failed to migrate config: ${res.errorMessage}`); | ||
} | ||
} | ||
data.salt = salt; | ||
return salt; | ||
} | ||
exports.getEncryptionSalt = getEncryptionSalt; | ||
/** | ||
* Manages encrypted persistence of data that is held by the instance and bundle managers. | ||
@@ -43,8 +149,8 @@ */ | ||
/** | ||
* Checks whether the passed password is correct. Only works if already loaded and a password is already set. | ||
* @param password the password which should be checked for correctness | ||
* Checks whether the passed encryption key is correct. Only works if already loaded and a encryption key is already set. | ||
* @param encryptionKey the encryption key which should be checked for correctness | ||
*/ | ||
checkPassword(password) { | ||
checkEncryptionKey(encryptionKey) { | ||
if (this.isLoaded()) { | ||
return this.password === password; | ||
return this.encryptionKey === encryptionKey; | ||
} | ||
@@ -56,28 +162,30 @@ else { | ||
/** | ||
* Returns if the locally stored configuration has been loaded and a password has been set. | ||
* Returns if the locally stored configuration has been loaded and a encryption key has been set. | ||
*/ | ||
isLoaded() { | ||
return this.password !== undefined; | ||
return this.encryptionKey !== undefined; | ||
} | ||
/** | ||
* Returns whether this is the first startup aka. whether any encrypted data has been saved. | ||
* If this returns true {{@link load}} will accept any password and use it to encrypt the configuration. | ||
* If this returns true {@link load} will accept any encryption key and use it to encrypt the configuration. | ||
*/ | ||
isFirstStartup() { | ||
return this.encryptedData.value.cipherText === undefined; | ||
var _a; | ||
return ((_a = this.encryptedData.value) === null || _a === void 0 ? void 0 : _a.cipherText) === undefined; | ||
} | ||
/** | ||
* Decrypts and loads the locally stored configuration using the passed password. | ||
* @param password the password of the encrypted config. | ||
* @return success if the password was correct and loading has been successful and an error if the password is wrong. | ||
* Decrypts and loads the locally stored configuration using the passed encryption key. | ||
* @param encryptionKey the encryption key of the encrypted config. | ||
* @return success if the encryption key was correct and loading has been successful and an error if the encryption key is wrong. | ||
*/ | ||
async load(password) { | ||
async load(encryptionKey) { | ||
var _a; | ||
if (this.isLoaded()) { | ||
return (0, result_1.error)("Config has already been decrypted and loaded."); | ||
} | ||
if (this.encryptedData.value.cipherText === undefined) { | ||
if (((_a = this.encryptedData.value) === null || _a === void 0 ? void 0 : _a.cipherText) === undefined) { | ||
// No encrypted data has been saved, probably because this is the first startup. | ||
// Therefore nothing needs to be decrypted and we write a empty config to disk. | ||
// Therefore nothing needs to be decrypted, and we write an empty config to disk. | ||
this.nodecg.log.info("No saved configuration found, creating a empty one."); | ||
this.password = password; | ||
this.encryptionKey = encryptionKey; | ||
this.save(); | ||
@@ -88,4 +196,6 @@ } | ||
this.nodecg.log.info("Decrypting and loading saved configuration."); | ||
const data = decryptData(this.encryptedData.value.cipherText, password); | ||
const encryptionKeyArr = crypto_js_1.default.enc.Hex.parse(encryptionKey); | ||
const data = decryptData(this.encryptedData.value.cipherText, encryptionKeyArr, this.encryptedData.value.iv); | ||
if (data.failed) { | ||
this.nodecg.log.error("Could not decrypt configuration: encryption key is invalid."); | ||
return data; | ||
@@ -99,4 +209,4 @@ } | ||
} | ||
// Save password, used in save() function | ||
this.password = password; | ||
// Save encryption key, used in save() function | ||
this.encryptionKey = encryptionKey; | ||
// Register handlers to save when something changes | ||
@@ -127,3 +237,3 @@ this.instances.on("change", () => this.save()); | ||
// before getting saved to disk. | ||
// This results in faster loading when the validation takes time, e.g. makes HTTP requests. | ||
// This results in faster loading when the validation takes time, e.g., makes HTTP requests. | ||
return this.instances | ||
@@ -150,6 +260,6 @@ .updateInstanceConfig(instanceName, instance.config, false) | ||
// We can ignore the case of undefined, because the default is that the bundle doesn't get any service | ||
// which is modeled by undefined. We are assuming that there was nobody setting it to something different. | ||
// which is modelled by undefined. We are assuming that there was nobody setting it to something different. | ||
if (svcDep.serviceInstance !== undefined) { | ||
const inst = this.instances.getServiceInstance(svcDep.serviceInstance); | ||
// Don't do anything if the service instance doesn't exist anymore (probably deleted) | ||
// Don't do anything if the service instance doesn't exist any more (probably deleted) | ||
if (inst !== undefined) { | ||
@@ -166,4 +276,4 @@ this.bundles.setServiceDependency(bundleName, svcDep.serviceInstance, inst); | ||
save() { | ||
// Check if we have a password to encrypt the data with. | ||
if (this.password === undefined) { | ||
// Check if we have a encryption key to encrypt the data with. | ||
if (this.encryptionKey === undefined) { | ||
return; | ||
@@ -177,4 +287,9 @@ } | ||
// Encrypt and save data to persistent replicant. | ||
const cipherText = crypto.AES.encrypt(JSON.stringify(data), this.password); | ||
this.encryptedData.value.cipherText = cipherText.toString(); | ||
if (this.encryptedData.value === undefined) { | ||
this.encryptedData.value = {}; | ||
} | ||
const encryptionKeyArr = crypto_js_1.default.enc.Hex.parse(this.encryptionKey); | ||
const [cipherText, iv] = encryptData(data, encryptionKeyArr); | ||
this.encryptedData.value.cipherText = cipherText; | ||
this.encryptedData.value.iv = iv; | ||
} | ||
@@ -210,3 +325,3 @@ /** | ||
/** | ||
* Checks whether automatic login is setup and enabled. If yes it will do it using {@link PersistenceManager.setupAutomaticLogin}. | ||
* Checks whether automatic login is set up and enabled. If yes, it will do it using {@link PersistenceManager.setupAutomaticLogin}. | ||
*/ | ||
@@ -222,3 +337,3 @@ checkAutomaticLogin() { | ||
if (enabled === false) { | ||
// We inform the user that automatic login is setup but not activated because having the ability | ||
// We inform the user that automatic login is set up but not activated because having the ability | ||
// to disable it by setting the enabled flag to false is meant for temporary cases. | ||
@@ -230,6 +345,10 @@ // If automatic login is permanently not used the user should remove the password from the config | ||
} | ||
if (password === undefined) { | ||
this.nodecg.log.error("Automatic login is setup but no password is provided."); | ||
return; | ||
} | ||
this.setupAutomaticLogin(password); | ||
} | ||
/** | ||
* Setups everything needed to automatically login using the provided password after nodecg has loaded. | ||
* Setups everything needed to automatically log in using the provided password after NodeCG has loaded. | ||
*/ | ||
@@ -239,12 +358,14 @@ setupAutomaticLogin(password) { | ||
// or even register services which we need to load nodecg-io. | ||
// There is no official way to wait for nodecg to be done loading so we use more or less a hack to find that out: | ||
// When we declare the replicant here we will trigger a change event with a empty array. | ||
// Once nodecg is done loading all bundles it'll assign a array of bundles that were loaded to this replicant | ||
// So if we want to wait for nodecg to be loaded we can watch for changes on this replicant and | ||
// if we get a non-empty array it means that nodecg has finished loading. | ||
// There is no official way to wait for NodeCG to be done loading, so we use more or less a hack to find that out: | ||
// When we declare the replicant here we will trigger a change event with an empty array. | ||
// Once NodeCG is done loading all bundles it'll assign an array of bundles that were loaded to this replicant | ||
// So if we want to wait for NodeCG to be loaded we can watch for changes on this replicant and | ||
// if we get a non-empty array it means that NodeCG has finished loading. | ||
this.nodecg.Replicant("bundles", "nodecg").on("change", async (bundles) => { | ||
if (bundles.length > 0) { | ||
if (bundles && bundles.length > 0 && this.encryptedData.value) { | ||
try { | ||
this.nodecg.log.info("Attempting to automatically login..."); | ||
const loadResult = await this.load(password); | ||
const salt = await getEncryptionSalt(this.encryptedData.value, password); | ||
const encryptionKey = await deriveEncryptionKey(password, salt); | ||
const loadResult = await this.load(encryptionKey); | ||
if (!loadResult.failed) { | ||
@@ -254,7 +375,17 @@ this.nodecg.log.info("Automatic login successful."); | ||
else { | ||
throw loadResult.errorMessage; | ||
throw new Error(loadResult.errorMessage); | ||
} | ||
} | ||
catch (err) { | ||
this.nodecg.log.error(`Failed to automatically login: ${err}`); | ||
const logMessage = `Failed to automatically login: ${err}`; | ||
if (this.isLoaded()) { | ||
// load() threw an error but nodecg-io is currently loaded nonetheless. | ||
// Anyway, nodecg-io is loaded which is what we wanted | ||
this.nodecg.log.warn(logMessage); | ||
} | ||
else { | ||
// Something went wrong and nodecg-io is not loaded. | ||
// This is a real error, the password might be wrong or some other issue. | ||
this.nodecg.log.error(logMessage); | ||
} | ||
} | ||
@@ -261,0 +392,0 @@ } |
@@ -5,24 +5,24 @@ import { Result } from "./utils/result"; | ||
/** | ||
* Models a map using a object, instead of a iterator like the javascript es6 map. | ||
* Models a map using an object, instead of an iterator like the JavaScript es6 map. | ||
* Enhances the {@link Record} type of typescript by always considering the case that the value is undefined. | ||
* This usually happens when the key doesn't exist. | ||
* Modeling a map using a javascript object has the advantage that it can be easily serialized. | ||
* This is used as this object can be stored in a NodeCG Replicant and this Replicant can then also be used by the gui. | ||
* A normal es6 map would use a iterator which can't be serialized by the NodeCG Replicant and thus | ||
* can't be used to give the gui access to the data in this map. | ||
* Modelling a map using a JavaScript object has the advantage that it can be easily serialized. | ||
* This is used as this object can be stored in a NodeCG replicant and this replicant can then also be used by the GUI. | ||
* A normal es6 map would use an iterator which can't be serialized by the NodeCG replicant and thus | ||
* can't be used to give the GUI access to the data in this map. | ||
*/ | ||
export declare type ObjectMap<V> = Record<string, V>; | ||
export type ObjectMap<V> = Record<string, V>; | ||
/** | ||
* Models a service that a bundle can depend upon and use to access e.g. a twitch chat or similar. | ||
* @typeParam R a interface type that describes the user provided config for the service. | ||
* Models a service that a bundle can depend upon and used to access e.g., a twitch chat or similar. | ||
* @typeParam R an interface type that describes the user provided config for the service. | ||
* Intended to hold configurations and authentication information that the service needs to provide a client. | ||
* @typeParam C the type of a client that the service will provide to bundles using {@link createClient}. | ||
* @typeParam C the type of client that the service will provide to bundles using {@link createClient}. | ||
*/ | ||
export interface Service<R, C> { | ||
/** | ||
* User friendly name of the service that should explain the type of service, e.g. "twitch". | ||
* User-friendly name of the service that should explain the type of service, e.g., "twitch". | ||
*/ | ||
readonly serviceType: string; | ||
/** | ||
* A json schema object of the config. The config will then be validated against this json schema. | ||
* A JSON schema object of the config. The config will then be validated against this JSON schema. | ||
* Ensures that the types of the config are correct and therefore is compatible with the provided config type. | ||
@@ -37,3 +37,3 @@ */ | ||
* Config presets that the user can choose to load as their config. | ||
* Useful for e.g. detected devices with everything already filled in for that specific device. | ||
* Useful for e.g., detected devices with everything already filled in for that specific device. | ||
* Can also be used to show the user multiple different authentication methods or similar. | ||
@@ -43,3 +43,3 @@ */ | ||
/** | ||
* This function validates the passed config after it has been validated against the json schema (if applicable). | ||
* This function validates the passed config after it has been validated against the JSON schema (if applicable). | ||
* Should make deeper checks like checking validity of auth tokens. | ||
@@ -53,11 +53,11 @@ * @param config the config which should be validated. | ||
* Creates a client to the service using the validated config. | ||
* The returned result will be passed to bundles and they should be able to use the service with this returned client. | ||
* The returned result will be passed to bundles, and they should be able to use the service with this returned client. | ||
* | ||
* @param config the user provided config for the service. | ||
* @param logger the logger which logs with the instance.name as prefix | ||
* @return the client if everything went well and an error string describing the issue if a error occured. | ||
* @return the client if everything went well and an error string describing the issue if an error occurred. | ||
*/ | ||
createClient(config: R, logger: Logger): Promise<Result<C>>; | ||
/** | ||
* Stops a client of this service that is not needed anymore. | ||
* Stops a client of this service that is not needed any more. | ||
* Services should close any connections that might exist here. | ||
@@ -73,6 +73,6 @@ * | ||
* Then this function is called that should remove all handlers | ||
* and then all bundles that are still using this client will asked to re-register their handlers | ||
* and then all bundles that are still using this client will ask to re-register their handlers | ||
* by running the onAvailable callback of the specific bundle. | ||
* | ||
* Can be left unimplemented if the serivce doesn't has any handlers e.g. a http wrapper | ||
* Can be left unimplemented if the service doesn't have any handlers e.g., a http wrapper | ||
* @param client the client of which all handlers should be removed | ||
@@ -85,5 +85,5 @@ */ | ||
* It gets rid of the handlers by stopping the client and creating a new one, to which then only the | ||
* now wanted handlers get registered (e.g. if a bundle doesn't uses this service anymore but another still does). | ||
* now wanted handlers get registered (e.g., if a bundle doesn't use this service any more, but another still does). | ||
* Not ideal, but if your service can't implement removeHandlers for some reason it is still better than | ||
* having dangling handlers that still fire eventho they shouldn't. | ||
* having dangling handlers that still fire even though they shouldn't. | ||
* @default false | ||
@@ -94,3 +94,3 @@ */ | ||
* This flag says that this service cannot be configured and doesn't need any config passed to {@link createClient}. | ||
* If this is set {@link validateConfig} will never be called. | ||
* If this is set, {@link validateConfig} will never be called. | ||
* @default false | ||
@@ -97,0 +97,0 @@ */ |
import { NodeCGIOCore } from "."; | ||
import { NodeCG } from "nodecg-types/types/server"; | ||
import NodeCG from "@nodecg/types"; | ||
import { ObjectMap, Service } from "./service"; | ||
@@ -9,10 +9,10 @@ import { Result } from "./utils/result"; | ||
* | ||
* Models a service that a bundle can depend upon and use to access e.g. a twitch chat or similar. | ||
* @typeParam R a interface type that describes the user provided config for the service. | ||
* Models a service that a bundle can depend upon and used to access e.g., a twitch chat or similar. | ||
* @typeParam R an interface type that describes the user provided config for the service. | ||
* Intended to hold configurations and authentication information that the service needs to provide a client. | ||
* @typeParam C the type of a client that the service will provide to bundles using {@link createClient}. | ||
* @typeParam C the type of client that the service will provide to bundles using {@link createClient}. | ||
*/ | ||
export declare abstract class ServiceBundle<R, C> implements Service<R, C> { | ||
core: NodeCGIOCore | undefined; | ||
nodecg: NodeCG; | ||
nodecg: NodeCG.ServerAPI; | ||
serviceType: string; | ||
@@ -26,3 +26,3 @@ schema?: ObjectMap<unknown>; | ||
* Config presets that the user can choose to load as their config. | ||
* Useful for e.g. detected devices with everything already filled in for that specific device. | ||
* Useful for e.g., detected devices with everything already filled in for that specific device. | ||
* Can also be used to show the user multiple different authentication methods or similar. | ||
@@ -37,3 +37,3 @@ */ | ||
*/ | ||
constructor(nodecg: NodeCG, serviceName: string, ...pathSegments: string[]); | ||
constructor(nodecg: NodeCG.ServerAPI, serviceName: string, ...pathSegments: string[]); | ||
/** | ||
@@ -45,3 +45,3 @@ * Registers this service bundle at the core bundle, makes it appear in the GUI and makes it usable. | ||
/** | ||
* This function validates the passed config after it has been validated against the json schema (if applicable). | ||
* This function validates the passed config after it has been validated against the JSON schema (if applicable). | ||
* Should make deeper checks like checking validity of auth tokens. | ||
@@ -55,11 +55,11 @@ * @param config the config which should be validated. | ||
* Creates a client to the service using the validated config. | ||
* The returned result will be passed to bundles and they should be able to use the service with this returned client. | ||
* The returned result will be passed to bundles, and they should be able to use the service with this returned client. | ||
* | ||
* @param config the user provided config for the service. | ||
* @param logger the logger which logs with the instance.name as prefix | ||
* @return the client if everything went well and an error string describing the issue if a error occured. | ||
* @return the client if everything went well and an error string describing the issue if an error occurred. | ||
*/ | ||
abstract createClient(config: R, logger: Logger): Promise<Result<C>>; | ||
/** | ||
* Stops a client of this service that is not needed anymore. | ||
* Stops a client of this service that is not needed any more. | ||
* Services should close any connections that might exist here. | ||
@@ -75,6 +75,6 @@ * | ||
* Then this function is called that should remove all handlers | ||
* and then all bundles that are still using this client will asked to re-register their handlers | ||
* and then all bundles that are still using this client will ask to re-register their handlers | ||
* by running the onAvailable callback of the specific bundle. | ||
* | ||
* Can be left unimplemented if the serivce doesn't has any handlers e.g. a http wrapper | ||
* Can be left unimplemented if the service doesn't have any handlers e.g., a http wrapper | ||
* @param client the client of which all handlers should be removed | ||
@@ -87,5 +87,5 @@ */ | ||
* It gets rid of the handlers by stopping the client and creating a new one, to which then only the | ||
* now wanted handlers get registered (e.g. if a bundle doesn't uses this service anymore but another still does). | ||
* now wanted handlers get registered (e.g., if a bundle doesn't use this service any more, but another still does). | ||
* Not ideal, but if your service can't implement removeHandlers for some reason it is still better than | ||
* having dangling handlers that still fire events eventho they shouldn't. | ||
* having dangling handlers that still fire events even though they shouldn't. | ||
*/ | ||
@@ -95,3 +95,3 @@ reCreateClientToRemoveHandlers: boolean; | ||
* This flag says that this service cannot be configured and doesn't need any config passed to {@link createClient}. | ||
* If this is set {@link validateConfig} will never be called. | ||
* If this is set, {@link validateConfig} will never be called. | ||
* @default false | ||
@@ -98,0 +98,0 @@ */ |
@@ -5,11 +5,11 @@ "use strict"; | ||
const tslib_1 = require("tslib"); | ||
const fs = (0, tslib_1.__importStar)(require("fs")); | ||
const path = (0, tslib_1.__importStar)(require("path")); | ||
const fs = tslib_1.__importStar(require("fs")); | ||
const path = tslib_1.__importStar(require("path")); | ||
/** | ||
* Class helping to create a nodecg-io service | ||
* | ||
* Models a service that a bundle can depend upon and use to access e.g. a twitch chat or similar. | ||
* @typeParam R a interface type that describes the user provided config for the service. | ||
* Models a service that a bundle can depend upon and used to access e.g., a twitch chat or similar. | ||
* @typeParam R an interface type that describes the user provided config for the service. | ||
* Intended to hold configurations and authentication information that the service needs to provide a client. | ||
* @typeParam C the type of a client that the service will provide to bundles using {@link createClient}. | ||
* @typeParam C the type of client that the service will provide to bundles using {@link createClient}. | ||
*/ | ||
@@ -28,5 +28,5 @@ class ServiceBundle { | ||
* It gets rid of the handlers by stopping the client and creating a new one, to which then only the | ||
* now wanted handlers get registered (e.g. if a bundle doesn't uses this service anymore but another still does). | ||
* now wanted handlers get registered (e.g., if a bundle doesn't use this service any more, but another still does). | ||
* Not ideal, but if your service can't implement removeHandlers for some reason it is still better than | ||
* having dangling handlers that still fire events eventho they shouldn't. | ||
* having dangling handlers that still fire events even though they shouldn't. | ||
*/ | ||
@@ -36,3 +36,3 @@ this.reCreateClientToRemoveHandlers = false; | ||
* This flag says that this service cannot be configured and doesn't need any config passed to {@link createClient}. | ||
* If this is set {@link validateConfig} will never be called. | ||
* If this is set, {@link validateConfig} will never be called. | ||
* @default false | ||
@@ -39,0 +39,0 @@ */ |
import { Service } from "./service"; | ||
import { NodeCG } from "nodecg-types/types/server"; | ||
import NodeCG from "@nodecg/types"; | ||
import { Result } from "./utils/result"; | ||
@@ -10,3 +10,3 @@ /** | ||
private services; | ||
constructor(nodecg: NodeCG); | ||
constructor(nodecg: NodeCG.ServerAPI); | ||
/** | ||
@@ -13,0 +13,0 @@ * Registers the passed service which show it in the GUI and allows it to be instanced using {@link createServiceInstance}. |
@@ -1,2 +0,1 @@ | ||
import { NodeCG } from "nodecg-types/types/server"; | ||
/** | ||
@@ -18,15 +17,15 @@ * A wrapper around a ServiceClient that has helper functions for setting up callbacks and | ||
/** | ||
* Registers a callback that gets fired everytime the available client gets updated and is available, | ||
* meaning the bundle has an assigned service instance and it didn't produce an error while creating the client. | ||
* @param {(client: C) => void} handler a handler that gets called everytime the client gets available. | ||
* Registers a callback that gets fired every time the available client gets updated and is available, | ||
* meaning the bundle has an assigned service instance, and it didn't produce an error while creating the client. | ||
* @param {(client: C) => void} handler a handler that gets called every time the client gets available. | ||
*/ | ||
onAvailable(handler: (client: C) => void): void; | ||
/** | ||
* Registers a callback that is everytime called when there is no assigned service instance anymore or it tried | ||
* Registers a callback that is every time called when there is no assigned service instance any more, or it tried | ||
* to create a service client and failed. | ||
* @param {() => void} handler a handler that gets called everytime the client gets unavailable. | ||
* @param {() => void} handler a handler that gets called every time the client gets unavailable. | ||
*/ | ||
onUnavailable(handler: () => void): void; | ||
/** | ||
* Updates the client and calls all registered handlers of {@link onAvailable} and {@link onUnavailable} depending | ||
* Updates the client and calls all registered handlers of {@link onAvailable} and {@link onUnavailable} depending on | ||
* whether the passed client parameter was undefined or not. | ||
@@ -39,9 +38,24 @@ * This is only intended to be called by the framework and not by a bundle. | ||
/** | ||
* Common interface between multiple NodeCG typings. | ||
* Ensures that the only user-facing function {@link requireService} works with the old official nodecg 1.x types, | ||
* the unofficial nodecg-types for 1.x without the rest of the repository and the new | ||
* official @nodecg/types for nodecg 2.x. | ||
*/ | ||
interface NodeCGCompatible { | ||
readonly extensions: Record<string, unknown>; | ||
bundleName: string; | ||
log: { | ||
error: (message: string) => void; | ||
}; | ||
} | ||
/** | ||
* Allows for bundles to require services. | ||
* @param {NodeCG} nodecg the nodecg instance of your bundle. Is used to get the bundle name of the calling bundle. | ||
* @param {string} serviceType the type of service you want to require, e.g. "twitch" or "spotify". | ||
* @param {NodeCGCompatible} nodecg the NodeCG instance of your bundle. Is used to get the bundle name of the calling bundle | ||
* and to get access to the nodecg-io-core bundle to register your service requirement. | ||
* @param {string} serviceType the type of service you want to require, e.g., "twitch" or "spotify". | ||
* @return {ServiceClientWrapper<C> | undefined} a service client wrapper for access to the service client | ||
* or undefined if the core wasn't loaded or the service type doesn't exist. | ||
*/ | ||
export declare function requireService<C>(nodecg: NodeCG, serviceType: string): ServiceProvider<C> | undefined; | ||
export declare function requireService<C>(nodecg: NodeCGCompatible, serviceType: string): ServiceProvider<C> | undefined; | ||
export {}; | ||
//# sourceMappingURL=serviceProvider.d.ts.map |
@@ -13,3 +13,3 @@ "use strict"; | ||
this.em = new events_1.EventEmitter(); | ||
// Make service client non enumerable, that way it won't be serialized, which would cause problems. | ||
// Make service client non-enumerable, that way it won't be serialized, which would cause problems. | ||
Object.defineProperty(this, "currentClient", { | ||
@@ -29,5 +29,5 @@ enumerable: false, | ||
/** | ||
* Registers a callback that gets fired everytime the available client gets updated and is available, | ||
* meaning the bundle has an assigned service instance and it didn't produce an error while creating the client. | ||
* @param {(client: C) => void} handler a handler that gets called everytime the client gets available. | ||
* Registers a callback that gets fired every time the available client gets updated and is available, | ||
* meaning the bundle has an assigned service instance, and it didn't produce an error while creating the client. | ||
* @param {(client: C) => void} handler a handler that gets called every time the client gets available. | ||
*/ | ||
@@ -38,5 +38,5 @@ onAvailable(handler) { | ||
/** | ||
* Registers a callback that is everytime called when there is no assigned service instance anymore or it tried | ||
* Registers a callback that is every time called when there is no assigned service instance any more, or it tried | ||
* to create a service client and failed. | ||
* @param {() => void} handler a handler that gets called everytime the client gets unavailable. | ||
* @param {() => void} handler a handler that gets called every time the client gets unavailable. | ||
*/ | ||
@@ -47,3 +47,3 @@ onUnavailable(handler) { | ||
/** | ||
* Updates the client and calls all registered handlers of {@link onAvailable} and {@link onUnavailable} depending | ||
* Updates the client and calls all registered handlers of {@link onAvailable} and {@link onUnavailable} depending on | ||
* whether the passed client parameter was undefined or not. | ||
@@ -61,4 +61,5 @@ * This is only intended to be called by the framework and not by a bundle. | ||
* Allows for bundles to require services. | ||
* @param {NodeCG} nodecg the nodecg instance of your bundle. Is used to get the bundle name of the calling bundle. | ||
* @param {string} serviceType the type of service you want to require, e.g. "twitch" or "spotify". | ||
* @param {NodeCGCompatible} nodecg the NodeCG instance of your bundle. Is used to get the bundle name of the calling bundle | ||
* and to get access to the nodecg-io-core bundle to register your service requirement. | ||
* @param {string} serviceType the type of service you want to require, e.g., "twitch" or "spotify". | ||
* @return {ServiceClientWrapper<C> | undefined} a service client wrapper for access to the service client | ||
@@ -65,0 +66,0 @@ * or undefined if the core wasn't loaded or the service type doesn't exist. |
@@ -1,6 +0,6 @@ | ||
import { NodeCG } from "nodecg-types/types/server"; | ||
import NodeCG from "@nodecg/types"; | ||
export declare class Logger { | ||
private name; | ||
private nodecg; | ||
constructor(name: string, nodecg: NodeCG); | ||
constructor(name: string, nodecg: NodeCG.ServerAPI); | ||
trace(...args: any[]): void; | ||
@@ -7,0 +7,0 @@ debug(...args: any[]): void; |
/** | ||
* Result is a return type for functions which might return a value | ||
* or might fail and return a string which describes the error. | ||
* Intended for functions which are called from a GUI and should provide a error message if one occurs. | ||
* Intended for functions which are called from a GUI and should provide an error message if one occurs. | ||
* | ||
@@ -10,3 +10,3 @@ * The result is represented by the {@link Failure} and {@link Success} types. | ||
*/ | ||
export declare type Result<T> = Failure | Success<T>; | ||
export type Result<T> = Failure | Success<T>; | ||
/** | ||
@@ -16,3 +16,3 @@ * Function has failed and has returned a string describing the error. | ||
*/ | ||
export declare type Failure = { | ||
export type Failure = { | ||
failed: true; | ||
@@ -25,3 +25,3 @@ errorMessage: string; | ||
*/ | ||
export declare type Success<T> = { | ||
export type Success<T> = { | ||
failed: false; | ||
@@ -28,0 +28,0 @@ result: T; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const tslib_1 = require("tslib"); | ||
(0, tslib_1.__exportStar)(require("./extension/utils/result"), exports); | ||
(0, tslib_1.__exportStar)(require("./extension/serviceBundle"), exports); | ||
(0, tslib_1.__exportStar)(require("./extension/serviceProvider"), exports); | ||
(0, tslib_1.__exportStar)(require("./extension/utils/logger"), exports); | ||
tslib_1.__exportStar(require("./extension/utils/result"), exports); | ||
tslib_1.__exportStar(require("./extension/serviceBundle"), exports); | ||
tslib_1.__exportStar(require("./extension/serviceProvider"), exports); | ||
tslib_1.__exportStar(require("./extension/utils/logger"), exports); | ||
//# sourceMappingURL=index.js.map |
{ | ||
"name": "nodecg-io-core", | ||
"version": "0.2.0", | ||
"version": "0.3.0", | ||
"description": "The core of nodecg-io. Connects everything up.", | ||
@@ -34,3 +34,3 @@ "homepage": "https://nodecg.io/RELEASE", | ||
"nodecg": { | ||
"compatibleRange": "^1.1.1", | ||
"compatibleRange": ">=1.1.1 <3.0.0", | ||
"dashboardPanels": [ | ||
@@ -48,15 +48,16 @@ { | ||
"devDependencies": { | ||
"@types/crypto-js": "^4.0.2", | ||
"@types/jest": "^27.0.2", | ||
"@types/node": "^16.11.7", | ||
"jest": "^27.3.1", | ||
"nodecg-types": "^1.8.3", | ||
"typescript": "^4.4.4", | ||
"ts-jest": "^27.0.7" | ||
"@types/crypto-js": "^4.1.1", | ||
"@types/jest": "^28.1.7", | ||
"@types/node": "^20.3.3", | ||
"jest": "^28.1.3", | ||
"@nodecg/types": "^2.1.11", | ||
"typescript": "^5.1.6", | ||
"ts-jest": "^28.0.8" | ||
}, | ||
"dependencies": { | ||
"ajv": "^8.6.3", | ||
"hash-wasm": "^4.9.0", | ||
"ajv": "^8.12.0", | ||
"crypto-js": "^4.1.1", | ||
"tslib": "^2.3.1" | ||
"tslib": "^2.6.0" | ||
} | ||
} |
{ | ||
"exclude": ["dashboard", "extension/__tests__"], | ||
"extends": "../tsconfig.common.json" | ||
"extends": "nodecg-io-tsconfig" | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
125157
40
1938
4
+ Addedhash-wasm@^4.9.0
+ Addedhash-wasm@4.12.0(transitive)
Updatedajv@^8.12.0
Updatedtslib@^2.6.0