Compare commits

...
Author SHA1 Message Date
Hornwitser 5f85c0575e v2.0.0-alpha.17 2024-02-12 17:54:48 +01:00
Hornwitser cf2b3db20d Add thank you note to contributors of Alpha 17 2024-02-12 17:53:16 +01:00
Hornwitser a9c6430531 Document current publishing workflow
Docement the adaptations I've made to the publishing procedure which
uses a less dangerous publish command and removes the dist folders
before publishing to avoid accidental upload of stale build files.
2024-02-12 17:53:13 +01:00
Hornwitser 8a580a66be Add breaking change note for 535e43b 2024-02-12 17:37:27 +01:00
Hornwitser 8ae5e11021 Add changelog for #587 2024-02-12 17:33:13 +01:00
Hornwitser fac04adc6f Add known issue about the base mod 2024-02-12 17:29:51 +01:00
Hornwitser d8f1d8a3e2 Add known issue about Factorio migrations 2024-02-12 17:25:10 +01:00
Hornwitser abcbf3fddc Add pagination to saves list (#592)
Prevent the saves list from pushing the console and config off the
screen by turning on the pagination for it.
2024-02-12 17:24:00 +01:00
Cooldude2606 e5a4d51c86 Merge pull request #590 from Hornwitser/bugfixes-3
Bugfixes 3
2024-02-11 23:01:43 +00:00
Hornwitser 97e615fc21 Merge pull request #584 from Hornwitser/build-rework
Build rework
2024-02-11 22:14:35 +01:00
HornwitserandCooldude2606 e990134b70 Use suggested wording in docs/contributing.md
Co-authored-by: Cooldude2606 <25043174+Cooldude2606@users.noreply.github.com>
2024-02-11 21:40:32 +01:00
Hornwitser c2af8a2377 Merge pull request #587 from Danielv123/make-resource-references-clickable
Make resource references clickable
2024-02-11 16:58:50 +01:00
Hornwitser c9609259db Ignore error loading mod in fetchMod
If a bad download or file transfer causes a mod in a host's mod folder
to become corrupt the attempt to load the mod will throw an error that's
not the file not existing.  This in turn causes starting the instance to
always fail as the mod is never attempted to be re-downloaded.

Fix by ignoring errors attempting to load mods already present in the
mods folder that aren't in the mod store so that a new download is
attempted after that fails.
2024-02-11 15:48:45 +01:00
Danielv123 3cddd2b9f8 Export Link component for use by plugins 2024-02-10 16:33:22 +03:00
Danielv123 8879f8dd80 Replaced react Link with custom component
The custom component merges the behaviour of antd Typography.Link and the react-router.dom Link after the custom component prop was removed in v6 according to this guide: https://reactrouter.com/en/main/upgrading/v5#remove-link-component-prop
2024-02-09 09:11:53 +03:00
Danielv123 4aca5050b9 Allow sending instance management messages from controller plugins 2024-02-09 07:00:05 +03:00
Danielv123 89919f7a89 Fix inconsistent indentation 2024-02-09 06:49:17 +03:00
Danielv123 e2da9ac03d Make role tag in users table clickable
As per issue #572
2024-02-09 06:30:43 +03:00
Hornwitser e8d9eb6916 Convert prototype icons to array during export
It's possible to set the .icons to an arbitrary key value mapping
instead of an array and the game will still accept it.  Convert the
icons to an array if it's an object mapping to handle this case.

Note that this doesn't preserve the order the game reads the keys in
because JavaScript reorders numeric key indexes on objects differently
to how the game orders them.
2024-02-08 21:18:48 +01:00
Hornwitser 8865509011 Delete player statistics when deleting instances
Clear the statistics stored per user for the instance when an instance
is deleted.
2024-02-08 19:06:21 +01:00
Danielv123 bcda88710e Make host name in instance list clickable
As per issue #572
2024-02-08 20:50:14 +03:00
Danielv123 d6b0e88dda Make host name on instance page clickable 2024-02-08 20:49:33 +03:00
Hornwitser 1021e29c98 Support shorthand notation for colours
Implement the shorthand notations that Factorio recognises for colours
where individual components may be left out or the colour specified as
an array of values instead of an object.
2024-02-08 18:42:38 +01:00
Hornwitser 96082e32b7 Replace deprecated usage of Select bordered 2024-02-08 15:25:06 +01:00
Hornwitser f70b4f74b1 Add watch script to rebuild on changes 2024-02-08 05:23:48 +01:00
Hornwitser ebb93ada94 Replace babel and ts-loader with swc
Build and minify the Web UI using swc instead of babel, tsc and terser.
This results in a significant speedup of the build process as it skips
checking types and uses native code to do the minification.

To prevent accidental type errors being introduced the TypeScript
compiler is still used to perform type checking on the Web UI files.

Due to peer dependency resolving shenanigans the swc/core package had to
be added to the workspace, controller and web_ui to avoid pnpm creating
a duplicate install of webpack that break the build.
2024-02-08 04:57:19 +01:00
Hornwitser 597d080d71 Add tsconfig.json that references both builds
Add back unqualified tsconfig.json files to each package that references
both the Node.js build and the browser build.  This makes tsc --build
compile both builds when the variant to do is not specified.
2024-02-08 03:20:26 +01:00
Hornwitser e4f90b3480 Move tsconfig.json to tsconfig.node.json
Distinguish the Node.js build in the same way the browser build is with
the .node suffix to the tsconfig files it uses.  This means
tsconfig.node outputs to dist/node and tsconfig.browser outputs to
dist/browser.
2024-02-08 03:20:26 +01:00
Hornwitser 8c4d2db192 Move TypeScript Node.js build to dist/node
Use a separate folder for the TypeScript build for Node.js in the dist
folder.  This makes each type of build consistently have its own output
folder instead of being interleaved with the Node.js build.
2024-02-08 03:20:26 +01:00
Hornwitser 152eb7d454 Move TypeScript browser build to dist/browser
Use dist/browser as the output of the browser oriented TypeScript build
of packages instead of dist/web/build.  This distinguishes it better
from the Web UI bundle which is put together with webpack.
2024-02-07 18:35:05 +01:00
Hornwitser 8e5d581b9b Move tsconfig.web.json to tsconfig.browser.json
Rename the config files for the browser build of modules to better
communicate that it's purpose is to compile code for a browser
environment rather than the Web UI in particular.  This also aligns with
the "browser" option in package.json, the existing browser.ts entrypoint
in lib, and how the browser environment is typically referred to.
2024-02-07 17:42:39 +01:00
Hornwitser 99990c7a0d Fix incorrect header level in CHANGELOG.md 2024-02-07 00:05:13 +01:00
Hornwitser ffd61b961d Merge pull request #583 from Hornwitser/bugfixes-2
Bugfixes 2
2024-02-07 00:03:05 +01:00
Hornwitser dcf216ca09 Fix research sync applying progress to wrong level
When the level of a technology changes either by setting level
directly or by setting researched to true, the saved progress of the
research is reset only if the technology is not the current research.

If during the initial synchronisation with the controller an instance
has progressed on a multi level research, it's the current research, and
the controller's level of that research is higher, then the level is set
to the higher level and the game keeps the progress.  This in turn
results in all of the progress made on the previous level being added to
the higher level.

Fix by setting the progress to zero when changing the level or
researched status of a technology that's the active research.
2024-02-06 22:43:04 +01:00
Hornwitser 6bf2bf51f2 Remove deprecated showArrow prop
Ant design has deprecated the Select's showArrow prop.  It's now always
shown by default.
2024-02-06 22:43:04 +01:00
Hornwitser 44b0a8ff83 Throw error on bad permission check in Wed UI
Throw an error if an attempt is made to check for a permission that does
not exist in the Web UI to prevent future bad permission checks going
unnoticed.
2024-02-06 22:43:04 +01:00
Hornwitser d3f8c993b1 Fix bad permission checks in Web UI
Fix attempts to check for permissions that don't exist in the Web UI
that has gone unnoticed because the check still succeeds when the
account has the cluster admin permission.
2024-02-06 22:43:04 +01:00
Hornwitser 535e43b527 Rename save.list.subscribe permission
Follow the same pattern as all the other subscribable lists by having a
separate .list and .subscribe permissions for saves.  This also fixes
the save list not being visible due to a typo in the permission in the
Web UI.
2024-02-06 22:43:04 +01:00
Hornwitser 2df67e32da Set key for top level page fragment
Without a unique per page key set react will try to reuse the existing
state and components of the previous page and update them to match the
new page.  This causes unexpected behaviour as components get turned
into into a Frankenstein abomination of the previous page and the
current one fused into one.

Fix by setting a key on the top level fragment of the page which in turn
forces React to re-render the whole page whenever the user switches to
another page.
2024-02-06 22:43:04 +01:00
Hornwitser 6b85567bc6 Provide theme to top level ErrorBoundary
If the theme is not provided then the App's ErrorBoundary will render
white text on white background, and vice versa for black.
2024-02-06 22:43:04 +01:00
Hornwitser 2e25831b4d Warn on subscription updates with same timestamp
If the value in a subscribed list is updated while the updatedAtMs
timestamp is kept the same then the caching logic of the subscription
system will cause updates to be silently dropped.  Log a warning if a
subscriber catches this happening to help tracking down such cases.
2024-02-06 22:43:04 +01:00
Hornwitser 021af793fc Handle instance status updates with port change
If an instance is reassigned its port may change without the status
changing, these updates should not be ignored.
2024-02-06 22:43:04 +01:00
Hornwitser 7ba14f7433 Fix instance details not updating on reassigmnent
Always send the instance details update when an instance is reassigned,
not just when it is unassigned.  The idea for not sending an update when
assigned to a host was that the host would send an update on the status
that in turn updates the instance details.  However this is not actually
the case; when a stopped instance is reassigned its status does not
change so the update is ignored.
2024-02-06 22:43:04 +01:00
Hornwitser 40f5a6ab7c Fix timestamp not set on clearing instance saves
Set the save's updatedAtMs timestamp when clearing the save list of a
reassigned or deleted instance.  This fixes the Web UI not updating the
saves list to remove the saves when an instance is reassigned.
2024-02-06 22:43:04 +01:00
Hornwitser 48c31b4825 Fix unhandled rejection on disconnect (#582)
If a link is closed while the prepareDisconnect action is in progress
the link will still try to signal the disconnect ready over the socket
when the prepareDisconnect action completes, which throws due to the
socket no longer being present.  This error is not caught due to the
catch happening before the finally on the promise and triggers an
unhandled rejection.

Fix by rearranging the finally to come before the catch on the promise
so that errors are always handled, and checking that the link still has
a session before attempting to send the disconnect ready signal.
2024-02-05 23:36:43 +01:00
Hornwitser 520007c8d1 Fix unhandled rejection on RCON lost (#580)
When using .finally on a promise a new promise is created which calls
the handler passed to it when the the original promise is resolved or
rejected, and resolves or rejects with same value as the original
promise.  This means if the rejection of the new promise that was
created by finally is not handled then an unhandled promise rejection
will be raised.

Fix by making sure the promise rejection from the finally promise
created in subspace_storage's handleUpdateStorageEvent is properly
handled.
2024-02-05 15:51:15 +01:00
126 changed files with 726 additions and 375 deletions

No files matched your search

+2 -2
View File
@@ -320,7 +320,7 @@ module.exports = {
"semi-style": "off",
"sort-imports": "off",
"sort-keys": "off",
"sort-vars": "error",
"sort-vars": "off",
"space-before-blocks": "error",
"space-before-function-paren": "off",
"space-in-parens": ["error", "never"],
@@ -391,7 +391,7 @@ module.exports = {
"node/process-exit-as-throw": "error",
"node/shebang": ["error", {
"convertPath": {
"*.ts": ["^(.+)\\.ts$", "dist/$1.js"],
"*.ts": ["^(.+)\\.ts$", "dist/node/$1.js"],
},
}],
},
+1
View File
@@ -0,0 +1 @@
auto-install-peers=false
+32 -1
View File
@@ -1,8 +1,39 @@
# Changelog
## Version 2.0.0-alpha.17
### Features
- Made the host an instance is assigned to link to the host page for that host in the Web UI [#587](https://github.com/clusterio/clusterio/pull/587).
- Added pagination to the saves list.
### Changes
- Fixed an unhandled error in Subspace Storage crashing the host.
- Fixed an unhandled rejection in disconnect logic crashing the host.
- Fixed instance screen in Web UI not updating when an instance was reassigned.
- Fixed an error thrown in the rendering of one page in the Web UI causing all pages to become inaccessible.
- Fixed Web UI not showing instance saves when the permission to list and subscribe the saves is granted but the user is not a cluster admin.
- Similarly fixed Web UI not showing the mod pack create, delete, list and host revoke token actions if the user is not a cluster admin.
- Fixed Research Sync applying progress of a previous techology level to the level on the controller when it's the active research during instance startup.
- Fixed data export with a mod using shorthand notation for the default value of a color-setting causing the ModPack data to be set to invalid data.
- Fixed per user per instance player stats not being deleted when the instance it's for is deleted.
- Fixed mods setting the item prototype icons property to an object mapping instead of an array breaking the data export.
- Fixed a bad mod in the host's mods folder causing instance start to fail instead of the host re-downloading the mod.
### Breaking Changes
- Renamed the permission `core.instance.save.list.subscribe` to `core.instance.save.subscribe`.
Many thanks to the following for contributing to this release:
[@Cooldude2606](https://github.com/Cooldude2606)
[@Danielv123](https://github.com/Danielv123)
[@Hornwitser](https://github.com/Hornwitser)
## Version 2.0.0-alpha.16
## Changes
### Changes
- Fixed message sequences not being renumbered when forwarded between links causing reconnects to send duplicated messages.
+4
View File
@@ -270,6 +270,10 @@ If you want to use a different user for the command line interface, you can gene
- When the `server_select` plugin is installed you may see `[error] Unhandled event server_select:UpdateInstancesEvent` logged when starting an instance.
This should be ignored as it does not indicate an error occured.
- After updating Factorio you may get the error `Error: Expected empty response but got "Cannot execute command. Error: [string "clusterio_private.update_instance(..."]:1: attempt to index global 'clusterio_private' (a nil value)?` on instance start.
This is due to Factorio applying a migration replacing the patched content of the save with the original scenario, attempting to start the instance again should resolve the issue.
- In the mod pack view of the web interface the "base" mod is displayed with an error complaining about the mod being missing from the controller storage.
This is due to the "base" mod currently not being connectly handled as a built-in mod to Factorio in the web interface, this error message should be ignored.
- After migrating from Alpha 13 to Alpha 14 running an instance data export fails with the error `Upload failed: 401 Unauthorized` due to the host token being issued to a slave.
To workaround this find the id of the host in the host config and generate a new host token with the same id.
Stop the host and replace the value of the `host.controller_token` field in `config-host.json` with the new host token before starting it up again.
+5 -2
View File
@@ -81,6 +81,9 @@ Note that to use `clusterioctl` you will have to create a control config first:
Once you've set up the cluster you can use `node packages/controller run` to run the controller and `node packages/host run` to run the host which connects to the controller.
When editing TypeScript files ran by Node.js you need to run `pnpm build` to compile the files to JavaScript before restarting the controller/host/ctl for changes take effect.
To have TypeScript watch for changes and rebuild immediadly you may run `pnpm watch` instead.
For web development on the controller there are also the following flags:
`--dev` - Recompile the `web_ui` package on the fly providing live reloading of it.
@@ -316,6 +319,6 @@ You will need commit access to the repository as well publish access to the @clu
4. Publish the packages.
```sh
# Warning: Double check your current working directory is the repository before running this
pnpm -r --filter='!./external_plugins/**' publish
rm -r ./{plugins,packages}/*/dist
pnpm -r --filter='./{plugins,packages}/*' publish
```
+1 -1
View File
@@ -78,7 +78,7 @@ import { BaseInstancePlugin } from "@clusterio/host";
Previously the info of a plugin would be loaded by requiring `./plugin_dir/info`, this has been changed to require `./plugin_dir` which in turn means that `info.js` is no longer the default entrypoint.
To solve this you can either add `"main": "info.js",` to package.json or rename `info.js` to `index.js` (the rest of this guide assumes the entrypoint was renamed to `index.js`).
The motivation behind this change was to allow TypeScript to output its build to a sub-folder of the plugin, if you're using TypeScript you'll likely have the entrypoint be located in `dist/index.js` instead.
The motivation behind this change was to allow TypeScript to output its build to a sub-folder of the plugin, if you're using TypeScript you'll likely have the entrypoint be located in `dist/node/index.js` instead.
The expected place where the declaration of a plugin is exported has also changed from `module.exports` to `module.exports.plugin`:
+2
View File
@@ -3,6 +3,7 @@
"private": true,
"scripts": {
"build": "tsc --build",
"watch": "tsc --build --watch",
"test": "pnpm run build && mocha --check-leaks -R spec --recursive test \"plugins/*/test/**\"",
"fast-test": "FAST_TEST=y pnpm test",
"cover": "nyc pnpm test",
@@ -27,6 +28,7 @@
"@clusterio/ctl": "workspace:*",
"@clusterio/host": "workspace:*",
"@clusterio/lib": "workspace:*",
"@swc/core": "^1.4.0",
"@typescript-eslint/eslint-plugin": "^6.4.1",
"@typescript-eslint/parser": "^6.4.1",
"eslint": "^8.45.0",
+1 -1
View File
@@ -1,2 +1,2 @@
webpack.config.js
dist/web/build
dist/browser
+6 -5
View File
@@ -3,14 +3,14 @@
"description": "Implementation of Clusterio controller",
"author": "Hornwitser",
"license": "MIT",
"version": "2.0.0-alpha.16",
"main": "dist/index.js",
"version": "2.0.0-alpha.17",
"main": "dist/node/index.js",
"scripts": {
"prepare": "tsc --build && webpack-cli --env production",
"test": "echo \"Error: no test specified\" && exit 1"
},
"bin": {
"clusteriocontroller": "./dist/controller.js"
"clusteriocontroller": "./dist/node/controller.js"
},
"keywords": [
"factorio"
@@ -19,7 +19,7 @@
"node": ">=18"
},
"dependencies": {
"@clusterio/lib": "^2.0.0-alpha.16",
"@clusterio/lib": "^2.0.0-alpha.17",
"@sinclair/typebox": "^0.30.4",
"busboy": "^1.6.0",
"compression": "^1.7.4",
@@ -35,7 +35,8 @@
"yargs": "^17.7.2"
},
"devDependencies": {
"@clusterio/web_ui": "^2.0.0-alpha.16",
"@clusterio/web_ui": "^2.0.0-alpha.17",
"@swc/core": "^1.4.0",
"@types/busboy": "^1.5.0",
"@types/compression": "^1.7.2",
"@types/express": "^4.17.17",
+9 -6
View File
@@ -274,7 +274,7 @@ export default class Controller {
Controller.addAppRoutes(this.app, this.pluginInfos);
if (!args.dev) {
let manifestPath = path.join(__dirname, "..", "web", "manifest.json");
let manifestPath = path.join(__dirname, "..", "..", "web", "manifest.json");
let manifest = await Controller.loadJsonObject(manifestPath);
if (!manifest["main.js"]) {
@@ -333,7 +333,7 @@ export default class Controller {
if (args.dev) {
// eslint-disable-next-line node/no-missing-require
webpackConfigs.push(require("../../webpack.config")({})); // Path outside of build
webpackConfigs.push(require("../../../webpack.config")({})); // Path outside of build
}
if (args.devPlugin) {
let devPlugins = new Map();
@@ -737,7 +737,7 @@ export default class Controller {
// Set folder to serve static content from (the website)
const staticOptions = { immutable: true, maxAge: 1000 * 86400 * 365 };
app.use("/static",
express.static(path.join(__dirname, "..", "..", "dist", "web", "static"), staticOptions)
express.static(path.join(__dirname, "..", "..", "web", "static"), staticOptions)
);
app.use("/static", express.static("static", staticOptions)); // Used for data export files
@@ -876,9 +876,11 @@ export default class Controller {
*/
clearSavesOfInstance(instanceId: number) {
const updates = [];
const now = Date.now();
for (const [id, save] of this.saves) {
if (save.instanceId === instanceId) {
save.isDeleted = true;
save.updatedAtMs = now;
updates.push(save);
this.saves.delete(id);
}
@@ -939,9 +941,9 @@ export default class Controller {
);
} else {
instance.status = "unassigned";
instance.updatedAtMs = Date.now();
this.instanceDetailsUpdated([instance]);
}
instance.updatedAtMs = Date.now();
this.instanceDetailsUpdated([instance]);
}
/**
@@ -962,6 +964,7 @@ export default class Controller {
this.instancesDirty = true;
this.clearSavesOfInstance(instanceId);
this.userManager.clearStatsOfInstance(instanceId);
let prev = instance.status;
instance.status = "deleted";
@@ -1224,7 +1227,7 @@ export default class Controller {
mainBundle = stats.toJson().assetsByChunkName["main"];
}
fs.readFile(path.join(__dirname, "..", "..", "web", "index.html"), "utf8").then((content) => {
fs.readFile(path.join(__dirname, "..", "..", "..", "web", "index.html"), "utf8").then((content) => {
res.type("text/html");
res.send(content
.replace(/__CLUSTER_NAME__/g, res.app.locals.controller.config.get("controller.name"))
+4 -1
View File
@@ -178,7 +178,10 @@ export default class HostConnection extends BaseConnection {
// changed. Hosts also send status updates on assignInstance which
// for hacky reason is also used to push config changes and
// restablish status after a connection loss.
if (instance.status === request.status) {
if (
instance.status === request.status
&& instance.gamePort === request.gamePort
) {
return;
}
+9
View File
@@ -113,4 +113,13 @@ export default class UserManager {
this.onlineUsers.delete(user);
}
}
clearStatsOfInstance(instanceId: number) {
for (const user of this.users.values()) {
this.notifyLeave(user, instanceId);
user.instanceStats.delete(instanceId);
user.recalculatePlayerStats();
}
this.dirty = true;
}
}
+12
View File
@@ -0,0 +1,12 @@
{
"extends": "../../tsconfig.browser.json",
"references": [
{ "path": "../lib/tsconfig.browser.json" },
{ "path": "../web_ui/tsconfig.browser.json" },
],
"include": [ "web/**/*.tsx", "web/**/*.ts", "package.json" ],
"compilerOptions": {
"rootDir": "..",
"outDir": "dist/browser",
}
}
+3 -6
View File
@@ -1,10 +1,7 @@
{
"extends": "../../tsconfig.node.json",
"files": [],
"references": [
{ "path": "../lib" },
{ "path": "./tsconfig.node.json" },
{ "path": "./tsconfig.browser.json" },
],
"include": ["index.ts", "controller.ts", "package.json", "src/**/*.ts"],
"compilerOptions": {
"outDir": "dist",
},
}
+10
View File
@@ -0,0 +1,10 @@
{
"extends": "../../tsconfig.node.json",
"references": [
{ "path": "../lib/tsconfig.node.json" },
],
"include": ["index.ts", "controller.ts", "package.json", "src/**/*.ts"],
"compilerOptions": {
"outDir": "dist/node",
},
}
-12
View File
@@ -1,12 +0,0 @@
{
"extends": "../../tsconfig.web.json",
"references": [
{ "path": "../lib/tsconfig.web.json" },
{ "path": "../web_ui/tsconfig.web.json" },
],
"include": [ "web/**/*.tsx", "web/**/*.ts", "package.json" ],
"compilerOptions": {
"rootDir": "..",
"outDir": "dist/web/build",
}
}
+1 -1
View File
@@ -3,7 +3,7 @@
"description": "Installer for clusterio",
"author": "Hornwitser",
"license": "MIT",
"version": "2.0.0-alpha.16",
"version": "2.0.0-alpha.17",
"main": "create.js",
"bin": {
"clusteriocreate": "./create.js"
+4 -4
View File
@@ -3,14 +3,14 @@
"description": "Clusterio command line management tool",
"author": "Hornwitser",
"license": "MIT",
"version": "2.0.0-alpha.16",
"main": "dist/index.js",
"version": "2.0.0-alpha.17",
"main": "dist/node/index.js",
"scripts": {
"prepare": "tsc --build",
"test": "echo \"Error: no test specified\" && exit 1"
},
"bin": {
"clusterioctl": "./dist/ctl.js"
"clusterioctl": "./dist/node/ctl.js"
},
"keywords": [
"factorio"
@@ -19,7 +19,7 @@
"node": ">=18"
},
"dependencies": {
"@clusterio/lib": "^2.0.0-alpha.16",
"@clusterio/lib": "^2.0.0-alpha.17",
"as-table": "^1.0.55",
"fs-extra": "^11.1.1",
"phin": "^3.7.0",
+2 -6
View File
@@ -1,10 +1,6 @@
{
"extends": "../../tsconfig.node.json",
"files": [],
"references": [
{ "path": "../lib" },
{ "path": "./tsconfig.node.json" },
],
"include": ["index.ts", "package.json", "ctl.ts", "src/**/*.ts"],
"compilerOptions": {
"outDir": "dist",
},
}
+10
View File
@@ -0,0 +1,10 @@
{
"extends": "../../tsconfig.node.json",
"references": [
{ "path": "../lib/tsconfig.node.json" },
],
"include": ["index.ts", "package.json", "ctl.ts", "src/**/*.ts"],
"compilerOptions": {
"outDir": "dist/node",
},
}
+4 -4
View File
@@ -3,14 +3,14 @@
"description": "Implementation of Clusterio host server",
"author": "Hornwitser",
"license": "MIT",
"version": "2.0.0-alpha.16",
"main": "dist/index.js",
"version": "2.0.0-alpha.17",
"main": "dist/node/index.js",
"scripts": {
"prepare": "tsc --build",
"test": "echo \"Error: no test specified\" && exit 1"
},
"bin": {
"clusteriohost": "./dist/host.js"
"clusteriohost": "./dist/node/host.js"
},
"keywords": [
"factorio"
@@ -19,7 +19,7 @@
"node": ">=18"
},
"dependencies": {
"@clusterio/lib": "^2.0.0-alpha.16",
"@clusterio/lib": "^2.0.0-alpha.17",
"@sinclair/typebox": "^0.30.4",
"fs-extra": "^11.1.1",
"jimp": "^0.22.8",
+2 -1
View File
@@ -600,8 +600,9 @@ export default class Host extends lib.Link {
try {
modInfo = await this.modStore.loadMod(mod.name, mod.version);
} catch (err: any) {
// A broken zip file may have been left in the mods folder
if (err.code !== "ENOENT") {
throw err;
logger.error(`Error loading mod ${mod.name} ${mod.version}: ${err.message}`);
}
}
if (modInfo && (!mod.sha1 || mod.sha1 === modInfo.sha1)) {
+1 -1
View File
@@ -800,7 +800,7 @@ rcon.print(game.table_to_json(players))`.replace(/\r?\n/g, " ");
// Find stand alone modules to load
// XXX for now only the included clusterio module is loaded
let modulesDirectory = path.join(__dirname, "..", "..", "modules");
let modulesDirectory = path.join(__dirname, "..", "..", "..", "modules");
for (let entry of await fs.readdir(modulesDirectory, { withFileTypes: true })) {
if (entry.isDirectory()) {
if (modules.has(entry.name)) {
+19 -13
View File
@@ -5,7 +5,7 @@ import Jimp from "jimp";
import JSZip from "jszip";
import * as lib from "@clusterio/lib";
import * as libBuildMod from "@clusterio/lib/dist/build_mod";
import * as libBuildMod from "@clusterio/lib/dist/node/build_mod";
import type { FactorioServer } from "./server";
interface Prototype {
@@ -18,11 +18,10 @@ interface SimpleIconSpecification {
icon_size: number;
icon_mipmaps?: number;
}
type Color = { r: number, g: number, b: number, a: number };
interface IconLayer {
icon: string;
icon_size: number;
tint?: Color,
tint?: lib.FactorioColor,
shift?: [number, number],
scale?: number,
icon_mipmaps?: number,
@@ -62,7 +61,7 @@ async function generateExportMod(server: FactorioServer) {
clean: false,
build: true,
pack: true,
sourceDir: path.join(__dirname, "..", "..", "lua", "export"),
sourceDir: path.join(__dirname, "..", "..", "..", "lua", "export"),
outputDir: server.writePath("mods"),
bumpPatch: false,
factorioVersion: server.version.replace(/\.\d+$/, ""),
@@ -202,15 +201,9 @@ async function loadLayeredIcon(
iconLayer = iconLayer.clone();
let tint: Color;
let tint: lib.Color;
if (layer.tint) {
let divisor = (layer.tint.r > 1 || layer.tint.g > 1 || layer.tint.b > 1) ? 255 : 1;
tint = {
r: (layer.tint.r || 0) / divisor,
g: (layer.tint.g || 0) / divisor,
b: (layer.tint.b || 0) / divisor,
a: layer.tint.a || 1,
};
tint = lib.normalizeColor(layer.tint);
} else {
tint = { r: 1, b: 1, g: 1, a: 1 };
}
@@ -252,6 +245,18 @@ async function loadLayeredIcon(
return icon;
}
function fixIcons(item: ItemPrototype) {
const icons = item.icons;
if (typeof icons === "object" && !(icons instanceof Array) && icons !== null) {
// It's possible to specify icons as an object with arbitrary keys
// and the game will still accept it, cast the value to array if
// this is the case.
item = { ...item };
item.icons = Object.values(icons);
}
return item;
}
const itemTypes = new Set([
"item",
"ammo",
@@ -284,7 +289,8 @@ const itemTypes = new Set([
function filterItems(prototypes: Prototypes): ItemPrototype[] {
return Object.entries(prototypes)
.filter(([type, _]) => itemTypes.has(type))
.flatMap(([_, typePrototypes]) => Object.values(typePrototypes)) as ItemPrototype[]
.flatMap(([_, typePrototypes]) => Object.values(typePrototypes) as ItemPrototype[])
.map(fixIcons)
;
}
+2 -6
View File
@@ -1,10 +1,6 @@
{
"extends": "../../tsconfig.node.json",
"files": [],
"references": [
{ "path": "../lib" },
{ "path": "./tsconfig.node.json" },
],
"include": ["index.ts", "host.ts", "package.json", "src/**/*.ts", "src/**/*.js"],
"compilerOptions": {
"outDir": "dist",
},
}
+10
View File
@@ -0,0 +1,10 @@
{
"extends": "../../tsconfig.node.json",
"references": [
{ "path": "../lib/tsconfig.node.json" },
],
"include": ["index.ts", "host.ts", "package.json", "src/**/*.ts", "src/**/*.js"],
"compilerOptions": {
"outDir": "dist/node",
},
}
+1 -1
View File
@@ -1 +1 @@
dist/web/build
dist/browser
+1
View File
@@ -6,6 +6,7 @@ export * from "./src/factorio/exchange_string";
export * from "./src/helpers";
export * from "./src/ini";
export * from "./src/link";
export * from "./src/lua_tools";
export * from "./src/logging";
export * from "./src/permissions";
export * from "./src/plugin";
+2 -2
View File
@@ -1,14 +1,14 @@
{
"name": "@clusterio/lib",
"description": "Shared library for Clusterio",
"version": "2.0.0-alpha.16",
"version": "2.0.0-alpha.17",
"repository": "https://github.com/clusterio/clusterio",
"license": "MIT",
"scripts": {
"prepare": "tsc --build && pnpm run compile_validator",
"compile_validator": "node scripts/compile_validator.js"
},
"main": "dist/index.js",
"main": "dist/node/index.js",
"browser": "browser.ts",
"keywords": [
"factorio"
+3 -3
View File
@@ -7,9 +7,9 @@ const standaloneCode = require("ajv/dist/standalone").default;
/* eslint-disable node/no-sync */
// Provide an empty file for message_core to load
fs.outputFileSync(path.join(__dirname, "../dist/src/data/message_validate.js"), "");
fs.outputFileSync(path.join(__dirname, "../dist/node/src/data/message_validate.js"), "");
const { Message } = require("../dist/src/data/messages_core");
const { Message } = require("../dist/node/src/data/messages_core");
const ajv = new Ajv({
allowUnionTypes: true,
@@ -28,5 +28,5 @@ function write(output) {
`// generated by scripts/compile_validator.js\n// @ts-nocheck\n/* eslint-disable */\n${moduleCode}`
);
}
write("../dist/src/data/message_validate.js");
write("../dist/node/src/data/message_validate.js");
write("../src/data/message_validate.js");
+6 -1
View File
@@ -2,6 +2,7 @@ import zlib from "zlib";
import { Type, Static } from "@sinclair/typebox";
import * as libSchema from "../schema";
import * as libLuaTools from "../lua_tools";
import ExportManifest from "./ExportManifest";
import { integerFactorioVersion } from "./version";
@@ -363,7 +364,11 @@ export default class ModPack {
continue;
}
this.settings[settingType].set(prototype.name, { value: prototype.default_value });
let value = prototype.default_value;
if (prototype.type === "color-setting") {
value = libLuaTools.normalizeColor(value);
}
this.settings[settingType].set(prototype.name, { value });
}
}
}
+9 -1
View File
@@ -19,12 +19,20 @@ export default class Role {
permissions: Type.Array(Type.String()),
});
static remapPermission(permission: string) {
// migrate: core.instance.save.list.subscribe was renamed in alpha 17
if (permission === "core.instance.save.list.subscribe") {
return "core.instance.save.subscribe";
}
return permission;
}
static fromJSON(json: Static<typeof this.jsonSchema>) {
return new this(
json.id,
json.name,
json.description,
new Set(json.permissions),
new Set(json.permissions.map(this.remapPermission)),
);
}
+8 -8
View File
@@ -361,7 +361,7 @@ export class InstanceSaveDetailsUpdatesEvent {
static type = "event" as const;
static src = ["instance", "host", "controller"] as const;
static dst = ["controller", "control"] as const;
static permission = "core.instance.save.list.subscribe" as const;
static permission = "core.instance.save.subscribe" as const;
constructor(
public updates: SaveDetails[],
@@ -385,7 +385,7 @@ export class InstanceSaveDetailsUpdatesEvent {
export class InstanceCreateSaveRequest {
declare ["constructor"]: typeof InstanceCreateSaveRequest;
static type = "request" as const;
static src = "control" as const;
static src = ["control", "controller"] as const;
static dst = "instance" as const;
static permission = "core.instance.save.create" as const;
@@ -596,7 +596,7 @@ export class InstancePushSaveRequest {
export class InstanceLoadScenarioRequest {
declare ["constructor"]: typeof InstanceLoadScenarioRequest;
static type = "request" as const;
static src = "control" as const;
static src = ["control", "controller"] as const;
static dst = "instance" as const;
static permission = "core.instance.load_scenario" as const;
@@ -622,7 +622,7 @@ export class InstanceLoadScenarioRequest {
export class InstanceExportDataRequest {
declare ["constructor"]: typeof InstanceExportDataRequest;
static type = "request" as const;
static src = "control" as const;
static src = ["control", "controller"] as const;
static dst = "instance" as const;
static permission = "core.instance.export_data" as const;
}
@@ -630,7 +630,7 @@ export class InstanceExportDataRequest {
export class InstanceExtractPlayersRequest {
declare ["constructor"]: typeof InstanceExtractPlayersRequest;
static type = "request" as const;
static src = "control" as const;
static src = ["control", "controller"] as const;
static dst = "instance" as const;
static permission = "core.instance.extract_players" as const;
}
@@ -638,7 +638,7 @@ export class InstanceExtractPlayersRequest {
export class InstanceStopRequest {
declare ["constructor"]: typeof InstanceStopRequest;
static type = "request" as const;
static src = "control" as const;
static src = ["control", "controller"] as const;
static dst = "instance" as const;
static permission = "core.instance.stop" as const;
}
@@ -646,7 +646,7 @@ export class InstanceStopRequest {
export class InstanceKillRequest {
declare ["constructor"]: typeof InstanceKillRequest;
static type = "request" as const;
static src = "control" as const;
static src = ["control", "controller"] as const;
static dst = "instance" as const;
static permission = "core.instance.kill" as const;
}
@@ -693,7 +693,7 @@ export class InstanceDeleteInternalRequest {
export class InstanceSendRconRequest {
declare ["constructor"]: typeof InstanceSendRconRequest;
static type = "request" as const;
static src = "control" as const;
static src = ["control", "controller"] as const;
static dst = "instance" as const;
static permission = "core.instance.send_rcon" as const;
+8 -9
View File
@@ -126,16 +126,15 @@ export class Link {
});
connector.on("disconnectPrepare", () => {
this.prepareDisconnect().catch(
err => {
logger.error(`Unexpected error preparing disconnect:\n${err.stack}`);
this.prepareDisconnect().finally(() => {
// Only WebSocket connectors issue disconnection events.
const webSocketConnector = this.connector as WebSocketBaseConnector;
if (webSocketConnector.hasSession) {
webSocketConnector.sendDisconnectReady();
}
).finally(
() => {
// Only WebSocket connectors issue disconnection events.
(this.connector as WebSocketBaseConnector).sendDisconnectReady();
},
);
}).catch(err => {
logger.error(`Unexpected error preparing disconnect:\n${err.stack}`);
});
});
connector.on("invalidate", () => {
+48
View File
@@ -3,6 +3,54 @@
* @module lib/lua_tools
*/
/**
* A colour as it may originate from Factorio Lua code, see
* https://lua-api.factorio.com/latest/types/Color.html
*/
export type FactorioColor = { r?: number, g?: number, b?: number, a?: number } | [number, number, number, number?];
export type Color = { r: number, g: number, b: number, a: number };
/**
* Normalise a color as it may appear in Factorio Lua code to be an object
* with r, g, b, a properties in the range 0 to 1.
*
* @param color -
* A colour that may be specified with left out components or as an
* array and have the range 1 to 0 or 255 to 0.
* @returns
* Normalised colour where all 4 components are present as properties in
* the range 0 to 1.
*/
export function normalizeColor(color: FactorioColor) {
let r, g, b, a;
if (color instanceof Array) {
([r, g, b, a] = color);
} else {
({ r, g, b, a } = color);
}
r ??= NaN;
g ??= NaN;
b ??= NaN;
a ??= NaN;
// Note NaN > 1 is false and NaN / 255 is NaN
if (r > 1 || g > 1 || b > 1 || a > 1) {
r /= 255;
g /= 255;
b /= 255;
a /= 255;
}
return {
r: Number.isNaN(r) ? 0 : r,
g: Number.isNaN(g) ? 0 : g,
b: Number.isNaN(b) ? 0 : b,
a: Number.isNaN(a) ? 1 : a,
};
}
/**
* Escapes a string for inclusion into a lua string
*
+3 -3
View File
@@ -239,11 +239,11 @@ definePermission({
definePermission({
name: "core.instance.save.list",
title: "List saves",
description: "List the saves currently on the instance.",
description: "List the saves currently on instances.",
});
definePermission({
name: "core.instance.save.list.subscribe",
title: "Subscribe to save list updatess",
name: "core.instance.save.subscribe",
title: "Subscribe to save updates",
description: "Subscribe to be notifed on updates to the list of saves of instances.",
});
definePermission({
+9
View File
@@ -2,6 +2,7 @@ import { Type, Static } from "@sinclair/typebox";
import { Link, Event, EventClass, RequestHandler, WebSocketClientConnector, WebSocketBaseConnector } from "./link";
import { logger } from "./logging";
import { Address, MessageRequest, IControllerUser } from "./data";
import isDeepStrictEqual from "./is_deep_strict_equal";
export type SubscriptionRequestHandler<T> = RequestHandler<SubscriptionRequest, Event<T> | null>;
export type EventSubscriberCallback<T> = (updates: T[], synced: boolean) => void
@@ -204,6 +205,14 @@ export class EventSubscriber<
async _handle(event: EventSubscribable<T, V>) {
for (const value of event.updates) {
this.lastResponseTimeMs = Math.max(this.lastResponseTimeMs, value.updatedAtMs);
// Warn about updates which changes content but don't update updatedAtMs
const existing = this.values.get(value.id as K);
if (existing && existing.updatedAtMs === value.updatedAtMs && !isDeepStrictEqual(existing, value)) {
logger.warn(
`${this.Event.name} contains update for value with id ${value.id} that has an identical ` +
"updatedAtMs timestamp but differing content"
);
}
if (value.isDeleted) {
this.values.delete(value.id as K);
} else {
@@ -1,5 +1,5 @@
{
"extends": "../../tsconfig.web.json",
"extends": "../../tsconfig.browser.json",
"include": [
"browser.ts",
"package.json",
@@ -12,6 +12,7 @@
"./src/hash.ts",
"./src/ini.ts",
"./src/link/*.ts",
"./src/lua_tools.ts",
"./src/logging.ts",
"./src/permissions.ts",
"./src/prometheus.ts",
@@ -25,6 +26,6 @@
],
"compilerOptions": {
"rootDir": ".",
"outDir": "dist/web/build",
"outDir": "dist/browser",
}
}
+5 -6
View File
@@ -1,8 +1,7 @@
{
"extends": "../../tsconfig.node.json",
"include": ["index.ts", "browser.ts", "build_mod.js", "src/**/*.ts", "src/**/*.js"],
"compilerOptions": {
"outDir": "dist",
"allowJs": true,
},
"files": [],
"references": [
{ "path": "./tsconfig.node.json" },
{ "path": "./tsconfig.browser.json" },
],
}
+8
View File
@@ -0,0 +1,8 @@
{
"extends": "../../tsconfig.node.json",
"include": ["index.ts", "browser.ts", "build_mod.js", "src/**/*.ts", "src/**/*.js"],
"compilerOptions": {
"outDir": "dist/node",
"allowJs": true,
},
}
+5 -7
View File
@@ -3,9 +3,9 @@
"description": "Clusterio web interface implementation",
"author": "Hornwitser",
"license": "MIT",
"version": "2.0.0-alpha.16",
"version": "2.0.0-alpha.17",
"browser": "src/index.ts",
"main": "dist/src/index.js",
"main": "dist/browser/src/index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
@@ -17,12 +17,10 @@
},
"dependencies": {
"@ant-design/icons": "^5.1.4",
"@babel/core": "^7.22.9",
"@babel/preset-react": "^7.22.5",
"@clusterio/lib": "^2.0.0-alpha.16",
"@clusterio/lib": "^2.0.0-alpha.17",
"@swc/core": "^1.4.0",
"antd": "^5.13.0",
"assert": "^2.0.0",
"babel-loader": "^9.1.3",
"browserify-zlib": "^0.2.0",
"buffer": "^6.0.3",
"clean-webpack-plugin": "^4.0.0",
@@ -39,8 +37,8 @@
"react-router-dom": "^6.14.2",
"stream-browserify": "^3.0.0",
"style-loader": "^3.3.3",
"swc-loader": "^0.2.6",
"terser-webpack-plugin": "^5.3.9",
"ts-loader": "^9.4.4",
"typescript": "^5.1.6",
"util": "^0.12.5",
"webpack": "^5.88.2",
+6 -6
View File
@@ -104,16 +104,16 @@ export default function App(props: AppProps) {
}
return (
<ErrorBoundary Component={ErrorCard}>
<ConfigProvider
theme={{ algorithm: theme.darkAlgorithm }}
>
<ConfigProvider
theme={{ algorithm: theme.darkAlgorithm }}
>
<ErrorBoundary Component={ErrorCard}>
<ControlContext.Provider value={props.control}>
<BrowserRouter basename={webRoot}>
{page}
</BrowserRouter>
</ControlContext.Provider>
</ConfigProvider>
</ErrorBoundary>
</ErrorBoundary>
</ConfigProvider>
);
}
@@ -48,7 +48,7 @@ export default function HostViewPage() {
let hostButtons = <Space>
{
account.hasPermission("core.host.revoke_access")
account.hasPermission("core.host.revoke_token")
&& <Popconfirm
title={`Revoke tokens of ${host.name}?`}
placement="bottomRight"
@@ -1,6 +1,6 @@
import React from "react";
import { useNavigate } from "react-router-dom";
import { message, Button, Space, Table } from "antd";
import { message, Button, Space, Table, Typography } from "antd";
import CopyOutlined from "@ant-design/icons/CopyOutlined";
import type { SizeType } from "antd/es/config-provider/SizeContext";
import type { ColumnsType } from "antd/es/table";
@@ -10,6 +10,7 @@ import { useHosts } from "../model/host";
import InstanceStatusTag from "./InstanceStatusTag";
import StartStopInstanceButton from "./StartStopInstanceButton";
import { InstanceDetails } from "@clusterio/lib";
import Link from "./Link";
const strcmp = new Intl.Collator(undefined, { numeric: true, sensitivity: "base" }).compare;
@@ -55,7 +56,12 @@ export default function InstanceList(props: InstanceListProps) {
{
title: "Assigned Host",
key: "assignedHost",
render: (_, instance) => hostName(instance.assignedHost),
render: (_, instance) => <Link
to={`/hosts/${instance.assignedHost}/view`}
onClick={e => e.stopPropagation()}
>
{hostName(instance.assignedHost)}
</Link>,
sorter: (a, b) => strcmp(hostName(a.assignedHost), hostName(b.assignedHost)),
responsive: ["sm"],
},
@@ -68,10 +74,10 @@ export default function InstanceList(props: InstanceListProps) {
{publicAddress}
<Button
type="text"
icon={<CopyOutlined/>}
icon={<CopyOutlined />}
onClick={(e) => {
e.stopPropagation();
navigator.clipboard.writeText(publicAddress??"");
navigator.clipboard.writeText(publicAddress ?? "");
message.success("Copied public address!");
}}
/>
@@ -23,6 +23,7 @@ import { notifyErrorHandler } from "../util/notify";
import { useInstance } from "../model/instance";
import { useHost } from "../model/host";
import InstanceStatusTag from "./InstanceStatusTag";
import Link from "./Link";
const { Title } = Typography;
@@ -42,7 +43,9 @@ function InstanceDescription(props: InstanceDescriptionProps) {
<Descriptions.Item label="Host">
{!assigned
? <em>Unassigned</em>
: host?.name ?? instance.assignedHost
: <Link to={`/hosts/${host?.id ?? instance.assignedHost}/view`}>
{host?.name ?? instance.assignedHost}
</Link>
}
{account.hasPermission("core.instance.assign") && <AssignInstanceModal
id={instance.id}
@@ -104,7 +107,7 @@ function InstanceButtons(props: { instance: lib.InstanceDetails }) {
}
let instanceButtonsMenuProps: MenuProps = {
items: instanceButtonMenuItems,
onClick: ({ key }: { key:string }) => {
onClick: ({ key }: { key: string }) => {
if (key === "export") {
setExportingData(true);
control.sendTo(
@@ -156,9 +159,10 @@ function InstanceButtons(props: { instance: lib.InstanceDetails }) {
"core.instance.extract_players",
"core.instance.kill",
"core.instance.delete",
) && <Dropdown placement="bottomRight" trigger={["click"]} menu={instanceButtonsMenuProps}>
<Button>More <DownOutlined /></Button>
</Dropdown>}
)
&& <Dropdown placement="bottomRight" trigger={["click"]} menu={instanceButtonsMenuProps}>
<Button>More <DownOutlined /></Button>
</Dropdown>}
</Space>;
}
@@ -180,7 +184,7 @@ export default function InstanceViewPage() {
return <PageLayout nav={nav}>
<Alert
message={"Instance not found" }
message={"Instance not found"}
showIcon
description={<>Instance with id {instanceId} was not found on the controller.</>}
type="warning"
@@ -198,13 +202,13 @@ export default function InstanceViewPage() {
return <PageLayout nav={nav}>
<PageHeader
title={instance.name??""}
extra={<InstanceButtons instance={instance}/>}
title={instance.name ?? ""}
extra={<InstanceButtons instance={instance} />}
/>
<InstanceDescription host={host} instance={instance} />
{
account.hasAllPermission("core.instance.save.list", "core.instance.save.list_subscribe")
account.hasAllPermission("core.instance.save.list", "core.instance.save.subscribe")
&& <SavesList instance={instance} />
}
{
+47
View File
@@ -0,0 +1,47 @@
import React from "react";
import { Typography } from "antd";
import {
LinkProps,
useHref,
useLinkClickHandler,
} from "react-router-dom";
import { BaseType } from "antd/es/typography/Base";
const Link = React.forwardRef(
(
{
onClick,
replace = false,
state,
target,
to,
type,
...rest
}: LinkProps,
ref
) => {
let href = useHref(to);
let handleClick = useLinkClickHandler(to, {
replace,
state,
target,
}) as (event: React.MouseEvent<HTMLElement, MouseEvent>) => void;
return (
<Typography.Link
{...rest}
href={href}
onClick={(event) => {
onClick?.(event as React.MouseEvent<HTMLAnchorElement, MouseEvent>);
if (!event.defaultPrevented) {
handleClick(event);
}
}}
ref={ref as React.RefObject<HTMLElement> | null}
target={target}
type={type as BaseType}
/>
);
}
);
export default Link;
@@ -202,7 +202,7 @@ function SearchModsTable(props: SearchModsTableProps) {
align: "right",
render: (_, result) => <Select
size="small"
bordered={false}
variant="borderless"
value={modResultSelectedVersion.get(result.name) || 0}
onChange={index => {
const newVersions = new Map(modResultSelectedVersion);
@@ -801,7 +801,7 @@ export default function ModPackViewPage() {
title={modPack.name}
extra={<Space>
<ExportButton modPack={modifiedModPack}/>
{account.hasPermission("core.mod-pack.delete") && <Popconfirm
{account.hasPermission("core.mod_pack.delete") && <Popconfirm
title="Delete mod pack and all of its settings?"
placement="bottomRight"
okText="Delete"
+2 -2
View File
@@ -186,8 +186,8 @@ export default function ModsPage() {
<SectionHeader
title="Mod Packs"
extra={<Space>
{account.hasPermission("core.mod-pack.create") && <ImportModPackButton />}
{account.hasPermission("core.mod-pack.create") && <CreateModPackButton />}
{account.hasPermission("core.mod_pack.create") && <ImportModPackButton />}
{account.hasPermission("core.mod_pack.create") && <CreateModPackButton />}
</Space>}
/>
<Table
@@ -1,6 +1,6 @@
import React from "react";
import { Link } from "react-router-dom";
import { Layout, Breadcrumb } from "antd";
import Link from "./Link";
const { Content } = Layout;
+1 -1
View File
@@ -207,6 +207,7 @@ export default function SavesList(props: { instance: lib.InstanceDetails }) {
let hostOffline = ["unassigned", "unknown"].includes(props.instance.status!);
const saveTable = <Table
className="save-list-table"
size="small"
columns={[
{
@@ -236,7 +237,6 @@ export default function SavesList(props: { instance: lib.InstanceDetails }) {
]}
dataSource={[...saves.values()]}
rowKey={save => save.name}
pagination={false}
expandable={{
columnWidth: 33,
expandRowByClick: true,
@@ -1,4 +1,4 @@
import React, { useContext, useEffect, useState } from "react";
import React, { Fragment, useContext, useEffect, useState } from "react";
import { Route, Routes, useNavigate } from "react-router-dom";
import { Dropdown, Layout, Menu, MenuProps } from "antd";
import UserOutlined from "@ant-design/icons/UserOutlined";
@@ -115,12 +115,12 @@ export default function SiteLayout() {
{combinedPages.map(({path, sidebarPath, content}) => <Route
path={path}
key={path}
element={<>
element={<Fragment key={path}>
<SetSidebar path={sidebarPath ? sidebarPath : path} />
<ErrorBoundary Component={ErrorPage}>
{content}
</ErrorBoundary>
</>}
</Fragment>}
/>)}
<Route element={<SetSidebar path={null} />} />
</Routes>
@@ -88,7 +88,6 @@ export default function UserViewPage() {
onChange={() => setRolesDirty(true)}
loading={!roles}
mode="multiple"
showArrow={true}
filterOption={(inputValue:string, option: any): boolean => {
let role = roles.get(option.value);
return role?.name.toLowerCase().includes(inputValue.toLowerCase()) ?? false;
+5 -2
View File
@@ -12,6 +12,7 @@ import PageHeader from "./PageHeader";
import PageLayout from "./PageLayout";
import PluginExtra from "./PluginExtra";
import { formatFirstSeen, formatLastSeen, sortFirstSeen, sortLastSeen, useUsers } from "../model/user";
import Link from "./Link";
const strcmp = new Intl.Collator(undefined, { numeric: true, sensitivity: "base" }).compare;
@@ -49,7 +50,7 @@ function CreateUserButton() {
>
<Form form={form}>
<Form.Item name="userName" label="Name">
<Input/>
<Input />
</Form.Item>
</Form>
</Modal>
@@ -97,7 +98,9 @@ export default function UsersPage() {
title: "Roles",
key: "roles",
render: (_, user) => (
[...user.roleIds].map(id => <Tag key={id}>{(roles.get(id) || { name: id }).name}</Tag>)
[...user.roleIds].map(id => <Link to={`/roles/${id}/view`} onClick={e => e.stopPropagation()}>
<Tag key={id}>{(roles.get(id) || { name: id }).name}</Tag>
</Link>)
),
},
{
+2
View File
@@ -28,3 +28,5 @@ export { default as PageHeader } from "./components/PageHeader";
export { default as PageLayout } from "./components/PageLayout";
export { default as SavesList } from "./components/SavesList";
export { default as SectionHeader } from "./components/SectionHeader";
export { default as Link } from "./components/Link";
+13
View File
@@ -22,6 +22,12 @@ export function useAccount(): UserAccount {
};
}, [control]);
function checkPermissionExists(permission: string) {
if (!lib.permissions.has(permission)) {
throw new Error(`permission ${permission} does not exist`);
}
}
function hasPermission(permission: string): boolean {
for (let role of roles) {
if (role.permissions.includes("core.admin") || role.permissions.includes(permission)) {
@@ -38,12 +44,16 @@ export function useAccount(): UserAccount {
if (!roles) {
return null;
}
checkPermissionExists(permission);
return hasPermission(permission);
},
hasAnyPermission(...permissions: string[]): boolean | null {
if (!roles) {
return null;
}
for (let permission of permissions) {
checkPermissionExists(permission);
}
for (let permission of permissions) {
if (hasPermission(permission)) {
return true;
@@ -55,6 +65,9 @@ export function useAccount(): UserAccount {
if (!roles) {
return null;
}
for (let permission of permissions) {
checkPermissionExists(permission);
}
for (let permission of permissions) {
if (!hasPermission(permission)) {
return false;
+1 -1
View File
@@ -47,7 +47,7 @@ export const pages: PluginPage[] = [
{
path: "/mods",
sidebarName: "Mods",
permission: (account: UserAccount) => account.hasAnyPermission("core.mod.list", "core.mod-pack.list"),
permission: (account: UserAccount) => account.hasAnyPermission("core.mod.list", "core.mod_pack.list"),
content: <ModsPage />,
},
{
+9
View File
@@ -182,10 +182,19 @@ body {
white-space: pre-wrap;
}
.save-list-dragger .ant-upload.ant-upload-drag {
border: none;
background: none;
}
.save-list-dragger .ant-upload.ant-upload-btn {
padding: 0;
}
.save-list-table .ant-table-pagination.ant-pagination {
margin-bottom: 0;
}
.rcon-result code {
display: block;
background: #000;
@@ -1,10 +1,10 @@
{
"extends": "../../tsconfig.web.json",
"extends": "../../tsconfig.browser.json",
"references": [
{ "path": "../lib/tsconfig.web.json" },
{ "path": "../lib/tsconfig.browser.json" },
],
"include": [ "src/**/*.tsx", "src/**/*.ts", "package.json", "global.d.ts" ],
"compilerOptions": {
"outDir": "dist",
"outDir": "dist/browser",
}
}
+6
View File
@@ -0,0 +1,6 @@
{
"files": [],
"references": [
{ "path": "./tsconfig.browser.json" },
],
}
+30 -15
View File
@@ -65,32 +65,47 @@ module.exports = (env = {}) => ({
},
],
},
{
test: /\.js$/,
resolve: {
// XXX Required due to bug in babel-runtime
// see: https://github.com/babel/babel/issues/12058
fullySpecified: false,
},
},
{
test: /\.tsx?$/,
exclude: /node_modules/,
use: {
loader: require.resolve("ts-loader"),
loader: require.resolve("swc-loader"),
options: {
configFile: "tsconfig.web.json",
projectReferences: true,
sourceMaps: true,
jsc: {
parser: {
syntax: "typescript",
tsx: true,
},
target: "es2022",
transform: {
react: {
runtime: "automatic",
},
},
},
},
},
},
{
test: /\.jsx$/,
test: /\.jsx?$/,
exclude: /node_modules/,
use: {
loader: require.resolve("babel-loader"),
loader: require.resolve("swc-loader"),
options: {
presets: [require.resolve("@babel/preset-react")],
sourceMaps: true,
jsc: {
parser: {
syntax: "ecmascript",
jsx: true,
},
target: "es2022",
transform: {
react: {
runtime: "automatic",
},
},
},
},
},
},
@@ -123,10 +138,10 @@ module.exports = (env = {}) => ({
moduleIds: "deterministic",
minimizer: [
new TerserPlugin({
minify: TerserPlugin.swcMinify,
terserOptions: {
compress: {
keep_classnames: true,
passes: 2,
},
mangle: {
keep_classnames: true,
+1 -1
View File
@@ -1,2 +1,2 @@
webpack.config.js
dist/web/build
dist/browser
+2 -2
View File
@@ -6,8 +6,8 @@ export const plugin: lib.PluginDeclaration = {
name: "global_chat",
title: "Global Chat",
description: "Forwards chat between instances.",
instanceEntrypoint: "dist/plugin/instance",
ctlEntrypoint: "dist/plugin/control",
instanceEntrypoint: "dist/node/instance",
ctlEntrypoint: "dist/node/control",
messages: [
ChatEvent,
+4 -4
View File
@@ -1,12 +1,12 @@
{
"name": "@clusterio/plugin-global_chat",
"version": "2.0.0-alpha.16",
"version": "2.0.0-alpha.17",
"description": "Clusterio plugin forwarding between Factorio servers",
"author": "Hornwitser <github@hornwitser.no>",
"homepage": "https://github.com/clusterio/clusterio#readme",
"license": "MIT",
"repository": "clusterio/clusterio",
"main": "dist/plugin/index.js",
"main": "dist/node/index.js",
"scripts": {
"prepare": "tsc --build && webpack-cli --env production",
"test": "echo \"Error: run tests from root\" && exit 1"
@@ -21,8 +21,8 @@
"@clusterio/lib": "^2.0.0-alpha.0"
},
"devDependencies": {
"@clusterio/lib": "^2.0.0-alpha.16",
"@clusterio/web_ui": "^2.0.0-alpha.16",
"@clusterio/lib": "^2.0.0-alpha.17",
"@clusterio/web_ui": "^2.0.0-alpha.17",
"@types/node": "^20.4.5",
"@types/react": "^18.2.21",
"typescript": "^5.1.6",
+3 -3
View File
@@ -4,9 +4,9 @@ const lib = require("@clusterio/lib");
const mock = require("../../../test/mock");
const lines = require("../../../test/lib/factorio/lines");
const instance = require("../dist/plugin/instance");
const info = require("../dist/plugin/index").plugin;
const { ChatEvent } = require("../dist/plugin/messages");
const instance = require("../dist/node/instance");
const info = require("../dist/node/index").plugin;
const { ChatEvent } = require("../dist/node/messages");
describe("global_chat plugin", function() {
+11
View File
@@ -0,0 +1,11 @@
{
"extends": "../../tsconfig.browser.json",
"references": [
{ "path": "../../packages/lib/tsconfig.browser.json" },
{ "path": "../../packages/web_ui/tsconfig.browser.json" },
],
"include": [ "web/**/*.tsx", "web/**/*.ts", "messages.ts", "package.json" ],
"compilerOptions": {
"outDir": "dist/browser",
}
}
+3 -7
View File
@@ -1,11 +1,7 @@
{
"extends": "../../tsconfig.node.json",
"files": [],
"references": [
{ "path": "../../packages/lib" },
{ "path": "./tsconfig.node.json" },
{ "path": "./tsconfig.browser.json" },
],
"include": ["./**/*.ts"],
"exclude": ["test/*", "./dist/*"],
"compilerOptions": {
"outDir": "dist/plugin",
},
}
+11
View File
@@ -0,0 +1,11 @@
{
"extends": "../../tsconfig.node.json",
"references": [
{ "path": "../../packages/lib/tsconfig.node.json" },
],
"include": ["./**/*.ts"],
"exclude": ["test/*", "./dist/*"],
"compilerOptions": {
"outDir": "dist/node",
},
}
-11
View File
@@ -1,11 +0,0 @@
{
"extends": "../../tsconfig.web.json",
"references": [
{ "path": "../../packages/lib/tsconfig.web.json" },
{ "path": "../../packages/web_ui/tsconfig.web.json" },
],
"include": [ "web/**/*.tsx", "web/**/*.ts", "messages.ts", "package.json" ],
"compilerOptions": {
"outDir": "dist/web/build",
}
}
+1 -1
View File
@@ -1,2 +1,2 @@
webpack.config.js
dist/web/build
dist/browser
+2 -2
View File
@@ -22,7 +22,7 @@ export const plugin: lib.PluginDeclaration = {
title: "Inventory sync",
description: "Synchronizes players inventories between instances",
instanceEntrypoint: "dist/plugin/instance",
instanceEntrypoint: "dist/node/instance",
instanceConfigFields: {
"inventory_sync.rcon_chunk_size": {
title: "Rcon inventory chunk size",
@@ -33,7 +33,7 @@ export const plugin: lib.PluginDeclaration = {
},
},
controllerEntrypoint: "dist/plugin/controller",
controllerEntrypoint: "dist/node/controller",
controllerConfigFields: {
"inventory_sync.player_lock_timeout": {
title: "Player Lock Timeout",
+4 -4
View File
@@ -1,12 +1,12 @@
{
"name": "@clusterio/plugin-inventory_sync",
"version": "2.0.0-alpha.16",
"version": "2.0.0-alpha.17",
"description": "Clusterio plugin forwarding between Factorio servers",
"author": "Danielv <danielv@danielv.no>",
"homepage": "https://github.com/clusterio/factorioClusterio#readme",
"license": "MIT",
"repository": "clusterio/factorioClusterio",
"main": "dist/plugin/index.js",
"main": "dist/node/index.js",
"scripts": {
"prepare": "tsc --build && webpack-cli --env production",
"test": "echo \"Error: run tests from root\" && exit 1"
@@ -25,8 +25,8 @@
"fs-extra": "^8.1.0"
},
"devDependencies": {
"@clusterio/lib": "^2.0.0-alpha.16",
"@clusterio/web_ui": "^2.0.0-alpha.16",
"@clusterio/lib": "^2.0.0-alpha.17",
"@clusterio/web_ui": "^2.0.0-alpha.17",
"@types/fs-extra": "^11.0.1",
"@types/node": "^20.4.5",
"@types/react": "^18.2.21",
@@ -0,0 +1,11 @@
{
"extends": "../../tsconfig.browser.json",
"references": [
{ "path": "../../packages/lib/tsconfig.browser.json" },
{ "path": "../../packages/web_ui/tsconfig.browser.json" },
],
"include": [ "web/**/*.tsx", "web/**/*.ts", "messages.ts", "package.json" ],
"compilerOptions": {
"outDir": "dist/browser",
}
}
+3 -7
View File
@@ -1,11 +1,7 @@
{
"extends": "../../tsconfig.node.json",
"files": [],
"references": [
{ "path": "../../packages/lib" },
{ "path": "./tsconfig.node.json" },
{ "path": "./tsconfig.browser.json" },
],
"include": ["./**/*.ts"],
"exclude": ["test/*", "./dist/*"],
"compilerOptions": {
"outDir": "dist/plugin",
},
}
+11
View File
@@ -0,0 +1,11 @@
{
"extends": "../../tsconfig.node.json",
"references": [
{ "path": "../../packages/lib/tsconfig.node.json" },
],
"include": ["./**/*.ts"],
"exclude": ["test/*", "./dist/*"],
"compilerOptions": {
"outDir": "dist/node",
},
}
-11
View File
@@ -1,11 +0,0 @@
{
"extends": "../../tsconfig.web.json",
"references": [
{ "path": "../../packages/lib/tsconfig.web.json" },
{ "path": "../../packages/web_ui/tsconfig.web.json" },
],
"include": [ "web/**/*.tsx", "web/**/*.ts", "messages.ts", "package.json" ],
"compilerOptions": {
"outDir": "dist/web/build",
}
}
+1 -1
View File
@@ -1,2 +1,2 @@
webpack.config.js
dist/web/build
dist/browser
+2 -2
View File
@@ -15,8 +15,8 @@ export const plugin: lib.PluginDeclaration = {
name: "player_auth",
title: "Player Auth",
description: "Provides authentication to the cluster via logging into a Factorio server.",
controllerEntrypoint: "dist/plugin/controller",
instanceEntrypoint: "dist/plugin/instance",
controllerEntrypoint: "dist/node/controller",
instanceEntrypoint: "dist/node/instance",
webEntrypoint: "./web",
controllerConfigFields: {
"player_auth.code_length": {
+4 -4
View File
@@ -1,12 +1,12 @@
{
"name": "@clusterio/plugin-player_auth",
"version": "2.0.0-alpha.16",
"version": "2.0.0-alpha.17",
"description": "Clusterio plugin authenticating logged in players to the web interface",
"author": "Hornwitser <github@hornwitser.no>",
"homepage": "https://github.com/clusterio/clusterio#readme",
"license": "MIT",
"repository": "clusterio/clusterio",
"main": "dist/plugin/index.js",
"main": "dist/node/index.js",
"scripts": {
"prepare": "tsc --build && webpack-cli --env production",
"test": "echo \"Error: run tests from root\" && exit 1"
@@ -26,8 +26,8 @@
"jsonwebtoken": "^9.0.1"
},
"devDependencies": {
"@clusterio/lib": "^2.0.0-alpha.16",
"@clusterio/web_ui": "^2.0.0-alpha.16",
"@clusterio/lib": "^2.0.0-alpha.17",
"@clusterio/web_ui": "^2.0.0-alpha.17",
"@types/express": "^4.17.17",
"@types/jsonwebtoken": "^9.0.2",
"@types/node": "^20.4.5",
+4 -4
View File
@@ -5,10 +5,10 @@ const jwt = require("jsonwebtoken");
const mock = require("../../../test/mock");
const controller = require("../dist/plugin/controller");
const instance = require("../dist/plugin/instance");
const info = require("../dist/plugin/index").plugin;
const { FetchPlayerCodeRequest, SetVerifyCodeRequest } = require("../dist/plugin/messages");
const controller = require("../dist/node/controller");
const instance = require("../dist/node/instance");
const info = require("../dist/node/index").plugin;
const { FetchPlayerCodeRequest, SetVerifyCodeRequest } = require("../dist/node/messages");
const lib = require("@clusterio/lib");
+11
View File
@@ -0,0 +1,11 @@
{
"extends": "../../tsconfig.browser.json",
"references": [
{ "path": "../../packages/lib/tsconfig.browser.json" },
{ "path": "../../packages/web_ui/tsconfig.browser.json" },
],
"include": [ "web/**/*.tsx", "web/**/*.ts", "messages.ts", "package.json" ],
"compilerOptions": {
"outDir": "dist/browser",
}
}
+3 -7
View File
@@ -1,11 +1,7 @@
{
"extends": "../../tsconfig.node.json",
"files": [],
"references": [
{ "path": "../../packages/lib" },
{ "path": "./tsconfig.node.json" },
{ "path": "./tsconfig.browser.json" },
],
"include": ["./**/*.ts"],
"exclude": ["test/*", "./dist/*"],
"compilerOptions": {
"outDir": "dist/plugin",
},
}
+11
View File
@@ -0,0 +1,11 @@
{
"extends": "../../tsconfig.node.json",
"references": [
{ "path": "../../packages/lib/tsconfig.node.json" },
],
"include": ["./**/*.ts"],
"exclude": ["test/*", "./dist/*"],
"compilerOptions": {
"outDir": "dist/node",
},
}
-11
View File
@@ -1,11 +0,0 @@
{
"extends": "../../tsconfig.web.json",
"references": [
{ "path": "../../packages/lib/tsconfig.web.json" },
{ "path": "../../packages/web_ui/tsconfig.web.json" },
],
"include": [ "web/**/*.tsx", "web/**/*.ts", "messages.ts", "package.json" ],
"compilerOptions": {
"outDir": "dist/web/build",
}
}
+1 -1
View File
@@ -1,2 +1,2 @@
webpack.config.js
dist/web/build
dist/browser
+2 -2
View File
@@ -6,8 +6,8 @@ export const plugin: lib.PluginDeclaration = {
name: "research_sync",
title: "Research Sync",
description: "Synchronises technology research progress between instances.",
instanceEntrypoint: "dist/plugin/instance",
controllerEntrypoint: "dist/plugin/controller",
instanceEntrypoint: "dist/node/instance",
controllerEntrypoint: "dist/node/controller",
messages: [
messages.ContributionEvent,
+12 -1
View File
@@ -137,10 +137,21 @@ function research_sync.sync_technologies(data)
for _, tech_data in pairs(game.json_to_table(data)) do
local tech = force.technologies[tech_data[nameIndex]]
if tech and tech.level <= tech_data[levelIndex] then
tech.level = math.min(tech_data[levelIndex], tech.prototype.max_level)
local new_level = math.min(tech_data[levelIndex], tech.prototype.max_level)
if new_level ~= tech.level then
-- when the level of the current research changes the
-- progress is not automatically reset.
if force.current_research == tech then
force.research_progress = 0
end
tech.level = new_level
end
local progress
if tech_data[researchedIndex] then
if force.current_research == tech then
force.research_progress = 0
end
tech.researched = true
progress = nil
elseif tech_data[progressIndex] then
+4 -4
View File
@@ -1,12 +1,12 @@
{
"name": "@clusterio/plugin-research_sync",
"version": "2.0.0-alpha.16",
"version": "2.0.0-alpha.17",
"description": "Clusterio plugin for synchronizing research between Factorio servers",
"author": "Hornwitser <github@hornwitser.no>",
"homepage": "https://github.com/clusterio/clusterio#readme",
"license": "MIT",
"repository": "clusterio/clusterio",
"main": "dist/plugin/index.js",
"main": "dist/node/index.js",
"scripts": {
"prepare": "tsc --build && webpack-cli --env production",
"test": "echo \"Error: run tests from root\" && exit 1"
@@ -25,8 +25,8 @@
"fs-extra": "^8.1.0"
},
"devDependencies": {
"@clusterio/lib": "^2.0.0-alpha.16",
"@clusterio/web_ui": "^2.0.0-alpha.16",
"@clusterio/lib": "^2.0.0-alpha.17",
"@clusterio/web_ui": "^2.0.0-alpha.17",
"@types/fs-extra": "^11.0.1",
"@types/node": "^20.4.5",
"@types/react": "^18.2.21",
+2 -2
View File
@@ -2,5 +2,5 @@
// required here in order to track coverage for them.
"use strict";
require("../dist/plugin/instance");
require("../dist/plugin/controller");
require("../dist/node/instance");
require("../dist/node/controller");
@@ -0,0 +1,11 @@
{
"extends": "../../tsconfig.browser.json",
"references": [
{ "path": "../../packages/lib/tsconfig.browser.json" },
{ "path": "../../packages/web_ui/tsconfig.browser.json" },
],
"include": [ "web/**/*.tsx", "web/**/*.ts", "messages.ts", "package.json" ],
"compilerOptions": {
"outDir": "dist/browser",
}
}
+3 -7
View File
@@ -1,11 +1,7 @@
{
"extends": "../../tsconfig.node.json",
"files": [],
"references": [
{ "path": "../../packages/lib" },
{ "path": "./tsconfig.node.json" },
{ "path": "./tsconfig.browser.json" },
],
"include": ["./**/*.ts"],
"exclude": ["test/*", "./dist/*"],
"compilerOptions": {
"outDir": "dist/plugin",
},
}
+11
View File
@@ -0,0 +1,11 @@
{
"extends": "../../tsconfig.node.json",
"references": [
{ "path": "../../packages/lib/tsconfig.node.json" },
],
"include": ["./**/*.ts"],
"exclude": ["test/*", "./dist/*"],
"compilerOptions": {
"outDir": "dist/node",
},
}
-11
View File
@@ -1,11 +0,0 @@
{
"extends": "../../tsconfig.web.json",
"references": [
{ "path": "../../packages/lib/tsconfig.web.json" },
{ "path": "../../packages/web_ui/tsconfig.web.json" },
],
"include": [ "web/**/*.tsx", "web/**/*.ts", "messages.ts", "package.json" ],
"compilerOptions": {
"outDir": "dist/web/build",
}
}
+1 -1
View File
@@ -1,2 +1,2 @@
webpack.config.js
dist/web/build
dist/browser
+1 -1
View File
@@ -12,7 +12,7 @@ export const plugin: lib.PluginDeclaration = {
description:
"Provides in-game item/fluid production, builds, kills, and pollution "+
"statistics to the cluster's Prometheus endpoint.",
instanceEntrypoint: "dist/plugin/instance",
instanceEntrypoint: "dist/node/instance",
instanceConfigFields: {
"statistics_exporter.command_timeout": {
title: "Command Timeout",
+4 -4
View File
@@ -1,12 +1,12 @@
{
"name": "@clusterio/plugin-statistics_exporter",
"version": "2.0.0-alpha.16",
"version": "2.0.0-alpha.17",
"description": "Clusterio plugin for gathering statistics from Factorio servers",
"author": "Hornwitser <github@hornwitser.no>",
"homepage": "https://github.com/clusterio/clusterio#readme",
"license": "MIT",
"repository": "clusterio/clusterio",
"main": "dist/plugin/index.js",
"main": "dist/node/index.js",
"scripts": {
"prepare": "tsc --build && webpack-cli --env production",
"test": "echo \"Error: run tests from root\" && exit 1"
@@ -21,8 +21,8 @@
"@clusterio/lib": "^2.0.0-alpha.0"
},
"devDependencies": {
"@clusterio/lib": "^2.0.0-alpha.16",
"@clusterio/web_ui": "^2.0.0-alpha.16",
"@clusterio/lib": "^2.0.0-alpha.17",
"@clusterio/web_ui": "^2.0.0-alpha.17",
"@types/node": "^20.4.5",
"@types/react": "^18.2.21",
"typescript": "^5.1.6",
+2 -2
View File
@@ -2,8 +2,8 @@
const assert = require("assert").strict;
const mock = require("../../../test/mock");
const instance = require("../dist/plugin/instance");
const info = require("../dist/plugin/index").plugin;
const instance = require("../dist/node/instance");
const info = require("../dist/node/index").plugin;
describe("statistics_exporter plugin", function() {
@@ -0,0 +1,11 @@
{
"extends": "../../tsconfig.browser.json",
"references": [
{ "path": "../../packages/lib/tsconfig.browser.json" },
{ "path": "../../packages/web_ui/tsconfig.browser.json" },
],
"include": [ "web/**/*.tsx", "web/**/*.ts", "package.json" ],
"compilerOptions": {
"outDir": "dist/browser",
}
}
+3 -7
View File
@@ -1,11 +1,7 @@
{
"extends": "../../tsconfig.node.json",
"files": [],
"references": [
{ "path": "../../packages/lib" },
{ "path": "./tsconfig.node.json" },
{ "path": "./tsconfig.browser.json" },
],
"include": ["./**/*.ts"],
"exclude": ["test/*", "./dist/*"],
"compilerOptions": {
"outDir": "dist/plugin",
},
}
@@ -0,0 +1,11 @@
{
"extends": "../../tsconfig.node.json",
"references": [
{ "path": "../../packages/lib/tsconfig.node.json" },
],
"include": ["./**/*.ts"],
"exclude": ["test/*", "./dist/*"],
"compilerOptions": {
"outDir": "dist/node",
},
}
@@ -1,11 +0,0 @@
{
"extends": "../../tsconfig.web.json",
"references": [
{ "path": "../../packages/lib/tsconfig.web.json" },
{ "path": "../../packages/web_ui/tsconfig.web.json" },
],
"include": [ "web/**/*.tsx", "web/**/*.ts", "package.json" ],
"compilerOptions": {
"outDir": "dist/web/build",
}
}
Loaded 100 of 126 files, more files were not shown because too many files have changed in this diff. Show more