Rename to SourceVersion

This commit is contained in:
Cooldude2606 committed 2026-07-02 00:27:05 +01:00
1 parent 2e27c4f499
commit 573274875c
12 files changed
+70 -67

No files matched your search

+1 -1
View File
@@ -80,7 +80,7 @@ export default class BaseConnection extends lib.Link {
return modPack;
}
getMod(mod: { name: string, version: lib.GameVersion, sha1?: string }) {
getMod(mod: { name: string, version: lib.SourceVersion, sha1?: string }) {
let filename = lib.ModInfo.filename(mod.name, mod.version);
let modInfo = this._controller.modStore.files.get(filename);
if (!modInfo) {
+1 -1
View File
@@ -707,7 +707,7 @@ export default class ControlConnection extends BaseConnection {
const release = releases.releases
.filter(info => versionRange.testVersion(info.version))
.reduce<ModPortalReleaseType | undefined>((max, cur) => (
max && lib.integerGameVersion(max.version) > lib.integerGameVersion(cur.version) ? max : cur
max && lib.integerSourceVersion(max.version) > lib.integerSourceVersion(cur.version) ? max : cur
), undefined);
if (!release) {
+1 -1
View File
@@ -766,7 +766,7 @@ export default class Controller {
// migrate: 2.0.0-alpha.22 - old buildins used X.Y rather than X.Y.Z
// All other versions are kept verbatim so they still match the mod's file name.
if (lib.isPartialVersion(mod.version)) {
mod.version = lib.normaliseFullVersion(mod.version) as lib.GameVersion;
mod.version = lib.normaliseFullVersion(mod.version) as lib.SourceVersion;
}
}
return json;
+4 -4
View File
@@ -20,7 +20,7 @@ modCommands.add(new lib.Command({
yargs.positional("mod-version", { describe: "Version of the mod", type: "string" });
}],
handler: async function(args: { name: string, modVersion: string }, control: Control) {
if (!lib.isGameVersion(args.modVersion)) {
if (!lib.isSourceVersion(args.modVersion)) {
throw new lib.CommandError("mod-version must match format digit.digit.digit");
}
let modInfo = await control.send(new lib.ModGetRequest(args.name, args.modVersion));
@@ -108,7 +108,7 @@ modCommands.add(new lib.Command({
},
control: Control
) {
const searchVersion = lib.normaliseGameVersion(String(args.factorioVersion));
const searchVersion = lib.normaliseSourceVersion(String(args.factorioVersion));
if (searchVersion === undefined) {
throw new Error(`Invalid factorio version "${args.factorioVersion}"`);
}
@@ -191,7 +191,7 @@ modCommands.add(new lib.Command({
yargs.positional("mod-version", { describe: "Version of mod to download", type: "string" });
}],
handler: async function(args: { name: string, modVersion: string }, control: Control) {
if (!lib.isGameVersion(args.modVersion)) {
if (!lib.isSourceVersion(args.modVersion)) {
throw new lib.CommandError("mod-version must match format digit.digit.digit");
}
let streamId = await control.send(new lib.ModDownloadRequest(args.name, args.modVersion));
@@ -211,7 +211,7 @@ modCommands.add(new lib.Command({
yargs.positional("mod-version", { describe: "Version of mod to delete", type: "string" });
}],
handler: async function(args: { name: string, modVersion: string }, control: Control) {
if (!lib.isGameVersion(args.modVersion)) {
if (!lib.isSourceVersion(args.modVersion)) {
throw new lib.CommandError("mod-version must match format digit.digit.digit");
}
await control.send(new lib.ModDeleteRequest(args.name, args.modVersion));
+1 -1
View File
@@ -132,7 +132,7 @@ function setModPackMods(modPack: lib.ModPack, mods: string[] | undefined) {
if (!version) {
throw new lib.CommandError("Added mod must be formatted as name:version or name:version:sha1");
}
if (!lib.isGameVersion(version)) {
if (!lib.isSourceVersion(version)) {
throw new lib.CommandError("version must match the format digit.digit.digit");
}
if (sha1 && !/^[0-9a-f]{40}$/.test(sha1)) {
+4 -4
View File
@@ -5,7 +5,7 @@ import { Static } from "@sinclair/typebox";
import { logger } from "./logging";
import { downloadFile, safeOutputFile } from "./file_ops";
import { ModPortalReleaseSchema, ModPortalDetailsSchema, ModNameVersionPair } from "./data/messages_mod";
import { ModInfo, ModPack, MajorMinorVersion, ModVersionEquality, GameVersion } from "./data";
import { ModInfo, ModPack, MajorMinorVersion, ModVersionEquality, SourceVersion } from "./data";
export interface ModStoreEvents {
/** A stored mod was created, updated or deleted */
@@ -44,7 +44,7 @@ export default class ModStore extends events.EventEmitter<ModStoreEvents> {
super();
}
getMod(name: string, version: GameVersion, sha1?: string) {
getMod(name: string, version: SourceVersion, sha1?: string) {
const mod = this.files.get(ModInfo.filename(name, version));
if (!mod || sha1 && sha1 !== mod.sha1) {
return undefined;
@@ -86,11 +86,11 @@ export default class ModStore extends events.EventEmitter<ModStoreEvents> {
this.emit("change", modInfo);
}
async loadMod(name: string, version: GameVersion) {
async loadMod(name: string, version: SourceVersion) {
return await this.loadFile(ModInfo.filename(name, version));
}
async deleteMod(name: string, version: GameVersion) {
async deleteMod(name: string, version: SourceVersion) {
return await this.deleteFile(ModInfo.filename(name, version));
}
+10 -10
View File
@@ -9,7 +9,7 @@ import { ModRecord } from "./ModPack";
import {
MajorMinorVersion, MajorMinorVersionSchema, normaliseMajorMinorVersion, integerMajorMinorVersion,
GameVersion, GameVersionSchema, isGameVersion, normaliseGameVersion, integerGameVersion,
SourceVersion, SourceVersionSchema, isSourceVersion, normaliseSourceVersion, integerSourceVersion,
ModVersionEquality,
} from "./version";
@@ -133,18 +133,18 @@ export default class ModInfo {
* it is only safe to use for building file names and identifiers; use
* {@link integerVersion} to compare it.
*/
version = "0.0.0" as GameVersion;
version = "0.0.0" as SourceVersion;
/**
* Integer representation of the version, normalised the lenient way the
* game reads it so it can be compared and sorted.
*/
get integerVersion() {
return integerGameVersion(this.version);
return integerSourceVersion(this.version);
}
get normalisedVersion() {
return normaliseGameVersion(this.version);
return normaliseSourceVersion(this.version);
}
/**
@@ -227,7 +227,7 @@ export default class ModInfo {
* @param version - Mod's version
* @returns string containing {name}_{version}.zip
*/
static filename(name: string, version: GameVersion) {
static filename(name: string, version: SourceVersion) {
return `${name}_${version}.zip`;
}
@@ -258,13 +258,13 @@ export default class ModInfo {
// lenient format the game accepts; they are normalised when read in fromJSON.
static infoJsonSchema = Type.Object({
"name": Type.String(),
"version": GameVersionSchema,
"version": SourceVersionSchema,
"title": Type.String(),
"author": Type.String(),
"contact": Type.Optional(Type.String()),
"homepage": Type.Optional(Type.String()),
"description": Type.Optional(Type.String()),
"factorio_version": Type.Optional(GameVersionSchema),
"factorio_version": Type.Optional(SourceVersionSchema),
"dependencies": Type.Optional(Type.Array(Type.String())),
});
@@ -287,7 +287,7 @@ export default class ModInfo {
// info.json fields
if (json.name) { modInfo.name = json.name; }
if (json.version) {
if (!isGameVersion(json.version)) {
if (!isSourceVersion(json.version)) {
throw new Error(`Invalid mod version "${json.version}"`);
}
modInfo.version = json.version;
@@ -298,7 +298,7 @@ export default class ModInfo {
if (json.homepage) { modInfo.homepage = json.homepage; }
if (json.description) { modInfo.description = json.description; }
if (json.factorio_version) {
const factorioVersion = normaliseGameVersion(json.factorio_version);
const factorioVersion = normaliseSourceVersion(json.factorio_version);
if (factorioVersion === undefined) {
throw new Error(`Invalid factorio_version "${json.factorio_version}"`);
}
@@ -332,7 +332,7 @@ export default class ModInfo {
if (this.contact) { json.contact = this.contact; }
if (this.homepage) { json.homepage = this.homepage; }
if (this.description) { json.description = this.description; }
if (this.factorioVersion !== "0.12") { json.factorio_version = this.factorioVersion as GameVersion; }
if (this.factorioVersion !== "0.12") { json.factorio_version = this.factorioVersion as SourceVersion; }
if (this.dependencies.length !== 1 || this.dependencies[0].name !== "base") {
json.dependencies = this.dependencySpecifications;
}
+4 -4
View File
@@ -12,7 +12,7 @@ import ModInfo, { ModDependencyUnsatisfiedReason } from "./ModInfo";
import {
PartialVersion, PartialVersionSchema, integerPartialVersion,
GameVersion, GameVersionSchema, normaliseFullVersion,
SourceVersion, SourceVersionSchema, normaliseFullVersion,
} from "./version";
@@ -47,7 +47,7 @@ export interface ModRecord {
/** if mod is to be loaded. */
enabled: boolean,
/** version of the mod, verbatim as it appears in the mod's file name. */
version: GameVersion,
version: SourceVersion,
/** SHA1 hash of the zip file. */
sha1?: string,
/** Used inside packages\web_ui\src\components\ModPackViewPage.tsx to define an error type. */
@@ -61,7 +61,7 @@ export interface ModRecord {
const ModRecordJsonSchema = Type.Object({
"name": Type.String(),
"enabled": Type.Boolean(),
"version": GameVersionSchema,
"version": SourceVersionSchema,
"sha1": Type.Optional(Type.String()),
});
@@ -434,7 +434,7 @@ export default class ModPack {
* @return Built in mods for the given version
*/
static getBuiltinMods(factorioVersion: PartialVersion) {
const version = normaliseFullVersion(factorioVersion) as GameVersion;
const version = normaliseFullVersion(factorioVersion) as SourceVersion;
const integerVersion = integerPartialVersion(factorioVersion);
let defaultMods: ModRecord[] = [
// "core" not included because core cannot be disabled
+9 -9
View File
@@ -5,7 +5,7 @@ import { JsonString, jsonArray } from "./composites";
import {
MajorMinorVersion, MajorMinorVersionSchema, normaliseMajorMinorVersion,
GameVersion, GameVersionSchema,
SourceVersion, SourceVersionSchema,
ModVersionEquality,
} from "./version";
@@ -119,13 +119,13 @@ export class ModGetRequest {
constructor(
public name: string,
public version: GameVersion,
public version: SourceVersion,
public sha1?: string,
) { }
static jsonSchema = Type.Object({
"name": Type.String(),
"version": GameVersionSchema,
"version": SourceVersionSchema,
"sha1": Type.Optional(Type.String()),
});
@@ -205,9 +205,9 @@ export class ModSearchRequest {
// Define the structure for the latest release info from the portal
export const ModPortalReleaseSchema = Type.Object({
version: GameVersionSchema,
version: SourceVersionSchema,
// Match the structure from ModStore's ModRelease/ModDetails
info_json: Type.Object({ factorio_version: GameVersionSchema }),
info_json: Type.Object({ factorio_version: SourceVersionSchema }),
released_at: Type.String(), // ISO 8601 date string
download_url: Type.String(),
file_name: Type.String(),
@@ -276,13 +276,13 @@ export class ModDownloadRequest {
constructor(
public name: string,
public version: GameVersion,
public version: SourceVersion,
public sha1?: string,
) { }
static jsonSchema = Type.Object({
"name": Type.String(),
"version": GameVersionSchema,
"version": SourceVersionSchema,
"sha1": Type.Optional(Type.String()),
});
@@ -302,12 +302,12 @@ export class ModDeleteRequest {
constructor(
public name: string,
public version: GameVersion,
public version: SourceVersion,
) { }
static jsonSchema = Type.Object({
"name": Type.String(),
"version": GameVersionSchema,
"version": SourceVersionSchema,
});
static fromJSON(json: Static<typeof this.jsonSchema>) {
+19 -19
View File
@@ -36,20 +36,24 @@ export const ApiVersions = ["0.13", "0.14", "0.15", "0.16", "0.17", "0.18", "1.0
* optional, and any trailing content is ignored. The pattern is intentionally
* not anchored at the end.
*/
const gameVersionRegExp = /^\s*(\d+)\.\s*(\d+)(?:\.\s*(\d+))?/;
const sourceVersionRegExp = /^\s*(\d+)\.\s*(\d+)(?:\.\s*(\d+))?/;
/**
* A raw, un-normalised version string exactly as the game reads it, such as a
* mod's version taken from info.json or a mod file name.
*/
declare const gameVersionSymbol: unique symbol;
export type GameVersion = string & { [gameVersionSymbol]: void };
export const GameVersionSchema = Type.Unsafe<GameVersion>(
Type.String({ pattern: gameVersionRegExp.source })
declare const sourceVersionSymbol: unique symbol;
export type SourceVersion = string & { [sourceVersionSymbol]: void };
export const SourceVersionSchema = Type.Unsafe<SourceVersion>(
Type.String({ pattern: sourceVersionRegExp.source })
);
export function isGameVersion(input: string): input is GameVersion {
return gameVersionRegExp.test(input);
export function isSourceVersion(input: string): input is SourceVersion {
return sourceVersionRegExp.test(input);
}
export function integerSourceVersion(version: SourceVersion) {
return integerFullVersion(normaliseSourceVersion(version));
}
/**
@@ -60,21 +64,17 @@ export function isGameVersion(input: string): input is GameVersion {
* @returns The normalised full version, or undefined if no major and minor
* could be read.
*/
export function normaliseGameVersion(input: GameVersion): FullVersion;
export function normaliseGameVersion(input: string): FullVersion | undefined;
export function normaliseSourceVersion(input: SourceVersion): FullVersion;
export function normaliseSourceVersion(input: string): FullVersion | undefined;
export function normaliseGameVersion(input: string | GameVersion): FullVersion | undefined {
const match = gameVersionRegExp.exec(input);
export function normaliseSourceVersion(input: string | SourceVersion): FullVersion | undefined {
const match = sourceVersionRegExp.exec(input);
if (match === null) {
return undefined;
}
return `${Number(match[1])}.${Number(match[2])}.${Number(match[3] ?? "0")}` as FullVersion;
}
export function integerGameVersion(version: GameVersion) {
return integerFullVersion(normaliseGameVersion(version));
}
/**
* Matches valid mod versions, where all parts are specified.
*/
@@ -213,8 +213,8 @@ export class ModVersionEquality {
}
}
testVersion(version: GameVersion) {
return this.testIntegerVersion(integerGameVersion(version));
testVersion(version: SourceVersion) {
return this.testIntegerVersion(integerSourceVersion(version));
}
toString() {
@@ -276,11 +276,11 @@ export class ModVersionRange {
return this.minVersion.testIntegerVersion(other) && this.maxVersion.testIntegerVersion(other);
}
testVersion(version: PartialVersion | GameVersion) {
testVersion(version: PartialVersion | SourceVersion) {
if (isPartialVersion(version)) {
return this.testIntegerVersion(integerPartialVersion(version));
}
return this.testIntegerVersion(integerGameVersion(version));
return this.testIntegerVersion(integerSourceVersion(version));
}
combineVersion(other: ModVersionEquality) {
+5 -2
View File
@@ -164,13 +164,16 @@ function SearchModsButton() {
const handleControllerDownload = (
modName: string,
modTitle: string | undefined,
modVersion: lib.GameVersion | undefined,
modVersion: lib.SourceVersion | undefined,
portalFactorioVersion: lib.MajorMinorVersion,
) => {
if (modVersion) {
control.send(
new lib.ModPortalDownloadRequest(
[{ name: modName, version: new lib.ModVersionEquality("=", lib.normaliseGameVersion(modVersion))}],
[{
name: modName,
version: new lib.ModVersionEquality("=", lib.normaliseSourceVersion(modVersion)),
}],
portalFactorioVersion
)
).then(() => {
+11 -11
View File
@@ -17,25 +17,25 @@ describe("lib/data/version", function() {
}
});
});
describe("normaliseGameVersion()", function() {
describe("normaliseSourceVersion()", function() {
it("should normalise lenient version strings the way the game reads them", function() {
// Major and minor required, patch optional and defaulting to 0.
assert.equal(lib.normaliseGameVersion("1.1"), "1.1.0");
assert.equal(lib.normaliseGameVersion("1.1.50"), "1.1.50");
assert.equal(lib.normaliseGameVersion("0.18"), "0.18.0");
assert.equal(lib.normaliseSourceVersion("1.1"), "1.1.0");
assert.equal(lib.normaliseSourceVersion("1.1.50"), "1.1.50");
assert.equal(lib.normaliseSourceVersion("0.18"), "0.18.0");
// Leading whitespace and zeros are dropped.
assert.equal(lib.normaliseGameVersion(" 1.1"), "1.1.0");
assert.equal(lib.normaliseGameVersion("01.02.03"), "1.2.3");
assert.equal(lib.normaliseGameVersion("1. 1. 50"), "1.1.50");
assert.equal(lib.normaliseSourceVersion(" 1.1"), "1.1.0");
assert.equal(lib.normaliseSourceVersion("01.02.03"), "1.2.3");
assert.equal(lib.normaliseSourceVersion("1. 1. 50"), "1.1.50");
// Anything after the patch (or a non-numeric patch) is ignored.
assert.equal(lib.normaliseGameVersion("1.1.50-beta"), "1.1.50");
assert.equal(lib.normaliseGameVersion("1.1.x"), "1.1.0");
assert.equal(lib.normaliseGameVersion("1.1.50.60"), "1.1.50");
assert.equal(lib.normaliseSourceVersion("1.1.50-beta"), "1.1.50");
assert.equal(lib.normaliseSourceVersion("1.1.x"), "1.1.0");
assert.equal(lib.normaliseSourceVersion("1.1.50.60"), "1.1.50");
});
it("should return undefined when no major.minor can be read", function() {
const invalid = ["1", "", "latest", "abc", ".1.1", "x.y"];
for (const test of invalid) {
assert.equal(lib.normaliseGameVersion(test), undefined, test);
assert.equal(lib.normaliseSourceVersion(test), undefined, test);
}
});
});