hostfn 0.1.0

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 (47) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1136 -0
  3. package/_conduct/specs/1.v0.spec.md +1041 -0
  4. package/examples/express-api/package.json +22 -0
  5. package/examples/express-api/src/index.ts +16 -0
  6. package/examples/express-api/tsconfig.json +11 -0
  7. package/examples/github-actions-deploy.yml +40 -0
  8. package/examples/monorepo-config.json +76 -0
  9. package/examples/monorepo-multi-server-config.json +74 -0
  10. package/package.json +39 -0
  11. package/packages/cli/package.json +40 -0
  12. package/packages/cli/src/__tests__/core/backup.test.ts +137 -0
  13. package/packages/cli/src/__tests__/core/health.test.ts +125 -0
  14. package/packages/cli/src/__tests__/core/lock.test.ts +173 -0
  15. package/packages/cli/src/__tests__/core/nginx-multi-domain.test.ts +176 -0
  16. package/packages/cli/src/__tests__/runtimes/pm2.test.ts +130 -0
  17. package/packages/cli/src/__tests__/utils/validation.test.ts +164 -0
  18. package/packages/cli/src/commands/deploy.ts +817 -0
  19. package/packages/cli/src/commands/env.ts +391 -0
  20. package/packages/cli/src/commands/expose.ts +438 -0
  21. package/packages/cli/src/commands/init.ts +192 -0
  22. package/packages/cli/src/commands/logs.ts +106 -0
  23. package/packages/cli/src/commands/rollback.ts +142 -0
  24. package/packages/cli/src/commands/server/info.ts +131 -0
  25. package/packages/cli/src/commands/server/setup.ts +200 -0
  26. package/packages/cli/src/commands/status.ts +149 -0
  27. package/packages/cli/src/config/loader.ts +66 -0
  28. package/packages/cli/src/config/schema.ts +140 -0
  29. package/packages/cli/src/core/backup.ts +128 -0
  30. package/packages/cli/src/core/health.ts +116 -0
  31. package/packages/cli/src/core/local.ts +67 -0
  32. package/packages/cli/src/core/lock.ts +108 -0
  33. package/packages/cli/src/core/nginx.ts +170 -0
  34. package/packages/cli/src/core/ssh.ts +335 -0
  35. package/packages/cli/src/core/sync.ts +138 -0
  36. package/packages/cli/src/core/workspace.ts +180 -0
  37. package/packages/cli/src/index.ts +240 -0
  38. package/packages/cli/src/runtimes/base.ts +144 -0
  39. package/packages/cli/src/runtimes/nodejs/detector.ts +157 -0
  40. package/packages/cli/src/runtimes/nodejs/index.ts +228 -0
  41. package/packages/cli/src/runtimes/nodejs/pm2.ts +71 -0
  42. package/packages/cli/src/runtimes/registry.ts +76 -0
  43. package/packages/cli/src/utils/logger.ts +86 -0
  44. package/packages/cli/src/utils/validation.ts +147 -0
  45. package/packages/cli/tsconfig.json +25 -0
  46. package/packages/cli/vitest.config.ts +19 -0
  47. package/turbo.json +24 -0
