gdk-version-archiver 1.0.0-alpha → 1.0.0-alpha.2

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.
Files changed (3) hide show
  1. package/README.md +10 -7
  2. package/dist/cli.js +36 -1102
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -10,22 +10,21 @@ A CLI tool for archiving and managing Minecraft GDK installations on Windows. It
10
10
  - **Concurrent Copying**: Fast archiving using a task concurrency channel.
11
11
 
12
12
  ## Setup and Usage
13
-
14
13
  **Prerequisites**: Windows OS, [Bun](https://bun.sh/) runtime, and Powershell.
15
14
 
16
15
  ```bash
17
- pnpm install
16
+ pnpm add gdk-version-archiver
18
17
  npm run build
19
18
  ```
20
19
 
21
- Binary name: `gdk-archive-manager`
20
+ CLI name: `gdkva`
22
21
 
23
- - **Archive**: `bunx gdk-archive-manager archive [package-pattern]`
22
+ - **Archive**: `gdkva archive [package-pattern]`
24
23
  - `--tag <name>`: Target tag (default: `current`).
25
24
  - `--force`: Overwrite existing mirrors.
26
25
  - `--concurrency <n>`: Parallel file copy limit. Default is `10`, recommended is to follow default value.
27
- - **List**: `bunx gdk-archive-manager list` - Show stored mirrors and tags.
28
- - **Run**: `bunx gdk-archive-manager run --tag <name>` - Launch a tagged version. Default is `current` tag.
26
+ - **List**: `gdkva list` - Show stored mirrors and tags.
27
+ - **Run**: `gdkva run --tag <name>` - Launch a tagged version. Default is `current` tag.
29
28
 
30
29
  Global flags: `--verbose`, `--tag <name>`.
31
30
 
@@ -38,4 +37,8 @@ Data is stored in `%APPDATA%\ConMaster.BedrockArchiver\clients\`.
38
37
 
39
38
  The tool bypasses GDK file protections by invoking a copy instruction within the package context. It then uses symlink junctions to allow external tools to reference a static path while the underlying version is updated.
40
39
 
41
- MIT License
40
+ ### Is it slow?
41
+ This tool performs have to call powershell.exe under the hood to get access to the windows package manager to obtain package information, after wards its only matter of your hard-drive write throughput, by default this tool performs 10 copy operations concurrently.
42
+
43
+ ### Why it feels slow?
44
+ We only logging directories thats are currently being traversed, not all files, so if you think its being stuck its probably coping hundreds of files.
package/dist/cli.js CHANGED
@@ -1,1102 +1,36 @@
1
- import { copyFile, mkdir, readFile, realpath, rm, rmdir, stat, symlink } from "node:fs/promises";
2
- import { join, resolve } from "node:path";
3
- import { exit } from "node:process";
4
- import { existsSync } from "node:fs";
5
- import { pathToFileURL } from "node:url";
6
-
7
- //#region node_modules/.pnpm/con-utils@0.0.2/node_modules/con-utils/dist/format-helper-CE23_M3S.js
8
- var FormatCode = class FormatCode$1 {
9
- prefixes;
10
- suffixes;
11
- constructor(prefix, suffix) {
12
- this.prefixes = prefix;
13
- this.suffixes = suffix;
14
- }
15
- static create(format, reset) {
16
- return new FormatCode$1([format], reset === void 0 ? [] : [reset]);
17
- }
18
- static createForegroundRGB(red, green, blue) {
19
- return new FormatCode$1([
20
- 38,
21
- 2,
22
- red,
23
- green,
24
- blue
25
- ], [39]);
26
- }
27
- static createBackgroundRGB(red, green, blue) {
28
- return new FormatCode$1([
29
- 48,
30
- 2,
31
- red,
32
- green,
33
- blue
34
- ], [49]);
35
- }
36
- get prefix() {
37
- return this.compile(this.prefixes);
38
- }
39
- get suffix() {
40
- return this.compile(this.suffixes);
41
- }
42
- compile(format) {
43
- return `\x1b[${format.join(";")}m`;
44
- }
45
- merge(format) {
46
- pushUnique([...this.prefixes], ...format.prefixes);
47
- pushUnique([...this.suffixes], ...this.suffixes);
48
- return new FormatCode$1(this.prefixes, this.suffixes);
49
- }
50
- get wrap() {
51
- return (raw) => `${this.prefix}${raw}${this.suffix}`;
52
- }
53
- get format() {
54
- return (template, ...args) => this.wrap(template.reduce((acc, part, i) => acc + this.wrap(part) + (args[i] ?? ""), ""));
55
- }
56
- };
57
- const DIM$1 = FormatCode.create(2, 22);
58
- const ITALIC = FormatCode.create(3, 23);
59
- const UNDERLINE = FormatCode.create(4, 24);
60
- const INVERSE = FormatCode.create(7, 27);
61
- const CENSOR = FormatCode.create(8, 28);
62
- const CANCELED = FormatCode.create(9, 29);
63
- const SOFT_RESET = [
64
- 21,
65
- 22,
66
- 23,
67
- 24,
68
- 25,
69
- 27,
70
- 28,
71
- 29,
72
- 39,
73
- 49,
74
- 54,
75
- 55,
76
- 65
77
- ].join(";");
78
- /**
79
- * Pushes value into array if it's not already present.
80
- * Returns true if inserted, false if already existed.
81
- */
82
- function pushUnique(arr, ...values) {
83
- for (const value of values) {
84
- if (arr.includes(value)) continue;
85
- arr.push(value);
86
- }
87
- }
88
- var Format = class {
89
- /**
90
- * Wraps words in a string to fit within a specified print length.
91
- * @param text The input string to wrap.
92
- * @param printLength The maximum length of each line.
93
- * @returns A generator yielding wrapped lines.
94
- */
95
- static *wrapWords(text, printLength) {
96
- const words = text.split(/\s+/);
97
- const currentLineWords = [];
98
- let currentLength = 0;
99
- for (const word of words) {
100
- const wordLength = word.length;
101
- if (currentLength + wordLength + currentLineWords.length > printLength && currentLineWords.length > 0) {
102
- yield currentLineWords.join(" ");
103
- currentLineWords.length = 0;
104
- currentLength = 0;
105
- }
106
- currentLineWords.push(word);
107
- currentLength += wordLength;
108
- }
109
- if (currentLineWords.length > 0) yield currentLineWords.join(" ");
110
- }
111
- };
112
-
113
- //#endregion
114
- //#region node_modules/.pnpm/con-utils@0.0.2/node_modules/con-utils/dist/con-cli.js
115
- var ValueTypeValidator = class {
116
- toString() {
117
- return this.name;
118
- }
119
- };
120
- const VALID_BOOLEAN_TYPES = new Set([
121
- "false",
122
- "true",
123
- "0",
124
- "1"
125
- ]);
126
- var BooleanTypeValidator = class extends ValueTypeValidator {
127
- name = "bool";
128
- description = "Boolean Type (true/false)";
129
- isValid(input) {
130
- return VALID_BOOLEAN_TYPES.has(input.toLowerCase());
131
- }
132
- coerce(input) {
133
- input = input.toLowerCase();
134
- return input === "false" || input === "0" ? false : Boolean(input);
135
- }
136
- };
137
- var NumberTypeValidator = class extends ValueTypeValidator {
138
- name = "number";
139
- description = "Floating point number type";
140
- isValid(input) {
141
- return !isNaN(Number(input));
142
- }
143
- coerce(input) {
144
- return Number(input);
145
- }
146
- };
147
- var StringTypeValidator = class extends ValueTypeValidator {
148
- name = "string";
149
- description = "Raw string type";
150
- isValid(input) {
151
- return typeof input === "string";
152
- }
153
- coerce(input) {
154
- return input;
155
- }
156
- };
157
- /**
158
- * Represents a command-line argument with validation and type coercion.
159
- * @template T - The type of the argument value after validation
160
- */
161
- var ArgumentLike = class {
162
- name;
163
- validator;
164
- description;
165
- defaultValue;
166
- constructor(name, opts) {
167
- this.name = name.toLowerCase();
168
- this.validator = opts.validator;
169
- this.description = opts?.description ?? void 0;
170
- this.defaultValue = opts.defaultValue;
171
- }
172
- /**
173
- * Determines if this argument is required (has no default value).
174
- * @returns True if the argument must be provided
175
- */
176
- isRequired() {
177
- return this.defaultValue === null;
178
- }
179
- /**
180
- * Validates and coerces a string value to the argument's type.
181
- * @param value - The string value to enforce
182
- * @returns The coerced value of type T
183
- * @throws {Error} If the value is invalid or required argument is missing
184
- */
185
- enforce(value) {
186
- if (value === void 0 || value === null) if (this.defaultValue === null) throw new Error("Following argument is required and can't be omitted! " + this.name);
187
- else return this.defaultValue;
188
- if (!this.validator.isValid(value)) throw new Error("Following argument has not valid value! " + this.name + " -> " + value);
189
- return this.validator.coerce(value);
190
- }
191
- /**
192
- * Returns a string representation of this argument for help text.
193
- * @returns Formatted string like `<name:type>` or `[name:type]`
194
- */
195
- toString() {
196
- const $ = `${this.name}:${this.validator.toString()}`;
197
- return this.isRequired() ? `<${$}>` : `[${$}]`;
198
- }
199
- };
200
- /**
201
- * Represents a named flag that accepts a value.
202
- * @template T - The type of the flag value
203
- */
204
- var ValueFlag = class extends ArgumentLike {
205
- long;
206
- short;
207
- isValueFlag;
208
- constructor(name, opts) {
209
- super(name, opts);
210
- this.long = opts.long?.toLowerCase();
211
- this.short = opts.short?.toLowerCase();
212
- this.isValueFlag = true;
213
- }
214
- /**
215
- * Determines if this flag accepts a value.
216
- * @returns True if this flag is value-based
217
- */
218
- isValueBased() {
219
- return this.isValueFlag;
220
- }
221
- /**
222
- * Returns a string representation of this flag for help text.
223
- * @returns Formatted string like `--long, -s`
224
- */
225
- toString() {
226
- return [this.long && `--${this.long}`, this.short && `-${this.short}`].filter(Boolean).join(", ");
227
- }
228
- };
229
- /**
230
- * Represents a boolean flag (presence indicator).
231
- */
232
- var Flag = class extends ValueFlag {
233
- constructor(name, opts) {
234
- const { description: description$1, long, short } = opts ?? {};
235
- super(name, {
236
- validator: new BooleanTypeValidator(),
237
- defaultValue: false,
238
- description: description$1,
239
- long,
240
- short
241
- });
242
- this.isValueFlag = false;
243
- }
244
- };
245
- /**
246
- * A collection of flags with lookup capabilities by name, long form, or short form.
247
- */
248
- var FlagsGroup = class {
249
- flags = /* @__PURE__ */ new Set();
250
- map = /* @__PURE__ */ new Map();
251
- base = null;
252
- constructor(base) {
253
- this.base = base ?? null;
254
- }
255
- /**
256
- * Checks if a flag exists in this group or inherited from base groups.
257
- * @param valueFlag - The flag to check for
258
- * @returns True if the flag is registered
259
- */
260
- hasFlag(valueFlag) {
261
- return this.hasOwnFlag(valueFlag) || (this.base?.hasFlag(valueFlag) ?? false);
262
- }
263
- /**
264
- * Checks if a flag exists directly in this group (not inherited).
265
- * @param valueFlag - The flag to check for
266
- * @returns True if the flag is registered in this group
267
- */
268
- hasOwnFlag(valueFlag) {
269
- return this.flags.has(valueFlag);
270
- }
271
- /**
272
- * Adds flags and registers their searchable keys.
273
- * @param flags - The flags to add
274
- * @returns This group for chaining
275
- */
276
- add(...flags) {
277
- for (const flag of flags) {
278
- this.flags.add(flag);
279
- this.setGeneralInternal(flag.name, flag);
280
- if (flag.long) this.setLongInternal(flag.long, flag);
281
- if (flag.short) this.setShortInternal(flag.short, flag);
282
- }
283
- return this;
284
- }
285
- /**
286
- * Adds a short alias for an existing flag.
287
- * @param flag - The flag to add an alias for
288
- * @param short - The short form alias
289
- * @returns This group for chaining
290
- */
291
- addShortAlias(flag, short) {
292
- this.flags.add(flag);
293
- this.setGeneralInternal(flag.name, flag);
294
- this.setShortInternal(short, flag);
295
- return this;
296
- }
297
- /**
298
- * Adds a long alias for an existing flag.
299
- * @param flag - The flag to add an alias for
300
- * @param long - The long form alias
301
- * @returns This group for chaining
302
- */
303
- addLongAlias(flag, long) {
304
- this.flags.add(flag);
305
- this.setGeneralInternal(flag.name, flag);
306
- this.setLongInternal(long, flag);
307
- return this;
308
- }
309
- setLongInternal(key, flag) {
310
- this.map.set(`long:${key}`, flag);
311
- }
312
- setShortInternal(key, flag) {
313
- this.map.set(`short:${key}`, flag);
314
- }
315
- setGeneralInternal(key, flag) {
316
- this.map.set(`flag:${key}`, flag);
317
- }
318
- getLong(key) {
319
- return this.map.get(`long:${key}`) ?? this.base?.getLong(key) ?? null;
320
- }
321
- /**
322
- * Retrieves a flag by its short form name.
323
- * @param key - The short form name
324
- * @returns The flag or null if not found
325
- */
326
- getShort(key) {
327
- return this.map.get(`short:${key}`) ?? this.base?.getShort(key) ?? null;
328
- }
329
- /**
330
- * Retrieves a flag by its primary name.
331
- * @param name - The flag name
332
- * @returns The flag or null if not found
333
- */
334
- getByName(name) {
335
- return this.map.get(`flag:${name}`) ?? this.base?.getByName(name) ?? null;
336
- }
337
- /**
338
- * Gets all flags directly registered in this group.
339
- * @returns A read-only set of all flags
340
- */
341
- getAll() {
342
- return this.flags;
343
- }
344
- };
345
- const dim$1 = DIM$1.wrap;
346
- var CommandHelpStringBuilder = class {
347
- static *buildHelp(command, preferredWidth$1) {
348
- yield dim$1(` Usage of ${command.syntax()}`);
349
- yield "";
350
- yield dim$1(" " + "-".repeat(preferredWidth$1));
351
- yield* Format.wrapWords(command.description ?? "", preferredWidth$1).map((e) => dim$1(" ") + e);
352
- yield dim$1(" " + "-".repeat(preferredWidth$1));
353
- yield "";
354
- yield* this.buildFlags(command);
355
- yield "";
356
- }
357
- static *buildFlags(command) {
358
- yield dim$1("Flags:");
359
- yield* command.flags.getAll().values().map((e) => " " + e.toString().padEnd(20) + " " + dim$1(e.description ?? ""));
360
- }
361
- };
362
- const dim = DIM$1.wrap;
363
- var Command = class {
364
- parent;
365
- uniqueHelpFlag;
366
- name;
367
- flags;
368
- description;
369
- onFlags;
370
- constructor(name, description$1, parent = null) {
371
- this.name = name.toLowerCase();
372
- this.description = description$1 ?? "";
373
- this.flags = new FlagsGroup(parent?.flags ?? null);
374
- this.parent = parent;
375
- this.uniqueHelpFlag = new Flag("help", {
376
- description: "Show's help message",
377
- short: "?",
378
- long: "help"
379
- });
380
- this.flags.add(this.uniqueHelpFlag);
381
- this.flags.addShortAlias(this.uniqueHelpFlag, "h");
382
- }
383
- getParentCommand() {
384
- return this.parent ?? null;
385
- }
386
- *getHelp() {
387
- yield* CommandHelpStringBuilder.buildHelp(this, preferredWidth);
388
- }
389
- *getFlags() {
390
- yield dim("Flags:");
391
- yield* this.flags.getAll().values().map((e) => " " + e.toString().padEnd(20) + " " + dim(e.description ?? ""));
392
- }
393
- getFullPathName() {
394
- return `${this.parent ? this.parent.getFullPathName() + " " : ""}${this.name}`;
395
- }
396
- };
397
- var CommandAction = class CommandAction$1 extends Command {
398
- argumentsTypes;
399
- action = null;
400
- constructor(name, opts) {
401
- super(name, opts.description, opts.parent);
402
- this.argumentsTypes = opts.argTypes;
403
- }
404
- *getHelp() {
405
- yield* super.getHelp();
406
- if (!this.argumentsTypes.length) return;
407
- yield dim(" Arguments:");
408
- yield* this.argumentsTypes.map((e) => ` ${e.toString().padEnd(20)} ${dim(e.description ?? "")}`);
409
- yield "";
410
- }
411
- static create(name, description$1, parent, args) {
412
- return new CommandAction$1(name, {
413
- argTypes: args,
414
- description: description$1,
415
- parent
416
- });
417
- }
418
- syntax() {
419
- return `${this.getFullPathName()} ${this.argumentsTypes.map((e) => e.isRequired() ? `<${e.name}:${e.validator.name}>` : `[${e.name}:${e.validator.name}]`).join(" ")}`;
420
- }
421
- };
422
- var GroupCommand = class GroupCommand$1 extends Command {
423
- action = null;
424
- subcommands = /* @__PURE__ */ new Map();
425
- static create(name, description$1, parent) {
426
- return new GroupCommand$1(name, description$1, parent);
427
- }
428
- createGroup(name, description$1) {
429
- const cmd = GroupCommand$1.create(name, description$1, this);
430
- this.subcommands.set(name, cmd);
431
- return cmd;
432
- }
433
- createAction(name, description$1, args) {
434
- const cmd = CommandAction.create(name, description$1, this, args);
435
- this.subcommands.set(name, cmd);
436
- return cmd;
437
- }
438
- syntax() {
439
- return `${this.getFullPathName()} <${this.subcommands.keys().toArray().join("|")}>`;
440
- }
441
- *getHelp() {
442
- yield* super.getHelp();
443
- yield dim(" SubCommands:");
444
- let p = preferredWidth - 25;
445
- yield* this.subcommands.values().map((e) => ` ${e.name.padEnd(20)} ${dim((e.description?.length ?? 0) > p ? (e.description?.substring(0, p) ?? "") + "..." : e.description ?? "")}`);
446
- yield "";
447
- }
448
- };
449
- let preferredWidth = 75;
450
- /**
451
- * Represents the result of parsing a command-line input.
452
- * Contains the matched command, parsed arguments, and resolved flags.
453
- */
454
- var ParseResult = class {
455
- constructor(command, args, flags) {
456
- this.command = command;
457
- this.args = args;
458
- this.flags = flags;
459
- }
460
- /**
461
- * Gets the value of a flag, falling back to its default if not provided.
462
- * @template T - The type of the flag value
463
- * @param flag - The flag to retrieve
464
- * @returns The flag value or its default value
465
- */
466
- getValue(flag) {
467
- return this.flags.get(flag) ?? flag.defaultValue;
468
- }
469
- /**
470
- * Gets the executable function for this command if it's an action command.
471
- * @returns The bound action function or null if this is a group command
472
- */
473
- getExecutable() {
474
- if ("action" in this.command && typeof this.command["action"] === "function") return this.command.action.bind(null, this, ...this.args);
475
- return null;
476
- }
477
- };
478
- /**
479
- * Error thrown during command-line parsing.
480
- * Contains context about where and why parsing failed.
481
- */
482
- var ParserError = class extends Error {
483
- command;
484
- argv;
485
- index;
486
- flags;
487
- constructor(command, message, argv, index, flags) {
488
- super(message);
489
- this.command = command;
490
- this.name = new.target.name;
491
- this.argv = argv;
492
- this.index = index;
493
- this.flags = flags ?? null;
494
- }
495
- };
496
- /**
497
- * Parses command-line arguments into commands, flags, and positional arguments.
498
- */
499
- var CommandsParser = class CommandsParser$1 {
500
- argv;
501
- index;
502
- lastError = null;
503
- constructor(argv, index) {
504
- this.argv = argv;
505
- this.index = index;
506
- }
507
- /**
508
- * Parses the provided command against the command-line arguments.
509
- * @param command - The command to parse
510
- * @returns The parse result containing the matched command and parsed values
511
- * @throws {ParserError} If parsing fails
512
- */
513
- parse(command) {
514
- if (command instanceof GroupCommand) return this.parseGroupCommand(command);
515
- else if (command instanceof CommandAction) return this.parseActionCommand(command);
516
- else throw new ParserError(command, "Unsupported command type", this.argv, this.index);
517
- }
518
- parseGroupCommand(command) {
519
- const RESULT = new ParseResult(command, [], /* @__PURE__ */ new Map());
520
- let argument = this.getArgument();
521
- if (!argument) {
522
- if (!command.action) throw new ParserError(command, `Expected subcommand name, received none: ${command.getFullPathName()}`, this.argv, this.index);
523
- return new ParseResult(command, [], /* @__PURE__ */ new Map());
524
- }
525
- if (argument.startsWith("-")) {
526
- while (argument = this.getArgument()) {
527
- const flag = CommandsParser$1.parseFlag(argument);
528
- if (!flag) break;
529
- const result = this.resolveFlag(command.flags, flag, this.getArgument(1));
530
- if (!result) if (this.getLastError()) throw new ParserError(command, this.getLastError(), this.argv, this.index, new Set(RESULT.flags.keys()));
531
- else throw new ParserError(command, "Failed to resolve tag with name: " + flag.name, this.argv, this.index, new Set(RESULT.flags.keys()));
532
- const { flag: FLAG_TYPE, nextValueUsed, value } = result;
533
- this.index++;
534
- if (nextValueUsed) this.index++;
535
- RESULT.flags.set(FLAG_TYPE, value);
536
- }
537
- if (!argument) return RESULT;
538
- }
539
- const sub = argument.toLowerCase();
540
- const cmd = command.subcommands.get(sub);
541
- if (!cmd) throw new ParserError(command, `Unknown subcommand: ${command.getFullPathName()} >>${sub}<<`, this.argv, this.index, new Set(RESULT.flags.keys()));
542
- this.index++;
543
- try {
544
- const HRESULT = this.parse(cmd);
545
- for (const flag of RESULT.flags.keys()) if (!HRESULT.flags.has(flag)) HRESULT.flags.set(flag, RESULT.flags.get(flag));
546
- return HRESULT;
547
- } catch (error) {
548
- if (error instanceof ParserError) {
549
- const flags = error.flags;
550
- if (flags) RESULT.flags.keys().forEach((_) => flags.add(_));
551
- else error.flags = new Set(RESULT.flags.keys());
552
- }
553
- throw error;
554
- }
555
- }
556
- parseActionCommand(command) {
557
- const positionals = [];
558
- let argument;
559
- const FLAGS_MAP = /* @__PURE__ */ new Map();
560
- while (argument = this.getArgument()) {
561
- const flag = CommandsParser$1.parseFlag(argument);
562
- this.index++;
563
- if (!flag) {
564
- positionals.push(argument);
565
- continue;
566
- }
567
- const result = this.resolveFlag(command.flags, flag, this.getArgument());
568
- if (!result) if (this.getLastError()) throw new ParserError(command, this.getLastError(), this.argv, this.index, new Set(FLAGS_MAP.keys()));
569
- else {
570
- positionals.push(argument);
571
- continue;
572
- }
573
- const { flag: FLAG_TYPE, nextValueUsed, value } = result;
574
- if (nextValueUsed) this.index++;
575
- FLAGS_MAP.set(FLAG_TYPE, value);
576
- }
577
- const finalArgs = [];
578
- for (let idx = 0; idx < command.argumentsTypes.length; idx++) {
579
- const def = command.argumentsTypes[idx];
580
- const posVal = positionals[idx];
581
- if (typeof posVal === "undefined") {
582
- if (def.isRequired()) throw new ParserError(command, `Missing required argument: ${command.getFullPathName()} ... ${def.toString()}`, this.argv, this.index, new Set(FLAGS_MAP.keys()));
583
- finalArgs.push(def.defaultValue);
584
- } else {
585
- if (!def.validator.isValid(posVal)) throw new ParserError(command, `Invalid value: ${posVal}`, this.argv, this.index, new Set(FLAGS_MAP.keys()));
586
- finalArgs.push(def.enforce(posVal));
587
- }
588
- }
589
- for (let j = command.argumentsTypes.length; j < positionals.length; j++) finalArgs.push(positionals[j]);
590
- return new ParseResult(command, finalArgs, FLAGS_MAP);
591
- }
592
- resolveFlag(group, flag, next) {
593
- const FLAG_TYPE = flag.isLong ? group.getLong(flag.name) : group.getShort(flag.name);
594
- if (!FLAG_TYPE) return this.setLastError(null);
595
- let nextValueUsed = false;
596
- if (FLAG_TYPE.isValueBased()) {
597
- let { value } = flag;
598
- if (value === null) {
599
- nextValueUsed = true;
600
- value = next;
601
- }
602
- if (value === null) {
603
- if (FLAG_TYPE.defaultValue) return {
604
- flag: FLAG_TYPE,
605
- value: null,
606
- nextValueUsed
607
- };
608
- return this.setLastError("Flag with no default value, requires value to be set explicitly, flag: " + flag.name);
609
- }
610
- if (!FLAG_TYPE.validator.isValid(value)) return this.setLastError("Incorrect type passed as value for flag: " + flag.name + " value: " + value);
611
- return {
612
- flag: FLAG_TYPE,
613
- value: FLAG_TYPE.validator.coerce(value),
614
- nextValueUsed
615
- };
616
- } else if (flag.value) return this.setLastError("Syntax error, this flag doesn't supports values: " + flag.name + " value: " + flag.value);
617
- return {
618
- flag: FLAG_TYPE,
619
- value: true,
620
- nextValueUsed
621
- };
622
- }
623
- getLastError() {
624
- return this.lastError;
625
- }
626
- setLastError(error) {
627
- this.lastError = error;
628
- return null;
629
- }
630
- getArgument(relative = 0) {
631
- return this.argv[this.index + (relative ?? 0)] ?? null;
632
- }
633
- /**
634
- * Parses a flag string into its components (name, type, and optional value).
635
- * @param argument - The argument string to parse (e.g., "-f", "--flag=value")
636
- * @returns The parsed flag result or null if the argument is not a flag
637
- */
638
- static parseFlag(argument) {
639
- if (argument.length < 2 || argument[0] !== "-") return null;
640
- const isLongBit = argument[1] === "-", hasValueIndex = argument.indexOf("="), hasValueBit = hasValueIndex !== -1;
641
- let name, value = hasValueBit ? argument.substring(hasValueIndex + 1) : null;
642
- if (isLongBit) name = argument.substring(2, hasValueBit ? hasValueIndex : void 0);
643
- else {
644
- name = argument[1];
645
- if (!value && argument.length > 2) value = argument.substring(2);
646
- }
647
- return {
648
- name: name.toLowerCase(),
649
- isLong: isLongBit,
650
- value
651
- };
652
- }
653
- };
654
- /**
655
- * Main entry point for parsing and executing command-line arguments.
656
- * Handles parsing, flag resolution, and command execution with error handling.
657
- */
658
- var CommandLine = class {
659
- constructor() {}
660
- /**
661
- * Parses command-line arguments and executes the appropriate command.
662
- * @param argv - The command-line arguments to parse (typically process.argv)
663
- * @param command - The root command to parse against
664
- * @throws {ParserError} If parsing fails or validation errors occur
665
- */
666
- static async run(argv, command) {
667
- const parser = new CommandsParser(argv, 2);
668
- try {
669
- const result = parser.parse(command);
670
- if (result.flags.has(result.command.uniqueHelpFlag)) return void console.info(Array.from(result.command.getHelp()).join("\r\n"));
671
- for (const flag of result.flags.keys()) for (let command$1 = result.command; command$1; command$1 = command$1.getParentCommand()) if (command$1.flags.hasOwnFlag(flag)) command$1.onFlags?.(result);
672
- result.getExecutable()?.();
673
- return;
674
- } catch (err) {
675
- if (err instanceof ParserError) {
676
- if (!err.flags?.has(err.command.uniqueHelpFlag)) console.error(FormatCode.create(31, 39).wrap("->" + err.message));
677
- console.error("\n", Array.from(err.command.getHelp()).join("\r\n"));
678
- } else throw err;
679
- }
680
- }
681
- /**
682
- * Creates a new group command for organizing subcommands.
683
- * @param name - The name of the command group
684
- * @param description - A description of what this command group does
685
- * @returns A new GroupCommand instance
686
- */
687
- static createGroup(name, description$1) {
688
- return GroupCommand.create(name, description$1, null);
689
- }
690
- /**
691
- * Creates a new action command that executes a handler function.
692
- * @template T - The tuple type of arguments the action accepts
693
- * @param name - The name of the command
694
- * @param description - A description of what this command does
695
- * @param args - The argument definitions for this command
696
- * @returns A new CommandAction instance
697
- */
698
- static createAction(name, description$1, args) {
699
- return CommandAction.create(name, description$1, null, args);
700
- }
701
- };
702
-
703
- //#endregion
704
- //#region package.json
705
- var description = "";
706
-
707
- //#endregion
708
- //#region node_modules/.pnpm/con-utils@0.0.2/node_modules/con-utils/dist/general.js
709
- /**
710
- * Manages concurrent execution of tasks with a maximum concurrency limit.
711
- */
712
- var TaskConcurrencyChannel = class {
713
- /**
714
- * Set of promises currently being tracked.
715
- */
716
- stack = /* @__PURE__ */ new Set();
717
- /**
718
- * Maximum number of concurrent tasks allowed.
719
- */
720
- concurrency;
721
- /**
722
- * Create a new TaskConcurrencyChannel.
723
- * @param maxConcurrency The maximum number of tasks to run concurrently.
724
- */
725
- constructor(maxConcurrency) {
726
- this.concurrency = maxConcurrency;
727
- }
728
- /**
729
- * Add a task to the queue. Waits if concurrency limit is reached.
730
- * @param task The task to execute.
731
- */
732
- async push(task) {
733
- const promise = Promise.resolve(task).then((_) => void this.stack.delete(promise), (_) => void 0);
734
- this.stack.add(promise);
735
- if (this.stack.size >= this.concurrency) await Promise.any(this.stack);
736
- }
737
- /**
738
- * Get a promise that resolves when all tasks complete.
739
- * @returns A promise that resolves when the stack is empty.
740
- */
741
- getAwaiter() {
742
- return Promise.all(this.stack).then((_) => void 0);
743
- }
744
- };
745
-
746
- //#endregion
747
- //#region src/logger.ts
748
- let LOG_ENABLED = false;
749
- const SET_VERBOSE = (value) => LOG_ENABLED = value;
750
- const rgb = FormatCode.createForegroundRGB;
751
- const INFO_CODE = rgb(80, 175, 199);
752
- const WARN_CODE = rgb(199, 133, 80);
753
- const E = rgb(199, 80, 96).wrap, I = INFO_CODE.wrap, W = WARN_CODE.wrap;
754
- const ACCENT = FormatCode.create(92, 39).wrap;
755
- const DIM = DIM$1.wrap;
756
- const OUTPUT = (raw) => process.stderr.write(raw);
757
- const LOG = (string) => void (LOG_ENABLED ? OUTPUT(DIM(`| ` + string)) : null);
758
- const INFO = (string) => void OUTPUT(I(`| ` + string));
759
- const WARN = (string) => void OUTPUT(W(`| ` + string));
760
- const ERROR = (string) => void OUTPUT(E(`| ` + string));
761
-
762
- //#endregion
763
- //#region src/constants.ts
764
- const { APPDATA, SAVE_DATA_PATH } = import.meta.env;
765
- if (!(SAVE_DATA_PATH || APPDATA)) {
766
- console.error("Couldn't find path to store the mirrors");
767
- exit(-1);
768
- }
769
- const DEFAULT_APPDATA_FOLDER_NAME = "ConMaster.BedrockArchiver";
770
- const PREFERRED_PATH = resolve(SAVE_DATA_PATH ?? join(APPDATA ?? ".", DEFAULT_APPDATA_FOLDER_NAME, "clients"));
771
- const MIRRORS_FOLDER_PATH = join(PREFERRED_PATH, "mirrors");
772
- const TAGS_FOLDER_PATH = join(PREFERRED_PATH, "tags");
773
-
774
- //#endregion
775
- //#region src/cli/root.ts
776
- const ROOT = CommandLine.createGroup(">.", description);
777
- const VERBOSE = new Flag("verbose", {
778
- description: "Shows LOG messages",
779
- long: "verbose"
780
- });
781
- const TAG = new ValueFlag("tag", {
782
- defaultValue: "current",
783
- description: "Environment Tag under this instance operates, archiving performs tag redirection of this name",
784
- validator: new StringTypeValidator(),
785
- long: "tag"
786
- });
787
- ROOT.flags.add(VERBOSE, TAG);
788
- ROOT.onFlags = (options) => {
789
- if (options.getValue(VERBOSE)) SET_VERBOSE(true);
790
- };
791
- const ENVIRONMENT_INFO = (options) => {
792
- LOG(`-----------------------\n`);
793
- LOG(`DATA: ${ACCENT(PREFERRED_PATH)}\n`);
794
- LOG(`TAG: ${ACCENT(options.getValue(TAG))} ${options.getValue(TAG) === TAG.defaultValue ? "(default)" : ""}\n`);
795
- LOG(`-----------------------\n`);
796
- };
797
-
798
- //#endregion
799
- //#region src/cli/instruction.ts
800
- const INSTRUCTION = ROOT.createAction("instruction", "Internal use", [new ArgumentLike("data", {
801
- defaultValue: null,
802
- validator: new StringTypeValidator(),
803
- description: "BASE64 Encoded data"
804
- })]);
805
- INSTRUCTION.action = async (_, base64) => {
806
- let data;
807
- try {
808
- LOG(`Trying to transform BASE64 to string\n`);
809
- const RAW = atob(base64);
810
- LOG(`Parsing as JSON\n`);
811
- data = JSON.parse(RAW);
812
- LOG(`Checking data validity\n`);
813
- if (!Array.isArray(data)) return void ERROR("INVALID INSTRUCTION FORMAT");
814
- if (typeof data[0] !== "number" || !(data[0] in InstructionType)) return void ERROR("INVALID INSTRUCTION FORMAT");
815
- } catch {
816
- ERROR("INVALID INSTRUCTION FORMAT");
817
- return;
818
- }
819
- switch (data[0]) {
820
- case InstructionType.Copy:
821
- if (!await copyFile(data[1], data[2]).then((_$1) => true, (_$1) => false)) {
822
- setTimeout(() => null, 5e3);
823
- ERROR("FAILED TO COPY THIS ITEM: " + data[1]);
824
- return;
825
- }
826
- break;
827
- default:
828
- ERROR("Unknown instruction");
829
- return;
830
- }
831
- };
832
- let InstructionType = /* @__PURE__ */ function(InstructionType$1) {
833
- InstructionType$1[InstructionType$1["Copy"] = 1] = "Copy";
834
- return InstructionType$1;
835
- }({});
836
- function createCommand(...args) {
837
- const [EXECUTABLE, SELF_CLI] = Bun.argv;
838
- return [
839
- EXECUTABLE,
840
- SELF_CLI,
841
- INSTRUCTION.name,
842
- btoa(JSON.stringify(args))
843
- ];
844
- }
845
-
846
- //#endregion
847
- //#region src/pwsh-helper.ts
848
- var PowershellHelper = class PowershellHelper {
849
- static POWERSHELL_EXECUTABLE = "powershell";
850
- static async GetAppxPackage(packageNameLike) {
851
- LOG(`Requesting Packages\n`);
852
- let { output, exitCode } = await PowershellHelper.runRawCommand(`((Get-AppxPackage -Name ${packageNameLike}) | ForEach-Object { "$($_.PackageFamilyName);$($_.Version);$($_.InstallLocation)" })`);
853
- if (exitCode != 0) return null;
854
- LOG(`Parsing and Resoling Installation Paths\n`);
855
- return output?.trim().split("\n").map((e) => {
856
- const [PackageFamilyName, Version, InstallationPath] = e.trim().split(";");
857
- return {
858
- PackageFamilyName,
859
- Version,
860
- InstallationPath
861
- };
862
- }).filter((e) => e?.PackageFamilyName) ?? null;
863
- }
864
- static async runRawCommand(command) {
865
- LOG(`Spawning PWSH command\n`);
866
- const process$1 = Bun.spawn({
867
- cmd: [
868
- this.POWERSHELL_EXECUTABLE,
869
- "-NoProfile",
870
- "-NonInteractive",
871
- "-Command",
872
- command
873
- ],
874
- stdout: "pipe",
875
- stderr: "pipe",
876
- stdin: "ignore"
877
- });
878
- LOG(`Waiting for exit\n`);
879
- const stdout = await new Response(process$1.stdout).text();
880
- const stderr = await new Response(process$1.stderr).text();
881
- const code = await process$1.exited;
882
- LOG(`PWSH Process exited with code: ${code}\n`);
883
- return {
884
- exitCode: code,
885
- output: code === 0 ? stdout : stderr.length > 0 ? stderr : null
886
- };
887
- }
888
- static async InvokeCommandInDesktopPackage(packageFamilyName, appId, executable, args) {
889
- let cmd = `Invoke-CommandInDesktopPackage -PackageFamilyName ${packageFamilyName} -AppId ${appId} -Command ${JSON.stringify(executable)}`;
890
- if (args.length > 0) cmd += ` -Args '${args.map((_) => `"${_}"`).join(" ")}'`;
891
- await this.runRawCommand(cmd);
892
- }
893
- };
894
-
895
- //#endregion
896
- //#region src/cli/archive.ts
897
- const RECURSIVE_GLOB = new Bun.Glob("**/*");
898
- const ARCHIVE = ROOT.createAction("archive", "Archive current version", [new ArgumentLike("package-name", {
899
- defaultValue: "*minecraftuwp*",
900
- validator: new StringTypeValidator(),
901
- description: "Minecraft GDK Installation Package Name Pattern (*minecraftuwp*)"
902
- })]);
903
- const FORCE_TAG = new Flag("force", {
904
- long: "force",
905
- description: "Forces to overwrite archive folder if expected version is already archived."
906
- });
907
- const CONCURRENCY_TAG = new ValueFlag("concurrency", {
908
- short: "c",
909
- defaultValue: 10,
910
- validator: new NumberTypeValidator(),
911
- long: "concurrency",
912
- description: "Forces to overwrite archive folder if expected version is already archived."
913
- });
914
- ARCHIVE.flags.add(FORCE_TAG, CONCURRENCY_TAG);
915
- ARCHIVE.action = async (_, packageName) => {
916
- const isForced = _.getValue(FORCE_TAG);
917
- INFO(`TARGET: ${ACCENT(packageName)}\n`);
918
- INFO(`----------------------------\n`);
919
- const data = await PowershellHelper.GetAppxPackage(packageName);
920
- LOG(`Found ${data?.length ?? 0} matches. . .\n`);
921
- if (!data || !data.length) return void ERROR("Failed to obtain installation\n");
922
- const packageInfo = data[0];
923
- if (!packageInfo) {
924
- ERROR("FAILED TO RESOLVE PACKAGE\n");
925
- ERROR(`Minecraft Package Not Found: Looking for preview? Run with *minecraftwindowsbeta*\n`);
926
- return;
927
- }
928
- const { InstallationPath, PackageFamilyName, Version } = packageInfo;
929
- if (!PackageFamilyName.toLowerCase().includes("minecraft")) return void ERROR(`Minecraft Package Not Found: Looking for preview? Run with *minecraftwindowsbeta*\n`);
930
- LOG(`RESOLVING REAL PATH FOR: ${JSON.stringify(InstallationPath)}\n`);
931
- LOG(`----------------------------\n`);
932
- let path = await realpath(InstallationPath).catch((_$1) => null);
933
- if (!path) {
934
- ERROR("FAILED TO RESOLVE INSTALLATION PATH\n");
935
- return;
936
- }
937
- INFO(`NAME: ${ACCENT(PackageFamilyName)}\n`);
938
- INFO(`VERSION: ${ACCENT(realVersion(Version))}\n`);
939
- INFO(`PATH: ${ACCENT(toURL(path))}\n`);
940
- const REAL_VERSION = realVersion(Version);
941
- const appId = await getPackageApplicationId(path);
942
- if (!appId) return void ERROR("FAILED TO GET APPLICATION ID");
943
- INFO(`APPID: ${ACCENT(appId)}\n`);
944
- const isPreview = PackageFamilyName.toLowerCase().match(/beta|preview|candidate/)?.[0];
945
- const name = `minecraft${isPreview ? "-" + isPreview : ""}_${REAL_VERSION}`;
946
- ENVIRONMENT_INFO(_);
947
- const ARCHIVE_FOLDER = join(MIRRORS_FOLDER_PATH, name);
948
- let OLD_FILES;
949
- const KNOWN_FOLDERS = /* @__PURE__ */ new Set();
950
- const KNOWN_FILES = /* @__PURE__ */ new Set();
951
- if (existsSync(ARCHIVE_FOLDER)) if (!isForced) return void ERROR("This version is already archived, if you are sure you can use --force tag to rewrite this version.\n");
952
- else OLD_FILES = new Set(Array.from(RECURSIVE_GLOB.scanSync({
953
- cwd: ARCHIVE_FOLDER,
954
- dot: true,
955
- absolute: false,
956
- onlyFiles: true,
957
- followSymlinks: false
958
- })).map((e) => e.toLowerCase()));
959
- else {
960
- LOG(`Creating archive folder for this version, ${name}\n`);
961
- if (!await mkdir(ARCHIVE_FOLDER, { recursive: true }).then((_$1) => true, (_$1) => false)) return void ERROR("Something went wrong when creating folder\n");
962
- OLD_FILES = /* @__PURE__ */ new Set();
963
- }
964
- INFO(`Invoking external process to decrypt the main executable and move it to archive\n`);
965
- const EXE_PATH = "Minecraft.Windows.exe";
966
- const SRC_EXE_PATH = join(path, EXE_PATH);
967
- const FINAL_EXE_PATH = join(ARCHIVE_FOLDER, EXE_PATH);
968
- const [runtime, ...args] = createCommand(InstructionType.Copy, SRC_EXE_PATH, FINAL_EXE_PATH);
969
- await PowershellHelper.InvokeCommandInDesktopPackage(PackageFamilyName, appId, runtime, args);
970
- const startTime = performance.now();
971
- await new Promise((res) => setTimeout(res, 1e3));
972
- LOG(`ORIGINAL STATS\n`);
973
- let { size } = await stat(SRC_EXE_PATH);
974
- while (performance.now() - startTime > 15 * 1e3) {
975
- LOG(`WAITING FOR NEW STATS TO MATCH ORIGINAL\n`);
976
- await new Promise((res) => setTimeout(res, 3e3));
977
- if ((await stat(FINAL_EXE_PATH).catch((_$1) => null))?.size === size) break;
978
- }
979
- INFO(`Successfully copied encrypted file: ${EXE_PATH}\n`);
980
- KNOWN_FILES.add(EXE_PATH.toLowerCase());
981
- const concurrency = _.getValue(CONCURRENCY_TAG);
982
- if (concurrency < 1) WARN(`Concurrency can not be less than 1!!!\n`);
983
- const channel = new TaskConcurrencyChannel(concurrency);
984
- let failedFiles = [];
985
- const FILTER = /(gamelaunchhelper.exe|layout.xml|layout_[^.\\\/]+.xml|minecraft.windows.exe)$/;
986
- let files = 0;
987
- for await (const file of RECURSIVE_GLOB.scan({
988
- cwd: path,
989
- absolute: false,
990
- dot: true,
991
- followSymlinks: false,
992
- onlyFiles: true
993
- })) {
994
- files++;
995
- if (FILTER.test(file.toLowerCase())) continue;
996
- let lio = file.lastIndexOf("\\");
997
- const folder = file.substring(0, lio);
998
- if (lio > 0 && !KNOWN_FOLDERS.has(folder)) {
999
- const ABSOLUTE_FOLDER = join(ARCHIVE_FOLDER, folder);
1000
- INFO(`\x1b[36m${folder}\n`);
1001
- if (!existsSync(ABSOLUTE_FOLDER) && !await mkdir(join(ARCHIVE_FOLDER, folder), { recursive: true }).then((_$1) => true, (_$1) => false)) return void ERROR(`FAILED TO CREATE FOLDER IN ARCHIVE PATHS, MAYBE ITS TOO LONG? ${ARCHIVE_FOLDER.length + folder.length + 1}, ${folder}\n`);
1002
- KNOWN_FOLDERS.add(folder);
1003
- }
1004
- await channel.push((async () => {
1005
- if (!await copyFile(join(path, file), join(ARCHIVE_FOLDER, file)).then((_$1) => true, (_$1) => false)) failedFiles.push(file);
1006
- else KNOWN_FILES.add(file.toLowerCase());
1007
- })());
1008
- }
1009
- await channel.getAwaiter();
1010
- if (failedFiles.length) {
1011
- ERROR(`Something went wrong when coping these specific files: \n${failedFiles.join("\n")}\n`);
1012
- return;
1013
- }
1014
- if (isForced) WARN(`FILES TO REMOVE: ${OLD_FILES.difference(KNOWN_FILES).size}\n`);
1015
- for (const file of OLD_FILES.difference(KNOWN_FILES)) {
1016
- WARN(`Removing ${ACCENT(file)}\n`);
1017
- await channel.push(rm(join(ARCHIVE_FOLDER, file)));
1018
- }
1019
- await channel.getAwaiter();
1020
- INFO(`TOTAL FILES ARCHIVED: ${ACCENT(String(files))}\n`);
1021
- const tags = [_.getValue(TAG).toLowerCase()];
1022
- if (tags[0] !== TAG.defaultValue) tags.push(TAG.defaultValue?.toLowerCase() ?? "current");
1023
- for (let tag of tags) {
1024
- tag = join(TAGS_FOLDER_PATH, tag);
1025
- LOG(`REMOVING OLD TAG: ${tag}\n`);
1026
- await rmdir(tag).catch((_$1) => null);
1027
- LOG(`CREATING NEW TAG: ${_.getValue(TAG).toLowerCase()}\n`);
1028
- await symlink(ARCHIVE_FOLDER, tag, "junction").catch((_$1) => null);
1029
- }
1030
- INFO(`OUTPUT: ${ACCENT(pathToFileURL(join(TAGS_FOLDER_PATH, tags[0])).href)}\n`);
1031
- };
1032
- function realVersion(version) {
1033
- const [MA = 1, MI = 0, B = 0, P = 0] = version.split(".").map(Number);
1034
- return [
1035
- MA,
1036
- MI,
1037
- Math.floor(B / 100),
1038
- P + B % 100
1039
- ].join(".");
1040
- }
1041
- function toURL(path) {
1042
- return `file:///${encodeURI(path.replaceAll("\\", "/"))}`;
1043
- }
1044
- async function getPackageApplicationId(installationPath) {
1045
- LOG(`Trying to read file: /AppxManifest.xml\n`);
1046
- return await readFile(join(installationPath, "AppxManifest.xml")).then((_) => {
1047
- LOG(`Trying to search for application id\n`);
1048
- const id = _.toString().match(/<Application(?:\s+[^>]*)??\s+Id\s*=\s*["']([^"']+)["']/is)?.[1];
1049
- if (!id) {
1050
- LOG(`FAILED TO SEARCH APPLICATION ID`);
1051
- return null;
1052
- }
1053
- return id;
1054
- }).catch((_) => (LOG(`FAILED TO READ FILE OR FAILED TO SEARCH FOR APPLICATION ID`), null));
1055
- }
1056
-
1057
- //#endregion
1058
- //#region src/cli/list.ts
1059
- const LIST = ROOT.createAction("list", "List your installations", []);
1060
- LIST.action = async (_) => {
1061
- ENVIRONMENT_INFO(_);
1062
- INFO(`----------------------------\n`);
1063
- INFO(`MIRRORS:\n`);
1064
- for await (const folder of new Bun.Glob("*").scan({
1065
- cwd: PREFERRED_PATH + "\\mirrors",
1066
- onlyFiles: false
1067
- })) INFO(` ${ACCENT(folder)}\n`);
1068
- INFO(`\n`);
1069
- INFO(`TAGS:\n`);
1070
- for await (const folder of new Bun.Glob("*").scan({
1071
- cwd: PREFERRED_PATH + "\\tags",
1072
- onlyFiles: false
1073
- })) INFO(` ${ACCENT(folder)}\n`);
1074
- INFO(`\n`);
1075
- INFO(`----------------------------\n`);
1076
- };
1077
-
1078
- //#endregion
1079
- //#region src/cli/run.ts
1080
- const RUN = ROOT.createAction("run", "Start new minecraft instance from tag or mirror version id", [new ArgumentLike("package-name", {
1081
- defaultValue: "",
1082
- validator: new StringTypeValidator(),
1083
- description: "Minecraft GDK Installation Mirror Id if empty then tag is used"
1084
- })]);
1085
- RUN.action = async (_, packageName) => {
1086
- ENVIRONMENT_INFO(_);
1087
- Bun.spawn({
1088
- cmd: [TAGS_FOLDER_PATH + "\\" + _.getValue(TAG) + "\\Minecraft.Windows.exe"],
1089
- detached: true,
1090
- stdio: [
1091
- "ignore",
1092
- "ignore",
1093
- "ignore"
1094
- ]
1095
- }).unref();
1096
- };
1097
-
1098
- //#endregion
1099
- //#region src/cli/main.ts
1100
- CommandLine.run(Bun.argv, ROOT);
1101
-
1102
- //#endregion
1
+ import{copyFile as e,mkdir as t,readFile as n,realpath as r,rm as i,rmdir as a,stat as o,symlink as s}from"node:fs/promises";import{join as c,resolve as l}from"node:path";import{exit as u}from"node:process";import{existsSync as d}from"node:fs";import{pathToFileURL as ee}from"node:url";var f=class e{prefixes;suffixes;constructor(e,t){this.prefixes=e,this.suffixes=t}static create(t,n){return new e([t],n===void 0?[]:[n])}static createForegroundRGB(t,n,r){return new e([38,2,t,n,r],[39])}static createBackgroundRGB(t,n,r){return new e([48,2,t,n,r],[49])}get prefix(){return this.compile(this.prefixes)}get suffix(){return this.compile(this.suffixes)}compile(e){return`\x1b[${e.join(`;`)}m`}merge(t){return m([...this.prefixes],...t.prefixes),m([...this.suffixes],...this.suffixes),new e(this.prefixes,this.suffixes)}get wrap(){return e=>`${this.prefix}${e}${this.suffix}`}get format(){return(e,...t)=>this.wrap(e.reduce((e,n,r)=>e+this.wrap(n)+(t[r]??``),``))}};const p=f.create(2,22);f.create(3,23),f.create(4,24),f.create(7,27),f.create(8,28),f.create(9,29),[21,22,23,24,25,27,28,29,39,49,54,55,65].join(`;`);function m(e,...t){for(let n of t)e.includes(n)||e.push(n)}var h=class{static*wrapWords(e,t){let n=e.split(/\s+/),r=[],i=0;for(let e of n){let n=e.length;i+n+r.length>t&&r.length>0&&(yield r.join(` `),r.length=0,i=0),r.push(e),i+=n}r.length>0&&(yield r.join(` `))}},g=class{toString(){return this.name}};const _=new Set([`false`,`true`,`0`,`1`]);var v=class extends g{name=`bool`;description=`Boolean Type (true/false)`;isValid(e){return _.has(e.toLowerCase())}coerce(e){return e=e.toLowerCase(),e===`false`||e===`0`?!1:!!e}},y=class extends g{name=`number`;description=`Floating point number type`;isValid(e){return!isNaN(Number(e))}coerce(e){return Number(e)}},b=class extends g{name=`string`;description=`Raw string type`;isValid(e){return typeof e==`string`}coerce(e){return e}},x=class{name;validator;description;defaultValue;constructor(e,t){this.name=e.toLowerCase(),this.validator=t.validator,this.description=t?.description??void 0,this.defaultValue=t.defaultValue}isRequired(){return this.defaultValue===null}enforce(e){if(e==null){if(this.defaultValue===null)throw Error(`Following argument is required and can't be omitted! `+this.name);return this.defaultValue}if(!this.validator.isValid(e))throw Error(`Following argument has not valid value! `+this.name+` -> `+e);return this.validator.coerce(e)}toString(){let e=`${this.name}:${this.validator.toString()}`;return this.isRequired()?`<${e}>`:`[${e}]`}},S=class extends x{long;short;isValueFlag;constructor(e,t){super(e,t),this.long=t.long?.toLowerCase(),this.short=t.short?.toLowerCase(),this.isValueFlag=!0}isValueBased(){return this.isValueFlag}toString(){return[this.long&&`--${this.long}`,this.short&&`-${this.short}`].filter(Boolean).join(`, `)}},C=class extends S{constructor(e,t){let{description:n,long:r,short:i}=t??{};super(e,{validator:new v,defaultValue:!1,description:n,long:r,short:i}),this.isValueFlag=!1}},w=class{flags=new Set;map=new Map;base=null;constructor(e){this.base=e??null}hasFlag(e){return this.hasOwnFlag(e)||(this.base?.hasFlag(e)??!1)}hasOwnFlag(e){return this.flags.has(e)}add(...e){for(let t of e)this.flags.add(t),this.setGeneralInternal(t.name,t),t.long&&this.setLongInternal(t.long,t),t.short&&this.setShortInternal(t.short,t);return this}addShortAlias(e,t){return this.flags.add(e),this.setGeneralInternal(e.name,e),this.setShortInternal(t,e),this}addLongAlias(e,t){return this.flags.add(e),this.setGeneralInternal(e.name,e),this.setLongInternal(t,e),this}setLongInternal(e,t){this.map.set(`long:${e}`,t)}setShortInternal(e,t){this.map.set(`short:${e}`,t)}setGeneralInternal(e,t){this.map.set(`flag:${e}`,t)}getLong(e){return this.map.get(`long:${e}`)??this.base?.getLong(e)??null}getShort(e){return this.map.get(`short:${e}`)??this.base?.getShort(e)??null}getByName(e){return this.map.get(`flag:${e}`)??this.base?.getByName(e)??null}getAll(){return this.flags}};const T=p.wrap;var E=class{static*buildHelp(e,t){yield T(` Usage of ${e.syntax()}`),yield``,yield T(` `+`-`.repeat(t)),yield*h.wrapWords(e.description??``,t).map(e=>T(` `)+e),yield T(` `+`-`.repeat(t)),yield``,yield*this.buildFlags(e),yield``}static*buildFlags(e){yield T(`Flags:`),yield*e.flags.getAll().values().map(e=>` `+e.toString().padEnd(20)+` `+T(e.description??``))}};const D=p.wrap;var O=class{parent;uniqueHelpFlag;name;flags;description;onFlags;constructor(e,t,n=null){this.name=e.toLowerCase(),this.description=t??``,this.flags=new w(n?.flags??null),this.parent=n,this.uniqueHelpFlag=new C(`help`,{description:`Show's help message`,short:`?`,long:`help`}),this.flags.add(this.uniqueHelpFlag),this.flags.addShortAlias(this.uniqueHelpFlag,`h`)}getParentCommand(){return this.parent??null}*getHelp(){yield*E.buildHelp(this,j)}*getFlags(){yield D(`Flags:`),yield*this.flags.getAll().values().map(e=>` `+e.toString().padEnd(20)+` `+D(e.description??``))}getFullPathName(){return`${this.parent?this.parent.getFullPathName()+` `:``}${this.name}`}},k=class e extends O{argumentsTypes;action=null;constructor(e,t){super(e,t.description,t.parent),this.argumentsTypes=t.argTypes}*getHelp(){yield*super.getHelp(),this.argumentsTypes.length&&(yield D(` Arguments:`),yield*this.argumentsTypes.map(e=>` ${e.toString().padEnd(20)} ${D(e.description??``)}`),yield``)}static create(t,n,r,i){return new e(t,{argTypes:i,description:n,parent:r})}syntax(){return`${this.getFullPathName()} ${this.argumentsTypes.map(e=>e.isRequired()?`<${e.name}:${e.validator.name}>`:`[${e.name}:${e.validator.name}]`).join(` `)}`}},A=class e extends O{action=null;subcommands=new Map;static create(t,n,r){return new e(t,n,r)}createGroup(t,n){let r=e.create(t,n,this);return this.subcommands.set(t,r),r}createAction(e,t,n){let r=k.create(e,t,this,n);return this.subcommands.set(e,r),r}syntax(){return`${this.getFullPathName()} <${this.subcommands.keys().toArray().join(`|`)}>`}*getHelp(){yield*super.getHelp(),yield D(` SubCommands:`);let e=j-25;yield*this.subcommands.values().map(t=>` ${t.name.padEnd(20)} ${D((t.description?.length??0)>e?(t.description?.substring(0,e)??``)+`...`:t.description??``)}`),yield``}};let j=75;var M=class{constructor(e,t,n){this.command=e,this.args=t,this.flags=n}getValue(e){return this.flags.get(e)??e.defaultValue}getExecutable(){return`action`in this.command&&typeof this.command.action==`function`?this.command.action.bind(null,this,...this.args):null}},N=class extends Error{command;argv;index;flags;constructor(e,t,n,r,i){super(t),this.command=e,this.name=new.target.name,this.argv=n,this.index=r,this.flags=i??null}},P=class e{argv;index;lastError=null;constructor(e,t){this.argv=e,this.index=t}parse(e){if(e instanceof A)return this.parseGroupCommand(e);if(e instanceof k)return this.parseActionCommand(e);throw new N(e,`Unsupported command type`,this.argv,this.index)}parseGroupCommand(t){let n=new M(t,[],new Map),r=this.getArgument();if(!r){if(!t.action)throw new N(t,`Expected subcommand name, received none: ${t.getFullPathName()}`,this.argv,this.index);return new M(t,[],new Map)}if(r.startsWith(`-`)){for(;r=this.getArgument();){let i=e.parseFlag(r);if(!i)break;let a=this.resolveFlag(t.flags,i,this.getArgument(1));if(!a)throw this.getLastError()?new N(t,this.getLastError(),this.argv,this.index,new Set(n.flags.keys())):new N(t,`Failed to resolve tag with name: `+i.name,this.argv,this.index,new Set(n.flags.keys()));let{flag:o,nextValueUsed:s,value:c}=a;this.index++,s&&this.index++,n.flags.set(o,c)}if(!r)return n}let i=r.toLowerCase(),a=t.subcommands.get(i);if(!a)throw new N(t,`Unknown subcommand: ${t.getFullPathName()} >>${i}<<`,this.argv,this.index,new Set(n.flags.keys()));this.index++;try{let e=this.parse(a);for(let t of n.flags.keys())e.flags.has(t)||e.flags.set(t,n.flags.get(t));return e}catch(e){if(e instanceof N){let t=e.flags;t?n.flags.keys().forEach(e=>t.add(e)):e.flags=new Set(n.flags.keys())}throw e}}parseActionCommand(t){let n=[],r,i=new Map;for(;r=this.getArgument();){let a=e.parseFlag(r);if(this.index++,!a){n.push(r);continue}let o=this.resolveFlag(t.flags,a,this.getArgument());if(!o){if(this.getLastError())throw new N(t,this.getLastError(),this.argv,this.index,new Set(i.keys()));n.push(r);continue}let{flag:s,nextValueUsed:c,value:l}=o;c&&this.index++,i.set(s,l)}let a=[];for(let e=0;e<t.argumentsTypes.length;e++){let r=t.argumentsTypes[e],o=n[e];if(o===void 0){if(r.isRequired())throw new N(t,`Missing required argument: ${t.getFullPathName()} ... ${r.toString()}`,this.argv,this.index,new Set(i.keys()));a.push(r.defaultValue)}else{if(!r.validator.isValid(o))throw new N(t,`Invalid value: ${o}`,this.argv,this.index,new Set(i.keys()));a.push(r.enforce(o))}}for(let e=t.argumentsTypes.length;e<n.length;e++)a.push(n[e]);return new M(t,a,i)}resolveFlag(e,t,n){let r=t.isLong?e.getLong(t.name):e.getShort(t.name);if(!r)return this.setLastError(null);let i=!1;if(r.isValueBased()){let{value:e}=t;return e===null&&(i=!0,e=n),e===null?r.defaultValue?{flag:r,value:null,nextValueUsed:i}:this.setLastError(`Flag with no default value, requires value to be set explicitly, flag: `+t.name):r.validator.isValid(e)?{flag:r,value:r.validator.coerce(e),nextValueUsed:i}:this.setLastError(`Incorrect type passed as value for flag: `+t.name+` value: `+e)}else if(t.value)return this.setLastError(`Syntax error, this flag doesn't supports values: `+t.name+` value: `+t.value);return{flag:r,value:!0,nextValueUsed:i}}getLastError(){return this.lastError}setLastError(e){return this.lastError=e,null}getArgument(e=0){return this.argv[this.index+(e??0)]??null}static parseFlag(e){if(e.length<2||e[0]!==`-`)return null;let t=e[1]===`-`,n=e.indexOf(`=`),r=n!==-1,i,a=r?e.substring(n+1):null;return t?i=e.substring(2,r?n:void 0):(i=e[1],!a&&e.length>2&&(a=e.substring(2))),{name:i.toLowerCase(),isLong:t,value:a}}},F=class{constructor(){}static async run(e,t){let n=new P(e,2);try{let e=n.parse(t);if(e.flags.has(e.command.uniqueHelpFlag))return void console.info(Array.from(e.command.getHelp()).join(`\r
2
+ `));for(let t of e.flags.keys())for(let n=e.command;n;n=n.getParentCommand())n.flags.hasOwnFlag(t)&&n.onFlags?.(e);e.getExecutable()?.();return}catch(e){if(e instanceof N)e.flags?.has(e.command.uniqueHelpFlag)||console.error(f.create(31,39).wrap(`->`+e.message)),console.error(`
3
+ `,Array.from(e.command.getHelp()).join(`\r
4
+ `));else throw e}}static createGroup(e,t){return A.create(e,t,null)}static createAction(e,t,n){return k.create(e,t,null,n)}},te=``,ne=class{stack=new Set;concurrency;constructor(e){this.concurrency=e}async push(e){let t=Promise.resolve(e).then(e=>void this.stack.delete(t),e=>void 0);this.stack.add(t),this.stack.size>=this.concurrency&&await Promise.any(this.stack)}getAwaiter(){return Promise.all(this.stack).then(e=>void 0)}};let I=!1;const L=e=>I=e,R=f.createForegroundRGB,re=R(80,175,199),ie=R(199,133,80),ae=R(199,80,96).wrap,oe=re.wrap,se=ie.wrap,z=f.create(92,39).wrap,ce=p.wrap,B=e=>process.stderr.write(e),V=e=>void(I?B(ce(`| `+e)):null),H=e=>void B(oe(`| `+e)),U=e=>void B(se(`| `+e)),W=e=>void B(ae(`| `+e)),{APPDATA:G,SAVE_DATA_PATH:K}=import.meta.env;K||G||(console.error(`Couldn't find path to store the mirrors`),u(-1));const q=l(K??c(G??`.`,`ConMaster.BedrockArchiver`,`clients`)),le=c(q,`mirrors`),J=c(q,`tags`),Y=F.createGroup(`>.`,te),ue=new C(`verbose`,{description:`Shows LOG messages`,long:`verbose`}),X=new S(`tag`,{defaultValue:`current`,description:`Environment Tag under this instance operates, archiving performs tag redirection of this name`,validator:new b,long:`tag`});Y.flags.add(ue,X),Y.onFlags=e=>{e.getValue(ue)&&L(!0)};const Z=e=>{V(`-----------------------
5
+ `),V(`DATA: ${z(q)}\n`),V(`TAG: ${z(e.getValue(X))} ${e.getValue(X)===X.defaultValue?`(default)`:``}\n`),V(`-----------------------
6
+ `)},de=Y.createAction(`instruction`,`Internal use`,[new x(`data`,{defaultValue:null,validator:new b,description:`BASE64 Encoded data`})]);de.action=async(t,n)=>{let r;try{V(`Trying to transform BASE64 to string
7
+ `);let e=atob(n);if(V(`Parsing as JSON
8
+ `),r=JSON.parse(e),V(`Checking data validity
9
+ `),!Array.isArray(r)||typeof r[0]!=`number`||!(r[0]in Q))return void W(`INVALID INSTRUCTION FORMAT`)}catch{W(`INVALID INSTRUCTION FORMAT`);return}switch(r[0]){case Q.Copy:if(!await e(r[1],r[2]).then(e=>!0,e=>!1)){setTimeout(()=>null,5e3),W(`FAILED TO COPY THIS ITEM: `+r[1]);return}break;default:W(`Unknown instruction`);return}};let Q=function(e){return e[e.Copy=1]=`Copy`,e}({});function fe(...e){let[t,n]=Bun.argv;return[t,n,de.name,btoa(JSON.stringify(e))]}var $=class e{static POWERSHELL_EXECUTABLE=`powershell`;static async GetAppxPackage(t){V(`Requesting Packages
10
+ `);let{output:n,exitCode:r}=await e.runRawCommand(`((Get-AppxPackage -Name ${t}) | ForEach-Object { "$($_.PackageFamilyName);$($_.Version);$($_.InstallLocation)" })`);return r==0?(V(`Parsing and Resoling Installation Paths
11
+ `),n?.trim().split(`
12
+ `).map(e=>{let[t,n,r]=e.trim().split(`;`);return{PackageFamilyName:t,Version:n,InstallationPath:r}}).filter(e=>e?.PackageFamilyName)??null):null}static async runRawCommand(e){V(`Spawning PWSH command
13
+ `);let t=Bun.spawn({cmd:[this.POWERSHELL_EXECUTABLE,`-NoProfile`,`-NonInteractive`,`-Command`,e],stdout:`pipe`,stderr:`pipe`,stdin:`ignore`});V(`Waiting for exit
14
+ `);let n=await new Response(t.stdout).text(),r=await new Response(t.stderr).text(),i=await t.exited;return V(`PWSH Process exited with code: ${i}\n`),{exitCode:i,output:i===0?n:r.length>0?r:null}}static async InvokeCommandInDesktopPackage(e,t,n,r){let i=`Invoke-CommandInDesktopPackage -PackageFamilyName ${e} -AppId ${t} -Command ${JSON.stringify(n)}`;r.length>0&&(i+=` -Args '${r.map(e=>`"${e}"`).join(` `)}'`),await this.runRawCommand(i)}};const pe=new Bun.Glob(`**/*`),me=Y.createAction(`archive`,`Archive current version`,[new x(`package-name`,{defaultValue:`*minecraftuwp*`,validator:new b,description:`Minecraft GDK Installation Package Name Pattern (*minecraftuwp*)`})]),he=new C(`force`,{long:`force`,description:`Forces to overwrite archive folder if expected version is already archived.`}),ge=new S(`concurrency`,{short:`c`,defaultValue:10,validator:new y,long:`concurrency`,description:`Forces to overwrite archive folder if expected version is already archived.`});me.flags.add(he,ge),me.action=async(n,l)=>{let u=n.getValue(he);H(`TARGET: ${z(l)}\n`),H(`----------------------------
15
+ `);let f=await $.GetAppxPackage(l);if(V(`Found ${f?.length??0} matches. . .\n`),!f||!f.length)return void W(`Failed to obtain installation
16
+ `);let p=f[0];if(!p){W(`FAILED TO RESOLVE PACKAGE
17
+ `),W(`Minecraft Package Not Found: Looking for preview? Run with *minecraftwindowsbeta*
18
+ `);return}let{InstallationPath:m,PackageFamilyName:h,Version:g}=p;if(!h.toLowerCase().includes(`minecraft`))return void W(`Minecraft Package Not Found: Looking for preview? Run with *minecraftwindowsbeta*
19
+ `);V(`RESOLVING REAL PATH FOR: ${JSON.stringify(m)}\n`),V(`----------------------------
20
+ `);let _=await r(m).catch(e=>null);if(!_){W(`FAILED TO RESOLVE INSTALLATION PATH
21
+ `);return}H(`NAME: ${z(h)}\n`),H(`VERSION: ${z(_e(g))}\n`),H(`PATH: ${z(ve(_))}\n`);let v=_e(g),y=await ye(_);if(!y)return void W(`FAILED TO GET APPLICATION ID`);H(`APPID: ${z(y)}\n`);let b=h.toLowerCase().match(/beta|preview|candidate/)?.[0],x=`minecraft${b?`-`+b:``}_${v}`;Z(n);let S=c(le,x),C,w=new Set,T=new Set;if(d(S))if(u)C=new Set(Array.from(pe.scanSync({cwd:S,dot:!0,absolute:!1,onlyFiles:!0,followSymlinks:!1})).map(e=>e.toLowerCase()));else return void W(`This version is already archived, if you are sure you can use --force tag to rewrite this version.
22
+ `);else{if(V(`Creating archive folder for this version, ${x}\n`),!await t(S,{recursive:!0}).then(e=>!0,e=>!1))return void W(`Something went wrong when creating folder
23
+ `);C=new Set}H(`Invoking external process to decrypt the main executable and move it to archive
24
+ `);let E=`Minecraft.Windows.exe`,D=c(_,E),O=c(S,E),[k,...A]=fe(Q.Copy,D,O);await $.InvokeCommandInDesktopPackage(h,y,k,A);let j=performance.now();await new Promise(e=>setTimeout(e,1e3)),V(`ORIGINAL STATS
25
+ `);let{size:M}=await o(D);for(;performance.now()-j>15*1e3&&(V(`WAITING FOR NEW STATS TO MATCH ORIGINAL
26
+ `),await new Promise(e=>setTimeout(e,3e3)),(await o(O).catch(e=>null))?.size!==M););H(`Successfully copied encrypted file: ${E}\n`),T.add(`minecraft.windows.exe`);let N=n.getValue(ge);N<1&&U(`Concurrency can not be less than 1!!!
27
+ `);let P=new ne(N),F=[],te=/(gamelaunchhelper.exe|layout.xml|layout_[^.\\\/]+.xml|minecraft.windows.exe)$/,I=0;for await(let n of pe.scan({cwd:_,absolute:!1,dot:!0,followSymlinks:!1,onlyFiles:!0})){if(I++,te.test(n.toLowerCase()))continue;let r=n.lastIndexOf(`\\`),i=n.substring(0,r);if(r>0&&!w.has(i)){let e=c(S,i);if(H(`\x1b[36m${i}\n`),!d(e)&&!await t(c(S,i),{recursive:!0}).then(e=>!0,e=>!1))return void W(`FAILED TO CREATE FOLDER IN ARCHIVE PATHS, MAYBE ITS TOO LONG? ${S.length+i.length+1}, ${i}\n`);w.add(i)}await P.push((async()=>{await e(c(_,n),c(S,n)).then(e=>!0,e=>!1)?T.add(n.toLowerCase()):F.push(n)})())}if(await P.getAwaiter(),F.length){W(`Something went wrong when coping these specific files: \n${F.join(`
28
+ `)}\n`);return}u&&U(`FILES TO REMOVE: ${C.difference(T).size}\n`);for(let e of C.difference(T))U(`Removing ${z(e)}\n`),await P.push(i(c(S,e)));await P.getAwaiter(),H(`TOTAL FILES ARCHIVED: ${z(String(I))}\n`);let L=[n.getValue(X).toLowerCase()];L[0]!==X.defaultValue&&L.push(X.defaultValue?.toLowerCase()??`current`);for(let e of L)e=c(J,e),V(`REMOVING OLD TAG: ${e}\n`),await a(e).catch(e=>null),V(`CREATING NEW TAG: ${n.getValue(X).toLowerCase()}\n`),await s(S,e,`junction`).catch(e=>null);H(`OUTPUT: ${z(ee(c(J,L[0])).href)}\n`)};function _e(e){let[t=1,n=0,r=0,i=0]=e.split(`.`).map(Number);return[t,n,Math.floor(r/100),i+r%100].join(`.`)}function ve(e){return`file:///${encodeURI(e.replaceAll(`\\`,`/`))}`}async function ye(e){return V(`Trying to read file: /AppxManifest.xml
29
+ `),await n(c(e,`AppxManifest.xml`)).then(e=>(V(`Trying to search for application id
30
+ `),e.toString().match(/<Application(?:\s+[^>]*)??\s+Id\s*=\s*["']([^"']+)["']/is)?.[1]||(V(`FAILED TO SEARCH APPLICATION ID`),null))).catch(e=>(V(`FAILED TO READ FILE OR FAILED TO SEARCH FOR APPLICATION ID`),null))}const be=Y.createAction(`list`,`List your installations`,[]);be.action=async e=>{Z(e),H(`----------------------------
31
+ `),H(`MIRRORS:
32
+ `);for await(let e of new Bun.Glob(`*`).scan({cwd:q+`\\mirrors`,onlyFiles:!1}))H(` ${z(e)}\n`);H(`
33
+ `),H(`TAGS:
34
+ `);for await(let e of new Bun.Glob(`*`).scan({cwd:q+`\\tags`,onlyFiles:!1}))H(` ${z(e)}\n`);H(`
35
+ `),H(`----------------------------
36
+ `)};const xe=Y.createAction(`run`,`Start new minecraft instance from tag or mirror version id`,[new x(`package-name`,{defaultValue:``,validator:new b,description:`Minecraft GDK Installation Mirror Id if empty then tag is used`})]);xe.action=async(e,t)=>{Z(e),Bun.spawn({cmd:[J+`\\`+e.getValue(X)+`\\Minecraft.Windows.exe`],detached:!0,stdio:[`ignore`,`ignore`,`ignore`]}).unref()},F.run(Bun.argv,Y);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gdk-version-archiver",
3
- "version": "1.0.0-alpha",
3
+ "version": "1.0.0-alpha.2",
4
4
  "description": "",
5
5
  "keywords": [],
6
6
  "license": "MIT",
@@ -35,4 +35,4 @@
35
35
  "node": "please-use-bun"
36
36
  },
37
37
  "packageManager": "pnpm@10.25.0"
38
- }
38
+ }