@zthun/romulator-api 1.6.0 → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/files/files-service.d.mts +80 -4
- package/dist/main.cjs +87 -46
- package/dist/main.cjs.map +1 -1
- package/dist/main.js +90 -49
- package/dist/main.js.map +1 -1
- package/dist/systems/systems-service.d.mts +4 -5
- package/package.json +6 -6
|
@@ -1,20 +1,96 @@
|
|
|
1
|
-
import { IZFileSystemNode } from '@zthun/crumbtrail-fs';
|
|
1
|
+
import { IZFileRepository, IZFileSystemNode, IZFileSystemService } from '@zthun/crumbtrail-fs';
|
|
2
2
|
import { IZRomulatorConfigsService } from '../config/configs-service.mjs';
|
|
3
3
|
export declare const ZRomulatorFilesToken: unique symbol;
|
|
4
|
+
/**
|
|
5
|
+
* Represents the service that you can use to
|
|
6
|
+
* scan the games folder for media, info, games, and systems.
|
|
7
|
+
*/
|
|
4
8
|
export interface IZRomulatorFilesService {
|
|
9
|
+
/**
|
|
10
|
+
* Retrieves all media found in the games .media folder.
|
|
11
|
+
*
|
|
12
|
+
* @returns
|
|
13
|
+
* A list of all media found in the game media folder.
|
|
14
|
+
*/
|
|
5
15
|
media(): Promise<IZFileSystemNode[]>;
|
|
16
|
+
/**
|
|
17
|
+
* Retrieves a single media node found in the .media folder.
|
|
18
|
+
*
|
|
19
|
+
* @param path -
|
|
20
|
+
* The path of the media node to retrieve. This will
|
|
21
|
+
* be relative to the configured games directory. If this
|
|
22
|
+
* starts with a root OS folder, then the path by itself
|
|
23
|
+
* is used.
|
|
24
|
+
*
|
|
25
|
+
* @returns
|
|
26
|
+
* The node with the given path or null if no such
|
|
27
|
+
* file exists.
|
|
28
|
+
*/
|
|
29
|
+
media(path: string): Promise<IZFileSystemNode | null>;
|
|
30
|
+
/**
|
|
31
|
+
* Retrieves all systems found in the games folder.
|
|
32
|
+
*
|
|
33
|
+
* A system is a root folder that is a slug of a supported
|
|
34
|
+
* system.
|
|
35
|
+
*
|
|
36
|
+
* @returns
|
|
37
|
+
* A list of all systems found in the games folder.
|
|
38
|
+
*/
|
|
39
|
+
systems(): Promise<IZFileSystemNode[]>;
|
|
40
|
+
/**
|
|
41
|
+
* Retrieves a single system found in the games folder.
|
|
42
|
+
*
|
|
43
|
+
* @param path -
|
|
44
|
+
* The id of the system, which is also the name of the folder.
|
|
45
|
+
*
|
|
46
|
+
* @returns
|
|
47
|
+
* The node that represents the system slug. Returns null if
|
|
48
|
+
* the folder does not exist or is not supported. Note
|
|
49
|
+
* that the path is relative to the configured games folder. If you
|
|
50
|
+
* want to supply a fully qualified absolute path, then this string
|
|
51
|
+
* should start with the root of an OS drive (not recommended).
|
|
52
|
+
*/
|
|
53
|
+
systems(path: string): Promise<IZFileSystemNode | null>;
|
|
54
|
+
/**
|
|
55
|
+
* Retrieves all info found in the games .info folder.
|
|
56
|
+
*
|
|
57
|
+
* Info is the metadata scraped from a scraper service.
|
|
58
|
+
* The data format is stored in json.
|
|
59
|
+
*
|
|
60
|
+
* @returns
|
|
61
|
+
* A list of all info found in the games folder.
|
|
62
|
+
*/
|
|
63
|
+
info(): Promise<IZFileSystemNode[]>;
|
|
64
|
+
/**
|
|
65
|
+
* Retrieves specific information found in the games .info folder.
|
|
66
|
+
*
|
|
67
|
+
* @param path -
|
|
68
|
+
* The path of the info node to retrieve.
|
|
69
|
+
*
|
|
70
|
+
* @returns
|
|
71
|
+
* The node with the given path or null if no such file exists.
|
|
72
|
+
*/
|
|
73
|
+
info(path: string): Promise<IZFileSystemNode | null>;
|
|
6
74
|
}
|
|
7
75
|
export declare class ZRomulatorFilesService implements IZRomulatorFilesService {
|
|
8
76
|
private readonly _configs;
|
|
77
|
+
private readonly _fileSystem;
|
|
9
78
|
private static readonly MediaFolderName;
|
|
10
79
|
private static readonly InfoFolderName;
|
|
11
80
|
private _repository;
|
|
12
|
-
private
|
|
81
|
+
private _folderStream;
|
|
13
82
|
private _globs;
|
|
14
|
-
|
|
83
|
+
private _systems;
|
|
84
|
+
constructor(_configs: IZRomulatorConfigsService, _fileSystem: IZFileSystemService);
|
|
15
85
|
private gamesFolder;
|
|
16
86
|
private mediaFolder;
|
|
17
87
|
private infoFolder;
|
|
18
|
-
private
|
|
88
|
+
private contents;
|
|
89
|
+
seed(): Promise<IZFileRepository>;
|
|
19
90
|
media(): Promise<IZFileSystemNode[]>;
|
|
91
|
+
media(path: string): Promise<IZFileSystemNode | null>;
|
|
92
|
+
systems(): Promise<IZFileSystemNode[]>;
|
|
93
|
+
systems(path: string): Promise<IZFileSystemNode | null>;
|
|
94
|
+
info(): Promise<IZFileSystemNode[]>;
|
|
95
|
+
info(path: string): Promise<IZFileSystemNode | null>;
|
|
20
96
|
}
|
package/dist/main.cjs
CHANGED
|
@@ -631,19 +631,24 @@ function _ts_param$4(paramIndex, decorator) {
|
|
|
631
631
|
const ZRomulatorFilesToken = Symbol("files");
|
|
632
632
|
class ZRomulatorFilesService {
|
|
633
633
|
_configs;
|
|
634
|
+
_fileSystem;
|
|
634
635
|
static MediaFolderName = ".media";
|
|
635
636
|
static InfoFolderName = ".info";
|
|
636
637
|
_repository = new crumbtrailFs.ZFileRepository();
|
|
637
|
-
|
|
638
|
+
_folderStream = new crumbtrailFs.ZStreamFolder();
|
|
638
639
|
_globs;
|
|
639
|
-
|
|
640
|
+
_systems;
|
|
641
|
+
constructor(_configs, _fileSystem){
|
|
640
642
|
this._configs = _configs;
|
|
643
|
+
this._fileSystem = _fileSystem;
|
|
641
644
|
const slugs = Object.values(romulatorClient.ZRomulatorSystemId);
|
|
642
645
|
this._globs = [
|
|
643
646
|
".media/**",
|
|
644
647
|
".info/**",
|
|
645
648
|
...slugs.map((s)=>`${s}/*.*`)
|
|
646
649
|
];
|
|
650
|
+
this._systems = Object.values(romulatorClient.ZRomulatorSystemId);
|
|
651
|
+
this.seed();
|
|
647
652
|
}
|
|
648
653
|
async gamesFolder() {
|
|
649
654
|
const config = await this._configs.get(romulatorClient.ZRomulatorConfigId.Games);
|
|
@@ -660,31 +665,55 @@ class ZRomulatorFilesService {
|
|
|
660
665
|
const gamesFolder = await this.gamesFolder();
|
|
661
666
|
return node_path.resolve(gamesFolder, ZRomulatorFilesService.InfoFolderName);
|
|
662
667
|
}
|
|
663
|
-
async
|
|
668
|
+
async contents(root, path) {
|
|
669
|
+
const repository = await this.seed();
|
|
670
|
+
const prefix = lodashEs.trimEnd(root, "/");
|
|
671
|
+
const folder = `${prefix}${node_path.sep}`;
|
|
672
|
+
let filter = new helpfulQuery.ZFilterBinaryBuilder().subject("path");
|
|
673
|
+
filter = path == null ? filter.startsWith().value(folder) : filter.equal().value(node_path.resolve(folder, path));
|
|
674
|
+
const sort = new helpfulQuery.ZSortBuilder().ascending("path").build();
|
|
675
|
+
const request = new helpfulQuery.ZDataRequestBuilder().filter(filter.build()).sort(sort).build();
|
|
676
|
+
const nodes = await repository.retrieve(request);
|
|
677
|
+
return path == null ? nodes : helpfulFn.firstDefined(null, lodashEs.first(nodes));
|
|
678
|
+
}
|
|
679
|
+
async seed() {
|
|
664
680
|
const path = await this.gamesFolder();
|
|
665
681
|
if (this._repository.path !== path) {
|
|
666
|
-
await this.
|
|
667
|
-
await this.
|
|
682
|
+
await this._folderStream.write(await this.mediaFolder());
|
|
683
|
+
await this._folderStream.write(await this.infoFolder());
|
|
668
684
|
await this._repository.initialize(path, this._globs);
|
|
669
685
|
}
|
|
670
686
|
return this._repository;
|
|
671
687
|
}
|
|
672
|
-
async media() {
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
688
|
+
async media(path) {
|
|
689
|
+
return this.contents(await this.mediaFolder(), path);
|
|
690
|
+
}
|
|
691
|
+
async systems(path) {
|
|
692
|
+
// Systems use directories. There's a maximum limit of about 200 systems.
|
|
693
|
+
// Since we don't actually need to read any metadata or scan through thousands
|
|
694
|
+
// of unknown folders, we can use the supported system ids to just grab the
|
|
695
|
+
// systems that we need. Since the file repository does a stat anyway, we can just
|
|
696
|
+
// grab the systems from the file system and it should be fast enough. These are all
|
|
697
|
+
// folders, so we don't even need the stats for them and we can assume folders.
|
|
698
|
+
const games = await this.gamesFolder();
|
|
699
|
+
const folders = path == null ? this._systems.map((s)=>`${s}/`) : node_path.resolve(games, path);
|
|
700
|
+
const items = await this._fileSystem.search(folders, {
|
|
701
|
+
cwd: games
|
|
702
|
+
});
|
|
703
|
+
return path == null ? items : helpfulFn.firstDefined(null, lodashEs.first(items));
|
|
704
|
+
}
|
|
705
|
+
async info(path) {
|
|
706
|
+
return this.contents(await this.infoFolder(), path);
|
|
680
707
|
}
|
|
681
708
|
}
|
|
682
709
|
ZRomulatorFilesService = _ts_decorate$8([
|
|
683
710
|
common.Injectable(),
|
|
684
711
|
_ts_param$4(0, common.Inject(ZRomulatorConfigsToken)),
|
|
712
|
+
_ts_param$4(1, common.Inject(crumbtrailNest.ZFileSystemToken)),
|
|
685
713
|
_ts_metadata$4("design:type", Function),
|
|
686
714
|
_ts_metadata$4("design:paramtypes", [
|
|
687
|
-
typeof IZRomulatorConfigsService === "undefined" ? Object : IZRomulatorConfigsService
|
|
715
|
+
typeof IZRomulatorConfigsService === "undefined" ? Object : IZRomulatorConfigsService,
|
|
716
|
+
typeof IZFileSystemService === "undefined" ? Object : IZFileSystemService
|
|
688
717
|
])
|
|
689
718
|
], ZRomulatorFilesService);
|
|
690
719
|
|
|
@@ -699,6 +728,7 @@ class ZRomulatorFilesModule {
|
|
|
699
728
|
ZRomulatorFilesModule = _ts_decorate$7([
|
|
700
729
|
common.Module({
|
|
701
730
|
imports: [
|
|
731
|
+
crumbtrailNest.ZFileSystemModule,
|
|
702
732
|
ZRomulatorConfigsModule
|
|
703
733
|
],
|
|
704
734
|
providers: [
|
|
@@ -1005,13 +1035,16 @@ function _ts_param$1(paramIndex, decorator) {
|
|
|
1005
1035
|
}
|
|
1006
1036
|
const ZRomulatorSystemsToken = Symbol("romulator-systems-service");
|
|
1007
1037
|
class ZRomulatorSystemsService {
|
|
1008
|
-
|
|
1009
|
-
_configs;
|
|
1038
|
+
_files;
|
|
1010
1039
|
logger;
|
|
1011
1040
|
_logger;
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1041
|
+
_fileStream = new crumbtrailFs.ZStreamFile({
|
|
1042
|
+
cache: {
|
|
1043
|
+
maxFiles: Object.keys(romulatorClient.ZRomulatorSystemId).length + 1
|
|
1044
|
+
}
|
|
1045
|
+
});
|
|
1046
|
+
constructor(_files, logger){
|
|
1047
|
+
this._files = _files;
|
|
1015
1048
|
this.logger = logger;
|
|
1016
1049
|
this._logger = new lumberjackyLog.ZLoggerContext("ZRomulatorSystemsService", logger);
|
|
1017
1050
|
}
|
|
@@ -1020,18 +1053,8 @@ class ZRomulatorSystemsService {
|
|
|
1020
1053
|
const size = helpfulFn.firstDefined(Infinity, req.size);
|
|
1021
1054
|
let msg = `Retrieving systems page, ${page}, with size, ${size}.`;
|
|
1022
1055
|
this._logger.log(new lumberjackyLog.ZLogEntryBuilder().info().message(msg).build());
|
|
1023
|
-
const
|
|
1024
|
-
const
|
|
1025
|
-
const { fallback } = romulatorClient.ZRomulatorConfigGamesMetadata.gamesFolder();
|
|
1026
|
-
const _folder = helpfulFn.firstDefined(fallback, gamesFolder);
|
|
1027
|
-
const cwd = helpfulFn.detokenize(_folder, process.env);
|
|
1028
|
-
msg = `Looking for systems in ${cwd}`;
|
|
1029
|
-
this._logger.log(new lumberjackyLog.ZLogEntryBuilder().info().message(msg).build());
|
|
1030
|
-
const searchOptions = {
|
|
1031
|
-
cwd
|
|
1032
|
-
};
|
|
1033
|
-
const folders = await this._file.search("*/", searchOptions);
|
|
1034
|
-
const systems = folders.map((folder)=>folder.path).map((path)=>node_path.basename(path)).map((slug)=>this._createSystemFromSlug(slug)).filter((system)=>system != null);
|
|
1056
|
+
const folders = await this._files.systems();
|
|
1057
|
+
const systems = await Promise.all(folders.map((folder)=>folder.path).map((path)=>node_path.basename(path)).filter((slug)=>romulatorClient.isSystemId(slug)).map((slug)=>this._createSystemFromSlug(slug)));
|
|
1035
1058
|
msg = `Found ${systems.length} systems`;
|
|
1036
1059
|
this._logger.log(new lumberjackyLog.ZLogEntryBuilder().info().message(msg).build());
|
|
1037
1060
|
const sourceOptions = new helpfulQuery.ZDataSourceStaticOptionsBuilder().search(new helpfulQuery.ZDataSearchFields([
|
|
@@ -1045,27 +1068,46 @@ class ZRomulatorSystemsService {
|
|
|
1045
1068
|
return new helpfulQuery.ZPageBuilder().data(data).count(count).build();
|
|
1046
1069
|
}
|
|
1047
1070
|
async get(id) {
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
throw new common.NotFoundException(
|
|
1071
|
+
// The system path should be the slug itself.
|
|
1072
|
+
if (!romulatorClient.isSystemId(id)) {
|
|
1073
|
+
const message = `The specified system slug, ${id}, is not supported.`;
|
|
1074
|
+
throw new common.NotFoundException(message);
|
|
1075
|
+
}
|
|
1076
|
+
const node = await this._files.systems(id);
|
|
1077
|
+
if (node == null) {
|
|
1078
|
+
const message = `System with slug, ${id}, was not found.`;
|
|
1079
|
+
throw new common.NotFoundException(message);
|
|
1052
1080
|
}
|
|
1053
|
-
return
|
|
1081
|
+
return this._createSystemFromSlug(id);
|
|
1054
1082
|
}
|
|
1055
|
-
_createSystemFromSlug(slug) {
|
|
1056
|
-
|
|
1057
|
-
|
|
1083
|
+
async _createSystemFromSlug(slug) {
|
|
1084
|
+
const system = new romulatorClient.ZRomulatorSystemBuilder().id(slug);
|
|
1085
|
+
const path = `${slug}/info.json`;
|
|
1086
|
+
const info = await this._files.info(path);
|
|
1087
|
+
if (info == null) {
|
|
1088
|
+
// Best we can do right now.
|
|
1089
|
+
return system.build();
|
|
1090
|
+
}
|
|
1091
|
+
try {
|
|
1092
|
+
const contents = await this._fileStream.read(info.path);
|
|
1093
|
+
const json = JSON.parse(contents.toString());
|
|
1094
|
+
return system.assign(json).redact().build();
|
|
1095
|
+
} catch (e) {
|
|
1096
|
+
// Best we can do
|
|
1097
|
+
const err = helpfulFn.createError(e);
|
|
1098
|
+
const msg = `Cannot read system metadata, ${err.message}`;
|
|
1099
|
+
this._logger.log(new lumberjackyLog.ZLogEntryBuilder().error().message(msg).build());
|
|
1100
|
+
return system.build();
|
|
1101
|
+
}
|
|
1058
1102
|
}
|
|
1059
1103
|
}
|
|
1060
1104
|
ZRomulatorSystemsService = _ts_decorate$3([
|
|
1061
1105
|
common.Injectable(),
|
|
1062
|
-
_ts_param$1(0, common.Inject(
|
|
1063
|
-
_ts_param$1(1, common.Inject(
|
|
1064
|
-
_ts_param$1(2, common.Inject(lumberjackyNest.ZLoggerToken)),
|
|
1106
|
+
_ts_param$1(0, common.Inject(ZRomulatorFilesToken)),
|
|
1107
|
+
_ts_param$1(1, common.Inject(lumberjackyNest.ZLoggerToken)),
|
|
1065
1108
|
_ts_metadata$1("design:type", Function),
|
|
1066
1109
|
_ts_metadata$1("design:paramtypes", [
|
|
1067
|
-
typeof
|
|
1068
|
-
typeof IZRomulatorConfigsService === "undefined" ? Object : IZRomulatorConfigsService,
|
|
1110
|
+
typeof IZRomulatorFilesService === "undefined" ? Object : IZRomulatorFilesService,
|
|
1069
1111
|
typeof IZLogger === "undefined" ? Object : IZLogger
|
|
1070
1112
|
])
|
|
1071
1113
|
], ZRomulatorSystemsService);
|
|
@@ -1139,8 +1181,7 @@ class ZRomulatorSystemsModule {
|
|
|
1139
1181
|
ZRomulatorSystemsModule = _ts_decorate$1([
|
|
1140
1182
|
common.Module({
|
|
1141
1183
|
imports: [
|
|
1142
|
-
|
|
1143
|
-
crumbtrailNest.ZFileSystemModule,
|
|
1184
|
+
ZRomulatorFilesModule,
|
|
1144
1185
|
lumberjackyNest.ZLoggerModule
|
|
1145
1186
|
],
|
|
1146
1187
|
controllers: [
|