@dx-pkg/mksymlink 1.0.7 → 1.0.9

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 (34) hide show
  1. package/examples/advanced-usage.d.ts +1 -0
  2. package/examples/advanced-usage.js +38 -0
  3. package/examples/basic-usage.d.ts +1 -0
  4. package/examples/basic-usage.js +14 -0
  5. package/examples/config-usage.d.ts +2 -0
  6. package/examples/config-usage.js +64 -0
  7. package/examples/cross-platform.d.ts +1 -0
  8. package/examples/cross-platform.js +40 -0
  9. package/examples/index.d.ts +1 -0
  10. package/examples/index.js +6 -0
  11. package/package.json +1 -1
  12. package/src/commands/config/config.command-factory.d.ts +4 -0
  13. package/src/commands/config/config.command-factory.js +17 -0
  14. package/src/commands/config/config.command.d.ts +22 -0
  15. package/src/commands/config/config.command.js +93 -0
  16. package/src/commands/config/config.option-validator.d.ts +8 -0
  17. package/src/commands/config/config.option-validator.js +40 -0
  18. package/src/commands/create/mksymlink.command-factory.d.ts +4 -0
  19. package/src/commands/create/mksymlink.option-validator.d.ts +8 -0
  20. package/src/commands/index.d.ts +2 -0
  21. package/src/commands/index.js +1 -1
  22. package/src/index.d.ts +11 -0
  23. package/src/services/config.service.d.ts +44 -0
  24. package/src/services/config.service.js +112 -0
  25. package/src/services/platform-detector.d.ts +10 -0
  26. package/src/services/platform-detector.js +35 -0
  27. package/src/services/symlink-error.d.ts +11 -0
  28. package/src/services/symlink-error.js +32 -0
  29. package/src/services/symlink-manager.d.ts +12 -0
  30. package/src/services/symlink-manager.js +114 -0
  31. package/src/services/symlink.service.d.ts +12 -0
  32. package/src/services/symlink.service.js +114 -0
  33. package/src/types/index.d.ts +39 -0
  34. package/src/types/index.js +2 -0
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const src_1 = require("../src");
4
+ const logger_1 = require("@dx-pkg/logger");
5
+ async function advancedExample() {
6
+ console.log('=== Advanced Symlink Manager Usage ===\n');
7
+ const logger = new logger_1.ConsoleLogger();
8
+ const osDetector = new src_1.PlatformDetector();
9
+ const symlinkManager = new src_1.SymlinkService(logger, osDetector);
10
+ console.log(`Detected OS: ${osDetector.getPlatform()}\n`);
11
+ const source = process.cwd();
12
+ const target = '/tmp/advanced-symlink';
13
+ console.log('Checking if symlink exists...');
14
+ const exists = await symlinkManager.symlinkExists(target);
15
+ console.log(`Symlink exists: ${exists}\n`);
16
+ if (exists) {
17
+ console.log('Removing existing symlink...');
18
+ await symlinkManager.removeSymlink(target);
19
+ console.log('Removed successfully\n');
20
+ }
21
+ console.log('Creating new symlink...');
22
+ const result = await symlinkManager.createSymlink({
23
+ source,
24
+ target,
25
+ type: osDetector.isWindows() ? 'junction' : 'dir',
26
+ force: false,
27
+ });
28
+ if (result.success) {
29
+ console.log('\nSymlink created successfully!');
30
+ console.log(` Source: ${result.source}`);
31
+ console.log(` Target: ${result.target}`);
32
+ }
33
+ else {
34
+ console.error('\nFailed to create symlink:');
35
+ console.error(` ${result.message || result.error?.message}`);
36
+ }
37
+ }
38
+ advancedExample().catch(console.error);
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const src_1 = require("../src");
4
+ const path_1 = require("path");
5
+ async function basicExample() {
6
+ console.log('=== Basic Symlink Creation ===\n');
7
+ const command = src_1.MkSymlinkCommandFactory.create({
8
+ source: process.cwd(),
9
+ target: (0, path_1.join)(process.cwd(), 'target-symlink'),
10
+ type: 'dir',
11
+ });
12
+ await command.execute();
13
+ }
14
+ basicExample().catch(console.error);
@@ -0,0 +1,2 @@
1
+ declare function configExamples(): Promise<void>;
2
+ export { configExamples };
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.configExamples = configExamples;
4
+ const config_command_factory_1 = require("../src/commands/config/config.command-factory");
5
+ const path_1 = require("path");
6
+ const os_1 = require("os");
7
+ async function configExamples() {
8
+ console.log('=== Config Command Examples ===\n');
9
+ try {
10
+ // Example 1: List all configuration
11
+ console.log('1. List all configuration:');
12
+ const listCommand = config_command_factory_1.ConfigCommandFactory.create({ action: 'list' });
13
+ await listCommand.execute();
14
+ console.log('');
15
+ // Example 2: Set default symlink directory
16
+ console.log('2. Set default symlink directory:');
17
+ const setCommand = config_command_factory_1.ConfigCommandFactory.create({
18
+ action: 'set',
19
+ key: 'symlink.defaultDir',
20
+ value: (0, path_1.join)((0, os_1.homedir)(), 'symlinks'),
21
+ });
22
+ await setCommand.execute();
23
+ console.log('');
24
+ // Example 3: Get a specific config value
25
+ console.log('3. Get default symlink directory:');
26
+ const getCommand = config_command_factory_1.ConfigCommandFactory.create({
27
+ action: 'get',
28
+ key: 'symlink.defaultDir',
29
+ });
30
+ await getCommand.execute();
31
+ console.log('');
32
+ // Example 4: Set a custom config key
33
+ console.log('4. Set a custom config key:');
34
+ const setCustomCommand = config_command_factory_1.ConfigCommandFactory.create({
35
+ action: 'set',
36
+ key: 'user.name',
37
+ value: 'John Doe',
38
+ });
39
+ await setCustomCommand.execute();
40
+ console.log('');
41
+ // Example 5: List all configuration again (to see changes)
42
+ console.log('5. List all configuration (after changes):');
43
+ const listCommand2 = config_command_factory_1.ConfigCommandFactory.create({ action: 'list' });
44
+ await listCommand2.execute();
45
+ console.log('');
46
+ // Example 6: Unset a config value
47
+ console.log('6. Unset user.name:');
48
+ const unsetCommand = config_command_factory_1.ConfigCommandFactory.create({
49
+ action: 'unset',
50
+ key: 'user.name',
51
+ });
52
+ await unsetCommand.execute();
53
+ console.log('');
54
+ // Example 7: List configuration one more time
55
+ console.log('7. List configuration (after unset):');
56
+ const listCommand3 = config_command_factory_1.ConfigCommandFactory.create({ action: 'list' });
57
+ await listCommand3.execute();
58
+ console.log('');
59
+ console.log('=== Config Examples Completed ===\n');
60
+ }
61
+ catch (error) {
62
+ console.error('Error running config examples:', error);
63
+ }
64
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const src_1 = require("../src");
4
+ async function crossPlatformExample() {
5
+ console.log('=== Cross-Platform Symlink Example ===\n');
6
+ const osDetector = new src_1.PlatformDetector();
7
+ const platform = osDetector.getPlatform();
8
+ console.log(`Current platform: ${platform}`);
9
+ console.log(`Is Windows: ${osDetector.isWindows()}`);
10
+ console.log(`Is macOS: ${osDetector.isMacOS()}`);
11
+ console.log(`Is Linux: ${osDetector.isLinux()}\n`);
12
+ const source = process.cwd();
13
+ let target;
14
+ let type;
15
+ if (osDetector.isWindows()) {
16
+ target = 'C:\\temp\\my-symlink';
17
+ type = 'junction';
18
+ console.log('Using Windows junction for better compatibility\n');
19
+ }
20
+ else {
21
+ target = '/tmp/my-symlink';
22
+ type = 'dir';
23
+ console.log('Using standard directory symlink\n');
24
+ }
25
+ const command = src_1.MkSymlinkCommandFactory.create({
26
+ source,
27
+ target,
28
+ type,
29
+ force: true,
30
+ });
31
+ try {
32
+ await command.execute();
33
+ console.log('\nCross-platform symlink created successfully!');
34
+ }
35
+ catch (error) {
36
+ console.error('\nFailed to create symlink:');
37
+ console.error(error);
38
+ }
39
+ }
40
+ crossPlatformExample().catch(console.error);
@@ -0,0 +1 @@
1
+ import './basic-usage';
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ require("./basic-usage");
4
+ // import { configExamples } from './config-usage';
5
+ // Uncomment to run config examples:
6
+ // configExamples().catch(console.error);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dx-pkg/mksymlink",
3
- "version": "1.0.7",
3
+ "version": "1.0.9",
4
4
  "description": "Create symbolic links across platforms (macOS, Windows)",
5
5
  "type": "commonjs",
6
6
  "main": "./src/index.js",
@@ -0,0 +1,4 @@
1
+ import { ConfigCommand, ConfigCommandOptions } from './config.command';
2
+ export declare class ConfigCommandFactory {
3
+ static create(options: ConfigCommandOptions): ConfigCommand;
4
+ }
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ConfigCommandFactory = void 0;
4
+ const logger_1 = require("@dx-pkg/logger");
5
+ const config_command_1 = require("./config.command");
6
+ const config_option_validator_1 = require("./config.option-validator");
7
+ const config_service_1 = require("../../services/config.service");
8
+ class ConfigCommandFactory {
9
+ static create(options) {
10
+ const validator = new config_option_validator_1.ConfigOptionValidator();
11
+ validator.validate(options);
12
+ const logger = new logger_1.ConsoleLogger();
13
+ const configService = new config_service_1.ConfigService();
14
+ return new config_command_1.ConfigCommand(logger, configService, options);
15
+ }
16
+ }
17
+ exports.ConfigCommandFactory = ConfigCommandFactory;
@@ -0,0 +1,22 @@
1
+ import { Logger } from '@dx-pkg/logger';
2
+ import { Command } from '../../types';
3
+ import { ConfigService } from '../../services/config.service';
4
+ export type ConfigAction = 'get' | 'set' | 'unset' | 'list' | 'info';
5
+ export interface ConfigCommandOptions {
6
+ action: ConfigAction;
7
+ key?: string;
8
+ value?: string;
9
+ [key: string]: unknown;
10
+ }
11
+ export declare class ConfigCommand implements Command<ConfigCommandOptions> {
12
+ private readonly logger;
13
+ private readonly configService;
14
+ private readonly options;
15
+ constructor(logger: Logger, configService: ConfigService, options: ConfigCommandOptions);
16
+ execute(): Promise<void>;
17
+ private handleGet;
18
+ private handleSet;
19
+ private handleUnset;
20
+ private handleList;
21
+ private handleInfo;
22
+ }
@@ -0,0 +1,93 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ConfigCommand = void 0;
4
+ class ConfigCommand {
5
+ logger;
6
+ configService;
7
+ options;
8
+ constructor(logger, configService, options) {
9
+ this.logger = logger;
10
+ this.configService = configService;
11
+ this.options = options;
12
+ }
13
+ async execute() {
14
+ try {
15
+ const { action, key, value } = this.options;
16
+ switch (action) {
17
+ case 'get':
18
+ await this.handleGet(key);
19
+ break;
20
+ case 'set':
21
+ await this.handleSet(key, value);
22
+ break;
23
+ case 'unset':
24
+ await this.handleUnset(key);
25
+ break;
26
+ case 'list':
27
+ await this.handleList();
28
+ break;
29
+ case 'info':
30
+ await this.handleInfo();
31
+ break;
32
+ default:
33
+ throw new Error(`Unknown config action: ${action}`);
34
+ }
35
+ }
36
+ catch (error) {
37
+ this.logger.error(`Config command failed: ${error instanceof Error ? error.message : String(error)}`);
38
+ throw error;
39
+ }
40
+ }
41
+ async handleGet(key) {
42
+ if (!key) {
43
+ throw new Error('Key is required for get action');
44
+ }
45
+ const value = this.configService.get(key);
46
+ if (value === undefined) {
47
+ this.logger.warning(`Config key '${key}' not found`);
48
+ return;
49
+ }
50
+ this.logger.info(value);
51
+ }
52
+ async handleSet(key, value) {
53
+ if (!key) {
54
+ throw new Error('Key is required for set action');
55
+ }
56
+ if (!value) {
57
+ throw new Error('Value is required for set action');
58
+ }
59
+ this.configService.set(key, value);
60
+ this.logger.success(`Config set: ${key} = ${value}`);
61
+ }
62
+ async handleUnset(key) {
63
+ if (!key) {
64
+ throw new Error('Key is required for unset action');
65
+ }
66
+ this.configService.unset(key);
67
+ this.logger.success(`Config unset: ${key}`);
68
+ }
69
+ async handleList() {
70
+ const config = this.configService.list();
71
+ const entries = Object.entries(config);
72
+ if (entries.length === 0) {
73
+ this.logger.info('No configuration found');
74
+ return;
75
+ }
76
+ this.logger.info('Current configuration:');
77
+ for (const [key, value] of entries) {
78
+ this.logger.info(` ${key} = ${value}`);
79
+ }
80
+ }
81
+ async handleInfo() {
82
+ const info = this.configService.getConfigInfo();
83
+ this.logger.info('Configuration file information:');
84
+ this.logger.info(` Path: ${info.path}`);
85
+ this.logger.info(` Exists: ${info.exists ? 'Yes' : 'No'}`);
86
+ if (info.exists) {
87
+ this.logger.info(` Size: ${info.size} bytes`);
88
+ this.logger.info(` Modified: ${info.modified?.toLocaleString()}`);
89
+ }
90
+ this.logger.info(` Entries: ${info.entries}`);
91
+ }
92
+ }
93
+ exports.ConfigCommand = ConfigCommand;
@@ -0,0 +1,8 @@
1
+ import { CommandOptionsValidator } from '../../types';
2
+ import { ConfigCommandOptions } from './config.command';
3
+ export declare class ConfigOptionValidator implements CommandOptionsValidator<ConfigCommandOptions> {
4
+ validate(options: ConfigCommandOptions): void;
5
+ private validateAction;
6
+ private validateKey;
7
+ private validateValue;
8
+ }
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ConfigOptionValidator = void 0;
4
+ const symlink_error_1 = require("../../services/symlink-error");
5
+ const VALID_ACTIONS = ['get', 'set', 'unset', 'list', 'info'];
6
+ class ConfigOptionValidator {
7
+ validate(options) {
8
+ this.validateAction(options.action);
9
+ this.validateKey(options.action, options.key);
10
+ this.validateValue(options.action, options.value);
11
+ }
12
+ validateAction(action) {
13
+ if (!VALID_ACTIONS.includes(action)) {
14
+ throw new symlink_error_1.SymlinkError(`Invalid config action: ${action}. Must be one of: ${VALID_ACTIONS.join(', ')}`, 'INVALID_CONFIG_ACTION');
15
+ }
16
+ }
17
+ validateKey(action, key) {
18
+ if (action === 'list' || action === 'info') {
19
+ return; // list and info don't require a key
20
+ }
21
+ if (!key) {
22
+ throw new symlink_error_1.SymlinkError(`Key is required for '${action}' action`, 'MISSING_CONFIG_KEY');
23
+ }
24
+ if (typeof key !== 'string' || key.trim().length === 0) {
25
+ throw new symlink_error_1.SymlinkError(`Invalid config key: ${key}. Key must be a non-empty string.`, 'INVALID_CONFIG_KEY');
26
+ }
27
+ }
28
+ validateValue(action, value) {
29
+ if (action !== 'set') {
30
+ return; // only 'set' requires a value
31
+ }
32
+ if (!value) {
33
+ throw new symlink_error_1.SymlinkError(`Value is required for 'set' action`, 'MISSING_CONFIG_VALUE');
34
+ }
35
+ if (typeof value !== 'string') {
36
+ throw new symlink_error_1.SymlinkError(`Invalid config value: ${value}. Value must be a string.`, 'INVALID_CONFIG_VALUE');
37
+ }
38
+ }
39
+ }
40
+ exports.ConfigOptionValidator = ConfigOptionValidator;
@@ -0,0 +1,4 @@
1
+ import { MkSymlinkCommand, MkSymlinkCommandOptions } from './mksymlink.command';
2
+ export declare class MkSymlinkCommandFactory {
3
+ static create(options: MkSymlinkCommandOptions): MkSymlinkCommand;
4
+ }
@@ -0,0 +1,8 @@
1
+ import { CommandOptionsValidator } from '../../types';
2
+ import { MkSymlinkCommandOptions } from './mksymlink.command';
3
+ export declare class MkSymlinkOptionValidator implements CommandOptionsValidator<MkSymlinkCommandOptions> {
4
+ validate(options: MkSymlinkCommandOptions): void;
5
+ private validateType;
6
+ private validateSource;
7
+ private validateForce;
8
+ }
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -15,7 +15,7 @@ program
15
15
  .description('Create a symbolic link')
16
16
  .option('-s, --source <path>', 'Source path (defaults to current directory)')
17
17
  .option('-t, --target <path>', 'Target symlink path (auto-generated if not provided)')
18
- .option('--type <type>', 'Symlink type for Windows: file, dir, junction (default: dir)', 'dir')
18
+ .option('--type <type>', 'Symlink type for Windows: file, dir, junction (default: junction)', 'junction')
19
19
  .option('-f, --force', 'Force overwrite existing symlink', false)
20
20
  .action(async (options) => {
21
21
  try {
package/src/index.d.ts ADDED
@@ -0,0 +1,11 @@
1
+ export * from './types';
2
+ export * from './services/platform-detector';
3
+ export * from './services/symlink.service';
4
+ export * from './services/symlink-error';
5
+ export * from './services/config.service';
6
+ export * from './commands/create/mksymlink.command';
7
+ export * from './commands/create/mksymlink.option-validator';
8
+ export * from './commands/create/mksymlink.command-factory';
9
+ export * from './commands/config/config.command';
10
+ export * from './commands/config/config.option-validator';
11
+ export * from './commands/config/config.command-factory';
@@ -0,0 +1,44 @@
1
+ export interface ConfigData {
2
+ [key: string]: string | undefined;
3
+ }
4
+ export declare class ConfigService {
5
+ private readonly configPath;
6
+ private config;
7
+ constructor(configPath?: string);
8
+ /**
9
+ * Get a configuration value by key
10
+ */
11
+ get(key: string): string | undefined;
12
+ /**
13
+ * Set a configuration value
14
+ */
15
+ set(key: string, value: string): void;
16
+ /**
17
+ * Unset (remove) a configuration value
18
+ */
19
+ unset(key: string): void;
20
+ /**
21
+ * List all configuration values
22
+ */
23
+ list(): ConfigData;
24
+ /**
25
+ * Get the default symlink directory from config
26
+ */
27
+ getDefaultSymlinkDir(): string | undefined;
28
+ /**
29
+ * Set the default symlink directory
30
+ */
31
+ setDefaultSymlinkDir(dir: string): void;
32
+ /**
33
+ * Get configuration file information
34
+ */
35
+ getConfigInfo(): {
36
+ path: string;
37
+ exists: boolean;
38
+ size?: number;
39
+ modified?: Date;
40
+ entries: number;
41
+ };
42
+ private loadConfig;
43
+ private saveConfig;
44
+ }
@@ -0,0 +1,112 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ConfigService = void 0;
4
+ const fs_1 = require("fs");
5
+ const path_1 = require("path");
6
+ const os_1 = require("os");
7
+ class ConfigService {
8
+ configPath;
9
+ config = {};
10
+ constructor(configPath) {
11
+ const homeDir = (0, os_1.homedir)();
12
+ this.configPath = configPath || (0, path_1.resolve)(homeDir, '.mksymlinkrc');
13
+ this.loadConfig();
14
+ }
15
+ /**
16
+ * Get a configuration value by key
17
+ */
18
+ get(key) {
19
+ return this.config[key];
20
+ }
21
+ /**
22
+ * Set a configuration value
23
+ */
24
+ set(key, value) {
25
+ this.config[key] = value;
26
+ this.saveConfig();
27
+ }
28
+ /**
29
+ * Unset (remove) a configuration value
30
+ */
31
+ unset(key) {
32
+ delete this.config[key];
33
+ this.saveConfig();
34
+ }
35
+ /**
36
+ * List all configuration values
37
+ */
38
+ list() {
39
+ return { ...this.config };
40
+ }
41
+ /**
42
+ * Get the default symlink directory from config
43
+ */
44
+ getDefaultSymlinkDir() {
45
+ return this.get('symlink.defaultDir');
46
+ }
47
+ /**
48
+ * Set the default symlink directory
49
+ */
50
+ setDefaultSymlinkDir(dir) {
51
+ this.set('symlink.defaultDir', dir);
52
+ }
53
+ /**
54
+ * Get configuration file information
55
+ */
56
+ getConfigInfo() {
57
+ const exists = (0, fs_1.existsSync)(this.configPath);
58
+ const entries = Object.keys(this.config).length;
59
+ if (!exists) {
60
+ return {
61
+ path: this.configPath,
62
+ exists: false,
63
+ entries,
64
+ };
65
+ }
66
+ const stats = (0, fs_1.statSync)(this.configPath);
67
+ return {
68
+ path: this.configPath,
69
+ exists: true,
70
+ size: stats.size,
71
+ modified: stats.mtime,
72
+ entries,
73
+ };
74
+ }
75
+ loadConfig() {
76
+ if (!(0, fs_1.existsSync)(this.configPath)) {
77
+ this.config = {};
78
+ return;
79
+ }
80
+ try {
81
+ const content = (0, fs_1.readFileSync)(this.configPath, 'utf-8');
82
+ const lines = content.split('\n').filter((line) => line.trim());
83
+ this.config = {};
84
+ for (const line of lines) {
85
+ const [key, ...valueParts] = line.split('=');
86
+ if (key && valueParts.length > 0) {
87
+ this.config[key.trim()] = valueParts.join('=').trim();
88
+ }
89
+ }
90
+ }
91
+ catch (error) {
92
+ throw new Error(`Failed to load config from ${this.configPath}: ${error instanceof Error ? error.message : String(error)}`);
93
+ }
94
+ }
95
+ saveConfig() {
96
+ try {
97
+ const configDir = (0, path_1.dirname)(this.configPath);
98
+ if (!(0, fs_1.existsSync)(configDir)) {
99
+ (0, fs_1.mkdirSync)(configDir, { recursive: true });
100
+ }
101
+ const content = Object.entries(this.config)
102
+ .filter(([, value]) => value !== undefined)
103
+ .map(([key, value]) => `${key}=${value}`)
104
+ .join('\n');
105
+ (0, fs_1.writeFileSync)(this.configPath, content + '\n', 'utf-8');
106
+ }
107
+ catch (error) {
108
+ throw new Error(`Failed to save config to ${this.configPath}: ${error instanceof Error ? error.message : String(error)}`);
109
+ }
110
+ }
111
+ }
112
+ exports.ConfigService = ConfigService;
@@ -0,0 +1,10 @@
1
+ import { PlatformInformation, OSPlatform } from '../types';
2
+ export declare class PlatformDetector implements PlatformInformation {
3
+ private readonly _platform;
4
+ constructor();
5
+ private normalizePlatform;
6
+ getPlatform(): OSPlatform;
7
+ isWindows(): boolean;
8
+ isMacOS(): boolean;
9
+ isLinux(): boolean;
10
+ }
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PlatformDetector = void 0;
4
+ const os_1 = require("os");
5
+ class PlatformDetector {
6
+ _platform;
7
+ constructor() {
8
+ this._platform = this.normalizePlatform((0, os_1.platform)());
9
+ }
10
+ normalizePlatform(osPlatform) {
11
+ switch (osPlatform) {
12
+ case 'darwin':
13
+ return 'darwin';
14
+ case 'win32':
15
+ return 'win32';
16
+ case 'linux':
17
+ return 'linux';
18
+ default:
19
+ return 'unknown';
20
+ }
21
+ }
22
+ getPlatform() {
23
+ return this._platform;
24
+ }
25
+ isWindows() {
26
+ return this._platform === 'win32';
27
+ }
28
+ isMacOS() {
29
+ return this._platform === 'darwin';
30
+ }
31
+ isLinux() {
32
+ return this._platform === 'linux';
33
+ }
34
+ }
35
+ exports.PlatformDetector = PlatformDetector;
@@ -0,0 +1,11 @@
1
+ import { Logger } from '@dx-pkg/logger';
2
+ import { CommandError } from '../types';
3
+ export declare class SymlinkError extends Error implements CommandError {
4
+ code?: string;
5
+ constructor(message: string, code?: string);
6
+ }
7
+ export declare class SymlinkErrorHandler {
8
+ private readonly logger;
9
+ constructor(logger: Logger);
10
+ handle(error: unknown): never;
11
+ }
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SymlinkErrorHandler = exports.SymlinkError = void 0;
4
+ class SymlinkError extends Error {
5
+ code;
6
+ constructor(message, code) {
7
+ super(message);
8
+ this.name = 'SymlinkError';
9
+ this.code = code;
10
+ Error.captureStackTrace(this, this.constructor);
11
+ }
12
+ }
13
+ exports.SymlinkError = SymlinkError;
14
+ class SymlinkErrorHandler {
15
+ logger;
16
+ constructor(logger) {
17
+ this.logger = logger;
18
+ }
19
+ handle(error) {
20
+ if (error instanceof SymlinkError) {
21
+ this.logger.error(`Symlink Error [${error.code || 'UNKNOWN'}]: ${error.message}`);
22
+ throw error;
23
+ }
24
+ if (error instanceof Error) {
25
+ this.logger.error(`Unexpected Error: ${error.message}`);
26
+ throw new SymlinkError(error.message, 'UNEXPECTED_ERROR');
27
+ }
28
+ this.logger.error(`Unknown Error: ${String(error)}`);
29
+ throw new SymlinkError(String(error), 'UNKNOWN_ERROR');
30
+ }
31
+ }
32
+ exports.SymlinkErrorHandler = SymlinkErrorHandler;
@@ -0,0 +1,12 @@
1
+ import { Logger } from '@dx-pkg/logger';
2
+ import { SymlinkOperations, SymlinkOptions, SymlinkResult, PlatformInformation } from '../types';
3
+ export declare class SymlinkService implements SymlinkOperations {
4
+ private readonly logger;
5
+ private readonly osDetector;
6
+ constructor(logger: Logger, osDetector: PlatformInformation);
7
+ createSymlink(options: SymlinkOptions): Promise<SymlinkResult>;
8
+ private createWindowsSymlink;
9
+ private createUnixSymlink;
10
+ symlinkExists(path: string): Promise<boolean>;
11
+ removeSymlink(path: string): Promise<void>;
12
+ }
@@ -0,0 +1,114 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SymlinkService = void 0;
4
+ const child_process_1 = require("child_process");
5
+ const util_1 = require("util");
6
+ const promises_1 = require("fs/promises");
7
+ const symlink_error_1 = require("./symlink-error");
8
+ const execAsync = (0, util_1.promisify)(child_process_1.exec);
9
+ class SymlinkService {
10
+ logger;
11
+ osDetector;
12
+ constructor(logger, osDetector) {
13
+ this.logger = logger;
14
+ this.osDetector = osDetector;
15
+ }
16
+ async createSymlink(options) {
17
+ const { source, target, type = 'dir', force = false } = options;
18
+ try {
19
+ // this.logger.info(`Creating symlink: ${target} -> ${source}`);
20
+ if (await this.symlinkExists(target)) {
21
+ if (!force) {
22
+ return {
23
+ success: false,
24
+ source,
25
+ target,
26
+ message: `Symlink already exists: ${target}`,
27
+ };
28
+ }
29
+ this.logger.info(`Force mode enabled. Removing existing symlink: ${target}`);
30
+ await this.removeSymlink(target);
31
+ }
32
+ if (this.osDetector.isWindows()) {
33
+ await this.createWindowsSymlink(source, target, type);
34
+ }
35
+ else {
36
+ await this.createUnixSymlink(source, target);
37
+ }
38
+ // this.logger.success(`Symlink created successfully: ${target} -> ${source}`);
39
+ return {
40
+ success: true,
41
+ source,
42
+ target,
43
+ message: 'Symlink created successfully',
44
+ };
45
+ }
46
+ catch (error) {
47
+ this.logger.error(`Failed to create symlink: ${error instanceof Error ? error.message : String(error)}`);
48
+ return {
49
+ success: false,
50
+ source,
51
+ target,
52
+ error: error instanceof Error ? error : new Error(String(error)),
53
+ };
54
+ }
55
+ }
56
+ async createWindowsSymlink(source, target, type) {
57
+ let flag;
58
+ switch (type) {
59
+ case 'junction':
60
+ flag = '/J';
61
+ break;
62
+ case 'file':
63
+ flag = '';
64
+ break;
65
+ case 'dir':
66
+ default:
67
+ flag = '/D';
68
+ break;
69
+ }
70
+ const command = flag
71
+ ? `mklink ${flag} "${target}" "${source}"`
72
+ : `mklink "${target}" "${source}"`;
73
+ this.logger.info(`Executing Windows command: ${command}`);
74
+ try {
75
+ const { stdout, stderr } = await execAsync(command, { shell: 'cmd.exe' });
76
+ if (stderr && !stderr.includes('Junction') && !stderr.includes('symbolic link')) {
77
+ throw new symlink_error_1.SymlinkError(stderr, 'WINDOWS_MKLINK_ERROR');
78
+ }
79
+ this.logger.info(`Windows mklink output: ${stdout}`);
80
+ }
81
+ catch (error) {
82
+ throw new symlink_error_1.SymlinkError(`Failed to create Windows symlink: ${error instanceof Error ? error.message : String(error)}`, 'WINDOWS_MKLINK_FAILED');
83
+ }
84
+ }
85
+ async createUnixSymlink(source, target) {
86
+ try {
87
+ await (0, promises_1.symlink)(source, target);
88
+ this.logger.info(`Unix symlink created: ln -s ${source} ${target}`);
89
+ }
90
+ catch (error) {
91
+ throw new symlink_error_1.SymlinkError(`Failed to create Unix symlink: ${error instanceof Error ? error.message : String(error)}`, 'UNIX_SYMLINK_FAILED');
92
+ }
93
+ }
94
+ async symlinkExists(path) {
95
+ try {
96
+ const stats = await (0, promises_1.lstat)(path);
97
+ return stats.isSymbolicLink();
98
+ }
99
+ catch {
100
+ return false;
101
+ }
102
+ }
103
+ async removeSymlink(path) {
104
+ try {
105
+ await (0, promises_1.access)(path);
106
+ await (0, promises_1.unlink)(path);
107
+ this.logger.info(`Symlink removed: ${path}`);
108
+ }
109
+ catch (error) {
110
+ throw new symlink_error_1.SymlinkError(`Failed to remove symlink: ${error instanceof Error ? error.message : String(error)}`, 'REMOVE_SYMLINK_FAILED');
111
+ }
112
+ }
113
+ }
114
+ exports.SymlinkService = SymlinkService;
@@ -0,0 +1,12 @@
1
+ import { Logger } from '@dx-pkg/logger';
2
+ import { SymlinkOperations, SymlinkOptions, SymlinkResult, PlatformInformation } from '../types';
3
+ export declare class SymlinkService implements SymlinkOperations {
4
+ private readonly logger;
5
+ private readonly osDetector;
6
+ constructor(logger: Logger, osDetector: PlatformInformation);
7
+ createSymlink(options: SymlinkOptions): Promise<SymlinkResult>;
8
+ private createWindowsSymlink;
9
+ private createUnixSymlink;
10
+ symlinkExists(path: string): Promise<boolean>;
11
+ removeSymlink(path: string): Promise<void>;
12
+ }
@@ -0,0 +1,114 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SymlinkService = void 0;
4
+ const child_process_1 = require("child_process");
5
+ const util_1 = require("util");
6
+ const promises_1 = require("fs/promises");
7
+ const symlink_error_1 = require("./symlink-error");
8
+ const execAsync = (0, util_1.promisify)(child_process_1.exec);
9
+ class SymlinkService {
10
+ logger;
11
+ osDetector;
12
+ constructor(logger, osDetector) {
13
+ this.logger = logger;
14
+ this.osDetector = osDetector;
15
+ }
16
+ async createSymlink(options) {
17
+ const { source, target, type = 'dir', force = false } = options;
18
+ try {
19
+ // this.logger.info(`Creating symlink: ${target} -> ${source}`);
20
+ if (await this.symlinkExists(target)) {
21
+ if (!force) {
22
+ return {
23
+ success: false,
24
+ source,
25
+ target,
26
+ message: `Symlink already exists: ${target}`,
27
+ };
28
+ }
29
+ this.logger.info(`Force mode enabled. Removing existing symlink: ${target}`);
30
+ await this.removeSymlink(target);
31
+ }
32
+ if (this.osDetector.isWindows()) {
33
+ await this.createWindowsSymlink(source, target, type);
34
+ }
35
+ else {
36
+ await this.createUnixSymlink(source, target);
37
+ }
38
+ // this.logger.success(`Symlink created successfully: ${target} -> ${source}`);
39
+ return {
40
+ success: true,
41
+ source,
42
+ target,
43
+ message: 'Symlink created successfully',
44
+ };
45
+ }
46
+ catch (error) {
47
+ this.logger.error(`Failed to create symlink: ${error instanceof Error ? error.message : String(error)}`);
48
+ return {
49
+ success: false,
50
+ source,
51
+ target,
52
+ error: error instanceof Error ? error : new Error(String(error)),
53
+ };
54
+ }
55
+ }
56
+ async createWindowsSymlink(source, target, type) {
57
+ let flag;
58
+ switch (type) {
59
+ case 'junction':
60
+ flag = '/J';
61
+ break;
62
+ case 'file':
63
+ flag = '';
64
+ break;
65
+ case 'dir':
66
+ default:
67
+ flag = '/D';
68
+ break;
69
+ }
70
+ const command = flag
71
+ ? `mklink ${flag} "${target}" "${source}"`
72
+ : `mklink "${target}" "${source}"`;
73
+ this.logger.info(`Executing Windows command: ${command}`);
74
+ try {
75
+ const { stdout, stderr } = await execAsync(command, { shell: 'cmd.exe' });
76
+ if (stderr && !stderr.includes('Junction') && !stderr.includes('symbolic link')) {
77
+ throw new symlink_error_1.SymlinkError(stderr, 'WINDOWS_MKLINK_ERROR');
78
+ }
79
+ this.logger.info(`Windows mklink output: ${stdout}`);
80
+ }
81
+ catch (error) {
82
+ throw new symlink_error_1.SymlinkError(`Failed to create Windows symlink: ${error instanceof Error ? error.message : String(error)}`, 'WINDOWS_MKLINK_FAILED');
83
+ }
84
+ }
85
+ async createUnixSymlink(source, target) {
86
+ try {
87
+ await (0, promises_1.symlink)(source, target);
88
+ this.logger.info(`Unix symlink created: ln -s ${source} ${target}`);
89
+ }
90
+ catch (error) {
91
+ throw new symlink_error_1.SymlinkError(`Failed to create Unix symlink: ${error instanceof Error ? error.message : String(error)}`, 'UNIX_SYMLINK_FAILED');
92
+ }
93
+ }
94
+ async symlinkExists(path) {
95
+ try {
96
+ const stats = await (0, promises_1.lstat)(path);
97
+ return stats.isSymbolicLink();
98
+ }
99
+ catch {
100
+ return false;
101
+ }
102
+ }
103
+ async removeSymlink(path) {
104
+ try {
105
+ await (0, promises_1.access)(path);
106
+ await (0, promises_1.unlink)(path);
107
+ this.logger.info(`Symlink removed: ${path}`);
108
+ }
109
+ catch (error) {
110
+ throw new symlink_error_1.SymlinkError(`Failed to remove symlink: ${error instanceof Error ? error.message : String(error)}`, 'REMOVE_SYMLINK_FAILED');
111
+ }
112
+ }
113
+ }
114
+ exports.SymlinkService = SymlinkService;
@@ -0,0 +1,39 @@
1
+ export type CommandName = string;
2
+ export interface CommandOptions {
3
+ [key: string]: unknown;
4
+ }
5
+ export interface Command<TOptions extends CommandOptions> {
6
+ execute(options: TOptions): Promise<void>;
7
+ }
8
+ export interface CommandError extends Error {
9
+ code?: string;
10
+ }
11
+ export interface CommandOptionsValidator<TOptions extends CommandOptions> {
12
+ validate(options: TOptions): void;
13
+ }
14
+ export type OSPlatform = 'darwin' | 'win32' | 'linux' | 'unknown';
15
+ export type WindowsSymlinkType = 'file' | 'dir' | 'junction';
16
+ export interface SymlinkOptions {
17
+ source: string;
18
+ target: string;
19
+ type?: WindowsSymlinkType;
20
+ force?: boolean;
21
+ }
22
+ export interface SymlinkResult {
23
+ success: boolean;
24
+ source: string;
25
+ target: string;
26
+ message?: string;
27
+ error?: Error;
28
+ }
29
+ export interface PlatformInformation {
30
+ getPlatform(): OSPlatform;
31
+ isWindows(): boolean;
32
+ isMacOS(): boolean;
33
+ isLinux(): boolean;
34
+ }
35
+ export interface SymlinkOperations {
36
+ createSymlink(options: SymlinkOptions): Promise<SymlinkResult>;
37
+ symlinkExists(path: string): Promise<boolean>;
38
+ removeSymlink(path: string): Promise<void>;
39
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });