@dx-pkg/mksymlink 1.0.17 → 1.0.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @dx-pkg/mksymlink
2
2
 
3
+ ## 1.0.18
4
+
5
+ ### Patch Changes
6
+
7
+ - 8598d03: simpler README.md
8
+
3
9
  ## 1.0.17
4
10
 
5
11
  ### Patch Changes
package/README.md CHANGED
@@ -81,26 +81,24 @@ mksymlink config <get|set|unset|list|info> [key] [value]
81
81
 
82
82
  ## Programmatic API
83
83
 
84
- ### Configuration API
84
+ ### Types
85
85
 
86
86
  ```typescript
87
- import { ConfigService, ConfigCommandFactory } from '@dx-pkg/mksymlink';
87
+ import type { SymlinkOptions, WindowsSymlinkType } from '@dx-pkg/mksymlink';
88
88
 
89
- // Using ConfigService directly
90
- const config = new ConfigService();
91
- config.set('symlink.defaultDir', '/path/to/symlinks');
92
- const dir = config.getDefaultSymlinkDir();
89
+ // WindowsSymlinkType
90
+ type WindowsSymlinkType = 'file' | 'dir' | 'junction';
93
91
 
94
- // Using ConfigCommandFactory
95
- const setCmd = ConfigCommandFactory.create({
96
- action: 'set',
97
- key: 'symlink.defaultDir',
98
- value: '/path/to/symlinks',
99
- });
100
- await setCmd.execute();
92
+ // SymlinkOptions
93
+ interface SymlinkOptions {
94
+ source: string;
95
+ target: string;
96
+ type?: WindowsSymlinkType;
97
+ force?: boolean;
98
+ }
101
99
  ```
102
100
 
103
- ### Symlink Creation API
101
+ ### Symlink Creation
104
102
 
105
103
  ```typescript
106
104
  import { MkSymlinkCommandFactory } from '@dx-pkg/mksymlink';
@@ -115,27 +113,40 @@ const command = MkSymlinkCommandFactory.create({
115
113
  await command.execute();
116
114
  ```
117
115
 
118
- ### Advanced Usage
116
+ ### Configuration
119
117
 
120
118
  ```typescript
121
- import { SymlinkService, PlatformDetector, ConsoleLogger } from '@dx-pkg/mksymlink';
119
+ import { ConfigCommandFactory } from '@dx-pkg/mksymlink';
122
120
 
123
- const logger = new ConsoleLogger();
124
- const osDetector = new PlatformDetector();
125
- const symlinkManager = new SymlinkService(logger, osDetector);
121
+ // Set a value
122
+ const setCmd = ConfigCommandFactory.create({
123
+ action: 'set',
124
+ key: 'symlink.defaultDir',
125
+ value: '/path/to/symlinks',
126
+ });
127
+ await setCmd.execute();
126
128
 
127
- const result = await symlinkManager.createSymlink({
128
- source: '/source/path',
129
- target: '/target/path',
130
- type: 'dir',
131
- force: true,
129
+ // Get a value
130
+ const getCmd = ConfigCommandFactory.create({
131
+ action: 'get',
132
+ key: 'symlink.defaultDir',
132
133
  });
134
+ await getCmd.execute();
133
135
 
134
- if (result.success) {
135
- console.log('Symlink created:', result.target);
136
- } else {
137
- console.error('Failed:', result.message);
138
- }
136
+ // List all values
137
+ const listCmd = ConfigCommandFactory.create({ action: 'list' });
138
+ await listCmd.execute();
139
+
140
+ // Unset a value
141
+ const unsetCmd = ConfigCommandFactory.create({
142
+ action: 'unset',
143
+ key: 'symlink.defaultDir',
144
+ });
145
+ await unsetCmd.execute();
146
+
147
+ // Show config file info
148
+ const infoCmd = ConfigCommandFactory.create({ action: 'info' });
149
+ await infoCmd.execute();
139
150
  ```
140
151
 
141
152
  ## Windows Symlink Types
@@ -191,4 +202,4 @@ npx mksymlink config info
191
202
  npx mksymlink create -s /path/to/source -t /path/to/target
192
203
  npx mksymlink create -f
193
204
  npx mksymlink create --type junction # Windows
