Files
factorio-clusterio/packages/lib/src/data/messages_mod.ts
T
bbassieandClaude Opus 5.5 7d421847f5 Drop the self-referencing constructor type on ModPortalGetAllResponse
TypeScript 7.1 reports TS5088 for it: the inferred type of the static
Response property references itself through the declared constructor.
The other inline Response classes do not declare it either.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 01:11:00 +00:00

485 lines
13 KiB
TypeScript

import { Type, Static } from "@sinclair/typebox";
import ModInfo, { ModDependency } from "./ModInfo.js";
import ModPack from "./ModPack.js";
import { JsonString, jsonArray } from "./composites.js";
import {
MajorMinorVersion, MajorMinorVersionSchema, normaliseMajorMinorVersion,
SourceVersion, SourceVersionSchema,
ModVersionEquality,
} from "./version.js";
export class ModPackGetRequest {
declare ["constructor"]: typeof ModPackGetRequest;
static type = "request" as const;
static src = ["instance", "control"] as const;
static dst = "controller" as const;
static permission = "core.mod_pack.get" as const;
constructor(
public id: number,
) { }
static jsonSchema = Type.Object({
"id": Type.Integer(),
});
static fromJSON(json: Static<typeof this.jsonSchema>) {
return new this(json.id);
}
static Response = ModPack;
}
export class ModPackGetDefaultRequest {
declare ["constructor"]: typeof ModPackGetDefaultRequest;
static type = "request" as const;
static src = ["instance", "control"] as const;
static dst = "controller" as const;
static permission = "core.mod_pack.get" as const;
static Response = ModPack;
}
export class ModPackListRequest {
declare ["constructor"]: typeof ModPackListRequest;
static type = "request" as const;
static src = "control" as const;
static dst = "controller" as const;
static permission = "core.mod_pack.list" as const;
static Response = jsonArray(ModPack);
}
export class ModPackCreateRequest {
declare ["constructor"]: typeof ModPackCreateRequest;
static type = "request" as const;
static src = "control" as const;
static dst = "controller" as const;
static permission = "core.mod_pack.create" as const;
constructor(
public modPack: ModPack,
) { }
static jsonSchema = Type.Object({
"modPack": ModPack.jsonSchema,
});
static fromJSON(json: Static<typeof this.jsonSchema>) {
return new this(ModPack.fromJSON(json.modPack));
}
}
export class ModPackUpdateRequest {
declare ["constructor"]: typeof ModPackUpdateRequest;
static type = "request" as const;
static src = "control" as const;
static dst = "controller" as const;
static permission = "core.mod_pack.update" as const;
constructor(
public modPack: ModPack,
) { }
static jsonSchema = Type.Object({
"modPack": ModPack.jsonSchema,
});
static fromJSON(json: Static<typeof this.jsonSchema>) {
return new this(ModPack.fromJSON(json.modPack));
}
}
export class ModPackDeleteRequest {
declare ["constructor"]: typeof ModPackDeleteRequest;
static type = "request" as const;
static src = "control" as const;
static dst = "controller" as const;
static permission = "core.mod_pack.delete" as const;
constructor(
public id: number,
) { }
static jsonSchema = Type.Object({
"id": Type.Integer(),
});
static fromJSON(json: Static<typeof this.jsonSchema>) {
return new this(json.id);
}
}
export class ModGetRequest {
declare ["constructor"]: typeof ModGetRequest;
static type = "request" as const;
static src = "control" as const;
static dst = "controller" as const;
static permission = "core.mod.get" as const;
constructor(
public name: string,
public version: SourceVersion,
public sha1?: string,
) { }
static jsonSchema = Type.Object({
"name": Type.String(),
"version": SourceVersionSchema,
"sha1": Type.Optional(Type.String()),
});
static fromJSON(json: Static<typeof this.jsonSchema>) {
return new this(json.name, json.version, json.sha1);
}
static Response = ModInfo;
}
export class ModListRequest {
declare ["constructor"]: typeof ModListRequest;
static type = "request" as const;
static src = "control" as const;
static dst = "controller" as const;
static permission = "core.mod.list" as const;
static Response = jsonArray(ModInfo);
}
export class ModSearchRequest {
declare ["constructor"]: typeof ModSearchRequest;
static type = "request" as const;
static src = "control" as const;
static dst = "controller" as const;
static permission = "core.mod.search" as const;
constructor(
public query: string,
public factorioVersion: MajorMinorVersion,
public page: number,
public pageSize?: number,
public sort?: string,
public sortOrder?: string,
) { }
static jsonSchema = Type.Object({
"query": Type.String(),
"factorioVersion": MajorMinorVersionSchema,
"page": Type.Integer(),
"pageSize": Type.Optional(Type.Integer()),
"sort": Type.Optional(Type.String()),
"sortOrder": Type.Optional(Type.String()),
});
static fromJSON(json: Static<typeof this.jsonSchema>) {
return new this(json.query, json.factorioVersion, json.page, json.pageSize, json.sort, json.sortOrder);
}
static Response = class Response {
constructor(
public queryIssues: string[],
public pageCount: number,
public resultCount: number,
public results: { name: string, versions: ModInfo[] }[],
) { }
static jsonSchema = Type.Object({
"queryIssues": Type.Array(Type.String()),
"pageCount": Type.Integer(),
"resultCount": Type.Integer(),
"results": Type.Array(
Type.Object({
"name": Type.String(),
"versions": Type.Array(ModInfo.jsonSchema),
}),
),
});
static fromJSON(json: Static<typeof this.jsonSchema>) {
let results = json.results.map(
({ name, versions }) => ({ name, versions: versions.map(mod => ModInfo.fromJSON(mod)) })
);
return new this(json.queryIssues, json.pageCount, json.resultCount, results);
}
};
}
// Define the structure for the latest release info from the portal
export const ModPortalReleaseSchema = Type.Object({
version: SourceVersionSchema,
// Match the structure from ModStore's ModRelease/ModDetails
info_json: Type.Object({ factorio_version: SourceVersionSchema }),
released_at: Type.String(), // ISO 8601 date string
download_url: Type.String(),
file_name: Type.String(),
sha1: Type.String(),
});
// Define the structure for mod details returned by the portal API
// This should align with the ModDetails interface in ModStore.ts
export const ModPortalDetailsSchema = Type.Object({
name: Type.String(),
title: Type.String(),
summary: Type.String(),
owner: Type.String(),
downloads_count: Type.Integer(),
category: Type.Optional(Type.String()),
score: Type.Optional(Type.Number()),
latest_release: Type.Optional(ModPortalReleaseSchema),
releases: Type.Optional(Type.Array(ModPortalReleaseSchema)),
});
export class ModPortalGetAllRequest {
declare ["constructor"]: typeof ModPortalGetAllRequest;
static type = "request" as const;
static src = "control" as const;
static dst = "controller" as const;
static permission = "core.mod.search_portal" as const;
constructor(
public factorioVersion: MajorMinorVersion,
public hide_deprecated?: boolean,
) { }
static jsonSchema = Type.Object({
"factorioVersion": MajorMinorVersionSchema,
"hide_deprecated": Type.Optional(Type.Boolean()),
});
static fromJSON(json: Static<typeof this.jsonSchema>) {
return new this(json.factorioVersion, json.hide_deprecated);
}
// Define the Response class inline
static Response = class ModPortalGetAllResponse {
constructor(
public mods: Static<typeof ModPortalDetailsSchema>[],
) { }
static jsonSchema = Type.Object({
mods: Type.Array(ModPortalDetailsSchema),
});
static fromJSON(json: Static<typeof this.jsonSchema>) {
// No transformation needed if schema matches API structure
return new this(json.mods);
}
};
}
export class ModDownloadRequest {
declare ["constructor"]: typeof ModDownloadRequest;
static type = "request" as const;
static src = ["host", "control"] as const;
static dst = "controller" as const;
static permission = "core.mod.download" as const;
constructor(
public name: string,
public version: SourceVersion,
public sha1?: string,
) { }
static jsonSchema = Type.Object({
"name": Type.String(),
"version": SourceVersionSchema,
"sha1": Type.Optional(Type.String()),
});
static fromJSON(json: Static<typeof this.jsonSchema>) {
return new this(json.name, json.version, json.sha1);
}
static Response = JsonString;
}
export class ModDeleteRequest {
declare ["constructor"]: typeof ModDeleteRequest;
static type = "request" as const;
static src = "control" as const;
static dst = "controller" as const;
static permission = "core.mod.delete" as const;
constructor(
public name: string,
public version: SourceVersion,
) { }
static jsonSchema = Type.Object({
"name": Type.String(),
"version": SourceVersionSchema,
});
static fromJSON(json: Static<typeof this.jsonSchema>) {
return new this(json.name, json.version);
}
}
export class ModPackUpdatesEvent {
declare ["constructor"]: typeof ModPackUpdatesEvent;
static type = "event" as const;
static src = "controller" as const;
static dst = "control" as const;
static permission = "core.mod_pack.subscribe" as const;
constructor(
public updates: ModPack[],
) { }
static jsonSchema = Type.Object({
"updates": Type.Array(ModPack.jsonSchema),
});
static fromJSON(json: Static<typeof this.jsonSchema>) {
return new this(json.updates.map(update => ModPack.fromJSON(update)));
}
}
export class ModUpdatesEvent {
declare ["constructor"]: typeof ModUpdatesEvent;
static type = "event" as const;
static src = "controller" as const;
static dst = "control" as const;
static permission = "core.mod.subscribe" as const;
constructor(
public updates: ModInfo[],
) { }
static jsonSchema = Type.Object({
"updates": Type.Array(ModInfo.jsonSchema),
});
static fromJSON(json: Static<typeof this.jsonSchema>) {
return new this(json.updates.map(update => ModInfo.fromJSON(update)));
}
}
export interface ModNameVersionPair {
name: string,
version: ModVersionEquality,
}
export const ModNameVersionPairSchema = Type.Object({
"name": Type.String(),
"version": ModVersionEquality.jsonSchema,
});
/**
* Request mods to be downloaded from the Factorio mod portal to the controller.
*
* The controller handles the actual download process asynchronously.
* Requires Factorio credentials to be configured on the controller if the portal
* requires authentication for downloads.
*
* @param mods - Array of mods to be downloaded.
* @param factorioVersion - Factorio version context for the download.
*/
export class ModPortalDownloadRequest {
declare ["constructor"]: typeof ModPortalDownloadRequest;
static type = "request" as const;
static src = "control" as const;
static dst = "controller" as const;
static permission = "core.mod.download_from_portal" as const;
static Response = jsonArray(ModInfo);
constructor(
public mods: ModNameVersionPair[],
public factorioVersion: MajorMinorVersion,
) { }
static jsonSchema = Type.Object({
"mods": Type.Array(ModNameVersionPairSchema),
"factorioVersion": MajorMinorVersionSchema,
});
static fromJSON(json: Static<typeof this.jsonSchema>) {
return new this(
json.mods.map(mod => ({
name: mod.name, version: ModVersionEquality.fromJSON(mod.version),
})),
json.factorioVersion,
);
}
}
const ModDependencyResolveErrors = ["incompatible", "unsatisfiable", "invalidDependency", "notFound"] as const;
export const ModDependencyResolveErrorsSchema = Type.Union(ModDependencyResolveErrors.map(v => Type.Literal(v)));
export type ModDependencyResolveErrors = Static<typeof ModDependencyResolveErrorsSchema>;
/**
* Request mods dependencies to be resolved using the Factorio mod portal.
*
* @param mods - Array of dependencies to resolve.
* @param factorioVersion - Factorio version context for resolution.
*/
export class ModDependencyResolveRequest {
declare ["constructor"]: typeof ModDependencyResolveRequest;
static type = "request" as const;
static src = "control" as const;
static dst = "controller" as const;
static permission = "core.mod.search_portal" as const;
constructor(
public mods: ModDependency[],
public factorioVersion: MajorMinorVersion,
public checkForUpdates: boolean = false,
) { }
static jsonSchema = Type.Object({
"mods": Type.Array(ModDependency.jsonSchema),
"factorioVersion": MajorMinorVersionSchema,
"checkForUpdates": Type.Boolean(),
});
static fromJSON(json: Static<typeof this.jsonSchema>) {
return new this(json.mods.map(spec => new ModDependency(spec)), json.factorioVersion, json.checkForUpdates);
}
static fromModPack(modPack: ModPack, checkForUpdates?: boolean) {
const equality = checkForUpdates ? ">=" : "=";
return new this(
[...modPack.mods.values()]
.map(mod => new ModDependency(`${mod.name} ${equality} ${mod.version}`)),
normaliseMajorMinorVersion(modPack.factorioVersion),
checkForUpdates,
);
}
static fromModPackEnabled(modPack: ModPack, checkForUpdates?: boolean) {
const equality = checkForUpdates ? ">=" : "=";
return new this(
[...modPack.mods.values()]
.filter(mod => mod.enabled)
.map(mod => new ModDependency(`${mod.name} ${equality} ${mod.version}`)),
normaliseMajorMinorVersion(modPack.factorioVersion),
checkForUpdates,
);
}
static Response = class ModDependencyResolveResponse {
constructor(
public dependencies: ModInfo[],
public errors: Map<string, ModDependencyResolveErrors>,
) {}
static jsonSchema = Type.Object({
"dependencies": Type.Array(ModInfo.jsonSchema),
"errors": Type.Record(Type.String(), ModDependencyResolveErrorsSchema),
});
static fromJSON(json: Static<typeof this.jsonSchema>) {
return new this(
json.dependencies.map(d => ModInfo.fromJSON(d)),
new Map(Object.entries(json.errors))
);
}
toJSON(): Static<typeof ModDependencyResolveResponse.jsonSchema> {
return {
dependencies: this.dependencies.map(d => d.toJSON()),
errors: Object.fromEntries(this.errors),
};
}
};
}