@decaf-ts/utils 0.4.1 → 0.4.3

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.
@@ -1,4163 +1 @@
1
- import prompts from 'prompts';
2
- import { parseArgs } from 'util';
3
- import { Logging, patchString, LoggedClass, LoggedEnvironment, LogLevel } from '@decaf-ts/logging';
4
- import fs from 'fs';
5
- import path from 'path';
6
- import { spawn } from 'child_process';
7
- import { style } from 'styled-string-builder';
8
- import https from 'https';
9
- import { rollup } from 'rollup';
10
- import typescript from '@rollup/plugin-typescript';
11
- import commonjs from '@rollup/plugin-commonjs';
12
- import { nodeResolve } from '@rollup/plugin-node-resolve';
13
- import json from '@rollup/plugin-json';
14
- import { builtinModules } from 'module';
15
- import * as ts from 'typescript';
16
- import { ModuleKind } from 'typescript';
17
-
18
- /**
19
- * @description Represents a user input prompt with various configuration options.
20
- * @summary This class provides a flexible interface for creating and managing user input prompts.
21
- * It implements the PromptObject interface from the 'prompts' library and offers methods to set
22
- * various properties of the prompt. The class also includes static methods for common input scenarios
23
- * and argument parsing.
24
- *
25
- * @template R - The type of the prompt name, extending string.
26
- *
27
- * @param name - The name of the prompt, used as the key in the returned answers object.
28
- *
29
- * @class
30
- * @example
31
- * ```typescript
32
- * import { UserInput } from '@decaf-ts/utils';
33
- *
34
- * // Create a simple text input
35
- * const nameInput = new UserInput('name')
36
- * .setMessage('What is your name?')
37
- * .setInitial('User');
38
- *
39
- * // Create a number input with validation
40
- * const ageInput = new UserInput('age')
41
- * .setType('number')
42
- * .setMessage('How old are you?')
43
- * .setMin(0)
44
- * .setMax(120);
45
- *
46
- * // Ask for input and process the results
47
- * async function getUserInfo() {
48
- * const answers = await UserInput.ask([nameInput, ageInput]);
49
- * console.log(`Hello ${answers.name}, you are ${answers.age} years old.`);
50
- * }
51
- *
52
- * getUserInfo();
53
- * ```
54
- *
55
- * @mermaid
56
- * sequenceDiagram
57
- * participant Client
58
- * participant UserInput
59
- * participant PromptLibrary
60
- *
61
- * Client->>UserInput: new UserInput(name)
62
- * Client->>UserInput: setMessage(message)
63
- * Client->>UserInput: setType(type)
64
- * Client->>UserInput: setInitial(initial)
65
- * Client->>UserInput: Other configuration methods
66
- *
67
- * Client->>UserInput: ask()
68
- * UserInput->>PromptLibrary: prompts(question)
69
- * PromptLibrary->>Client: Display prompt
70
- * Client->>PromptLibrary: User provides input
71
- * PromptLibrary->>UserInput: Return answers
72
- * UserInput->>Client: Return processed answers
73
- */
74
- class UserInput {
75
- static { this.logger = Logging.for(UserInput); }
76
- constructor(name) {
77
- /**
78
- * @description The type of the prompt.
79
- * @summary Determines the input method (e.g., text, number, confirm).
80
- */
81
- this.type = "text";
82
- this.name = name;
83
- }
84
- /**
85
- * @description Sets the type of the prompt.
86
- * @summary Configures the input method for the prompt.
87
- *
88
- * @param type - The type of the prompt.
89
- * @returns This UserInput instance for method chaining.
90
- */
91
- setType(type) {
92
- UserInput.logger.verbose(`Setting type to: ${type}`);
93
- this.type = type;
94
- return this;
95
- }
96
- /**
97
- * @description Sets the message of the prompt.
98
- * @summary Configures the question or instruction presented to the user.
99
- *
100
- * @param value - The message to be displayed.
101
- * @returns This UserInput instance for method chaining.
102
- */
103
- setMessage(value) {
104
- UserInput.logger.verbose(`Setting message to: ${value}`);
105
- this.message = value;
106
- return this;
107
- }
108
- /**
109
- * @description Sets the initial value of the prompt.
110
- * @summary Configures the default value presented to the user.
111
- *
112
- * @param value - The initial value.
113
- * @returns This UserInput instance for method chaining.
114
- */
115
- setInitial(value) {
116
- UserInput.logger.verbose(`Setting initial value to: ${value}`);
117
- this.initial = value;
118
- return this;
119
- }
120
- /**
121
- * @description Sets the style of the prompt.
122
- * @summary Configures the visual style of the prompt.
123
- *
124
- * @param value - The style to be applied.
125
- * @returns This UserInput instance for method chaining.
126
- */
127
- setStyle(value) {
128
- UserInput.logger.verbose(`Setting style to: ${value}`);
129
- this.style = value;
130
- return this;
131
- }
132
- /**
133
- * @description Sets the format function of the prompt.
134
- * @summary Configures a function to format the user's input before it's returned.
135
- *
136
- * @param value - The format function.
137
- * @returns This UserInput instance for method chaining.
138
- */
139
- setFormat(value) {
140
- UserInput.logger.verbose(`Setting format function`);
141
- this.format = value;
142
- return this;
143
- }
144
- /**
145
- * @description Sets the validation function of the prompt.
146
- * @summary Configures a function to validate the user's input.
147
- *
148
- * @param value - The validation function.
149
- * @returns This UserInput instance for method chaining.
150
- */
151
- setValidate(value) {
152
- UserInput.logger.verbose(`Setting validate function`);
153
- this.validate = value;
154
- return this;
155
- }
156
- /**
157
- * @description Sets the onState callback of the prompt.
158
- * @summary Configures a function to be called when the state of the prompt changes.
159
- *
160
- * @param value - The onState callback function.
161
- * @returns This UserInput instance for method chaining.
162
- */
163
- setOnState(value) {
164
- UserInput.logger.verbose(`Setting onState callback`);
165
- this.onState = value;
166
- return this;
167
- }
168
- /**
169
- * @description Sets the minimum value for number inputs.
170
- * @summary Configures the lowest number the user can input.
171
- *
172
- * @param value - The minimum value.
173
- * @returns This UserInput instance for method chaining.
174
- */
175
- setMin(value) {
176
- UserInput.logger.verbose(`Setting min value to: ${value}`);
177
- this.min = value;
178
- return this;
179
- }
180
- /**
181
- * @description Sets the maximum value for number inputs.
182
- * @summary Configures the highest number the user can input.
183
- *
184
- * @param value - The maximum value.
185
- * @returns This UserInput instance for method chaining.
186
- */
187
- setMax(value) {
188
- UserInput.logger.verbose(`Setting max value to: ${value}`);
189
- this.max = value;
190
- return this;
191
- }
192
- /**
193
- * @description Sets whether to allow float values for number inputs.
194
- * @summary Configures whether decimal numbers are allowed.
195
- *
196
- * @param value - Whether to allow float values.
197
- * @returns This UserInput instance for method chaining.
198
- */
199
- setFloat(value) {
200
- UserInput.logger.verbose(`Setting float to: ${value}`);
201
- this.float = value;
202
- return this;
203
- }
204
- /**
205
- * @description Sets the number of decimal places to round to for float inputs.
206
- * @summary Configures the precision of float inputs.
207
- *
208
- * @param value - The number of decimal places.
209
- * @returns This UserInput instance for method chaining.
210
- */
211
- setRound(value) {
212
- UserInput.logger.verbose(`Setting round to: ${value}`);
213
- this.round = value;
214
- return this;
215
- }
216
- /**
217
- * @description Sets the instructions for the user.
218
- * @summary Configures additional guidance provided to the user.
219
- *
220
- * @param value - The instructions.
221
- * @returns This UserInput instance for method chaining.
222
- */
223
- setInstructions(value) {
224
- UserInput.logger.verbose(`Setting instructions to: ${value}`);
225
- this.instructions = value;
226
- return this;
227
- }
228
- /**
229
- * @description Sets the increment value for number inputs.
230
- * @summary Configures the step size when increasing or decreasing the number.
231
- *
232
- * @param value - The increment value.
233
- * @returns This UserInput instance for method chaining.
234
- */
235
- setIncrement(value) {
236
- UserInput.logger.verbose(`Setting increment to: ${value}`);
237
- this.increment = value;
238
- return this;
239
- }
240
- /**
241
- * @description Sets the separator for list inputs.
242
- * @summary Configures the character used to separate list items.
243
- *
244
- * @param value - The separator character.
245
- * @returns This UserInput instance for method chaining.
246
- */
247
- setSeparator(value) {
248
- UserInput.logger.verbose(`Setting separator to: ${value}`);
249
- this.separator = value;
250
- return this;
251
- }
252
- /**
253
- * @description Sets the active option style for select inputs.
254
- * @summary Configures the style applied to the currently selected option.
255
- *
256
- * @param value - The active option style.
257
- * @returns This UserInput instance for method chaining.
258
- */
259
- setActive(value) {
260
- UserInput.logger.verbose(`Setting active style to: ${value}`);
261
- this.active = value;
262
- return this;
263
- }
264
- /**
265
- * @description Sets the inactive option style for select inputs.
266
- * @summary Configures the style applied to non-selected options.
267
- *
268
- * @param value - The inactive option style.
269
- * @returns This UserInput instance for method chaining.
270
- */
271
- setInactive(value) {
272
- UserInput.logger.verbose(`Setting inactive style to: ${value}`);
273
- this.inactive = value;
274
- return this;
275
- }
276
- /**
277
- * @description Sets the choices for select, multiselect, or autocomplete inputs.
278
- * @summary Configures the available options that the user can select from in choice-based prompts.
279
- *
280
- * @param value - The array of choices or a function to determine the choices.
281
- * @returns This UserInput instance for method chaining.
282
- */
283
- setChoices(value) {
284
- UserInput.logger.verbose(`Setting choices: ${JSON.stringify(value)}`);
285
- this.choices = value;
286
- return this;
287
- }
288
- /**
289
- * @description Sets the hint text for the prompt.
290
- * @summary Configures additional information displayed to the user.
291
- *
292
- * @param value - The hint text.
293
- * @returns This UserInput instance for method chaining.
294
- */
295
- setHint(value) {
296
- UserInput.logger.verbose(`Setting hint to: ${value}`);
297
- this.hint = value;
298
- return this;
299
- }
300
- /**
301
- * @description Sets the warning text for the prompt.
302
- * @summary Configures a warning message displayed to the user.
303
- *
304
- * @param value - The warning text.
305
- * @returns This UserInput instance for method chaining.
306
- */
307
- setWarn(value) {
308
- UserInput.logger.verbose(`Setting warn to: ${value}`);
309
- this.warn = value;
310
- return this;
311
- }
312
- /**
313
- * @description Sets the suggestion function for autocomplete inputs.
314
- * @summary Configures a function that provides suggestions based on the user's input and available choices.
315
- *
316
- * @param value - A function that takes the current input and available choices and returns a Promise resolving to suggestions.
317
- * @returns This UserInput instance for method chaining.
318
- */
319
- setSuggest(value) {
320
- UserInput.logger.verbose(`Setting suggest function`);
321
- this.suggest = value;
322
- return this;
323
- }
324
- /**
325
- * @description Sets the limit for list inputs.
326
- * @summary Configures the maximum number of items that can be selected in list-type prompts.
327
- * @template R - The type of the prompt name, extending string.
328
- * @param value - The maximum number of items that can be selected, or a function to determine this value.
329
- * @return This UserInput instance for method chaining.
330
- */
331
- setLimit(value) {
332
- UserInput.logger.verbose(`Setting limit to: ${value}`);
333
- this.limit = value;
334
- return this;
335
- }
336
- /**
337
- * @description Sets the mask for password inputs.
338
- * @summary Configures the character used to hide the user's input in password-type prompts.
339
- * @template R - The type of the prompt name, extending string.
340
- * @param value - The character used to mask the input, or a function to determine this value.
341
- * @return This UserInput instance for method chaining.
342
- */
343
- setMask(value) {
344
- UserInput.logger.verbose(`Setting mask to: ${value}`);
345
- this.mask = value;
346
- return this;
347
- }
348
- /**
349
- * @description Sets the stdout stream for the prompt.
350
- * @summary Configures the output stream used by the prompt for displaying messages and results.
351
- * @param value - The Writable stream to be used as stdout.
352
- * @return This UserInput instance for method chaining.
353
- */
354
- setStdout(value) {
355
- UserInput.logger.verbose(`Setting stdout stream`);
356
- this.stdout = value;
357
- return this;
358
- }
359
- /**
360
- * @description Sets the stdin stream for the prompt.
361
- * @summary Configures the input stream used by the prompt for receiving user input.
362
- * @param value - The Readable stream to be used as stdin.
363
- * @return This UserInput instance for method chaining.
364
- */
365
- setStdin(value) {
366
- this.stdin = value;
367
- return this;
368
- }
369
- /**
370
- * @description Asks the user for input based on the current UserInput configuration.
371
- * @summary Prompts the user and returns their response as a single value.
372
- * @template R - The type of the prompt name, extending string.
373
- * @return A Promise that resolves to the user's answer.
374
- */
375
- async ask() {
376
- return (await UserInput.ask(this))[this.name];
377
- }
378
- /**
379
- * @description Asks the user one or more questions based on the provided UserInput configurations.
380
- * @summary Prompts the user with one or more questions and returns their answers as an object.
381
- * @template R - The type of the prompt name, extending string.
382
- * @param question - A single UserInput instance or an array of UserInput instances.
383
- * @return A Promise that resolves to an object containing the user's answers.
384
- * @mermaid
385
- * sequenceDiagram
386
- * participant U as User
387
- * participant A as ask method
388
- * participant P as prompts library
389
- * A->>P: Call prompts with question(s)
390
- * P->>U: Display prompt(s)
391
- * U->>P: Provide input
392
- * P->>A: Return answers
393
- * A->>A: Process answers
394
- * A-->>Caller: Return processed answers
395
- */
396
- static async ask(question) {
397
- const log = UserInput.logger.for(this.ask);
398
- if (!Array.isArray(question)) {
399
- question = [question];
400
- }
401
- let answers;
402
- try {
403
- log.verbose(`Asking questions: ${question.map((q) => q.name).join(", ")}`);
404
- answers = await prompts(question);
405
- log.verbose(`Received answers: ${JSON.stringify(answers, null, 2)}`);
406
- }
407
- catch (error) {
408
- throw new Error(`Error while getting input: ${error}`);
409
- }
410
- return answers;
411
- }
412
- /**
413
- * @description Asks the user for a number input.
414
- * @summary Prompts the user to enter a number, with optional minimum, maximum, and initial values.
415
- * @param name - The name of the prompt, used as the key in the returned answers object.
416
- * @param question - The message displayed to the user.
417
- * @param min - The minimum allowed value (optional).
418
- * @param max - The maximum allowed value (optional).
419
- * @param initial - The initial value presented to the user (optional).
420
- * @return A Promise that resolves to the number entered by the user.
421
- */
422
- static async askNumber(name, question, min, max, initial) {
423
- const log = UserInput.logger.for(this.askNumber);
424
- log.verbose(`Asking number input: undefined, question: ${question}, min: ${min}, max: ${max}, initial: ${initial}`);
425
- const userInput = new UserInput(name)
426
- .setMessage(question)
427
- .setType("number");
428
- if (typeof min === "number")
429
- userInput.setMin(min);
430
- if (typeof max === "number")
431
- userInput.setMax(max);
432
- if (typeof initial === "number")
433
- userInput.setInitial(initial);
434
- return (await this.ask(userInput))[name];
435
- }
436
- /**
437
- * @description Asks the user for a text input.
438
- * @summary Prompts the user to enter text, with optional masking and initial value.
439
- * @param name - The name of the prompt, used as the key in the returned answers object.
440
- * @param question - The message displayed to the user.
441
- * @param mask - The character used to mask the input (optional, for password-like inputs).
442
- * @param initial - The initial value presented to the user (optional).
443
- * @return A Promise that resolves to the text entered by the user.
444
- */
445
- static async askText(name, question, mask = undefined, initial) {
446
- const log = UserInput.logger.for(this.askText);
447
- log.verbose(`Asking text input: undefined, question: ${question}, mask: ${mask}, initial: ${initial}`);
448
- const userInput = new UserInput(name).setMessage(question);
449
- if (mask)
450
- userInput.setMask(mask);
451
- if (typeof initial === "string")
452
- userInput.setInitial(initial);
453
- return (await this.ask(userInput))[name];
454
- }
455
- /**
456
- * @description Asks the user for a confirmation (yes/no).
457
- * @summary Prompts the user with a yes/no question and returns a boolean result.
458
- * @param name - The name of the prompt, used as the key in the returned answers object.
459
- * @param question - The message displayed to the user.
460
- * @param initial - The initial value presented to the user (optional).
461
- * @return A Promise that resolves to a boolean representing the user's answer.
462
- */
463
- static async askConfirmation(name, question, initial) {
464
- const log = UserInput.logger.for(this.askConfirmation);
465
- log.verbose(`Asking confirmation input: undefined, question: ${question}, initial: ${initial}`);
466
- const userInput = new UserInput(name)
467
- .setMessage(question)
468
- .setType("confirm");
469
- if (typeof initial !== "undefined")
470
- userInput.setInitial(initial);
471
- return (await this.ask(userInput))[name];
472
- }
473
- /**
474
- * @description Repeatedly asks for input until a valid response is given or the limit is reached.
475
- * @summary This method insists on getting a valid input from the user, allowing for a specified number of attempts.
476
- *
477
- * @template R - The type of the expected result.
478
- * @param input - The UserInput instance to use for prompting.
479
- * @param {function(string):boolean} test - Validator function receiving the user input and returning whether it is valid.
480
- * @param defaultConfirmation - The default value for the confirmation prompt (true for yes, false for no).
481
- * @param limit - The maximum number of attempts allowed (default is 1).
482
- * @return A Promise that resolves to the valid input or undefined if the limit is reached.
483
- *
484
- * @mermaid
485
- * sequenceDiagram
486
- * participant U as User
487
- * participant I as insist method
488
- * participant A as ask method
489
- * participant T as test function
490
- * participant C as askConfirmation method
491
- * loop Until valid input or limit reached
492
- * I->>A: Call ask with input
493
- * A->>U: Prompt user
494
- * U->>A: Provide input
495
- * A->>I: Return result
496
- * I->>T: Test result
497
- * alt Test passes
498
- * I->>C: Ask for confirmation
499
- * C->>U: Confirm input
500
- * U->>C: Provide confirmation
501
- * C->>I: Return confirmation
502
- * alt Confirmed
503
- * I-->>Caller: Return valid result
504
- * else Not confirmed
505
- * I->>I: Continue loop
506
- * end
507
- * else Test fails
508
- * I->>I: Continue loop
509
- * end
510
- * end
511
- * I-->>Caller: Return undefined if limit reached
512
- */
513
- static async insist(input, test, defaultConfirmation, limit = 1) {
514
- const log = UserInput.logger.for(this.insist);
515
- log.verbose(`Insisting on input: ${input.name}, test: ${test.toString()}, defaultConfirmation: ${defaultConfirmation}, limit: ${limit}`);
516
- let result = undefined;
517
- let count = 0;
518
- let confirmation;
519
- try {
520
- do {
521
- result = (await UserInput.ask(input))[input.name];
522
- if (!test(result)) {
523
- result = undefined;
524
- continue;
525
- }
526
- confirmation = await UserInput.askConfirmation(`${input.name}-confirm`, `Is the ${input.type} correct?`, defaultConfirmation);
527
- if (!confirmation)
528
- result = undefined;
529
- } while (typeof result === "undefined" && limit > 1 && count++ < limit);
530
- }
531
- catch (e) {
532
- log.error(`Error while insisting: ${e}`);
533
- throw e;
534
- }
535
- if (typeof result === "undefined")
536
- log.info("no selection...");
537
- return result;
538
- }
539
- /**
540
- * @description Repeatedly asks for text input until a valid response is given or the limit is reached.
541
- * @summary This method insists on getting a valid text input from the user, allowing for a specified number of attempts.
542
- *
543
- * @param name - The name of the prompt, used as the key in the returned answers object.
544
- * @param question - The message displayed to the user.
545
- * @param {function(number):boolean} test - Validator function receiving the user input and returning whether it is valid.
546
- * @param mask - The character used to mask the input (optional, for password-like inputs).
547
- * @param initial - The initial value presented to the user (optional).
548
- * @param defaultConfirmation - The default value for the confirmation prompt (true for yes, false for no).
549
- * @param limit - The maximum number of attempts allowed (default is -1, meaning unlimited).
550
- * @return A Promise that resolves to the valid input or undefined if the limit is reached.
551
- */
552
- static async insistForText(name, question, test, mask = undefined, initial, defaultConfirmation = false, limit = -1) {
553
- const log = UserInput.logger.for(this.insistForText);
554
- log.verbose(`Insisting for text input: undefined, question: ${question}, test: ${test.toString()}, mask: ${mask}, initial: ${initial}, defaultConfirmation: ${defaultConfirmation}, limit: ${limit}`);
555
- const userInput = new UserInput(name).setMessage(question);
556
- if (mask)
557
- userInput.setMask(mask);
558
- if (typeof initial === "string")
559
- userInput.setInitial(initial);
560
- return (await this.insist(userInput, test, defaultConfirmation, limit));
561
- }
562
- /**
563
- * @description Repeatedly asks for number input until a valid response is given or the limit is reached.
564
- * @summary This method insists on getting a valid number input from the user, allowing for a specified number of attempts.
565
- *
566
- * @param name - The name of the prompt, used as the key in the returned answers object.
567
- * @param question - The message displayed to the user.
568
- * @param test - A function to validate the user's input.
569
- * @param min - The minimum allowed value (optional).
570
- * @param max - The maximum allowed value (optional).
571
- * @param initial - The initial value presented to the user (optional).
572
- * @param defaultConfirmation - The default value for the confirmation prompt (true for yes, false for no).
573
- * @param limit - The maximum number of attempts allowed (default is -1, meaning unlimited).
574
- * @return A Promise that resolves to the valid input or undefined if the limit is reached.
575
- */
576
- static async insistForNumber(name, question, test, min, max, initial, defaultConfirmation = false, limit = -1) {
577
- const log = UserInput.logger.for(this.insistForNumber);
578
- log.verbose(`Insisting for number input: undefined, question: ${question}, test: ${test.toString()}, min: ${min}, max: ${max}, initial: ${initial}, defaultConfirmation: ${defaultConfirmation}, limit: ${limit}`);
579
- const userInput = new UserInput(name)
580
- .setMessage(question)
581
- .setType("number");
582
- if (typeof min === "number")
583
- userInput.setMin(min);
584
- if (typeof max === "number")
585
- userInput.setMax(max);
586
- if (typeof initial === "number")
587
- userInput.setInitial(initial);
588
- return (await this.insist(userInput, test, defaultConfirmation, limit));
589
- }
590
- /**
591
- * @description Parses command-line arguments based on the provided options.
592
- * @summary Uses Node.js's util.parseArgs to parse command-line arguments and return the result.
593
- * @param options - Configuration options for parsing arguments.
594
- * @return An object containing the parsed arguments.
595
- * @mermaid
596
- * sequenceDiagram
597
- * participant C as Caller
598
- * participant P as parseArgs method
599
- * participant U as util.parseArgs
600
- * C->>P: Call with options
601
- * P->>P: Prepare args object
602
- * P->>U: Call parseArgs with prepared args
603
- * U->>P: Return parsed result
604
- * P-->>C: Return ParseArgsResult
605
- */
606
- static parseArgs(options) {
607
- const log = UserInput.logger.for(this.parseArgs);
608
- const args = {
609
- args: process.argv.slice(2),
610
- options: options,
611
- };
612
- log.debug(`Parsing arguments: ${JSON.stringify(args, null, 2)}`);
613
- try {
614
- return parseArgs(args);
615
- }
616
- catch (error) {
617
- log.debug(`Error while parsing arguments:\n${JSON.stringify(args, null, 2)}\n | options\n${JSON.stringify(options, null, 2)}\n | ${error}`);
618
- throw new Error(`Error while parsing arguments: ${error}`);
619
- }
620
- }
621
- }
622
-
623
- /**
624
- * @description Default command options for CLI commands.
625
- * @summary Defines the structure and default values for common command-line options used across various CLI commands.
626
- * @const DefaultCommandOptions
627
- * @typedef {Object} DefaultCommandOptions
628
- * @property {Object} verbose - Verbosity level option.
629
- * @property {string} verbose.type - The type of the verbose option (number).
630
- * @property {string} verbose.short - The short flag for the verbose option (V).
631
- * @property {number} verbose.default - The default value for verbosity (0).
632
- * @property {Object} version - Version display option.
633
- * @property {string} version.type - The type of the version option (boolean).
634
- * @property {string} version.short - The short flag for the version option (v).
635
- * @property {undefined} version.default - The default value for version display (undefined).
636
- * @property {Object} help - Help display option.
637
- * @property {string} help.type - The type of the help option (boolean).
638
- * @property {string} help.short - The short flag for the help option (h).
639
- * @property {boolean} help.default - The default value for help display (false).
640
- * @property {Object} logLevel - Log level option.
641
- * @property {string} logLevel.type - The type of the logLevel option (string).
642
- * @property {string} logLevel.default - The default value for log level ("info").
643
- * @property {Object} logStyle - Log styling option.
644
- * @property {string} logStyle.type - The type of the logStyle option (boolean).
645
- * @property {boolean} logStyle.default - The default value for log styling (true).
646
- * @property {Object} timestamp - Timestamp display option.
647
- * @property {string} timestamp.type - The type of the timestamp option (boolean).
648
- * @property {boolean} timestamp.default - The default value for timestamp display (true).
649
- * @property {Object} banner - Banner display option.
650
- * @property {string} banner.type - The type of the banner option (boolean).
651
- * @property {boolean} banner.default - The default value for banner display (false).
652
- * @memberOf module:utils
653
- */
654
- const DefaultCommandOptions = {
655
- verbose: {
656
- type: "boolean",
657
- short: "V",
658
- default: undefined,
659
- },
660
- version: {
661
- type: "boolean",
662
- short: "v",
663
- default: undefined,
664
- },
665
- help: {
666
- type: "boolean",
667
- short: "h",
668
- default: false,
669
- },
670
- logLevel: {
671
- type: "string",
672
- default: "info",
673
- },
674
- logStyle: {
675
- type: "boolean",
676
- default: true,
677
- },
678
- timestamp: {
679
- type: "boolean",
680
- default: true,
681
- },
682
- banner: {
683
- type: "boolean",
684
- default: true,
685
- },
686
- };
687
- /**
688
- * @description Default command values derived from DefaultCommandOptions.
689
- * @summary Creates an object with the default values of all options defined in DefaultCommandOptions.
690
- * @const DefaultCommandValues
691
- * @typedef {Object} DefaultCommandValues
692
- * @property {unknown} [key: string] - The default value for each option in DefaultCommandOptions.
693
- * @memberOf module:utils
694
- */
695
- const DefaultCommandValues = Object.keys(DefaultCommandOptions).reduce((acc, key) => {
696
- acc[key] =
697
- DefaultCommandOptions[key].default;
698
- return acc;
699
- }, {});
700
-
701
- /**
702
- * @description Default encoding for text operations.
703
- * @summary The standard UTF-8 encoding used for text processing.
704
- * @const {string} Encoding
705
- * @memberOf module:utils
706
- */
707
- const Encoding = "utf-8";
708
- /**
709
- * @description Regular expression for semantic versioning.
710
- * @summary A regex pattern to match and parse semantic version strings.
711
- * @const {RegExp} SemVersionRegex
712
- * @memberOf module:utils
713
- */
714
- const SemVersionRegex = /^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z])))/g;
715
- /**
716
- * @description Enum for semantic version components.
717
- * @summary Defines the three levels of semantic versioning: PATCH, MINOR, and MAJOR.
718
- * @enum {string}
719
- * @memberOf module:utils
720
- */
721
- var SemVersion;
722
- (function (SemVersion) {
723
- /** Patch version for backwards-compatible bug fixes. */
724
- SemVersion["PATCH"] = "patch";
725
- /** Minor version for backwards-compatible new features. */
726
- SemVersion["MINOR"] = "minor";
727
- /** Major version for changes that break backwards compatibility. */
728
- SemVersion["MAJOR"] = "major";
729
- })(SemVersion || (SemVersion = {}));
730
- /**
731
- * @description Flag to indicate non-CI environment.
732
- * @summary Used to specify that a command should run outside of a Continuous Integration environment.
733
- * @const {string} NoCIFLag
734
- * @memberOf module:utils
735
- */
736
- const NoCIFLag = "-no-ci";
737
- /**
738
- * @description Key for the setup script in package.json.
739
- * @summary Identifies the script that runs after package installation.
740
- * @const {string} SetupScriptKey
741
- * @memberOf module:utils
742
- */
743
- const SetupScriptKey = "postinstall";
744
- /**
745
- * @description Enum for various authentication tokens.
746
- * @summary Defines the file names for storing different types of authentication tokens.
747
- * @enum {string}
748
- * @memberOf module:utils
749
- */
750
- var Tokens;
751
- (function (Tokens) {
752
- /** Git authentication token file name. */
753
- Tokens["GIT"] = ".token";
754
- /** NPM authentication token file name. */
755
- Tokens["NPM"] = ".npmtoken";
756
- /** Docker authentication token file name. */
757
- Tokens["DOCKER"] = ".dockertoken";
758
- /** Confluence authentication token file name. */
759
- Tokens["CONFLUENCE"] = ".confluence-token";
760
- })(Tokens || (Tokens = {}));
761
- /**
762
- * @description Code used to indicate an operation was aborted.
763
- * @summary Standard message used when a process is manually terminated.
764
- * @const {string} AbortCode
765
- * @memberOf module:utils
766
- */
767
- const AbortCode = "Aborted";
768
-
769
- /**
770
- * @description A standard output writer for handling command execution output.
771
- * @summary This class implements the OutputWriter interface and provides methods for
772
- * handling various types of output from command execution, including standard output,
773
- * error output, and exit codes. It also includes utility methods for parsing commands
774
- * and resolving or rejecting promises based on execution results.
775
- *
776
- * @template R - The type of the resolved value, defaulting to string.
777
- *
778
- * @param cmd - The command string to be executed.
779
- * @param lock - A PromiseExecutor to control the asynchronous flow.
780
- * @param args - Additional arguments (unused in the current implementation).
781
- *
782
- * @class
783
- * @example
784
- * ```typescript
785
- * import { StandardOutputWriter } from '@decaf-ts/utils';
786
- * import { PromiseExecutor } from '@decaf-ts/utils';
787
- *
788
- * // Create a promise executor
789
- * const executor: PromiseExecutor<string> = {
790
- * resolve: (value) => console.log(`Resolved: ${value}`),
791
- * reject: (error) => console.error(`Rejected: ${error.message}`)
792
- * };
793
- *
794
- * // Create a standard output writer
795
- * const writer = new StandardOutputWriter('ls -la', executor);
796
- *
797
- * // Use the writer to handle command output
798
- * writer.data('File list output...');
799
- * writer.exit(0, ['Command executed successfully']);
800
- * ```
801
- *
802
- * @mermaid
803
- * sequenceDiagram
804
- * participant Client
805
- * participant StandardOutputWriter
806
- * participant Logger
807
- * participant PromiseExecutor
808
- *
809
- * Client->>StandardOutputWriter: new StandardOutputWriter(cmd, lock)
810
- * StandardOutputWriter->>Logger: Logging.for(cmd)
811
- *
812
- * Client->>StandardOutputWriter: data(chunk)
813
- * StandardOutputWriter->>StandardOutputWriter: log("stdout", chunk)
814
- * StandardOutputWriter->>Logger: logger.info(log)
815
- *
816
- * Client->>StandardOutputWriter: error(chunk)
817
- * StandardOutputWriter->>StandardOutputWriter: log("stderr", chunk)
818
- * StandardOutputWriter->>Logger: logger.info(log)
819
- *
820
- * Client->>StandardOutputWriter: exit(code, logs)
821
- * StandardOutputWriter->>StandardOutputWriter: log("stdout", exitMessage)
822
- * alt code === 0
823
- * StandardOutputWriter->>StandardOutputWriter: resolve(logs)
824
- * StandardOutputWriter->>PromiseExecutor: lock.resolve(reason)
825
- * else code !== 0
826
- * StandardOutputWriter->>StandardOutputWriter: reject(error)
827
- * StandardOutputWriter->>PromiseExecutor: lock.reject(reason)
828
- * end
829
- */
830
- class StandardOutputWriter {
831
- constructor(cmd, lock,
832
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
833
- ...args) {
834
- this.cmd = cmd;
835
- this.lock = lock;
836
- this.logger = Logging.for(this.cmd);
837
- }
838
- /**
839
- * @description Logs output to the console.
840
- * @summary Formats and logs the given data with a timestamp and type indicator.
841
- *
842
- * @param type - The type of output (stdout or stderr).
843
- * @param data - The data to be logged.
844
- */
845
- log(type, data) {
846
- data = Buffer.isBuffer(data) ? data.toString(Encoding) : data;
847
- const log = type === "stderr" ? style(data).red.text : data;
848
- this.logger.info(log);
849
- }
850
- /**
851
- * @description Handles standard output data.
852
- * @summary Logs the given chunk as standard output.
853
- *
854
- * @param chunk - The data chunk to be logged.
855
- */
856
- data(chunk) {
857
- this.log("stdout", String(chunk));
858
- }
859
- /**
860
- * @description Handles error output data.
861
- * @summary Logs the given chunk as error output.
862
- *
863
- * @param chunk - The error data chunk to be logged.
864
- */
865
- error(chunk) {
866
- this.log("stderr", String(chunk));
867
- }
868
- /**
869
- * @description Handles error objects.
870
- * @summary Logs the error message from the given Error object.
871
- *
872
- * @param err - The Error object to be logged.
873
- */
874
- errors(err) {
875
- this.log("stderr", `Error executing command exited : ${err}`);
876
- }
877
- /**
878
- * @description Handles the exit of a command.
879
- * @summary Logs the exit code and resolves or rejects the promise based on the code.
880
- *
881
- * @param code - The exit code of the command.
882
- * @param logs - Array of log messages to be processed before exiting.
883
- */
884
- exit(code, logs) {
885
- this.log("stdout", `command exited code : ${code === 0 ? style(code.toString()).green.text : style(code === null ? "null" : code.toString()).red.text}`);
886
- if (code === 0) {
887
- this.resolve(logs.map((l) => l.trim()).join("\n"));
888
- }
889
- else {
890
- this.reject(new Error(logs.length ? logs.join("\n") : code.toString()));
891
- }
892
- }
893
- /**
894
- * @description Parses a command string or array into components.
895
- * @summary Converts the command into a consistent format and stores it, then returns it split into command and arguments.
896
- *
897
- * @param command - The command as a string or array of strings.
898
- * @return A tuple containing the command and its arguments as separate elements.
899
- */
900
- parseCommand(command) {
901
- command = typeof command === "string" ? command.split(" ") : command;
902
- this.cmd = command.join(" ");
903
- return [command[0], command.slice(1)];
904
- }
905
- /**
906
- * @description Resolves the promise with a success message.
907
- * @summary Logs a success message and resolves the promise with the given reason.
908
- *
909
- * @param reason - The reason for resolving the promise.
910
- */
911
- resolve(reason) {
912
- this.log("stdout", `${this.cmd} executed successfully: ${style(reason ? "ran to completion" : reason).green}`);
913
- this.lock.resolve(reason);
914
- }
915
- /**
916
- * @description Rejects the promise with an error message.
917
- * @summary Logs an error message and rejects the promise with the given reason.
918
- *
919
- * @param reason - The reason for rejecting the promise, either a number (exit code) or a string.
920
- */
921
- reject(reason) {
922
- if (!(reason instanceof Error)) {
923
- reason = new Error(typeof reason === "number" ? `Exit code ${reason}` : reason);
924
- }
925
- this.log("stderr", `${this.cmd} failed to execute: ${style(reason.message).red}`);
926
- this.lock.reject(reason);
927
- }
928
- }
929
-
930
- /**
931
- * @description Creates a locked version of a function.
932
- * @summary This higher-order function takes a function and returns a new function that ensures
933
- * sequential execution of the original function, even when called multiple times concurrently.
934
- * It uses a Promise-based locking mechanism to queue function calls.
935
- *
936
- * @template R - The return type of the input function.
937
- *
938
- * @param f - The function to be locked. It can take any number of parameters and return a value of type R.
939
- * @return A new function with the same signature as the input function, but with sequential execution guaranteed.
940
- *
941
- * @function lockify
942
- *
943
- * @mermaid
944
- * sequenceDiagram
945
- * participant Caller
946
- * participant LockedFunction
947
- * participant OriginalFunction
948
- * Caller->>LockedFunction: Call with params
949
- * LockedFunction->>LockedFunction: Check current lock
950
- * alt Lock is resolved
951
- * LockedFunction->>OriginalFunction: Execute with params
952
- * OriginalFunction-->>LockedFunction: Return result
953
- * LockedFunction-->>Caller: Return result
954
- * else Lock is pending
955
- * LockedFunction->>LockedFunction: Queue execution
956
- * LockedFunction-->>Caller: Return promise
957
- * Note over LockedFunction: Wait for previous execution
958
- * LockedFunction->>OriginalFunction: Execute with params
959
- * OriginalFunction-->>LockedFunction: Return result
960
- * LockedFunction-->>Caller: Resolve promise with result
961
- * end
962
- * LockedFunction->>LockedFunction: Update lock
963
- *
964
- * @memberOf module:utils
965
- */
966
- function lockify(f) {
967
- let lock = Promise.resolve();
968
- return (...params) => {
969
- const result = lock.then(() => f(...params));
970
- lock = result.catch(() => { });
971
- return result;
972
- };
973
- }
974
- function chainAbortController(argument0, ...remainder) {
975
- let signals;
976
- let controller;
977
- // normalize args
978
- if (argument0 instanceof AbortSignal) {
979
- controller = new AbortController();
980
- signals = [argument0, ...remainder];
981
- }
982
- else {
983
- controller = argument0;
984
- signals = remainder;
985
- }
986
- // if the controller is already aborted, exit early
987
- if (controller.signal.aborted) {
988
- return controller;
989
- }
990
- const handler = () => controller.abort();
991
- for (const signal of signals) {
992
- // check before adding! (and assume there is no possible way that the signal could
993
- // abort between the `if` check and adding the event listener)
994
- if (signal.aborted) {
995
- controller.abort();
996
- break;
997
- }
998
- signal.addEventListener("abort", handler, {
999
- once: true,
1000
- signal: controller.signal,
1001
- });
1002
- }
1003
- return controller;
1004
- }
1005
- /**
1006
- * @description Spawns a command as a child process with output handling.
1007
- * @summary Creates a child process to execute a command with support for piping multiple commands,
1008
- * custom output handling, and abort control. This function handles the low-level details of
1009
- * spawning processes and connecting their inputs/outputs when piping is used.
1010
- *
1011
- * @template R - The type of the processed output, defaulting to string.
1012
- * @param {StandardOutputWriter<R>} output - The output writer to handle command output.
1013
- * @param {string} command - The command to execute, can include pipe operators.
1014
- * @param {SpawnOptionsWithoutStdio} opts - Options for the spawned process.
1015
- * @param {AbortController} abort - Controller to abort the command execution.
1016
- * @param {Logger} logger - Logger for recording command execution details.
1017
- * @return {ChildProcessWithoutNullStreams} The spawned child process.
1018
- *
1019
- * @function spawnCommand
1020
- *
1021
- * @memberOf module:utils
1022
- */
1023
- function spawnCommand(output, command, opts, abort, logger) {
1024
- function spawnInner(command, controller) {
1025
- const [cmd, argz] = output.parseCommand(command);
1026
- logger.info(`Running command: ${cmd}`);
1027
- logger.debug(`with args: ${argz.join(" ")}`);
1028
- const childProcess = spawn(cmd, argz, {
1029
- ...opts,
1030
- cwd: opts.cwd || process.cwd(),
1031
- env: Object.assign({}, process.env, opts.env, { PATH: process.env.PATH }),
1032
- shell: opts.shell || false,
1033
- signal: controller.signal,
1034
- });
1035
- logger.verbose(`pid : ${childProcess.pid}`);
1036
- return childProcess;
1037
- }
1038
- const m = command.match(/[<>$#]/g);
1039
- if (m)
1040
- throw new Error(`Invalid command: ${command}. contains invalid characters: ${m}`);
1041
- if (command.includes(" | ")) {
1042
- const cmds = command.split(" | ");
1043
- const spawns = [];
1044
- const controllers = new Array(cmds.length);
1045
- controllers[0] = abort;
1046
- for (let i = 0; i < cmds.length; i++) {
1047
- if (i !== 0)
1048
- controllers[i] = chainAbortController(controllers[i - 1].signal);
1049
- spawns.push(spawnInner(cmds[i], controllers[i]));
1050
- if (i === 0)
1051
- continue;
1052
- spawns[i - 1].stdout.pipe(spawns[i].stdin);
1053
- }
1054
- return spawns[cmds.length - 1];
1055
- }
1056
- return spawnInner(command, abort);
1057
- }
1058
- /**
1059
- * @description Executes a command asynchronously with customizable output handling.
1060
- * @summary This function runs a shell command as a child process, providing fine-grained
1061
- * control over its execution and output handling. It supports custom output writers,
1062
- * allows for command abortion, and captures both stdout and stderr.
1063
- *
1064
- * @template R - The type of the resolved value from the command execution.
1065
- *
1066
- * @param command - The command to run, either as a string or an array of strings.
1067
- * @param opts - Spawn options for the child process. Defaults to an empty object.
1068
- * @param outputConstructor - Constructor for the output writer. Defaults to StandardOutputWriter.
1069
- * @param args - Additional arguments to pass to the output constructor.
1070
- * @return {CommandResult} A promise that resolves to the command result of type R.
1071
- *
1072
- * @function runCommand
1073
- *
1074
- * @mermaid
1075
- * sequenceDiagram
1076
- * participant Caller
1077
- * participant runCommand
1078
- * participant OutputWriter
1079
- * participant ChildProcess
1080
- * Caller->>runCommand: Call with command and options
1081
- * runCommand->>OutputWriter: Create new instance
1082
- * runCommand->>OutputWriter: Parse command
1083
- * runCommand->>ChildProcess: Spawn process
1084
- * ChildProcess-->>runCommand: Return process object
1085
- * runCommand->>ChildProcess: Set up event listeners
1086
- * loop For each stdout data
1087
- * ChildProcess->>runCommand: Emit stdout data
1088
- * runCommand->>OutputWriter: Handle stdout data
1089
- * end
1090
- * loop For each stderr data
1091
- * ChildProcess->>runCommand: Emit stderr data
1092
- * runCommand->>OutputWriter: Handle stderr data
1093
- * end
1094
- * ChildProcess->>runCommand: Emit error (if any)
1095
- * runCommand->>OutputWriter: Handle error
1096
- * ChildProcess->>runCommand: Emit exit
1097
- * runCommand->>OutputWriter: Handle exit
1098
- * OutputWriter-->>runCommand: Resolve or reject promise
1099
- * runCommand-->>Caller: Return CommandResult
1100
- *
1101
- * @memberOf module:utils
1102
- */
1103
- function runCommand(command, opts = {}, outputConstructor = (StandardOutputWriter), ...args) {
1104
- const logger = Logging.for(runCommand);
1105
- const abort = new AbortController();
1106
- const result = {
1107
- abort: abort,
1108
- command: command,
1109
- logs: [],
1110
- errs: [],
1111
- };
1112
- const lock = new Promise((resolve, reject) => {
1113
- let output;
1114
- try {
1115
- output = new outputConstructor(command, {
1116
- resolve,
1117
- reject,
1118
- }, ...args);
1119
- result.cmd = spawnCommand(output, command, opts, abort, logger);
1120
- }
1121
- catch (e) {
1122
- return reject(new Error(`Error running command ${command}: ${e}`));
1123
- }
1124
- result.cmd.stdout.setEncoding("utf8");
1125
- result.cmd.stdout.on("data", (chunk) => {
1126
- chunk = chunk.toString();
1127
- result.logs.push(chunk);
1128
- output.data(chunk);
1129
- });
1130
- result.cmd.stderr.on("data", (data) => {
1131
- data = data.toString();
1132
- result.errs.push(data);
1133
- output.error(data);
1134
- });
1135
- result.cmd.once("error", (err) => {
1136
- output.exit(err.message, result.errs);
1137
- });
1138
- result.cmd.once("exit", (code = 0) => {
1139
- if (abort.signal.aborted && code === null)
1140
- code = AbortCode;
1141
- output.exit(code, code === 0 ? result.logs : result.errs);
1142
- });
1143
- });
1144
- Object.assign(result, {
1145
- promise: lock,
1146
- pipe: async (cb) => {
1147
- const l = logger.for("pipe");
1148
- try {
1149
- l.verbose(`Executing pipe function ${command}...`);
1150
- const result = await lock;
1151
- l.verbose(`Piping output to ${cb.name}: ${result}`);
1152
- return cb(result);
1153
- }
1154
- catch (e) {
1155
- l.error(`Error piping command output: ${e}`);
1156
- throw e;
1157
- }
1158
- },
1159
- });
1160
- return result;
1161
- }
1162
-
1163
- const logger = Logging.for("fs");
1164
- /**
1165
- * @description Patches a file with given values.
1166
- * @summary Reads a file, applies patches using TextUtils, and writes the result back to the file.
1167
- *
1168
- * @param {string} path - The path to the file to be patched.
1169
- * @param {Record<string, number | string>} values - The values to patch into the file.
1170
- * @return {void}
1171
- *
1172
- * @function patchFile
1173
- *
1174
- * @mermaid
1175
- * sequenceDiagram
1176
- * participant Caller
1177
- * participant patchFile
1178
- * participant fs
1179
- * participant readFile
1180
- * participant TextUtils
1181
- * participant writeFile
1182
- * Caller->>patchFile: Call with path and values
1183
- * patchFile->>fs: Check if file exists
1184
- * patchFile->>readFile: Read file content
1185
- * readFile->>fs: Read file
1186
- * fs-->>readFile: Return file content
1187
- * readFile-->>patchFile: Return file content
1188
- * patchFile->>TextUtils: Patch string
1189
- * TextUtils-->>patchFile: Return patched content
1190
- * patchFile->>writeFile: Write patched content
1191
- * writeFile->>fs: Write to file
1192
- * fs-->>writeFile: File written
1193
- * writeFile-->>patchFile: File written
1194
- * patchFile-->>Caller: Patching complete
1195
- *
1196
- * @memberOf module:utils
1197
- */
1198
- function patchFile(path, values) {
1199
- const log = logger.for(patchFile);
1200
- if (!fs.existsSync(path))
1201
- throw new Error(`File not found at path "${path}".`);
1202
- let content = readFile(path);
1203
- try {
1204
- log.verbose(`Patching file "${path}"...`);
1205
- log.debug(`with value: ${JSON.stringify(values)}`);
1206
- content = patchString(content, values);
1207
- }
1208
- catch (error) {
1209
- throw new Error(`Error patching file: ${error}`);
1210
- }
1211
- writeFile(path, content);
1212
- }
1213
- /**
1214
- * @description Reads a file and returns its content.
1215
- * @summary Reads the content of a file at the specified path and returns it as a string.
1216
- *
1217
- * @param {string} path - The path to the file to be read.
1218
- * @return {string} The content of the file.
1219
- *
1220
- * @function readFile
1221
- *
1222
- * @memberOf module:utils
1223
- */
1224
- function readFile(path) {
1225
- const log = logger.for(readFile);
1226
- try {
1227
- log.verbose(`Reading file "${path}"...`);
1228
- return fs.readFileSync(path, "utf8");
1229
- }
1230
- catch (error) {
1231
- log.verbose(`Error reading file "${path}": ${error}`);
1232
- throw new Error(`Error reading file "${path}": ${error}`);
1233
- }
1234
- }
1235
- /**
1236
- * @description Writes data to a file.
1237
- * @summary Writes the provided data to a file at the specified path.
1238
- *
1239
- * @param {string} path - The path to the file to be written.
1240
- * @param {string | Buffer} data - The data to be written to the file.
1241
- * @return {void}
1242
- *
1243
- * @function writeFile
1244
- *
1245
- * @memberOf module:utils
1246
- */
1247
- function writeFile(path, data) {
1248
- const log = logger.for(writeFile);
1249
- try {
1250
- log.verbose(`Writing file "${path} with ${data.length} bytes...`);
1251
- fs.writeFileSync(path, data, "utf8");
1252
- }
1253
- catch (error) {
1254
- log.verbose(`Error writing file "${path}": ${error}`);
1255
- throw new Error(`Error writing file "${path}": ${error}`);
1256
- }
1257
- }
1258
- /**
1259
- * @description Retrieves all files recursively from a directory.
1260
- * @summary Traverses through directories and subdirectories to collect all file paths.
1261
- *
1262
- * @param {string} p - The path to start searching from.
1263
- * @param {function} [filter] - Optional function to filter files by name or index.
1264
- * @return {string[]} Array of file paths.
1265
- *
1266
- * @function getAllFiles
1267
- *
1268
- * @memberOf module:utils
1269
- */
1270
- function getAllFiles(p, filter) {
1271
- const log = logger.for(getAllFiles);
1272
- const files = [];
1273
- try {
1274
- log.verbose(`Retrieving all files from "${p}"...`);
1275
- const entries = fs.readdirSync(p);
1276
- entries.forEach((entry) => {
1277
- const fullPath = path.join(p, entry);
1278
- const stat = fs.statSync(fullPath);
1279
- if (stat.isFile()) {
1280
- files.push(fullPath);
1281
- }
1282
- else if (stat.isDirectory()) {
1283
- files.push(...getAllFiles(fullPath));
1284
- }
1285
- });
1286
- if (!filter)
1287
- return files;
1288
- return files.filter(filter);
1289
- }
1290
- catch (error) {
1291
- log.verbose(`Error retrieving files from "${p}": ${error}`);
1292
- throw new Error(`Error retrieving files from "${p}": ${error}`);
1293
- }
1294
- }
1295
- /**
1296
- * @description Renames a file or directory.
1297
- * @summary Moves a file or directory from the source path to the destination path.
1298
- *
1299
- * @param {string} source - The source path of the file or directory.
1300
- * @param {string} dest - The destination path for the file or directory.
1301
- * @return {Promise<void>} A promise that resolves when the rename operation is complete.
1302
- *
1303
- * @function renameFile
1304
- *
1305
- * @memberOf module:utils
1306
- */
1307
- async function renameFile(source, dest) {
1308
- const log = logger.for(renameFile);
1309
- let descriptorSource, descriptorDest;
1310
- try {
1311
- descriptorSource = fs.statSync(source);
1312
- }
1313
- catch (error) {
1314
- log.verbose(`Source path "${source}" does not exist: ${error}`);
1315
- throw new Error(`Source path "${source}" does not exist: ${error}`);
1316
- }
1317
- try {
1318
- descriptorDest = fs.statSync(dest);
1319
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
1320
- }
1321
- catch (e) {
1322
- // do nothing. its ok
1323
- }
1324
- if (descriptorDest) {
1325
- log.verbose(`Destination path "${dest}" already exists`);
1326
- throw new Error(`Destination path "${dest}" already exists`);
1327
- }
1328
- try {
1329
- log.verbose(`Renaming ${descriptorSource.isFile() ? "file" : "directory"} "${source}" to "${dest}...`);
1330
- fs.renameSync(source, dest);
1331
- log.verbose(`Successfully renamed to "${dest}"`);
1332
- }
1333
- catch (error) {
1334
- log.verbose(`Error renaming ${descriptorSource.isFile() ? "file" : "directory"} "${source}" to "${dest}": ${error}`);
1335
- throw new Error(`Error renaming ${descriptorSource.isFile() ? "file" : "directory"} "${source}" to "${dest}": ${error}`);
1336
- }
1337
- }
1338
- /**
1339
- * @description Copies a file or directory.
1340
- * @summary Creates a copy of a file or directory from the source path to the destination path.
1341
- *
1342
- * @param {string} source - The source path of the file or directory.
1343
- * @param {string} dest - The destination path for the file or directory.
1344
- * @return {void}
1345
- *
1346
- * @function copyFile
1347
- *
1348
- * @memberOf module:utils
1349
- */
1350
- function copyFile(source, dest) {
1351
- const log = logger.for(copyFile);
1352
- let descriptorSource, descriptorDest;
1353
- try {
1354
- descriptorSource = fs.statSync(source);
1355
- }
1356
- catch (error) {
1357
- log.verbose(`Source path "${source}" does not exist: ${error}`);
1358
- throw new Error(`Source path "${source}" does not exist: ${error}`);
1359
- }
1360
- try {
1361
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
1362
- descriptorDest = fs.statSync(dest);
1363
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
1364
- }
1365
- catch (error) {
1366
- if (descriptorSource.isDirectory()) {
1367
- log.verbose(`Dest path "${dest}" does not exist. creating`);
1368
- fs.mkdirSync(dest, { recursive: true });
1369
- }
1370
- }
1371
- try {
1372
- log.verbose(`Copying ${descriptorSource.isFile() ? "file" : "directory"} "${source}" to "${dest}...`);
1373
- fs.cpSync(source, dest, { recursive: true });
1374
- }
1375
- catch (error) {
1376
- log.verbose(`Error copying ${descriptorSource.isFile() ? "file" : "directory"} "${source}" to "${dest}: ${error}`);
1377
- throw new Error(`Error copying ${descriptorSource.isFile() ? "file" : "directory"} "${source}" to "${dest}: ${error}`);
1378
- }
1379
- }
1380
- /**
1381
- * @description Deletes a file or directory.
1382
- * @summary Removes a file or directory at the specified path, with recursive and force options enabled.
1383
- *
1384
- * @param {string} p - The path to the file or directory to delete.
1385
- * @return {void}
1386
- *
1387
- * @function deletePath
1388
- *
1389
- * @memberOf module:utils
1390
- */
1391
- function deletePath(p) {
1392
- const log = logger.for(deletePath);
1393
- try {
1394
- const descriptor = fs.statSync(p);
1395
- if (descriptor.isFile()) {
1396
- log.verbose(`Deleting file "${p}...`);
1397
- fs.rmSync(p, { recursive: true, force: true });
1398
- }
1399
- else if (descriptor.isDirectory())
1400
- fs.rmSync(p, { recursive: true, force: true });
1401
- }
1402
- catch (error) {
1403
- log.verbose(`Error Deleting "${p}": ${error}`);
1404
- throw new Error(`Error Deleting "${p}": ${error}`);
1405
- }
1406
- }
1407
- /**
1408
- * @description Retrieves package information from package.json.
1409
- * @summary Loads and parses the package.json file from a specified directory or the current working directory. Can return the entire package object or a specific property.
1410
- * @param {string} [p=process.cwd()] - The directory path where the package.json file is located.
1411
- * @param {string} [property] - Optional. The specific property to retrieve from package.json.
1412
- * @return {object | string} The parsed contents of package.json or the value of the specified property.
1413
- * @function getPackage
1414
- * @mermaid
1415
- * sequenceDiagram
1416
- * participant Caller
1417
- * participant getPackage
1418
- * participant readFile
1419
- * participant JSON
1420
- * Caller->>getPackage: Call with path and optional property
1421
- * getPackage->>readFile: Read package.json
1422
- * readFile-->>getPackage: Return file content
1423
- * getPackage->>JSON: Parse file content
1424
- * JSON-->>getPackage: Return parsed object
1425
- * alt property specified
1426
- * getPackage->>getPackage: Check if property exists
1427
- * alt property exists
1428
- * getPackage-->>Caller: Return property value
1429
- * else property doesn't exist
1430
- * getPackage-->>Caller: Throw Error
1431
- * end
1432
- * else no property specified
1433
- * getPackage-->>Caller: Return entire package object
1434
- * end
1435
- * @memberOf module:utils
1436
- */
1437
- function getPackage(p = process.cwd(), property) {
1438
- let pkg;
1439
- try {
1440
- pkg = JSON.parse(readFile(path.join(p, `package.json`)));
1441
- }
1442
- catch (error) {
1443
- throw new Error(`Failed to retrieve package information" ${error}`);
1444
- }
1445
- if (property) {
1446
- if (!(property in pkg))
1447
- throw new Error(`Property "${property}" not found in package.json`);
1448
- return pkg[property];
1449
- }
1450
- return pkg;
1451
- }
1452
- /**
1453
- * @description Sets an attribute in the package.json file.
1454
- * @summary Updates a specific attribute in the package.json file with the provided value.
1455
- *
1456
- * @param {string} attr - The attribute name to set in package.json.
1457
- * @param {string | number | object} value - The value to set for the attribute.
1458
- * @param {string} [p=process.cwd()] - The directory path where the package.json file is located.
1459
- * @return {void}
1460
- *
1461
- * @function setPackageAttribute
1462
- *
1463
- * @memberOf module:utils
1464
- */
1465
- function setPackageAttribute(attr, value, p = process.cwd()) {
1466
- const pkg = getPackage(p);
1467
- pkg[attr] = value;
1468
- writeFile(path.join(p, `package.json`), JSON.stringify(pkg, null, 2));
1469
- }
1470
- /**
1471
- * @description Retrieves the version from package.json.
1472
- * @summary A convenience function that calls getPackage to retrieve the "version" property from package.json.
1473
- * @param {string} [p=process.cwd()] - The directory path where the package.json file is located.
1474
- * @return {string} The version string from package.json.
1475
- * @function getPackageVersion
1476
- * @memberOf module:utils
1477
- */
1478
- function getPackageVersion(p = process.cwd()) {
1479
- return getPackage(p, "version");
1480
- }
1481
- /**
1482
- * @description Retrieves all dependencies from the project.
1483
- * @summary Executes 'npm ls --json' command to get a detailed list of all dependencies (production, development, and peer) and their versions.
1484
- * @param {string} [path=process.cwd()] - The directory path of the project.
1485
- * @return {Promise<{prod: Array<{name: string, version: string}>, dev: Array<{name: string, version: string}>, peer: Array<{name: string, version: string}>}>} An object containing arrays of production, development, and peer dependencies.
1486
- * @function getDependencies
1487
- * @mermaid
1488
- * sequenceDiagram
1489
- * participant Caller
1490
- * participant getDependencies
1491
- * participant runCommand
1492
- * participant JSON
1493
- * Caller->>getDependencies: Call with optional path
1494
- * getDependencies->>runCommand: Execute 'npm ls --json'
1495
- * runCommand-->>getDependencies: Return command output
1496
- * getDependencies->>JSON: Parse command output
1497
- * JSON-->>getDependencies: Return parsed object
1498
- * getDependencies->>getDependencies: Process dependencies
1499
- * getDependencies-->>Caller: Return processed dependencies
1500
- * @memberOf module:utils
1501
- */
1502
- async function getDependencies(path = process.cwd()) {
1503
- let pkg;
1504
- try {
1505
- pkg = JSON.parse(await runCommand(`npm ls --json`, { cwd: path }).promise);
1506
- }
1507
- catch (e) {
1508
- throw new Error(`Failed to retrieve dependencies: ${e}`);
1509
- }
1510
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
1511
- const mapper = (entry, index) => ({
1512
- name: entry[0],
1513
- version: entry[1].version,
1514
- });
1515
- return {
1516
- prod: Object.entries(pkg.dependencies || {}).map(mapper),
1517
- dev: Object.entries(pkg.devDependencies || {}).map(mapper),
1518
- peer: Object.entries(pkg.peerDependencies || {}).map(mapper),
1519
- };
1520
- }
1521
- /**
1522
- * @description Updates project dependencies to their latest versions.
1523
- * @summary Runs npm-check-updates to update package.json and then installs the updated dependencies.
1524
- *
1525
- * @return {Promise<void>} A promise that resolves when dependencies are updated.
1526
- *
1527
- * @function updateDependencies
1528
- *
1529
- * @memberOf module:utils
1530
- */
1531
- async function updateDependencies() {
1532
- const log = logger.for(updateDependencies);
1533
- log.info("checking for updates...");
1534
- await runCommand("npx npm-check-updates -u").promise;
1535
- log.info("updating...");
1536
- await runCommand("npx npm run do-install").promise;
1537
- }
1538
- /**
1539
- * @description Installs dependencies if they are not already available.
1540
- * @summary Checks if specified dependencies are installed and installs any that are missing.
1541
- *
1542
- * @param {string[] | string} deps - The dependencies to check and potentially install.
1543
- * @param {SimpleDependencyMap} [dependencies] - Optional map of existing dependencies.
1544
- * @return {Promise<SimpleDependencyMap>} Updated map of dependencies.
1545
- *
1546
- * @function installIfNotAvailable
1547
- *
1548
- * @memberOf module:utils
1549
- */
1550
- async function installIfNotAvailable(deps, dependencies) {
1551
- if (!dependencies) {
1552
- const d = await getDependencies();
1553
- dependencies = {
1554
- prod: d.prod?.map((p) => p.name) || [],
1555
- dev: d.dev?.map((d) => d.name) || [],
1556
- peer: d.peer?.map((p) => p.name) || [],
1557
- };
1558
- }
1559
- const { prod, dev, peer } = dependencies;
1560
- const installed = Array.from(new Set([...(prod || []), ...(dev || []), ...(peer || [])]));
1561
- deps = typeof deps === "string" ? [deps] : deps;
1562
- const toInstall = deps.filter((d) => !installed.includes(d));
1563
- if (toInstall.length)
1564
- await installDependencies({ dev: toInstall });
1565
- dependencies.dev = dependencies.dev || [];
1566
- dependencies.dev.push(...toInstall);
1567
- return dependencies;
1568
- }
1569
- /**
1570
- * @description Pushes changes to Git repository.
1571
- * @summary Temporarily changes Git user configuration, commits all changes, pushes to remote, and restores original user configuration.
1572
- *
1573
- * @return {Promise<void>} A promise that resolves when changes are pushed.
1574
- *
1575
- * @function pushToGit
1576
- *
1577
- * @memberOf module:utils
1578
- */
1579
- async function pushToGit() {
1580
- const log = logger.for(pushToGit);
1581
- const gitUser = await runCommand("git config user.name").promise;
1582
- const gitEmail = await runCommand("git config user.email").promise;
1583
- log.verbose(`cached git id: ${gitUser}/${gitEmail}. changing to automation`);
1584
- await runCommand('git config user.email "automation@decaf.ts"').promise;
1585
- await runCommand('git config user.name "decaf"').promise;
1586
- log.info("Pushing changes to git...");
1587
- await runCommand("git add .").promise;
1588
- await runCommand(`git commit -m "refs #1 - after repo setup"`).promise;
1589
- await runCommand("git push").promise;
1590
- await runCommand(`git config user.email "${gitEmail}"`).promise;
1591
- await runCommand(`git config user.name "${gitUser}"`).promise;
1592
- log.verbose(`reverted to git id: ${gitUser}/${gitEmail}`);
1593
- }
1594
- /**
1595
- * @description Installs project dependencies.
1596
- * @summary Installs production, development, and peer dependencies as specified.
1597
- *
1598
- * @param {object} dependencies - Object containing arrays of dependencies to install.
1599
- * @param {string[]} [dependencies.prod] - Production dependencies to install.
1600
- * @param {string[]} [dependencies.dev] - Development dependencies to install.
1601
- * @param {string[]} [dependencies.peer] - Peer dependencies to install.
1602
- * @return {Promise<void>} A promise that resolves when all dependencies are installed.
1603
- *
1604
- * @function installDependencies
1605
- *
1606
- * @memberOf module:utils
1607
- */
1608
- async function installDependencies(dependencies) {
1609
- const log = logger.for(installDependencies);
1610
- const prod = dependencies.prod || [];
1611
- const dev = dependencies.dev || [];
1612
- const peer = dependencies.peer || [];
1613
- if (prod.length) {
1614
- log.info(`Installing dependencies ${prod.join(", ")}...`);
1615
- await runCommand(`npm install ${prod.join(" ")}`, { cwd: process.cwd() })
1616
- .promise;
1617
- }
1618
- if (dev.length) {
1619
- log.info(`Installing devDependencies ${dev.join(", ")}...`);
1620
- await runCommand(`npm install --save-dev ${dev.join(" ")}`, {
1621
- cwd: process.cwd(),
1622
- }).promise;
1623
- }
1624
- if (peer.length) {
1625
- log.info(`Installing peerDependencies ${peer.join(", ")}...`);
1626
- await runCommand(`npm install --save-peer ${peer.join(" ")}`, {
1627
- cwd: process.cwd(),
1628
- }).promise;
1629
- }
1630
- }
1631
- /**
1632
- * @description Normalizes imports to handle both CommonJS and ESModule formats.
1633
- * @summary Utility function to handle module import differences between formats.
1634
- *
1635
- * @template T - Type of the imported module.
1636
- * @param {Promise<T>} importPromise - Promise returned by dynamic import.
1637
- * @return {Promise<T>} Normalized module.
1638
- *
1639
- * @function normalizeImport
1640
- *
1641
- * @memberOf module:utils
1642
- */
1643
- async function normalizeImport(importPromise) {
1644
- // CommonJS's `module.exports` is wrapped as `default` in ESModule.
1645
- return importPromise.then((m) => (m.default || m));
1646
- }
1647
-
1648
- /**
1649
- * @description Definition of a slogan item.
1650
- * @summary Represents a single slogan entry with text and tags.
1651
- * @typedef {Object} SloganItem
1652
- * @property {string} Slogan - The slogan text.
1653
- * @property {string} Tags - Comma-separated tags describing the slogan.
1654
- * @memberOf module:utils
1655
- */
1656
- /**
1657
- * @description List of available slogans for banners and messages.
1658
- * @summary Immutable array of slogan entries used by {@link getSlogan} and banner rendering.
1659
- * @type {SloganItem[]}
1660
- * @const slogans
1661
- * @memberOf module:utils
1662
- */
1663
- const slogans = [
1664
- {
1665
- Slogan: "No caffeine, no chaos. Just clean code.",
1666
- Tags: "Coffee-themed, Calm, Tech",
1667
- },
1668
- {
1669
- Slogan: "Full flavor, no jitters. That's Decaf-TS.",
1670
- Tags: "Coffee-themed, Cheerful",
1671
- },
1672
- {
1673
- Slogan: "Chill fullstack. Powered by Decaf.",
1674
- Tags: "Coffee-themed, Fun, Tech",
1675
- },
1676
- {
1677
- Slogan: "Decaf-TS: Brewed for calm code.",
1678
- Tags: "Coffee-themed, Branding",
1679
- },
1680
- {
1681
- Slogan: "Smooth as your morning Decaf.",
1682
- Tags: "Coffee-themed, Chill",
1683
- },
1684
- {
1685
- Slogan: "All the kick, none of the crash.",
1686
- Tags: "Coffee-themed, Energetic",
1687
- },
1688
- {
1689
- Slogan: "Sip back and ship faster.",
1690
- Tags: "Coffee-themed, Fun",
1691
- },
1692
- {
1693
- Slogan: "Keep calm and code Decaf.",
1694
- Tags: "Coffee-themed, Playful",
1695
- },
1696
- {
1697
- Slogan: "Code without the caffeine shakes.",
1698
- Tags: "Coffee-themed, Humorous",
1699
- },
1700
- {
1701
- Slogan: "Your fullstack, decaffeinated.",
1702
- Tags: "Coffee-themed, Technical",
1703
- },
1704
- {
1705
- Slogan: "No caffeine, no chaos. Just clean code.",
1706
- Tags: "Coffee-themed, Calm, Tech",
1707
- },
1708
- {
1709
- Slogan: "Full flavor, no jitters. That\u2019s Decaf-TS.",
1710
- Tags: "Coffee-themed, Cheerful",
1711
- },
1712
- {
1713
- Slogan: "Chill fullstack. Powered by Decaf.",
1714
- Tags: "Coffee-themed, Fun, Tech",
1715
- },
1716
- {
1717
- Slogan: "Decaf-TS: Brewed for calm code.",
1718
- Tags: "Coffee-themed, Branding",
1719
- },
1720
- {
1721
- Slogan: "Smooth as your morning Decaf.",
1722
- Tags: "Coffee-themed, Chill",
1723
- },
1724
- {
1725
- Slogan: "All the kick, none of the crash.",
1726
- Tags: "Coffee-themed, Energetic",
1727
- },
1728
- {
1729
- Slogan: "Sip back and ship faster.",
1730
- Tags: "Coffee-themed, Fun",
1731
- },
1732
- {
1733
- Slogan: "Keep calm and code Decaf.",
1734
- Tags: "Coffee-themed, Playful",
1735
- },
1736
- {
1737
- Slogan: "Code without the caffeine shakes.",
1738
- Tags: "Coffee-themed, Humorous",
1739
- },
1740
- {
1741
- Slogan: "Your fullstack, decaffeinated.",
1742
- Tags: "Coffee-themed, Technical",
1743
- },
1744
- {
1745
- Slogan: "No caffeine, no chaos. Just clean code.",
1746
- Tags: "Coffee-themed, Calm, Tech",
1747
- },
1748
- {
1749
- Slogan: "Full flavor, no jitters. That\u2019s Decaf-TS.",
1750
- Tags: "Coffee-themed, Cheerful",
1751
- },
1752
- {
1753
- Slogan: "Chill fullstack. Powered by Decaf.",
1754
- Tags: "Coffee-themed, Fun, Tech",
1755
- },
1756
- {
1757
- Slogan: "Decaf-TS: Brewed for calm code.",
1758
- Tags: "Coffee-themed, Branding",
1759
- },
1760
- {
1761
- Slogan: "Smooth as your morning Decaf.",
1762
- Tags: "Coffee-themed, Chill",
1763
- },
1764
- {
1765
- Slogan: "All the kick, none of the crash.",
1766
- Tags: "Coffee-themed, Energetic",
1767
- },
1768
- {
1769
- Slogan: "Sip back and ship faster.",
1770
- Tags: "Coffee-themed, Fun",
1771
- },
1772
- {
1773
- Slogan: "Keep calm and code Decaf.",
1774
- Tags: "Coffee-themed, Playful",
1775
- },
1776
- {
1777
- Slogan: "Code without the caffeine shakes.",
1778
- Tags: "Coffee-themed, Humorous",
1779
- },
1780
- {
1781
- Slogan: "Your fullstack, decaffeinated.",
1782
- Tags: "Coffee-themed, Technical",
1783
- },
1784
- {
1785
- Slogan: "No caffeine, no chaos. Just clean code.",
1786
- Tags: "Coffee-themed, Calm, Tech",
1787
- },
1788
- {
1789
- Slogan: "Full flavor, no jitters. That\u2019s Decaf-TS.",
1790
- Tags: "Coffee-themed, Cheerful",
1791
- },
1792
- {
1793
- Slogan: "Chill fullstack. Powered by Decaf.",
1794
- Tags: "Coffee-themed, Fun, Tech",
1795
- },
1796
- {
1797
- Slogan: "Decaf-TS: Brewed for calm code.",
1798
- Tags: "Coffee-themed, Branding",
1799
- },
1800
- {
1801
- Slogan: "Smooth as your morning Decaf.",
1802
- Tags: "Coffee-themed, Chill",
1803
- },
1804
- {
1805
- Slogan: "All the kick, none of the crash.",
1806
- Tags: "Coffee-themed, Energetic",
1807
- },
1808
- {
1809
- Slogan: "Sip back and ship faster.",
1810
- Tags: "Coffee-themed, Fun",
1811
- },
1812
- {
1813
- Slogan: "Keep calm and code Decaf.",
1814
- Tags: "Coffee-themed, Playful",
1815
- },
1816
- {
1817
- Slogan: "Code without the caffeine shakes.",
1818
- Tags: "Coffee-themed, Humorous",
1819
- },
1820
- {
1821
- Slogan: "Your fullstack, decaffeinated.",
1822
- Tags: "Coffee-themed, Technical",
1823
- },
1824
- {
1825
- Slogan: "No caffeine, no chaos. Just clean code.",
1826
- Tags: "Coffee-themed, Calm, Tech",
1827
- },
1828
- {
1829
- Slogan: "Full flavor, no jitters. That\u2019s Decaf-TS.",
1830
- Tags: "Coffee-themed, Cheerful",
1831
- },
1832
- {
1833
- Slogan: "Chill fullstack. Powered by Decaf.",
1834
- Tags: "Coffee-themed, Fun, Tech",
1835
- },
1836
- {
1837
- Slogan: "Decaf-TS: Brewed for calm code.",
1838
- Tags: "Coffee-themed, Branding",
1839
- },
1840
- {
1841
- Slogan: "Smooth as your morning Decaf.",
1842
- Tags: "Coffee-themed, Chill",
1843
- },
1844
- {
1845
- Slogan: "All the kick, none of the crash.",
1846
- Tags: "Coffee-themed, Energetic",
1847
- },
1848
- {
1849
- Slogan: "Sip back and ship faster.",
1850
- Tags: "Coffee-themed, Fun",
1851
- },
1852
- {
1853
- Slogan: "Keep calm and code Decaf.",
1854
- Tags: "Coffee-themed, Playful",
1855
- },
1856
- {
1857
- Slogan: "Code without the caffeine shakes.",
1858
- Tags: "Coffee-themed, Humorous",
1859
- },
1860
- {
1861
- Slogan: "Your fullstack, decaffeinated.",
1862
- Tags: "Coffee-themed, Technical",
1863
- },
1864
- {
1865
- Slogan: "No caffeine, no chaos. Just clean code.",
1866
- Tags: "Coffee-themed, Calm, Tech",
1867
- },
1868
- {
1869
- Slogan: "Full flavor, no jitters. That\u2019s Decaf-TS.",
1870
- Tags: "Coffee-themed, Cheerful",
1871
- },
1872
- {
1873
- Slogan: "Chill fullstack. Powered by Decaf.",
1874
- Tags: "Coffee-themed, Fun, Tech",
1875
- },
1876
- {
1877
- Slogan: "Decaf-TS: Brewed for calm code.",
1878
- Tags: "Coffee-themed, Branding",
1879
- },
1880
- {
1881
- Slogan: "Smooth as your morning Decaf.",
1882
- Tags: "Coffee-themed, Chill",
1883
- },
1884
- {
1885
- Slogan: "All the kick, none of the crash.",
1886
- Tags: "Coffee-themed, Energetic",
1887
- },
1888
- {
1889
- Slogan: "Sip back and ship faster.",
1890
- Tags: "Coffee-themed, Fun",
1891
- },
1892
- {
1893
- Slogan: "Keep calm and code Decaf.",
1894
- Tags: "Coffee-themed, Playful",
1895
- },
1896
- {
1897
- Slogan: "Code without the caffeine shakes.",
1898
- Tags: "Coffee-themed, Humorous",
1899
- },
1900
- {
1901
- Slogan: "Your fullstack, decaffeinated.",
1902
- Tags: "Coffee-themed, Technical",
1903
- },
1904
- {
1905
- Slogan: "No caffeine, no chaos. Just clean code.",
1906
- Tags: "Coffee-themed, Calm, Tech",
1907
- },
1908
- {
1909
- Slogan: "Full flavor, no jitters. That\u2019s Decaf-TS.",
1910
- Tags: "Coffee-themed, Cheerful",
1911
- },
1912
- {
1913
- Slogan: "Chill fullstack. Powered by Decaf.",
1914
- Tags: "Coffee-themed, Fun, Tech",
1915
- },
1916
- {
1917
- Slogan: "Decaf-TS: Brewed for calm code.",
1918
- Tags: "Coffee-themed, Branding",
1919
- },
1920
- {
1921
- Slogan: "Smooth as your morning Decaf.",
1922
- Tags: "Coffee-themed, Chill",
1923
- },
1924
- {
1925
- Slogan: "All the kick, none of the crash.",
1926
- Tags: "Coffee-themed, Energetic",
1927
- },
1928
- {
1929
- Slogan: "Sip back and ship faster.",
1930
- Tags: "Coffee-themed, Fun",
1931
- },
1932
- {
1933
- Slogan: "Keep calm and code Decaf.",
1934
- Tags: "Coffee-themed, Playful",
1935
- },
1936
- {
1937
- Slogan: "Code without the caffeine shakes.",
1938
- Tags: "Coffee-themed, Humorous",
1939
- },
1940
- {
1941
- Slogan: "Your fullstack, decaffeinated.",
1942
- Tags: "Coffee-themed, Technical",
1943
- },
1944
- {
1945
- Slogan: "No caffeine, no chaos. Just clean code.",
1946
- Tags: "Coffee-themed, Calm, Tech",
1947
- },
1948
- {
1949
- Slogan: "Full flavor, no jitters. That\u2019s Decaf-TS.",
1950
- Tags: "Coffee-themed, Cheerful",
1951
- },
1952
- {
1953
- Slogan: "Chill fullstack. Powered by Decaf.",
1954
- Tags: "Coffee-themed, Fun, Tech",
1955
- },
1956
- {
1957
- Slogan: "Decaf-TS: Brewed for calm code.",
1958
- Tags: "Coffee-themed, Branding",
1959
- },
1960
- {
1961
- Slogan: "Smooth as your morning Decaf.",
1962
- Tags: "Coffee-themed, Chill",
1963
- },
1964
- {
1965
- Slogan: "All the kick, none of the crash.",
1966
- Tags: "Coffee-themed, Energetic",
1967
- },
1968
- {
1969
- Slogan: "Sip back and ship faster.",
1970
- Tags: "Coffee-themed, Fun",
1971
- },
1972
- {
1973
- Slogan: "Keep calm and code Decaf.",
1974
- Tags: "Coffee-themed, Playful",
1975
- },
1976
- {
1977
- Slogan: "Code without the caffeine shakes.",
1978
- Tags: "Coffee-themed, Humorous",
1979
- },
1980
- {
1981
- Slogan: "Your fullstack, decaffeinated.",
1982
- Tags: "Coffee-themed, Technical",
1983
- },
1984
- {
1985
- Slogan: "No caffeine, no chaos. Just clean code.",
1986
- Tags: "Coffee-themed, Calm, Tech",
1987
- },
1988
- {
1989
- Slogan: "Full flavor, no jitters. That\u2019s Decaf-TS.",
1990
- Tags: "Coffee-themed, Cheerful",
1991
- },
1992
- {
1993
- Slogan: "Chill fullstack. Powered by Decaf.",
1994
- Tags: "Coffee-themed, Fun, Tech",
1995
- },
1996
- {
1997
- Slogan: "Decaf-TS: Brewed for calm code.",
1998
- Tags: "Coffee-themed, Branding",
1999
- },
2000
- {
2001
- Slogan: "Smooth as your morning Decaf.",
2002
- Tags: "Coffee-themed, Chill",
2003
- },
2004
- {
2005
- Slogan: "All the kick, none of the crash.",
2006
- Tags: "Coffee-themed, Energetic",
2007
- },
2008
- {
2009
- Slogan: "Sip back and ship faster.",
2010
- Tags: "Coffee-themed, Fun",
2011
- },
2012
- {
2013
- Slogan: "Keep calm and code Decaf.",
2014
- Tags: "Coffee-themed, Playful",
2015
- },
2016
- {
2017
- Slogan: "Code without the caffeine shakes.",
2018
- Tags: "Coffee-themed, Humorous",
2019
- },
2020
- {
2021
- Slogan: "Your fullstack, decaffeinated.",
2022
- Tags: "Coffee-themed, Technical",
2023
- },
2024
- {
2025
- Slogan: "No caffeine, no chaos. Just clean code.",
2026
- Tags: "Coffee-themed, Calm, Tech",
2027
- },
2028
- {
2029
- Slogan: "Full flavor, no jitters. That\u2019s Decaf-TS.",
2030
- Tags: "Coffee-themed, Cheerful",
2031
- },
2032
- {
2033
- Slogan: "Chill fullstack. Powered by Decaf.",
2034
- Tags: "Coffee-themed, Fun, Tech",
2035
- },
2036
- {
2037
- Slogan: "Decaf-TS: Brewed for calm code.",
2038
- Tags: "Coffee-themed, Branding",
2039
- },
2040
- {
2041
- Slogan: "Smooth as your morning Decaf.",
2042
- Tags: "Coffee-themed, Chill",
2043
- },
2044
- {
2045
- Slogan: "All the kick, none of the crash.",
2046
- Tags: "Coffee-themed, Energetic",
2047
- },
2048
- {
2049
- Slogan: "Sip back and ship faster.",
2050
- Tags: "Coffee-themed, Fun",
2051
- },
2052
- {
2053
- Slogan: "Keep calm and code Decaf.",
2054
- Tags: "Coffee-themed, Playful",
2055
- },
2056
- {
2057
- Slogan: "Code without the caffeine shakes.",
2058
- Tags: "Coffee-themed, Humorous",
2059
- },
2060
- {
2061
- Slogan: "Your fullstack, decaffeinated.",
2062
- Tags: "Coffee-themed, Technical",
2063
- },
2064
- {
2065
- Slogan: "Decaf-TS: Where smart contracts meet smart interfaces.",
2066
- Tags: "Blockchain, Smart Contracts, Tech",
2067
- },
2068
- {
2069
- Slogan: "Ship dApps without the stress.",
2070
- Tags: "Blockchain, Cheerful, Developer",
2071
- },
2072
- {
2073
- Slogan: "No CRUD, no problem \u2014 Decaf your data.",
2074
- Tags: "Data, No-CRUD, Chill",
2075
- },
2076
- {
2077
- Slogan: "From DID to UI, without breaking a sweat.",
2078
- Tags: "DID, SSI, UI, Calm",
2079
- },
2080
- {
2081
- Slogan: "Decaf-TS: Your frontend already understands your smart contract.",
2082
- Tags: "Smart Contracts, DX, Magic",
2083
- },
2084
- {
2085
- Slogan: "Self-sovereign by design. Productive by default.",
2086
- Tags: "SSI, Developer, Calm",
2087
- },
2088
- {
2089
- Slogan: "Build once. Deploy everywhere. Decentralized and delightful.",
2090
- Tags: "Blockchain, Multi-platform, Happy",
2091
- },
2092
- {
2093
- Slogan: "Data that defines its own destiny.",
2094
- Tags: "SSI, Data-driven, Empowerment",
2095
- },
2096
- {
2097
- Slogan: "Goodbye CRUD, hello intent-based interfaces.",
2098
- Tags: "No-CRUD, UI, Technical",
2099
- },
2100
- {
2101
- Slogan: "The smoothest path from DID to done.",
2102
- Tags: "DID, Workflow, Chill",
2103
- },
2104
- {
2105
- Slogan: "Because your dApp deserves more than boilerplate.",
2106
- Tags: "Blockchain, DevX, Efficiency",
2107
- },
2108
- {
2109
- Slogan: "Own your data. Own your flow.",
2110
- Tags: "SSI, Control, Ownership",
2111
- },
2112
- {
2113
- Slogan: "Write logic like it belongs with the data \u2014 because it does.",
2114
- Tags: "Data Logic, Developer, Smart",
2115
- },
2116
- {
2117
- Slogan: "From smart contracts to smarter frontends.",
2118
- Tags: "Smart Contracts, UI, DX",
2119
- },
2120
- {
2121
- Slogan: "No caffeine. No CRUD. Just the future.",
2122
- Tags: "No-CRUD, Coffee-themed, Futuristic",
2123
- },
2124
- {
2125
- Slogan: "The future of web3 UX is Decaf.",
2126
- Tags: "Blockchain, UX, Vision",
2127
- },
2128
- {
2129
- Slogan: "Code with confidence. Govern with clarity.",
2130
- Tags: "Blockchain, Governance, Calm",
2131
- },
2132
- {
2133
- Slogan: "Interfaces that obey the data, not the other way around.",
2134
- Tags: "UI, Data Logic, Self-aware",
2135
- },
2136
- {
2137
- Slogan: "Brew business logic right into your bytes.",
2138
- Tags: "Data Logic, Coffee-themed, Fun",
2139
- },
2140
- {
2141
- Slogan: "DIDs done differently \u2014 and delightfully.",
2142
- Tags: "DID, Self-Sovereign, Playful",
2143
- },
2144
- {
2145
- Slogan: "Decaf-TS-TS: Where blockchain contracts meet smart interfaces.",
2146
- Tags: "Blockchain, Smart Contracts, Tech",
2147
- },
2148
- {
2149
- Slogan: "Ship dApps without the stress.",
2150
- Tags: "Blockchain, Cheerful, Developer",
2151
- },
2152
- {
2153
- Slogan: "No boilerplate, no problem \u2014 Decaf-TS your data.",
2154
- Tags: "Data, No-CRUD, Chill",
2155
- },
2156
- {
2157
- Slogan: "From DID to UI, without breaking a sweat.",
2158
- Tags: "DID, SSI, UI, Calm",
2159
- },
2160
- {
2161
- Slogan: "Decaf-TS-TS: Your frontend already understands your blockchain contract.",
2162
- Tags: "Smart Contracts, DX, Magic",
2163
- },
2164
- {
2165
- Slogan: "Self-sovereign by design. Productive by default.",
2166
- Tags: "SSI, Developer, Calm",
2167
- },
2168
- {
2169
- Slogan: "Build once. Deploy everywhere. Decentralized and delightful.",
2170
- Tags: "Blockchain, Multi-platform, Happy",
2171
- },
2172
- {
2173
- Slogan: "Data that defines its own destiny.",
2174
- Tags: "SSI, Data-driven, Empowerment",
2175
- },
2176
- {
2177
- Slogan: "Goodbye boilerplate, hello intent-based interfaces.",
2178
- Tags: "No-CRUD, UI, Technical",
2179
- },
2180
- {
2181
- Slogan: "The smoothest path from DID to done.",
2182
- Tags: "DID, Workflow, Chill",
2183
- },
2184
- {
2185
- Slogan: "Because your dApp deserves more than boilerplate.",
2186
- Tags: "Blockchain, DevX, Efficiency",
2187
- },
2188
- {
2189
- Slogan: "Own your data. Own your flow.",
2190
- Tags: "SSI, Control, Ownership",
2191
- },
2192
- {
2193
- Slogan: "Write logic like it belongs with the data \u2014 because it does.",
2194
- Tags: "Data Logic, Developer, Smart",
2195
- },
2196
- {
2197
- Slogan: "From blockchain contracts to smarter frontends.",
2198
- Tags: "Smart Contracts, UI, DX",
2199
- },
2200
- {
2201
- Slogan: "No caffeine. No boilerplate. Just the future.",
2202
- Tags: "No-CRUD, Coffee-themed, Futuristic",
2203
- },
2204
- {
2205
- Slogan: "The future of web3 UX is Decaf-TS.",
2206
- Tags: "Blockchain, UX, Vision",
2207
- },
2208
- {
2209
- Slogan: "Code with confidence. Govern with clarity.",
2210
- Tags: "Blockchain, Governance, Calm",
2211
- },
2212
- {
2213
- Slogan: "Interfaces that obey the data, not the other way around.",
2214
- Tags: "UI, Data Logic, Self-aware",
2215
- },
2216
- {
2217
- Slogan: "Brew business logic right into your bytes.",
2218
- Tags: "Data Logic, Coffee-themed, Fun",
2219
- },
2220
- {
2221
- Slogan: "DIDs done differently \u2014 and delightfully.",
2222
- Tags: "DID, Self-Sovereign, Playful",
2223
- },
2224
- {
2225
- Slogan: "Decaf-TS-TS: Where blockchain contracts meet smart interfaces.",
2226
- Tags: "Blockchain, Smart Contracts, Tech",
2227
- },
2228
- {
2229
- Slogan: "Ship dApps without the stress.",
2230
- Tags: "Blockchain, Cheerful, Developer",
2231
- },
2232
- {
2233
- Slogan: "No boilerplate, no problem \u2014 Decaf-TS your data.",
2234
- Tags: "Data, No-CRUD, Chill",
2235
- },
2236
- {
2237
- Slogan: "From DID to UI, without breaking a sweat.",
2238
- Tags: "DID, SSI, UI, Calm",
2239
- },
2240
- {
2241
- Slogan: "Decaf-TS-TS: Your frontend already understands your blockchain contract.",
2242
- Tags: "Smart Contracts, DX, Magic",
2243
- },
2244
- {
2245
- Slogan: "Self-sovereign by design. Productive by default.",
2246
- Tags: "SSI, Developer, Calm",
2247
- },
2248
- {
2249
- Slogan: "Build once. Deploy everywhere. Decentralized and delightful.",
2250
- Tags: "Blockchain, Multi-platform, Happy",
2251
- },
2252
- {
2253
- Slogan: "Data that defines its own destiny.",
2254
- Tags: "SSI, Data-driven, Empowerment",
2255
- },
2256
- {
2257
- Slogan: "Goodbye boilerplate, hello intent-based interfaces.",
2258
- Tags: "No-CRUD, UI, Technical",
2259
- },
2260
- {
2261
- Slogan: "The smoothest path from DID to done.",
2262
- Tags: "DID, Workflow, Chill",
2263
- },
2264
- {
2265
- Slogan: "Because your dApp deserves more than boilerplate.",
2266
- Tags: "Blockchain, DevX, Efficiency",
2267
- },
2268
- {
2269
- Slogan: "Own your data. Own your flow.",
2270
- Tags: "SSI, Control, Ownership",
2271
- },
2272
- {
2273
- Slogan: "Write logic like it belongs with the data \u2014 because it does.",
2274
- Tags: "Data Logic, Developer, Smart",
2275
- },
2276
- {
2277
- Slogan: "From blockchain contracts to smarter frontends.",
2278
- Tags: "Smart Contracts, UI, DX",
2279
- },
2280
- {
2281
- Slogan: "No caffeine. No boilerplate. Just the future.",
2282
- Tags: "No-CRUD, Coffee-themed, Futuristic",
2283
- },
2284
- {
2285
- Slogan: "The future of web3 UX is Decaf-TS.",
2286
- Tags: "Blockchain, UX, Vision",
2287
- },
2288
- {
2289
- Slogan: "Code with confidence. Govern with clarity.",
2290
- Tags: "Blockchain, Governance, Calm",
2291
- },
2292
- {
2293
- Slogan: "Interfaces that obey the data, not the other way around.",
2294
- Tags: "UI, Data Logic, Self-aware",
2295
- },
2296
- {
2297
- Slogan: "Brew business logic right into your bytes.",
2298
- Tags: "Data Logic, Coffee-themed, Fun",
2299
- },
2300
- {
2301
- Slogan: "DIDs done differently \u2014 and delightfully.",
2302
- Tags: "DID, Self-Sovereign, Playful",
2303
- },
2304
- {
2305
- Slogan: "Decaf-TS-TS: Where blockchain contracts meet smart interfaces.",
2306
- Tags: "Blockchain, Smart Contracts, Tech",
2307
- },
2308
- {
2309
- Slogan: "Ship dApps without the stress.",
2310
- Tags: "Blockchain, Cheerful, Developer",
2311
- },
2312
- {
2313
- Slogan: "No boilerplate, no problem \u2014 Decaf-TS your data.",
2314
- Tags: "Data, No-CRUD, Chill",
2315
- },
2316
- {
2317
- Slogan: "From DID to UI, without breaking a sweat.",
2318
- Tags: "DID, SSI, UI, Calm",
2319
- },
2320
- {
2321
- Slogan: "Decaf-TS-TS: Your frontend already understands your blockchain contract.",
2322
- Tags: "Smart Contracts, DX, Magic",
2323
- },
2324
- {
2325
- Slogan: "Self-sovereign by design. Productive by default.",
2326
- Tags: "SSI, Developer, Calm",
2327
- },
2328
- {
2329
- Slogan: "Build once. Deploy everywhere. Decentralized and delightful.",
2330
- Tags: "Blockchain, Multi-platform, Happy",
2331
- },
2332
- {
2333
- Slogan: "Data that defines its own destiny.",
2334
- Tags: "SSI, Data-driven, Empowerment",
2335
- },
2336
- {
2337
- Slogan: "Goodbye boilerplate, hello intent-based interfaces.",
2338
- Tags: "No-CRUD, UI, Technical",
2339
- },
2340
- {
2341
- Slogan: "The smoothest path from DID to done.",
2342
- Tags: "DID, Workflow, Chill",
2343
- },
2344
- {
2345
- Slogan: "Because your dApp deserves more than boilerplate.",
2346
- Tags: "Blockchain, DevX, Efficiency",
2347
- },
2348
- {
2349
- Slogan: "Own your data. Own your flow.",
2350
- Tags: "SSI, Control, Ownership",
2351
- },
2352
- {
2353
- Slogan: "Write logic like it belongs with the data \u2014 because it does.",
2354
- Tags: "Data Logic, Developer, Smart",
2355
- },
2356
- {
2357
- Slogan: "From blockchain contracts to smarter frontends.",
2358
- Tags: "Smart Contracts, UI, DX",
2359
- },
2360
- {
2361
- Slogan: "No caffeine. No boilerplate. Just the future.",
2362
- Tags: "No-CRUD, Coffee-themed, Futuristic",
2363
- },
2364
- {
2365
- Slogan: "The future of web3 UX is Decaf-TS.",
2366
- Tags: "Blockchain, UX, Vision",
2367
- },
2368
- {
2369
- Slogan: "Code with confidence. Govern with clarity.",
2370
- Tags: "Blockchain, Governance, Calm",
2371
- },
2372
- {
2373
- Slogan: "Interfaces that obey the data, not the other way around.",
2374
- Tags: "UI, Data Logic, Self-aware",
2375
- },
2376
- {
2377
- Slogan: "Brew business logic right into your bytes.",
2378
- Tags: "Data Logic, Coffee-themed, Fun",
2379
- },
2380
- {
2381
- Slogan: "DIDs done differently \u2014 and delightfully.",
2382
- Tags: "DID, Self-Sovereign, Playful",
2383
- },
2384
- {
2385
- Slogan: "Decaf-TS-TS: Where blockchain contracts meet smart interfaces.",
2386
- Tags: "Blockchain, Smart Contracts, Tech",
2387
- },
2388
- {
2389
- Slogan: "Ship dApps without the stress.",
2390
- Tags: "Blockchain, Cheerful, Developer",
2391
- },
2392
- {
2393
- Slogan: "No boilerplate, no problem \u2014 Decaf-TS your data.",
2394
- Tags: "Data, No-CRUD, Chill",
2395
- },
2396
- {
2397
- Slogan: "From DID to UI, without breaking a sweat.",
2398
- Tags: "DID, SSI, UI, Calm",
2399
- },
2400
- {
2401
- Slogan: "Decaf-TS-TS: Your frontend already understands your blockchain contract.",
2402
- Tags: "Smart Contracts, DX, Magic",
2403
- },
2404
- {
2405
- Slogan: "Self-sovereign by design. Productive by default.",
2406
- Tags: "SSI, Developer, Calm",
2407
- },
2408
- {
2409
- Slogan: "Build once. Deploy everywhere. Decentralized and delightful.",
2410
- Tags: "Blockchain, Multi-platform, Happy",
2411
- },
2412
- {
2413
- Slogan: "Data that defines its own destiny.",
2414
- Tags: "SSI, Data-driven, Empowerment",
2415
- },
2416
- {
2417
- Slogan: "Goodbye boilerplate, hello intent-based interfaces.",
2418
- Tags: "No-CRUD, UI, Technical",
2419
- },
2420
- {
2421
- Slogan: "The smoothest path from DID to done.",
2422
- Tags: "DID, Workflow, Chill",
2423
- },
2424
- {
2425
- Slogan: "Because your dApp deserves more than boilerplate.",
2426
- Tags: "Blockchain, DevX, Efficiency",
2427
- },
2428
- {
2429
- Slogan: "Own your data. Own your flow.",
2430
- Tags: "SSI, Control, Ownership",
2431
- },
2432
- {
2433
- Slogan: "Write logic like it belongs with the data \u2014 because it does.",
2434
- Tags: "Data Logic, Developer, Smart",
2435
- },
2436
- {
2437
- Slogan: "From blockchain contracts to smarter frontends.",
2438
- Tags: "Smart Contracts, UI, DX",
2439
- },
2440
- {
2441
- Slogan: "No caffeine. No boilerplate. Just the future.",
2442
- Tags: "No-CRUD, Coffee-themed, Futuristic",
2443
- },
2444
- {
2445
- Slogan: "The future of web3 UX is Decaf-TS.",
2446
- Tags: "Blockchain, UX, Vision",
2447
- },
2448
- {
2449
- Slogan: "Code with confidence. Govern with clarity.",
2450
- Tags: "Blockchain, Governance, Calm",
2451
- },
2452
- {
2453
- Slogan: "Interfaces that obey the data, not the other way around.",
2454
- Tags: "UI, Data Logic, Self-aware",
2455
- },
2456
- {
2457
- Slogan: "Brew business logic right into your bytes.",
2458
- Tags: "Data Logic, Coffee-themed, Fun",
2459
- },
2460
- {
2461
- Slogan: "DIDs done differently \u2014 and delightfully.",
2462
- Tags: "DID, Self-Sovereign, Playful",
2463
- },
2464
- ];
2465
-
2466
- /**
2467
- * @description Array of ANSI color codes for banner styling.
2468
- * @summary Defines a set of ANSI color codes used to style the banner text.
2469
- * @memberOf module:utils
2470
- */
2471
- const colors = [
2472
- "\x1b[38;5;215m", // soft orange
2473
- "\x1b[38;5;209m", // coral
2474
- "\x1b[38;5;205m", // pink
2475
- "\x1b[38;5;210m", // peachy
2476
- "\x1b[38;5;217m", // salmon
2477
- "\x1b[38;5;216m", // light coral
2478
- "\x1b[38;5;224m", // light peach
2479
- "\x1b[38;5;230m", // soft cream
2480
- "\x1b[38;5;230m", // soft cream
2481
- ];
2482
- /**
2483
- * @description Prints a styled banner to the console.
2484
- * @summary Generates and prints a colorful ASCII art banner with a random slogan.
2485
- * @param {Logger} [logger] - Optional logger for verbose output.
2486
- * @memberOf module:utils
2487
- * @function printBanner
2488
- * @mermaid
2489
- * sequenceDiagram
2490
- * participant printBanner
2491
- * participant getSlogan
2492
- * participant padEnd
2493
- * participant console
2494
- * printBanner->>getSlogan: Call getSlogan()
2495
- * getSlogan-->>printBanner: Return random slogan
2496
- * printBanner->>printBanner: Create banner ASCII art
2497
- * printBanner->>printBanner: Split banner into lines
2498
- * printBanner->>printBanner: Calculate max line length
2499
- * printBanner->>padEnd: Call padEnd with slogan
2500
- * padEnd-->>printBanner: Return padded slogan line
2501
- * loop For each banner line
2502
- * printBanner->>style: Call style(line)
2503
- * style-->>printBanner: Return styled line
2504
- * printBanner->>console: Log styled line
2505
- * end
2506
- */
2507
- function printBanner(logger) {
2508
- const message = getSlogan();
2509
- const banner = `# ░▒▓███████▓▒░ ░▒▓████████▓▒░ ░▒▓██████▓▒░ ░▒▓██████▓▒░ ░▒▓████████▓▒░ ░▒▓████████▓▒░ ░▒▓███████▓▒░
2510
- # ( ( ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░
2511
- # ) ) ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░
2512
- # [=======] ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓██████▓▒░ ░▒▓█▓▒░ ░▒▓████████▓▒░ ░▒▓██████▓▒░ ░▒▓█▓▒░ ░▒▓██████▓▒░
2513
- # \`-----´ ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░
2514
- # ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░
2515
- # ░▒▓███████▓▒░ ░▒▓████████▓▒░ ░▒▓██████▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓███████▓▒░
2516
- #`.split("\n");
2517
- const maxLength = banner.reduce((max, line) => Math.max(max, line.length), 0);
2518
- banner.push(`# ${message.padStart(maxLength - 3)}`);
2519
- banner.forEach((line, index) => {
2520
- (logger ? logger.info.bind(logger) : console.log.bind(console))(style(line || "").raw(colors[index]).text);
2521
- });
2522
- }
2523
- /**
2524
- * @description Retrieves a slogan from the predefined list.
2525
- * @summary Fetches a random slogan or a specific one by index from the slogans list.
2526
- * @param {number} [i] - Optional index to retrieve a specific slogan.
2527
- * @return {string} The selected slogan.
2528
- * @function getSlogan
2529
- * @memberOf module:utils
2530
- * @mermaid
2531
- * sequenceDiagram
2532
- * participant getSlogan
2533
- * participant Math.random
2534
- * participant slogans
2535
- * alt i is undefined
2536
- * getSlogan->>Math.random: Generate random index
2537
- * Math.random-->>getSlogan: Return random index
2538
- * else i is defined
2539
- * Note over getSlogan: Use provided index
2540
- * end
2541
- * getSlogan->>slogans: Access slogan at index
2542
- * slogans-->>getSlogan: Return slogan
2543
- * alt Error occurs
2544
- * getSlogan->>getSlogan: Throw error
2545
- * end
2546
- * getSlogan-->>Caller: Return slogan
2547
- */
2548
- function getSlogan(i) {
2549
- try {
2550
- i =
2551
- typeof i === "undefined" ? Math.floor(Math.random() * slogans.length) : i;
2552
- return slogans[i].Slogan;
2553
- }
2554
- catch (error) {
2555
- throw new Error(`Failed to retrieve slogans: ${error}`);
2556
- }
2557
- }
2558
-
2559
- /**
2560
- * @class Command
2561
- * @abstract
2562
- * @template I - The type of input options for the command.
2563
- * @template R - The return type of the command execution.
2564
- * @memberOf module:utils
2565
- * @description Abstract base class for command implementation.
2566
- * @summary Provides a structure for creating command-line interface commands with input handling, logging, and execution flow.
2567
- *
2568
- * @param {string} name - The name of the command.
2569
- * @param {CommandOptions<I>} [inputs] - The input options for the command.
2570
- * @param {string[]} [requirements] - The list of required dependencies for the command.
2571
- */
2572
- class Command extends LoggedClass {
2573
- constructor(name, inputs = {}, requirements = []) {
2574
- super();
2575
- this.name = name;
2576
- this.inputs = inputs;
2577
- this.requirements = requirements;
2578
- if (!Command.log) {
2579
- Object.defineProperty(Command, "log", {
2580
- writable: false,
2581
- value: Logging.for(Command.name),
2582
- });
2583
- }
2584
- this.inputs = Object.assign({}, DefaultCommandOptions, inputs);
2585
- }
2586
- /**
2587
- * @protected
2588
- * @async
2589
- * @description Checks if all required dependencies are present.
2590
- * @summary Retrieves the list of dependencies and compares it against the required dependencies for the command.
2591
- * @returns {Promise<void>} A promise that resolves when the check is complete.
2592
- *
2593
- * @mermaid
2594
- * sequenceDiagram
2595
- * participant Command
2596
- * participant getDependencies
2597
- * participant Set
2598
- * Command->>getDependencies: Call
2599
- * getDependencies-->>Command: Return {prod, dev, peer}
2600
- * Command->>Set: Create Set from prod, dev, peer
2601
- * Set-->>Command: Return unique dependencies
2602
- * Command->>Command: Compare against requirements
2603
- * alt Missing dependencies
2604
- * Command->>Command: Add to missing list
2605
- * end
2606
- * Note over Command: If missing.length > 0, handle missing dependencies
2607
- */
2608
- async checkRequirements() {
2609
- const { prod, dev, peer } = await getDependencies();
2610
- const missing = [];
2611
- const fullList = Array.from(new Set([...prod, ...dev, ...peer]).values()).map((d) => d.name);
2612
- for (const dep of this.requirements)
2613
- if (!fullList.includes(dep))
2614
- missing.push(dep);
2615
- if (!missing.length)
2616
- return;
2617
- }
2618
- /**
2619
- * @protected
2620
- * @description Provides help information for the command.
2621
- * @summary This method should be overridden in derived classes to provide specific help information.
2622
- * @param {ParseArgsResult} args - The parsed command-line arguments.
2623
- * @returns {void}
2624
- */
2625
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
2626
- help(args) {
2627
- return this.log.info(`This is help. I'm no use because I should have been overridden.`);
2628
- }
2629
- /**
2630
- * @async
2631
- * @description Executes the command.
2632
- * @summary This method handles the overall execution flow of the command, including parsing arguments,
2633
- * setting up logging, checking for version or help requests, and running the command.
2634
- * @returns {Promise<R | string | void>} A promise that resolves with the command's result.
2635
- *
2636
- * @mermaid
2637
- * sequenceDiagram
2638
- * participant Command
2639
- * participant UserInput
2640
- * participant Logging
2641
- * participant getPackageVersion
2642
- * participant printBanner
2643
- * Command->>UserInput: parseArgs(inputs)
2644
- * UserInput-->>Command: Return ParseArgsResult
2645
- * Command->>Command: Process options
2646
- * Command->>Logging: setConfig(options)
2647
- * alt version requested
2648
- * Command->>getPackageVersion: Call
2649
- * getPackageVersion-->>Command: Return version
2650
- * else help requested
2651
- * Command->>Command: help(args)
2652
- * else banner requested
2653
- * Command->>printBanner: Call
2654
- * end
2655
- * Command->>Command: run(args)
2656
- * alt error occurs
2657
- * Command->>Command: Log error
2658
- * end
2659
- * Command-->>Command: Return result
2660
- */
2661
- async execute() {
2662
- const args = UserInput.parseArgs(this.inputs);
2663
- const env = LoggedEnvironment.accumulate(DefaultCommandValues).accumulate(args.values);
2664
- const { version, help, banner } = env;
2665
- if (version) {
2666
- return getPackageVersion();
2667
- }
2668
- if (help) {
2669
- return this.help(args);
2670
- }
2671
- if (banner)
2672
- printBanner(this.log.for(printBanner, {
2673
- timestamp: false,
2674
- style: false,
2675
- context: false,
2676
- logLevel: false,
2677
- }));
2678
- let result;
2679
- try {
2680
- result = await this.run(env);
2681
- }
2682
- catch (e) {
2683
- this.log.error(`Error while running provided cli function: ${e}`);
2684
- throw e;
2685
- }
2686
- return result;
2687
- }
2688
- }
2689
-
2690
- /* istanbul ignore file */
2691
- /**
2692
- * @description A simple HTTP client for downloading files.
2693
- * @summary This class provides functionality to download files from HTTPS URLs.
2694
- * It uses Node.js built-in https module to make requests.
2695
- *
2696
- * @class HttpClient
2697
- */
2698
- class HttpClient {
2699
- static { this.log = Logging.for(HttpClient); }
2700
- /**
2701
- * @description Downloads a file from a given URL.
2702
- * @summary This method sends a GET request to the specified URL and returns the response body as a string.
2703
- * It handles different scenarios such as non-200 status codes and network errors.
2704
- *
2705
- * @param url - The URL of the file to download.
2706
- * @return A promise that resolves with the file content as a string.
2707
- *
2708
- * @mermaid
2709
- * sequenceDiagram
2710
- * participant Client
2711
- * participant HttpClient
2712
- * participant HTTPS
2713
- * participant Server
2714
- * Client->>HttpClient: downloadFile(url)
2715
- * HttpClient->>HTTPS: get(url)
2716
- * HTTPS->>Server: GET request
2717
- * Server-->>HTTPS: Response
2718
- * HTTPS-->>HttpClient: Response object
2719
- * alt Status code is 200
2720
- * loop For each data chunk
2721
- * HTTPS->>HttpClient: 'data' event
2722
- * HttpClient->>HttpClient: Accumulate data
2723
- * end
2724
- * HTTPS->>HttpClient: 'end' event
2725
- * HttpClient-->>Client: Resolve with data
2726
- * else Status code is not 200
2727
- * HttpClient-->>Client: Reject with error
2728
- * end
2729
- */
2730
- static async downloadFile(url) {
2731
- return new Promise((resolve, reject) => {
2732
- function request(url) {
2733
- url = encodeURI(url);
2734
- https.get(url, (res) => {
2735
- if (res.statusCode === 301 || res.statusCode === 307)
2736
- return request(res.headers.location);
2737
- if (res.statusCode !== 200) {
2738
- HttpClient.log.error(`Failed to fetch ${url} (status: ${res.statusCode})`);
2739
- return reject(new Error(`Failed to fetch ${url}`));
2740
- }
2741
- let data = "";
2742
- res.on("data", (chunk) => {
2743
- data += chunk;
2744
- });
2745
- res.on("error", (error) => {
2746
- reject(error);
2747
- });
2748
- res.on("end", () => {
2749
- resolve(data);
2750
- });
2751
- });
2752
- }
2753
- request(url);
2754
- });
2755
- }
2756
- }
2757
-
2758
- function parseList(input) {
2759
- if (!input)
2760
- return [];
2761
- if (Array.isArray(input))
2762
- return input.map((i) => `${i}`.trim()).filter(Boolean);
2763
- return `${input}`
2764
- .split(",")
2765
- .map((p) => p.trim())
2766
- .filter(Boolean);
2767
- }
2768
- function packageToGlobal(name) {
2769
- // Remove scope and split by non-alphanumeric chars, then camelCase
2770
- const withoutScope = name.replace(/^@/, "");
2771
- const parts = withoutScope.split(/[/\-_.]+/).filter(Boolean);
2772
- return parts
2773
- .map((p, i) => i === 0
2774
- ? p.replace(/[^a-zA-Z0-9]/g, "")
2775
- : `${p.charAt(0).toUpperCase()}${p.slice(1)}`)
2776
- .join("");
2777
- }
2778
- function getPackageDependencies() {
2779
- // Try the current working directory first
2780
- let pkg;
2781
- try {
2782
- pkg = getPackage(process.cwd());
2783
- }
2784
- catch {
2785
- pkg = undefined;
2786
- }
2787
- // If no dependencies found in cwd, try the package next to this source file (fallback for tests)
2788
- try {
2789
- const hasDeps = pkg &&
2790
- (Object.keys(pkg.dependencies || {}).length > 0 ||
2791
- Object.keys(pkg.devDependencies || {}).length > 0 ||
2792
- Object.keys(pkg.peerDependencies || {}).length > 0);
2793
- if (!hasDeps) {
2794
- const fallbackDir = path.resolve(__dirname, "../../..");
2795
- try {
2796
- pkg = getPackage(fallbackDir);
2797
- }
2798
- catch {
2799
- // ignore and keep pkg as-is
2800
- }
2801
- }
2802
- }
2803
- catch {
2804
- // ignore
2805
- }
2806
- const deps = Object.keys((pkg && pkg.dependencies) || {});
2807
- const peer = Object.keys((pkg && pkg.peerDependencies) || {});
2808
- const dev = Object.keys((pkg && pkg.devDependencies) || {});
2809
- return Array.from(new Set([...deps, ...peer, ...dev]));
2810
- }
2811
- const VERSION_STRING = "0.4.1";
2812
- const PACKAGE_STRING = "@decaf-ts/utils";
2813
- var Modes;
2814
- (function (Modes) {
2815
- Modes["CJS"] = "commonjs";
2816
- Modes["ESM"] = "es2022";
2817
- })(Modes || (Modes = {}));
2818
- var BuildMode;
2819
- (function (BuildMode) {
2820
- BuildMode["BUILD"] = "build";
2821
- BuildMode["BUNDLE"] = "bundle";
2822
- BuildMode["ALL"] = "all";
2823
- })(BuildMode || (BuildMode = {}));
2824
- const options$2 = {
2825
- prod: {
2826
- type: "boolean",
2827
- default: false,
2828
- },
2829
- dev: {
2830
- type: "boolean",
2831
- default: false,
2832
- },
2833
- buildMode: {
2834
- type: "string",
2835
- default: BuildMode.ALL,
2836
- },
2837
- includes: {
2838
- type: "string",
2839
- default: "",
2840
- },
2841
- externals: {
2842
- type: "string",
2843
- default: "",
2844
- },
2845
- docs: {
2846
- type: "boolean",
2847
- default: false,
2848
- },
2849
- commands: {
2850
- type: "boolean",
2851
- default: false,
2852
- },
2853
- banner: {
2854
- type: "boolean",
2855
- default: false,
2856
- },
2857
- };
2858
- const cjs2Transformer = (ext = ".cjs") => {
2859
- const log = BuildScripts.log.for(cjs2Transformer);
2860
- const resolutionCache = new Map();
2861
- return (transformationContext) => {
2862
- return (sourceFile) => {
2863
- const sourceDir = path.dirname(sourceFile.fileName);
2864
- function resolvePath(importPath) {
2865
- const cacheKey = JSON.stringify([sourceDir, importPath]);
2866
- const cachedValue = resolutionCache.get(cacheKey);
2867
- if (cachedValue != null)
2868
- return cachedValue;
2869
- let resolvedPath = importPath;
2870
- try {
2871
- resolvedPath = path.resolve(sourceDir, resolvedPath + ".ts");
2872
- }
2873
- catch (error) {
2874
- throw new Error(`Failed to resolve path ${importPath}: ${error}`);
2875
- }
2876
- let stat;
2877
- try {
2878
- stat = fs.statSync(resolvedPath);
2879
- }
2880
- catch (e) {
2881
- try {
2882
- log.verbose(`Testing existence of path ${resolvedPath} as a folder defaulting to index file`);
2883
- stat = fs.statSync(resolvedPath.replace(/\.ts$/gm, ""));
2884
- }
2885
- catch (e2) {
2886
- throw new Error(`Failed to resolve path ${importPath}: ${e}, ${e2}`);
2887
- }
2888
- }
2889
- if (stat.isDirectory())
2890
- resolvedPath = resolvedPath.replace(/\.ts$/gm, "/index.ts");
2891
- if (path.isAbsolute(resolvedPath)) {
2892
- const extension = (/\.tsx?$/.exec(path.basename(resolvedPath)) || [])[0] || void 0;
2893
- resolvedPath =
2894
- "./" +
2895
- path.relative(sourceDir, path.resolve(path.dirname(resolvedPath), path.basename(resolvedPath, extension) + ext));
2896
- }
2897
- resolutionCache.set(cacheKey, resolvedPath);
2898
- return resolvedPath;
2899
- }
2900
- function visitNode(node) {
2901
- if (shouldMutateModuleSpecifier(node)) {
2902
- if (ts.isImportDeclaration(node)) {
2903
- const resolvedPath = resolvePath(node.moduleSpecifier.text);
2904
- const newModuleSpecifier = transformationContext.factory.createStringLiteral(resolvedPath);
2905
- return transformationContext.factory.updateImportDeclaration(node, node.modifiers, node.importClause, newModuleSpecifier, undefined);
2906
- }
2907
- else if (ts.isExportDeclaration(node)) {
2908
- const resolvedPath = resolvePath(node.moduleSpecifier.text);
2909
- const newModuleSpecifier = transformationContext.factory.createStringLiteral(resolvedPath);
2910
- return transformationContext.factory.updateExportDeclaration(node, node.modifiers, node.isTypeOnly, node.exportClause, newModuleSpecifier, undefined);
2911
- }
2912
- }
2913
- return ts.visitEachChild(node, visitNode, transformationContext);
2914
- }
2915
- function shouldMutateModuleSpecifier(node) {
2916
- if (!ts.isImportDeclaration(node) && !ts.isExportDeclaration(node))
2917
- return false;
2918
- if (node.moduleSpecifier === undefined)
2919
- return false;
2920
- // only when module specifier is valid
2921
- if (!ts.isStringLiteral(node.moduleSpecifier))
2922
- return false;
2923
- // only when path is relative
2924
- if (!node.moduleSpecifier.text.startsWith("./") &&
2925
- !node.moduleSpecifier.text.startsWith("../"))
2926
- return false;
2927
- // only when module specifier has no extension
2928
- if (path.extname(node.moduleSpecifier.text) !== "")
2929
- return false;
2930
- return true;
2931
- }
2932
- return ts.visitNode(sourceFile, visitNode);
2933
- };
2934
- };
2935
- };
2936
- class BuildScripts extends Command {
2937
- constructor() {
2938
- super("BuildScripts", Object.assign({}, DefaultCommandOptions, options$2));
2939
- this.replacements = {};
2940
- const pkg = getPackage();
2941
- const { name, version } = pkg;
2942
- this.pkgName = name.includes("@") ? name.split("/")[1] : name;
2943
- this.pkgVersion = version;
2944
- this.replacements[VERSION_STRING] = this.pkgVersion;
2945
- this.replacements[PACKAGE_STRING] = name;
2946
- }
2947
- patchFiles(p) {
2948
- const log = this.log.for(this.patchFiles);
2949
- const { name, version } = getPackage();
2950
- log.info(`Patching ${name} ${version} module in ${p}...`);
2951
- const stat = fs.statSync(p);
2952
- if (stat.isDirectory())
2953
- fs.readdirSync(p, { withFileTypes: true, recursive: true })
2954
- .filter((p) => p.isFile())
2955
- .forEach((file) => patchFile(path.join(file.parentPath, file.name), this.replacements));
2956
- log.verbose(`Module ${name} ${version} patched in ${p}...`);
2957
- }
2958
- reportDiagnostics(diagnostics, logLevel) {
2959
- const msg = this.formatDiagnostics(diagnostics);
2960
- if (!Object.values(LogLevel).includes(logLevel))
2961
- throw new Error(`Invalid LogLevel ${logLevel}`);
2962
- try {
2963
- this.log[logLevel](msg);
2964
- }
2965
- catch (e) {
2966
- console.warn(`Failed to get logger for ${logLevel}`);
2967
- throw e;
2968
- }
2969
- return msg;
2970
- }
2971
- // Format diagnostics into a single string for throwing or logging
2972
- formatDiagnostics(diagnostics) {
2973
- return diagnostics
2974
- .map((diagnostic) => {
2975
- let message = "";
2976
- if (diagnostic.file && diagnostic.start) {
2977
- const { line, character } = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start);
2978
- message += `${diagnostic.file.fileName} (${line + 1},${character + 1})`;
2979
- }
2980
- message +=
2981
- ": " + ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n");
2982
- return message;
2983
- })
2984
- .join("\n");
2985
- }
2986
- readConfigFile(configFileName) {
2987
- // Read config file
2988
- const configFileText = fs.readFileSync(configFileName).toString();
2989
- // Parse JSON, after removing comments. Just fancier JSON.parse
2990
- const result = ts.parseConfigFileTextToJson(configFileName, configFileText);
2991
- const configObject = result.config;
2992
- if (!configObject) {
2993
- this.reportDiagnostics([result.error], LogLevel.error);
2994
- }
2995
- // Extract config infromation
2996
- const configParseResult = ts.parseJsonConfigFileContent(configObject, ts.sys, path.dirname(configFileName));
2997
- if (configParseResult.errors.length > 0)
2998
- this.reportDiagnostics(configParseResult.errors, LogLevel.error);
2999
- return configParseResult;
3000
- }
3001
- evalDiagnostics(diagnostics) {
3002
- if (diagnostics && diagnostics.length > 0) {
3003
- const errors = diagnostics.filter((d) => d.category === ts.DiagnosticCategory.Error);
3004
- const warnings = diagnostics.filter((d) => d.category === ts.DiagnosticCategory.Warning);
3005
- const suggestions = diagnostics.filter((d) => d.category === ts.DiagnosticCategory.Suggestion);
3006
- const messages = diagnostics.filter((d) => d.category === ts.DiagnosticCategory.Message);
3007
- // Log diagnostics to console
3008
- if (warnings.length)
3009
- this.reportDiagnostics(warnings, LogLevel.info);
3010
- if (errors.length) {
3011
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
3012
- this.reportDiagnostics(diagnostics, LogLevel.error);
3013
- this.log.info(`TypeScript reported ${diagnostics.length} diagnostic(s) during check; aborting.`);
3014
- // throw new Error(
3015
- // `TypeScript reported ${diagnostics.length} diagnostic(s) during check; aborting.`
3016
- // );
3017
- }
3018
- if (suggestions.length)
3019
- this.reportDiagnostics(suggestions, LogLevel.info);
3020
- if (messages.length)
3021
- this.reportDiagnostics(messages, LogLevel.info);
3022
- }
3023
- }
3024
- preCheckDiagnostics(program) {
3025
- const diagnostics = ts.getPreEmitDiagnostics(program);
3026
- this.evalDiagnostics(diagnostics);
3027
- }
3028
- // Create a TypeScript program for the current tsconfig and fail if there are any error diagnostics.
3029
- async checkTsDiagnostics(isDev, mode, bundle = false) {
3030
- const log = this.log.for(this.checkTsDiagnostics);
3031
- let tsConfig;
3032
- try {
3033
- tsConfig = this.readConfigFile("./tsconfig.json");
3034
- }
3035
- catch (e) {
3036
- throw new Error(`Failed to parse tsconfig.json: ${e}`);
3037
- }
3038
- if (bundle) {
3039
- tsConfig.options.module = ModuleKind.AMD;
3040
- tsConfig.options.outDir = "dist";
3041
- tsConfig.options.isolatedModules = false;
3042
- tsConfig.options.outFile = this.pkgName;
3043
- }
3044
- else {
3045
- tsConfig.options.outDir = `lib${mode === Modes.ESM ? "/esm" : ""}`;
3046
- tsConfig.options.module =
3047
- mode === Modes.ESM ? ModuleKind.ES2022 : ModuleKind.CommonJS;
3048
- }
3049
- if (isDev) {
3050
- tsConfig.options.inlineSourceMap = true;
3051
- tsConfig.options.sourceMap = false;
3052
- }
3053
- else {
3054
- tsConfig.options.sourceMap = false;
3055
- }
3056
- const program = ts.createProgram(tsConfig.fileNames, tsConfig.options);
3057
- this.preCheckDiagnostics(program);
3058
- log.verbose(`TypeScript checks passed (${bundle ? "bundle" : "normal"} mode).`);
3059
- }
3060
- async buildTs(isDev, mode, bundle = false) {
3061
- const log = this.log.for(this.buildTs);
3062
- log.info(`Building ${this.pkgName} ${this.pkgVersion} module (${mode}) in ${isDev ? "dev" : "prod"} mode...`);
3063
- let tsConfig;
3064
- try {
3065
- tsConfig = this.readConfigFile("./tsconfig.json");
3066
- }
3067
- catch (e) {
3068
- throw new Error(`Failed to parse tsconfig.json: ${e}`);
3069
- }
3070
- if (bundle) {
3071
- tsConfig.options.module = ModuleKind.AMD;
3072
- tsConfig.options.outDir = "dist";
3073
- tsConfig.options.isolatedModules = false;
3074
- tsConfig.options.outFile = this.pkgName;
3075
- }
3076
- else {
3077
- tsConfig.options.outDir = `lib${mode === Modes.ESM ? "/esm" : ""}`;
3078
- tsConfig.options.module =
3079
- mode === Modes.ESM ? ModuleKind.ES2022 : ModuleKind.CommonJS;
3080
- }
3081
- if (isDev) {
3082
- tsConfig.options.inlineSourceMap = true;
3083
- tsConfig.options.sourceMap = false;
3084
- }
3085
- else {
3086
- tsConfig.options.sourceMap = false;
3087
- }
3088
- const program = ts.createProgram(tsConfig.fileNames, tsConfig.options);
3089
- const transformations = {};
3090
- if (mode === Modes.CJS) {
3091
- transformations.before = [cjs2Transformer(".cjs")];
3092
- }
3093
- else if (mode === Modes.ESM) {
3094
- transformations.before = [cjs2Transformer(".js")];
3095
- }
3096
- const emitResult = program.emit(undefined, undefined, undefined, undefined, transformations);
3097
- const allDiagnostics = ts
3098
- .getPreEmitDiagnostics(program)
3099
- .concat(emitResult.diagnostics);
3100
- this.evalDiagnostics(allDiagnostics);
3101
- }
3102
- async build(isDev, mode, bundle = false) {
3103
- const log = this.log.for(this.build);
3104
- await this.buildTs(isDev, mode, bundle);
3105
- log.verbose(`Module ${this.pkgName} ${this.pkgVersion} (${mode}) built in ${isDev ? "dev" : "prod"} mode...`);
3106
- if (mode === Modes.CJS && !bundle) {
3107
- const files = getAllFiles("lib", (file) => file.endsWith(".js") && !file.includes("/esm/"));
3108
- for (const file of files) {
3109
- log.verbose(`Patching ${file}'s cjs imports...`);
3110
- const f = file.replace(".js", ".cjs");
3111
- await renameFile(file, f);
3112
- }
3113
- }
3114
- }
3115
- copyAssets(mode) {
3116
- const log = this.log.for(this.copyAssets);
3117
- let hasAssets = false;
3118
- try {
3119
- hasAssets = fs.statSync("./src/assets").isDirectory();
3120
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
3121
- }
3122
- catch (e) {
3123
- return log.verbose(`No assets found in ./src/assets to copy`);
3124
- }
3125
- if (hasAssets)
3126
- copyFile("./src/assets", `./${mode === Modes.CJS ? "lib" : "dist"}/assets`);
3127
- }
3128
- async bundle(mode, isDev, isLib, entryFile = "src/index.ts", nameOverride = this.pkgName, externalsArg, includeArg = [
3129
- "prompts",
3130
- "styled-string-builder",
3131
- "typed-object-accumulator",
3132
- "@decaf-ts/logging",
3133
- ]) {
3134
- // Run a TypeScript-only diagnostic check for the bundling configuration and fail fast on any errors.
3135
- await this.checkTsDiagnostics(isDev, mode, true);
3136
- const isEsm = mode === Modes.ESM;
3137
- const pkgName = this.pkgName;
3138
- // normalize include and externals
3139
- const include = Array.from(new Set([...parseList(includeArg)]));
3140
- let externalsList = parseList(externalsArg);
3141
- if (externalsList.length === 0) {
3142
- // if no externals specified, include package.json dependencies to avoid rollup treating them as resolvable
3143
- externalsList = getPackageDependencies();
3144
- }
3145
- const ext = Array.from(new Set([
3146
- // builtins and always external runtime deps
3147
- ...(function builtinList() {
3148
- try {
3149
- return (Array.isArray(builtinModules) ? builtinModules : []);
3150
- }
3151
- catch {
3152
- // fallback to a reasonable subset if `builtinModules` is unavailable
3153
- return [
3154
- "fs",
3155
- "path",
3156
- "process",
3157
- "child_process",
3158
- "util",
3159
- "https",
3160
- "http",
3161
- "os",
3162
- "stream",
3163
- "crypto",
3164
- "zlib",
3165
- "net",
3166
- "tls",
3167
- "url",
3168
- "querystring",
3169
- "assert",
3170
- "events",
3171
- "tty",
3172
- "dns",
3173
- "querystring",
3174
- ];
3175
- }
3176
- })(),
3177
- ...externalsList,
3178
- ]));
3179
- const plugins = [
3180
- typescript({
3181
- compilerOptions: {
3182
- module: "esnext",
3183
- declaration: false,
3184
- outDir: isLib ? "bin" : "dist",
3185
- },
3186
- include: ["src/**/*.ts"],
3187
- exclude: ["node_modules", "**/*.spec.ts"],
3188
- tsconfig: "./tsconfig.json",
3189
- }),
3190
- json(),
3191
- ];
3192
- // production minification
3193
- if (!isDev) {
3194
- // terser is optional at runtime; import lazily inside bundle to avoid test-time resolution errors
3195
- try {
3196
- const terserMod = await import('@rollup/plugin-terser');
3197
- const terserFn = (terserMod && terserMod.terser) || terserMod.default || terserMod;
3198
- plugins.push(terserFn());
3199
- }
3200
- catch {
3201
- // if terser isn't available, ignore
3202
- }
3203
- }
3204
- if (isLib) {
3205
- plugins.push(commonjs({
3206
- include: [],
3207
- exclude: externalsList,
3208
- }), nodeResolve({
3209
- resolveOnly: include,
3210
- }));
3211
- }
3212
- const input = {
3213
- input: entryFile,
3214
- plugins: plugins,
3215
- external: ext,
3216
- };
3217
- // prepare output globals mapping for externals
3218
- const globals = {};
3219
- // include all externals and builtins (ext) so Rollup won't guess names for builtins
3220
- ext.forEach((e) => {
3221
- globals[e] = packageToGlobal(e);
3222
- });
3223
- const outputs = [
3224
- {
3225
- file: `${isLib ? "bin/" : "dist/"}${nameOverride ? nameOverride : `.bundle.${!isDev ? "min" : ""}`}${isEsm ? ".esm" : ""}.cjs`,
3226
- format: isLib ? "cjs" : isEsm ? "esm" : "umd",
3227
- name: pkgName,
3228
- esModule: isEsm,
3229
- sourcemap: isDev ? "inline" : false,
3230
- globals: globals,
3231
- exports: "auto",
3232
- },
3233
- ];
3234
- try {
3235
- const bundle = await rollup(input);
3236
- console.log(bundle.watchFiles);
3237
- async function generateOutputs(bundle) {
3238
- for (const outputOptions of outputs) {
3239
- await bundle.write(outputOptions);
3240
- }
3241
- }
3242
- await generateOutputs(bundle);
3243
- }
3244
- catch (e) {
3245
- throw new Error(`Failed to bundle: ${e}`);
3246
- }
3247
- }
3248
- async buildByEnv(isDev, mode = BuildMode.ALL, includesArg, externalsArg) {
3249
- // note: includes and externals will be passed through from run() into this method by callers
3250
- try {
3251
- deletePath("lib");
3252
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
3253
- }
3254
- catch (e) {
3255
- // do nothing
3256
- }
3257
- try {
3258
- deletePath("dist");
3259
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
3260
- }
3261
- catch (e) {
3262
- // do nothing
3263
- }
3264
- fs.mkdirSync("lib");
3265
- fs.mkdirSync("dist");
3266
- if ([BuildMode.ALL, BuildMode.BUILD].includes(mode)) {
3267
- await this.build(isDev, Modes.ESM);
3268
- await this.build(isDev, Modes.CJS);
3269
- this.patchFiles("lib");
3270
- }
3271
- if ([BuildMode.ALL, BuildMode.BUNDLE].includes(mode)) {
3272
- await this.bundle(Modes.ESM, isDev, false, "src/index.ts", this.pkgName, externalsArg, includesArg);
3273
- await this.bundle(Modes.CJS, isDev, false, "src/index.ts", this.pkgName, externalsArg, includesArg);
3274
- this.patchFiles("dist");
3275
- }
3276
- this.copyAssets(Modes.CJS);
3277
- this.copyAssets(Modes.ESM);
3278
- }
3279
- async buildDev(mode = BuildMode.ALL, includesArg, externalsArg) {
3280
- return this.buildByEnv(true, mode, includesArg, externalsArg);
3281
- }
3282
- async buildProd(mode = BuildMode.ALL, includesArg, externalsArg) {
3283
- return this.buildByEnv(false, mode, includesArg, externalsArg);
3284
- }
3285
- async buildDocs() {
3286
- await runCommand(`npm install better-docs taffydb`).promise;
3287
- await runCommand(`npx markdown-include ./workdocs/readme-md.json`).promise;
3288
- await runCommand(`npx jsdoc -c ./workdocs/jsdocs.json -t ./node_modules/better-docs`).promise;
3289
- await runCommand(`npm remove better-docs taffydb`).promise;
3290
- [
3291
- {
3292
- src: "workdocs/assets",
3293
- dest: "./docs/workdocs/assets",
3294
- },
3295
- {
3296
- src: "workdocs/reports/coverage",
3297
- dest: "./docs/workdocs/reports/coverage",
3298
- },
3299
- {
3300
- src: "workdocs/reports/html",
3301
- dest: "./docs/workdocs/reports/html",
3302
- },
3303
- {
3304
- src: "workdocs/resources",
3305
- dest: "./docs/workdocs/resources",
3306
- },
3307
- {
3308
- src: "LICENSE.md",
3309
- dest: "./docs/LICENSE.md",
3310
- },
3311
- ].forEach((f) => {
3312
- const { src, dest } = f;
3313
- copyFile(src, dest);
3314
- });
3315
- }
3316
- async run(answers) {
3317
- const { dev, prod, docs, buildMode, includes, externals } = answers;
3318
- if (dev) {
3319
- return await this.buildDev(buildMode, includes, externals);
3320
- }
3321
- if (prod) {
3322
- return await this.buildProd(buildMode, includes, externals);
3323
- }
3324
- if (docs) {
3325
- return await this.buildDocs();
3326
- }
3327
- }
3328
- }
3329
-
3330
- /* istanbul ignore file */
3331
- const options$1 = {
3332
- ci: {
3333
- type: "boolean",
3334
- default: true,
3335
- },
3336
- message: {
3337
- type: "string",
3338
- short: "m",
3339
- },
3340
- tag: {
3341
- type: "string",
3342
- short: "t",
3343
- default: undefined,
3344
- },
3345
- };
3346
- /**
3347
- * @class ReleaseScript
3348
- * @extends {Command}
3349
- * @cavegory scripts
3350
- * @description A command-line script for managing releases and version updates.
3351
- * @summary This script automates the process of creating and pushing new releases. It handles version updates,
3352
- * commit messages, and optionally publishes to NPM. The script supports semantic versioning and can work in both CI and non-CI environments.
3353
- *
3354
- * @param {Object} options - Configuration options for the script
3355
- * @param {boolean} options.ci - Whether the script is running in a CI environment (default: true)
3356
- * @param {string} options.message - The release message (short: 'm')
3357
- * @param {string} options.tag - The version tag to use (short: 't', default: undefined)
3358
- */
3359
- class ReleaseScript extends Command {
3360
- constructor() {
3361
- super("ReleaseScript", options$1);
3362
- }
3363
- /**
3364
- * @description Prepares the version for the release.
3365
- * @summary This method validates the provided tag or prompts the user for a new one if not provided or invalid.
3366
- * It also displays the latest git tags for reference.
3367
- * @param {string} tag - The version tag to prepare
3368
- * @returns {Promise<string>} The prepared version tag
3369
- *
3370
- * @mermaid
3371
- * sequenceDiagram
3372
- * participant R as ReleaseScript
3373
- * participant T as TestVersion
3374
- * participant U as UserInput
3375
- * participant G as Git
3376
- * R->>T: testVersion(tag)
3377
- * alt tag is valid
3378
- * T-->>R: return tag
3379
- * else tag is invalid or not provided
3380
- * R->>G: List latest git tags
3381
- * R->>U: Prompt for new tag
3382
- * U-->>R: return new tag
3383
- * end
3384
- */
3385
- async prepareVersion(tag) {
3386
- const log = this.log.for(this.prepareVersion);
3387
- tag = this.testVersion(tag || "");
3388
- if (!tag) {
3389
- log.verbose("No release message provided. Prompting for one:");
3390
- log.info(`Listing latest git tags:`);
3391
- await runCommand("git tag --sort=-taggerdate | head -n 5").promise;
3392
- return await UserInput.insistForText("tag", "Enter the new tag number (accepts v*.*.*[-...])", (val) => !!val.toString().match(/^v[0-9]+\.[0-9]+.[0-9]+(-[0-9a-zA-Z-]+)?$/));
3393
- }
3394
- return tag;
3395
- }
3396
- /**
3397
- * @description Tests if the provided version is valid.
3398
- * @summary This method checks if the version is a valid semantic version or a predefined update type (PATCH, MINOR, MAJOR).
3399
- * @param {string} version - The version to test
3400
- * @returns {string | undefined} The validated version or undefined if invalid
3401
- */
3402
- testVersion(version) {
3403
- const log = this.log.for(this.testVersion);
3404
- version = version.trim().toLowerCase();
3405
- switch (version) {
3406
- case SemVersion.PATCH:
3407
- case SemVersion.MINOR:
3408
- case SemVersion.MAJOR:
3409
- log.verbose(`Using provided SemVer update: ${version}`, 1);
3410
- return version;
3411
- default:
3412
- log.verbose(`Testing provided version for SemVer compatibility: ${version}`, 1);
3413
- if (!new RegExp(SemVersionRegex).test(version)) {
3414
- log.debug(`Invalid version number: ${version}`);
3415
- return undefined;
3416
- }
3417
- log.verbose(`version approved: ${version}`, 1);
3418
- return version;
3419
- }
3420
- }
3421
- /**
3422
- * @description Prepares the release message.
3423
- * @summary This method either returns the provided message or prompts the user for a new one if not provided.
3424
- * @param {string} [message] - The release message
3425
- * @returns {Promise<string>} The prepared release message
3426
- */
3427
- async prepareMessage(message) {
3428
- const log = this.log.for(this.prepareMessage);
3429
- if (!message) {
3430
- log.verbose("No release message provided. Prompting for one");
3431
- return await UserInput.insistForText("message", "What should be the release message/ticket?", (val) => !!val && val.toString().length > 5);
3432
- }
3433
- return message;
3434
- }
3435
- /**
3436
- * @description Runs the release script.
3437
- * @summary This method orchestrates the entire release process, including version preparation, message creation,
3438
- * git operations, and npm publishing (if not in CI environment).
3439
- * @param {ParseArgsResult} args - The parsed command-line arguments
3440
- * @returns {Promise<void>}
3441
- *
3442
- * @mermaid
3443
- * sequenceDiagram
3444
- * participant R as ReleaseScript
3445
- * participant V as PrepareVersion
3446
- * participant M as PrepareMessage
3447
- * participant N as NPM
3448
- * participant G as Git
3449
- * participant U as UserInput
3450
- * R->>V: prepareVersion(tag)
3451
- * R->>M: prepareMessage(message)
3452
- * R->>N: Run prepare-release script
3453
- * R->>G: Check git status
3454
- * alt changes exist
3455
- * R->>U: Ask for confirmation
3456
- * U-->>R: Confirm
3457
- * R->>G: Add and commit changes
3458
- * end
3459
- * R->>N: Update npm version
3460
- * R->>G: Push changes and tags
3461
- * alt not CI environment
3462
- * R->>N: Publish to npm
3463
- * end
3464
- */
3465
- async run(args) {
3466
- let result;
3467
- const { ci } = args;
3468
- let { tag, message } = args;
3469
- tag = await this.prepareVersion(tag);
3470
- message = await this.prepareMessage(message);
3471
- result = await runCommand(`npm run prepare-release -- ${tag} ${message}`, {
3472
- cwd: process.cwd(),
3473
- }).promise;
3474
- result = await runCommand("git status --porcelain").promise;
3475
- await result;
3476
- if (result.logs.length &&
3477
- (await UserInput.askConfirmation("git-changes", "Do you want to push the changes to the remote repository?", true))) {
3478
- await runCommand("git add .").promise;
3479
- await runCommand(`git commit -m "${tag} - ${message} - after release preparation${ci ? "" : NoCIFLag}"`).promise;
3480
- }
3481
- await runCommand(`npm version "${tag}" -m "${message}${ci ? "" : NoCIFLag}"`).promise;
3482
- await runCommand("git push --follow-tags").promise;
3483
- if (!ci) {
3484
- await runCommand("NPM_TOKEN=$(cat .npmtoken) npm publish --access public")
3485
- .promise;
3486
- }
3487
- }
3488
- }
3489
-
3490
- /* istanbul ignore file */
3491
- const baseUrl = "https://raw.githubusercontent.com/decaf-ts/ts-workspace/master";
3492
- const options = {
3493
- templates: [
3494
- ".github/ISSUE_TEMPLATE/bug_report.md",
3495
- ".github/ISSUE_TEMPLATE/feature_request.md",
3496
- ".github/FUNDING.yml",
3497
- ],
3498
- workflows: [
3499
- ".github/workflows/codeql-analysis.yml",
3500
- ".github/workflows/jest-coverage.yaml",
3501
- ".github/workflows/nodejs-build-prod.yaml",
3502
- ".github/workflows/pages.yaml",
3503
- ".github/workflows/publish-on-release.yaml",
3504
- ".github/workflows/release-on-tag.yaml",
3505
- ".github/workflows/snyk-analysis.yaml",
3506
- ],
3507
- ide: [
3508
- ".idea/runConfigurations/All Tests.run.xml",
3509
- ".idea/runConfigurations/build.run.xml",
3510
- ".idea/runConfigurations/build_prod.run.xml",
3511
- ".idea/runConfigurations/coverage.run.xml",
3512
- ".idea/runConfigurations/docs.run.xml",
3513
- ".idea/runConfigurations/drawings.run.xml",
3514
- ".idea/runConfigurations/flash-forward.run.xml",
3515
- ".idea/runConfigurations/Integration_Tests.run.xml",
3516
- ".idea/runConfigurations/Bundling_Tests.run.xml",
3517
- ".idea/runConfigurations/lint-fix.run.xml",
3518
- ".idea/runConfigurations/release.run.xml",
3519
- ".idea/runConfigurations/test_circular.run.xml",
3520
- ".idea/runConfigurations/uml.run.xml",
3521
- ".idea/runConfigurations/Unit Tests.run.xml",
3522
- ".idea/runConfigurations/update-scripts.run.xml",
3523
- ],
3524
- docs: [
3525
- "workdocs/tutorials/Contributing.md",
3526
- "workdocs/tutorials/Documentation.md",
3527
- "workdocs/tutorials/For Developers.md",
3528
- "workdocs/2-Badges.md",
3529
- "workdocs/jsdocs.json",
3530
- "workdocs/readme-md.json",
3531
- ],
3532
- styles: [".prettierrc", "eslint.config.js"],
3533
- scripts: [
3534
- "bin/update-scripts.cjs",
3535
- "bin/tag-release.cjs",
3536
- "bin/build-scripts.cjs",
3537
- ],
3538
- tests: ["jest.config.ts", "workdocs/reports/jest.coverage.config.ts"],
3539
- typescript: ["tsconfig.json"],
3540
- docker: ["Dockerfile"],
3541
- automation: [
3542
- "workdocs/confluence/Continuous Integration-Deployment/GitHub.md",
3543
- "workdocs/confluence/Continuous Integration-Deployment/Jira.md",
3544
- "workdocs/confluence/Continuous Integration-Deployment/Teams.md",
3545
- ],
3546
- };
3547
- const argzz = {
3548
- // init attributes
3549
- boot: {
3550
- type: "boolean",
3551
- },
3552
- org: {
3553
- type: "string",
3554
- short: "o",
3555
- },
3556
- name: {
3557
- type: "string",
3558
- short: "n",
3559
- default: undefined,
3560
- },
3561
- author: {
3562
- type: "string",
3563
- short: "a",
3564
- default: undefined,
3565
- },
3566
- // update attributes
3567
- all: {
3568
- type: "boolean",
3569
- },
3570
- license: {
3571
- type: "string",
3572
- message: "Pick the license",
3573
- },
3574
- scripts: {
3575
- type: "boolean",
3576
- },
3577
- styles: {
3578
- type: "boolean",
3579
- },
3580
- docs: {
3581
- type: "boolean",
3582
- },
3583
- ide: {
3584
- type: "boolean",
3585
- },
3586
- workflows: {
3587
- type: "boolean",
3588
- },
3589
- templates: {
3590
- type: "boolean",
3591
- },
3592
- typescript: {
3593
- type: "boolean",
3594
- },
3595
- docker: {
3596
- type: "boolean",
3597
- },
3598
- pkg: {
3599
- type: "boolean",
3600
- },
3601
- dependencies: {
3602
- type: "boolean",
3603
- },
3604
- tests: {
3605
- type: "boolean",
3606
- },
3607
- automation: {
3608
- type: "boolean",
3609
- },
3610
- };
3611
- /**
3612
- * @class TemplateSync
3613
- * @extends {Command}
3614
- * @category scripts
3615
- * @description A command-line tool for synchronizing project templates and configurations.
3616
- * @summary This class provides functionality to download and update various project files and configurations from a remote repository.
3617
- * It supports updating licenses, IDE configurations, scripts, styles, documentation, workflows, and templates.
3618
- *
3619
- * @param {CommandOptions} args - The command options for TemplateSync
3620
- */
3621
- class TemplateSync extends Command {
3622
- constructor() {
3623
- super("TemplateSync", argzz);
3624
- this.replacements = {};
3625
- /**
3626
- * @description Downloads style configuration files.
3627
- * @returns {Promise<void>}
3628
- */
3629
- this.getStyles = () => this.downloadOption("styles");
3630
- /**
3631
- * @description Downloads template files.
3632
- * @returns {Promise<void>}
3633
- */
3634
- this.getTemplates = () => this.downloadOption("templates");
3635
- /**
3636
- * @description Downloads workflow configuration files.
3637
- * @returns {Promise<void>}
3638
- */
3639
- this.getWorkflows = () => this.downloadOption("workflows");
3640
- /**
3641
- * @description Downloads documentation files.
3642
- * @returns {Promise<void>}
3643
- */
3644
- this.getDocs = () => this.downloadOption("docs");
3645
- /**
3646
- * @description Downloads typescript config files.
3647
- * @returns {Promise<void>}
3648
- */
3649
- this.getTypescript = () => this.downloadOption("typescript");
3650
- /**
3651
- * @description Downloads automation documentation files.
3652
- * @returns {Promise<void>}
3653
- */
3654
- this.getAutomation = () => this.downloadOption("automation");
3655
- /**
3656
- * @description Downloads automation documentation files.
3657
- * @returns {Promise<void>}
3658
- */
3659
- this.getTests = () => this.downloadOption("tests");
3660
- /**
3661
- * @description Downloads docker image files.
3662
- * @returns {Promise<void>}
3663
- */
3664
- this.getDocker = () => this.downloadOption("docker");
3665
- }
3666
- loadValuesFromPackage() {
3667
- const p = process.cwd();
3668
- const author = getPackage(p, "author");
3669
- const scopedName = getPackage(p, "name");
3670
- let name = scopedName;
3671
- let org;
3672
- if (name.startsWith("@")) {
3673
- const split = name.split("/");
3674
- name = split[1];
3675
- org = split[0].replace("@", "");
3676
- }
3677
- ["Tiago Venceslau", "TiagoVenceslau", "${author}"].forEach((el) => (this.replacements[el] = author));
3678
- ["TS-Workspace", "ts-workspace", "${name}"].forEach((el) => (this.replacements[el] = name));
3679
- ["decaf-ts", "${org}"].forEach((el) => (this.replacements[el] = org || '""'));
3680
- this.replacements["${org_or_owner}"] = org || name;
3681
- }
3682
- /**
3683
- * @description Downloads files for a specific option category.
3684
- * @summary This method downloads all files associated with a given option key from the remote repository.
3685
- * @param {string} key - The key representing the option category to download
3686
- * @returns {Promise<void>}
3687
- * @throws {Error} If the specified option key is not found
3688
- */
3689
- async downloadOption(key) {
3690
- if (!(key in options)) {
3691
- throw new Error(`Option "${key}" not found in options`);
3692
- }
3693
- const files = options[key];
3694
- for (const file of files) {
3695
- this.log.info(`Downloading ${file}`);
3696
- let data = await HttpClient.downloadFile(`${baseUrl}/${file}`);
3697
- data = patchString(data, this.replacements);
3698
- writeFile(path.join(process.cwd(), file), data);
3699
- }
3700
- }
3701
- /**
3702
- * @description Downloads and sets up the specified license.
3703
- * @summary This method downloads the chosen license file, saves it to the project, and updates the package.json license field.
3704
- * @param {"MIT" | "GPL" | "Apache" | "LGPL" | "AGPL"} license - The license to download and set up
3705
- * @returns {Promise<void>}
3706
- */
3707
- async getLicense(license) {
3708
- this.log.info(`Downloading ${license} license`);
3709
- const url = `${baseUrl}/workdocs/licenses/${license}.md`;
3710
- let data = await HttpClient.downloadFile(url);
3711
- data = patchString(data, this.replacements);
3712
- writeFile(path.join(process.cwd(), "LICENSE.md"), data);
3713
- setPackageAttribute("license", license);
3714
- }
3715
- /**
3716
- * @description Downloads IDE configuration files.
3717
- * @returns {Promise<void>}
3718
- */
3719
- async getIde() {
3720
- fs.mkdirSync(path.join(process.cwd(), ".idea", "runConfigurations"), {
3721
- recursive: true,
3722
- });
3723
- await this.downloadOption("ide");
3724
- }
3725
- /**
3726
- * @description Update npm scripts
3727
- * @returns {Promise<void>}
3728
- */
3729
- async getScripts() {
3730
- await this.downloadOption("scripts");
3731
- this.log.info("please re-run the command");
3732
- process.exit(0);
3733
- }
3734
- async initPackage(pkgName, author, license) {
3735
- try {
3736
- const pkg = getPackage();
3737
- delete pkg[SetupScriptKey];
3738
- pkg.name = pkgName;
3739
- pkg.version = "0.0.1";
3740
- pkg.author = author;
3741
- pkg.license = license;
3742
- fs.writeFileSync("package.json", JSON.stringify(pkg, null, 2));
3743
- }
3744
- catch (e) {
3745
- throw new Error(`Error fixing package.json: ${e}`);
3746
- }
3747
- }
3748
- async updatePackageScrips() {
3749
- try {
3750
- const originalPkg = JSON.parse(await HttpClient.downloadFile(`${baseUrl}/package.json`));
3751
- const { scripts } = originalPkg;
3752
- const pkg = getPackage();
3753
- Object.keys(pkg.scripts).forEach((key) => {
3754
- if (key in scripts) {
3755
- const replaced = patchString(scripts[key], this.replacements);
3756
- if (replaced !== scripts[key]) {
3757
- pkg.scripts[key] = replaced;
3758
- }
3759
- }
3760
- });
3761
- pkg["exports"]["require"] = originalPkg["exports"]["require"];
3762
- pkg["exports"]["import"] = originalPkg["exports"]["import"];
3763
- pkg["types"] = originalPkg["types"];
3764
- fs.writeFileSync("package.json", JSON.stringify(pkg, null, 2));
3765
- }
3766
- catch (e) {
3767
- throw new Error(`Error fixing package.json scripts: ${e}`);
3768
- }
3769
- }
3770
- async createTokenFiles() {
3771
- const log = this.log.for(this.createTokenFiles);
3772
- const gitToken = await UserInput.insistForText("token", "please input your github token", (res) => {
3773
- return !!res.match(/^ghp_[0-9a-zA-Z]{36}$/g);
3774
- });
3775
- Object.values(Tokens).forEach((token) => {
3776
- try {
3777
- let status;
3778
- try {
3779
- status = fs.existsSync(token);
3780
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
3781
- }
3782
- catch (e) {
3783
- log.info(`Token file ${token} not found. Creating a new one...`);
3784
- fs.writeFileSync(token, token === ".token" ? gitToken : "");
3785
- return;
3786
- }
3787
- if (!status) {
3788
- fs.writeFileSync(token, token === ".token" ? gitToken : "");
3789
- }
3790
- }
3791
- catch (e) {
3792
- throw new Error(`Error creating token file ${token}: ${e}`);
3793
- }
3794
- });
3795
- }
3796
- async getOrg() {
3797
- const org = await UserInput.askText("Organization", "Enter the organization name (will be used to scope your npm project. leave blank to create a unscoped project):");
3798
- const confirmation = await UserInput.askConfirmation("Confirm organization", "Is this organization correct?", true);
3799
- if (!confirmation)
3800
- return this.getOrg();
3801
- return org;
3802
- }
3803
- async auditFix() {
3804
- return await runCommand("npm audit fix --force").promise;
3805
- }
3806
- patchFiles() {
3807
- const files = [
3808
- ...fs
3809
- .readdirSync(path.join(process.cwd(), "src"), {
3810
- recursive: true,
3811
- withFileTypes: true,
3812
- })
3813
- .filter((entry) => entry.isFile())
3814
- .map((entry) => path.join(entry.parentPath, entry.name)),
3815
- ...fs
3816
- .readdirSync(path.join(process.cwd(), "workdocs"), {
3817
- recursive: true,
3818
- withFileTypes: true,
3819
- })
3820
- .filter((entry) => entry.isFile() && entry.name.endsWith(".md"))
3821
- .map((entry) => path.join(entry.parentPath, entry.name)),
3822
- path.join(process.cwd(), ".gitlab-ci.yml"),
3823
- path.join(process.cwd(), "workdocs", "jsdocs.json"),
3824
- ];
3825
- for (const file of files) {
3826
- patchFile(file, this.replacements);
3827
- }
3828
- }
3829
- async updateDependencies() {
3830
- try {
3831
- const originalPkg = JSON.parse(await HttpClient.downloadFile(`${baseUrl}/package.json`));
3832
- const { devDependencies } = originalPkg;
3833
- const pkg = getPackage();
3834
- Object.keys(pkg.scripts).forEach((key) => {
3835
- if (key in devDependencies) {
3836
- const replaced = devDependencies[key];
3837
- if (replaced !== devDependencies[key]) {
3838
- pkg["devDependencies"] =
3839
- pkg["devDependencies"] || {};
3840
- pkg["devDependencies"][key] = replaced;
3841
- }
3842
- }
3843
- });
3844
- fs.writeFileSync("package.json", JSON.stringify(pkg, null, 2));
3845
- await runCommand("npm install").promise;
3846
- }
3847
- catch (e) {
3848
- throw new Error(`Error fixing package.json dependencies: ${e}`);
3849
- }
3850
- }
3851
- /**
3852
- * @description Runs the template synchronization process.
3853
- * @summary This method orchestrates the downloading of various project components based on the provided arguments.
3854
- * @param {ParseArgsResult} args - The parsed command-line arguments
3855
- * @returns {Promise<void>}
3856
- *
3857
- * @mermaid
3858
- * sequenceDiagram
3859
- * participant T as TemplateSync
3860
- * participant L as getLicense
3861
- * participant I as getIde
3862
- * participant S as getScripts
3863
- * participant St as getStyles
3864
- * participant D as getDocs
3865
- * participant W as getWorkflows
3866
- * participant Te as getTemplates
3867
- * T->>T: Parse arguments
3868
- * alt all flag is true
3869
- * T->>T: Set all component flags to true
3870
- * end
3871
- * alt license is specified
3872
- * T->>L: getLicense(license)
3873
- * end
3874
- * alt ide flag is true
3875
- * T->>I: getIde()
3876
- * end
3877
- * alt scripts flag is true
3878
- * T->>S: getScripts()
3879
- * end
3880
- * alt styles flag is true
3881
- * T->>St: getStyles()
3882
- * end
3883
- * alt docs flag is true
3884
- * T->>D: getDocs()
3885
- * end
3886
- * alt workflows flag is true
3887
- * T->>W: getWorkflows()
3888
- * end
3889
- * alt templates flag is true
3890
- * T->>Te: getTemplates()
3891
- * end
3892
- */
3893
- async run(args) {
3894
- let { license } = args;
3895
- const { boot } = args;
3896
- let { all, scripts, styles, docs, ide, workflows, templates, docker, typescript, dependencies, tests, automation, pkg, } = args;
3897
- if (scripts ||
3898
- styles ||
3899
- docs ||
3900
- ide ||
3901
- workflows ||
3902
- templates ||
3903
- docker ||
3904
- typescript ||
3905
- automation ||
3906
- dependencies ||
3907
- tests ||
3908
- pkg)
3909
- all = false;
3910
- if (boot) {
3911
- const org = await this.getOrg();
3912
- const name = await UserInput.insistForText("Project name", "Enter the project name:", (res) => res.length > 1);
3913
- const author = await UserInput.insistForText("Author", "Enter the author name:", (res) => res.length > 1);
3914
- const pkgName = org ? `@${org}/${name}` : name;
3915
- await this.initPackage(pkgName, author, license);
3916
- await this.createTokenFiles();
3917
- await this.auditFix();
3918
- this.patchFiles();
3919
- }
3920
- if (all) {
3921
- scripts = false;
3922
- styles = true;
3923
- docs = true;
3924
- ide = true;
3925
- workflows = true;
3926
- templates = true;
3927
- docker = true;
3928
- typescript = true;
3929
- pkg = true;
3930
- dependencies = true;
3931
- tests = true;
3932
- automation = false;
3933
- }
3934
- if (typeof scripts === "undefined")
3935
- scripts = await UserInput.askConfirmation("scripts", "Do you want to get scripts?", true);
3936
- if (scripts)
3937
- await this.getScripts();
3938
- this.loadValuesFromPackage();
3939
- if (!all && typeof license === "undefined") {
3940
- const confirmation = await UserInput.askConfirmation("license", "Do you want to set a license?", true);
3941
- if (confirmation)
3942
- license = await UserInput.insistForText("license", "Enter the desired License (MIT|GPL|Apache|LGPL|AGPL):", (val) => !!val && !!val.match(/^(MIT|GPL|Apache|LGPL|AGPL)$/g));
3943
- }
3944
- if (typeof license !== "undefined")
3945
- await this.getLicense(license);
3946
- if (typeof ide === "undefined")
3947
- ide = await UserInput.askConfirmation("ide", "Do you want to get ide configs?", true);
3948
- if (ide)
3949
- await this.getIde();
3950
- if (typeof typescript === "undefined")
3951
- typescript = await UserInput.askConfirmation("typescript", "Do you want to get typescript configs?", true);
3952
- if (typescript)
3953
- await this.getTypescript();
3954
- if (typeof docker === "undefined")
3955
- docker = await UserInput.askConfirmation("docker", "Do you want to get docker configs?", true);
3956
- if (docker)
3957
- await this.getDocker();
3958
- if (typeof automation === "undefined")
3959
- automation = await UserInput.askConfirmation("automation", "Do you want to get automation configs?", true);
3960
- if (automation)
3961
- await this.getAutomation();
3962
- if (typeof styles === "undefined")
3963
- styles = await UserInput.askConfirmation("styles", "Do you want to get styles?", true);
3964
- if (styles)
3965
- await this.getStyles();
3966
- if (typeof docs === "undefined")
3967
- docs = await UserInput.askConfirmation("docs", "Do you want to get docs?", true);
3968
- if (docs)
3969
- await this.getDocs();
3970
- if (typeof workflows === "undefined")
3971
- workflows = await UserInput.askConfirmation("workflows", "Do you want to get workflows?", true);
3972
- if (workflows)
3973
- await this.getWorkflows();
3974
- if (typeof templates === "undefined")
3975
- templates = await UserInput.askConfirmation("templates", "Do you want to get templates?", true);
3976
- if (templates)
3977
- await this.getTemplates();
3978
- if (typeof pkg === "undefined")
3979
- pkg = await UserInput.askConfirmation("pkg", "Do you want to update your package.json scripts?", true);
3980
- if (pkg)
3981
- await this.updatePackageScrips();
3982
- if (typeof tests === "undefined")
3983
- tests = await UserInput.askConfirmation("pkg", "Do you want to update your test configs?", true);
3984
- if (tests)
3985
- await this.getTests();
3986
- if (typeof dependencies === "undefined")
3987
- dependencies = await UserInput.askConfirmation("pkg", "Do you want to update dev dependencies?", true);
3988
- if (dependencies)
3989
- await this.updateDependencies();
3990
- }
3991
- }
3992
-
3993
- /**
3994
- * @description A specialized output writer that uses regular expressions to process output.
3995
- * @summary This class extends StandardOutputWriter to provide regex-based output processing.
3996
- * It allows for pattern matching in the output stream and can trigger specific actions
3997
- * based on matched patterns.
3998
- *
3999
- * @template T - The type of the resolved value, defaulting to string.
4000
- *
4001
- * @param cmd - The command string to be executed.
4002
- * @param lock - A PromiseExecutor to control the asynchronous flow.
4003
- * @param regexp - A string or RegExp to match against the output.
4004
- * @param flags - Optional flags for the RegExp constructor, defaults to "g".
4005
- *
4006
- * @class
4007
- * @example
4008
- * ```typescript
4009
- * import { RegexpOutputWriter } from '@decaf-ts/utils';
4010
- * import { PromiseExecutor } from '@decaf-ts/utils';
4011
- *
4012
- * // Create a promise executor
4013
- * const executor: PromiseExecutor<string, Error> = {
4014
- * resolve: (value) => console.log(`Resolved: ${value}`),
4015
- * reject: (error) => console.error(`Rejected: ${error.message}`)
4016
- * };
4017
- *
4018
- * // Create a regexp output writer that matches version numbers
4019
- * const writer = new RegexpOutputWriter('node --version', executor, /v(\d+\.\d+\.\d+)/);
4020
- *
4021
- * // Use the writer to handle command output
4022
- * writer.data('v14.17.0'); // This will automatically resolve with "v14.17.0"
4023
- * ```
4024
- *
4025
- * @mermaid
4026
- * sequenceDiagram
4027
- * participant Client
4028
- * participant RegexpOutputWriter
4029
- * participant StandardOutputWriter
4030
- * participant Logger
4031
- *
4032
- * Client->>RegexpOutputWriter: new RegexpOutputWriter(cmd, lock, regexp, flags)
4033
- * RegexpOutputWriter->>StandardOutputWriter: super(cmd, lock)
4034
- * StandardOutputWriter->>Logger: Logging.for(cmd)
4035
- * RegexpOutputWriter->>RegexpOutputWriter: compile regexp
4036
- *
4037
- * Client->>RegexpOutputWriter: data(chunk)
4038
- * RegexpOutputWriter->>StandardOutputWriter: super.data(chunk)
4039
- * StandardOutputWriter->>Logger: logger.info(log)
4040
- * RegexpOutputWriter->>RegexpOutputWriter: testAndResolve(chunk)
4041
- * RegexpOutputWriter->>RegexpOutputWriter: test(chunk)
4042
- * alt match found
4043
- * RegexpOutputWriter->>RegexpOutputWriter: resolve(match[0])
4044
- * RegexpOutputWriter->>StandardOutputWriter: resolve(match[0])
4045
- * end
4046
- *
4047
- * Client->>RegexpOutputWriter: error(chunk)
4048
- * RegexpOutputWriter->>StandardOutputWriter: super.error(chunk)
4049
- * StandardOutputWriter->>Logger: logger.info(log)
4050
- * RegexpOutputWriter->>RegexpOutputWriter: testAndReject(chunk)
4051
- * RegexpOutputWriter->>RegexpOutputWriter: test(chunk)
4052
- * alt match found
4053
- * RegexpOutputWriter->>RegexpOutputWriter: reject(match[0])
4054
- * RegexpOutputWriter->>StandardOutputWriter: reject(match[0])
4055
- * end
4056
- */
4057
- class RegexpOutputWriter extends StandardOutputWriter {
4058
- constructor(cmd, lock, regexp, flags = "g") {
4059
- super(cmd, lock);
4060
- try {
4061
- this.regexp =
4062
- typeof regexp === "string" ? new RegExp(regexp, flags) : regexp;
4063
- }
4064
- catch (e) {
4065
- throw new Error(`Invalid regular expression: ${e}`);
4066
- }
4067
- }
4068
- /**
4069
- * @description Tests the input data against the stored regular expression.
4070
- * @summary Executes the regular expression on the input data and returns the match result.
4071
- *
4072
- * @param data - The string to test against the regular expression.
4073
- * @return The result of the regular expression execution, or undefined if an error occurs.
4074
- */
4075
- test(data) {
4076
- this.regexp.lastIndex = 0;
4077
- let match;
4078
- try {
4079
- match = this.regexp.exec(data);
4080
- }
4081
- catch (e) {
4082
- return console.debug(`Failed to parse chunk: ${data}\nError: ${e} `);
4083
- }
4084
- return match;
4085
- }
4086
- /**
4087
- * @description Tests the data and resolves the promise if a match is found.
4088
- * @summary Executes the test method and resolves the promise with the first match group if successful.
4089
- *
4090
- * @param data - The string to test against the regular expression.
4091
- */
4092
- testAndResolve(data) {
4093
- const match = this.test(data);
4094
- if (match)
4095
- this.resolve(match[0]);
4096
- }
4097
- /**
4098
- * @description Tests the data and rejects the promise if a match is found.
4099
- * @summary Executes the test method and rejects the promise with the first match group if successful.
4100
- *
4101
- * @param data - The string to test against the regular expression.
4102
- */
4103
- testAndReject(data) {
4104
- const match = this.test(data);
4105
- if (match)
4106
- this.reject(match[0]);
4107
- }
4108
- /**
4109
- * @description Processes incoming data chunks.
4110
- * @summary Calls the parent class data method and then tests the data for a match to potentially resolve the promise.
4111
- *
4112
- * @param chunk - The data chunk to process.
4113
- */
4114
- data(chunk) {
4115
- super.data(chunk);
4116
- this.testAndResolve(String(chunk));
4117
- }
4118
- /**
4119
- * @description Processes incoming error chunks.
4120
- * @summary Calls the parent class error method and then tests the data for a match to potentially reject the promise.
4121
- *
4122
- * @param chunk - The error chunk to process.
4123
- */
4124
- error(chunk) {
4125
- super.error(chunk);
4126
- this.testAndReject(String(chunk));
4127
- }
4128
- }
4129
-
4130
- /**
4131
- * @module utils
4132
- * @description Utilities and building blocks for Decaf-TS CLI and scripting.
4133
- * @summary Aggregates CLI command infrastructure, input helpers, output writers, filesystem/network utilities,
4134
- * and shared types. Consumers typically use {@link Command}, {@link UserInput}, {@link StandardOutputWriter},
4135
- * {@link printBanner}, and utilities from {@link module:utils|utils}.
4136
- *
4137
- * This entrypoint re-exports subpackages:
4138
- * - CLI framework under `./cli` (command base, options, built-in commands)
4139
- * - Input helpers under `./input` (prompting and arg parsing)
4140
- * - Output helpers under `./output` (banner and styling)
4141
- * - General utilities under `./utils` (fs, http, exec, types)
4142
- * - Writers under `./writers` (stdout/stderr processors)
4143
- *
4144
- * Note: Individual exports are documented in their source files.
4145
- */
4146
- /**
4147
- * @description Represents the current version of the module.
4148
- * @summary Stores the version for the @decaf-ts/utils package. The build replaces
4149
- * the placeholder with the actual version number at publish time.
4150
- * @const VERSION
4151
- * @memberOf module:utils
4152
- */
4153
- const VERSION = "0.4.1";
4154
- /**
4155
- * @description Represents the current version of the module.
4156
- * @summary Stores the version for the @decaf-ts/utils package. The build replaces
4157
- * the placeholder with the actual version number at publish time.
4158
- * @const VERSION
4159
- * @memberOf module:utils
4160
- */
4161
- const PACKAGE_NAME = "@decaf-ts/utils";
4162
-
4163
- export { AbortCode, BuildScripts, Command, DefaultCommandOptions, DefaultCommandValues, Encoding, HttpClient, NoCIFLag, PACKAGE_NAME, RegexpOutputWriter, ReleaseScript, SemVersion, SemVersionRegex, SetupScriptKey, StandardOutputWriter, TemplateSync, Tokens, UserInput, VERSION, chainAbortController, copyFile, deletePath, getAllFiles, getDependencies, getPackage, getPackageDependencies, getPackageVersion, getSlogan, installDependencies, installIfNotAvailable, lockify, normalizeImport, packageToGlobal, parseList, patchFile, printBanner, pushToGit, readFile, renameFile, runCommand, setPackageAttribute, spawnCommand, updateDependencies, writeFile };
1
+ import e from"prompts";import{parseArgs as t}from"util";import{Logging as o,patchString as a,LoggedClass as s,LoggedEnvironment as n,LogLevel as r}from"@decaf-ts/logging";import i from"fs";import c from"path";import{spawn as l}from"child_process";import{style as g}from"styled-string-builder";import d from"https";import{rollup as f}from"rollup";import h from"@rollup/plugin-typescript";import u from"@rollup/plugin-commonjs";import{nodeResolve as p}from"@rollup/plugin-node-resolve";import m from"@rollup/plugin-json";import{builtinModules as S}from"module";import*as y from"typescript";import{ModuleKind as w}from"typescript";class T{static{this.logger=o.for(T)}constructor(e){this.type="text",this.name=e}setType(e){return T.logger.verbose(`Setting type to: ${e}`),this.type=e,this}setMessage(e){return T.logger.verbose(`Setting message to: ${e}`),this.message=e,this}setInitial(e){return T.logger.verbose(`Setting initial value to: ${e}`),this.initial=e,this}setStyle(e){return T.logger.verbose(`Setting style to: ${e}`),this.style=e,this}setFormat(e){return T.logger.verbose("Setting format function"),this.format=e,this}setValidate(e){return T.logger.verbose("Setting validate function"),this.validate=e,this}setOnState(e){return T.logger.verbose("Setting onState callback"),this.onState=e,this}setMin(e){return T.logger.verbose(`Setting min value to: ${e}`),this.min=e,this}setMax(e){return T.logger.verbose(`Setting max value to: ${e}`),this.max=e,this}setFloat(e){return T.logger.verbose(`Setting float to: ${e}`),this.float=e,this}setRound(e){return T.logger.verbose(`Setting round to: ${e}`),this.round=e,this}setInstructions(e){return T.logger.verbose(`Setting instructions to: ${e}`),this.instructions=e,this}setIncrement(e){return T.logger.verbose(`Setting increment to: ${e}`),this.increment=e,this}setSeparator(e){return T.logger.verbose(`Setting separator to: ${e}`),this.separator=e,this}setActive(e){return T.logger.verbose(`Setting active style to: ${e}`),this.active=e,this}setInactive(e){return T.logger.verbose(`Setting inactive style to: ${e}`),this.inactive=e,this}setChoices(e){return T.logger.verbose(`Setting choices: ${JSON.stringify(e)}`),this.choices=e,this}setHint(e){return T.logger.verbose(`Setting hint to: ${e}`),this.hint=e,this}setWarn(e){return T.logger.verbose(`Setting warn to: ${e}`),this.warn=e,this}setSuggest(e){return T.logger.verbose("Setting suggest function"),this.suggest=e,this}setLimit(e){return T.logger.verbose(`Setting limit to: ${e}`),this.limit=e,this}setMask(e){return T.logger.verbose(`Setting mask to: ${e}`),this.mask=e,this}setStdout(e){return T.logger.verbose("Setting stdout stream"),this.stdout=e,this}setStdin(e){return this.stdin=e,this}async ask(){return(await T.ask(this))[this.name]}static async ask(t){const o=T.logger.for(this.ask);let a;Array.isArray(t)||(t=[t]);try{o.verbose(`Asking questions: ${t.map(e=>e.name).join(", ")}`),a=await e(t),o.verbose(`Received answers: ${JSON.stringify(a,null,2)}`)}catch(e){throw new Error(`Error while getting input: ${e}`)}return a}static async askNumber(e,t,o,a,s){T.logger.for(this.askNumber).verbose(`Asking number input: undefined, question: ${t}, min: ${o}, max: ${a}, initial: ${s}`);const n=new T(e).setMessage(t).setType("number");return"number"==typeof o&&n.setMin(o),"number"==typeof a&&n.setMax(a),"number"==typeof s&&n.setInitial(s),(await this.ask(n))[e]}static async askText(e,t,o=void 0,a){T.logger.for(this.askText).verbose(`Asking text input: undefined, question: ${t}, mask: ${o}, initial: ${a}`);const s=new T(e).setMessage(t);return o&&s.setMask(o),"string"==typeof a&&s.setInitial(a),(await this.ask(s))[e]}static async askConfirmation(e,t,o){T.logger.for(this.askConfirmation).verbose(`Asking confirmation input: undefined, question: ${t}, initial: ${o}`);const a=new T(e).setMessage(t).setType("confirm");return void 0!==o&&a.setInitial(o),(await this.ask(a))[e]}static async insist(e,t,o,a=1){const s=T.logger.for(this.insist);let n;s.verbose(`Insisting on input: ${e.name}, test: ${t.toString()}, defaultConfirmation: ${o}, limit: ${a}`);let r,i=0;try{do{n=(await T.ask(e))[e.name],t(n)?(r=await T.askConfirmation(`${e.name}-confirm`,`Is the ${e.type} correct?`,o),r||(n=void 0)):n=void 0}while(void 0===n&&a>1&&i++<a)}catch(e){throw s.error(`Error while insisting: ${e}`),e}return void 0===n&&s.info("no selection..."),n}static async insistForText(e,t,o,a=void 0,s,n=!1,r=-1){T.logger.for(this.insistForText).verbose(`Insisting for text input: undefined, question: ${t}, test: ${o.toString()}, mask: ${a}, initial: ${s}, defaultConfirmation: ${n}, limit: ${r}`);const i=new T(e).setMessage(t);return a&&i.setMask(a),"string"==typeof s&&i.setInitial(s),await this.insist(i,o,n,r)}static async insistForNumber(e,t,o,a,s,n,r=!1,i=-1){T.logger.for(this.insistForNumber).verbose(`Insisting for number input: undefined, question: ${t}, test: ${o.toString()}, min: ${a}, max: ${s}, initial: ${n}, defaultConfirmation: ${r}, limit: ${i}`);const c=new T(e).setMessage(t).setType("number");return"number"==typeof a&&c.setMin(a),"number"==typeof s&&c.setMax(s),"number"==typeof n&&c.setInitial(n),await this.insist(c,o,r,i)}static parseArgs(e){const o=T.logger.for(this.parseArgs),a={args:process.argv.slice(2),options:e};o.debug(`Parsing arguments: ${JSON.stringify(a,null,2)}`);try{return t(a)}catch(t){throw o.debug(`Error while parsing arguments:\n${JSON.stringify(a,null,2)}\n | options\n${JSON.stringify(e,null,2)}\n | ${t}`),new Error(`Error while parsing arguments: ${t}`)}}}const b={verbose:{type:"boolean",short:"V",default:void 0},version:{type:"boolean",short:"v",default:void 0},help:{type:"boolean",short:"h",default:!1},logLevel:{type:"string",default:"info"},logStyle:{type:"boolean",default:!0},timestamp:{type:"boolean",default:!0},banner:{type:"boolean",default:!0}},C=Object.keys(b).reduce((e,t)=>(e[t]=b[t].default,e),{}),D="utf-8",v=/^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z])))/g;var k;!function(e){e.PATCH="patch",e.MINOR="minor",e.MAJOR="major"}(k||(k={}));const $="-no-ci",E="postinstall";var I;!function(e){e.GIT=".token",e.NPM=".npmtoken",e.DOCKER=".dockertoken",e.CONFLUENCE=".confluence-token"}(I||(I={}));const F="Aborted";class x{constructor(e,t,...a){this.cmd=e,this.lock=t,this.logger=o.for(this.cmd)}log(e,t){t=Buffer.isBuffer(t)?t.toString(D):t;const o="stderr"===e?g(t).red.text:t;this.logger.info(o)}data(e){this.log("stdout",String(e))}error(e){this.log("stderr",String(e))}errors(e){this.log("stderr",`Error executing command exited : ${e}`)}exit(e,t){this.log("stdout",`command exited code : ${0===e?g(e.toString()).green.text:g(null===e?"null":e.toString()).red.text}`),0===e?this.resolve(t.map(e=>e.trim()).join("\n")):this.reject(new Error(t.length?t.join("\n"):e.toString()))}parseCommand(e){return e="string"==typeof e?e.split(" "):e,this.cmd=e.join(" "),[e[0],e.slice(1)]}resolve(e){this.log("stdout",`${this.cmd} executed successfully: ${g(e?"ran to completion":e).green}`),this.lock.resolve(e)}reject(e){e instanceof Error||(e=new Error("number"==typeof e?`Exit code ${e}`:e)),this.log("stderr",`${this.cmd} failed to execute: ${g(e.message).red}`),this.lock.reject(e)}}function j(e){let t=Promise.resolve();return(...o)=>{const a=t.then(()=>e(...o));return t=a.catch(()=>{}),a}}function N(e,...t){let o,a;if(e instanceof AbortSignal?(a=new AbortController,o=[e,...t]):(a=e,o=t),a.signal.aborted)return a;const s=()=>a.abort();for(const e of o){if(e.aborted){a.abort();break}e.addEventListener("abort",s,{once:!0,signal:a.signal})}return a}function A(e,t,o,a,s){function n(t,a){const[n,r]=e.parseCommand(t);s.info(`Running command: ${n}`),s.debug(`with args: ${r.join(" ")}`);const i=l(n,r,{...o,cwd:o.cwd||process.cwd(),env:Object.assign({},process.env,o.env,{PATH:process.env.PATH}),shell:o.shell||!1,signal:a.signal});return s.verbose(`pid : ${i.pid}`),i}const r=t.match(/[<>$#]/g);if(r)throw new Error(`Invalid command: ${t}. contains invalid characters: ${r}`);if(t.includes(" | ")){const e=t.split(" | "),o=[],s=new Array(e.length);s[0]=a;for(let t=0;t<e.length;t++)0!==t&&(s[t]=N(s[t-1].signal)),o.push(n(e[t],s[t])),0!==t&&o[t-1].stdout.pipe(o[t].stdin);return o[e.length-1]}return n(t,a)}function B(e,t={},a=x,...s){const n=o.for(B),r=new AbortController,i={abort:r,command:e,logs:[],errs:[]},c=new Promise((o,c)=>{let l;try{l=new a(e,{resolve:o,reject:c},...s),i.cmd=A(l,e,t,r,n)}catch(t){return c(new Error(`Error running command ${e}: ${t}`))}i.cmd.stdout.setEncoding("utf8"),i.cmd.stdout.on("data",e=>{e=e.toString(),i.logs.push(e),l.data(e)}),i.cmd.stderr.on("data",e=>{e=e.toString(),i.errs.push(e),l.error(e)}),i.cmd.once("error",e=>{l.exit(e.message,i.errs)}),i.cmd.once("exit",(e=0)=>{r.signal.aborted&&null===e&&(e=F),l.exit(e,0===e?i.logs:i.errs)})});return Object.assign(i,{promise:c,pipe:async t=>{const o=n.for("pipe");try{o.verbose(`Executing pipe function ${e}...`);const a=await c;return o.verbose(`Piping output to ${t.name}: ${a}`),t(a)}catch(e){throw o.error(`Error piping command output: ${e}`),e}}}),i}const O=o.for("fs");function P(e,t){const o=O.for(P);if(!i.existsSync(e))throw new Error(`File not found at path "${e}".`);let s=M(e);try{o.verbose(`Patching file "${e}"...`),o.debug(`with value: ${JSON.stringify(t)}`),s=a(s,t)}catch(e){throw new Error(`Error patching file: ${e}`)}U(e,s)}function M(e){const t=O.for(M);try{return t.verbose(`Reading file "${e}"...`),i.readFileSync(e,"utf8")}catch(o){throw t.verbose(`Error reading file "${e}": ${o}`),new Error(`Error reading file "${e}": ${o}`)}}function U(e,t){const o=O.for(U);try{o.verbose(`Writing file "${e} with ${t.length} bytes...`),i.writeFileSync(e,t,"utf8")}catch(t){throw o.verbose(`Error writing file "${e}": ${t}`),new Error(`Error writing file "${e}": ${t}`)}}function L(e,t){const o=O.for(L),a=[];try{o.verbose(`Retrieving all files from "${e}"...`);return i.readdirSync(e).forEach(t=>{const o=c.join(e,t),s=i.statSync(o);s.isFile()?a.push(o):s.isDirectory()&&a.push(...L(o))}),t?a.filter(t):a}catch(t){throw o.verbose(`Error retrieving files from "${e}": ${t}`),new Error(`Error retrieving files from "${e}": ${t}`)}}async function J(e,t){const o=O.for(J);let a,s;try{a=i.statSync(e)}catch(t){throw o.verbose(`Source path "${e}" does not exist: ${t}`),new Error(`Source path "${e}" does not exist: ${t}`)}try{s=i.statSync(t)}catch(e){}if(s)throw o.verbose(`Destination path "${t}" already exists`),new Error(`Destination path "${t}" already exists`);try{o.verbose(`Renaming ${a.isFile()?"file":"directory"} "${e}" to "${t}...`),i.renameSync(e,t),o.verbose(`Successfully renamed to "${t}"`)}catch(s){throw o.verbose(`Error renaming ${a.isFile()?"file":"directory"} "${e}" to "${t}": ${s}`),new Error(`Error renaming ${a.isFile()?"file":"directory"} "${e}" to "${t}": ${s}`)}}function R(e,t){const o=O.for(R);let a,s;try{a=i.statSync(e)}catch(t){throw o.verbose(`Source path "${e}" does not exist: ${t}`),new Error(`Source path "${e}" does not exist: ${t}`)}try{s=i.statSync(t)}catch(e){a.isDirectory()&&(o.verbose(`Dest path "${t}" does not exist. creating`),i.mkdirSync(t,{recursive:!0}))}try{o.verbose(`Copying ${a.isFile()?"file":"directory"} "${e}" to "${t}...`),i.cpSync(e,t,{recursive:!0})}catch(s){throw o.verbose(`Error copying ${a.isFile()?"file":"directory"} "${e}" to "${t}: ${s}`),new Error(`Error copying ${a.isFile()?"file":"directory"} "${e}" to "${t}: ${s}`)}}function W(e){const t=O.for(W);try{const o=i.statSync(e);o.isFile()?(t.verbose(`Deleting file "${e}...`),i.rmSync(e,{recursive:!0,force:!0})):o.isDirectory()&&i.rmSync(e,{recursive:!0,force:!0})}catch(o){throw t.verbose(`Error Deleting "${e}": ${o}`),new Error(`Error Deleting "${e}": ${o}`)}}function G(e=process.cwd(),t){let o;try{o=JSON.parse(M(c.join(e,"package.json")))}catch(e){throw new Error(`Failed to retrieve package information" ${e}`)}if(t){if(!(t in o))throw new Error(`Property "${t}" not found in package.json`);return o[t]}return o}function V(e,t,o=process.cwd()){const a=G(o);a[e]=t,U(c.join(o,"package.json"),JSON.stringify(a,null,2))}function X(e=process.cwd()){return G(e,"version")}async function H(e=process.cwd()){let t;try{t=JSON.parse(await B("npm ls --json",{cwd:e}).promise)}catch(e){throw new Error(`Failed to retrieve dependencies: ${e}`)}const o=(e,t)=>({name:e[0],version:e[1].version});return{prod:Object.entries(t.dependencies||{}).map(o),dev:Object.entries(t.devDependencies||{}).map(o),peer:Object.entries(t.peerDependencies||{}).map(o)}}async function _(){const e=O.for(_);e.info("checking for updates..."),await B("npx npm-check-updates -u").promise,e.info("updating..."),await B("npx npm run do-install").promise}async function q(e,t){if(!t){const e=await H();t={prod:e.prod?.map(e=>e.name)||[],dev:e.dev?.map(e=>e.name)||[],peer:e.peer?.map(e=>e.name)||[]}}const{prod:o,dev:a,peer:s}=t,n=Array.from(new Set([...o||[],...a||[],...s||[]])),r=(e="string"==typeof e?[e]:e).filter(e=>!n.includes(e));return r.length&&await Y({dev:r}),t.dev=t.dev||[],t.dev.push(...r),t}async function z(){const e=O.for(z),t=await B("git config user.name").promise,o=await B("git config user.email").promise;e.verbose(`cached git id: ${t}/${o}. changing to automation`),await B('git config user.email "automation@decaf.ts"').promise,await B('git config user.name "decaf"').promise,e.info("Pushing changes to git..."),await B("git add .").promise,await B('git commit -m "refs #1 - after repo setup"').promise,await B("git push").promise,await B(`git config user.email "${o}"`).promise,await B(`git config user.name "${t}"`).promise,e.verbose(`reverted to git id: ${t}/${o}`)}async function Y(e){const t=O.for(Y),o=e.prod||[],a=e.dev||[],s=e.peer||[];o.length&&(t.info(`Installing dependencies ${o.join(", ")}...`),await B(`npm install ${o.join(" ")}`,{cwd:process.cwd()}).promise),a.length&&(t.info(`Installing devDependencies ${a.join(", ")}...`),await B(`npm install --save-dev ${a.join(" ")}`,{cwd:process.cwd()}).promise),s.length&&(t.info(`Installing peerDependencies ${s.join(", ")}...`),await B(`npm install --save-peer ${s.join(" ")}`,{cwd:process.cwd()}).promise)}async function K(e){return e.then(e=>e.default||e)}const Z=[{Slogan:"No caffeine, no chaos. Just clean code.",Tags:"Coffee-themed, Calm, Tech"},{Slogan:"Full flavor, no jitters. That's Decaf-TS.",Tags:"Coffee-themed, Cheerful"},{Slogan:"Chill fullstack. Powered by Decaf.",Tags:"Coffee-themed, Fun, Tech"},{Slogan:"Decaf-TS: Brewed for calm code.",Tags:"Coffee-themed, Branding"},{Slogan:"Smooth as your morning Decaf.",Tags:"Coffee-themed, Chill"},{Slogan:"All the kick, none of the crash.",Tags:"Coffee-themed, Energetic"},{Slogan:"Sip back and ship faster.",Tags:"Coffee-themed, Fun"},{Slogan:"Keep calm and code Decaf.",Tags:"Coffee-themed, Playful"},{Slogan:"Code without the caffeine shakes.",Tags:"Coffee-themed, Humorous"},{Slogan:"Your fullstack, decaffeinated.",Tags:"Coffee-themed, Technical"},{Slogan:"No caffeine, no chaos. Just clean code.",Tags:"Coffee-themed, Calm, Tech"},{Slogan:"Full flavor, no jitters. That’s Decaf-TS.",Tags:"Coffee-themed, Cheerful"},{Slogan:"Chill fullstack. Powered by Decaf.",Tags:"Coffee-themed, Fun, Tech"},{Slogan:"Decaf-TS: Brewed for calm code.",Tags:"Coffee-themed, Branding"},{Slogan:"Smooth as your morning Decaf.",Tags:"Coffee-themed, Chill"},{Slogan:"All the kick, none of the crash.",Tags:"Coffee-themed, Energetic"},{Slogan:"Sip back and ship faster.",Tags:"Coffee-themed, Fun"},{Slogan:"Keep calm and code Decaf.",Tags:"Coffee-themed, Playful"},{Slogan:"Code without the caffeine shakes.",Tags:"Coffee-themed, Humorous"},{Slogan:"Your fullstack, decaffeinated.",Tags:"Coffee-themed, Technical"},{Slogan:"No caffeine, no chaos. Just clean code.",Tags:"Coffee-themed, Calm, Tech"},{Slogan:"Full flavor, no jitters. That’s Decaf-TS.",Tags:"Coffee-themed, Cheerful"},{Slogan:"Chill fullstack. Powered by Decaf.",Tags:"Coffee-themed, Fun, Tech"},{Slogan:"Decaf-TS: Brewed for calm code.",Tags:"Coffee-themed, Branding"},{Slogan:"Smooth as your morning Decaf.",Tags:"Coffee-themed, Chill"},{Slogan:"All the kick, none of the crash.",Tags:"Coffee-themed, Energetic"},{Slogan:"Sip back and ship faster.",Tags:"Coffee-themed, Fun"},{Slogan:"Keep calm and code Decaf.",Tags:"Coffee-themed, Playful"},{Slogan:"Code without the caffeine shakes.",Tags:"Coffee-themed, Humorous"},{Slogan:"Your fullstack, decaffeinated.",Tags:"Coffee-themed, Technical"},{Slogan:"No caffeine, no chaos. Just clean code.",Tags:"Coffee-themed, Calm, Tech"},{Slogan:"Full flavor, no jitters. That’s Decaf-TS.",Tags:"Coffee-themed, Cheerful"},{Slogan:"Chill fullstack. Powered by Decaf.",Tags:"Coffee-themed, Fun, Tech"},{Slogan:"Decaf-TS: Brewed for calm code.",Tags:"Coffee-themed, Branding"},{Slogan:"Smooth as your morning Decaf.",Tags:"Coffee-themed, Chill"},{Slogan:"All the kick, none of the crash.",Tags:"Coffee-themed, Energetic"},{Slogan:"Sip back and ship faster.",Tags:"Coffee-themed, Fun"},{Slogan:"Keep calm and code Decaf.",Tags:"Coffee-themed, Playful"},{Slogan:"Code without the caffeine shakes.",Tags:"Coffee-themed, Humorous"},{Slogan:"Your fullstack, decaffeinated.",Tags:"Coffee-themed, Technical"},{Slogan:"No caffeine, no chaos. Just clean code.",Tags:"Coffee-themed, Calm, Tech"},{Slogan:"Full flavor, no jitters. That’s Decaf-TS.",Tags:"Coffee-themed, Cheerful"},{Slogan:"Chill fullstack. Powered by Decaf.",Tags:"Coffee-themed, Fun, Tech"},{Slogan:"Decaf-TS: Brewed for calm code.",Tags:"Coffee-themed, Branding"},{Slogan:"Smooth as your morning Decaf.",Tags:"Coffee-themed, Chill"},{Slogan:"All the kick, none of the crash.",Tags:"Coffee-themed, Energetic"},{Slogan:"Sip back and ship faster.",Tags:"Coffee-themed, Fun"},{Slogan:"Keep calm and code Decaf.",Tags:"Coffee-themed, Playful"},{Slogan:"Code without the caffeine shakes.",Tags:"Coffee-themed, Humorous"},{Slogan:"Your fullstack, decaffeinated.",Tags:"Coffee-themed, Technical"},{Slogan:"No caffeine, no chaos. Just clean code.",Tags:"Coffee-themed, Calm, Tech"},{Slogan:"Full flavor, no jitters. That’s Decaf-TS.",Tags:"Coffee-themed, Cheerful"},{Slogan:"Chill fullstack. Powered by Decaf.",Tags:"Coffee-themed, Fun, Tech"},{Slogan:"Decaf-TS: Brewed for calm code.",Tags:"Coffee-themed, Branding"},{Slogan:"Smooth as your morning Decaf.",Tags:"Coffee-themed, Chill"},{Slogan:"All the kick, none of the crash.",Tags:"Coffee-themed, Energetic"},{Slogan:"Sip back and ship faster.",Tags:"Coffee-themed, Fun"},{Slogan:"Keep calm and code Decaf.",Tags:"Coffee-themed, Playful"},{Slogan:"Code without the caffeine shakes.",Tags:"Coffee-themed, Humorous"},{Slogan:"Your fullstack, decaffeinated.",Tags:"Coffee-themed, Technical"},{Slogan:"No caffeine, no chaos. Just clean code.",Tags:"Coffee-themed, Calm, Tech"},{Slogan:"Full flavor, no jitters. That’s Decaf-TS.",Tags:"Coffee-themed, Cheerful"},{Slogan:"Chill fullstack. Powered by Decaf.",Tags:"Coffee-themed, Fun, Tech"},{Slogan:"Decaf-TS: Brewed for calm code.",Tags:"Coffee-themed, Branding"},{Slogan:"Smooth as your morning Decaf.",Tags:"Coffee-themed, Chill"},{Slogan:"All the kick, none of the crash.",Tags:"Coffee-themed, Energetic"},{Slogan:"Sip back and ship faster.",Tags:"Coffee-themed, Fun"},{Slogan:"Keep calm and code Decaf.",Tags:"Coffee-themed, Playful"},{Slogan:"Code without the caffeine shakes.",Tags:"Coffee-themed, Humorous"},{Slogan:"Your fullstack, decaffeinated.",Tags:"Coffee-themed, Technical"},{Slogan:"No caffeine, no chaos. Just clean code.",Tags:"Coffee-themed, Calm, Tech"},{Slogan:"Full flavor, no jitters. That’s Decaf-TS.",Tags:"Coffee-themed, Cheerful"},{Slogan:"Chill fullstack. Powered by Decaf.",Tags:"Coffee-themed, Fun, Tech"},{Slogan:"Decaf-TS: Brewed for calm code.",Tags:"Coffee-themed, Branding"},{Slogan:"Smooth as your morning Decaf.",Tags:"Coffee-themed, Chill"},{Slogan:"All the kick, none of the crash.",Tags:"Coffee-themed, Energetic"},{Slogan:"Sip back and ship faster.",Tags:"Coffee-themed, Fun"},{Slogan:"Keep calm and code Decaf.",Tags:"Coffee-themed, Playful"},{Slogan:"Code without the caffeine shakes.",Tags:"Coffee-themed, Humorous"},{Slogan:"Your fullstack, decaffeinated.",Tags:"Coffee-themed, Technical"},{Slogan:"No caffeine, no chaos. Just clean code.",Tags:"Coffee-themed, Calm, Tech"},{Slogan:"Full flavor, no jitters. That’s Decaf-TS.",Tags:"Coffee-themed, Cheerful"},{Slogan:"Chill fullstack. Powered by Decaf.",Tags:"Coffee-themed, Fun, Tech"},{Slogan:"Decaf-TS: Brewed for calm code.",Tags:"Coffee-themed, Branding"},{Slogan:"Smooth as your morning Decaf.",Tags:"Coffee-themed, Chill"},{Slogan:"All the kick, none of the crash.",Tags:"Coffee-themed, Energetic"},{Slogan:"Sip back and ship faster.",Tags:"Coffee-themed, Fun"},{Slogan:"Keep calm and code Decaf.",Tags:"Coffee-themed, Playful"},{Slogan:"Code without the caffeine shakes.",Tags:"Coffee-themed, Humorous"},{Slogan:"Your fullstack, decaffeinated.",Tags:"Coffee-themed, Technical"},{Slogan:"No caffeine, no chaos. Just clean code.",Tags:"Coffee-themed, Calm, Tech"},{Slogan:"Full flavor, no jitters. That’s Decaf-TS.",Tags:"Coffee-themed, Cheerful"},{Slogan:"Chill fullstack. Powered by Decaf.",Tags:"Coffee-themed, Fun, Tech"},{Slogan:"Decaf-TS: Brewed for calm code.",Tags:"Coffee-themed, Branding"},{Slogan:"Smooth as your morning Decaf.",Tags:"Coffee-themed, Chill"},{Slogan:"All the kick, none of the crash.",Tags:"Coffee-themed, Energetic"},{Slogan:"Sip back and ship faster.",Tags:"Coffee-themed, Fun"},{Slogan:"Keep calm and code Decaf.",Tags:"Coffee-themed, Playful"},{Slogan:"Code without the caffeine shakes.",Tags:"Coffee-themed, Humorous"},{Slogan:"Your fullstack, decaffeinated.",Tags:"Coffee-themed, Technical"},{Slogan:"Decaf-TS: Where smart contracts meet smart interfaces.",Tags:"Blockchain, Smart Contracts, Tech"},{Slogan:"Ship dApps without the stress.",Tags:"Blockchain, Cheerful, Developer"},{Slogan:"No CRUD, no problem — Decaf your data.",Tags:"Data, No-CRUD, Chill"},{Slogan:"From DID to UI, without breaking a sweat.",Tags:"DID, SSI, UI, Calm"},{Slogan:"Decaf-TS: Your frontend already understands your smart contract.",Tags:"Smart Contracts, DX, Magic"},{Slogan:"Self-sovereign by design. Productive by default.",Tags:"SSI, Developer, Calm"},{Slogan:"Build once. Deploy everywhere. Decentralized and delightful.",Tags:"Blockchain, Multi-platform, Happy"},{Slogan:"Data that defines its own destiny.",Tags:"SSI, Data-driven, Empowerment"},{Slogan:"Goodbye CRUD, hello intent-based interfaces.",Tags:"No-CRUD, UI, Technical"},{Slogan:"The smoothest path from DID to done.",Tags:"DID, Workflow, Chill"},{Slogan:"Because your dApp deserves more than boilerplate.",Tags:"Blockchain, DevX, Efficiency"},{Slogan:"Own your data. Own your flow.",Tags:"SSI, Control, Ownership"},{Slogan:"Write logic like it belongs with the data — because it does.",Tags:"Data Logic, Developer, Smart"},{Slogan:"From smart contracts to smarter frontends.",Tags:"Smart Contracts, UI, DX"},{Slogan:"No caffeine. No CRUD. Just the future.",Tags:"No-CRUD, Coffee-themed, Futuristic"},{Slogan:"The future of web3 UX is Decaf.",Tags:"Blockchain, UX, Vision"},{Slogan:"Code with confidence. Govern with clarity.",Tags:"Blockchain, Governance, Calm"},{Slogan:"Interfaces that obey the data, not the other way around.",Tags:"UI, Data Logic, Self-aware"},{Slogan:"Brew business logic right into your bytes.",Tags:"Data Logic, Coffee-themed, Fun"},{Slogan:"DIDs done differently — and delightfully.",Tags:"DID, Self-Sovereign, Playful"},{Slogan:"Decaf-TS-TS: Where blockchain contracts meet smart interfaces.",Tags:"Blockchain, Smart Contracts, Tech"},{Slogan:"Ship dApps without the stress.",Tags:"Blockchain, Cheerful, Developer"},{Slogan:"No boilerplate, no problem — Decaf-TS your data.",Tags:"Data, No-CRUD, Chill"},{Slogan:"From DID to UI, without breaking a sweat.",Tags:"DID, SSI, UI, Calm"},{Slogan:"Decaf-TS-TS: Your frontend already understands your blockchain contract.",Tags:"Smart Contracts, DX, Magic"},{Slogan:"Self-sovereign by design. Productive by default.",Tags:"SSI, Developer, Calm"},{Slogan:"Build once. Deploy everywhere. Decentralized and delightful.",Tags:"Blockchain, Multi-platform, Happy"},{Slogan:"Data that defines its own destiny.",Tags:"SSI, Data-driven, Empowerment"},{Slogan:"Goodbye boilerplate, hello intent-based interfaces.",Tags:"No-CRUD, UI, Technical"},{Slogan:"The smoothest path from DID to done.",Tags:"DID, Workflow, Chill"},{Slogan:"Because your dApp deserves more than boilerplate.",Tags:"Blockchain, DevX, Efficiency"},{Slogan:"Own your data. Own your flow.",Tags:"SSI, Control, Ownership"},{Slogan:"Write logic like it belongs with the data — because it does.",Tags:"Data Logic, Developer, Smart"},{Slogan:"From blockchain contracts to smarter frontends.",Tags:"Smart Contracts, UI, DX"},{Slogan:"No caffeine. No boilerplate. Just the future.",Tags:"No-CRUD, Coffee-themed, Futuristic"},{Slogan:"The future of web3 UX is Decaf-TS.",Tags:"Blockchain, UX, Vision"},{Slogan:"Code with confidence. Govern with clarity.",Tags:"Blockchain, Governance, Calm"},{Slogan:"Interfaces that obey the data, not the other way around.",Tags:"UI, Data Logic, Self-aware"},{Slogan:"Brew business logic right into your bytes.",Tags:"Data Logic, Coffee-themed, Fun"},{Slogan:"DIDs done differently — and delightfully.",Tags:"DID, Self-Sovereign, Playful"},{Slogan:"Decaf-TS-TS: Where blockchain contracts meet smart interfaces.",Tags:"Blockchain, Smart Contracts, Tech"},{Slogan:"Ship dApps without the stress.",Tags:"Blockchain, Cheerful, Developer"},{Slogan:"No boilerplate, no problem — Decaf-TS your data.",Tags:"Data, No-CRUD, Chill"},{Slogan:"From DID to UI, without breaking a sweat.",Tags:"DID, SSI, UI, Calm"},{Slogan:"Decaf-TS-TS: Your frontend already understands your blockchain contract.",Tags:"Smart Contracts, DX, Magic"},{Slogan:"Self-sovereign by design. Productive by default.",Tags:"SSI, Developer, Calm"},{Slogan:"Build once. Deploy everywhere. Decentralized and delightful.",Tags:"Blockchain, Multi-platform, Happy"},{Slogan:"Data that defines its own destiny.",Tags:"SSI, Data-driven, Empowerment"},{Slogan:"Goodbye boilerplate, hello intent-based interfaces.",Tags:"No-CRUD, UI, Technical"},{Slogan:"The smoothest path from DID to done.",Tags:"DID, Workflow, Chill"},{Slogan:"Because your dApp deserves more than boilerplate.",Tags:"Blockchain, DevX, Efficiency"},{Slogan:"Own your data. Own your flow.",Tags:"SSI, Control, Ownership"},{Slogan:"Write logic like it belongs with the data — because it does.",Tags:"Data Logic, Developer, Smart"},{Slogan:"From blockchain contracts to smarter frontends.",Tags:"Smart Contracts, UI, DX"},{Slogan:"No caffeine. No boilerplate. Just the future.",Tags:"No-CRUD, Coffee-themed, Futuristic"},{Slogan:"The future of web3 UX is Decaf-TS.",Tags:"Blockchain, UX, Vision"},{Slogan:"Code with confidence. Govern with clarity.",Tags:"Blockchain, Governance, Calm"},{Slogan:"Interfaces that obey the data, not the other way around.",Tags:"UI, Data Logic, Self-aware"},{Slogan:"Brew business logic right into your bytes.",Tags:"Data Logic, Coffee-themed, Fun"},{Slogan:"DIDs done differently — and delightfully.",Tags:"DID, Self-Sovereign, Playful"},{Slogan:"Decaf-TS-TS: Where blockchain contracts meet smart interfaces.",Tags:"Blockchain, Smart Contracts, Tech"},{Slogan:"Ship dApps without the stress.",Tags:"Blockchain, Cheerful, Developer"},{Slogan:"No boilerplate, no problem — Decaf-TS your data.",Tags:"Data, No-CRUD, Chill"},{Slogan:"From DID to UI, without breaking a sweat.",Tags:"DID, SSI, UI, Calm"},{Slogan:"Decaf-TS-TS: Your frontend already understands your blockchain contract.",Tags:"Smart Contracts, DX, Magic"},{Slogan:"Self-sovereign by design. Productive by default.",Tags:"SSI, Developer, Calm"},{Slogan:"Build once. Deploy everywhere. Decentralized and delightful.",Tags:"Blockchain, Multi-platform, Happy"},{Slogan:"Data that defines its own destiny.",Tags:"SSI, Data-driven, Empowerment"},{Slogan:"Goodbye boilerplate, hello intent-based interfaces.",Tags:"No-CRUD, UI, Technical"},{Slogan:"The smoothest path from DID to done.",Tags:"DID, Workflow, Chill"},{Slogan:"Because your dApp deserves more than boilerplate.",Tags:"Blockchain, DevX, Efficiency"},{Slogan:"Own your data. Own your flow.",Tags:"SSI, Control, Ownership"},{Slogan:"Write logic like it belongs with the data — because it does.",Tags:"Data Logic, Developer, Smart"},{Slogan:"From blockchain contracts to smarter frontends.",Tags:"Smart Contracts, UI, DX"},{Slogan:"No caffeine. No boilerplate. Just the future.",Tags:"No-CRUD, Coffee-themed, Futuristic"},{Slogan:"The future of web3 UX is Decaf-TS.",Tags:"Blockchain, UX, Vision"},{Slogan:"Code with confidence. Govern with clarity.",Tags:"Blockchain, Governance, Calm"},{Slogan:"Interfaces that obey the data, not the other way around.",Tags:"UI, Data Logic, Self-aware"},{Slogan:"Brew business logic right into your bytes.",Tags:"Data Logic, Coffee-themed, Fun"},{Slogan:"DIDs done differently — and delightfully.",Tags:"DID, Self-Sovereign, Playful"},{Slogan:"Decaf-TS-TS: Where blockchain contracts meet smart interfaces.",Tags:"Blockchain, Smart Contracts, Tech"},{Slogan:"Ship dApps without the stress.",Tags:"Blockchain, Cheerful, Developer"},{Slogan:"No boilerplate, no problem — Decaf-TS your data.",Tags:"Data, No-CRUD, Chill"},{Slogan:"From DID to UI, without breaking a sweat.",Tags:"DID, SSI, UI, Calm"},{Slogan:"Decaf-TS-TS: Your frontend already understands your blockchain contract.",Tags:"Smart Contracts, DX, Magic"},{Slogan:"Self-sovereign by design. Productive by default.",Tags:"SSI, Developer, Calm"},{Slogan:"Build once. Deploy everywhere. Decentralized and delightful.",Tags:"Blockchain, Multi-platform, Happy"},{Slogan:"Data that defines its own destiny.",Tags:"SSI, Data-driven, Empowerment"},{Slogan:"Goodbye boilerplate, hello intent-based interfaces.",Tags:"No-CRUD, UI, Technical"},{Slogan:"The smoothest path from DID to done.",Tags:"DID, Workflow, Chill"},{Slogan:"Because your dApp deserves more than boilerplate.",Tags:"Blockchain, DevX, Efficiency"},{Slogan:"Own your data. Own your flow.",Tags:"SSI, Control, Ownership"},{Slogan:"Write logic like it belongs with the data — because it does.",Tags:"Data Logic, Developer, Smart"},{Slogan:"From blockchain contracts to smarter frontends.",Tags:"Smart Contracts, UI, DX"},{Slogan:"No caffeine. No boilerplate. Just the future.",Tags:"No-CRUD, Coffee-themed, Futuristic"},{Slogan:"The future of web3 UX is Decaf-TS.",Tags:"Blockchain, UX, Vision"},{Slogan:"Code with confidence. Govern with clarity.",Tags:"Blockchain, Governance, Calm"},{Slogan:"Interfaces that obey the data, not the other way around.",Tags:"UI, Data Logic, Self-aware"},{Slogan:"Brew business logic right into your bytes.",Tags:"Data Logic, Coffee-themed, Fun"},{Slogan:"DIDs done differently — and delightfully.",Tags:"DID, Self-Sovereign, Playful"}],Q=["","","","","","","","",""];function ee(e){const t=te(),o="# ░▒▓███████▓▒░ ░▒▓████████▓▒░ ░▒▓██████▓▒░ ░▒▓██████▓▒░ ░▒▓████████▓▒░ ░▒▓████████▓▒░ ░▒▓███████▓▒░ \n# ( ( ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░ \n# ) ) ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░ \n# [=======] ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓██████▓▒░ ░▒▓█▓▒░ ░▒▓████████▓▒░ ░▒▓██████▓▒░ ░▒▓█▓▒░ ░▒▓██████▓▒░ \n# `-----´ ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░ \n# ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░ \n# ░▒▓███████▓▒░ ░▒▓████████▓▒░ ░▒▓██████▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓███████▓▒░ \n#".split("\n"),a=o.reduce((e,t)=>Math.max(e,t.length),0);o.push(`# ${t.padStart(a-3)}`),o.forEach((t,o)=>{(e?e.info.bind(e):console.log.bind(console))(g(t||"").raw(Q[o]).text)})}function te(e){try{return e=void 0===e?Math.floor(Math.random()*Z.length):e,Z[e].Slogan}catch(e){throw new Error(`Failed to retrieve slogans: ${e}`)}}class oe extends s{constructor(e,t={},a=[]){super(),this.name=e,this.inputs=t,this.requirements=a,oe.log||Object.defineProperty(oe,"log",{writable:!1,value:o.for(oe.name)}),this.inputs=Object.assign({},b,t)}async checkRequirements(){const{prod:e,dev:t,peer:o}=await H(),a=[],s=Array.from(new Set([...e,...t,...o]).values()).map(e=>e.name);for(const e of this.requirements)s.includes(e)||a.push(e);a.length}help(e){return this.log.info("This is help. I'm no use because I should have been overridden.")}async execute(){const e=T.parseArgs(this.inputs),t=n.accumulate(C).accumulate(e.values),{version:o,help:a,banner:s}=t;if(o)return X();if(a)return this.help(e);let r;s&&ee(this.log.for(ee,{timestamp:!1,style:!1,context:!1,logLevel:!1}));try{r=await this.run(t)}catch(e){throw e}return r}}class ae{static{this.log=o.for(ae)}static async downloadFile(e){return new Promise((t,o)=>{!function e(a){a=encodeURI(a),d.get(a,s=>{if(301===s.statusCode||307===s.statusCode)return e(s.headers.location);if(200!==s.statusCode)return ae.log.error(`Failed to fetch ${a} (status: ${s.statusCode})`),o(new Error(`Failed to fetch ${a}`));let n="";s.on("data",e=>{n+=e}),s.on("error",e=>{o(e)}),s.on("end",()=>{t(n)})})}(e)})}}function se(e){return e?Array.isArray(e)?e.map(e=>`${e}`.trim()).filter(Boolean):`${e}`.split(",").map(e=>e.trim()).filter(Boolean):[]}function ne(e){return e.replace(/^@/,"").split(/[/\-_.]+/).filter(Boolean).map((e,t)=>0===t?e.replace(/[^a-zA-Z0-9]/g,""):`${e.charAt(0).toUpperCase()}${e.slice(1)}`).join("")}function re(){let e;try{e=G(process.cwd())}catch{e=void 0}try{if(!(e&&(Object.keys(e.dependencies||{}).length>0||Object.keys(e.devDependencies||{}).length>0||Object.keys(e.peerDependencies||{}).length>0))){const t=c.resolve(__dirname,"../../..");try{e=G(t)}catch{}}}catch{}const t=Object.keys(e&&e.dependencies||{}),o=Object.keys(e&&e.peerDependencies||{}),a=Object.keys(e&&e.devDependencies||{});return Array.from(new Set([...t,...o,...a]))}var ie,ce;!function(e){e.CJS="commonjs",e.ESM="es2022"}(ie||(ie={})),function(e){e.BUILD="build",e.BUNDLE="bundle",e.ALL="all"}(ce||(ce={}));const le={prod:{type:"boolean",default:!1},dev:{type:"boolean",default:!1},buildMode:{type:"string",default:ce.ALL},includes:{type:"string",default:""},externals:{type:"string",default:""},docs:{type:"boolean",default:!1},commands:{type:"boolean",default:!1},banner:{type:"boolean",default:!1}},ge=(e=".cjs")=>{const t=de.log.for(ge),o=new Map;return a=>s=>{const n=c.dirname(s.fileName);function r(a){const s=JSON.stringify([n,a]),r=o.get(s);if(null!=r)return r;let l,g=a;try{g=c.resolve(n,g+".ts")}catch(e){throw new Error(`Failed to resolve path ${a}: ${e}`)}try{l=i.statSync(g)}catch(e){try{t.verbose(`Testing existence of path ${g} as a folder defaulting to index file`),l=i.statSync(g.replace(/\.ts$/gm,""))}catch(t){throw new Error(`Failed to resolve path ${a}: ${e}, ${t}`)}}if(l.isDirectory()&&(g=g.replace(/\.ts$/gm,"/index.ts")),c.isAbsolute(g)){const t=(/\.tsx?$/.exec(c.basename(g))||[])[0]||void 0;g="./"+c.relative(n,c.resolve(c.dirname(g),c.basename(g,t)+e))}return o.set(s,g),g}return y.visitNode(s,function e(t){if(function(e){return!(!y.isImportDeclaration(e)&&!y.isExportDeclaration(e))&&(void 0!==e.moduleSpecifier&&(!!y.isStringLiteral(e.moduleSpecifier)&&(!(!e.moduleSpecifier.text.startsWith("./")&&!e.moduleSpecifier.text.startsWith("../"))&&""===c.extname(e.moduleSpecifier.text))))}(t)){if(y.isImportDeclaration(t)){const e=r(t.moduleSpecifier.text),o=a.factory.createStringLiteral(e);return a.factory.updateImportDeclaration(t,t.modifiers,t.importClause,o,void 0)}if(y.isExportDeclaration(t)){const e=r(t.moduleSpecifier.text),o=a.factory.createStringLiteral(e);return a.factory.updateExportDeclaration(t,t.modifiers,t.isTypeOnly,t.exportClause,o,void 0)}}return y.visitEachChild(t,e,a)})}};class de extends oe{constructor(){super("BuildScripts",Object.assign({},b,le)),this.replacements={};const e=G(),{name:t,version:o}=e;this.pkgName=t.includes("@")?t.split("/")[1]:t,this.pkgVersion=o,this.replacements["0.4.2"]=this.pkgVersion,this.replacements["@decaf-ts/utils"]=t}patchFiles(e){const t=this.log.for(this.patchFiles),{name:o,version:a}=G();t.info(`Patching ${o} ${a} module in ${e}...`);i.statSync(e).isDirectory()&&i.readdirSync(e,{withFileTypes:!0,recursive:!0}).filter(e=>e.isFile()).forEach(e=>P(c.join(e.parentPath,e.name),this.replacements)),t.verbose(`Module ${o} ${a} patched in ${e}...`)}reportDiagnostics(e,t){const o=this.formatDiagnostics(e);if(!Object.values(r).includes(t))throw new Error(`Invalid LogLevel ${t}`);try{this.log[t](o)}catch(e){throw console.warn(`Failed to get logger for ${t}`),e}return o}formatDiagnostics(e){return e.map(e=>{let t="";if(e.file&&e.start){const{line:o,character:a}=e.file.getLineAndCharacterOfPosition(e.start);t+=`${e.file.fileName} (${o+1},${a+1})`}return t+=": "+y.flattenDiagnosticMessageText(e.messageText,"\n"),t}).join("\n")}readConfigFile(e){const t=i.readFileSync(e).toString(),o=y.parseConfigFileTextToJson(e,t),a=o.config;a||this.reportDiagnostics([o.error],r.error);const s=y.parseJsonConfigFileContent(a,y.sys,c.dirname(e));return s.errors.length>0&&this.reportDiagnostics(s.errors,r.error),s}evalDiagnostics(e){if(e&&e.length>0){const t=e.filter(e=>e.category===y.DiagnosticCategory.Error),o=e.filter(e=>e.category===y.DiagnosticCategory.Warning),a=e.filter(e=>e.category===y.DiagnosticCategory.Suggestion),s=e.filter(e=>e.category===y.DiagnosticCategory.Message);o.length&&this.reportDiagnostics(o,r.info),t.length&&(this.reportDiagnostics(e,r.error),this.log.info(`TypeScript reported ${e.length} diagnostic(s) during check; aborting.`)),a.length&&this.reportDiagnostics(a,r.info),s.length&&this.reportDiagnostics(s,r.info)}}preCheckDiagnostics(e){const t=y.getPreEmitDiagnostics(e);this.evalDiagnostics(t)}async checkTsDiagnostics(e,t,o=!1){const a=this.log.for(this.checkTsDiagnostics);let s;try{s=this.readConfigFile("./tsconfig.json")}catch(e){throw new Error(`Failed to parse tsconfig.json: ${e}`)}o?(s.options.module=w.AMD,s.options.outDir="dist",s.options.isolatedModules=!1,s.options.outFile=this.pkgName):(s.options.outDir="lib"+(t===ie.ESM?"/esm":""),s.options.module=t===ie.ESM?w.ES2022:w.CommonJS),e?(s.options.inlineSourceMap=!0,s.options.sourceMap=!1):s.options.sourceMap=!1;const n=y.createProgram(s.fileNames,s.options);this.preCheckDiagnostics(n),a.verbose(`TypeScript checks passed (${o?"bundle":"normal"} mode).`)}async buildTs(e,t,o=!1){let a;this.log.for(this.buildTs).info(`Building ${this.pkgName} ${this.pkgVersion} module (${t}) in ${e?"dev":"prod"} mode...`);try{a=this.readConfigFile("./tsconfig.json")}catch(e){throw new Error(`Failed to parse tsconfig.json: ${e}`)}o?(a.options.module=w.AMD,a.options.outDir="dist",a.options.isolatedModules=!1,a.options.outFile=this.pkgName):(a.options.outDir="lib"+(t===ie.ESM?"/esm":""),a.options.module=t===ie.ESM?w.ES2022:w.CommonJS),e?(a.options.inlineSourceMap=!0,a.options.sourceMap=!1):a.options.sourceMap=!1;const s=y.createProgram(a.fileNames,a.options),n={};t===ie.CJS?n.before=[ge(".cjs")]:t===ie.ESM&&(n.before=[ge(".js")]);const r=s.emit(void 0,void 0,void 0,void 0,n),i=y.getPreEmitDiagnostics(s).concat(r.diagnostics);this.evalDiagnostics(i)}async build(e,t,o=!1){const a=this.log.for(this.build);if(await this.buildTs(e,t,o),a.verbose(`Module ${this.pkgName} ${this.pkgVersion} (${t}) built in ${e?"dev":"prod"} mode...`),t===ie.CJS&&!o){const e=L("lib",e=>e.endsWith(".js")&&!e.includes("/esm/"));for(const t of e){a.verbose(`Patching ${t}'s cjs imports...`);const e=t.replace(".js",".cjs");await J(t,e)}}}copyAssets(e){const t=this.log.for(this.copyAssets);let o=!1;try{o=i.statSync("./src/assets").isDirectory()}catch(e){return t.verbose("No assets found in ./src/assets to copy")}o&&R("./src/assets",`./${e===ie.CJS?"lib":"dist"}/assets`)}async bundle(e,t,o,a="src/index.ts",s=this.pkgName,n,r=["prompts","styled-string-builder","typed-object-accumulator","@decaf-ts/logging"]){await this.checkTsDiagnostics(t,e,!0);const i=e===ie.ESM,c=this.pkgName,l=Array.from(new Set([...se(r)]));let g=se(n);0===g.length&&(g=re());const d=Array.from(new Set([...function(){try{return Array.isArray(S)?S:[]}catch{return["fs","path","process","child_process","util","https","http","os","stream","crypto","zlib","net","tls","url","querystring","assert","events","tty","dns","querystring"]}}(),...g])),y=[h({compilerOptions:{module:"esnext",declaration:!1,outDir:o?"bin":"dist"},include:["src/**/*.ts"],exclude:["node_modules","**/*.spec.ts"],tsconfig:"./tsconfig.json"}),m()];if(!t)try{const C=await import("@rollup/plugin-terser"),D=C&&C.terser||C.default||C;y.push(D())}catch{}o&&y.push(u({include:[],exclude:g}),p({resolveOnly:l}));const w={input:a,plugins:y,external:d},T={};d.forEach(e=>{T[e]=ne(e)});const b=[{file:`${o?"bin/":"dist/"}${s||".bundle."+(t?"":"min")}${i?".esm":""}.cjs`,format:o?"cjs":i?"esm":"umd",name:c,esModule:i,sourcemap:!!t&&"inline",globals:T,exports:"auto"}];try{const v=await f(w);async function k(e){for(const t of b)await e.write(t)}console.log(v.watchFiles),await k(v)}catch($){throw new Error(`Failed to bundle: ${$}`)}}async buildByEnv(e,t=ce.ALL,o,a){try{W("lib")}catch(e){}try{W("dist")}catch(e){}i.mkdirSync("lib"),i.mkdirSync("dist"),[ce.ALL,ce.BUILD].includes(t)&&(await this.build(e,ie.ESM),await this.build(e,ie.CJS),this.patchFiles("lib")),[ce.ALL,ce.BUNDLE].includes(t)&&(await this.bundle(ie.ESM,e,!1,"src/index.ts",this.pkgName,a,o),await this.bundle(ie.CJS,e,!1,"src/index.ts",this.pkgName,a,o),this.patchFiles("dist")),this.copyAssets(ie.CJS),this.copyAssets(ie.ESM)}async buildDev(e=ce.ALL,t,o){return this.buildByEnv(!0,e,t,o)}async buildProd(e=ce.ALL,t,o){return this.buildByEnv(!1,e,t,o)}async buildDocs(){await B("npm install better-docs taffydb").promise,await B("npx markdown-include ./workdocs/readme-md.json").promise,await B("npx jsdoc -c ./workdocs/jsdocs.json -t ./node_modules/better-docs").promise,await B("npm remove better-docs taffydb").promise,[{src:"workdocs/assets",dest:"./docs/workdocs/assets"},{src:"workdocs/reports/coverage",dest:"./docs/workdocs/reports/coverage"},{src:"workdocs/reports/html",dest:"./docs/workdocs/reports/html"},{src:"workdocs/resources",dest:"./docs/workdocs/resources"},{src:"LICENSE.md",dest:"./docs/LICENSE.md"}].forEach(e=>{const{src:t,dest:o}=e;R(t,o)})}async run(e){const{dev:t,prod:o,docs:a,buildMode:s,includes:n,externals:r}=e;return t?await this.buildDev(s,n,r):o?await this.buildProd(s,n,r):a?await this.buildDocs():void 0}}const fe={ci:{type:"boolean",default:!0},message:{type:"string",short:"m"},tag:{type:"string",short:"t",default:void 0}};class he extends oe{constructor(){super("ReleaseScript",fe)}async prepareVersion(e){const t=this.log.for(this.prepareVersion);return(e=this.testVersion(e||""))||(t.verbose("No release message provided. Prompting for one:"),t.info("Listing latest git tags:"),await B("git tag --sort=-taggerdate | head -n 5").promise,await T.insistForText("tag","Enter the new tag number (accepts v*.*.*[-...])",e=>!!e.toString().match(/^v[0-9]+\.[0-9]+.[0-9]+(-[0-9a-zA-Z-]+)?$/)))}testVersion(e){const t=this.log.for(this.testVersion);switch(e=e.trim().toLowerCase()){case k.PATCH:case k.MINOR:case k.MAJOR:return t.verbose(`Using provided SemVer update: ${e}`,1),e;default:return t.verbose(`Testing provided version for SemVer compatibility: ${e}`,1),new RegExp(v).test(e)?(t.verbose(`version approved: ${e}`,1),e):void t.debug(`Invalid version number: ${e}`)}}async prepareMessage(e){const t=this.log.for(this.prepareMessage);return e||(t.verbose("No release message provided. Prompting for one"),await T.insistForText("message","What should be the release message/ticket?",e=>!!e&&e.toString().length>5))}async run(e){let t;const{ci:o}=e;let{tag:a,message:s}=e;a=await this.prepareVersion(a),s=await this.prepareMessage(s),t=await B(`npm run prepare-release -- ${a} ${s}`,{cwd:process.cwd()}).promise,t=await B("git status --porcelain").promise,await t,t.logs.length&&await T.askConfirmation("git-changes","Do you want to push the changes to the remote repository?",!0)&&(await B("git add .").promise,await B(`git commit -m "${a} - ${s} - after release preparation${o?"":$}"`).promise),await B(`npm version "${a}" -m "${s}${o?"":$}"`).promise,await B("git push --follow-tags").promise,o||await B("NPM_TOKEN=$(cat .npmtoken) npm publish --access public").promise}}const ue="https://raw.githubusercontent.com/decaf-ts/ts-workspace/master",pe={templates:[".github/ISSUE_TEMPLATE/bug_report.md",".github/ISSUE_TEMPLATE/feature_request.md",".github/FUNDING.yml"],workflows:[".github/workflows/codeql-analysis.yml",".github/workflows/jest-coverage.yaml",".github/workflows/nodejs-build-prod.yaml",".github/workflows/pages.yaml",".github/workflows/publish-on-release.yaml",".github/workflows/release-on-tag.yaml",".github/workflows/snyk-analysis.yaml"],ide:[".idea/runConfigurations/All Tests.run.xml",".idea/runConfigurations/build.run.xml",".idea/runConfigurations/build_prod.run.xml",".idea/runConfigurations/coverage.run.xml",".idea/runConfigurations/docs.run.xml",".idea/runConfigurations/drawings.run.xml",".idea/runConfigurations/flash-forward.run.xml",".idea/runConfigurations/Integration_Tests.run.xml",".idea/runConfigurations/Bundling_Tests.run.xml",".idea/runConfigurations/lint-fix.run.xml",".idea/runConfigurations/release.run.xml",".idea/runConfigurations/test_circular.run.xml",".idea/runConfigurations/uml.run.xml",".idea/runConfigurations/Unit Tests.run.xml",".idea/runConfigurations/update-scripts.run.xml"],docs:["workdocs/tutorials/Contributing.md","workdocs/tutorials/Documentation.md","workdocs/tutorials/For Developers.md","workdocs/2-Badges.md","workdocs/jsdocs.json","workdocs/readme-md.json"],styles:[".prettierrc","eslint.config.js"],scripts:["bin/update-scripts.cjs","bin/tag-release.cjs","bin/build-scripts.cjs"],tests:["jest.config.ts","workdocs/reports/jest.coverage.config.ts"],typescript:["tsconfig.json"],docker:["Dockerfile"],automation:["workdocs/confluence/Continuous Integration-Deployment/GitHub.md","workdocs/confluence/Continuous Integration-Deployment/Jira.md","workdocs/confluence/Continuous Integration-Deployment/Teams.md"]},me={boot:{type:"boolean"},org:{type:"string",short:"o"},name:{type:"string",short:"n",default:void 0},author:{type:"string",short:"a",default:void 0},all:{type:"boolean"},license:{type:"string",message:"Pick the license"},scripts:{type:"boolean"},styles:{type:"boolean"},docs:{type:"boolean"},ide:{type:"boolean"},workflows:{type:"boolean"},templates:{type:"boolean"},typescript:{type:"boolean"},docker:{type:"boolean"},pkg:{type:"boolean"},dependencies:{type:"boolean"},tests:{type:"boolean"},automation:{type:"boolean"}};class Se extends oe{constructor(){super("TemplateSync",me),this.replacements={},this.getStyles=()=>this.downloadOption("styles"),this.getTemplates=()=>this.downloadOption("templates"),this.getWorkflows=()=>this.downloadOption("workflows"),this.getDocs=()=>this.downloadOption("docs"),this.getTypescript=()=>this.downloadOption("typescript"),this.getAutomation=()=>this.downloadOption("automation"),this.getTests=()=>this.downloadOption("tests"),this.getDocker=()=>this.downloadOption("docker")}loadValuesFromPackage(){const e=process.cwd(),t=G(e,"author");let o,a=G(e,"name");if(a.startsWith("@")){const e=a.split("/");a=e[1],o=e[0].replace("@","")}["Tiago Venceslau","TiagoVenceslau","${author}"].forEach(e=>this.replacements[e]=t),["TS-Workspace","ts-workspace","${name}"].forEach(e=>this.replacements[e]=a),["decaf-ts","${org}"].forEach(e=>this.replacements[e]=o||'""'),this.replacements["${org_or_owner}"]=o||a}async downloadOption(e){if(!(e in pe))throw new Error(`Option "${e}" not found in options`);const t=pe[e];for(const e of t){this.log.info(`Downloading ${e}`);let t=await ae.downloadFile(`${ue}/${e}`);t=a(t,this.replacements),U(c.join(process.cwd(),e),t)}}async getLicense(e){this.log.info(`Downloading ${e} license`);const t=`${ue}/workdocs/licenses/${e}.md`;let o=await ae.downloadFile(t);o=a(o,this.replacements),U(c.join(process.cwd(),"LICENSE.md"),o),V("license",e)}async getIde(){i.mkdirSync(c.join(process.cwd(),".idea","runConfigurations"),{recursive:!0}),await this.downloadOption("ide")}async getScripts(){await this.downloadOption("scripts"),this.log.info("please re-run the command"),process.exit(0)}async initPackage(e,t,o){try{const a=G();delete a[E],a.name=e,a.version="0.0.1",a.author=t,a.license=o,i.writeFileSync("package.json",JSON.stringify(a,null,2))}catch(e){throw new Error(`Error fixing package.json: ${e}`)}}async updatePackageScrips(){try{const e=JSON.parse(await ae.downloadFile(`${ue}/package.json`)),{scripts:t}=e,o=G();Object.keys(o.scripts).forEach(e=>{if(e in t){const s=a(t[e],this.replacements);s!==t[e]&&(o.scripts[e]=s)}}),o.exports.require=e.exports.require,o.exports.import=e.exports.import,o.types=e.types,i.writeFileSync("package.json",JSON.stringify(o,null,2))}catch(e){throw new Error(`Error fixing package.json scripts: ${e}`)}}async createTokenFiles(){const e=this.log.for(this.createTokenFiles),t=await T.insistForText("token","please input your github token",e=>!!e.match(/^ghp_[0-9a-zA-Z]{36}$/g));Object.values(I).forEach(o=>{try{let a;try{a=i.existsSync(o)}catch(a){return e.info(`Token file ${o} not found. Creating a new one...`),void i.writeFileSync(o,".token"===o?t:"")}a||i.writeFileSync(o,".token"===o?t:"")}catch(e){throw new Error(`Error creating token file ${o}: ${e}`)}})}async getOrg(){const e=await T.askText("Organization","Enter the organization name (will be used to scope your npm project. leave blank to create a unscoped project):");return await T.askConfirmation("Confirm organization","Is this organization correct?",!0)?e:this.getOrg()}async auditFix(){return await B("npm audit fix --force").promise}patchFiles(){const e=[...i.readdirSync(c.join(process.cwd(),"src"),{recursive:!0,withFileTypes:!0}).filter(e=>e.isFile()).map(e=>c.join(e.parentPath,e.name)),...i.readdirSync(c.join(process.cwd(),"workdocs"),{recursive:!0,withFileTypes:!0}).filter(e=>e.isFile()&&e.name.endsWith(".md")).map(e=>c.join(e.parentPath,e.name)),c.join(process.cwd(),".gitlab-ci.yml"),c.join(process.cwd(),"workdocs","jsdocs.json")];for(const t of e)P(t,this.replacements)}async updateDependencies(){try{const e=JSON.parse(await ae.downloadFile(`${ue}/package.json`)),{devDependencies:t}=e,o=G();Object.keys(o.scripts).forEach(e=>{if(e in t){const a=t[e];a!==t[e]&&(o.devDependencies=o.devDependencies||{},o.devDependencies[e]=a)}}),i.writeFileSync("package.json",JSON.stringify(o,null,2)),await B("npm install").promise}catch(e){throw new Error(`Error fixing package.json dependencies: ${e}`)}}async run(e){let{license:t}=e;const{boot:o}=e;let{all:a,scripts:s,styles:n,docs:r,ide:i,workflows:c,templates:l,docker:g,typescript:d,dependencies:f,tests:h,automation:u,pkg:p}=e;if((s||n||r||i||c||l||g||d||u||f||h||p)&&(a=!1),o){const e=await this.getOrg(),o=await T.insistForText("Project name","Enter the project name:",e=>e.length>1),a=await T.insistForText("Author","Enter the author name:",e=>e.length>1),s=e?`@${e}/${o}`:o;await this.initPackage(s,a,t),await this.createTokenFiles(),await this.auditFix(),this.patchFiles()}if(a&&(s=!1,n=!0,r=!0,i=!0,c=!0,l=!0,g=!0,d=!0,p=!0,f=!0,h=!0,u=!1),void 0===s&&(s=await T.askConfirmation("scripts","Do you want to get scripts?",!0)),s&&await this.getScripts(),this.loadValuesFromPackage(),!a&&void 0===t){await T.askConfirmation("license","Do you want to set a license?",!0)&&(t=await T.insistForText("license","Enter the desired License (MIT|GPL|Apache|LGPL|AGPL):",e=>!!e&&!!e.match(/^(MIT|GPL|Apache|LGPL|AGPL)$/g)))}void 0!==t&&await this.getLicense(t),void 0===i&&(i=await T.askConfirmation("ide","Do you want to get ide configs?",!0)),i&&await this.getIde(),void 0===d&&(d=await T.askConfirmation("typescript","Do you want to get typescript configs?",!0)),d&&await this.getTypescript(),void 0===g&&(g=await T.askConfirmation("docker","Do you want to get docker configs?",!0)),g&&await this.getDocker(),void 0===u&&(u=await T.askConfirmation("automation","Do you want to get automation configs?",!0)),u&&await this.getAutomation(),void 0===n&&(n=await T.askConfirmation("styles","Do you want to get styles?",!0)),n&&await this.getStyles(),void 0===r&&(r=await T.askConfirmation("docs","Do you want to get docs?",!0)),r&&await this.getDocs(),void 0===c&&(c=await T.askConfirmation("workflows","Do you want to get workflows?",!0)),c&&await this.getWorkflows(),void 0===l&&(l=await T.askConfirmation("templates","Do you want to get templates?",!0)),l&&await this.getTemplates(),void 0===p&&(p=await T.askConfirmation("pkg","Do you want to update your package.json scripts?",!0)),p&&await this.updatePackageScrips(),void 0===h&&(h=await T.askConfirmation("pkg","Do you want to update your test configs?",!0)),h&&await this.getTests(),void 0===f&&(f=await T.askConfirmation("pkg","Do you want to update dev dependencies?",!0)),f&&await this.updateDependencies()}}class ye extends x{constructor(e,t,o,a="g"){super(e,t);try{this.regexp="string"==typeof o?new RegExp(o,a):o}catch(e){throw new Error(`Invalid regular expression: ${e}`)}}test(e){let t;this.regexp.lastIndex=0;try{t=this.regexp.exec(e)}catch(t){return console.debug(`Failed to parse chunk: ${e}\nError: ${t} `)}return t}testAndResolve(e){const t=this.test(e);t&&this.resolve(t[0])}testAndReject(e){const t=this.test(e);t&&this.reject(t[0])}data(e){super.data(e),this.testAndResolve(String(e))}error(e){super.error(e),this.testAndReject(String(e))}}const we="0.4.2",Te="@decaf-ts/utils";export{F as AbortCode,de as BuildScripts,oe as Command,b as DefaultCommandOptions,C as DefaultCommandValues,D as Encoding,ae as HttpClient,$ as NoCIFLag,Te as PACKAGE_NAME,ye as RegexpOutputWriter,he as ReleaseScript,k as SemVersion,v as SemVersionRegex,E as SetupScriptKey,x as StandardOutputWriter,Se as TemplateSync,I as Tokens,T as UserInput,we as VERSION,N as chainAbortController,R as copyFile,W as deletePath,L as getAllFiles,H as getDependencies,G as getPackage,re as getPackageDependencies,X as getPackageVersion,te as getSlogan,Y as installDependencies,q as installIfNotAvailable,j as lockify,K as normalizeImport,ne as packageToGlobal,se as parseList,P as patchFile,ee as printBanner,z as pushToGit,M as readFile,J as renameFile,B as runCommand,V as setPackageAttribute,A as spawnCommand,_ as updateDependencies,U as writeFile};