194
- ```
205
+ ```
package/examples/index.js CHANGED
@@ -1,5 +1,2 @@
1
- var e=(e,t)=>()=>(e&&(t=e(e=0)),t),t=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports);let n=require(`os`),r=require(`child_process`),i=require(`util`),a=require(`fs/promises`),o=require(`fs`),s=require(`path`),c=require(`@dx-pkg/logger`);var l=e((()=>{})),u,d=e((()=>{u=class{_platform;constructor(){this._platform=this.normalizePlatform((0,n.platform)())}normalizePlatform(e){switch(e){case`darwin`:return`darwin`;case`win32`:return`win32`;case`linux`:return`linux`;default:return`unknown`}}getPlatform(){return this._platform}isWindows(){return this._platform===`win32`}isMacOS(){return this._platform===`darwin`}isLinux(){return this._platform===`linux`}}})),f,p=e((()=>{f=class extends Error{code;constructor(e,t){super(e),this.name=`SymlinkError`,this.code=t,Error.captureStackTrace(this,this.constructor)}}})),m,h,g=e((()=>{p(),m=(0,i.promisify)(r.exec),h=class{constructor(e,t){this.logger=e,this.osDetector=t}async createSymlink(e){let{source:t,target:n,type:r=`dir`,force:i=!1}=e;try{if(await this.symlinkExists(n)){if(!i)return{success:!1,source:t,target:n,message:`Symlink already exists: ${n}`};this.logger.info(`Force mode enabled. Removing existing symlink: ${n}`),await this.removeSymlink(n)}return this.osDetector.isWindows()?await this.createWindowsSymlink(t,n,r):await this.createUnixSymlink(t,n),{success:!0,source:t,target:n,message:`Symlink created successfully`}}catch(e){return this.logger.error(`Failed to create symlink: ${e instanceof Error?e.message:String(e)}`),{success:!1,source:t,target:n,error:e instanceof Error?e:Error(String(e))}}}async createWindowsSymlink(e,t,n){let r;switch(n){case`junction`:r=`/J`;break;case`file`:r=``;break;default:r=`/D`;break}let i=r?`mklink ${r} "${t}" "${e}"`:`mklink "${t}" "${e}"`;this.logger.info(`Executing Windows command: ${i}`);try{let{stdout:e,stderr:t}=await m(i,{shell:`cmd.exe`});if(t&&!t.includes(`Junction`)&&!t.includes(`symbolic link`))throw new f(t,`WINDOWS_MKLINK_ERROR`);this.logger.info(`Windows mklink output: ${e}`)}catch(e){throw new f(`Failed to create Windows symlink: ${e instanceof Error?e.message:String(e)}`,`WINDOWS_MKLINK_FAILED`)}}async createUnixSymlink(e,t){try{await(0,a.symlink)(e,t),this.logger.info(`Unix symlink created: ln -s ${e} ${t}`)}catch(e){throw new f(`Failed to create Unix symlink: ${e instanceof Error?e.message:String(e)}`,`UNIX_SYMLINK_FAILED`)}}async symlinkExists(e){try{return(await(0,a.lstat)(e)).isSymbolicLink()}catch{return!1}}async removeSymlink(e){try{await(0,a.access)(e),await(0,a.unlink)(e),this.logger.info(`Symlink removed: ${e}`)}catch(e){throw new f(`Failed to remove symlink: ${e instanceof Error?e.message:String(e)}`,`REMOVE_SYMLINK_FAILED`)}}}})),_,v=e((()=>{_=class{configPath;config={};constructor(e){let t=(0,n.homedir)();this.configPath=e||(0,s.resolve)(t,`.mksymlinkrc`),this.loadConfig()}get(e){return this.config[e]}set(e,t){this.config[e]=t,this.saveConfig()}unset(e){delete this.config[e],this.saveConfig()}list(){return{...this.config}}getDefaultSymlinkDir(){return this.get(`symlink.defaultDir`)}setDefaultSymlinkDir(e){this.set(`symlink.defaultDir`,e)}getConfigInfo(){let e=(0,o.existsSync)(this.configPath),t=Object.keys(this.config).length;if(!e)return{path:this.configPath,exists:!1,entries:t};let n=(0,o.statSync)(this.configPath);return{path:this.configPath,exists:!0,size:n.size,modified:n.mtime,entries:t}}loadConfig(){if(!(0,o.existsSync)(this.configPath)){this.config={};return}try{let e=(0,o.readFileSync)(this.configPath,`utf-8`).split(`
2
- `).filter(e=>e.trim());this.config={};for(let t of e){let[e,...n]=t.split(`=`);e&&n.length>0&&(this.config[e.trim()]=n.join(`=`).trim())}}catch(e){throw Error(`Failed to load config from ${this.configPath}: ${e instanceof Error?e.message:String(e)}`)}}saveConfig(){try{let e=(0,s.dirname)(this.configPath);(0,o.existsSync)(e)||(0,o.mkdirSync)(e,{recursive:!0});let t=Object.entries(this.config).filter(([,e])=>e!==void 0).map(([e,t])=>`${e}=${t}`).join(`
3
- `);(0,o.writeFileSync)(this.configPath,t+`
4
- `,`utf-8`)}catch(e){throw Error(`Failed to save config to ${this.configPath}: ${e instanceof Error?e.message:String(e)}`)}}}})),y,b,x=e((()=>{v(),y=[`file`,`dir`,`junction`],b=class{constructor(e,t,n,r,i=new _){this.optionValidator=e,this.logger=t,this.symlinkManager=n,this.options=r,this.configService=i}async execute(){this.optionValidator.validate(this.options);try{let{source:e,target:t,type:n,force:r}=this.resolveOptions();this.logger.info(`Creating symlink with options:`),this.logger.info(` Source: ${e}`),this.logger.info(` Target: ${t}`),this.logger.info(` Type: ${n}`),this.logger.info(` Force: ${r}`);let i=await this.symlinkManager.createSymlink({source:e,target:t,type:n,force:r});if(!i.success){if(i.error)throw i.error;this.logger.warning(i.message||`Symlink creation failed`);return}this.logger.success(`Symlink created successfully!`),this.logger.info(` Source: ${i.source}`),this.logger.info(` Target: ${i.target}`)}catch(e){throw this.logger.error(`Failed to create symlink: ${e instanceof Error?e.message:String(e)}`),e}}resolveOptions(){let e=process.cwd();return{source:this.options.source||e,target:this.resolveTarget(this.options)||this.generateDefaultTarget(e),type:this.options.type||`junction`,force:this.options.force||!1}}resolveTarget(e){if(e.target!==`.`&&e.target!==`./`)return e.target;let t=process.cwd();return(0,s.resolve)(t,(0,s.basename)(e.source||t))}generateDefaultTarget(e){let t=(0,s.basename)(e),n=`${(0,s.basename)((0,s.dirname)(e))}--${t}`;return(0,s.resolve)(this.configService.getDefaultSymlinkDir()||e,n)}}})),S,C=e((()=>{p(),x(),S=class{validate(e){this.validateType(e.type),this.validateSource(e.source),this.validateForce(e.force)}validateType(e){if(e&&!y.includes(e))throw new f(`Invalid symlink type: ${e}. Must be one of: ${y.join(`, `)}`,`INVALID_SYMLINK_TYPE`)}validateSource(e){if(!e)return;let t=(0,s.resolve)(e);if(!(0,o.existsSync)(t))throw new f(`Source path does not exist: ${t}`,`SOURCE_NOT_FOUND`)}validateForce(e){if(e!==void 0&&typeof e!=`boolean`)throw new f(`Invalid force option: ${e}. Must be a boolean.`,`INVALID_FORCE_OPTION`)}}})),w,T=e((()=>{x(),g(),d(),C(),w=class{static create(e){let t=new S,n=new c.ConsoleLogger;return new b(t,n,new h(n,new u),e)}}})),E=e((()=>{})),D=e((()=>{})),O=e((()=>{})),k=e((()=>{l(),d(),g(),p(),v(),x(),C(),T(),E(),D(),O()}));t((()=>{k();async function e(){console.log(`=== Basic Symlink Creation ===
5
- `),await w.create({source:process.cwd(),target:(0,s.join)(process.cwd(),`target-symlink`),type:`dir`}).execute()}e().catch(console.error)}))();
1
+ var e=(e,t)=>()=>(e&&(t=e(e=0)),t),t=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports);let n=require(`path`);var r=e((()=>{}));t((()=>{r();async function e(){console.log(`=== Basic Symlink Creation ===
2
+ `),await MkSymlinkCommandFactory.create({source:process.cwd(),target:(0,n.join)(process.cwd(),`target-symlink`),type:`dir`}).execute()}e().catch(console.error)}))();
@@ -1,5 +1,2 @@
1
- import{homedir as e,platform as t}from"os";import{exec as n}from"child_process";import{promisify as r}from"util";import{access as i,lstat as a,symlink as o,unlink as s}from"fs/promises";import{existsSync as c,mkdirSync as l,readFileSync as u,statSync as d,writeFileSync as f}from"fs";import{basename as p,dirname as m,join as h,resolve as g}from"path";import{ConsoleLogger as _}from"@dx-pkg/logger";var v=(e,t)=>()=>(e&&(t=e(e=0)),t),y=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports),b=v((()=>{})),x,S=v((()=>{x=class{_platform;constructor(){this._platform=this.normalizePlatform(t())}normalizePlatform(e){switch(e){case`darwin`:return`darwin`;case`win32`:return`win32`;case`linux`:return`linux`;default:return`unknown`}}getPlatform(){return this._platform}isWindows(){return this._platform===`win32`}isMacOS(){return this._platform===`darwin`}isLinux(){return this._platform===`linux`}}})),C,w=v((()=>{C=class extends Error{code;constructor(e,t){super(e),this.name=`SymlinkError`,this.code=t,Error.captureStackTrace(this,this.constructor)}}})),T,E,D=v((()=>{w(),T=r(n),E=class{constructor(e,t){this.logger=e,this.osDetector=t}async createSymlink(e){let{source:t,target:n,type:r=`dir`,force:i=!1}=e;try{if(await this.symlinkExists(n)){if(!i)return{success:!1,source:t,target:n,message:`Symlink already exists: ${n}`};this.logger.info(`Force mode enabled. Removing existing symlink: ${n}`),await this.removeSymlink(n)}return this.osDetector.isWindows()?await this.createWindowsSymlink(t,n,r):await this.createUnixSymlink(t,n),{success:!0,source:t,target:n,message:`Symlink created successfully`}}catch(e){return this.logger.error(`Failed to create symlink: ${e instanceof Error?e.message:String(e)}`),{success:!1,source:t,target:n,error:e instanceof Error?e:Error(String(e))}}}async createWindowsSymlink(e,t,n){let r;switch(n){case`junction`:r=`/J`;break;case`file`:r=``;break;default:r=`/D`;break}let i=r?`mklink ${r} "${t}" "${e}"`:`mklink "${t}" "${e}"`;this.logger.info(`Executing Windows command: ${i}`);try{let{stdout:e,stderr:t}=await T(i,{shell:`cmd.exe`});if(t&&!t.includes(`Junction`)&&!t.includes(`symbolic link`))throw new C(t,`WINDOWS_MKLINK_ERROR`);this.logger.info(`Windows mklink output: ${e}`)}catch(e){throw new C(`Failed to create Windows symlink: ${e instanceof Error?e.message:String(e)}`,`WINDOWS_MKLINK_FAILED`)}}async createUnixSymlink(e,t){try{await o(e,t),this.logger.info(`Unix symlink created: ln -s ${e} ${t}`)}catch(e){throw new C(`Failed to create Unix symlink: ${e instanceof Error?e.message:String(e)}`,`UNIX_SYMLINK_FAILED`)}}async symlinkExists(e){try{return(await a(e)).isSymbolicLink()}catch{return!1}}async removeSymlink(e){try{await i(e),await s(e),this.logger.info(`Symlink removed: ${e}`)}catch(e){throw new C(`Failed to remove symlink: ${e instanceof Error?e.message:String(e)}`,`REMOVE_SYMLINK_FAILED`)}}}})),O,k=v((()=>{O=class{configPath;config={};constructor(t){let n=e();this.configPath=t||g(n,`.mksymlinkrc`),this.loadConfig()}get(e){return this.config[e]}set(e,t){this.config[e]=t,this.saveConfig()}unset(e){delete this.config[e],this.saveConfig()}list(){return{...this.config}}getDefaultSymlinkDir(){return this.get(`symlink.defaultDir`)}setDefaultSymlinkDir(e){this.set(`symlink.defaultDir`,e)}getConfigInfo(){let e=c(this.configPath),t=Object.keys(this.config).length;if(!e)return{path:this.configPath,exists:!1,entries:t};let n=d(this.configPath);return{path:this.configPath,exists:!0,size:n.size,modified:n.mtime,entries:t}}loadConfig(){if(!c(this.configPath)){this.config={};return}try{let e=u(this.configPath,`utf-8`).split(`
2
- `).filter(e=>e.trim());this.config={};for(let t of e){let[e,...n]=t.split(`=`);e&&n.length>0&&(this.config[e.trim()]=n.join(`=`).trim())}}catch(e){throw Error(`Failed to load config from ${this.configPath}: ${e instanceof Error?e.message:String(e)}`)}}saveConfig(){try{let e=m(this.configPath);c(e)||l(e,{recursive:!0});let t=Object.entries(this.config).filter(([,e])=>e!==void 0).map(([e,t])=>`${e}=${t}`).join(`
3
- `);f(this.configPath,t+`
4
- `,`utf-8`)}catch(e){throw Error(`Failed to save config to ${this.configPath}: ${e instanceof Error?e.message:String(e)}`)}}}})),A,j,M=v((()=>{k(),A=[`file`,`dir`,`junction`],j=class{constructor(e,t,n,r,i=new O){this.optionValidator=e,this.logger=t,this.symlinkManager=n,this.options=r,this.configService=i}async execute(){this.optionValidator.validate(this.options);try{let{source:e,target:t,type:n,force:r}=this.resolveOptions();this.logger.info(`Creating symlink with options:`),this.logger.info(` Source: ${e}`),this.logger.info(` Target: ${t}`),this.logger.info(` Type: ${n}`),this.logger.info(` Force: ${r}`);let i=await this.symlinkManager.createSymlink({source:e,target:t,type:n,force:r});if(!i.success){if(i.error)throw i.error;this.logger.warning(i.message||`Symlink creation failed`);return}this.logger.success(`Symlink created successfully!`),this.logger.info(` Source: ${i.source}`),this.logger.info(` Target: ${i.target}`)}catch(e){throw this.logger.error(`Failed to create symlink: ${e instanceof Error?e.message:String(e)}`),e}}resolveOptions(){let e=process.cwd();return{source:this.options.source||e,target:this.resolveTarget(this.options)||this.generateDefaultTarget(e),type:this.options.type||`junction`,force:this.options.force||!1}}resolveTarget(e){if(e.target!==`.`&&e.target!==`./`)return e.target;let t=process.cwd();return g(t,p(e.source||t))}generateDefaultTarget(e){let t=p(e),n=`${p(m(e))}--${t}`;return g(this.configService.getDefaultSymlinkDir()||e,n)}}})),N,P=v((()=>{w(),M(),N=class{validate(e){this.validateType(e.type),this.validateSource(e.source),this.validateForce(e.force)}validateType(e){if(e&&!A.includes(e))throw new C(`Invalid symlink type: ${e}. Must be one of: ${A.join(`, `)}`,`INVALID_SYMLINK_TYPE`)}validateSource(e){if(!e)return;let t=g(e);if(!c(t))throw new C(`Source path does not exist: ${t}`,`SOURCE_NOT_FOUND`)}validateForce(e){if(e!==void 0&&typeof e!=`boolean`)throw new C(`Invalid force option: ${e}. Must be a boolean.`,`INVALID_FORCE_OPTION`)}}})),F,I=v((()=>{M(),D(),S(),P(),F=class{static create(e){let t=new N,n=new _;return new j(t,n,new E(n,new x),e)}}})),L=v((()=>{})),R=v((()=>{})),z=v((()=>{})),B=v((()=>{b(),S(),D(),w(),k(),M(),P(),I(),L(),R(),z()})),V=y((()=>{B();async function e(){console.log(`=== Basic Symlink Creation ===
5
- `),await F.create({source:process.cwd(),target:h(process.cwd(),`target-symlink`),type:`dir`}).execute()}e().catch(console.error)})),H=y((()=>{V()}));export default H();export{};
1
+ import{join as e}from"path";var t=(e,t)=>()=>(e&&(t=e(e=0)),t),n=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports),r=t((()=>{})),i=n((()=>{r();async function t(){console.log(`=== Basic Symlink Creation ===
2
+ `),await MkSymlinkCommandFactory.create({source:process.cwd(),target:e(process.cwd(),`target-symlink`),type:`dir`}).execute()}t().catch(console.error)})),a=n((()=>{i()}));export default a();export{};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dx-pkg/mksymlink",
3
- "version": "1.0.17",
3
+ "version": "1.0.18",
4
4
  "description": "Create symbolic links across platforms (macOS, Windows)",
