@fgv/ts-res-browser-cli 5.0.0-10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/.rush/temp/3af64718d5ebb0670455dcda3a089e94f135a801.tar.log +32 -0
  2. package/.rush/temp/chunked-rush-logs/ts-res-browser-cli.build.chunks.jsonl +5 -0
  3. package/.rush/temp/operation/build/all.log +5 -0
  4. package/.rush/temp/operation/build/log-chunks.jsonl +5 -0
  5. package/.rush/temp/operation/build/state.json +3 -0
  6. package/.rush/temp/shrinkwrap-deps.json +656 -0
  7. package/README.md +155 -0
  8. package/bin/ts-res-browser-cli.js +40 -0
  9. package/config/rig.json +16 -0
  10. package/lib/cli.d.ts +33 -0
  11. package/lib/cli.d.ts.map +1 -0
  12. package/lib/cli.js +364 -0
  13. package/lib/cli.js.map +1 -0
  14. package/lib/index.d.ts +4 -0
  15. package/lib/index.d.ts.map +1 -0
  16. package/lib/index.js +42 -0
  17. package/lib/index.js.map +1 -0
  18. package/lib/options.d.ts +100 -0
  19. package/lib/options.d.ts.map +1 -0
  20. package/lib/options.js +24 -0
  21. package/lib/options.js.map +1 -0
  22. package/lib/simpleBrowserLauncher.d.ts +32 -0
  23. package/lib/simpleBrowserLauncher.d.ts.map +1 -0
  24. package/lib/simpleBrowserLauncher.js +275 -0
  25. package/lib/simpleBrowserLauncher.js.map +1 -0
  26. package/lib/zipArchiver.d.ts +35 -0
  27. package/lib/zipArchiver.d.ts.map +1 -0
  28. package/lib/zipArchiver.js +148 -0
  29. package/lib/zipArchiver.js.map +1 -0
  30. package/package.json +50 -0
  31. package/rush-logs/ts-res-browser-cli.build.cache.log +3 -0
  32. package/rush-logs/ts-res-browser-cli.build.log +5 -0
  33. package/src/cli.ts +420 -0
  34. package/src/index.ts +26 -0
  35. package/src/options.ts +139 -0
  36. package/src/simpleBrowserLauncher.ts +278 -0
  37. package/src/zipArchiver.ts +153 -0
  38. package/tsconfig.json +7 -0
