Skip to content
This repository was archived by the owner on Apr 13, 2025. It is now read-only.

Migrate NodeCG types from nodecg-types to the new NodeCG v2 official ones #930

Merged
merged 3 commits into from
May 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .scripts/create-service.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
os.mkdir(f'services/nodecg-io-{service_name}/extension')
with open(f'services/nodecg-io-{service_name}/extension/index.ts', mode='w') as file:
file.writelines([
'import { NodeCG } from "nodecg-types/types/server";\n',
'import NodeCG from "@nodecg/types";\n',
'import { Result, emptySuccess, success, ServiceBundle } from "nodecg-io-core";\n',
f'import {{ {service_name_c}Client }} from "./{service_name_cc}Client";\n',
'\n',
Expand All @@ -57,7 +57,7 @@
'\n',
f'export {{ {service_name_c}Client }} from "./{service_name_cc}Client";\n',
'\n',
'module.exports = (nodecg: NodeCG) => {\n',
'module.exports = (nodecg: NodeCG.ServerAPI) => {\n',
f' new {service_name_c}Service(nodecg, "{service_name}", __dirname, "../schema.json").register();\n',
'};\n',
'\n',
Expand Down Expand Up @@ -135,11 +135,11 @@
os.mkdir(f'samples/{sample_name}/extension')
with open(f'samples/{sample_name}/extension/index.ts', mode='w') as file:
file.writelines([
'import { NodeCG } from "nodecg-types/types/server";\n',
'import NodeCG from "@nodecg/types";\n',
f'import {{ {service_name_c}Client }} from "nodecg-io-{service_name}";\n',
'import { requireService } from "nodecg-io-core";\n',
'\n',
'module.exports = function (nodecg: NodeCG) {\n',
'module.exports = function (nodecg: NodeCG.ServerAPI) {\n',
f' nodecg.log.info("Sample bundle for {service_name_c} started.");\n',
'\n',
f' const {service_name_cc} = requireService<{service_name_c}Client>(nodecg, "{service_name}");\n',
Expand Down
4 changes: 1 addition & 3 deletions nodecg-io-core/dashboard/authentication.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
/// <reference types="nodecg-types/types/browser" />

import { updateMonacoLayout } from "./serviceInstance";
import { setPassword, isPasswordSet } from "./crypto";

Expand Down Expand Up @@ -41,7 +39,7 @@ document.addEventListener("DOMContentLoaded", () => {

export async function isLoaded(): Promise<boolean> {
return new Promise((resolve, _reject) => {
nodecg.sendMessage("isLoaded", (_err, res) => resolve(res));
nodecg.sendMessage("isLoaded", (_err, res: boolean) => resolve(res));
setTimeout(() => resolve(false), 5000); // Fallback in case connection gets lost.
});
}
Expand Down
8 changes: 8 additions & 0 deletions nodecg-io-core/dashboard/main.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
// Define NodeCG instance on global scope
import type { NodeCGAPIClient } from "@nodecg/types/client/api/api.client";

declare global {
const NodeCG: typeof NodeCGAPIClient;
const nodecg: NodeCGAPIClient;
}

// Re-export functions that are used in panel.html to the global scope
import { loadFramework } from "./authentication";
import {
Expand Down
2 changes: 1 addition & 1 deletion nodecg-io-core/dashboard/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"devDependencies": {
"esbuild": "^0.16.12",
"monaco-editor": "^0.36.1",
"nodecg-types": "^1.9.0",
"@nodecg/types": "^2.1.3",
"typescript": "^5.0.3"
},
"dependencies": {
Expand Down
6 changes: 3 additions & 3 deletions nodecg-io-core/extension/__tests__/bundleManager.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { MockNodeCG, testBundle, testService, testServiceInstance } from "./mocks";
import { mockNodeCG, testBundle, testService, testServiceInstance } from "./mocks";
import { BundleManager } from "../bundleManager";

describe("BundleManager", () => {
Expand All @@ -12,7 +12,7 @@ describe("BundleManager", () => {

beforeEach(() => {
changeCb.mockReset();
bundleManager = new BundleManager(new MockNodeCG());
bundleManager = new BundleManager(mockNodeCG());
bundleManager.on("change", changeCb);
});

Expand All @@ -30,7 +30,7 @@ describe("BundleManager", () => {
});

test("should error if registering a dependency on the same service twice", () => {
const bundleManager = new BundleManager(new MockNodeCG());
const bundleManager = new BundleManager(mockNodeCG());
// Depending on testService for the first time => fine
bundleManager.registerServiceDependency(testBundle, testService);
// Depending on testService for the second time => not fine
Expand Down
4 changes: 2 additions & 2 deletions nodecg-io-core/extension/__tests__/instanceManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { ServiceInstance } from "../service";
import { ServiceManager } from "../serviceManager";
import { emptySuccess, error, success } from "../utils/result";
import {
MockNodeCG,
mockNodeCG,
testBundle,
testBundle2,
testInstance,
Expand All @@ -14,7 +14,7 @@ import {
} from "./mocks";

describe("InstanceManager", () => {
const nodecg = new MockNodeCG();
const nodecg = mockNodeCG();

const noConfigService = {
...testService,
Expand Down
36 changes: 24 additions & 12 deletions nodecg-io-core/extension/__tests__/mocks.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ObjectMap, ServiceInstance } from "../service";
import type { NodeCG, ReplicantOptions, Replicant, Logger } from "nodecg-types/types/server";
import NodeCG from "@nodecg/types";
import { EventEmitter } from "events";

// The mock-nodecg package has a few problems like no typings and some unimplemented functions that are a dealbreaker for us.
Expand All @@ -8,7 +8,7 @@ import { EventEmitter } from "events";
// But for now we use these mocks that we can easily change if we need something
// that mock-nodecg hasn't implemented yet.

export class MockNodeCG implements NodeCG {
export class MockNodeCG {
constructor(
public bundleName: string = "nodecg-io-core",
public bundleConfig = {},
Expand All @@ -31,7 +31,9 @@ export class MockNodeCG implements NodeCG {
logging: {},
sentry: {},
login: {},
};
exitOnUncaught: false,
bundles: {},
} as unknown as NodeCG.Config;

sendMessage = jest.fn();
sendMessageToBundle = jest.fn();
Expand All @@ -41,8 +43,12 @@ export class MockNodeCG implements NodeCG {
// We don't care about the type that all replicants have. The user has to ensure that the type they provide
// to the replicant method matches the actual content of the replicant.
// eslint-disable-next-line @typescript-eslint/no-explicit-any
private declaredReplicants: ObjectMap<ObjectMap<Replicant<any>>> = {};
Replicant<V>(name: string, namespaceOrOpts?: string | ReplicantOptions<V>, o?: ReplicantOptions<V>): Replicant<V> {
private declaredReplicants: ObjectMap<ObjectMap<MockedReplicant<any>>> = {};
Replicant<V>(
name: string,
namespaceOrOpts?: string | NodeCG.Replicant.OptionsWithDefault<V>,
o?: NodeCG.Replicant.OptionsWithDefault<V>,
): MockedReplicant<V> {
const namespace = typeof namespaceOrOpts === "string" ? namespaceOrOpts : this.bundleName;

// Check if this replicant was already declared once and return it if found
Expand All @@ -54,7 +60,7 @@ export class MockNodeCG implements NodeCG {
// This replicant was not already declared and needs to be created.

const opts = typeof namespaceOrOpts === "object" ? namespaceOrOpts : o;
const newReplicant = new MockReplicant(this.log, name, namespace, opts ?? {});
const newReplicant = new MockReplicant(this.log, name, namespace, opts ?? {}) as unknown as MockedReplicant<V>;

const namespaceObj = this.declaredReplicants[namespace];
if (namespaceObj === undefined) {
Expand All @@ -68,7 +74,7 @@ export class MockNodeCG implements NodeCG {

readReplicant<V>(name: string): V;
readReplicant<V>(name: string, namespace: string): V;
readReplicant<V>(name: string, namespace?: string): V {
readReplicant<V>(name: string, namespace?: string): V | undefined {
return this.Replicant<V>(name, namespace).value;
}

Expand All @@ -82,24 +88,26 @@ export class MockNodeCG implements NodeCG {
}

class MockLogger {
name: "logger";
trace = jest.fn();
debug = jest.fn();
info = jest.fn();
warn = jest.fn();
error = jest.fn();
replicants = jest.fn();
static globalReconfigure = jest.fn();
}

class MockReplicant<V> extends EventEmitter implements Replicant<V> {
private _value: V | undefined = this.opts.defaultValue;
type MockedReplicant<V> = MockReplicant<V> & NodeCG.ServerReplicant<V>;

class MockReplicant<V> extends EventEmitter {
_value: V | undefined = this.opts.defaultValue;
revision = 0;

constructor(
public readonly log: Logger,
public readonly log: NodeCG.Logger,
public readonly name: string,
public readonly namespace: string,
public readonly opts: ReplicantOptions<V>,
public readonly opts: Partial<NodeCG.Replicant.OptionsWithDefault<V>>,
) {
super();
}
Expand All @@ -124,6 +132,10 @@ class MockReplicant<V> extends EventEmitter implements Replicant<V> {
}
}

export function mockNodeCG(): MockNodeCG & NodeCG.ServerAPI {
return new MockNodeCG() as unknown as MockNodeCG & NodeCG.ServerAPI;
}

// Test objects

// These variables all contain a string of their name and are mainly
Expand Down
8 changes: 6 additions & 2 deletions nodecg-io-core/extension/__tests__/persistenceManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ import { decryptData, EncryptedData, PersistenceManager, PersistentData } from "
import { ServiceManager } from "../serviceManager";
import { ServiceProvider } from "../serviceProvider";
import { emptySuccess, error } from "../utils/result";
import { MockNodeCG, testBundle, testInstance, testService, testServiceInstance } from "./mocks";
import { mockNodeCG, testBundle, testInstance, testService, testServiceInstance } from "./mocks";

describe("PersistenceManager", () => {
const validPassword = "myPassword";
const invalidPassword = "someOtherPassword";

const nodecg = new MockNodeCG();
const nodecg = mockNodeCG();
const serviceManager = new ServiceManager(nodecg);
serviceManager.registerService(testService);

Expand Down Expand Up @@ -379,6 +379,10 @@ describe("PersistenceManager", () => {
test("should automatically save if BundleManager or InstanceManager emit a change event", async () => {
await persistenceManager.load(validPassword); // Set password so that we can save stuff

if (!encryptedDataReplicant.value) {
throw new Error("encryptedDataReplicant.value was undefined");
}

encryptedDataReplicant.value.cipherText = undefined;
bundleManager.emit("change");
expect(encryptedDataReplicant.value.cipherText).toBeDefined();
Expand Down
10 changes: 5 additions & 5 deletions nodecg-io-core/extension/__tests__/serviceManager.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { ServiceManager } from "../serviceManager";
import { MockNodeCG, testService } from "./mocks";
import { mockNodeCG, testService } from "./mocks";

describe("ServiceManager", () => {
test("should start with no services", () => {
const serviceManager = new ServiceManager(new MockNodeCG());
const serviceManager = new ServiceManager(mockNodeCG());
expect(serviceManager.getServices().length).toBe(0);
});

test("should return all registered services", () => {
const serviceManager = new ServiceManager(new MockNodeCG());
const serviceManager = new ServiceManager(mockNodeCG());
serviceManager.registerService(testService);

// Make sure that the freshly registered service is also in the service list.
Expand All @@ -17,14 +17,14 @@ describe("ServiceManager", () => {
});

test("getService should return a error if service is not registered", () => {
const serviceManager = new ServiceManager(new MockNodeCG());
const serviceManager = new ServiceManager(mockNodeCG());

const result = serviceManager.getService("someInvalidServiceType");
expect(result.failed).toBe(true);
});

test("getService should return success if service is registered", () => {
const serviceManager = new ServiceManager(new MockNodeCG());
const serviceManager = new ServiceManager(mockNodeCG());
serviceManager.registerService(testService);

const result = serviceManager.getService(testService.serviceType);
Expand Down
4 changes: 2 additions & 2 deletions nodecg-io-core/extension/bundleManager.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { NodeCG } from "nodecg-types/types/server";
import NodeCG from "@nodecg/types";
import { ObjectMap, Service, ServiceDependency, ServiceInstance } from "./service";
import { emptySuccess, error, Result } from "./utils/result";
import { EventEmitter } from "events";
Expand All @@ -11,7 +11,7 @@ export class BundleManager extends EventEmitter {
// Object that maps a bundle name to the array that contains all services that this bundle depends upon
private readonly bundles: ObjectMap<ServiceDependency<unknown>[]> = {};

constructor(private readonly nodecg: NodeCG) {
constructor(private readonly nodecg: NodeCG.ServerAPI) {
super();
}

Expand Down
24 changes: 18 additions & 6 deletions nodecg-io-core/extension/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { NodeCG } from "nodecg-types/types/server";
import NodeCG from "@nodecg/types";
import { ServiceManager } from "./serviceManager";
import { BundleManager } from "./bundleManager";
import { MessageManager } from "./messageManager";
Expand All @@ -7,16 +7,28 @@ import { Service } from "./service";
import { PersistenceManager } from "./persistenceManager";
import { ServiceProvider } from "./serviceProvider";
import { Logger } from "./utils/logger";

/**
* 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.
* Contains references to all internal modules.
*/
export interface NodeCGIOCore {
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;
}

module.exports = (nodecg: NodeCG): NodeCGIOCore => {
module.exports = (nodecg: NodeCG.ServerAPI): NodeCGIOCore => {
nodecg.log.info("Minzig!");

const serviceManager = new ServiceManager(nodecg);
Expand All @@ -40,7 +52,7 @@ module.exports = (nodecg: NodeCG): NodeCGIOCore => {
registerService<R, C>(service: Service<R, C>): void {
serviceManager.registerService(service);
},
requireService<C>(nodecg: NodeCG, serviceType: string): ServiceProvider<C> | undefined {
requireService<C>(nodecg: NodeCG.ServerAPI, serviceType: string): ServiceProvider<C> | undefined {
const bundleName = nodecg.bundleName;
const svc = serviceManager.getService(serviceType);

Expand All @@ -58,7 +70,7 @@ module.exports = (nodecg: NodeCG): NodeCGIOCore => {
};

function onExit(
nodecg: NodeCG,
nodecg: NodeCG.ServerAPI,
bundleManager: BundleManager,
instanceManager: InstanceManager,
serviceManager: ServiceManager,
Expand Down Expand Up @@ -100,7 +112,7 @@ function onExit(
}

function registerExitHandlers(
nodecg: NodeCG,
nodecg: NodeCG.ServerAPI,
bundleManager: BundleManager,
instanceManager: InstanceManager,
serviceManager: ServiceManager,
Expand Down
4 changes: 2 additions & 2 deletions nodecg-io-core/extension/instanceManager.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { NodeCG } from "nodecg-types/types/server";
import NodeCG from "@nodecg/types";
import { ObjectMap, Service, ServiceInstance } from "./service";
import { emptySuccess, error, Result } from "./utils/result";
import { ServiceManager } from "./serviceManager";
Expand All @@ -14,7 +14,7 @@ export class InstanceManager extends EventEmitter {
private ajv = new Ajv();

constructor(
private readonly nodecg: NodeCG,
private readonly nodecg: NodeCG.ServerAPI,
private readonly services: ServiceManager,
private readonly bundles: BundleManager,
) {
Expand Down
4 changes: 2 additions & 2 deletions nodecg-io-core/extension/messageManager.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { NodeCG } from "nodecg-types/types/server";
import NodeCG from "@nodecg/types";
import { emptySuccess, error, Result, success } from "./utils/result";
import { InstanceManager } from "./instanceManager";
import { BundleManager } from "./bundleManager";
Expand Down Expand Up @@ -35,7 +35,7 @@ export interface SetServiceDependencyMessage extends PasswordMessage {
*/
export class MessageManager {
constructor(
private nodecg: NodeCG,
private nodecg: NodeCG.ServerAPI,
private services: ServiceManager,
private instances: InstanceManager,
private bundles: BundleManager,
Expand Down
Loading