5
5
  "private": false,
6
6
  "type": "commonjs",
@@ -35,6 +35,25 @@
35
35
  "bin": {
36
36
  "mksymlink": "bin/index.js"
37
37
  },
38
+ "scripts": {
39
+ "build": "npm run build:tsdown",
40
+ "dev": "npm run dev:tsdown",
41
+ "build:tsdown": "tsdown --config tsdown.config.build.ts --config-loader unrun && node scripts/clean-dts-comments.js",
42
+ "dev:tsdown": "tsdown --config tsdown.config.dev.ts --config-loader unrun && node scripts/clean-dts-comments.js",
43
+ "build:tsup": "tsup --config tsup.config.build.ts",
44
+ "dev:tsup": "tsup --config tsup.config.dev.ts",
45
+ "build:tsc": "tsc -p tsconfig.prod.json",
46
+ "dev:tsc": "tsc -p tsconfig.json --watch",
47
+ "example:build": "npm run build && npm run example",
48
+ "example:dev:tsc": "tsc-watch -b --onSuccess \"npm run example\"",
49
+ "example": "node ../../dist/packages/mksymlink/examples/index.js",
50
+ "test": "jest",
51
+ "test:watch": "jest --watch",
52
+ "test:coverage": "jest --coverage",
53
+ "lint": "eslint . --ext .ts",
54
+ "format": "prettier --write \"**/*.{ts,json,md}\"",
55
+ "bin": "node ../../dist/packages/mksymlink/bin/index.js"
56
+ },
38
57
  "keywords": [
39
58
  "symlink",
40
59
  "symbolic-link",
@@ -47,5 +66,13 @@
47
66
  "@dx-pkg/logger": "^1.0.9",
48
67
  "chalk": "^5.6.2",
49
68
  "commander": "^12.0.0"
69
+ },
70
+ "devDependencies": {
71
+ "@types/jest": "^30.0.0",
72
+ "@types/node": "^24.10.1",
73
+ "ts-jest": "^29.4.5",
74
+ "tsc-watch": "^7.2.0",
75
+ "tsdown": "^0.21.2",
76
+ "tsup": "^8.5.1"
50
77
  }
51
78
  }
package/src/index.d.mts CHANGED
@@ -1,25 +1,20 @@
1
1
  import { Logger } from "@dx-pkg/logger";
2
- type CommandName = string;
2
+ type WindowsSymlinkType = 'file' | 'dir' | 'junction';
3
+ interface SymlinkOptions {
4
+ source: string;
5
+ target: string;
6
+ type?: WindowsSymlinkType;
7
+ force?: boolean;
8
+ }
3
9
  interface CommandOptions {
4
10
  [key: string]: unknown;
5
11
  }
6
12
  interface Command<TOptions extends CommandOptions> {
7
13
  execute(options: TOptions): Promise<void>;
8
14
  }
9
- interface CommandError extends Error {
10
- code?: string;
11
- }
12
15
  interface CommandOptionsValidator<TOptions extends CommandOptions> {
13
16
  validate(options: TOptions): void;
14
17
  }
