launchpd 1.0.5 → 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.
package/README.md CHANGED
@@ -6,7 +6,7 @@
6
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
7
  [![GitHub stars](https://img.shields.io/github/stars/kents00/launchpd.svg?style=social)](https://github.com/kents00/launchpd)
8
8
  [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=kents00_Launchpd&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=kents00_Launchpd)
9
- [![DeepSource](https://app.deepsource.com/gh/kents00/Launchpd.svg/?label=resolved+issues&show_trend=true&token=UIl3aQ-ZhB-iXYsoVgn0-spU)](https://app.deepsource.com/gh/kents00/Launchpd/)
9
+ [![DeepSource](https://app.deepsource.com/gh/kents00/Launchpd.svg/?label=active+issues&show_trend=true&token=UIl3aQ-ZhB-iXYsoVgn0-spU)](https://app.deepsource.com/gh/kents00/Launchpd/)
10
10
 
11
11
  ---
12
12
 
@@ -46,14 +46,28 @@ _Requires **Node.js 20** or higher._
46
46
 
47
47
  ### Deployment
48
48
 
49
- | Command | Description |
50
- | :---------------------------------- | :----------------------------------------------------------------- |
51
- | `launchpd init` | Link current folder to a subdomain (persisted in `.launchpd.json`) |
52
- | `launchpd deploy <folder>` | Deploy a local folder (uses linked subdomain if available) |
53
- | `launchpd deploy . -m "Fix layout"` | Deploy with a message (like a git commit) |
54
- | `launchpd deploy . --name site` | Deploy with a custom subdomain explicitly |
55
- | `launchpd deploy . --expires 2h` | Set auto-deletion (e.g., `30m`, `1d`, `7d`) |
56
- | `launchpd deploy . --open` | Deploy and immediately open the site in your browser |
49
+ | Command | Description |
50
+ | :------------------------------------ | :----------------------------------------------------------------- |
51
+ | `launchpd init` | Link current folder to a subdomain (persisted in `.launchpd.json`) |
52
+ | `launchpd deploy <folder\|url>` | Deploy a local folder or remote URL (GitHub/Gist) |
53
+ | `launchpd deploy . --name <site>` | Deploy with a custom subdomain explicitly |
54
+ | `launchpd deploy . --expires <time>` | Set auto-deletion (e.g., `30m`, `1d`, `7d`) |
55
+ | `launchpd deploy . --open` | Deploy and immediately open the site in your browser |
56
+
57
+ ### Remote Deployments (GitHub & Gist)
58
+
59
+ Deploy directly from a public GitHub repository or Gist without cloning it locally:
60
+
61
+ ```bash
62
+ # Deploy from a GitHub repo
63
+ launchpd deploy https://github.com/user/repo
64
+
65
+ # Deploy from a specific branch and subdirectory
66
+ launchpd deploy https://github.com/user/repo --branch main --dir dist
67
+
68
+ # Deploy from a GitHub Gist
69
+ launchpd deploy https://gist.github.com/user/@username
70
+ ```
57
71
 
58
72
  ### Management
59
73
 
@@ -106,16 +120,3 @@ Run `launchpd register` to unlock these benefits!
106
120
  ## License
107
121
 
108
122
  [MIT](LICENSE) © [Kent Edoloverio](https://github.com/kents00)
109
-
110
- ---
111
-
112
- ## Publishing (Maintainers)
113
-
114
- Publishing is automated from GitHub Releases. Create a release tag like `v1.0.4` and the workflow will:
115
-
116
- 1. Extract the version from the tag
117
- 2. Update `package.json`
118
- 3. Run tests
119
- 4. Publish to npm
120
-
121
- Ensure `NPM_TOKEN` is set in GitHub Actions secrets.
package/bin/cli.js CHANGED
@@ -32,14 +32,16 @@ program
32
32
 
33
33
  program
34
34
  .command('deploy')
35
- .description('Deploy a folder to a live URL')
36
- .argument('[folder]', 'Path to the folder to deploy', '.')
35
+ .description('Deploy a folder or remote URL to a live URL')
36
+ .argument('[source]', 'Path to folder, GitHub repo URL, or Gist URL', '.')
37
37
  .option('--name <subdomain>', 'Use a custom subdomain (optional)')
38
38
  .option('-m, --message <text>', 'Deployment message (optional)')
39
39
  .option(
40
40
  '--expires <time>',
41
41
  'Auto-delete after time (e.g., 30m, 2h, 1d). Minimum: 30m'
42
42
  )
43
+ .option('--branch <branch>', 'Git branch to deploy from (for repo URLs)')
44
+ .option('--dir <path>', 'Subdirectory within the repo to deploy')
43
45
  .option('-y, --yes', 'Auto-confirm all prompts')
44
46
  .option('--force', 'Force deployment even with warnings')
45
47
  .option(
@@ -48,8 +50,8 @@ program
48
50
  )
49
51
  .option('--verbose', 'Show detailed error information')
50
52
  .option('--qr', 'Show QR code for deployment')
51
- .action(async (folder, options) => {
52
- await deploy(folder || '.', options)
53
+ .action(async (source, options) => {
54
+ await deploy(source || '.', options)
53
55
  })
54
56
 
55
57
  program
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "launchpd",
3
- "version": "1.0.5",
3
+ "version": "1.0.6",
4
4
  "description": "Deploy static sites instantly to a live URL",
5
5
  "keywords": [
6
6
  "static",
@@ -51,11 +51,11 @@
51
51
  "dependencies": {
52
52
  "chalk": "^5.4.0",
53
53
  "commander": "^14.0.0",
54
- "launchpd": "^1.0.0",
55
54
  "mime-types": "^2.1.35",
56
55
  "nanoid": "^5.1.0",
57
56
  "ora": "^8.0.1",
58
57
  "qrcode": "^1.5.4",
58
+ "tar": "^7.5.10",
59
59
  "update-notifier": "^7.3.1"
60
60
  },
61
61
  "devDependencies": {
@@ -23,7 +23,11 @@ import {
23
23
  } from '../utils/logger.js'
24
24
  import { formatBytes } from '../utils/quota.js'
25
25
  import { handleCommonError } from '../utils/errors.js'
26
- import { resendVerification } from '../utils/api.js'
26
+ import {
27
+ resendVerification,
28
+ createFetchTimeout,
29
+ API_TIMEOUT_MS
30
+ } from '../utils/api.js'
27
31
  import chalk from 'chalk'
28
32
 
29
33
  const API_BASE_URL = config.apiUrl
@@ -33,7 +37,7 @@ const REGISTER_URL = `https://${config.domain}/`
33
37
  * Validate API key format
34
38
  * Returns true if the key matches expected format: lpd_ followed by alphanumeric/special chars
35
39
  */
36
- function isValidApiKeyFormat(apiKey) {
40
+ function isValidApiKeyFormat (apiKey) {
37
41
  if (!apiKey || typeof apiKey !== 'string') {
38
42
  return false
39
43
  }
@@ -45,18 +49,20 @@ function isValidApiKeyFormat(apiKey) {
45
49
  /**
46
50
  * Validate API key with the server
47
51
  */
48
- async function validateApiKey(apiKey) {
52
+ async function validateApiKey (apiKey) {
49
53
  // Validate API key format before sending to network
50
54
  // This ensures we only send properly formatted keys, not arbitrary file data
51
55
  if (!isValidApiKeyFormat(apiKey)) {
52
56
  return null
53
57
  }
54
58
 
59
+ const { signal, clear } = createFetchTimeout(API_TIMEOUT_MS)
55
60
  try {
56
61
  const response = await fetch(`${API_BASE_URL}/api/quota`, {
57
62
  headers: {
58
63
  'X-API-Key': apiKey
59
- }
64
+ },
65
+ signal
60
66
  })
61
67
 
62
68
  if (response.status === 401) {
@@ -76,15 +82,20 @@ async function validateApiKey(apiKey) {
76
82
  return data
77
83
  }
78
84
  return null
79
- } catch {
85
+ } catch (err) {
86
+ if (err.name === 'AbortError') {
87
+ return { timeout: true }
88
+ }
80
89
  return null
90
+ } finally {
91
+ clear()
81
92
  }
82
93
  }
83
94
 
84
95
  /**
85
96
  * Background update credentials if new data (like apiSecret) is available
86
97
  */
87
- async function updateCredentialsIfNeeded(creds, result) {
98
+ async function updateCredentialsIfNeeded (creds, result) {
88
99
  if (result.user?.api_secret && !creds.apiSecret) {
89
100
  await saveCredentials({
90
101
  ...creds,
@@ -98,7 +109,7 @@ async function updateCredentialsIfNeeded(creds, result) {
98
109
  /**
99
110
  * Login with API key (original method)
100
111
  */
101
- async function loginWithApiKey() {
112
+ async function loginWithApiKey () {
102
113
  log('Enter your API key from the dashboard.')
103
114
  log(`Don't have one? Run ${chalk.cyan('"launchpd register"')} first.\n`)
104
115
 
@@ -127,6 +138,16 @@ async function loginWithApiKey() {
127
138
  return null
128
139
  }
129
140
 
141
+ if (result.timeout) {
142
+ validateSpinner.fail('Request timed out')
143
+ errorWithSuggestions('The server did not respond in time.', [
144
+ 'Check your internet connection',
145
+ 'Try again later',
146
+ 'If the problem persists, check https://status.launchpd.cloud'
147
+ ])
148
+ return null
149
+ }
150
+
130
151
  if (result.requires_2fa) {
131
152
  validateSpinner.fail('2FA Required')
132
153
  info(
@@ -146,7 +167,7 @@ async function loginWithApiKey() {
146
167
  /**
147
168
  * Login command - prompts for API key and validates it
148
169
  */
149
- export async function login() {
170
+ export async function login () {
150
171
  // Check if already logged in
151
172
  if (await isLoggedIn()) {
152
173
  const creds = await getCredentials()
@@ -203,7 +224,7 @@ export async function login() {
203
224
  /**
204
225
  * Logout command - clears stored credentials and invalidates server session
205
226
  */
206
- export async function logout() {
227
+ export async function logout () {
207
228
  const loggedIn = await isLoggedIn()
208
229
 
209
230
  if (!loggedIn) {
@@ -227,7 +248,7 @@ export async function logout() {
227
248
  /**
228
249
  * Register command - opens browser to registration page
229
250
  */
230
- export function register() {
251
+ export function register () {
231
252
  log('\nRegister for Launchpd\n')
232
253
  log(`Opening registration page: ${chalk.cyan(REGISTER_URL)}\n`)
233
254
 
@@ -273,7 +294,7 @@ export function register() {
273
294
  /**
274
295
  * Whoami command - shows current user info and quota status
275
296
  */
276
- export async function whoami() {
297
+ export async function whoami () {
277
298
  const creds = await getCredentials()
278
299
 
279
300
  if (!creds) {
@@ -387,7 +408,7 @@ export async function whoami() {
387
408
  /**
388
409
  * Quota command - shows detailed quota information
389
410
  */
390
- export async function quota() {
411
+ export async function quota () {
391
412
  const creds = await getCredentials()
392
413
 
393
414
  if (!creds) {
@@ -491,7 +512,7 @@ export async function quota() {
491
512
  /**
492
513
  * Create a simple progress bar with color coding
493
514
  */
494
- function createProgressBar(current, max, width = 20) {
515
+ function createProgressBar (current, max, width = 20) {
495
516
  const filled = Math.round((current / max) * width)
496
517
  const empty = width - filled
497
518
  const percent = (current / max) * 100
@@ -514,7 +535,7 @@ function createProgressBar(current, max, width = 20) {
514
535
  /**
515
536
  * Get colored percentage text
516
537
  */
517
- function getPercentColor(percent) {
538
+ function getPercentColor (percent) {
518
539
  if (percent >= 90) {
519
540
  return chalk.red(`${percent}%`)
520
541
  } else if (percent >= 70) {
@@ -526,7 +547,7 @@ function getPercentColor(percent) {
526
547
  /**
527
548
  * Resend email verification command
528
549
  */
529
- export async function resendEmailVerification() {
550
+ export async function resendEmailVerification () {
530
551
  const loggedIn = await isLoggedIn()
531
552
 
532
553
  if (!loggedIn) {