package/README.md ADDED
@@ -0,0 +1,155 @@
1
+ # @fgv/ts-res-browser-cli
2
+
3
+ Command-line interface to launch ts-res-browser with specified resources and configuration.
4
+
5
+ ## Features
6
+
7
+ - **ZIP Archive Creation**: Automatically creates ZIP archives containing resources and configuration for easy browser loading
8
+ - **Development Server Integration**: Can automatically start and manage the ts-res-browser development server
9
+ - **Flexible Resource Input**: Supports both individual files and entire directory trees
10
+ - **Configuration Management**: Works with predefined configurations or custom configuration files
11
+ - **Context Filtering**: Apply context filters and qualifier settings via command line
12
+ - **Interactive Mode**: Launch with sample data for exploration
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ npm install -g @fgv/ts-res-browser-cli
18
+ ```
19
+
20
+ Or use directly with npx (no installation required):
21
+
22
+ ```bash
23
+ npx @fgv/ts-res-browser-cli --help
24
+ ```
25
+
26
+ ## Usage
27
+
28
+ ### Basic Usage
29
+
30
+ ```bash
31
+ # Launch browser with resources from a directory
32
+ ts-res-browser-cli browse --input ./resources --config default --serve
33
+
34
+ # With npx (no installation required)
35
+ npx @fgv/ts-res-browser-cli --input ./resources --config default --serve
36
+
37
+ # Launch with a specific configuration file
38
+ ts-res-browser-cli browse --input ./my-resources --config ./config.json --serve
39
+
40
+ # Create ZIP and launch without opening browser automatically
41
+ ts-res-browser-cli browse --input ./resources --config extended-example --serve --no-open
42
+ ```
43
+
44
+ ### ZIP Workflow
45
+
46
+ The CLI automatically creates ZIP archives containing your resources and configuration:
47
+
48
+ 1. **ZIP Creation**: Resources and configuration are bundled into a timestamped ZIP file in your Downloads folder
49
+ 2. **Manifest Generation**: Each ZIP includes a manifest with metadata about the bundled content
50
+ 3. **Browser Integration**: The browser can directly load and process these ZIP archives
51
+
52
+ ```bash
53
+ # Creates: ~/Downloads/ts-res-bundle-[timestamp].zip
54
+ ts-res-browser-cli browse --input ./resources --config my-config --serve
55
+ ```
56
+
57
+ ### Server Options
58
+
59
+ The CLI provides several ways to launch the browser with a server:
60
+
61
+ ```bash
62
+ # Recommended: Universal server start (works everywhere)
63
+ ts-res-browser-cli browse --input ./resources --serve
64
+
65
+ # Development server (monorepo environment only)
66
+ ts-res-browser-cli browse --input ./resources --dev
67
+
68
+ # Connect to existing server
69
+ ts-res-browser-cli browse --input ./resources --url http://localhost:3001
70
+ ```
71
+
72
+ **Server Flag Behavior:**
73
+ - `--serve`: Works in both monorepo and published packages
74
+ - **Monorepo**: Starts webpack dev server with hot reloading (port 3000)
75
+ - **Published packages**: Starts static file server (port 8080)
76
+ - `--dev`: Development server with hot reloading (monorepo only)
77
+ - **Monorepo**: Same as `--serve`
78
+ - **Published packages**: Shows error with instructions to use `--serve`
79
+ - `--url`: Connect to existing server at specified URL
80
+
81
+ ### Advanced Options
82
+
83
+ ```bash
84
+ # Apply context filtering
85
+ ts-res-browser-cli browse \
86
+ --input ./resources \
87
+ --context-filter "language=en-US|territory=US" \
88
+ --reduce-qualifiers
89
+
90
+ # Interactive mode with sample data (requires server)
91
+ ts-res-browser-cli browse --interactive --serve
92
+
93
+ # Verbose output
94
+ ts-res-browser-cli browse --input ./resources --verbose
95
+ ```
96
+
97
+ ## Command Reference
98
+
99
+ ### `browse` Command
100
+
101
+ Launch ts-res-browser with specified resources and configuration.
102
+
103
+ **Options:**
104
+ - `-i, --input <path>`: Input file or directory path
105
+ - `--config <name|path>`: Predefined configuration name or file path
106
+ - `-c, --context <json>`: Context filter (JSON string)
107
+ - `--context-filter <token>`: Context filter (pipe-separated)
108
+ - `--qualifier-defaults <token>`: Qualifier defaults (pipe-separated)
109
+ - `--resource-types <types>`: Resource type filter (comma-separated)
110
+ - `--max-distance <number>`: Maximum distance for language matching
111
+ - `--reduce-qualifiers`: Remove perfectly matching qualifier conditions
112
+ - `-p, --port <number>`: Port for local browser instance
113
+ - `--url <url>`: URL of remote browser instance
114
+ - `--no-open`: Do not open browser automatically
115
+ - `--interactive`: Launch in interactive mode with sample data (requires --serve, --dev, or --url)
116
+ - `--serve`: Start server (dev in monorepo, serve in published packages) and connect automatically
117
+ - `--dev`: Start development server locally (monorepo only)
118
+ - `-v, --verbose`: Verbose output
119
+ - `-q, --quiet`: Quiet output
120
+
121
+ ### `config` Command
122
+
123
+ Manage system configurations (future enhancement).
124
+
125
+ ## ZIP Archive Format
126
+
127
+ Created ZIP archives contain:
128
+
129
+ ```
130
+ ts-res-bundle-[timestamp].zip
131
+ ├── manifest.json # Archive metadata
132
+ ├── input/ # Resource files (preserving structure)
133
+ │ ├── file1.json
134
+ │ └── subdirectory/
135
+ │ └── file2.json
136
+ └── config.json # Configuration (if provided)
137
+ ```
138
+
139
+ The manifest includes:
140
+ - Timestamp of archive creation
141
+ - Input file/directory metadata
142
+ - Configuration file metadata
143
+ - Original paths for reference
144
+
145
+ ## Integration with ts-res-browser
146
+
147
+ The browser application includes a dedicated ZIP Loader tool that can:
148
+ - Directly load ZIP archives created by this CLI
149
+ - Parse manifests and apply configurations automatically
150
+ - Process resources through the standard ts-res pipeline
151
+ - Provide seamless transition from CLI to browser workflow
152
+
153
+ ## License
154
+
155
+ MIT
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env node
2
+
3
+ /*
4
+ * Copyright (c) 2025 Erik Fortune
5
+ *
6
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ * of this software and associated documentation files (the "Software"), to deal
8
+ * in the Software without restriction, including without limitation the rights
9
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ * copies of the Software, and to permit persons to whom the Software is
11
+ * furnished to do so, subject to the following conditions:
12
+ *
13
+ * The above copyright notice and this permission notice shall be included in all
14
+ * copies or substantial portions of the Software.
15
+ *
16
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ * SOFTWARE.
23
+ */
24
+
25
+ const { TsResBrowserCliApp } = require('../lib/cli');
26
+
27
+ async function main() {
28
+ try {
29
+ const app = new TsResBrowserCliApp();
30
+ await app.run(process.argv);
31
+ } catch (error) {
32
+ console.error('Fatal error:', error.message);
33
+ process.exit(1);
34
+ }
35
+ }
36
+
37
+ main().catch((error) => {
38
+ console.error('Unhandled error:', error);
39
+ process.exit(1);
40
+ });
@@ -0,0 +1,16 @@
1
+ // The "rig.json" file directs tools to look for their config files in an external package.
2
+ // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package
3
+ {
4
+ "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json",
5
+ /**
6
+ * (Required) The name of the rig package to inherit from.
7
+ * It should be an NPM package name with the "-rig" suffix.
8
+ */
9
+ "rigPackageName": "@rushstack/heft-node-rig",
10
+ /**
11
+ * (Optional) Selects a config profile from the rig package. The name must consist of
12
+ * lowercase alphanumeric words separated by hyphens, for example "sample-profile".
13
+ * If omitted, then the "default" profile will be used."
14
+ */
15
+ "rigProfile": "default"
16
+ }
package/lib/cli.d.ts ADDED
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Main CLI class for ts-res-browser-cli
3
+ */
4
+ export declare class TsResBrowserCliApp {
5
+ private readonly _program;
6
+ private readonly _launcher;
7
+ constructor();
8
+ /**
9
+ * Runs the CLI with the provided arguments
10
+ */
11
+ run(argv: string[]): Promise<void>;
12
+ /**
13
+ * Sets up the CLI commands and options
14
+ */
15
+ private _setupCommands;
16
+ /**
17
+ * Check if any meaningful options were provided
18
+ */
19
+ private _hasOptions;
20
+ /**
21
+ * Handles the browse command
22
+ */
23
+ private _handleBrowseCommand;
24
+ /**
25
+ * Handles the config command (similar to ts-res-cli)
26
+ */
27
+ private _handleConfigCommand;
28
+ /**
29
+ * Parses and validates browse options
30
+ */
31
+ private _parseBrowseOptions;
32
+ }
33
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AA+BA;;GAEG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAU;IACnC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAwB;;IAQlD;;OAEG;IACU,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAI/C;;OAEG;IACH,OAAO,CAAC,cAAc;IA6GtB;;OAEG;IACH,OAAO,CAAC,WAAW;IAiBnB;;OAEG;YACW,oBAAoB;IAclC;;OAEG;YACW,oBAAoB;IA6KlC;;OAEG;IACH,OAAO,CAAC,mBAAmB;CAwC5B"}
package/lib/cli.js ADDED
@@ -0,0 +1,364 @@
1
+ "use strict";
2
+ /*
3
+ * Copyright (c) 2025 Erik Fortune
4
+ *
5
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ * of this software and associated documentation files (the "Software"), to deal
7
+ * in the Software without restriction, including without limitation the rights
8
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ * copies of the Software, and to permit persons to whom the Software is
10
+ * furnished to do so, subject to the following conditions:
11
+ *
12
+ * The above copyright notice and this permission notice shall be included in all
13
+ * copies or substantial portions of the Software.
14
+ *
15
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ * SOFTWARE.
22
+ */
23
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
24
+ if (k2 === undefined) k2 = k;
25
+ var desc = Object.getOwnPropertyDescriptor(m, k);
26
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
27
+ desc = { enumerable: true, get: function() { return m[k]; } };
28
+ }
29
+ Object.defineProperty(o, k2, desc);
30
+ }) : (function(o, m, k, k2) {
31
+ if (k2 === undefined) k2 = k;
32
+ o[k2] = m[k];
33
+ }));
34
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
35
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
36
+ }) : function(o, v) {
37
+ o["default"] = v;
38
+ });
39
+ var __importStar = (this && this.__importStar) || (function () {
40
+ var ownKeys = function(o) {
41
+ ownKeys = Object.getOwnPropertyNames || function (o) {
42
+ var ar = [];
43
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
44
+ return ar;
45
+ };
46
+ return ownKeys(o);
47
+ };
48
+ return function (mod) {
49
+ if (mod && mod.__esModule) return mod;
50
+ var result = {};
51
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
52
+ __setModuleDefault(result, mod);
53
+ return result;
54
+ };
55
+ })();
56
+ Object.defineProperty(exports, "__esModule", { value: true });
57
+ exports.TsResBrowserCliApp = void 0;
58
+ const commander_1 = require("commander");
59
+ const ts_utils_1 = require("@fgv/ts-utils");
60
+ const TsRes = __importStar(require("@fgv/ts-res"));
61
+ const fs_1 = require("fs");
62
+ const path = __importStar(require("path"));
63
+ const simpleBrowserLauncher_1 = require("./simpleBrowserLauncher");
64
+ /**
65
+ * Main CLI class for ts-res-browser-cli
66
+ */
67
+ class TsResBrowserCliApp {
68
+ constructor() {
69
+ this._program = new commander_1.Command();
70
+ this._launcher = new simpleBrowserLauncher_1.SimpleBrowserLauncher();
71
+ this._setupCommands();
72
+ }
73
+ /**
74
+ * Runs the CLI with the provided arguments
75
+ */
76
+ async run(argv) {
77
+ await this._program.parseAsync(argv);
78
+ }
79
+ /**
80
+ * Sets up the CLI commands and options
81
+ */
82
+ _setupCommands() {
83
+ this._program
84
+ .name('ts-res-browser-cli')
85
+ .description('Launch ts-res-browser with specified resources and configuration')
86
+ .version('1.0.0');
87
+ // Default behavior (no command) - launch with options or show help
88
+ this._program
89
+ .option('-i, --input <path>', 'Input file or directory path')
90
+ .option('--config <name|path>', 'Predefined configuration name or system configuration file path (JSON, ISystemConfiguration format)')
91
+ .option('-c, --context <json>', 'Context filter for resources (JSON string)')
92
+ .option('--context-filter <token>', 'Context filter token (pipe-separated, e.g., "language=en-US|territory=US")')
93
+ .option('--qualifier-defaults <token>', 'Qualifier default values token (pipe-separated, e.g., "language=en-US,en-CA|territory=US")')
94
+ .option('--resource-types <types>', 'Resource type filter (comma-separated)')
95
+ .option('--max-distance <number>', 'Maximum distance for language matching', parseInt)
96
+ .option('--reduce-qualifiers', 'Remove perfectly matching qualifier conditions from filtered resources', false)
97
+ .option('-v, --verbose', 'Verbose output', false)
98
+ .option('-q, --quiet', 'Quiet output', false)
99
+ .option('-p, --port <number>', 'Port for local browser instance', parseInt)
100
+ .option('--url <url>', 'URL of remote browser instance')
101
+ .option('--no-open', 'Do not open browser automatically')
102
+ .option('--interactive', 'Launch in interactive mode with sample data', false)
103
+ .option('--dev', 'Automatically start development server locally', false)
104
+ .option('--serve', 'Start server (dev in monorepo, serve in published packages) and connect automatically', false)
105
+ .action(async (options) => {
106
+ // If no options provided, show help
107
+ if (!this._hasOptions(options)) {
108
+ this._program.help();
109
+ }
110
+ else {
111
+ await this._handleBrowseCommand(options);
112
+ }
113
+ });
114
+ // Browse command (explicit)
115
+ this._program
116
+ .command('browse')
117
+ .description('Launch browser with specified resources and configuration')
118
+ .option('-i, --input <path>', 'Input file or directory path')
119
+ .option('--config <name|path>', 'Predefined configuration name or system configuration file path (JSON, ISystemConfiguration format)')
120
+ .option('-c, --context <json>', 'Context filter for resources (JSON string)')
121
+ .option('--context-filter <token>', 'Context filter token (pipe-separated, e.g., "language=en-US|territory=US")')
122
+ .option('--qualifier-defaults <token>', 'Qualifier default values token (pipe-separated, e.g., "language=en-US,en-CA|territory=US")')
123
+ .option('--resource-types <types>', 'Resource type filter (comma-separated)')
124
+ .option('--max-distance <number>', 'Maximum distance for language matching', parseInt)
125
+ .option('--reduce-qualifiers', 'Remove perfectly matching qualifier conditions from filtered resources', false)
126
+ .option('-v, --verbose', 'Verbose output', false)
127
+ .option('-q, --quiet', 'Quiet output', false)
128
+ .option('-p, --port <number>', 'Port for local browser instance', parseInt)
129
+ .option('--url <url>', 'URL of remote browser instance')
130
+ .option('--no-open', 'Do not open browser automatically')
131
+ .option('--interactive', 'Launch in interactive mode with sample data', false)
132
+ .option('--dev', 'Automatically start development server locally', false)
133
+ .option('--serve', 'Start server (dev in monorepo, serve in published packages) and connect automatically', false)
134
+ .action(async (options) => {
135
+ await this._handleBrowseCommand(options);
136
+ });
137
+ // Config command (similar to ts-res-cli)
138
+ this._program
139
+ .command('config')
140
+ .description('Manage system configurations')
141
+ .option('-n, --name <name>', 'Predefined configuration name to print')
142
+ .option('-o, --output <path>', 'Output file path to save configuration')
143
+ .option('--validate <path>', 'Validate a configuration file')
144
+ .option('--normalize <path>', 'Normalize and validate a configuration file')
145
+ .option('--qualifier-defaults <token>', 'Qualifier default values token (pipe-separated, e.g., "language=en-US,en-CA|territory=US")')
146
+ .option('-l, --list', 'List all available predefined configurations')
147
+ .action(async (options) => {
148
+ await this._handleConfigCommand(options);
149
+ });
150
+ }
151
+ /**
152
+ * Check if any meaningful options were provided
153
+ */
154
+ _hasOptions(options) {
155
+ return !!(options.input ||
156
+ options.config ||
157
+ options.context ||
158
+ options.contextFilter ||
159
+ options.qualifierDefaults ||
160
+ options.resourceTypes ||
161
+ options.maxDistance !== undefined ||
162
+ options.reduceQualifiers ||
163
+ options.interactive ||
164
+ options.url ||
165
+ options.dev ||
166
+ options.serve);
167
+ }
168
+ /**
169
+ * Handles the browse command
170
+ */
171
+ async _handleBrowseCommand(options) {
172
+ const browseOptions = this._parseBrowseOptions(options);
173
+ if (browseOptions.isFailure()) {
174
+ console.error(`Error: ${browseOptions.message}`);
175
+ process.exit(1);
176
+ }
177
+ const result = await this._launcher.launch(browseOptions.value);
178
+ if (result.isFailure()) {
179
+ console.error(`Error: ${result.message}`);
180
+ process.exit(1);
181
+ }
182
+ }
183
+ /**
184
+ * Handles the config command (similar to ts-res-cli)
185
+ */
186
+ async _handleConfigCommand(options) {
187
+ try {
188
+ // List all predefined configurations
189
+ if (options.list) {
190
+ console.log('Available predefined configurations:');
191
+ console.log('- default');
192
+ console.log('- language-priority');
193
+ console.log('- territory-priority');
194
+ console.log('- extended-example');
195
+ return;
196
+ }
197
+ // Print a specific predefined configuration
198
+ if (options.name) {
199
+ const predefinedNames = [
200
+ 'default',
201
+ 'language-priority',
202
+ 'territory-priority',
203
+ 'extended-example'
204
+ ];
205
+ if (!predefinedNames.includes(options.name)) {
206
+ console.error(`Error: Predefined configuration '${options.name}' not found`);
207
+ console.error('Available configurations: default, language-priority, territory-priority, extended-example');
208
+ process.exit(1);
209
+ }
210
+ const configResult = TsRes.Config.getPredefinedDeclaration(options.name);
211
+ if (configResult.isFailure()) {
212
+ console.error(`Error: Failed to load predefined configuration '${options.name}': ${configResult.message}`);
213
+ process.exit(1);
214
+ }
215
+ let config = configResult.value;
216
+ // Apply qualifier defaults if provided
217
+ if (options.qualifierDefaults) {
218
+ const tempSystemConfigResult = TsRes.Config.SystemConfiguration.create(config);
219
+ if (tempSystemConfigResult.isFailure()) {
220
+ console.error(`Error: Failed to create system configuration: ${tempSystemConfigResult.message}`);
221
+ process.exit(1);
222
+ }
223
+ const tokens = new TsRes.Qualifiers.QualifierDefaultValueTokens(tempSystemConfigResult.value.qualifiers);
224
+ const defaultsResult = tokens.qualifierDefaultValuesTokenToDecl(options.qualifierDefaults);
225
+ if (defaultsResult.isFailure()) {
226
+ console.error(`Error: Failed to parse qualifier defaults: ${defaultsResult.message}`);
227
+ process.exit(1);
228
+ }
229
+ const updatedConfigResult = TsRes.Config.updateSystemConfigurationQualifierDefaultValues(config, defaultsResult.value);
230
+ if (updatedConfigResult.isFailure()) {
231
+ console.error(`Error: Failed to apply qualifier defaults: ${updatedConfigResult.message}`);
232
+ process.exit(1);
233
+ }
234
+ config = updatedConfigResult.value;
235
+ }
236
+ const output = JSON.stringify(config, null, 2);
237
+ if (options.output) {
238
+ const outputPath = path.resolve(options.output);
239
+ const outputDir = path.dirname(outputPath);
240
+ await fs_1.promises.mkdir(outputDir, { recursive: true });
241
+ await fs_1.promises.writeFile(outputPath, output, 'utf-8');
242
+ console.log(`Configuration saved to: ${outputPath}`);
243
+ }
244
+ else {
245
+ console.log(output);
246
+ }
247
+ return;
248
+ }
249
+ // Validate a configuration file
250
+ if (options.validate) {
251
+ const configPath = path.resolve(options.validate);
252
+ const configContent = await fs_1.promises.readFile(configPath, 'utf-8');
253
+ const configData = JSON.parse(configContent);
254
+ const configResult = TsRes.Config.Convert.systemConfiguration.convert(configData);
255
+ if (configResult.isFailure()) {
256
+ console.error(`Validation failed: ${configResult.message}`);
257
+ process.exit(1);
258
+ }
259
+ console.log(`Configuration file '${configPath}' is valid`);
260
+ return;
261
+ }
262
+ // Normalize a configuration file
263
+ if (options.normalize) {
264
+ const configPath = path.resolve(options.normalize);
265
+ const configContent = await fs_1.promises.readFile(configPath, 'utf-8');
266
+ const configData = JSON.parse(configContent);
267
+ const configResult = TsRes.Config.Convert.systemConfiguration.convert(configData);
268
+ if (configResult.isFailure()) {
269
+ console.error(`Normalization failed: ${configResult.message}`);
270
+ process.exit(1);
271
+ }
272
+ let normalizedConfig = configResult.value;
273
+ // Apply qualifier defaults if provided
274
+ if (options.qualifierDefaults) {
275
+ const tempSystemConfigResult = TsRes.Config.SystemConfiguration.create(normalizedConfig);
276
+ if (tempSystemConfigResult.isFailure()) {
277
+ console.error(`Error: Failed to create system configuration: ${tempSystemConfigResult.message}`);
278
+ process.exit(1);
279
+ }
280
+ const tokens = new TsRes.Qualifiers.QualifierDefaultValueTokens(tempSystemConfigResult.value.qualifiers);
281
+ const defaultsResult = tokens.qualifierDefaultValuesTokenToDecl(options.qualifierDefaults);
282
+ if (defaultsResult.isFailure()) {
283
+ console.error(`Error: Failed to parse qualifier defaults: ${defaultsResult.message}`);
284
+ process.exit(1);
285
+ }
286
+ const updatedConfigResult = TsRes.Config.updateSystemConfigurationQualifierDefaultValues(normalizedConfig, defaultsResult.value);
287
+ if (updatedConfigResult.isFailure()) {
288
+ console.error(`Error: Failed to apply qualifier defaults: ${updatedConfigResult.message}`);
289
+ process.exit(1);
290
+ }
291
+ normalizedConfig = updatedConfigResult.value;
292
+ }
293
+ const output = JSON.stringify(normalizedConfig, null, 2);
294
+ if (options.output) {
295
+ const outputPath = path.resolve(options.output);
296
+ const outputDir = path.dirname(outputPath);
297
+ await fs_1.promises.mkdir(outputDir, { recursive: true });
298
+ await fs_1.promises.writeFile(outputPath, output, 'utf-8');
299
+ console.log(`Normalized configuration saved to: ${outputPath}`);
300
+ }
301
+ else {
302
+ console.log(output);
303
+ }
304
+ return;
305
+ }
306
+ // If no specific action, print default configuration
307
+ const defaultResult = TsRes.Config.getPredefinedDeclaration('default');
308
+ if (defaultResult.isFailure()) {
309
+ console.error(`Error: Default configuration not available`);
310
+ process.exit(1);
311
+ }
312
+ console.log(JSON.stringify(defaultResult.value, null, 2));
313
+ }
314
+ catch (error) {
315
+ console.error(`Error: ${error}`);
316
+ process.exit(1);
317
+ }
318
+ }
319
+ /**
320
+ * Parses and validates browse options
321
+ */
322
+ _parseBrowseOptions(options) {
323
+ try {
324
+ // Convert JSON context to contextFilter if provided
325
+ let contextFilter = options.contextFilter;
326
+ if (options.context && !contextFilter) {
327
+ try {
328
+ const contextObj = JSON.parse(options.context);
329
+ const tokens = [];
330
+ for (const [key, value] of Object.entries(contextObj)) {
331
+ tokens.push(`${key}=${value}`);
332
+ }
333
+ contextFilter = tokens.join('|');
334
+ }
335
+ catch (error) {
336
+ return (0, ts_utils_1.fail)(`Invalid context JSON: ${error}`);
337
+ }
338
+ }
339
+ const browseOptions = {
340
+ input: options.input,
341
+ config: options.config,
342
+ contextFilter,
343
+ qualifierDefaults: options.qualifierDefaults,
344
+ resourceTypes: options.resourceTypes,
345
+ maxDistance: options.maxDistance,
346
+ reduceQualifiers: options.reduceQualifiers || false,
347
+ verbose: options.verbose || false,
348
+ quiet: options.quiet || false,
349
+ port: options.port,
350
+ url: options.url,
351
+ open: options.open,
352
+ interactive: options.interactive || false,
353
+ dev: options.dev || false,
354
+ serve: options.serve || false
355
+ };
356
+ return (0, ts_utils_1.succeed)(browseOptions);
357
+ }
358
+ catch (error) {
359
+ return (0, ts_utils_1.fail)(`Failed to parse options: ${error}`);
360
+ }
361
+ }
362
+ }
363
+ exports.TsResBrowserCliApp = TsResBrowserCliApp;
364
+ //# sourceMappingURL=cli.js.map