15
- type OSPlatform = 'darwin' | 'win32' | 'linux' | 'unknown';
16
- type WindowsSymlinkType = 'file' | 'dir' | 'junction';
17
- interface SymlinkOptions {
18
- source: string;
19
- target: string;
20
- type?: WindowsSymlinkType;
21
- force?: boolean;
22
- }
23
18
  interface SymlinkResult {
24
19
  success: boolean;
25
20
  source: string;
@@ -27,45 +22,11 @@ interface SymlinkResult {
27
22
  message?: string;
28
23
  error?: Error;
29
24
  }
30
- interface PlatformInformation {
31
- getPlatform(): OSPlatform;
32
- isWindows(): boolean;
33
- isMacOS(): boolean;
34
- isLinux(): boolean;
35
- }
36
25
  interface SymlinkOperations {
37
26
  createSymlink(options: SymlinkOptions): Promise<SymlinkResult>;
38
27
  symlinkExists(path: string): Promise<boolean>;
39
28
  removeSymlink(path: string): Promise<void>;
40
29
  }
41
- declare class PlatformDetector implements PlatformInformation {
42
- private readonly _platform;
43
- constructor();
44
- private normalizePlatform;
45
- getPlatform(): OSPlatform;
46
- isWindows(): boolean;
47
- isMacOS(): boolean;
48
- isLinux(): boolean;
49
- }
50
- declare class SymlinkService implements SymlinkOperations {
51
- private readonly logger;
52
- private readonly osDetector;
53
- constructor(logger: Logger, osDetector: PlatformInformation);
54
- createSymlink(options: SymlinkOptions): Promise<SymlinkResult>;
55
- private createWindowsSymlink;
56
- private createUnixSymlink;
57
- symlinkExists(path: string): Promise<boolean>;
58
- removeSymlink(path: string): Promise<void>;
59
- }
60
- declare class SymlinkError extends Error implements CommandError {
61
- code?: string;
62
- constructor(message: string, code?: string);
63
- }
64
- declare class SymlinkErrorHandler {
65
- private readonly logger;
66
- constructor(logger: Logger);
67
- handle(error: unknown): never;
68
- }
69
30
  interface ConfigData {
70
31
  [key: string]: string | undefined;
71
32
  }
@@ -102,7 +63,6 @@ interface MkSymlinkCommandOptions {
102
63
  force?: boolean;
103
64
  [key: string]: unknown;
104
65
  }
105
- declare const SYMLINK_TYPES: readonly WindowsSymlinkType[];
106
66
  declare class MkSymlinkCommand implements Command<MkSymlinkCommandOptions> {
107
67
  private readonly optionValidator;
108
68
  private readonly logger;
@@ -137,13 +97,7 @@ declare class ConfigCommand implements Command<ConfigCommandOptions> {
137
97
  private handleList;
138
98
  private handleInfo;
139
99
  }
140
- declare class ConfigOptionValidator implements CommandOptionsValidator<ConfigCommandOptions> {
141
- validate(options: ConfigCommandOptions): void;
142
- private validateAction;
143
- private validateKey;
144
- private validateValue;
145
- }
146
100
  declare class ConfigCommandFactory {
147
101
  static create(options: ConfigCommandOptions): ConfigCommand;
148
102
  }
149
- export { Command, CommandError, CommandName, CommandOptions, CommandOptionsValidator, ConfigAction, ConfigCommand, ConfigCommandFactory, ConfigCommandOptions, ConfigData, ConfigOptionValidator, ConfigService, MkSymlinkCommand, MkSymlinkCommandFactory, MkSymlinkCommandOptions, MkSymlinkOptionValidator, OSPlatform, PlatformDetector, PlatformInformation, SYMLINK_TYPES, SymlinkError, SymlinkErrorHandler, SymlinkOperations, SymlinkOptions, SymlinkResult, SymlinkService, WindowsSymlinkType };
103
+ export { ConfigCommandFactory, MkSymlinkCommandFactory, SymlinkOptions, WindowsSymlinkType };
package/src/index.d.ts CHANGED
@@ -1,25 +1,20 @@
1
1
  import { Logger } from "@dx-pkg/logger";
2
- type CommandName = string;
2
+ type WindowsSymlinkType = 'file' | 'dir' | 'junction';
3
+ interface SymlinkOptions {
4
+ source: string;
5
+ target: string;
6
+ type?: WindowsSymlinkType;
7
+ force?: boolean;
8
+ }
3
9
  interface CommandOptions {
4
10
  [key: string]: unknown;
5
11
  }
6
12
  interface Command<TOptions extends CommandOptions> {
7
13
  execute(options: TOptions): Promise<void>;
8
14
  }
9
- interface CommandError extends Error {
10
- code?: string;
11
- }
12
15
  interface CommandOptionsValidator<TOptions extends CommandOptions> {
13
16
  validate(options: TOptions): void;
14
17
  }
15
- type OSPlatform = 'darwin' | 'win32' | 'linux' | 'unknown';
16
- type WindowsSymlinkType = 'file' | 'dir' | 'junction';
17
- interface SymlinkOptions {
18
- source: string;
19
- target: string;
20
- type?: WindowsSymlinkType;
21
- force?: boolean;
22
- }
23
18
  interface SymlinkResult {
24
19
  success: boolean;
25
20
  source: string;
@@ -27,45 +22,11 @@ interface SymlinkResult {
27
22
  message?: string;
28
23
  error?: Error;
29
24
  }
30
- interface PlatformInformation {
31
- getPlatform(): OSPlatform;
32
- isWindows(): boolean;
33
- isMacOS(): boolean;
34
- isLinux(): boolean;
35
- }
36
25
  interface SymlinkOperations {
37
26
  createSymlink(options: SymlinkOptions): Promise<SymlinkResult>;
38
27
  symlinkExists(path: string): Promise<boolean>;
39
28
  removeSymlink(path: string): Promise<void>;
40
29
  }
41
- declare class PlatformDetector implements PlatformInformation {
42
- private readonly _platform;
43
- constructor();
44
- private normalizePlatform;
45
- getPlatform(): OSPlatform;
46
- isWindows(): boolean;
47
- isMacOS(): boolean;
48
- isLinux(): boolean;
49
- }
50
- declare class SymlinkService implements SymlinkOperations {
51
- private readonly logger;
52
- private readonly osDetector;
53
- constructor(logger: Logger, osDetector: PlatformInformation);
54
- createSymlink(options: SymlinkOptions): Promise<SymlinkResult>;
55
- private createWindowsSymlink;
56
- private createUnixSymlink;
57
- symlinkExists(path: string): Promise<boolean>;
58
- removeSymlink(path: string): Promise<void>;
59
- }
60
- declare class SymlinkError extends Error implements CommandError {
61
- code?: string;
62
- constructor(message: string, code?: string);
63
- }
64
- declare class SymlinkErrorHandler {
65
- private readonly logger;
66
- constructor(logger: Logger);
67
- handle(error: unknown): never;
68
- }
69
30
  interface ConfigData {
70
31
  [key: string]: string | undefined;
71
32
  }
@@ -102,7 +63,6 @@ interface MkSymlinkCommandOptions {
102
63
  force?: boolean;
103
64
  [key: string]: unknown;
104
65
  }
105
- declare const SYMLINK_TYPES: readonly WindowsSymlinkType[];
106
66
  declare class MkSymlinkCommand implements Command<MkSymlinkCommandOptions> {
107
67
  private readonly optionValidator;
108
68
  private readonly logger;
@@ -137,13 +97,7 @@ declare class ConfigCommand implements Command<ConfigCommandOptions> {
137
97
  private handleList;
138
98
  private handleInfo;
139
99
  }
140
- declare class ConfigOptionValidator implements CommandOptionsValidator<ConfigCommandOptions> {
141
- validate(options: ConfigCommandOptions): void;
142
- private validateAction;
143
- private validateKey;
144
- private validateValue;
145
- }
146
100
  declare class ConfigCommandFactory {
147
101
  static create(options: ConfigCommandOptions): ConfigCommand;
148
102
  }
149
- export { Command, CommandError, CommandName, CommandOptions, CommandOptionsValidator, ConfigAction, ConfigCommand, ConfigCommandFactory, ConfigCommandOptions, ConfigData, ConfigOptionValidator, ConfigService, MkSymlinkCommand, MkSymlinkCommandFactory, MkSymlinkCommandOptions, MkSymlinkOptionValidator, OSPlatform, PlatformDetector, PlatformInformation, SYMLINK_TYPES, SymlinkError, SymlinkErrorHandler, SymlinkOperations, SymlinkOptions, SymlinkResult, SymlinkService, WindowsSymlinkType };
103
+ export { ConfigCommandFactory, MkSymlinkCommandFactory, SymlinkOptions, WindowsSymlinkType };
package/src/index.js CHANGED
@@ -1,4 +1,4 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require(`os`),t=require(`child_process`),n=require(`util`),r=require(`fs/promises`),i=require(`fs`),a=require(`path`),o=require(`@dx-pkg/logger`);var s=class{_platform;constructor(){this._platform=this.normalizePlatform((0,e.platform)())}normalizePlatform(e){switch(e){case`darwin`:return`darwin`;case`win32`:return`win32`;case`linux`:return`linux`;default:return`unknown`}}getPlatform(){return this._platform}isWindows(){return this._platform===`win32`}isMacOS(){return this._platform===`darwin`}isLinux(){return this._platform===`linux`}},c=class extends Error{code;constructor(e,t){super(e),this.name=`SymlinkError`,this.code=t,Error.captureStackTrace(this,this.constructor)}},l=class{constructor(e){this.logger=e}handle(e){throw e instanceof c?(this.logger.error(`Symlink Error [${e.code||`UNKNOWN`}]: ${e.message}`),e):e instanceof Error?(this.logger.error(`Unexpected Error: ${e.message}`),new c(e.message,`UNEXPECTED_ERROR`)):(this.logger.error(`Unknown Error: ${String(e)}`),new c(String(e),`UNKNOWN_ERROR`))}};const u=(0,n.promisify)(t.exec);var d=class{constructor(e,t){this.logger=e,this.osDetector=t}async createSymlink(e){let{source:t,target:n,type:r=`dir`,force:i=!1}=e;try{if(await this.symlinkExists(n)){if(!i)return{success:!1,source:t,target:n,message:`Symlink already exists: ${n}`};this.logger.info(`Force mode enabled. Removing existing symlink: ${n}`),await this.removeSymlink(n)}return this.osDetector.isWindows()?await this.createWindowsSymlink(t,n,r):await this.createUnixSymlink(t,n),{success:!0,source:t,target:n,message:`Symlink created successfully`}}catch(e){return this.logger.error(`Failed to create symlink: ${e instanceof Error?e.message:String(e)}`),{success:!1,source:t,target:n,error:e instanceof Error?e:Error(String(e))}}}async createWindowsSymlink(e,t,n){let r;switch(n){case`junction`:r=`/J`;break;case`file`:r=``;break;default:r=`/D`;break}let i=r?`mklink ${r} "${t}" "${e}"`:`mklink "${t}" "${e}"`;this.logger.info(`Executing Windows command: ${i}`);try{let{stdout:e,stderr:t}=await u(i,{shell:`cmd.exe`});if(t&&!t.includes(`Junction`)&&!t.includes(`symbolic link`))throw new c(t,`WINDOWS_MKLINK_ERROR`);this.logger.info(`Windows mklink output: ${e}`)}catch(e){throw new c(`Failed to create Windows symlink: ${e instanceof Error?e.message:String(e)}`,`WINDOWS_MKLINK_FAILED`)}}async createUnixSymlink(e,t){try{await(0,r.symlink)(e,t),this.logger.info(`Unix symlink created: ln -s ${e} ${t}`)}catch(e){throw new c(`Failed to create Unix symlink: ${e instanceof Error?e.message:String(e)}`,`UNIX_SYMLINK_FAILED`)}}async symlinkExists(e){try{return(await(0,r.lstat)(e)).isSymbolicLink()}catch{return!1}}async removeSymlink(e){try{await(0,r.access)(e),await(0,r.unlink)(e),this.logger.info(`Symlink removed: ${e}`)}catch(e){throw new c(`Failed to remove symlink: ${e instanceof Error?e.message:String(e)}`,`REMOVE_SYMLINK_FAILED`)}}},f=class{configPath;config={};constructor(t){let n=(0,e.homedir)();this.configPath=t||(0,a.resolve)(n,`.mksymlinkrc`),this.loadConfig()}get(e){return this.config[e]}set(e,t){this.config[e]=t,this.saveConfig()}unset(e){delete this.config[e],this.saveConfig()}list(){return{...this.config}}getDefaultSymlinkDir(){return this.get(`symlink.defaultDir`)}setDefaultSymlinkDir(e){this.set(`symlink.defaultDir`,e)}getConfigInfo(){let e=(0,i.existsSync)(this.configPath),t=Object.keys(this.config).length;if(!e)return{path:this.configPath,exists:!1,entries:t};let n=(0,i.statSync)(this.configPath);return{path:this.configPath,exists:!0,size:n.size,modified:n.mtime,entries:t}}loadConfig(){if(!(0,i.existsSync)(this.configPath)){this.config={};return}try{let e=(0,i.readFileSync)(this.configPath,`utf-8`).split(`
2
- `).filter(e=>e.trim());this.config={};for(let t of e){let[e,...n]=t.split(`=`);e&&n.length>0&&(this.config[e.trim()]=n.join(`=`).trim())}}catch(e){throw Error(`Failed to load config from ${this.configPath}: ${e instanceof Error?e.message:String(e)}`)}}saveConfig(){try{let e=(0,a.dirname)(this.configPath);(0,i.existsSync)(e)||(0,i.mkdirSync)(e,{recursive:!0});let t=Object.entries(this.config).filter(([,e])=>e!==void 0).map(([e,t])=>`${e}=${t}`).join(`
3
- `);(0,i.writeFileSync)(this.configPath,t+`
4
- `,`utf-8`)}catch(e){throw Error(`Failed to save config to ${this.configPath}: ${e instanceof Error?e.message:String(e)}`)}}};const p=[`file`,`dir`,`junction`];var m=class{constructor(e,t,n,r,i=new f){this.optionValidator=e,this.logger=t,this.symlinkManager=n,this.options=r,this.configService=i}async execute(){this.optionValidator.validate(this.options);try{let{source:e,target:t,type:n,force:r}=this.resolveOptions();this.logger.info(`Creating symlink with options:`),this.logger.info(` Source: ${e}`),this.logger.info(` Target: ${t}`),this.logger.info(` Type: ${n}`),this.logger.info(` Force: ${r}`);let i=await this.symlinkManager.createSymlink({source:e,target:t,type:n,force:r});if(!i.success){if(i.error)throw i.error;this.logger.warning(i.message||`Symlink creation failed`);return}this.logger.success(`Symlink created successfully!`),this.logger.info(` Source: ${i.source}`),this.logger.info(` Target: ${i.target}`)}catch(e){throw this.logger.error(`Failed to create symlink: ${e instanceof Error?e.message:String(e)}`),e}}resolveOptions(){let e=process.cwd();return{source:this.options.source||e,target:this.resolveTarget(this.options)||this.generateDefaultTarget(e),type:this.options.type||`junction`,force:this.options.force||!1}}resolveTarget(e){if(e.target!==`.`&&e.target!==`./`)return e.target;let t=process.cwd();return(0,a.resolve)(t,(0,a.basename)(e.source||t))}generateDefaultTarget(e){let t=(0,a.basename)(e),n=`${(0,a.basename)((0,a.dirname)(e))}--${t}`;return(0,a.resolve)(this.configService.getDefaultSymlinkDir()||e,n)}},h=class{validate(e){this.validateType(e.type),this.validateSource(e.source),this.validateForce(e.force)}validateType(e){if(e&&!p.includes(e))throw new c(`Invalid symlink type: ${e}. Must be one of: ${p.join(`, `)}`,`INVALID_SYMLINK_TYPE`)}validateSource(e){if(!e)return;let t=(0,a.resolve)(e);if(!(0,i.existsSync)(t))throw new c(`Source path does not exist: ${t}`,`SOURCE_NOT_FOUND`)}validateForce(e){if(e!==void 0&&typeof e!=`boolean`)throw new c(`Invalid force option: ${e}. Must be a boolean.`,`INVALID_FORCE_OPTION`)}},g=class{static create(e){let t=new h,n=new o.ConsoleLogger;return new m(t,n,new d(n,new s),e)}},_=class{constructor(e,t,n){this.logger=e,this.configService=t,this.options=n}async execute(){try{let{action:e,key:t,value:n}=this.options;switch(e){case`get`:await this.handleGet(t);break;case`set`:await this.handleSet(t,n);break;case`unset`:await this.handleUnset(t);break;case`list`:await this.handleList();break;case`info`:await this.handleInfo();break;default:throw Error(`Unknown config action: ${e}`)}}catch(e){throw this.logger.error(`Config command failed: ${e instanceof Error?e.message:String(e)}`),e}}async handleGet(e){if(!e)throw Error(`Key is required for get action`);let t=this.configService.get(e);if(t===void 0){this.logger.warning(`Config key '${e}' not found`);return}this.logger.info(t)}async handleSet(e,t){if(!e)throw Error(`Key is required for set action`);if(!t)throw Error(`Value is required for set action`);this.configService.set(e,t),this.logger.success(`Config set: ${e} = ${t}`)}async handleUnset(e){if(!e)throw Error(`Key is required for unset action`);this.configService.unset(e),this.logger.success(`Config unset: ${e}`)}async handleList(){let e=this.configService.list(),t=Object.entries(e);if(t.length===0){this.logger.info(`No configuration found`);return}this.logger.info(`Current configuration:`);for(let[e,n]of t)this.logger.info(` ${e} = ${n}`)}async handleInfo(){let e=this.configService.getConfigInfo();this.logger.info(`Configuration file information:`),this.logger.info(` Path: ${e.path}`),this.logger.info(` Exists: ${e.exists?`Yes`:`No`}`),e.exists&&(this.logger.info(` Size: ${e.size} bytes`),this.logger.info(` Modified: ${e.modified?.toLocaleString()}`)),this.logger.info(` Entries: ${e.entries}`)}};const v=[`get`,`set`,`unset`,`list`,`info`];var y=class{validate(e){this.validateAction(e.action),this.validateKey(e.action,e.key),this.validateValue(e.action,e.value)}validateAction(e){if(!v.includes(e))throw new c(`Invalid config action: ${e}. Must be one of: ${v.join(`, `)}`,`INVALID_CONFIG_ACTION`)}validateKey(e,t){if(!(e===`list`||e===`info`)){if(!t)throw new c(`Key is required for '${e}' action`,`MISSING_CONFIG_KEY`);if(typeof t!=`string`||t.trim().length===0)throw new c(`Invalid config key: ${t}. Key must be a non-empty string.`,`INVALID_CONFIG_KEY`)}}validateValue(e,t){if(e===`set`){if(!t)throw new c(`Value is required for 'set' action`,`MISSING_CONFIG_VALUE`);if(typeof t!=`string`)throw new c(`Invalid config value: ${t}. Value must be a string.`,`INVALID_CONFIG_VALUE`)}}},b=class{static create(e){return new y().validate(e),new _(new o.ConsoleLogger,new f,e)}};exports.ConfigCommand=_,exports.ConfigCommandFactory=b,exports.ConfigOptionValidator=y,exports.ConfigService=f,exports.MkSymlinkCommand=m,exports.MkSymlinkCommandFactory=g,exports.MkSymlinkOptionValidator=h,exports.PlatformDetector=s,exports.SYMLINK_TYPES=p,exports.SymlinkError=c,exports.SymlinkErrorHandler=l,exports.SymlinkService=d;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require(`@dx-pkg/logger`),t=require(`path`),n=require(`fs`),r=require(`os`),i=require(`child_process`),a=require(`util`),o=require(`fs/promises`);var s=class{configPath;config={};constructor(e){let n=(0,r.homedir)();this.configPath=e||(0,t.resolve)(n,`.mksymlinkrc`),this.loadConfig()}get(e){return this.config[e]}set(e,t){this.config[e]=t,this.saveConfig()}unset(e){delete this.config[e],this.saveConfig()}list(){return{...this.config}}getDefaultSymlinkDir(){return this.get(`symlink.defaultDir`)}setDefaultSymlinkDir(e){this.set(`symlink.defaultDir`,e)}getConfigInfo(){let e=(0,n.existsSync)(this.configPath),t=Object.keys(this.config).length;if(!e)return{path:this.configPath,exists:!1,entries:t};let r=(0,n.statSync)(this.configPath);return{path:this.configPath,exists:!0,size:r.size,modified:r.mtime,entries:t}}loadConfig(){if(!(0,n.existsSync)(this.configPath)){this.config={};return}try{let e=(0,n.readFileSync)(this.configPath,`utf-8`).split(`
2
+ `).filter(e=>e.trim());this.config={};for(let t of e){let[e,...n]=t.split(`=`);e&&n.length>0&&(this.config[e.trim()]=n.join(`=`).trim())}}catch(e){throw Error(`Failed to load config from ${this.configPath}: ${e instanceof Error?e.message:String(e)}`)}}saveConfig(){try{let e=(0,t.dirname)(this.configPath);(0,n.existsSync)(e)||(0,n.mkdirSync)(e,{recursive:!0});let r=Object.entries(this.config).filter(([,e])=>e!==void 0).map(([e,t])=>`${e}=${t}`).join(`
3
+ `);(0,n.writeFileSync)(this.configPath,r+`
4
+ `,`utf-8`)}catch(e){throw Error(`Failed to save config to ${this.configPath}: ${e instanceof Error?e.message:String(e)}`)}}};const c=[`file`,`dir`,`junction`];var l=class{constructor(e,t,n,r,i=new s){this.optionValidator=e,this.logger=t,this.symlinkManager=n,this.options=r,this.configService=i}async execute(){this.optionValidator.validate(this.options);try{let{source:e,target:t,type:n,force:r}=this.resolveOptions();this.logger.info(`Creating symlink with options:`),this.logger.info(` Source: ${e}`),this.logger.info(` Target: ${t}`),this.logger.info(` Type: ${n}`),this.logger.info(` Force: ${r}`);let i=await this.symlinkManager.createSymlink({source:e,target:t,type:n,force:r});if(!i.success){if(i.error)throw i.error;this.logger.warning(i.message||`Symlink creation failed`);return}this.logger.success(`Symlink created successfully!`),this.logger.info(` Source: ${i.source}`),this.logger.info(` Target: ${i.target}`)}catch(e){throw this.logger.error(`Failed to create symlink: ${e instanceof Error?e.message:String(e)}`),e}}resolveOptions(){let e=process.cwd();return{source:this.options.source||e,target:this.resolveTarget(this.options)||this.generateDefaultTarget(e),type:this.options.type||`junction`,force:this.options.force||!1}}resolveTarget(e){if(e.target!==`.`&&e.target!==`./`)return e.target;let n=process.cwd();return(0,t.resolve)(n,(0,t.basename)(e.source||n))}generateDefaultTarget(e){let n=(0,t.basename)(e),r=`${(0,t.basename)((0,t.dirname)(e))}--${n}`;return(0,t.resolve)(this.configService.getDefaultSymlinkDir()||e,r)}},u=class extends Error{code;constructor(e,t){super(e),this.name=`SymlinkError`,this.code=t,Error.captureStackTrace(this,this.constructor)}};const d=(0,a.promisify)(i.exec);var f=class{constructor(e,t){this.logger=e,this.osDetector=t}async createSymlink(e){let{source:t,target:n,type:r=`dir`,force:i=!1}=e;try{if(await this.symlinkExists(n)){if(!i)return{success:!1,source:t,target:n,message:`Symlink already exists: ${n}`};this.logger.info(`Force mode enabled. Removing existing symlink: ${n}`),await this.removeSymlink(n)}return this.osDetector.isWindows()?await this.createWindowsSymlink(t,n,r):await this.createUnixSymlink(t,n),{success:!0,source:t,target:n,message:`Symlink created successfully`}}catch(e){return this.logger.error(`Failed to create symlink: ${e instanceof Error?e.message:String(e)}`),{success:!1,source:t,target:n,error:e instanceof Error?e:Error(String(e))}}}async createWindowsSymlink(e,t,n){let r;switch(n){case`junction`:r=`/J`;break;case`file`:r=``;break;default:r=`/D`;break}let i=r?`mklink ${r} "${t}" "${e}"`:`mklink "${t}" "${e}"`;this.logger.info(`Executing Windows command: ${i}`);try{let{stdout:e,stderr:t}=await d(i,{shell:`cmd.exe`});if(t&&!t.includes(`Junction`)&&!t.includes(`symbolic link`))throw new u(t,`WINDOWS_MKLINK_ERROR`);this.logger.info(`Windows mklink output: ${e}`)}catch(e){throw new u(`Failed to create Windows symlink: ${e instanceof Error?e.message:String(e)}`,`WINDOWS_MKLINK_FAILED`)}}async createUnixSymlink(e,t){try{await(0,o.symlink)(e,t),this.logger.info(`Unix symlink created: ln -s ${e} ${t}`)}catch(e){throw new u(`Failed to create Unix symlink: ${e instanceof Error?e.message:String(e)}`,`UNIX_SYMLINK_FAILED`)}}async symlinkExists(e){try{return(await(0,o.lstat)(e)).isSymbolicLink()}catch{return!1}}async removeSymlink(e){try{await(0,o.access)(e),await(0,o.unlink)(e),this.logger.info(`Symlink removed: ${e}`)}catch(e){throw new u(`Failed to remove symlink: ${e instanceof Error?e.message:String(e)}`,`REMOVE_SYMLINK_FAILED`)}}},p=class{_platform;constructor(){this._platform=this.normalizePlatform((0,r.platform)())}normalizePlatform(e){switch(e){case`darwin`:return`darwin`;case`win32`:return`win32`;case`linux`:return`linux`;default:return`unknown`}}getPlatform(){return this._platform}isWindows(){return this._platform===`win32`}isMacOS(){return this._platform===`darwin`}isLinux(){return this._platform===`linux`}},m=class{validate(e){this.validateType(e.type),this.validateSource(e.source),this.validateForce(e.force)}validateType(e){if(e&&!c.includes(e))throw new u(`Invalid symlink type: ${e}. Must be one of: ${c.join(`, `)}`,`INVALID_SYMLINK_TYPE`)}validateSource(e){if(!e)return;let r=(0,t.resolve)(e);if(!(0,n.existsSync)(r))throw new u(`Source path does not exist: ${r}`,`SOURCE_NOT_FOUND`)}validateForce(e){if(e!==void 0&&typeof e!=`boolean`)throw new u(`Invalid force option: ${e}. Must be a boolean.`,`INVALID_FORCE_OPTION`)}},h=class{static create(t){let n=new m,r=new e.ConsoleLogger;return new l(n,r,new f(r,new p),t)}},g=class{constructor(e,t,n){this.logger=e,this.configService=t,this.options=n}async execute(){try{let{action:e,key:t,value:n}=this.options;switch(e){case`get`:await this.handleGet(t);break;case`set`:await this.handleSet(t,n);break;case`unset`:await this.handleUnset(t);break;case`list`:await this.handleList();break;case`info`:await this.handleInfo();break;default:throw Error(`Unknown config action: ${e}`)}}catch(e){throw this.logger.error(`Config command failed: ${e instanceof Error?e.message:String(e)}`),e}}async handleGet(e){if(!e)throw Error(`Key is required for get action`);let t=this.configService.get(e);if(t===void 0){this.logger.warning(`Config key '${e}' not found`);return}this.logger.info(t)}async handleSet(e,t){if(!e)throw Error(`Key is required for set action`);if(!t)throw Error(`Value is required for set action`);this.configService.set(e,t),this.logger.success(`Config set: ${e} = ${t}`)}async handleUnset(e){if(!e)throw Error(`Key is required for unset action`);this.configService.unset(e),this.logger.success(`Config unset: ${e}`)}async handleList(){let e=this.configService.list(),t=Object.entries(e);if(t.length===0){this.logger.info(`No configuration found`);return}this.logger.info(`Current configuration:`);for(let[e,n]of t)this.logger.info(` ${e} = ${n}`)}async handleInfo(){let e=this.configService.getConfigInfo();this.logger.info(`Configuration file information:`),this.logger.info(` Path: ${e.path}`),this.logger.info(` Exists: ${e.exists?`Yes`:`No`}`),e.exists&&(this.logger.info(` Size: ${e.size} bytes`),this.logger.info(` Modified: ${e.modified?.toLocaleString()}`)),this.logger.info(` Entries: ${e.entries}`)}};const _=[`get`,`set`,`unset`,`list`,`info`];var v=class{validate(e){this.validateAction(e.action),this.validateKey(e.action,e.key),this.validateValue(e.action,e.value)}validateAction(e){if(!_.includes(e))throw new u(`Invalid config action: ${e}. Must be one of: ${_.join(`, `)}`,`INVALID_CONFIG_ACTION`)}validateKey(e,t){if(!(e===`list`||e===`info`)){if(!t)throw new u(`Key is required for '${e}' action`,`MISSING_CONFIG_KEY`);if(typeof t!=`string`||t.trim().length===0)throw new u(`Invalid config key: ${t}. Key must be a non-empty string.`,`INVALID_CONFIG_KEY`)}}validateValue(e,t){if(e===`set`){if(!t)throw new u(`Value is required for 'set' action`,`MISSING_CONFIG_VALUE`);if(typeof t!=`string`)throw new u(`Invalid config value: ${t}. Value must be a string.`,`INVALID_CONFIG_VALUE`)}}},y=class{static create(t){return new v().validate(t),new g(new e.ConsoleLogger,new s,t)}};exports.ConfigCommandFactory=y,exports.MkSymlinkCommandFactory=h;
package/src/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import{homedir as e,platform as t}from"os";import{exec as n}from"child_process";import{promisify as r}from"util";import{access as i,lstat as a,symlink as o,unlink as s}from"fs/promises";import{existsSync as c,mkdirSync as l,readFileSync as u,statSync as d,writeFileSync as f}from"fs";import{basename as p,dirname as m,resolve as h}from"path";import{ConsoleLogger as g}from"@dx-pkg/logger";var _=class{_platform;constructor(){this._platform=this.normalizePlatform(t())}normalizePlatform(e){switch(e){case`darwin`:return`darwin`;case`win32`:return`win32`;case`linux`:return`linux`;default:return`unknown`}}getPlatform(){return this._platform}isWindows(){return this._platform===`win32`}isMacOS(){return this._platform===`darwin`}isLinux(){return this._platform===`linux`}},v=class extends Error{code;constructor(e,t){super(e),this.name=`SymlinkError`,this.code=t,Error.captureStackTrace(this,this.constructor)}},y=class{constructor(e){this.logger=e}handle(e){throw e instanceof v?(this.logger.error(`Symlink Error [${e.code||`UNKNOWN`}]: ${e.message}`),e):e instanceof Error?(this.logger.error(`Unexpected Error: ${e.message}`),new v(e.message,`UNEXPECTED_ERROR`)):(this.logger.error(`Unknown Error: ${String(e)}`),new v(String(e),`UNKNOWN_ERROR`))}};const b=r(n);var x=class{constructor(e,t){this.logger=e,this.osDetector=t}async createSymlink(e){let{source:t,target:n,type:r=`dir`,force:i=!1}=e;try{if(await this.symlinkExists(n)){if(!i)return{success:!1,source:t,target:n,message:`Symlink already exists: ${n}`};this.logger.info(`Force mode enabled. Removing existing symlink: ${n}`),await this.removeSymlink(n)}return this.osDetector.isWindows()?await this.createWindowsSymlink(t,n,r):await this.createUnixSymlink(t,n),{success:!0,source:t,target:n,message:`Symlink created successfully`}}catch(e){return this.logger.error(`Failed to create symlink: ${e instanceof Error?e.message:String(e)}`),{success:!1,source:t,target:n,error:e instanceof Error?e:Error(String(e))}}}async createWindowsSymlink(e,t,n){let r;switch(n){case`junction`:r=`/J`;break;case`file`:r=``;break;default:r=`/D`;break}let i=r?`mklink ${r} "${t}" "${e}"`:`mklink "${t}" "${e}"`;this.logger.info(`Executing Windows command: ${i}`);try{let{stdout:e,stderr:t}=await b(i,{shell:`cmd.exe`});if(t&&!t.includes(`Junction`)&&!t.includes(`symbolic link`))throw new v(t,`WINDOWS_MKLINK_ERROR`);this.logger.info(`Windows mklink output: ${e}`)}catch(e){throw new v(`Failed to create Windows symlink: ${e instanceof Error?e.message:String(e)}`,`WINDOWS_MKLINK_FAILED`)}}async createUnixSymlink(e,t){try{await o(e,t),this.logger.info(`Unix symlink created: ln -s ${e} ${t}`)}catch(e){throw new v(`Failed to create Unix symlink: ${e instanceof Error?e.message:String(e)}`,`UNIX_SYMLINK_FAILED`)}}async symlinkExists(e){try{return(await a(e)).isSymbolicLink()}catch{return!1}}async removeSymlink(e){try{await i(e),await s(e),this.logger.info(`Symlink removed: ${e}`)}catch(e){throw new v(`Failed to remove symlink: ${e instanceof Error?e.message:String(e)}`,`REMOVE_SYMLINK_FAILED`)}}},S=class{configPath;config={};constructor(t){let n=e();this.configPath=t||h(n,`.mksymlinkrc`),this.loadConfig()}get(e){return this.config[e]}set(e,t){this.config[e]=t,this.saveConfig()}unset(e){delete this.config[e],this.saveConfig()}list(){return{...this.config}}getDefaultSymlinkDir(){return this.get(`symlink.defaultDir`)}setDefaultSymlinkDir(e){this.set(`symlink.defaultDir`,e)}getConfigInfo(){let e=c(this.configPath),t=Object.keys(this.config).length;if(!e)return{path:this.configPath,exists:!1,entries:t};let n=d(this.configPath);return{path:this.configPath,exists:!0,size:n.size,modified:n.mtime,entries:t}}loadConfig(){if(!c(this.configPath)){this.config={};return}try{let e=u(this.configPath,`utf-8`).split(`
2
- `).filter(e=>e.trim());this.config={};for(let t of e){let[e,...n]=t.split(`=`);e&&n.length>0&&(this.config[e.trim()]=n.join(`=`).trim())}}catch(e){throw Error(`Failed to load config from ${this.configPath}: ${e instanceof Error?e.message:String(e)}`)}}saveConfig(){try{let e=m(this.configPath);c(e)||l(e,{recursive:!0});let t=Object.entries(this.config).filter(([,e])=>e!==void 0).map(([e,t])=>`${e}=${t}`).join(`
3
- `);f(this.configPath,t+`
4
- `,`utf-8`)}catch(e){throw Error(`Failed to save config to ${this.configPath}: ${e instanceof Error?e.message:String(e)}`)}}};const C=[`file`,`dir`,`junction`];var w=class{constructor(e,t,n,r,i=new S){this.optionValidator=e,this.logger=t,this.symlinkManager=n,this.options=r,this.configService=i}async execute(){this.optionValidator.validate(this.options);try{let{source:e,target:t,type:n,force:r}=this.resolveOptions();this.logger.info(`Creating symlink with options:`),this.logger.info(` Source: ${e}`),this.logger.info(` Target: ${t}`),this.logger.info(` Type: ${n}`),this.logger.info(` Force: ${r}`);let i=await this.symlinkManager.createSymlink({source:e,target:t,type:n,force:r});if(!i.success){if(i.error)throw i.error;this.logger.warning(i.message||`Symlink creation failed`);return}this.logger.success(`Symlink created successfully!`),this.logger.info(` Source: ${i.source}`),this.logger.info(` Target: ${i.target}`)}catch(e){throw this.logger.error(`Failed to create symlink: ${e instanceof Error?e.message:String(e)}`),e}}resolveOptions(){let e=process.cwd();return{source:this.options.source||e,target:this.resolveTarget(this.options)||this.generateDefaultTarget(e),type:this.options.type||`junction`,force:this.options.force||!1}}resolveTarget(e){if(e.target!==`.`&&e.target!==`./`)return e.target;let t=process.cwd();return h(t,p(e.source||t))}generateDefaultTarget(e){let t=p(e),n=`${p(m(e))}--${t}`;return h(this.configService.getDefaultSymlinkDir()||e,n)}},T=class{validate(e){this.validateType(e.type),this.validateSource(e.source),this.validateForce(e.force)}validateType(e){if(e&&!C.includes(e))throw new v(`Invalid symlink type: ${e}. Must be one of: ${C.join(`, `)}`,`INVALID_SYMLINK_TYPE`)}validateSource(e){if(!e)return;let t=h(e);if(!c(t))throw new v(`Source path does not exist: ${t}`,`SOURCE_NOT_FOUND`)}validateForce(e){if(e!==void 0&&typeof e!=`boolean`)throw new v(`Invalid force option: ${e}. Must be a boolean.`,`INVALID_FORCE_OPTION`)}},E=class{static create(e){let t=new T,n=new g;return new w(t,n,new x(n,new _),e)}},D=class{constructor(e,t,n){this.logger=e,this.configService=t,this.options=n}async execute(){try{let{action:e,key:t,value:n}=this.options;switch(e){case`get`:await this.handleGet(t);break;case`set`:await this.handleSet(t,n);break;case`unset`:await this.handleUnset(t);break;case`list`:await this.handleList();break;case`info`:await this.handleInfo();break;default:throw Error(`Unknown config action: ${e}`)}}catch(e){throw this.logger.error(`Config command failed: ${e instanceof Error?e.message:String(e)}`),e}}async handleGet(e){if(!e)throw Error(`Key is required for get action`);let t=this.configService.get(e);if(t===void 0){this.logger.warning(`Config key '${e}' not found`);return}this.logger.info(t)}async handleSet(e,t){if(!e)throw Error(`Key is required for set action`);if(!t)throw Error(`Value is required for set action`);this.configService.set(e,t),this.logger.success(`Config set: ${e} = ${t}`)}async handleUnset(e){if(!e)throw Error(`Key is required for unset action`);this.configService.unset(e),this.logger.success(`Config unset: ${e}`)}async handleList(){let e=this.configService.list(),t=Object.entries(e);if(t.length===0){this.logger.info(`No configuration found`);return}this.logger.info(`Current configuration:`);for(let[e,n]of t)this.logger.info(` ${e} = ${n}`)}async handleInfo(){let e=this.configService.getConfigInfo();this.logger.info(`Configuration file information:`),this.logger.info(` Path: ${e.path}`),this.logger.info(` Exists: ${e.exists?`Yes`:`No`}`),e.exists&&(this.logger.info(` Size: ${e.size} bytes`),this.logger.info(` Modified: ${e.modified?.toLocaleString()}`)),this.logger.info(` Entries: ${e.entries}`)}};const O=[`get`,`set`,`unset`,`list`,`info`];var k=class{validate(e){this.validateAction(e.action),this.validateKey(e.action,e.key),this.validateValue(e.action,e.value)}validateAction(e){if(!O.includes(e))throw new v(`Invalid config action: ${e}. Must be one of: ${O.join(`, `)}`,`INVALID_CONFIG_ACTION`)}validateKey(e,t){if(!(e===`list`||e===`info`)){if(!t)throw new v(`Key is required for '${e}' action`,`MISSING_CONFIG_KEY`);if(typeof t!=`string`||t.trim().length===0)throw new v(`Invalid config key: ${t}. Key must be a non-empty string.`,`INVALID_CONFIG_KEY`)}}validateValue(e,t){if(e===`set`){if(!t)throw new v(`Value is required for 'set' action`,`MISSING_CONFIG_VALUE`);if(typeof t!=`string`)throw new v(`Invalid config value: ${t}. Value must be a string.`,`INVALID_CONFIG_VALUE`)}}},A=class{static create(e){return new k().validate(e),new D(new g,new S,e)}};export{D as ConfigCommand,A as ConfigCommandFactory,k as ConfigOptionValidator,S as ConfigService,w as MkSymlinkCommand,E as MkSymlinkCommandFactory,T as MkSymlinkOptionValidator,_ as PlatformDetector,C as SYMLINK_TYPES,v as SymlinkError,y as SymlinkErrorHandler,x as SymlinkService};
1
+ import{ConsoleLogger as e}from"@dx-pkg/logger";import{basename as t,dirname as n,resolve as r}from"path";import{existsSync as i,mkdirSync as a,readFileSync as o,statSync as s,writeFileSync as c}from"fs";import{homedir as l,platform as u}from"os";import{exec as d}from"child_process";import{promisify as f}from"util";import{access as p,lstat as m,symlink as h,unlink as g}from"fs/promises";var _=class{configPath;config={};constructor(e){let t=l();this.configPath=e||r(t,`.mksymlinkrc`),this.loadConfig()}get(e){return this.config[e]}set(e,t){this.config[e]=t,this.saveConfig()}unset(e){delete this.config[e],this.saveConfig()}list(){return{...this.config}}getDefaultSymlinkDir(){return this.get(`symlink.defaultDir`)}setDefaultSymlinkDir(e){this.set(`symlink.defaultDir`,e)}getConfigInfo(){let e=i(this.configPath),t=Object.keys(this.config).length;if(!e)return{path:this.configPath,exists:!1,entries:t};let n=s(this.configPath);return{path:this.configPath,exists:!0,size:n.size,modified:n.mtime,entries:t}}loadConfig(){if(!i(this.configPath)){this.config={};return}try{let e=o(this.configPath,`utf-8`).split(`
2
+ `).filter(e=>e.trim());this.config={};for(let t of e){let[e,...n]=t.split(`=`);e&&n.length>0&&(this.config[e.trim()]=n.join(`=`).trim())}}catch(e){throw Error(`Failed to load config from ${this.configPath}: ${e instanceof Error?e.message:String(e)}`)}}saveConfig(){try{let e=n(this.configPath);i(e)||a(e,{recursive:!0});let t=Object.entries(this.config).filter(([,e])=>e!==void 0).map(([e,t])=>`${e}=${t}`).join(`
3
+ `);c(this.configPath,t+`
4
+ `,`utf-8`)}catch(e){throw Error(`Failed to save config to ${this.configPath}: ${e instanceof Error?e.message:String(e)}`)}}};const v=[`file`,`dir`,`junction`];var y=class{constructor(e,t,n,r,i=new _){this.optionValidator=e,this.logger=t,this.symlinkManager=n,this.options=r,this.configService=i}async execute(){this.optionValidator.validate(this.options);try{let{source:e,target:t,type:n,force:r}=this.resolveOptions();this.logger.info(`Creating symlink with options:`),this.logger.info(` Source: ${e}`),this.logger.info(` Target: ${t}`),this.logger.info(` Type: ${n}`),this.logger.info(` Force: ${r}`);let i=await this.symlinkManager.createSymlink({source:e,target:t,type:n,force:r});if(!i.success){if(i.error)throw i.error;this.logger.warning(i.message||`Symlink creation failed`);return}this.logger.success(`Symlink created successfully!`),this.logger.info(` Source: ${i.source}`),this.logger.info(` Target: ${i.target}`)}catch(e){throw this.logger.error(`Failed to create symlink: ${e instanceof Error?e.message:String(e)}`),e}}resolveOptions(){let e=process.cwd();return{source:this.options.source||e,target:this.resolveTarget(this.options)||this.generateDefaultTarget(e),type:this.options.type||`junction`,force:this.options.force||!1}}resolveTarget(e){if(e.target!==`.`&&e.target!==`./`)return e.target;let n=process.cwd();return r(n,t(e.source||n))}generateDefaultTarget(e){let i=t(e),a=`${t(n(e))}--${i}`;return r(this.configService.getDefaultSymlinkDir()||e,a)}},b=class extends Error{code;constructor(e,t){super(e),this.name=`SymlinkError`,this.code=t,Error.captureStackTrace(this,this.constructor)}};const x=f(d);var S=class{constructor(e,t){this.logger=e,this.osDetector=t}async createSymlink(e){let{source:t,target:n,type:r=`dir`,force:i=!1}=e;try{if(await this.symlinkExists(n)){if(!i)return{success:!1,source:t,target:n,message:`Symlink already exists: ${n}`};this.logger.info(`Force mode enabled. Removing existing symlink: ${n}`),await this.removeSymlink(n)}return this.osDetector.isWindows()?await this.createWindowsSymlink(t,n,r):await this.createUnixSymlink(t,n),{success:!0,source:t,target:n,message:`Symlink created successfully`}}catch(e){return this.logger.error(`Failed to create symlink: ${e instanceof Error?e.message:String(e)}`),{success:!1,source:t,target:n,error:e instanceof Error?e:Error(String(e))}}}async createWindowsSymlink(e,t,n){let r;switch(n){case`junction`:r=`/J`;break;case`file`:r=``;break;default:r=`/D`;break}let i=r?`mklink ${r} "${t}" "${e}"`:`mklink "${t}" "${e}"`;this.logger.info(`Executing Windows command: ${i}`);try{let{stdout:e,stderr:t}=await x(i,{shell:`cmd.exe`});if(t&&!t.includes(`Junction`)&&!t.includes(`symbolic link`))throw new b(t,`WINDOWS_MKLINK_ERROR`);this.logger.info(`Windows mklink output: ${e}`)}catch(e){throw new b(`Failed to create Windows symlink: ${e instanceof Error?e.message:String(e)}`,`WINDOWS_MKLINK_FAILED`)}}async createUnixSymlink(e,t){try{await h(e,t),this.logger.info(`Unix symlink created: ln -s ${e} ${t}`)}catch(e){throw new b(`Failed to create Unix symlink: ${e instanceof Error?e.message:String(e)}`,`UNIX_SYMLINK_FAILED`)}}async symlinkExists(e){try{return(await m(e)).isSymbolicLink()}catch{return!1}}async removeSymlink(e){try{await p(e),await g(e),this.logger.info(`Symlink removed: ${e}`)}catch(e){throw new b(`Failed to remove symlink: ${e instanceof Error?e.message:String(e)}`,`REMOVE_SYMLINK_FAILED`)}}},C=class{_platform;constructor(){this._platform=this.normalizePlatform(u())}normalizePlatform(e){switch(e){case`darwin`:return`darwin`;case`win32`:return`win32`;case`linux`:return`linux`;default:return`unknown`}}getPlatform(){return this._platform}isWindows(){return this._platform===`win32`}isMacOS(){return this._platform===`darwin`}isLinux(){return this._platform===`linux`}},w=class{validate(e){this.validateType(e.type),this.validateSource(e.source),this.validateForce(e.force)}validateType(e){if(e&&!v.includes(e))throw new b(`Invalid symlink type: ${e}. Must be one of: ${v.join(`, `)}`,`INVALID_SYMLINK_TYPE`)}validateSource(e){if(!e)return;let t=r(e);if(!i(t))throw new b(`Source path does not exist: ${t}`,`SOURCE_NOT_FOUND`)}validateForce(e){if(e!==void 0&&typeof e!=`boolean`)throw new b(`Invalid force option: ${e}. Must be a boolean.`,`INVALID_FORCE_OPTION`)}},T=class{static create(t){let n=new w,r=new e;return new y(n,r,new S(r,new C),t)}},E=class{constructor(e,t,n){this.logger=e,this.configService=t,this.options=n}async execute(){try{let{action:e,key:t,value:n}=this.options;switch(e){case`get`:await this.handleGet(t);break;case`set`:await this.handleSet(t,n);break;case`unset`:await this.handleUnset(t);break;case`list`:await this.handleList();break;case`info`:await this.handleInfo();break;default:throw Error(`Unknown config action: ${e}`)}}catch(e){throw this.logger.error(`Config command failed: ${e instanceof Error?e.message:String(e)}`),e}}async handleGet(e){if(!e)throw Error(`Key is required for get action`);let t=this.configService.get(e);if(t===void 0){this.logger.warning(`Config key '${e}' not found`);return}this.logger.info(t)}async handleSet(e,t){if(!e)throw Error(`Key is required for set action`);if(!t)throw Error(`Value is required for set action`);this.configService.set(e,t),this.logger.success(`Config set: ${e} = ${t}`)}async handleUnset(e){if(!e)throw Error(`Key is required for unset action`);this.configService.unset(e),this.logger.success(`Config unset: ${e}`)}async handleList(){let e=this.configService.list(),t=Object.entries(e);if(t.length===0){this.logger.info(`No configuration found`);return}this.logger.info(`Current configuration:`);for(let[e,n]of t)this.logger.info(` ${e} = ${n}`)}async handleInfo(){let e=this.configService.getConfigInfo();this.logger.info(`Configuration file information:`),this.logger.info(` Path: ${e.path}`),this.logger.info(` Exists: ${e.exists?`Yes`:`No`}`),e.exists&&(this.logger.info(` Size: ${e.size} bytes`),this.logger.info(` Modified: ${e.modified?.toLocaleString()}`)),this.logger.info(` Entries: ${e.entries}`)}};const D=[`get`,`set`,`unset`,`list`,`info`];var O=class{validate(e){this.validateAction(e.action),this.validateKey(e.action,e.key),this.validateValue(e.action,e.value)}validateAction(e){if(!D.includes(e))throw new b(`Invalid config action: ${e}. Must be one of: ${D.join(`, `)}`,`INVALID_CONFIG_ACTION`)}validateKey(e,t){if(!(e===`list`||e===`info`)){if(!t)throw new b(`Key is required for '${e}' action`,`MISSING_CONFIG_KEY`);if(typeof t!=`string`||t.trim().length===0)throw new b(`Invalid config key: ${t}. Key must be a non-empty string.`,`INVALID_CONFIG_KEY`)}}validateValue(e,t){if(e===`set`){if(!t)throw new b(`Value is required for 'set' action`,`MISSING_CONFIG_VALUE`);if(typeof t!=`string`)throw new b(`Invalid config value: ${t}. Value must be a string.`,`INVALID_CONFIG_VALUE`)}}},k=class{static create(t){return new O().validate(t),new E(new e,new _,t)}};export{k as ConfigCommandFactory,T as MkSymlinkCommandFactory};