@zthun/romulator-api 1.9.0 → 1.11.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/main.js CHANGED
@@ -3,18 +3,17 @@ import { ApiProperty, ApiParam, ApiBody, ApiQuery, ApiResponse, ApiTags, Documen
3
3
  import { InternalServerErrorException, NotFoundException, Injectable, Inject, Get, Query, Patch, UsePipes, ValidationPipe, Param, Body, Controller, Module, NotAcceptableException, StreamableFile, ForbiddenException, Req, Delete } from '@nestjs/common';
4
4
  import { ZFileSystemModule, ZFileSystemToken } from '@zthun/crumbtrail-nest';
5
5
  import { ZLoggerToken, ZLoggerModule } from '@zthun/lumberjacky-nest';
6
- import { ZDataSourceStaticOptionsBuilder, ZDataSearchFields, ZDataSourceStatic, ZPageBuilder, ZDataRequestBuilder, ZSortBuilder, ZFilterBinaryBuilder, ZFilterLogicBuilder } from '@zthun/helpful-query';
6
+ import { ZDataSourceStaticOptionsBuilder, ZDataSearchFields, ZDataSourceStatic, ZPageBuilder, ZDataRequestBuilder, ZFilterBinaryBuilder, ZSortBuilder, ZFilterCollectionBuilder, ZFilterLogicBuilder } from '@zthun/helpful-query';
7
7
  import { IsDefined, IsObject, IsNotEmptyObject } from 'class-validator';
8
- import { firstDefined, createError, detokenize, firstTruthy, html } from '@zthun/helpful-fn';
9
- import { ZLoggerContext, ZLogEntryBuilder } from '@zthun/lumberjacky-log';
10
- import { ZRomulatorConfigBuilder, ZRomulatorConfigId as ZRomulatorConfigId$1, ZRomulatorConfigGamesMetadata, ZRomulatorSystemBuilder, ZRomulatorSystemId, ZRomulatorConfigGamesBuilder, ZRomulatorGameBuilder, ZRomulatorMediaBuilder, isSystemId } from '@zthun/romulator-client';
11
- import { find, kebabCase, castArray, trimEnd, first, findIndex } from 'lodash-es';
8
+ import { firstDefined, createError, detokenize, mib, firstTruthy, html } from '@zthun/helpful-fn';
9
+ import { ZLogEntryBuilder, ZLoggerContext } from '@zthun/lumberjacky-log';
10
+ import { ZRomulatorConfigBuilder, ZRomulatorConfigId as ZRomulatorConfigId$1, ZRomulatorConfigGamesMetadata, ZRomulatorConfigGamesBuilder, ZRomulatorSystemId, isSystemId, ZRomulatorSystemBuilder, ZRomulatorGameBuilder, ZRomulatorMediaBuilder } from '@zthun/romulator-client';
11
+ import { find, trimStart, flatten, castArray, uniq, kebabCase, get, findIndex } from 'lodash-es';
12
12
  import { readFile, mkdir, writeFile, unlink } from 'node:fs/promises';
13
- import { resolve, dirname, basename, parse } from 'node:path';
13
+ import { resolve, dirname, basename } from 'node:path';
14
14
  import { homedir } from 'node:os';
15
- import { isTagged, ZTag } from '@zthun/helpful-reflection';
16
- import 'reflect-metadata';
17
- import { ZFileRepository, ZStreamFolder, ZStreamFile } from '@zthun/crumbtrail-fs';
15
+ import { ZFileRepository, ZStreamFolder, ZStreamFile, ZFileSystemNodeBuilder } from '@zthun/crumbtrail-fs';
16
+ import { resolve as resolve$1 } from 'path';
18
17
  import { env } from 'node:process';
19
18
  import { ZHttpCodeSuccess, ZHttpCodeClient } from '@zthun/webigail-http';
20
19
  import { ZMimeTypeImage } from '@zthun/webigail-url';
@@ -22,19 +21,34 @@ import { lookup } from 'mime-types';
22
21
  import { createReadStream } from 'node:fs';
23
22
  import { Readable } from 'node:stream';
24
23
 
25
- function _ts_decorate$h(decorators, target, key, desc) {
24
+ function _define_property$c(obj, key, value) {
25
+ if (key in obj) {
26
+ Object.defineProperty(obj, key, {
27
+ value: value,
28
+ enumerable: true,
29
+ configurable: true,
30
+ writable: true
31
+ });
32
+ } else {
33
+ obj[key] = value;
34
+ }
35
+ return obj;
36
+ }
37
+ function _ts_decorate$i(decorators, target, key, desc) {
26
38
  var c = arguments.length, r = c < 3 ? target : desc, d;
27
39
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
28
40
  else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
29
41
  return c > 3 && r && Object.defineProperty(target, key, r), r;
30
42
  }
31
- function _ts_metadata$b(k, v) {
43
+ function _ts_metadata$c(k, v) {
32
44
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
33
45
  }
34
46
  class ZRomulatorConfigUpdateDto {
35
- contents;
47
+ constructor(){
48
+ _define_property$c(this, "contents", void 0);
49
+ }
36
50
  }
37
- _ts_decorate$h([
51
+ _ts_decorate$i([
38
52
  ApiProperty({
39
53
  type: "object",
40
54
  properties: {}
@@ -46,7 +60,7 @@ _ts_decorate$h([
46
60
  message: "The contents of the config must be an object "
47
61
  }),
48
62
  IsNotEmptyObject(),
49
- _ts_metadata$b("design:type", typeof T === "undefined" ? Object : T)
63
+ _ts_metadata$c("design:type", typeof T === "undefined" ? Object : T)
50
64
  ], ZRomulatorConfigUpdateDto.prototype, "contents", void 0);
51
65
 
52
66
  class ZDir {
@@ -73,26 +87,35 @@ class ZRomulatorConfigKnown {
73
87
  }
74
88
  }
75
89
 
76
- function _ts_decorate$g(decorators, target, key, desc) {
90
+ function _define_property$b(obj, key, value) {
91
+ if (key in obj) {
92
+ Object.defineProperty(obj, key, {
93
+ value: value,
94
+ enumerable: true,
95
+ configurable: true,
96
+ writable: true
97
+ });
98
+ } else {
99
+ obj[key] = value;
100
+ }
101
+ return obj;
102
+ }
103
+ function _ts_decorate$h(decorators, target, key, desc) {
77
104
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
78
105
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
79
106
  else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
80
107
  return c > 3 && r && Object.defineProperty(target, key, r), r;
81
108
  }
82
- function _ts_metadata$a(k, v) {
109
+ function _ts_metadata$b(k, v) {
83
110
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
84
111
  }
85
- function _ts_param$9(paramIndex, decorator) {
112
+ function _ts_param$b(paramIndex, decorator) {
86
113
  return function(target, key) {
87
114
  decorator(target, key, paramIndex);
88
115
  };
89
116
  }
90
117
  const ZRomulatorConfigsToken = Symbol("configs");
91
118
  class ZRomulatorConfigsService {
92
- _logger;
93
- constructor(_logger){
94
- this._logger = new ZLoggerContext("ZRomulatorConfigsService", _logger);
95
- }
96
119
  async list(req) {
97
120
  const page = firstDefined(1, req.page);
98
121
  const size = firstDefined(Infinity, req.size);
@@ -163,35 +186,48 @@ class ZRomulatorConfigsService {
163
186
  this._logger.log(new ZLogEntryBuilder().info().message(msg).build());
164
187
  return config;
165
188
  }
189
+ constructor(_logger){
190
+ _define_property$b(this, "_logger", void 0);
191
+ this._logger = new ZLoggerContext("ZRomulatorConfigsService", _logger);
192
+ }
166
193
  }
167
- ZRomulatorConfigsService = _ts_decorate$g([
194
+ ZRomulatorConfigsService = _ts_decorate$h([
168
195
  Injectable(),
169
- _ts_param$9(0, Inject(ZLoggerToken)),
170
- _ts_metadata$a("design:type", Function),
171
- _ts_metadata$a("design:paramtypes", [
196
+ _ts_param$b(0, Inject(ZLoggerToken)),
197
+ _ts_metadata$b("design:type", Function),
198
+ _ts_metadata$b("design:paramtypes", [
172
199
  typeof IZLogger === "undefined" ? Object : IZLogger
173
200
  ])
174
201
  ], ZRomulatorConfigsService);
175
202
 
176
- function _ts_decorate$f(decorators, target, key, desc) {
203
+ function _define_property$a(obj, key, value) {
204
+ if (key in obj) {
205
+ Object.defineProperty(obj, key, {
206
+ value: value,
207
+ enumerable: true,
208
+ configurable: true,
209
+ writable: true
210
+ });
211
+ } else {
212
+ obj[key] = value;
213
+ }
214
+ return obj;
215
+ }
216
+ function _ts_decorate$g(decorators, target, key, desc) {
177
217
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
178
218
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
179
219
  else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
180
220
  return c > 3 && r && Object.defineProperty(target, key, r), r;
181
221
  }
182
- function _ts_metadata$9(k, v) {
222
+ function _ts_metadata$a(k, v) {
183
223
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
184
224
  }
185
- function _ts_param$8(paramIndex, decorator) {
225
+ function _ts_param$a(paramIndex, decorator) {
186
226
  return function(target, key) {
187
227
  decorator(target, key, paramIndex);
188
228
  };
189
229
  }
190
230
  class ZRomulatorConfigsController {
191
- _configs;
192
- constructor(_configs){
193
- this._configs = _configs;
194
- }
195
231
  async list(query) {
196
232
  const request = new ZDataRequestBuilder().query(query).build();
197
233
  return this._configs.list(request);
@@ -202,17 +238,21 @@ class ZRomulatorConfigsController {
202
238
  async get(identification) {
203
239
  return await this._configs.get(identification);
204
240
  }
241
+ constructor(_configs){
242
+ _define_property$a(this, "_configs", void 0);
243
+ this._configs = _configs;
244
+ }
205
245
  }
206
- _ts_decorate$f([
246
+ _ts_decorate$g([
207
247
  Get(),
208
- _ts_param$8(0, Query()),
209
- _ts_metadata$9("design:type", Function),
210
- _ts_metadata$9("design:paramtypes", [
248
+ _ts_param$a(0, Query()),
249
+ _ts_metadata$a("design:type", Function),
250
+ _ts_metadata$a("design:paramtypes", [
211
251
  typeof IZDataRequestQuery === "undefined" ? Object : IZDataRequestQuery
212
252
  ]),
213
- _ts_metadata$9("design:returntype", Promise)
253
+ _ts_metadata$a("design:returntype", Promise)
214
254
  ], ZRomulatorConfigsController.prototype, "list", null);
215
- _ts_decorate$f([
255
+ _ts_decorate$g([
216
256
  ApiParam({
217
257
  type: "string",
218
258
  name: "identification",
@@ -229,39 +269,39 @@ _ts_decorate$f([
229
269
  skipNullProperties: false,
230
270
  skipUndefinedProperties: false
231
271
  })),
232
- _ts_param$8(0, Param("identification")),
233
- _ts_param$8(1, Body()),
234
- _ts_metadata$9("design:type", Function),
235
- _ts_metadata$9("design:paramtypes", [
272
+ _ts_param$a(0, Param("identification")),
273
+ _ts_param$a(1, Body()),
274
+ _ts_metadata$a("design:type", Function),
275
+ _ts_metadata$a("design:paramtypes", [
236
276
  typeof ZRomulatorConfigId === "undefined" ? Object : ZRomulatorConfigId,
237
277
  typeof ZRomulatorConfigUpdateDto === "undefined" ? Object : ZRomulatorConfigUpdateDto
238
278
  ]),
239
- _ts_metadata$9("design:returntype", Promise)
279
+ _ts_metadata$a("design:returntype", Promise)
240
280
  ], ZRomulatorConfigsController.prototype, "update", null);
241
- _ts_decorate$f([
281
+ _ts_decorate$g([
242
282
  ApiParam({
243
283
  type: "string",
244
284
  name: "identification",
245
285
  description: "The id of the config"
246
286
  }),
247
287
  Get(":identification"),
248
- _ts_param$8(0, Param("identification")),
249
- _ts_metadata$9("design:type", Function),
250
- _ts_metadata$9("design:paramtypes", [
288
+ _ts_param$a(0, Param("identification")),
289
+ _ts_metadata$a("design:type", Function),
290
+ _ts_metadata$a("design:paramtypes", [
251
291
  typeof ZRomulatorConfigId === "undefined" ? Object : ZRomulatorConfigId
252
292
  ]),
253
- _ts_metadata$9("design:returntype", Promise)
293
+ _ts_metadata$a("design:returntype", Promise)
254
294
  ], ZRomulatorConfigsController.prototype, "get", null);
255
- ZRomulatorConfigsController = _ts_decorate$f([
295
+ ZRomulatorConfigsController = _ts_decorate$g([
256
296
  Controller("configs"),
257
- _ts_param$8(0, Inject(ZRomulatorConfigsToken)),
258
- _ts_metadata$9("design:type", Function),
259
- _ts_metadata$9("design:paramtypes", [
297
+ _ts_param$a(0, Inject(ZRomulatorConfigsToken)),
298
+ _ts_metadata$a("design:type", Function),
299
+ _ts_metadata$a("design:paramtypes", [
260
300
  typeof IZRomulatorConfigsService === "undefined" ? Object : IZRomulatorConfigsService
261
301
  ])
262
302
  ], ZRomulatorConfigsController);
263
303
 
264
- function _ts_decorate$e(decorators, target, key, desc) {
304
+ function _ts_decorate$f(decorators, target, key, desc) {
265
305
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
266
306
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
267
307
  else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
@@ -269,7 +309,7 @@ function _ts_decorate$e(decorators, target, key, desc) {
269
309
  }
270
310
  class ZRomulatorConfigsModule {
271
311
  }
272
- ZRomulatorConfigsModule = _ts_decorate$e([
312
+ ZRomulatorConfigsModule = _ts_decorate$f([
273
313
  Module({
274
314
  imports: [
275
315
  ZFileSystemModule,
@@ -290,151 +330,240 @@ ZRomulatorConfigsModule = _ts_decorate$e([
290
330
  })
291
331
  ], ZRomulatorConfigsModule);
292
332
 
293
- function _ts_decorate$d(decorators, target, key, desc) {
333
+ function _define_property$9(obj, key, value) {
334
+ if (key in obj) {
335
+ Object.defineProperty(obj, key, {
336
+ value: value,
337
+ enumerable: true,
338
+ configurable: true,
339
+ writable: true
340
+ });
341
+ } else {
342
+ obj[key] = value;
343
+ }
344
+ return obj;
345
+ }
346
+ function _ts_decorate$e(decorators, target, key, desc) {
294
347
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
295
348
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
296
349
  else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
297
350
  return c > 3 && r && Object.defineProperty(target, key, r), r;
298
351
  }
299
- function _ts_metadata$8(k, v) {
352
+ function _ts_metadata$9(k, v) {
300
353
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
301
354
  }
302
- const KnownSystem = "@zthunworks/romulator/known-system";
303
- /**
304
- * A helper factory class for creating supported systems.
305
- */ class ZRomulatorSystemKnown {
306
- /**
307
- * Returns all known systems.
308
- *
309
- * @returns
310
- * All supported systems.
311
- */ static all() {
312
- const properties = Object.getOwnPropertyNames(ZRomulatorSystemKnown);
313
- const methods = properties.filter((p)=>isTagged(KnownSystem, ZRomulatorSystemKnown, p)).map((p)=>ZRomulatorSystemKnown[p]).map((f)=>f);
314
- return methods.map((m)=>m.call(null));
355
+ function _ts_param$9(paramIndex, decorator) {
356
+ return function(target, key) {
357
+ decorator(target, key, paramIndex);
358
+ };
359
+ }
360
+ const ZRomulatorFilesRepositoryToken = Symbol("files-repository");
361
+ class ZRomulatorFilesRepository {
362
+ async gamesFolder() {
363
+ const config = await this._configs.get(ZRomulatorConfigId$1.Games);
364
+ const { gamesFolder } = new ZRomulatorConfigGamesBuilder().copy(config.contents).build();
365
+ const { fallback } = ZRomulatorConfigGamesMetadata.gamesFolder();
366
+ const _gamesFolder = firstDefined(fallback.gamesFolder, gamesFolder);
367
+ return detokenize(_gamesFolder, env);
315
368
  }
316
- /**
317
- * Creates a system that represents the Nintendo
318
- * Entertainment System (NES).
319
- *
320
- * @returns
321
- * A {@link ZRomulatorSystemBuilder} instance that has
322
- * built the nes.
323
- */ static nes() {
324
- return new ZRomulatorSystemBuilder().id(ZRomulatorSystemId.Nintendo).name("Nintendo Entertainment System").build();
369
+ async mediaFolder() {
370
+ const gamesFolder = await this.gamesFolder();
371
+ return resolve(gamesFolder, ZRomulatorFilesRepository.MediaFolderName);
325
372
  }
326
- /**
327
- * Creates a system that represents the Super
328
- * Nintendo Entertainment System (SNES).
329
- *
330
- * @returns
331
- * This instance.
332
- */ static snes() {
333
- return new ZRomulatorSystemBuilder().id(ZRomulatorSystemId.SuperNintendo).name("Super Nintendo Entertainment System").build();
373
+ async infoFolder() {
374
+ const gamesFolder = await this.gamesFolder();
375
+ return resolve(gamesFolder, ZRomulatorFilesRepository.InfoFolderName);
334
376
  }
335
- /**
336
- * Creates a system that represents the Nintendo
337
- * 64 (N64).
338
- *
339
- * @returns
340
- * This instance.
341
- */ static n64() {
342
- return new ZRomulatorSystemBuilder().id(ZRomulatorSystemId.Nintendo64).name("Nintendo 64").build();
377
+ async dispose() {
378
+ await this._repository.reset();
343
379
  }
344
- /**
345
- * Creates a system that represents the Nintendo
346
- * GameCube (GC).
347
- *
348
- * @returns
349
- * This instance.
350
- */ static gc() {
351
- return new ZRomulatorSystemBuilder().id(ZRomulatorSystemId.GameCube).name("Nintendo GameCube").build();
380
+ async init() {
381
+ const path = await this.gamesFolder();
382
+ if (this._repository.path !== path) {
383
+ await this._folderStream.write(await this.mediaFolder());
384
+ await this._folderStream.write(await this.infoFolder());
385
+ await this._repository.initialize(path, this._globs);
386
+ }
387
+ return this._repository;
352
388
  }
353
- /**
354
- * Creates a system that represents the Nintendo
355
- * Wii (Wii).
356
- *
357
- * @returns
358
- * This instance.
359
- */ static wii() {
360
- return new ZRomulatorSystemBuilder().id(ZRomulatorSystemId.Wii).name("Nintendo Wii").build();
389
+ async media() {
390
+ const repository = await this.init();
391
+ const folder = `${await this.mediaFolder()}/`;
392
+ const request = new ZDataRequestBuilder().filter(new ZFilterBinaryBuilder().subject("path").startsWith().value(folder).build()).sort(new ZSortBuilder().ascending("path").build()).build();
393
+ return repository.retrieve(request);
361
394
  }
362
- /**
363
- * Creates a system that represents the Nintendo
364
- * Wii U (WiiU).
365
- *
366
- * @returns
367
- * This instance.
368
- */ static wiiu() {
369
- return new ZRomulatorSystemBuilder().id(ZRomulatorSystemId.WiiU).name("Nintendo Wii U").build();
395
+ async info(id) {
396
+ // The info json files are json files that contain arrays of games grouped by systems,
397
+ // or systems.json which describes system information.
398
+ const folder = await this.infoFolder();
399
+ const path = resolve(folder, `${id}.json`);
400
+ return this._repository.get(path);
370
401
  }
371
- /**
372
- * Creates a system that represents the Nintendo
373
- * Switch (Switch).
374
- *
375
- * @returns
376
- * This instance.
377
- */ static switch() {
378
- return new ZRomulatorSystemBuilder().id(ZRomulatorSystemId.Switch).name("Nintendo Switch").build();
402
+ async json(node) {
403
+ if (node == null) {
404
+ return null;
405
+ }
406
+ try {
407
+ const contents = await this._fileStream.read(node.path);
408
+ return JSON.parse(contents.toString());
409
+ } catch (e) {
410
+ const err = createError(e);
411
+ const msg = `Unable to read ${node.path}: ${err.message}`;
412
+ this._logger.log(new ZLogEntryBuilder().error().message(msg).build());
413
+ return null;
414
+ }
379
415
  }
380
- /**
381
- * Creates a system from an id slug.
382
- *
383
- * @param id -
384
- * The id slug of the system to create.
385
- *
386
- * @return
387
- * The system, or null if the id is not known.
388
- */ static from(id) {
389
- if (isTagged(KnownSystem, ZRomulatorSystemKnown, id)) {
390
- return ZRomulatorSystemKnown[id]();
416
+ async systems() {
417
+ // Systems use directories. There's a maximum limit of about 200 systems.
418
+ // Since we don't actually need to read any metadata or scan through thousands
419
+ // of unknown folders, we can use the supported system ids to just grab the
420
+ // systems that we need. Since the file repository does a stat anyway, we can just
421
+ // grab the systems from the file system and it should be fast enough. These are all
422
+ // folders, so we don't even need the stats for them and we can assume folders.
423
+ const games = await this.gamesFolder();
424
+ const folders = this._systems.map((s)=>`${s}/`);
425
+ return await this._fileSystem.search(folders, {
426
+ cwd: games,
427
+ stat: false
428
+ });
429
+ }
430
+ async games(systems) {
431
+ const repository = await this.init();
432
+ const folder = await this.gamesFolder();
433
+ const queries = systems.map((s)=>{
434
+ const dir = `${folder}/${s.id}`;
435
+ const byExtension = new ZFilterCollectionBuilder().subject("extension").in().values(s.extensions.map((e)=>`.${trimStart(e, ".")}`)).build();
436
+ const inPath = new ZFilterBinaryBuilder().subject("parent").equal().value(dir).build();
437
+ const filter = new ZFilterLogicBuilder().and().clause(inPath).clause(byExtension).build();
438
+ const request = new ZDataRequestBuilder().filter(filter).build();
439
+ return repository.retrieve(request);
440
+ });
441
+ const results = await Promise.all(queries);
442
+ return flatten(results);
443
+ }
444
+ constructor(_configs, _fileSystem, logger){
445
+ _define_property$9(this, "_configs", void 0);
446
+ _define_property$9(this, "_fileSystem", void 0);
447
+ _define_property$9(this, "logger", void 0);
448
+ _define_property$9(this, "_logger", void 0);
449
+ _define_property$9(this, "_repository", void 0);
450
+ _define_property$9(this, "_folderStream", void 0);
451
+ _define_property$9(this, "_fileStream", void 0);
452
+ _define_property$9(this, "_globs", void 0);
453
+ _define_property$9(this, "_systems", void 0);
454
+ this._configs = _configs;
455
+ this._fileSystem = _fileSystem;
456
+ this.logger = logger;
457
+ this._repository = new ZFileRepository();
458
+ this._folderStream = new ZStreamFolder();
459
+ this._fileStream = new ZStreamFile({
460
+ cache: {
461
+ fileSize: BigInt(mib(1)),
462
+ maxFiles: 250
463
+ }
464
+ });
465
+ const slugs = Object.values(ZRomulatorSystemId);
466
+ this._globs = [
467
+ ".media/**",
468
+ ".info/**",
469
+ ...slugs.map((s)=>`${s}/*.*`)
470
+ ];
471
+ this._systems = Object.values(ZRomulatorSystemId);
472
+ this._logger = new ZLoggerContext("ZRomulatorFilesRepository", logger);
473
+ }
474
+ }
475
+ _define_property$9(ZRomulatorFilesRepository, "MediaFolderName", ".media");
476
+ _define_property$9(ZRomulatorFilesRepository, "InfoFolderName", ".info");
477
+ ZRomulatorFilesRepository = _ts_decorate$e([
478
+ Injectable(),
479
+ _ts_param$9(0, Inject(ZRomulatorConfigsToken)),
480
+ _ts_param$9(1, Inject(ZFileSystemToken)),
481
+ _ts_param$9(2, Inject(ZLoggerToken)),
482
+ _ts_metadata$9("design:type", Function),
483
+ _ts_metadata$9("design:paramtypes", [
484
+ typeof IZRomulatorConfigsService === "undefined" ? Object : IZRomulatorConfigsService,
485
+ typeof IZFileSystemService === "undefined" ? Object : IZFileSystemService,
486
+ typeof IZLogger === "undefined" ? Object : IZLogger
487
+ ])
488
+ ], ZRomulatorFilesRepository);
489
+
490
+ function _define_property$8(obj, key, value) {
491
+ if (key in obj) {
492
+ Object.defineProperty(obj, key, {
493
+ value: value,
494
+ enumerable: true,
495
+ configurable: true,
496
+ writable: true
497
+ });
498
+ } else {
499
+ obj[key] = value;
500
+ }
501
+ return obj;
502
+ }
503
+ function _ts_decorate$d(decorators, target, key, desc) {
504
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
505
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
506
+ else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
507
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
508
+ }
509
+ function _ts_metadata$8(k, v) {
510
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
511
+ }
512
+ function _ts_param$8(paramIndex, decorator) {
513
+ return function(target, key) {
514
+ decorator(target, key, paramIndex);
515
+ };
516
+ }
517
+ const ZRomulatorFilesSystemsRepositoryToken = Symbol("files-systems-repository");
518
+ class ZRomulatorFilesSystemsRepository {
519
+ async systems() {
520
+ const info = await this._filesRepository.info("systems");
521
+ const json = await this._filesRepository.json(info);
522
+ const candidates = castArray(firstDefined([], json));
523
+ const folders = await this._filesRepository.systems();
524
+ function hasId(candidate) {
525
+ return Object.prototype.hasOwnProperty.call(candidate, "id");
391
526
  }
392
- return null;
527
+ const entries = candidates.filter((c)=>hasId(c)).filter((c)=>isSystemId(c.id)).map((c)=>[
528
+ c.id,
529
+ c
530
+ ]);
531
+ const lookup = new Map(entries);
532
+ const systems = folders.map((folder)=>folder.path).map((path)=>basename(path)).filter((slug)=>isSystemId(slug)).map((slug)=>new ZRomulatorSystemBuilder().parse(lookup.get(slug)).id(slug).build());
533
+ return new Map(systems.map((s)=>[
534
+ s.id,
535
+ s
536
+ ]));
537
+ }
538
+ /**
539
+ * Initializes a new instance of this object.
540
+ */ constructor(_filesRepository){
541
+ _define_property$8(this, "_filesRepository", void 0);
542
+ this._filesRepository = _filesRepository;
393
543
  }
394
544
  }
395
- _ts_decorate$d([
396
- ZTag(KnownSystem),
397
- _ts_metadata$8("design:type", Function),
398
- _ts_metadata$8("design:paramtypes", []),
399
- _ts_metadata$8("design:returntype", void 0)
400
- ], ZRomulatorSystemKnown, "nes", null);
401
- _ts_decorate$d([
402
- ZTag(KnownSystem),
403
- _ts_metadata$8("design:type", Function),
404
- _ts_metadata$8("design:paramtypes", []),
405
- _ts_metadata$8("design:returntype", void 0)
406
- ], ZRomulatorSystemKnown, "snes", null);
407
- _ts_decorate$d([
408
- ZTag(KnownSystem),
409
- _ts_metadata$8("design:type", Function),
410
- _ts_metadata$8("design:paramtypes", []),
411
- _ts_metadata$8("design:returntype", void 0)
412
- ], ZRomulatorSystemKnown, "n64", null);
413
- _ts_decorate$d([
414
- ZTag(KnownSystem),
415
- _ts_metadata$8("design:type", Function),
416
- _ts_metadata$8("design:paramtypes", []),
417
- _ts_metadata$8("design:returntype", void 0)
418
- ], ZRomulatorSystemKnown, "gc", null);
419
- _ts_decorate$d([
420
- ZTag(KnownSystem),
421
- _ts_metadata$8("design:type", Function),
422
- _ts_metadata$8("design:paramtypes", []),
423
- _ts_metadata$8("design:returntype", void 0)
424
- ], ZRomulatorSystemKnown, "wii", null);
425
- _ts_decorate$d([
426
- ZTag(KnownSystem),
427
- _ts_metadata$8("design:type", Function),
428
- _ts_metadata$8("design:paramtypes", []),
429
- _ts_metadata$8("design:returntype", void 0)
430
- ], ZRomulatorSystemKnown, "wiiu", null);
431
- _ts_decorate$d([
432
- ZTag(KnownSystem),
545
+ ZRomulatorFilesSystemsRepository = _ts_decorate$d([
546
+ Injectable(),
547
+ _ts_param$8(0, Inject(ZRomulatorFilesRepositoryToken)),
433
548
  _ts_metadata$8("design:type", Function),
434
- _ts_metadata$8("design:paramtypes", []),
435
- _ts_metadata$8("design:returntype", void 0)
436
- ], ZRomulatorSystemKnown, "switch", null);
549
+ _ts_metadata$8("design:paramtypes", [
550
+ typeof IZRomulatorFilesRepository === "undefined" ? Object : IZRomulatorFilesRepository
551
+ ])
552
+ ], ZRomulatorFilesSystemsRepository);
437
553
 
554
+ function _define_property$7(obj, key, value) {
555
+ if (key in obj) {
556
+ Object.defineProperty(obj, key, {
557
+ value: value,
558
+ enumerable: true,
559
+ configurable: true,
560
+ writable: true
561
+ });
562
+ } else {
563
+ obj[key] = value;
564
+ }
565
+ return obj;
566
+ }
438
567
  function _ts_decorate$c(decorators, target, key, desc) {
439
568
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
440
569
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
@@ -449,84 +578,72 @@ function _ts_param$7(paramIndex, decorator) {
449
578
  decorator(target, key, paramIndex);
450
579
  };
451
580
  }
452
- const ZRomulatorGamesToken = Symbol("romulator-games-service");
453
- class ZRomulatorGamesService {
454
- _file;
455
- _config;
456
- _logger;
457
- constructor(_file, _config, logger){
458
- this._file = _file;
459
- this._config = _config;
460
- this._logger = new ZLoggerContext("ZRomulatorGamesService", logger);
461
- }
462
- async list(req) {
463
- const config = ZRomulatorConfigKnown.games();
464
- const { contents } = await this._config.get(config.id);
465
- const { gamesFolder } = new ZRomulatorConfigGamesBuilder().copy(contents).build();
466
- const { fallback } = ZRomulatorConfigGamesMetadata.gamesFolder();
467
- const cwd = detokenize(firstDefined(fallback, gamesFolder), process.env);
468
- const time = new Date();
469
- let msg = `Searching for games in ${cwd}`;
470
- this._logger.log(new ZLogEntryBuilder().info().message(msg).build());
471
- const systems = ZRomulatorSystemKnown.all().map((system)=>system.id);
472
- const glob = `{${systems.join(",")}}/**/*.zip`;
473
- const nodes = await this._file.search(glob, {
474
- cwd,
475
- stat: false
476
- });
477
- const span = new Date().getTime() - time.getTime();
478
- msg = `Found ${nodes.length} games. Search took ${span} milliseconds.`;
479
- this._logger.log(new ZLogEntryBuilder().info().message(msg).build());
480
- const games = nodes.map(({ path })=>{
481
- const dir = basename(dirname(path));
482
- const { name: file } = parse(path);
483
- const id = `${dir}-${kebabCase(file)}`;
484
- return new ZRomulatorGameBuilder().id(id).system(dir).file(path).name(file).build();
485
- });
486
- const options = new ZDataSourceStaticOptionsBuilder().search(this).build();
487
- const source = new ZDataSourceStatic(games, options);
488
- const $sort = new ZSortBuilder().sorts(firstDefined([], req.sort)).ascending("system").ascending("name").build();
489
- const $request = new ZDataRequestBuilder().copy(req).sort($sort).build();
490
- const data = await source.retrieve($request);
491
- const count = await source.count($request);
492
- return new ZPageBuilder().count(count).data(data).build();
493
- }
494
- async get(id) {
495
- const { data: games } = await this.list(new ZDataRequestBuilder().build());
496
- const match = games.find((game)=>game.id === id);
497
- if (!match) {
498
- throw new NotFoundException(`Unable to find game with id, ${id}.`);
581
+ const ZRomulatorFilesGamesRepositoryToken = Symbol("files-games-repository");
582
+ class ZRomulatorFilesGamesRepository {
583
+ async games() {
584
+ function hasPath(entry) {
585
+ return typeof get(entry, "path") === "string";
499
586
  }
500
- return match;
501
- }
502
- match(data, filter) {
503
- const needle = filter?.trim().toLowerCase();
504
- const { name = "", system = "" } = data;
505
- const target = ZRomulatorSystemKnown.from(system);
506
- const systemName = firstTruthy("", target?.name);
507
- if (!needle?.length) {
508
- return true;
587
+ const gamesFolder = await this._filesRepository.gamesFolder();
588
+ const games = [];
589
+ const systemLookup = await this._systemsRepository.systems();
590
+ const systems = Array.from(systemLookup.values());
591
+ const gameFiles = await this._filesRepository.games(systems);
592
+ const systemsInUse = uniq(gameFiles.map((g)=>g.parent)).map((dir)=>new ZFileSystemNodeBuilder().path(dir).folder().build()).map((node)=>node.title).filter((title)=>isSystemId(title));
593
+ const gameInfo = new Map();
594
+ for await (const system of systemsInUse){
595
+ const info = await this._filesRepository.info(system);
596
+ const json = await this._filesRepository.json(info);
597
+ const gameList = castArray(json).filter((e)=>hasPath(e));
598
+ gameList.forEach((entry)=>{
599
+ gameInfo.set(resolve$1(gamesFolder, system, entry.path), entry);
600
+ });
509
601
  }
510
- return [
511
- name,
512
- system,
513
- systemName
514
- ].filter((s)=>s.length).some((k)=>k.toLowerCase().includes(needle));
602
+ // Variable, gameInfo, now has all of the information data that we need.
603
+ for (const file of gameFiles){
604
+ const { title, path } = file;
605
+ const { title: systemId } = new ZFileSystemNodeBuilder().path(file.parent).folder().build();
606
+ const id = `${kebabCase(systemId)}-${kebabCase(title)}`;
607
+ const info = gameInfo.get(path);
608
+ const game = new ZRomulatorGameBuilder().id(id).system(systemId).file(path).parse(info).build();
609
+ games.push(game);
610
+ }
611
+ return new Map(games.map((g)=>[
612
+ g.id,
613
+ g
614
+ ]));
615
+ }
616
+ constructor(_systemsRepository, _filesRepository){
617
+ _define_property$7(this, "_systemsRepository", void 0);
618
+ _define_property$7(this, "_filesRepository", void 0);
619
+ this._systemsRepository = _systemsRepository;
620
+ this._filesRepository = _filesRepository;
515
621
  }
516
622
  }
517
- ZRomulatorGamesService = _ts_decorate$c([
623
+ ZRomulatorFilesGamesRepository = _ts_decorate$c([
518
624
  Injectable(),
519
- _ts_param$7(0, Inject(ZFileSystemToken)),
520
- _ts_param$7(1, Inject(ZRomulatorConfigsToken)),
521
- _ts_param$7(2, Inject(ZLoggerToken)),
625
+ _ts_param$7(0, Inject(ZRomulatorFilesSystemsRepositoryToken)),
626
+ _ts_param$7(1, Inject(ZRomulatorFilesRepositoryToken)),
522
627
  _ts_metadata$7("design:type", Function),
523
628
  _ts_metadata$7("design:paramtypes", [
524
- typeof IZFileSystemService === "undefined" ? Object : IZFileSystemService,
525
- typeof IZRomulatorConfigsService === "undefined" ? Object : IZRomulatorConfigsService,
526
- typeof IZLogger === "undefined" ? Object : IZLogger
629
+ typeof IZRomulatorFilesSystemsRepository === "undefined" ? Object : IZRomulatorFilesSystemsRepository,
630
+ typeof IZRomulatorFilesRepository === "undefined" ? Object : IZRomulatorFilesRepository
527
631
  ])
528
- ], ZRomulatorGamesService);
632
+ ], ZRomulatorFilesGamesRepository);
529
633
 
634
+ function _define_property$6(obj, key, value) {
635
+ if (key in obj) {
636
+ Object.defineProperty(obj, key, {
637
+ value: value,
638
+ enumerable: true,
639
+ configurable: true,
640
+ writable: true
641
+ });
642
+ } else {
643
+ obj[key] = value;
644
+ }
645
+ return obj;
646
+ }
530
647
  function _ts_decorate$b(decorators, target, key, desc) {
531
648
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
532
649
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
@@ -541,73 +658,66 @@ function _ts_param$6(paramIndex, decorator) {
541
658
  decorator(target, key, paramIndex);
542
659
  };
543
660
  }
544
- class ZRomulatorGamesController {
545
- _games;
546
- constructor(_games){
547
- this._games = _games;
661
+ class ZRomulatorFilesModule {
662
+ async onModuleInit() {
663
+ await this._files.init();
548
664
  }
549
- list(query) {
550
- return this._games.list(new ZDataRequestBuilder().query(query).build());
665
+ async onModuleDestroy() {
666
+ await this._files.dispose();
551
667
  }
552
- get(identification) {
553
- return this._games.get(identification);
668
+ constructor(_files){
669
+ _define_property$6(this, "_files", void 0);
670
+ this._files = _files;
554
671
  }
555
672
  }
556
- _ts_decorate$b([
557
- Get(),
558
- _ts_param$6(0, Query()),
559
- _ts_metadata$6("design:type", Function),
560
- _ts_metadata$6("design:paramtypes", [
561
- typeof IZDataRequestQuery === "undefined" ? Object : IZDataRequestQuery
562
- ]),
563
- _ts_metadata$6("design:returntype", typeof Promise === "undefined" ? Object : Promise)
564
- ], ZRomulatorGamesController.prototype, "list", null);
565
- _ts_decorate$b([
566
- Get(":identification"),
567
- _ts_param$6(0, Param("identification")),
568
- _ts_metadata$6("design:type", Function),
569
- _ts_metadata$6("design:paramtypes", [
570
- String
571
- ]),
572
- _ts_metadata$6("design:returntype", void 0)
573
- ], ZRomulatorGamesController.prototype, "get", null);
574
- ZRomulatorGamesController = _ts_decorate$b([
575
- Controller("games"),
576
- _ts_param$6(0, Inject(ZRomulatorGamesToken)),
577
- _ts_metadata$6("design:type", Function),
578
- _ts_metadata$6("design:paramtypes", [
579
- typeof IZRomulatorGamesService === "undefined" ? Object : IZRomulatorGamesService
580
- ])
581
- ], ZRomulatorGamesController);
582
-
583
- function _ts_decorate$a(decorators, target, key, desc) {
584
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
585
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
586
- else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
587
- return c > 3 && r && Object.defineProperty(target, key, r), r;
588
- }
589
- class ZRomulatorGamesModule {
590
- }
591
- ZRomulatorGamesModule = _ts_decorate$a([
673
+ ZRomulatorFilesModule = _ts_decorate$b([
592
674
  Module({
593
675
  imports: [
594
- ZRomulatorConfigsModule,
595
676
  ZFileSystemModule,
677
+ ZRomulatorConfigsModule,
596
678
  ZLoggerModule
597
679
  ],
598
- controllers: [
599
- ZRomulatorGamesController
600
- ],
601
680
  providers: [
602
681
  {
603
- provide: ZRomulatorGamesToken,
604
- useClass: ZRomulatorGamesService
682
+ provide: ZRomulatorFilesRepositoryToken,
683
+ useClass: ZRomulatorFilesRepository
684
+ },
685
+ {
686
+ provide: ZRomulatorFilesSystemsRepositoryToken,
687
+ useClass: ZRomulatorFilesSystemsRepository
688
+ },
689
+ {
690
+ provide: ZRomulatorFilesGamesRepositoryToken,
691
+ useClass: ZRomulatorFilesGamesRepository
605
692
  }
693
+ ],
694
+ exports: [
695
+ ZRomulatorFilesRepositoryToken,
696
+ ZRomulatorFilesSystemsRepositoryToken,
697
+ ZRomulatorFilesGamesRepositoryToken
606
698
  ]
607
- })
608
- ], ZRomulatorGamesModule);
699
+ }),
700
+ _ts_param$6(0, Inject(ZRomulatorFilesRepositoryToken)),
701
+ _ts_metadata$6("design:type", Function),
702
+ _ts_metadata$6("design:paramtypes", [
703
+ typeof IZRomulatorFilesRepository === "undefined" ? Object : IZRomulatorFilesRepository
704
+ ])
705
+ ], ZRomulatorFilesModule);
609
706
 
610
- function _ts_decorate$9(decorators, target, key, desc) {
707
+ function _define_property$5(obj, key, value) {
708
+ if (key in obj) {
709
+ Object.defineProperty(obj, key, {
710
+ value: value,
711
+ enumerable: true,
712
+ configurable: true,
713
+ writable: true
714
+ });
715
+ } else {
716
+ obj[key] = value;
717
+ }
718
+ return obj;
719
+ }
720
+ function _ts_decorate$a(decorators, target, key, desc) {
611
721
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
612
722
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
613
723
  else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
@@ -621,113 +731,86 @@ function _ts_param$5(paramIndex, decorator) {
621
731
  decorator(target, key, paramIndex);
622
732
  };
623
733
  }
624
- const ZRomulatorFilesToken = Symbol("files");
625
- class ZRomulatorFilesService {
626
- _configs;
627
- _fileSystem;
628
- static MediaFolderName = ".media";
629
- static InfoFolderName = ".info";
630
- _repository = new ZFileRepository();
631
- _folderStream = new ZStreamFolder();
632
- _fileStream = new ZStreamFile();
633
- _globs;
634
- _systems;
635
- constructor(_configs, _fileSystem){
636
- this._configs = _configs;
637
- this._fileSystem = _fileSystem;
638
- const slugs = Object.values(ZRomulatorSystemId);
639
- this._globs = [
640
- ".media/**",
641
- ".info/**",
642
- ...slugs.map((s)=>`${s}/*.*`)
643
- ];
644
- this._systems = Object.values(ZRomulatorSystemId);
645
- }
646
- async gamesFolder() {
647
- const config = await this._configs.get(ZRomulatorConfigId$1.Games);
648
- const { gamesFolder } = new ZRomulatorConfigGamesBuilder().copy(config.contents).build();
649
- const { fallback } = ZRomulatorConfigGamesMetadata.gamesFolder();
650
- const _gamesFolder = firstDefined(fallback.gamesFolder, gamesFolder);
651
- return detokenize(_gamesFolder, env);
652
- }
653
- async mediaFolder() {
654
- const gamesFolder = await this.gamesFolder();
655
- return resolve(gamesFolder, ZRomulatorFilesService.MediaFolderName);
656
- }
657
- async infoFolder() {
658
- const gamesFolder = await this.gamesFolder();
659
- return resolve(gamesFolder, ZRomulatorFilesService.InfoFolderName);
660
- }
661
- async contents(roots, path) {
662
- const repository = await this.seed();
663
- const folders = castArray(roots).map((root)=>`${trimEnd(root, "/")}/`);
664
- const byPaths = folders.map((folder)=>{
665
- const byPath = new ZFilterBinaryBuilder().subject("path");
666
- const pathFilter = path == null ? byPath.startsWith().value(folder) : byPath.equal().value(resolve(folder, path));
667
- return pathFilter.build();
668
- });
669
- const filter = byPaths.length > 1 ? new ZFilterLogicBuilder().or().clauses(byPaths).build() : first(byPaths);
670
- const sort = new ZSortBuilder().ascending("path").build();
671
- const request = new ZDataRequestBuilder().filter(filter).sort(sort).build();
672
- const nodes = await repository.retrieve(request);
673
- return path == null ? nodes : firstDefined(null, first(nodes));
674
- }
675
- async init() {
676
- await this.seed();
677
- }
678
- async dispose() {
679
- await this._repository.reset();
734
+ const ZRomulatorGamesToken = Symbol("romulator-games-service");
735
+ class ZRomulatorGamesService {
736
+ async list(req) {
737
+ const msg = `Searching for games`;
738
+ this._logger.log(new ZLogEntryBuilder().info().message(msg).build());
739
+ const systems = await this._systemsRepository.systems();
740
+ const games = await this._filesRepository.games();
741
+ const match = (data, filter)=>{
742
+ const needle = filter?.trim().toLowerCase();
743
+ const { name = "", system = "" } = data;
744
+ const systemId = system;
745
+ const systemName = firstTruthy("", systems.get(systemId)?.name);
746
+ if (!needle?.length) {
747
+ return true;
748
+ }
749
+ return [
750
+ name,
751
+ system,
752
+ systemName
753
+ ].filter((s)=>s.length).some((k)=>k.toLowerCase().includes(needle));
754
+ };
755
+ const options = new ZDataSourceStaticOptionsBuilder().search({
756
+ match
757
+ }).build();
758
+ const source = new ZDataSourceStatic(Array.from(games.values()), options);
759
+ const $sort = new ZSortBuilder().sorts(firstDefined([], req.sort)).ascending("system").ascending("name").build();
760
+ const $request = new ZDataRequestBuilder().copy(req).sort($sort).build();
761
+ const data = await source.retrieve($request);
762
+ const count = await source.count($request);
763
+ return new ZPageBuilder().count(count).data(data).build();
680
764
  }
681
- async seed() {
682
- const path = await this.gamesFolder();
683
- if (this._repository.path !== path) {
684
- await this._folderStream.write(await this.mediaFolder());
685
- await this._folderStream.write(await this.infoFolder());
686
- await this._repository.initialize(path, this._globs);
765
+ async get(id) {
766
+ const filter = new ZFilterBinaryBuilder().subject("id").equal().value(id).build();
767
+ const request = new ZDataRequestBuilder().filter(filter).size(1).build();
768
+ const { data: games } = await this.list(request);
769
+ const [game] = games;
770
+ if (game == null) {
771
+ const message = `Game, ${id}, was not found.`;
772
+ throw new NotFoundException(message);
687
773
  }
688
- return this._repository;
689
- }
690
- async media(path) {
691
- return this.contents(await this.mediaFolder(), path);
692
- }
693
- async systems(path) {
694
- // Systems use directories. There's a maximum limit of about 200 systems.
695
- // Since we don't actually need to read any metadata or scan through thousands
696
- // of unknown folders, we can use the supported system ids to just grab the
697
- // systems that we need. Since the file repository does a stat anyway, we can just
698
- // grab the systems from the file system and it should be fast enough. These are all
699
- // folders, so we don't even need the stats for them and we can assume folders.
700
- const games = await this.gamesFolder();
701
- const folders = path == null ? this._systems.map((s)=>`${s}/`) : resolve(games, path);
702
- const items = await this._fileSystem.search(folders, {
703
- cwd: games,
704
- stat: false
705
- });
706
- return path == null ? items : firstDefined(null, first(items));
707
- }
708
- async info(path) {
709
- return this.contents(await this.infoFolder(), path);
710
- }
711
- async games(path) {
712
- const root = await this.gamesFolder();
713
- return this.contents(this._systems.map((s)=>resolve(root, s)), path);
714
- }
715
- read(node) {
716
- return this._fileStream.read(node.path);
774
+ return game;
775
+ }
776
+ constructor(_systemsRepository, _filesRepository, logger){
777
+ _define_property$5(this, "_systemsRepository", void 0);
778
+ _define_property$5(this, "_filesRepository", void 0);
779
+ _define_property$5(this, "logger", void 0);
780
+ _define_property$5(this, "_logger", void 0);
781
+ this._systemsRepository = _systemsRepository;
782
+ this._filesRepository = _filesRepository;
783
+ this.logger = logger;
784
+ this._logger = new ZLoggerContext("ZRomulatorGamesService", logger);
717
785
  }
718
786
  }
719
- ZRomulatorFilesService = _ts_decorate$9([
787
+ ZRomulatorGamesService = _ts_decorate$a([
720
788
  Injectable(),
721
- _ts_param$5(0, Inject(ZRomulatorConfigsToken)),
722
- _ts_param$5(1, Inject(ZFileSystemToken)),
789
+ _ts_param$5(0, Inject(ZRomulatorFilesSystemsRepositoryToken)),
790
+ _ts_param$5(1, Inject(ZRomulatorFilesGamesRepositoryToken)),
791
+ _ts_param$5(2, Inject(ZLoggerToken)),
723
792
  _ts_metadata$5("design:type", Function),
724
793
  _ts_metadata$5("design:paramtypes", [
725
- typeof IZRomulatorConfigsService === "undefined" ? Object : IZRomulatorConfigsService,
726
- typeof IZFileSystemService === "undefined" ? Object : IZFileSystemService
794
+ typeof IZRomulatorFilesSystemsRepository === "undefined" ? Object : IZRomulatorFilesSystemsRepository,
795
+ typeof IZRomulatorFilesGamesRepository === "undefined" ? Object : IZRomulatorFilesGamesRepository,
796
+ typeof IZLogger === "undefined" ? Object : IZLogger
727
797
  ])
728
- ], ZRomulatorFilesService);
798
+ ], ZRomulatorGamesService);
729
799
 
730
- function _ts_decorate$8(decorators, target, key, desc) {
800
+ function _define_property$4(obj, key, value) {
801
+ if (key in obj) {
802
+ Object.defineProperty(obj, key, {
803
+ value: value,
804
+ enumerable: true,
805
+ configurable: true,
806
+ writable: true
807
+ });
808
+ } else {
809
+ obj[key] = value;
810
+ }
811
+ return obj;
812
+ }
813
+ function _ts_decorate$9(decorators, target, key, desc) {
731
814
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
732
815
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
733
816
  else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
@@ -741,40 +824,70 @@ function _ts_param$4(paramIndex, decorator) {
741
824
  decorator(target, key, paramIndex);
742
825
  };
743
826
  }
744
- class ZRomulatorFilesModule {
745
- _files;
746
- constructor(_files){
747
- this._files = _files;
827
+ class ZRomulatorGamesController {
828
+ list(query) {
829
+ return this._games.list(new ZDataRequestBuilder().query(query).build());
748
830
  }
749
- async onModuleInit() {
750
- await this._files.init();
831
+ get(identification) {
832
+ return this._games.get(identification);
751
833
  }
752
- async onModuleDestroy() {
753
- await this._files.dispose();
834
+ constructor(_games){
835
+ _define_property$4(this, "_games", void 0);
836
+ this._games = _games;
754
837
  }
755
838
  }
756
- ZRomulatorFilesModule = _ts_decorate$8([
839
+ _ts_decorate$9([
840
+ Get(),
841
+ _ts_param$4(0, Query()),
842
+ _ts_metadata$4("design:type", Function),
843
+ _ts_metadata$4("design:paramtypes", [
844
+ typeof IZDataRequestQuery === "undefined" ? Object : IZDataRequestQuery
845
+ ]),
846
+ _ts_metadata$4("design:returntype", typeof Promise === "undefined" ? Object : Promise)
847
+ ], ZRomulatorGamesController.prototype, "list", null);
848
+ _ts_decorate$9([
849
+ Get(":identification"),
850
+ _ts_param$4(0, Param("identification")),
851
+ _ts_metadata$4("design:type", Function),
852
+ _ts_metadata$4("design:paramtypes", [
853
+ String
854
+ ]),
855
+ _ts_metadata$4("design:returntype", void 0)
856
+ ], ZRomulatorGamesController.prototype, "get", null);
857
+ ZRomulatorGamesController = _ts_decorate$9([
858
+ Controller("games"),
859
+ _ts_param$4(0, Inject(ZRomulatorGamesToken)),
860
+ _ts_metadata$4("design:type", Function),
861
+ _ts_metadata$4("design:paramtypes", [
862
+ typeof IZRomulatorGamesService === "undefined" ? Object : IZRomulatorGamesService
863
+ ])
864
+ ], ZRomulatorGamesController);
865
+
866
+ function _ts_decorate$8(decorators, target, key, desc) {
867
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
868
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
869
+ else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
870
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
871
+ }
872
+ class ZRomulatorGamesModule {
873
+ }
874
+ ZRomulatorGamesModule = _ts_decorate$8([
757
875
  Module({
758
876
  imports: [
759
- ZFileSystemModule,
760
- ZRomulatorConfigsModule
877
+ ZRomulatorFilesModule,
878
+ ZLoggerModule
879
+ ],
880
+ controllers: [
881
+ ZRomulatorGamesController
761
882
  ],
762
883
  providers: [
763
884
  {
764
- provide: ZRomulatorFilesToken,
765
- useClass: ZRomulatorFilesService
885
+ provide: ZRomulatorGamesToken,
886
+ useClass: ZRomulatorGamesService
766
887
  }
767
- ],
768
- exports: [
769
- ZRomulatorFilesToken
770
888
  ]
771
- }),
772
- _ts_param$4(0, Inject(ZRomulatorFilesToken)),
773
- _ts_metadata$4("design:type", Function),
774
- _ts_metadata$4("design:paramtypes", [
775
- typeof IZRomulatorFilesService === "undefined" ? Object : IZRomulatorFilesService
776
- ])
777
- ], ZRomulatorFilesModule);
889
+ })
890
+ ], ZRomulatorGamesModule);
778
891
 
779
892
  function _ts_decorate$7(decorators, target, key, desc) {
780
893
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
@@ -822,6 +935,19 @@ ZRomulatorMediaGenerator = _ts_decorate$7([
822
935
  Injectable()
823
936
  ], ZRomulatorMediaGenerator);
824
937
 
938
+ function _define_property$3(obj, key, value) {
939
+ if (key in obj) {
940
+ Object.defineProperty(obj, key, {
941
+ value: value,
942
+ enumerable: true,
943
+ configurable: true,
944
+ writable: true
945
+ });
946
+ } else {
947
+ obj[key] = value;
948
+ }
949
+ return obj;
950
+ }
825
951
  function _ts_decorate$6(decorators, target, key, desc) {
826
952
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
827
953
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
@@ -838,14 +964,6 @@ function _ts_param$3(paramIndex, decorator) {
838
964
  }
839
965
  const ZRomulatorMediaToken = Symbol("romulator-media-service");
840
966
  class ZRomulatorMediaService {
841
- _generator;
842
- _files;
843
- _logger;
844
- constructor(_generator, _files, logger){
845
- this._generator = _generator;
846
- this._files = _files;
847
- this._logger = new ZLoggerContext("ZRomulatorMediaService", logger);
848
- }
849
967
  async findAllMedia() {
850
968
  const files = await this._files.media();
851
969
  return files.map((f)=>new ZRomulatorMediaBuilder().from(f.path).build()).filter((media)=>!!media.id);
@@ -940,20 +1058,41 @@ class ZRomulatorMediaService {
940
1058
  log = `Deleted ${url}`;
941
1059
  this._logger.log(new ZLogEntryBuilder().info().message(log).build());
942
1060
  }
1061
+ constructor(_generator, _files, logger){
1062
+ _define_property$3(this, "_generator", void 0);
1063
+ _define_property$3(this, "_files", void 0);
1064
+ _define_property$3(this, "_logger", void 0);
1065
+ this._generator = _generator;
1066
+ this._files = _files;
1067
+ this._logger = new ZLoggerContext("ZRomulatorMediaService", logger);
1068
+ }
943
1069
  }
944
1070
  ZRomulatorMediaService = _ts_decorate$6([
945
1071
  Injectable(),
946
1072
  _ts_param$3(0, Inject(ZRomulatorMediaGeneratorToken)),
947
- _ts_param$3(1, Inject(ZRomulatorFilesToken)),
1073
+ _ts_param$3(1, Inject(ZRomulatorFilesRepositoryToken)),
948
1074
  _ts_param$3(2, Inject(ZLoggerToken)),
949
1075
  _ts_metadata$3("design:type", Function),
950
1076
  _ts_metadata$3("design:paramtypes", [
951
1077
  typeof IZRomulatorMediaGenerator === "undefined" ? Object : IZRomulatorMediaGenerator,
952
- typeof IZRomulatorFilesService === "undefined" ? Object : IZRomulatorFilesService,
1078
+ typeof IZRomulatorFilesRepository === "undefined" ? Object : IZRomulatorFilesRepository,
953
1079
  typeof IZLogger === "undefined" ? Object : IZLogger
954
1080
  ])
955
1081
  ], ZRomulatorMediaService);
956
1082
 
1083
+ function _define_property$2(obj, key, value) {
1084
+ if (key in obj) {
1085
+ Object.defineProperty(obj, key, {
1086
+ value: value,
1087
+ enumerable: true,
1088
+ configurable: true,
1089
+ writable: true
1090
+ });
1091
+ } else {
1092
+ obj[key] = value;
1093
+ }
1094
+ return obj;
1095
+ }
957
1096
  function _ts_decorate$5(decorators, target, key, desc) {
958
1097
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
959
1098
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
@@ -969,10 +1108,6 @@ function _ts_param$2(paramIndex, decorator) {
969
1108
  };
970
1109
  }
971
1110
  class ZRomulatorMediaController {
972
- _media;
973
- constructor(_media){
974
- this._media = _media;
975
- }
976
1111
  list(params) {
977
1112
  const request = new ZDataRequestBuilder().query(params).build();
978
1113
  return this._media.list(request);
@@ -984,6 +1119,10 @@ class ZRomulatorMediaController {
984
1119
  async delete(identification) {
985
1120
  await this._media.delete(identification);
986
1121
  }
1122
+ constructor(_media){
1123
+ _define_property$2(this, "_media", void 0);
1124
+ this._media = _media;
1125
+ }
987
1126
  }
988
1127
  _ts_decorate$5([
989
1128
  ApiQuery({
@@ -1120,6 +1259,19 @@ ZRomulatorMediaModule = _ts_decorate$4([
1120
1259
  })
1121
1260
  ], ZRomulatorMediaModule);
1122
1261
 
1262
+ function _define_property$1(obj, key, value) {
1263
+ if (key in obj) {
1264
+ Object.defineProperty(obj, key, {
1265
+ value: value,
1266
+ enumerable: true,
1267
+ configurable: true,
1268
+ writable: true
1269
+ });
1270
+ } else {
1271
+ obj[key] = value;
1272
+ }
1273
+ return obj;
1274
+ }
1123
1275
  function _ts_decorate$3(decorators, target, key, desc) {
1124
1276
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1125
1277
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
@@ -1136,27 +1288,18 @@ function _ts_param$1(paramIndex, decorator) {
1136
1288
  }
1137
1289
  const ZRomulatorSystemsToken = Symbol("romulator-systems-service");
1138
1290
  class ZRomulatorSystemsService {
1139
- _files;
1140
- logger;
1141
- _logger;
1142
- constructor(_files, logger){
1143
- this._files = _files;
1144
- this.logger = logger;
1145
- this._logger = new ZLoggerContext("ZRomulatorSystemsService", logger);
1146
- }
1147
1291
  async list(req) {
1148
1292
  const page = firstDefined(1, req.page);
1149
1293
  const size = firstDefined(Infinity, req.size);
1150
1294
  let msg = `Retrieving systems page, ${page}, with size, ${size}.`;
1151
1295
  this._logger.log(new ZLogEntryBuilder().info().message(msg).build());
1152
- const folders = await this._files.systems();
1153
- const systems = await Promise.all(folders.map((folder)=>folder.path).map((path)=>basename(path)).filter((slug)=>isSystemId(slug)).map((slug)=>this._createSystemFromSlug(slug)));
1296
+ const systemMap = await this._systemsRepository.systems();
1297
+ const systems = Array.from(systemMap.values());
1154
1298
  msg = `Found ${systems.length} systems`;
1155
1299
  this._logger.log(new ZLogEntryBuilder().info().message(msg).build());
1156
1300
  const sourceOptions = new ZDataSourceStaticOptionsBuilder().search(new ZDataSearchFields([
1157
1301
  "id",
1158
- "name",
1159
- "short"
1302
+ "name"
1160
1303
  ])).build();
1161
1304
  const source = new ZDataSourceStatic(systems, sourceOptions);
1162
1305
  const data = await source.retrieve(req);
@@ -1169,45 +1312,49 @@ class ZRomulatorSystemsService {
1169
1312
  const message = `The specified system slug, ${id}, is not supported.`;
1170
1313
  throw new NotFoundException(message);
1171
1314
  }
1172
- const node = await this._files.systems(id);
1173
- if (node == null) {
1315
+ const filter = new ZFilterBinaryBuilder().subject("id").equal().value(id).build();
1316
+ const request = new ZDataRequestBuilder().filter(filter).size(1).build();
1317
+ const { data: systems } = await this.list(request);
1318
+ const [system] = systems;
1319
+ if (system == null) {
1174
1320
  const message = `System with slug, ${id}, was not found.`;
1175
1321
  throw new NotFoundException(message);
1176
1322
  }
1177
- return this._createSystemFromSlug(id);
1178
- }
1179
- async _createSystemFromSlug(slug) {
1180
- const system = new ZRomulatorSystemBuilder().id(slug);
1181
- const path = `${slug}/info.json`;
1182
- const info = await this._files.info(path);
1183
- if (info == null) {
1184
- // Best we can do right now.
1185
- return system.build();
1186
- }
1187
- try {
1188
- const contents = await this._files.read(info);
1189
- const json = JSON.parse(contents.toString());
1190
- return system.assign(json).redact().build();
1191
- } catch (e) {
1192
- // Best we can do
1193
- const err = createError(e);
1194
- const msg = `Cannot read system metadata, ${err.message}`;
1195
- this._logger.log(new ZLogEntryBuilder().error().message(msg).build());
1196
- return system.build();
1197
- }
1323
+ return system;
1324
+ }
1325
+ constructor(_systemsRepository, logger){
1326
+ _define_property$1(this, "_systemsRepository", void 0);
1327
+ _define_property$1(this, "logger", void 0);
1328
+ _define_property$1(this, "_logger", void 0);
1329
+ this._systemsRepository = _systemsRepository;
1330
+ this.logger = logger;
1331
+ this._logger = new ZLoggerContext("ZRomulatorSystemsService", logger);
1198
1332
  }
1199
1333
  }
1200
1334
  ZRomulatorSystemsService = _ts_decorate$3([
1201
1335
  Injectable(),
1202
- _ts_param$1(0, Inject(ZRomulatorFilesToken)),
1336
+ _ts_param$1(0, Inject(ZRomulatorFilesSystemsRepositoryToken)),
1203
1337
  _ts_param$1(1, Inject(ZLoggerToken)),
1204
1338
  _ts_metadata$1("design:type", Function),
1205
1339
  _ts_metadata$1("design:paramtypes", [
1206
- typeof IZRomulatorFilesService === "undefined" ? Object : IZRomulatorFilesService,
1340
+ typeof IZRomulatorFilesSystemsRepository === "undefined" ? Object : IZRomulatorFilesSystemsRepository,
1207
1341
  typeof IZLogger === "undefined" ? Object : IZLogger
1208
1342
  ])
1209
1343
  ], ZRomulatorSystemsService);
1210
1344
 
1345
+ function _define_property(obj, key, value) {
1346
+ if (key in obj) {
1347
+ Object.defineProperty(obj, key, {
1348
+ value: value,
1349
+ enumerable: true,
1350
+ configurable: true,
1351
+ writable: true
1352
+ });
1353
+ } else {
1354
+ obj[key] = value;
1355
+ }
1356
+ return obj;
1357
+ }
1211
1358
  function _ts_decorate$2(decorators, target, key, desc) {
1212
1359
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1213
1360
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
@@ -1223,16 +1370,16 @@ function _ts_param(paramIndex, decorator) {
1223
1370
  };
1224
1371
  }
1225
1372
  class ZRomulatorSystemsController {
1226
- _systems;
1227
- constructor(_systems){
1228
- this._systems = _systems;
1229
- }
1230
1373
  list(query) {
1231
1374
  return this._systems.list(new ZDataRequestBuilder().query(query).build());
1232
1375
  }
1233
1376
  get(identification) {
1234
1377
  return this._systems.get(identification);
1235
1378
  }
1379
+ constructor(_systems){
1380
+ _define_property(this, "_systems", void 0);
1381
+ this._systems = _systems;
1382
+ }
1236
1383
  }
1237
1384
  _ts_decorate$2([
1238
1385
  Get(),
@@ -1288,6 +1435,9 @@ ZRomulatorSystemsModule = _ts_decorate$1([
1288
1435
  provide: ZRomulatorSystemsToken,
1289
1436
  useClass: ZRomulatorSystemsService
1290
1437
  }
1438
+ ],
1439
+ exports: [
1440
+ ZRomulatorSystemsToken
1291
1441
  ]
1292
1442
  })
1293
1443
  ], ZRomulatorSystemsModule);