@@ -0,0 +1,147 @@
1
+ import { Logger } from './logger.js';
2
+
3
+ /**
4
+ * Validation utilities
5
+ */
6
+
7
+ /**
8
+ * Validate SSH connection string format
9
+ */
10
+ export function validateSSHConnection(connectionString: string): boolean {
11
+ const regex = /^([^@]+)@([^:]+)(?::(\d+))?$/;
12
+
13
+ if (!regex.test(connectionString)) {
14
+ Logger.error(`Invalid SSH connection string: ${connectionString}`);
15
+ Logger.info('Expected format: user@host or user@host:port');
16
+ Logger.info('Examples:');
17
+ Logger.log(' ubuntu@123.45.67.89');
18
+ Logger.log(' ubuntu@myserver.com:2222');
19
+ return false;
20
+ }
21
+
22
+ return true;
23
+ }
24
+
25
+ /**
26
+ * Validate HTTP URL format
27
+ */
28
+ export function validateHttpUrl(url: string): boolean {
29
+ try {
30
+ const parsed = new URL(url);
31
+ if (!['http:', 'https:'].includes(parsed.protocol)) {
32
+ Logger.error(`Invalid URL protocol: ${parsed.protocol}`);
33
+ Logger.info('Expected: http:// or https://');
34
+ return false;
35
+ }
36
+ return true;
37
+ } catch (error) {
38
+ Logger.error(`Invalid URL format: ${url}`);
39
+ Logger.info('Expected format: http://host:port/path');
40
+ return false;
41
+ }
42
+ }
43
+
44
+ /**
45
+ * Validate environment name
46
+ */
47
+ export function validateEnvironmentName(name: string): boolean {
48
+ const regex = /^[a-zA-Z0-9_-]+$/;
49
+
50
+ if (!regex.test(name)) {
51
+ Logger.error(`Invalid environment name: ${name}`);
52
+ Logger.info('Environment names can only contain: a-z, A-Z, 0-9, _, -');
53
+ Logger.info('Examples: production, staging, dev, prod-eu');
54
+ return false;
55
+ }
56
+
57
+ return true;
58
+ }
59
+
60
+ /**
61
+ * Validate port number
62
+ */
63
+ export function validatePort(port: number): boolean {
64
+ if (port < 1 || port > 65535) {
65
+ Logger.error(`Invalid port number: ${port}`);
66
+ Logger.info('Port must be between 1 and 65535');
67
+ return false;
68
+ }
69
+
70
+ if (port < 1024) {
71
+ Logger.warn(`Port ${port} is privileged (< 1024) - may require sudo`);
72
+ }
73
+
74
+ return true;
75
+ }
76
+
77
+ /**
78
+ * Validate Node.js version format
79
+ */
80
+ export function validateNodeVersion(version: string): boolean {
81
+ const regex = /^\d+(\.\d+)?(\.\d+)?$/;
82
+
83
+ if (!regex.test(version)) {
84
+ Logger.error(`Invalid Node.js version: ${version}`);
85
+ Logger.info('Expected format: 18 or 18.19 or 18.19.0');
86
+ return false;
87
+ }
88
+
89
+ const major = parseInt(version.split('.')[0]);
90
+ if (major < 14) {
91
+ Logger.warn(`Node.js ${version} is very old - consider using 18 or 20`);
92
+ }
93
+
94
+ return true;
95
+ }
96
+
97
+ /**
98
+ * Validate application name
99
+ */
100
+ export function validateAppName(name: string): boolean {
101
+ const regex = /^[a-zA-Z0-9_-]+$/;
102
+
103
+ if (!regex.test(name)) {
104
+ Logger.error(`Invalid application name: ${name}`);
105
+ Logger.info('Names can only contain: a-z, A-Z, 0-9, _, -');
106
+ Logger.info('Examples: my-app, api_server, webapp-v2');
107
+ return false;
108
+ }
109
+
110
+ if (name.length < 2) {
111
+ Logger.error('Application name must be at least 2 characters');
112
+ return false;
113
+ }
114
+
115
+ if (name.length > 50) {
116
+ Logger.error('Application name must be less than 50 characters');
117
+ return false;
118
+ }
119
+
120
+ return true;
121
+ }
122
+
123
+ /**
124
+ * Validate remote directory path
125
+ */
126
+ export function validateRemotePath(path: string): boolean {
127
+ if (!path.startsWith('/')) {
128
+ Logger.error(`Remote path must be absolute: ${path}`);
129
+ Logger.info('Example: /var/www/my-app');
130
+ return false;
131
+ }
132
+
133
+ if (path.includes('..')) {
134
+ Logger.error('Remote path cannot contain ..');
135
+ return false;
136
+ }
137
+
138
+ // Warn about dangerous paths
139
+ const dangerousPaths = ['/', '/etc', '/bin', '/usr', '/var/log', '/root', '/home'];
140
+ if (dangerousPaths.includes(path)) {
141
+ Logger.error(`Refusing to deploy to system directory: ${path}`);
142
+ Logger.info('Use a subdirectory like /var/www/my-app');
143
+ return false;
144
+ }
145
+
146
+ return true;
147
+ }
@@ -0,0 +1,25 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "lib": ["ES2022"],
6
+ "moduleResolution": "node",
7
+ "esModuleInterop": true,
8
+ "allowSyntheticDefaultImports": true,
9
+ "strict": true,
10
+ "skipLibCheck": true,
11
+ "forceConsistentCasingInFileNames": true,
12
+ "resolveJsonModule": true,
13
+ "declaration": true,
14
+ "declarationMap": true,
15
+ "sourceMap": true,
16
+ "outDir": "./dist",
17
+ "rootDir": "./src",
18
+ "baseUrl": "./src",
19
+ "paths": {
20
+ "@/*": ["./*"]
21
+ }
22
+ },
23
+ "include": ["src/**/*"],
24
+ "exclude": ["node_modules", "dist"]
25
+ }
@@ -0,0 +1,19 @@
1
+ import { defineConfig } from 'vitest/config';
2
+
3
+ export default defineConfig({
4
+ test: {
5
+ globals: true,
6
+ environment: 'node',
7
+ coverage: {
8
+ provider: 'v8',
9
+ reporter: ['text', 'json', 'html'],
10
+ exclude: [
11
+ 'node_modules/**',
12
+ 'dist/**',
13
+ '**/*.d.ts',
14
+ '**/*.config.*',
15
+ '**/index.ts',
16
+ ],
17
+ },
18
+ },
19
+ });
package/turbo.json ADDED
@@ -0,0 +1,24 @@
1
+ {
2
+ "$schema": "https://turbo.build/schema.json",
3
+ "globalDependencies": ["**/.env.*local"],
4
+ "pipeline": {
5
+ "build": {
6
+ "dependsOn": ["^build"],
7
+ "outputs": ["dist/**"]
8
+ },
9
+ "dev": {
10
+ "cache": false,
11
+ "persistent": true
12
+ },
13
+ "lint": {
14
+ "outputs": []
15
+ },
16
+ "test": {
17
+ "dependsOn": ["build"],
18
+ "outputs": []
19
+ },
20
+ "clean": {
21
+ "cache": false
22
+ }
23
+ }
24
+ }