launchpd 1.0.3 → 1.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,8 +1,19 @@
1
- import { getVersionsForSubdomain, getActiveVersion } from '../utils/metadata.js';
2
- import { getVersions as getVersionsFromAPI } from '../utils/api.js';
3
- import { isLoggedIn } from '../utils/credentials.js';
4
- import { success, errorWithSuggestions, info, spinner, warning, formatSize, log } from '../utils/logger.js';
5
- import chalk from 'chalk';
1
+ import {
2
+ getVersionsForSubdomain,
3
+ getActiveVersion
4
+ } from '../utils/metadata.js'
5
+ import { getVersions as getVersionsFromAPI } from '../utils/api.js'
6
+ import { isLoggedIn } from '../utils/credentials.js'
7
+ import {
8
+ success,
9
+ errorWithSuggestions,
10
+ info,
11
+ spinner,
12
+ warning,
13
+ formatSize,
14
+ log
15
+ } from '../utils/logger.js'
16
+ import chalk from 'chalk'
6
17
 
7
18
  /**
8
19
  * List all versions for a subdomain
@@ -11,117 +22,146 @@ import chalk from 'chalk';
11
22
  * @param {boolean} options.json - Output as JSON
12
23
  * @param {boolean} options.verbose - Show verbose error details
13
24
  */
