Commit Graph
555 Commits
Author SHA1 Message Date
Cooldude2606 c0138c3fe0 Merge pull request #961 from bbassie/feature/instance-host-broadcast
Deliver broadcasts to all hosts, not just the local one
2026-08-08 23:51:48 +01:00
Cooldude2606 e9450db83b Move test location 2026-08-08 23:45:03 +01:00
Cooldude2606 bdf5e3011f Merge pull request #959 from bbassie/feature/forwarded-request-cleanup
Forget a forwarded request that could not be sent
2026-08-08 23:34:19 +01:00
Cooldude2606 6e74db1f9e Merge pull request #963 from lntutor/feat/transitive-plugin-discovery-953
Discover transitive NPM plugins
2026-07-31 18:44:55 +01:00
Cooldude2606 bfbd41d64b Cleaner path resolution and tests 2026-07-31 18:33:19 +01:00
Loi Nguyen dfb6037797 fix(lib): address transitive plugin review 2026-07-31 00:01:16 +07:00
Cooldude2606 3a689b379a Merge branch 'master' into feature/download-file-cleanup 2026-07-29 23:13:29 +01:00
Cooldude2606 e4c2ccd58f Merge pull request #954 from bbassie/feature/redact-mod-portal-token
Redact credentials from URLs in download errors
2026-07-29 23:02:12 +01:00
Cooldude2606 417ee87422 Maintain repeated credential parameters 2026-07-29 22:56:19 +01:00
Cooldude2606 d7eb647c09 Merge pull request #952 from lntutor/feat/mod-info-recommendations-949
Show informational mod recommendations
2026-07-26 12:34:09 +01:00
Loi NguyenandLoi Nguyen 08651b5917 fix: prevent restart downgrades (#951)
Co-authored-by: Loi Nguyen <1948922+lntutor@users.noreply.github.com>
2026-07-26 12:20:22 +01:00
Loi Nguyen dfe52df7bb Show informational mod recommendations 2026-07-26 17:05:44 +07:00
Loi Nguyen 40a6142e3e feat(lib): discover transitive npm plugins 2026-07-26 13:23:20 +07:00
bbassieandClaude Opus 4.8 09c667252c Deliver broadcasts to all hosts, not just the local one
An event broadcast from an instance to all hosts only reached the host
running that instance. The host side of an instance connection has the
host's own address as its source, so a broadcast aimed at hosts counts as
addressed to it, and a link that a message is addressed to handles it and
stops. The event was delivered locally and never routed onwards.

A broadcast is addressed to every link of the type it targets, so arriving
at one of them does not mean it has arrived everywhere. Keep routing such a
message after handling it locally. Only links that route are affected, as
instances and controls have no router and would otherwise throw on every
broadcast they receive.

The only links whose source type matches a broadcast target and which route
are the host and its instance connections, both matching a broadcast to
hosts, so add the missing case for that to the host router. It forwards to
the controller, which already passes broadcasts on to every other host, and
stops when the message came from the controller to begin with.

Drop the exclusion this bug had in the routing test matrix, which now
covers it along with the plugin filtering variants.

Fixes #575

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-25 17:39:25 +00:00
Cooldude2606 9ff090c30b Merge pull request #958 from bbassie/feature/subscription-event-address
Stop addressing subscription events to a request id
2026-07-25 15:28:55 +01:00
Cooldude2606 9fe1819ea5 Fix tests 2026-07-25 15:23:34 +01:00
Cooldude2606 870e1f2125 Remove unneeded comments 2026-07-25 14:51:14 +01:00
bbassieandClaude Opus 4.8 70305b7937 Silence the deprecation warning on install
execFile was called with shell: true and an array of arguments, which Node
deprecates in DEP0190 because it concatenates the arguments by space
without escaping anything, giving the impression they have been made safe.
The warning was printed to the console on every install.

The arguments here are already escaped for the shell by escapeArg, so join
them into the command instead of handing them to execFile to concatenate.
Nothing about what ends up being run changes.

Point the escaping test at the same form so it covers what the installer
actually does, and stops emitting the warning itself. Its several hundred
round trip cases still pass, which is what confirms the escaping is
unaffected.

Fixes #851

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-23 23:02:37 +00:00
bbassieandClaude Opus 4.8 3512042cf3 Forget a forwarded request that could not be sent
forwardRequest records a request so that its response can be sent back the
way it came, but recorded it before handing the message to the connector.
When the connector threw, because the session to the next hop was gone, the
record stayed behind for a request that had never left.

Routers answer the origin themselves when forwarding throws, so the request
was already accounted for. The leftover record then had it answered a
second time when the link cleared its pending requests on disconnect,
producing a duplicate response and "Received response N without a pending
request" at the far end.

Remove the record when the forward throws. Doing it here rather than in the
routers covers both the host, which catches and responds, and the
controller router, which does not.

Fixes #650

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-23 22:58:37 +00:00
bbassieandClaude Opus 4.8 4a9c241eee Stop addressing subscription events to a request id
A subscribe arrives as a request, so the source address the subscription
controller handles it with carries the request id used to route the
response back to the caller. That address was stored verbatim as the
destination for the subscription, so every event broadcast to it, and the
replay event sent when the subscription is created, went out addressed to
a request that had long since been answered.

A request id identifies one request in flight and has no meaning on an
event. The receiving link keys response routing off it, so events carrying
one are put through paths meant for response traffic.

Strip the request id when taking the subscription's destination from the
request that created it. Add Address.withoutRequestId for that, as the
same address is used in three places here.

The subscription map is keyed on addressIndex, which already excludes the
request id, so subscriptions were never duplicated by this and only the
address events are sent to was wrong.

Fixes #847

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-23 22:49:04 +00:00
bbassieandClaude Opus 4.8 3883c1932c Discard the temporary file when a download fails
downloadFile opens a write stream before fetching, but only closed the
stream and removed the file it was writing to when the server answered
with a bad status. A request that never reached the server, or a
connection dropped part way through the body, left both the temporary file
and an open file handle behind.

Mods are downloaded through this in a Promise.all over every mod in a pack,
so one bad moment on the network scatters temporary files across the mods
directory. They are not harmless: createTempWriteStream picks the first
free name, so each leftover file permanently shifts the name used for
later downloads of the same mod, as the existing "should not overwrite
existing temp files" test describes.

Move the fetch and the body write into a try block and discard the stream
and its file on the way out. Destroy rather than end the stream, as the
content is being thrown away, and skip both if the stream has already
closed so that a failure after the body was written does not wait forever
for a close event that has already fired.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-23 22:26:18 +00:00
bbassieandClaude Opus 4.8 feb0e46aab Redact credentials from URLs in download errors
The mod portal takes the Factorio account token as a query parameter on the
download URL. When a download failed, downloadFile put the whole URL into the
error message, which ModStore then logged verbatim, printing the token into the
log at error level on every failed mod download.

Add a redactUrl helper that replaces the value of credential-carrying query
parameters with REDACTED, and use it when downloadFile builds its error.
Parameter names and the rest of the URL are kept so the message stays useful
for diagnosing which request failed; username is deliberately left intact as it
is not a secret and identifies the account in use.

Fixes #795

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-23 20:38:09 +00:00
Cooldude2606 ff28357d7a Remove hard coded versions 2026-07-18 22:12:15 +01:00
Cooldude2606 8b4c8ad337 New exchange string format for enemy expansion settings 2026-07-18 22:12:15 +01:00
Cooldude2606 198ea76674 Add support for recommended mod dependencies (#946) 2026-07-18 21:57:50 +01:00
Cooldude2606 15781bb038 Setting filepath on config makes it dirty 2026-07-10 22:00:42 +01:00
Cooldude2606 07fcdb0d4e Merge pull request #942 from Cooldude2606/refactor/sub-filters-again
Make SubscriberFilter extend Set and expose errors in EventSubscriber
2026-07-08 08:30:37 +01:00
Cooldude2606 573274875c Rename to SourceVersion 2026-07-02 00:27:05 +01:00
bbassieandClaude Opus 4.8 8029ad3b2d Resolve GameVersion type as a plain-string supertype
Cooldude's GameVersion brand was disjoint from the clean version types in
both directions, forcing safeVersion/safeFactorioVersion companions and
breaking widening from FullVersion at file-name call sites.

Make GameVersion a plain string alias: the raw, un-normalised version as the
game reads it. The stricter template-literal types (FullVersion,
MajorMinorVersion, PartialVersion) now widen into it for free, while a
GameVersion still can't be used where a normalised version is required
without going through normaliseGameVersion/integerGameVersion. Runtime
validation stays in GameVersionSchema.

- Drop safeVersion/safeFactorioVersion; ModInfo.version stays raw (verbatim so
  it matches the mod's file name), integerVersion normalises via the new
  integerGameVersion. factorioVersion reverts to a normalised MajorMinorVersion
  since it is never part of a file name.
- ModRecord.version becomes a GameVersion so mod packs round-trip the exact
  file-name version.
- ModVersionEquality.testVersion / ModVersionRange.testVersion accept a raw
  GameVersion and normalise it; the equality bound stays strictly validated.
- Migration only pads clean partial versions; raw versions are kept verbatim.
- Cover the leading-zeros case: version kept verbatim in the file name while
  comparing equal to its normalised form.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 13:54:06 +00:00
Cooldude2606 523293d4d9 Manual adjustments to GameVersion and ReleaseChannel types
Through the creation of this commit, it was discovered that ModInfo.version must be typed as GameVersion due to file names. This results in wide spread propagation of the unsafe type.  The full impact including on ModRecord and ModVersion.testVersion have no been implemented until a path forward is decided.
2026-06-30 23:39:08 +01:00
bbassieandClaude Opus 4.8 5d20f8fc84 Revert strict ModInfo.jsonSchema and cover the factorio_version error
The strict major.minor jsonSchema did not improve any consuming type (the
factorioVersion field is already typed) and only complicated the fromJSON
signature, so revert it (and drop the now-unused MajorMinorVersionSchema).
Add a test for the unparseable factorio_version branch in fromJSON.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-29 23:20:37 +00:00
bbassie e27ad5fdf9 Merge remote-tracking branch 'upstream/master' into feature/lenient-mod-version-parsing
# Conflicts:
#	packages/lib/src/data/version.ts
2026-06-29 22:57:48 +00:00
bbassieandClaude Opus 4.8 7c2793752d Add MajorMinorVersion type and dedupe the game version regex
Address review feedback: introduce a MajorMinorVersion type with
isMajorMinorVersion, rename majorMinorVersion to normaliseMajorMinorVersion,
and type ModInfo.factorioVersion as MajorMinorVersion so already-reduced
values are not re-reduced (ModPackViewPage uses mod.info.factorioVersion
directly). Reuse the single gameVersionRegExp in both GameVersionSchema and
normaliseGameVersion instead of duplicating the pattern.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-29 22:12:36 +00:00
Cooldude2606 7e89e7fb6f Fix assert in test case 2026-06-26 22:15:28 +01:00
Cooldude2606 95ea073b77 Expose error on event subscriber snapshot 2026-06-26 22:04:06 +01:00
bbassieandClaude Opus 4.8 97a6c32838 Parse mod versions leniently like the game does
The mod portal "ApiVersion" enum was validated strictly when loading a
mod's info.json, which rejected versions the game itself accepts. Versions
are now normalised the same lenient way the game reads them: leading
whitespace is allowed, the major and minor are required, the patch is
optional, any trailing content is ignored, and numbers are read as integers.

This applies to both a mod's version and its factorio_version, validated by
a regex schema rather than the fixed enum. The enum is no longer used to
validate versions at all - the mod portal is the source of truth, so its
error is now surfaced when a version is unsupported. The known-version list
kept for the web interface fallback gains 2.1.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 16:24:43 +00:00
bbassieandClaude Opus 4.8 dfe132699e Add integration test for starting an instance with a release channel
Configures an instance with the "experimental" channel and starts it,
exercising channel resolution end to end across the controller and host.
The resolved version may or may not be installed depending on the host's
Factorio install, so the test asserts the instance reaches a terminal state
rather than a specific outcome.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 15:38:29 +00:00
bbassieandClaude Opus 4.8 f793ec33b3 Add tests for release channel resolution and caching
Cover fetchLatestReleases and resolveReleaseChannel, the controller's
latest-releases cache with its on-disk offline fallback, the
LatestReleasesRequest round-trip, and FactorioServer.setTargetVersion.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 15:10:06 +00:00
bbassieandClaude Opus 4.8 9da48d5387 Add release channels as instance version targets
An instance's factorio.version can now target a release channel such as
"stable" or "experimental" (any key from factorio.com/api/latest-releases)
in addition to "latest" and specific X.Y / X.Y.Z versions.

The controller proxies the latest-releases API through a ValueCache and
persists the last successful response to latest-releases.json, serving that
copy when the API is unreachable. On instance start the host resolves a
channel to the concrete version it points to and feeds it into the existing
checkForUpdates/findVersion path, so a channel auto-installs and runs like a
pinned version. The version selector in the web UI lists the available
channels alongside "latest".

Also clarifies the host log/error when a newer version cannot be downloaded
automatically because the host uses a direct (single-version) Factorio
install rather than a versioned directory.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 14:19:34 +00:00
Cooldude2606 e3d5f434a4 Better toString and test coverage 2026-05-29 20:14:23 +01:00
Cooldude2606 e54b292fce Fix tests relating to permissions 2026-05-29 18:38:07 +01:00
Cooldude2606 41222343e3 Add tests 2026-05-29 17:45:14 +01:00
Hornwitser 2e48b728dd Remove fs-extra and klaw (#882)
These packages are obsoleted by the promises API to the built-in Node fs
module.  The fs-extra package is also causing problems with broken APIs
that don't behave the same as Node's fs module, and throwing errors
without code properties on them.
2026-04-09 22:53:13 +02:00
Cooldude2606 2afd7aab38 Fix failing tests 2026-03-24 18:25:53 +00:00
Cooldude2606 790c2834f2 Add validator to load_plugin 2026-03-24 17:36:19 +00:00
Cooldude2606 81e419dad4 Merge pull request #856 from Cooldude2606/feature/config-validation
Add extended config validation
2026-03-21 00:15:16 +00:00
Cooldude2606 93271730eb Make configs readonly inside validators 2026-03-20 18:56:55 +00:00
Cooldude2606 5d0985cafc Fix tests not checking that validators are called 2026-03-20 17:42:48 +00:00
Hornwitser 86eefab9f5 Refactor file write streams (#877)
A lot of code is repeated in various different ways in order to handle
streaming content to files without overwriting them, whether it's from
web resources or local streams.

Refactor the handling of these streams to all use the same utility
function for creating a write stream, and for the places downloading
urls use the same downloadFile function.
2026-03-19 21:55:52 +01:00
Cooldude2606 f0fb52ab74 Revert change to mock exports 2026-03-18 00:17:17 +00:00