slipway-cli 0.0.1 → 0.0.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "slipway-cli",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "description": "CLI for Slipway - Deploy Sails apps with ease",
5
5
  "type": "module",
6
6
  "bin": {
@@ -4,6 +4,56 @@ import { api } from '../lib/api.js'
4
4
  import { isLoggedIn, getCredentials } from '../lib/config.js'
5
5
  import { error, requireProject, createSpinner } from '../lib/utils.js'
6
6
 
7
+ const maritimeMessages = {
8
+ pending: [
9
+ 'Waiting for the tide...',
10
+ 'Checking the compass...',
11
+ 'Reading the stars...',
12
+ 'Gathering the crew...',
13
+ ],
14
+ building: [
15
+ 'Hoisting the sails...',
16
+ 'Loading the cargo...',
17
+ 'Checking the rigging...',
18
+ 'Swabbing the deck...',
19
+ 'Tying the knots...',
20
+ 'Hammering the hull...',
21
+ ],
22
+ pushing: [
23
+ 'Signaling the fleet...',
24
+ 'Sending up a flare...',
25
+ 'Raising the flag...',
26
+ ],
27
+ deploying: [
28
+ 'Charting the course...',
29
+ 'Setting sail...',
30
+ 'Catching the wind...',
31
+ 'Navigating the waters...',
32
+ 'Full speed ahead...',
33
+ 'Approaching the harbor...',
34
+ ],
35
+ }
36
+
37
+ const statusLabels = {
38
+ pending: 'Waiting',
39
+ building: 'Building',
40
+ pushing: 'Pushing',
41
+ deploying: 'Deploying',
42
+ }
43
+
44
+ function createMessageRotator(spinner, status) {
45
+ const messages = maritimeMessages[status]
46
+ if (!messages) return null
47
+ let index = 0
48
+ const label = statusLabels[status] || status
49
+ spinner.setText(`${label} — ${c.dim(messages[0])}`)
50
+ index = 1
51
+ return setInterval(() => {
52
+ spinner.setText(`${label} — ${c.dim(messages[index % messages.length])}`)
53
+ index++
54
+ }, 4000)
55
+ }
56
+
7
57
  export default async function slide(options) {
8
58
  if (!isLoggedIn()) {
9
59
  error('Not logged in. Run `slipway login` first.')
@@ -183,9 +233,11 @@ function watchDeploymentSSE(deploymentId) {
183
233
  const controller = new AbortController()
184
234
  let currentSpin = null
185
235
  let lastStatus = ''
236
+ let messageRotatorInterval = null
186
237
 
187
238
  const timeout = setTimeout(() => {
188
239
  controller.abort()
240
+ if (messageRotatorInterval) clearInterval(messageRotatorInterval)
189
241
  if (currentSpin) currentSpin.stop()
190
242
  reject(new Error('timeout'))
191
243
  }, 10 * 60 * 1000) // 10 minute timeout
@@ -207,6 +259,7 @@ function watchDeploymentSSE(deploymentId) {
207
259
  let buffer = ''
208
260
 
209
261
  currentSpin = createSpinner('Building...').start()
262
+ messageRotatorInterval = createMessageRotator(currentSpin, 'building')
210
263
 
211
264
  while (true) {
212
265
  const { done, value } = await reader.read()
@@ -227,12 +280,12 @@ function watchDeploymentSSE(deploymentId) {
227
280
  // Update spinner based on status
228
281
  if (data.status !== lastStatus) {
229
282
  lastStatus = data.status
283
+ if (messageRotatorInterval) clearInterval(messageRotatorInterval)
230
284
  if (currentSpin) currentSpin.stop()
231
285
 
232
- if (data.status === 'building') {
233
- currentSpin = createSpinner('Building...').start()
234
- } else if (data.status === 'deploying') {
235
- currentSpin = createSpinner('Deploying...').start()
286
+ if (['building', 'pushing', 'deploying'].includes(data.status)) {
287
+ currentSpin = createSpinner('...').start()
288
+ messageRotatorInterval = createMessageRotator(currentSpin, data.status)
236
289
  } else if (data.status === 'running' || data.status === 'failed' || data.status === 'cancelled') {
237
290
  clearTimeout(timeout)
238
291
  controller.abort()
@@ -243,10 +296,12 @@ function watchDeploymentSSE(deploymentId) {
243
296
 
244
297
  // Show build output if available
245
298
  if (data.output) {
299
+ if (messageRotatorInterval) clearInterval(messageRotatorInterval)
246
300
  if (currentSpin) currentSpin.stop()
247
301
  console.log(` ${c.dim(data.output)}`)
248
- if (lastStatus === 'building' || lastStatus === 'deploying') {
249
- currentSpin = createSpinner(lastStatus === 'building' ? 'Building...' : 'Deploying...').start()
302
+ if (['building', 'pushing', 'deploying'].includes(lastStatus)) {
303
+ currentSpin = createSpinner('...').start()
304
+ messageRotatorInterval = createMessageRotator(currentSpin, lastStatus)
250
305
  }
251
306
  }
252
307
  } catch {
@@ -258,6 +313,7 @@ function watchDeploymentSSE(deploymentId) {
258
313
 
259
314
  // Stream ended — resolve with last known status or fall back to polling
260
315
  clearTimeout(timeout)
316
+ if (messageRotatorInterval) clearInterval(messageRotatorInterval)
261
317
  if (currentSpin) currentSpin.stop()
262
318
  if (lastStatus && ['running', 'failed', 'cancelled'].includes(lastStatus)) {
263
319
  resolve({ status: lastStatus })
@@ -267,6 +323,7 @@ function watchDeploymentSSE(deploymentId) {
267
323
  })
268
324
  .catch((err) => {
269
325
  clearTimeout(timeout)
326
+ if (messageRotatorInterval) clearInterval(messageRotatorInterval)
270
327
  if (currentSpin) currentSpin.stop()
271
328
  if (err.name !== 'AbortError') {
272
329
  reject(err)
@@ -281,6 +338,7 @@ function watchDeploymentSSE(deploymentId) {
281
338
  async function watchDeploymentPolling(deploymentId) {
282
339
  const spin = createSpinner('Building...').start()
283
340
  let lastStatus = ''
341
+ let messageRotatorInterval = createMessageRotator(spin, 'building')
284
342
 
285
343
  const maxAttempts = 300 // 10 minutes at 2s intervals
286
344
  let attempts = 0
@@ -296,13 +354,12 @@ async function watchDeploymentPolling(deploymentId) {
296
354
  // Update spinner text based on status
297
355
  if (status !== lastStatus) {
298
356
  lastStatus = status
299
- spin.stop()
357
+ if (messageRotatorInterval) clearInterval(messageRotatorInterval)
300
358
 
301
- if (status === 'building') {
302
- createSpinner('Building...').start()
303
- } else if (status === 'deploying') {
304
- createSpinner('Deploying...').start()
359
+ if (['building', 'pushing', 'deploying'].includes(status)) {
360
+ messageRotatorInterval = createMessageRotator(spin, status)
305
361
  } else if (status === 'running' || status === 'failed' || status === 'cancelled') {
362
+ spin.stop()
306
363
  return { status }
307
364
  }
308
365
  }
@@ -311,6 +368,7 @@ async function watchDeploymentPolling(deploymentId) {
311
368
  }
312
369
  }
313
370
 
371
+ if (messageRotatorInterval) clearInterval(messageRotatorInterval)
314
372
  spin.stop()
315
373
  return { status: 'timeout' }
316
374
  }
package/src/lib/utils.js CHANGED
@@ -68,22 +68,27 @@ export function createSpinner(text) {
68
68
  const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']
69
69
  let i = 0
70
70
  let interval = null
71
+ let currentText = text
71
72
 
72
73
  return {
73
74
  start() {
74
- process.stdout.write(`\r${c.info(frames[0])} ${text}`)
75
+ process.stdout.write(`\r${c.info(frames[0])} ${currentText}`)
75
76
  interval = setInterval(() => {
76
77
  i = (i + 1) % frames.length
77
- process.stdout.write(`\r${c.info(frames[i])} ${text}`)
78
+ process.stdout.write(`\x1b[2K\r${c.info(frames[i])} ${currentText}`)
78
79
  }, 80)
79
80
  return this
80
81
  },
82
+ setText(newText) {
83
+ currentText = newText
84
+ return this
85
+ },
81
86
  stop(finalText) {
82
87
  if (interval) {
83
88
  clearInterval(interval)
84
89
  interval = null
85
90
  }
86
- process.stdout.write('\r' + ' '.repeat(text.length + 4) + '\r')
91
+ process.stdout.write(`\x1b[2K\r`)
87
92
  if (finalText) console.log(finalText)
88
93
  return this
89
94
  },