14
- export async function versions(subdomainInput, options) {
15
- const subdomain = subdomainInput.toLowerCase();
16
- const verbose = options.verbose || false;
17
-
18
- if (!await isLoggedIn()) {
19
- errorWithSuggestions('The versions feature is only available for authenticated users.', [
20
- 'Run "launchpd login" to log in to your account',
21
- 'Run "launchpd register" to create a new account',
22
- ], { verbose });
23
- process.exit(1);
25
+ export async function versions (subdomainInput, options) {
26
+ const subdomain = subdomainInput.toLowerCase()
27
+ const verbose = options.verbose || false
28
+
29
+ if (!(await isLoggedIn())) {
30
+ errorWithSuggestions(
31
+ 'The versions feature is only available for authenticated users.',
32
+ [
33
+ 'Run "launchpd login" to log in to your account',
34
+ 'Run "launchpd register" to create a new account'
35
+ ],
36
+ { verbose }
37
+ )
38
+ process.exit(1)
39
+ }
40
+
41
+ if (options.to) {
42
+ warning(`The --to option is for the ${chalk.bold('rollback')} command.`)
43
+ const rollbackCmd = `launchpd rollback ${subdomain} --to ${options.to}`
44
+ info(`Try: ${chalk.cyan(rollbackCmd)}`)
45
+ log('')
46
+ }
47
+
48
+ try {
49
+ const fetchSpinner = spinner(`Fetching versions for ${subdomain}...`)
50
+
51
+ let versionList = []
52
+ let activeVersion = 1
53
+
54
+ // Try API first
55
+ const apiResult = await getVersionsFromAPI(subdomain)
56
+ if (apiResult?.versions) {
57
+ versionList = apiResult.versions.map((v) => ({
58
+ version: v.version,
59
+ timestamp: v.created_at || v.timestamp,
60
+ fileCount: v.file_count || v.fileCount,
61
+ totalBytes: v.total_bytes || v.totalBytes,
62
+ message: v.message || ''
63
+ }))
64
+ activeVersion = apiResult.activeVersion || 1
65
+ } else {
66
+ // Fallback to R2 metadata
67
+ versionList = await getVersionsForSubdomain(subdomain)
68
+ activeVersion = await getActiveVersion(subdomain)
24
69
  }
25
70
 
26
- if (options.to) {
27
- warning(`The --to option is for the ${chalk.bold('rollback')} command.`);
28
- info(`Try: ${chalk.cyan(`launchpd rollback ${subdomain} --to ${options.to}`)}`);
29
- log('');
71
+ if (versionList.length === 0) {
72
+ fetchSpinner.fail(`No deployments found for: ${subdomain}`)
73
+ errorWithSuggestions(
74
+ `No deployments found for subdomain: ${subdomain}`,
75
+ [
76
+ 'Check the subdomain name is correct',
77
+ 'Run "launchpd list" to see your deployments',
78
+ 'Deploy a new site with "launchpd deploy ./folder"'
79
+ ],
80
+ { verbose }
81
+ )
82
+ process.exit(1)
30
83
  }
31
84
 
32
- try {
33
- const fetchSpinner = spinner(`Fetching versions for ${subdomain}...`);
34
-
35
- let versionList = [];
36
- let activeVersion = 1;
37
-
38
- // Try API first
39
- const apiResult = await getVersionsFromAPI(subdomain);
40
- if (apiResult && apiResult.versions) {
41
- versionList = apiResult.versions.map(v => ({
42
- version: v.version,
43
- timestamp: v.created_at || v.timestamp,
44
- fileCount: v.file_count || v.fileCount,
45
- totalBytes: v.total_bytes || v.totalBytes,
46
- message: v.message || '',
47
- }));
48
- activeVersion = apiResult.activeVersion || 1;
49
- } else {
50
- // Fallback to R2 metadata
51
- versionList = await getVersionsForSubdomain(subdomain);
52
- activeVersion = await getActiveVersion(subdomain);
53
- }
54
-
55
- if (versionList.length === 0) {
56
- fetchSpinner.fail(`No deployments found for: ${subdomain}`);
57
- errorWithSuggestions(`No deployments found for subdomain: ${subdomain}`, [
58
- 'Check the subdomain name is correct',
59
- 'Run "launchpd list" to see your deployments',
60
- 'Deploy a new site with "launchpd deploy ./folder"',
61
- ], { verbose });
62
- process.exit(1);
63
- }
64
-
65
- fetchSpinner.succeed(`Found ${versionList.length} version(s)`);
66
-
67
- if (options.json) {
68
- log(JSON.stringify({
69
- subdomain,
70
- activeVersion,
71
- versions: versionList.map(v => ({
72
- version: v.version,
73
- timestamp: v.timestamp,
74
- fileCount: v.fileCount,
75
- totalBytes: v.totalBytes,
76
- isActive: v.version === activeVersion,
77
- message: v.message,
78
- })),
79
- }, null, 2));
80
- return;
81
- }
82
-
83
- log('');
84
- success(`Versions for ${chalk.cyan(subdomain)}.launchpd.cloud:`);
85
- log('');
86
-
87
- // Table header
88
- log(chalk.gray(' Version Date Files Size Status Message'));
89
- log(chalk.gray(' ' + '─'.repeat(100)));
90
-
91
- for (const v of versionList) {
92
- const isActive = v.version === activeVersion;
93
-
94
- // Format raw strings for correct padding calculation
95
- const versionRaw = `v${v.version}`;
96
- const dateRaw = new Date(v.timestamp).toLocaleString();
97
- const filesRaw = `${v.fileCount} files`;
98
- const sizeRaw = v.totalBytes ? formatSize(v.totalBytes) : 'unknown';
99
-
100
- // Apply colors and padding separately
101
- const versionStr = chalk.bold.cyan(versionRaw.padEnd(12));
102
- const dateStr = chalk.gray(dateRaw.padEnd(25));
103
- const filesStr = chalk.white(filesRaw.padEnd(10));
104
- const sizeStr = chalk.white(sizeRaw.padEnd(12));
105
- const statusStr = isActive
106
- ? chalk.green.bold('● active'.padEnd(12))
107
- : chalk.gray('○ inactive'.padEnd(12));
108
-
109
- const messageStr = chalk.italic.gray(v.message || '');
110
-
111
- log(` ${versionStr}${dateStr}${filesStr}${sizeStr}${statusStr}${messageStr}`);
112
- }
113
-
114
- log(chalk.gray(' ' + '─'.repeat(100)));
115
- log('');
116
- info(`Use ${chalk.cyan(`launchpd rollback ${subdomain} --to <n>`)} to restore a version.`);
117
- log('');
118
-
119
- } catch (err) {
120
- errorWithSuggestions(`Failed to list versions: ${err.message}`, [
121
- 'Check your internet connection',
122
- 'Verify the subdomain exists',
123
- 'Try running with --verbose for more details',
124
- ], { verbose, cause: err });
125
- process.exit(1);
85
+ fetchSpinner.succeed(`Found ${versionList.length} version(s)`)
86
+
87
+ if (options.json) {
88
+ log(
89
+ JSON.stringify(
90
+ {
91
+ subdomain,
92
+ activeVersion,
93
+ versions: versionList.map((v) => ({
94
+ version: v.version,
95
+ timestamp: v.timestamp,
96
+ fileCount: v.fileCount,
97
+ totalBytes: v.totalBytes,
98
+ isActive: v.version === activeVersion,
99
+ message: v.message
100
+ }))
101
+ },
102
+ null,
103
+ 2
104
+ )
105
+ )
106
+ return
126
107
  }
108
+
109
+ log('')
110
+ success(`Versions for ${chalk.cyan(subdomain)}.launchpd.cloud:`)
111
+ log('')
112
+
113
+ // Table header
114
+ log(
115
+ chalk.gray(
116
+ ' Version Date Files Size Status Message'
117
+ )
118
+ )
119
+ log(chalk.gray(` ${'─'.repeat(100)}`))
120
+
121
+ for (const v of versionList) {
122
+ const isActive = v.version === activeVersion
123
+
124
+ // Format raw strings for correct padding calculation
125
+ const versionRaw = `v${v.version}`
126
+ const dateRaw = new Date(v.timestamp).toLocaleString()
127
+ const filesRaw = `${v.fileCount} files`
128
+ const sizeRaw = v.totalBytes ? formatSize(v.totalBytes) : 'unknown'
129
+
130
+ // Apply colors and padding separately
131
+ const versionStr = chalk.bold.cyan(versionRaw.padEnd(12))
132
+ const dateStr = chalk.gray(dateRaw.padEnd(25))
133
+ const filesStr = chalk.white(filesRaw.padEnd(10))
134
+ const sizeStr = chalk.white(sizeRaw.padEnd(12))
135
+ const statusStr = isActive
136
+ ? chalk.green.bold('● active'.padEnd(12))
137
+ : chalk.gray('○ inactive'.padEnd(12))
138
+
139
+ const messageStr = chalk.italic.gray(v.message || '')
140
+
141
+ log(
142
+ ` ${versionStr}${dateStr}${filesStr}${sizeStr}${statusStr}${messageStr}`
143
+ )
144
+ }
145
+
146
+ log(chalk.gray(` ${'─'.repeat(100)}`))
147
+ log('')
148
+ info(
149
+ (() => {
150
+ const rollbackCmd = `launchpd rollback ${subdomain} --to <n>`
151
+ return `Use ${chalk.cyan(rollbackCmd)} to restore a version.`
152
+ })()
153
+ )
154
+ log('')
155
+ } catch (err) {
156
+ errorWithSuggestions(
157
+ `Failed to list versions: ${err.message}`,
158
+ [
159
+ 'Check your internet connection',
160
+ 'Verify the subdomain exists',
161
+ 'Try running with --verbose for more details'
162
+ ],
163
+ { verbose, cause: err }
164
+ )
165
+ process.exit(1)
166
+ }
127
167
  }
package/src/config.js CHANGED
@@ -1,31 +1,32 @@
1
- import { readFileSync } from 'node:fs';
2
- import { join } from 'node:path';
3
- import { fileURLToPath } from 'node:url';
4
-
5
- const __dirname = fileURLToPath(new URL('.', import.meta.url));
6
-
7
- let packageJson = { version: '1.0.0' }; // Default fallback
8
- try {
9
- const content = readFileSync(join(__dirname, '../package.json'), 'utf8');
10
- if (content) {
11
- packageJson = JSON.parse(content);
12
- }
13
- } catch {
14
- // In some test environments or if package.json is missing,
15
- // we use the fallback version
16
- }
17
-
18
- /**
19
- * Application configuration for Launchpd CLI
20
- * No credentials needed - uploads go through the API proxy
21
- */
22
- export const config = {
23
- // Base domain for deployments
24
- domain: 'launchpd.cloud',
25
-
26
- // API endpoint
27
- apiUrl: 'https://api.launchpd.cloud',
28
-
29
- // CLI version
30
- version: packageJson.version,
31
- };
1
+ import { readFileSync } from 'node:fs'
2
+ import { join } from 'node:path'
3
+ import { fileURLToPath } from 'node:url'
4
+
5
+ const __dirname = fileURLToPath(new URL('.', import.meta.url))
6
+
7
+ let packageJson = { version: '1.0.0' } // Default fallback
8
+ try {
9
+ const content = readFileSync(join(__dirname, '../package.json'), 'utf8')
10
+ if (content) {
11
+ packageJson = JSON.parse(content)
12
+ }
13
+ } catch (err) {
14
+ if (err.code !== 'ENOENT') {
15
+ console.warn('Warning: Could not read package.json:', err.message)
16
+ }
17
+ }
18
+
19
+ /**
20
+ * Application configuration for Launchpd CLI
21
+ * No credentials needed - uploads go through the API proxy
22
+ */
23
+ export const config = {
24
+ // Base domain for deployments
25
+ domain: 'launchpd.cloud',
26
+
27
+ // API endpoint
28
+ apiUrl: 'https://api.launchpd.cloud',
29
+
30
+ // CLI version
31
+ version: packageJson.version
32
+ }