Move exp_reports to ES modules and declare its permissions

Same changes the other packages got in #475 and #476. The permissions move
from definePermission calls into the plugin declaration, with the
Permissions augmentation in messages.ts so the web build sees it.
"type": "module", .js entrypoints and imports, webpack.config.cjs, and the
tap tests use import with --type-strip-only.

The web plugin now uses a default export function instead of the
deprecated BaseWebPlugin class, like exp_roles.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
bbassieandClaude Opus 5.5 committed 2026-09-26 23:22:18 +00:00
1 parent 9ddb7b362b
commit 12ac7052a1
11 files changed
+91 -83

No files matched your search

+1 -1
View File
@@ -1,6 +1,6 @@
import { BaseControllerPlugin } from "@clusterio/controller";
import * as lib from "@clusterio/lib";
import * as messages from "./messages";
import * as messages from "./messages.js";
import * as path from "node:path";
export class ControllerPlugin extends BaseControllerPlugin {
+36 -34
View File
@@ -1,5 +1,5 @@
import * as lib from "@clusterio/lib";
import * as messages from "./messages";
import * as messages from "./messages.js";
declare module "@clusterio/lib" {
export interface ControllerConfigFields {
@@ -8,37 +8,6 @@ declare module "@clusterio/lib" {
}
}
lib.definePermission({
name: "exp_reports.report.get",
title: "Get Reports",
description: "Retrieve a specific report by id.",
grantByDefault: true,
});
lib.definePermission({
name: "exp_reports.report.list",
title: "List Reports",
description: "List the reports against every player, or against one player.",
grantByDefault: true,
});
lib.definePermission({
name: "exp_reports.report.subscribe",
title: "Subscribe to Report Updates",
description: "Receive updates when reports are made or deleted.",
grantByDefault: true,
});
lib.definePermission({
name: "exp_reports.report.create",
title: "Create Reports",
description: "Report a player.",
grantByDefault: false,
});
lib.definePermission({
name: "exp_reports.report.delete",
title: "Delete Reports",
description: "Delete reports made against a player.",
grantByDefault: false,
});
export const plugin: lib.PluginDeclaration = {
name: "exp_reports",
title: "ExpGaming - Reports",
@@ -58,9 +27,42 @@ export const plugin: lib.PluginDeclaration = {
messages.ReportDeleteRequest,
],
instanceEntrypoint: "./dist/node/instance",
permissions: [
{
name: "exp_reports.report.get",
title: "Get Reports",
description: "Retrieve a specific report by id.",
grantByDefault: true,
},
{
name: "exp_reports.report.list",
title: "List Reports",
description: "List the reports against every player, or against one player.",
grantByDefault: true,
},
{
name: "exp_reports.report.subscribe",
title: "Subscribe to Report Updates",
description: "Receive updates when reports are made or deleted.",
grantByDefault: true,
},
{
name: "exp_reports.report.create",
title: "Create Reports",
description: "Report a player.",
grantByDefault: false,
},
{
name: "exp_reports.report.delete",
title: "Delete Reports",
description: "Delete reports made against a player.",
grantByDefault: false,
},
],
controllerEntrypoint: "./dist/node/controller",
instanceEntrypoint: "./dist/node/instance.js",
controllerEntrypoint: "./dist/node/controller.js",
controllerConfigFields: {
"exp_reports.discord_webhook_url": {
title: "Discord Webhook URL",
+1 -1
View File
@@ -1,5 +1,5 @@
import { BaseInstancePlugin } from "@clusterio/host";
import * as messages from "./messages";
import * as messages from "./messages.js";
/** Sent by the lua side when a player reports another. */
export type IpcReportCreate = {
+10
View File
@@ -1,6 +1,16 @@
import * as lib from "@clusterio/lib";
import { Type, Static } from "@sinclair/typebox";
declare module "@clusterio/lib" {
export interface Permissions {
"exp_reports.report.get": never;
"exp_reports.report.list": never;
"exp_reports.report.subscribe": never;
"exp_reports.report.create": never;
"exp_reports.report.delete": never;
}
}
/*
Data records
*/
+2 -1
View File
@@ -5,10 +5,11 @@
"author": "Cooldude2606 <https://github.com/Cooldude2606>",
"license": "MIT",
"repository": "explosivegaming/ExpCluster",
"type": "module",
"main": "dist/node/index.js",
"scripts": {
"prepare": "tsc --build && webpack-cli --env production",
"test": "tap --disable-coverage --allow-empty-coverage test/*.test.js",
"test": "tap --type-strip-only --disable-coverage --allow-empty-coverage test/*.test.js",
"coverage": "tap --coverage-report=text test/*.test.js"
},
"engines": {
+6 -7
View File
@@ -1,10 +1,9 @@
"use strict";
const t = require("tap");
const lib = require("@clusterio/lib");
const { Controller, InstanceRecord } = require("@clusterio/controller");
const { ControllerPlugin } = require("../dist/node/controller");
const messages = require("../dist/node/messages");
const { plugin: pluginDeclaration } = require("../dist/node/index");
import t from "tap";
import * as lib from "@clusterio/lib";
import { Controller, InstanceRecord } from "@clusterio/controller";
import { ControllerPlugin } from "../dist/node/controller.js";
import * as messages from "../dist/node/messages.js";
import { plugin as pluginDeclaration } from "../dist/node/index.js";
// The controller validates message classes against the link registry, and the
// plugin's config fields must be defined before a ControllerConfig can set them
+6 -7
View File
@@ -1,10 +1,9 @@
"use strict";
const t = require("tap");
const lib = require("@clusterio/lib");
const { Instance } = require("@clusterio/host");
const { InstancePlugin } = require("../dist/node/instance");
const { plugin: pluginDeclaration } = require("../dist/node/index");
const messages = require("../dist/node/messages");
import t from "tap";
import * as lib from "@clusterio/lib";
import { Instance } from "@clusterio/host";
import { InstancePlugin } from "../dist/node/instance.js";
import { plugin as pluginDeclaration } from "../dist/node/index.js";
import * as messages from "../dist/node/messages.js";
// The instance validates message classes against the link registry
for (const Message of pluginDeclaration.messages) {
+3 -4
View File
@@ -1,7 +1,6 @@
"use strict";
const t = require("tap");
const messages = require("../dist/node/messages");
const { testMatrix, testRoundTripJsonSerialisable } = require("../../test/common");
import t from "tap";
import * as messages from "../dist/node/messages.js";
import { testMatrix, testRoundTripJsonSerialisable } from "../../test/common.js";
const sampleReport = new messages.ReportRecord(7, "bob", "alice", "griefing", "EXP", 12345);
+5 -6
View File
@@ -1,14 +1,13 @@
"use strict";
const path = require("node:path");
const t = require("tap");
const { reportLuaTests } = require("../../test/lua/runner");
import path from "node:path";
import t from "tap";
import { reportLuaTests } from "../../test/lua/runner.js";
const envFile = path.join(__dirname, "module", "env.lua");
const envFile = path.join(import.meta.dirname, "module", "env.lua");
// Each file runs in its own lua state, and each test in a fresh environment
t.test("control.lua", t2 => {
for (const file of ["requests.lua", "receivers.lua"]) {
t2.test(file, t3 => reportLuaTests(t3, envFile, path.join(__dirname, "module", file)));
t2.test(file, t3 => reportLuaTests(t3, envFile, path.join(import.meta.dirname, "module", file)));
}
t2.end();
});
+3 -4
View File
@@ -6,8 +6,8 @@ import {
useColumnSearch, useTableQueryState,
} from "@clusterio/web_ui";
import { ReportCreateRequest, ReportDeleteRequest, ReportRecord } from "../../messages";
import type { WebPlugin } from "..";
import { ReportCreateRequest, ReportDeleteRequest, ReportRecord } from "../../messages.js";
import { useReports } from "..";
const strcmp = new Intl.Collator(undefined, { numeric: true, sensitivity: "base" }).compare;
@@ -50,8 +50,7 @@ function CreateReportButton() {
function ReportsTable() {
const control = useContext(ControlContext);
const account = useAccount();
const plugin = control.plugins.get("exp_reports") as WebPlugin;
const [reports, synced] = plugin.useReports();
const [reports, synced] = useReports();
const tableState = useTableQueryState<ReportRecord>({
namespace: "report",
+18 -18
View File
@@ -1,27 +1,27 @@
import React, { useCallback, useSyncExternalStore } from "react";
import { BaseWebPlugin } from "@clusterio/web_ui";
import { WebPluginContext } from "@clusterio/web_ui";
import * as lib from "@clusterio/lib";
import * as messages from "../messages";
import * as messages from "../messages.js";
import ReportsPage from "./components/ReportsPage";
export class WebPlugin extends BaseWebPlugin {
reports = new lib.MapSubscriber(messages.ReportUpdatedEvent, this.control);
let reports: lib.MapSubscriber<messages.ReportUpdatedEvent> | undefined;
async init() {
this.pages = [
{
path: "/reports",
sidebarName: "Reports",
permission: "exp_reports.report.list",
content: <ReportsPage />,
},
];
}
export function useReports() {
const subscribe = useCallback((cb: () => void) => reports!.subscribe(cb), []);
return useSyncExternalStore(subscribe, () => reports!.getSnapshot());
}
useReports() {
const subscribe = useCallback((cb: () => void) => this.reports.subscribe(cb), []);
return useSyncExternalStore(subscribe, () => this.reports.getSnapshot());
}
export default function registerPlugin(context: WebPluginContext) {
context.control.hooks.pages.attach("exp_reports", () => [
{
path: "/reports",
sidebarName: "Reports",
permission: "exp_reports.report.list",
content: <ReportsPage />,
},
]);
reports = new lib.MapSubscriber(messages.ReportUpdatedEvent, context.control);
}