Commit Graph
3136 Commits
Author SHA1 Message Date
7122cbccf8 Add typed hook system for plugins
Replace invokeHook and the per plugin on* methods with AsyncHook
collections on the controller, host, instance, clusterioctl and web
control objects. Plugin entrypoints export a default function that
receives a load context and attaches handlers to the hooks it needs.
Class exports still load through fromContext with a deprecation
warning. The create templates and docs use the entrypoint function.

This is clusterio/clusterio#921 recreated on top of master after the
ESM migration and the PluginDeclaration permissions change.

Closes #730

Co-Authored-By: Cooldude2606 <25043174+Cooldude2606@users.noreply.github.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-11 22:49:09 +00:00
8c03ac15ab Define permissions on PluginDeclaration (#988)
Adds a permissions array to PluginDeclaration, a type-level Permissions
registry that plugins extend with module augmentation, and
registerPluginPermissions() called from all entrypoints. checkPermission
and the web UI hasPermission helpers now take PermissionName so typos are
compile errors. Core definitions move into a corePermissions array that
the interface derives its keys from.

Closes #987

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: Cooldude2606 <25043174+Cooldude2606@users.noreply.github.com>
2026-09-11 23:28:30 +01:00
Cooldude2606 2aee5379b5 Merge pull request #984 from Hornwitser/migrate-to-esm
Migrate to ESM
2026-09-11 23:14:50 +01:00
Hornwitser 5dcaca4d0b Migrate to ECMAScript Modules
Migrate the usage of CommonJS specific code and imports to ESM.  Roughly
summarised this:
- Changes all require() calls to import statement or expressions.
- Changes usage of module.exports to export statements.
- Replaces require.resolve with import.meta.resolve.
- Replaces conditional imports with conditional subpath imports.
- Replaces module === require.main with import.meta.main.
- Adds the full filename with extention to all relative imports.
- Changes all usage of __dirname and __filename to their import.meta
  equivalents.
- Sets "type": "module" in package.json files.
- Removes usage of "use strict".
- Adds .ts an an alias of .js for the webpack build.

Due to differences in how require and import resolves files the plugin
entrypoints will have to include the filename and extension after this
migration in order to work.
2026-09-11 22:43:57 +02:00
BastiaanandClaude Fable 5.1 897c18bd08 Resolve numeric names in ctl id lookups (#989)
resolveHost, resolveInstance, resolveModPack and retrieveRole treated any
integer like argument as an id and never looked it up as a name. An
instance created as "10" could not be referenced by name afterwards. The
lookup now treats the argument as an id only if an entry with that id
exists, and otherwise falls back to a name match.

Closes #886

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-11 20:25:45 +01:00
Hornwitser b69111dda3 Fix obsolete usage of EventSubscriber in template
This functionality was moved to the MapSubscriber class in e49b82842d.
2026-09-11 20:18:49 +02:00
Hornwitser a025d01294 Fix mainBundle being passed as array in dev mode
Values in the assetsByChunkName object are arrays not strings.  This
caused the dev mode to fail after 2f4e0c4418 added an additional step
filtering out the static/ prefix from what it expected to be a string.
2026-09-11 19:59:41 +02:00
Hornwitser 9d061a0f53 Bump webpack version
Use the latest version of webpack and webpack-cli.  It's possible that
using ESM imports + having an outdated webpack-cli caused issues.
2026-09-11 19:51:16 +02:00
Hornwitser 511b8b7d97 Remove global_chat dependency in restart test
Use a fictional restart_test mock plugin added as a before step instead
of global_chat for the .checkRestartRequired() test.  Prevents the test
from failing if the integration tests are not also run.

Also fixes the fixture not accounting for setting load_plugin causing
restartRequired to be set to true due to the integration tests having
already set it to the same value.
2026-09-11 19:51:16 +02:00
Hornwitser 30083b96c1 Add packagePath to plugin info
Provide the absolute path to the package.json file in the resolved
plugin info to simplify the logic around loading files from it.
2026-09-11 19:51:15 +02:00
Hornwitser d6dce12acc Use factory pattern for yargs
The ESM import of yargs does not support the singleton way of using
yargs, instead we have to call the yargs function to get a parser that
can be augmented with the options.

The .argv property has also been deprecated. Replace it with a call to
.parse() instead.
2026-09-11 19:51:15 +02:00
Cooldude2606 7987a5d925 Merge pull request #991 from bbassie/feature/clone-instance-save
Add save copying and start options when creating an instance
2026-09-09 20:03:27 +01:00
Cooldude2606 6671e3493e Merge pull request #994 from bbassie/fix/log-invalid-message-errors
Include schema validation errors in InvalidMessage stack
2026-09-09 15:29:03 +01:00
bbassieandClaude Fable 5.1 4730faea0c Cover the message fallback in Link error logging
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-09 14:15:46 +00:00
bbassieandClaude Fable 5.1 dc129b2fad Include schema validation errors in InvalidMessage stack
Generic error handlers such as FactorioServer.handle() and the event
handler catch in Link only log err.stack, so the ajv errors attached to
an InvalidMessage were lost unless the catch site checked for the class
explicitly. Appending the errors to the stack in the constructor makes
every stack-logging handler show which field failed validation without
special casing each one.

Fixes #986

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-09 11:57:05 +00:00
bbassieandClaude Opus 5 9b17fa2b93 Add host, server name and start options to the create instance modal
Accounts with core.instance.assign get a host selector, defaulting to
unassigned. Picking a host offers "Start after creation", which creates a
new save on first run when no save is copied, and together with a config
to copy it offers "Copy Save". Copying a config also offers a server
name, prefilled from the source, which overwrites factorio.settings.name
so the clone does not show up under the source name in the server
browser.

useInstanceConfig now clears the config while loading and drops errors,
so an account without core.instance.get_config just does not get the
server name field.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-09 09:40:00 +00:00
Cooldude2606 abfc5ff8d5 Merge pull request #990 from bbassie/fix/inventory-sync-shortcut-toggles
Sync shortcut toggles and game view settings in inventory_sync
2026-09-08 00:33:35 +01:00
bbassieandClaude Fable 5.1 9aeb23e5da Sync roboport, movement bonus, and game view settings in inventory_sync
The built-in shortcuts are not toggleable in their prototypes and
set_shortcut_toggled only affects custom Lua shortcuts, so the state
behind them is synced instead: allow_dispatching_robots on the
character, inhibit_movement_bonus on the armor grid, and every field of
game_view_settings. map_view_settings is write-only and cannot be read.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 23:16:33 +00:00
bbassieandClaude Fable 5.1 d9bce1e39e Sync every toggleable shortcut instead of a fixed list
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 01:10:43 +00:00
bbassieandClaude Fable 5.1 c997fd8ea7 Add save copying and start options when creating an instance
The create instance modal can now copy the active save of the instance
whose config is copied, optionally saving the running game first, and
start the new instance once the copy is done. The new instance is
assigned to the host of the source so the save can be transferred.

Saving a running game is a new InstanceSaveGameRequest handled by the
host through /server-save, which waits for the save to finish. It is
also exposed as clusterioctl instance save-game.

Closes #405

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-05 22:19:24 +00:00
bbassieandClaude Fable 5.1 75ac8bb491 Sync personal roboport and movement bonus toggles in inventory_sync
The toggle state of the personal roboport and equipment movement bonus
shortcuts lives on the player and was not part of the serialized player
data, so it reset when switching instances.

Closes #898

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-05 22:13:12 +00:00
Hornwitser e09e1c5f80 Merge pull request #983 from Hornwitser/configurable-static-url
Configurable static assets URL
2026-08-31 21:39:25 +02:00
Hornwitser 71f78c14a3 Add command for copying static assets to a folder
This is useful in case the static assets are hosted by a separate web
server like Nginx, or for testing that the static_url option works as
intended.
2026-08-31 20:14:13 +02:00
Hornwitser 7c0d7faa24 Add config to set URL for static assets
Add option in the controller config to set where static assets are
loaded from in order to support deployments where the static assets are
server via a CDN while the API is directly accessed.

This is useful because some CDNs put low limits on how large requests
bodies can be, which causes issues with uploading large mods and saves.
2026-08-31 19:17:31 +02:00
Cooldude2606 0f1fbb8fe5 Merge pull request #981 from bbassie/fix/static-root-web-root
Make staticRoot include the static/ prefix and use it for plugin bundles
2026-08-31 00:38:31 +03:00
Cooldude2606 1915250529 Merge pull request #979 from bbassie/feature/emmylua-lint
Add emmylua lint CI for module Lua
2026-08-31 00:25:06 +03:00
Cooldude2606 4d75f8d402 Add luarc to git ignore file 2026-08-30 22:15:56 +01:00
Cooldude2606 cc8f590bf4 Merge pull request #982 from Cooldude2606/ci/change-strategy
Update the test strategy
2026-08-30 23:55:49 +03:00
Cooldude2606 384a185a2e Update the test strategy
We now only perform one test on the oldest node version. In addition, timeout values were added and some other small tidy ups.
2026-08-30 21:50:47 +01:00
Cooldude2606 285a0018c9 Merge pull request #978 from bbassie/feature/version-compat-ci
Add CI check for controller and host version compatibility
2026-08-30 23:07:49 +03:00
bbassieandClaude Fable 5 930223019e Add serveWeb test for webRoot and staticRoot substitution
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-28 10:54:25 +00:00
bbassieandClaude Fable 5 4c252a0763 Add tests for stripStaticPrefix and /api/plugins
The codecov/patch check flagged the new routes code as uncovered. The
unit tests never hit /api/plugins, so cover the static/ stripping and the
manifest error paths directly.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-28 10:48:54 +00:00
bbassieandClaude Fable 5 bd6bf480da Drop changelog edit, entries go in the PR description
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-28 10:35:35 +00:00
bbassieandClaude Fable 5 2f4e0c4418 Make staticRoot include the static/ prefix and use it for plugin bundles
staticRoot was set to the same value as webRoot, so the static/ prefix
ended up scattered across consumers: index.html relied on the manifest
path containing it, the export stores hardcoded `${staticRoot}static/`,
and bootstrap loaded plugin bundles from webRoot instead. Now staticRoot
is `${webRoot}static/` and the controller strips the static/ prefix from
bundle paths before handing them to the page and to /api/plugins.

Plugins that fetch assets with `${staticRoot}static/...` need to drop the
static/ part.

Fixes #980

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-28 09:50:08 +00:00
bbassieandClaude Fable 5 4caf585048 Add emmylua lua lint CI check
Same setup as established in ExpCluster, emmylua_check with factorio
typedefs generated by fmtk luals-addon. The moduleMap resolves
modules/clusterio and modules/<plugin> requires to their source under
packages/host and plugins, and the mod scaffolding under
packages/host/lua to bare sibling requires.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-27 22:25:24 +00:00
bbassieandClaude Fable 5 a25f8c8611 Fix lua lint warnings reported by emmylua
The largest source was compat.lua returning the setmetatable call
directly, which made emmylua infer the module type from the metatable
rather than the LibCompat class, hiding every field from consumers.

The remaining fixes are nil guards on gui element and character
accesses, type annotations for research_sync's script data and
player_auth's rcon arguments, casts where the api's read/write union
types are wider than the values at runtime, and scoped diagnostic
disables for the vendored json.lua and the version fallback code that
touches apis removed from the current typedefs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-27 22:25:24 +00:00
bbassieandClaude Fable 5 6cf3ab7007 Compare link message serialization in version compat check
The smoke test only proves the handshake and a few core requests still
work, while compat most often breaks because the serialization of a
link message changed. Dump the registered link messages of the local
and published lib in separate processes (the lib forbids loading two
copies in one) and diff their src, dst and json schemas. A changed
message fails the test, added and removed messages only produce a
warning annotation since breaking compat is unavoidable there.

Requested by Cooldude2606 in review of #978.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-27 12:29:26 +00:00
bbassieandClaude Fable 5 687c6c15f5 Add CI check for controller and host version compatibility
Runs a smoke test in both directions between the current code and the
latest release published to npm: the host connects to the controller and
gets an instance assigned. Testing only against the latest release relies
on each release being compatible with the previous one.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-27 11:55:07 +00:00
Cooldude2606andClaude Fable 5 59cd193b65 Support enemy_expansion.build_base_unit_dispatch_cooldown in exchange strings (#977)
* Support enemy_expansion.build_base_unit_dispatch_cooldown in exchange strings

Factorio 2.1.13 added build_base_unit_dispatch_cooldown as the last
field of enemy_expansion in the map exchange string, and rejects map
settings without it. Track the full version in the reader state and
read the field for 2.1.13 and later. Adds a fixture string generated
with 2.1.14 and uses it in the load-scenario integration test.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Replace version flags with versionAtLeast in exchange string reader

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-19 23:24:58 +01:00
BastiaanandClaude Fable 5 7bd408b45e Restore pagination on the instance save list (#973)
useTableQueryState returns pagination=false unless a pagination config
is passed, and SavesList did not pass one after #930, so the saves table
rendered all saves on one page.

Fixes #967

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-19 23:22:57 +01:00
Cooldude2606andClaude Fable 5 00ce7cdca6 Filter dependency resolution by the mod pack's Factorio version (#974)
* Filter dependency resolution by the mod pack's Factorio version

The dependency resolver picked the newest release satisfying the version
range regardless of which Factorio version it targeted, so a 2.0 mod pack
could end up with 2.1 dependencies. Only consider portal releases and
local mods whose factorio_version matches the mod pack.

Fixes #965

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Move dependency resolver tests to test/messages/mod.test.js

Also update the existing local mod fixtures to carry a factorio_version
and drop enable-all-feature-flags from the live expectations: the 2.0
releases of pyalternativeenergy only list it as optional, it was only
pulled in before because 2.1 releases were being selected.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-19 15:21:21 +01:00
BastiaanandClaude Fable 5 1c5210c210 Handle create_character failures in inventory sync (#975)
ensure_character asserted on player.create_character() returning false
without any context (#912). The most likely trigger is the player being
physically inside a space platform hub (or otherwise stuck in remote view)
on the destination server: the character in the hub is destroyed while
downloading and the god controller ends up on the platform surface where
a character can not be created.

- Explicitly exit remote view and move the god controller off platform
  surfaces before create_character; restore_position re-enters the platform
- Include controller/surface/connection state in the error message
- On deserialization failure treat the player as a temporary inventory
  (sync = false) so the incomplete state is not uploaded over synced data
- Release the acquisition on leave when nothing is uploaded so the player
  is not left "busy" on other instances

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-19 15:12:30 +01:00
Cooldude2606andClaude Fable 5 b5a0d43c96 Add NO_EXTERNAL_TEST flag to skip tests using external APIs (#976)
* Add NO_EXTERNAL_TEST flag to skip tests using external APIs

Adds an externalTest(this) helper alongside slowTest that skips the test
when NO_EXTERNAL_TEST is set and raises the timeout to 60s, plus a
no-external-test script. Marks the tests hitting factorio.com, the mod
portal and GitHub with it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Disable user verification in start-all and auto_start tests

Factorio contacts auth.factorio.com on startup when
require_user_verification is enabled, which made these tests fail
offline. Match the other integration tests by turning it off.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-19 15:07:08 +01:00
BastiaanandClaude Fable 5 c17c582818 Download Factorio in host tests to temp/test instead of test/file (#972)
* Ignore Factorio download directory created by host tests

The live-API download test in test/host/server.js writes a full headless
Factorio install (~140 MB) to test/file/factorioDownload and does not
remove it, which then shows up as untracked files.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Download Factorio in host tests to temp/test instead of test/file

The live-API download test wrote a full headless install (~140 MB) into
test/file/factorioDownload, which is tracked test fixture territory and
showed up as untracked files. temp/ is already gitignored and is where
the other tests put generated data.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-19 15:00:55 +01:00
bbassieandClaude Fable 5 d3c618a7ad Replace version flags with versionAtLeast in exchange string reader
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-18 22:57:36 +00:00
bbassieandClaude Fable 5 021ee1725b Support enemy_expansion.build_base_unit_dispatch_cooldown in exchange strings
Factorio 2.1.13 added build_base_unit_dispatch_cooldown as the last
field of enemy_expansion in the map exchange string, and rejects map
settings without it. Track the full version in the reader state and
read the field for 2.1.13 and later. Adds a fixture string generated
with 2.1.14 and uses it in the load-scenario integration test.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-18 22:21:28 +00:00
bbassieandClaude Fable 5 ffc79ea0e7 Disable user verification in start-all and auto_start tests
Factorio contacts auth.factorio.com on startup when
require_user_verification is enabled, which made these tests fail
offline. Match the other integration tests by turning it off.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-18 20:26:30 +00:00
bbassieandClaude Fable 5 0cfa048d42 Add NO_EXTERNAL_TEST flag to skip tests using external APIs
Adds an externalTest(this) helper alongside slowTest that skips the test
when NO_EXTERNAL_TEST is set and raises the timeout to 60s, plus a
no-external-test script. Marks the tests hitting factorio.com, the mod
portal and GitHub with it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-18 19:55:18 +00:00
bbassieandClaude Fable 5 5e633ac2f1 Move dependency resolver tests to test/messages/mod.test.js
Also update the existing local mod fixtures to carry a factorio_version
and drop enable-all-feature-flags from the live expectations: the 2.0
releases of pyalternativeenergy only list it as optional, it was only
pulled in before because 2.1 releases were being selected.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-18 16:54:19 +00:00
bbassieandClaude Fable 5 5e6b06f009 Filter dependency resolution by the mod pack's Factorio version
The dependency resolver picked the newest release satisfying the version
range regardless of which Factorio version it targeted, so a 2.0 mod pack
could end up with 2.1 dependencies. Only consider portal releases and
local mods whose factorio_version matches the mod pack.

Fixes #965

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-18 14:30:32 +00:00