@tothalex/nulljs 0.0.48 → 0.0.53

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 (58) hide show
  1. package/package.json +22 -32
  2. package/src/cli.ts +24 -0
  3. package/src/commands/config.ts +130 -0
  4. package/src/commands/deploy.ts +182 -123
  5. package/src/commands/dev.ts +10 -0
  6. package/src/commands/host.ts +130 -139
  7. package/src/commands/index.ts +6 -8
  8. package/src/commands/secret.ts +364 -56
  9. package/src/commands/status.ts +41 -0
  10. package/src/components/DeployAnimation.tsx +92 -0
  11. package/src/components/DeploymentLogsPane.tsx +79 -0
  12. package/src/components/Header.tsx +57 -0
  13. package/src/components/HelpModal.tsx +64 -0
  14. package/src/components/SystemLogsPane.tsx +78 -0
  15. package/src/config/index.ts +181 -0
  16. package/src/lib/bundle/function.ts +125 -0
  17. package/src/lib/bundle/index.ts +3 -0
  18. package/src/lib/bundle/react.ts +149 -0
  19. package/src/lib/deploy.ts +103 -0
  20. package/src/lib/server.ts +160 -0
  21. package/src/lib/vite.ts +120 -0
  22. package/src/lib/watcher.ts +274 -0
  23. package/src/ui.tsx +363 -0
  24. package/tsconfig.json +30 -0
  25. package/scripts/install-server.js +0 -199
  26. package/src/commands/api.ts +0 -16
  27. package/src/commands/auth.ts +0 -54
  28. package/src/commands/create.ts +0 -43
  29. package/src/commands/dev/function/index.ts +0 -221
  30. package/src/commands/dev/function/utils.ts +0 -99
  31. package/src/commands/dev/index.tsx +0 -126
  32. package/src/commands/dev/logging-manager.ts +0 -87
  33. package/src/commands/dev/server/index.ts +0 -48
  34. package/src/commands/dev/server/utils.ts +0 -37
  35. package/src/commands/dev/ui/components/scroll-area.tsx +0 -141
  36. package/src/commands/dev/ui/components/tab-bar.tsx +0 -67
  37. package/src/commands/dev/ui/index.tsx +0 -71
  38. package/src/commands/dev/ui/logging-context.tsx +0 -76
  39. package/src/commands/dev/ui/tabs/functions-tab.tsx +0 -35
  40. package/src/commands/dev/ui/tabs/server-tab.tsx +0 -36
  41. package/src/commands/dev/ui/tabs/vite-tab.tsx +0 -35
  42. package/src/commands/dev/ui/use-logging.tsx +0 -34
  43. package/src/commands/dev/vite/index.ts +0 -54
  44. package/src/commands/dev/vite/utils.ts +0 -71
  45. package/src/commands/profile.ts +0 -189
  46. package/src/index.ts +0 -346
  47. package/src/lib/api.ts +0 -189
  48. package/src/lib/bundle/function/index.ts +0 -46
  49. package/src/lib/bundle/react/index.ts +0 -2
  50. package/src/lib/bundle/react/spa.ts +0 -77
  51. package/src/lib/bundle/react/ssr/client.ts +0 -93
  52. package/src/lib/bundle/react/ssr/config.ts +0 -77
  53. package/src/lib/bundle/react/ssr/index.ts +0 -4
  54. package/src/lib/bundle/react/ssr/props.ts +0 -71
  55. package/src/lib/bundle/react/ssr/server.ts +0 -83
  56. package/src/lib/config.ts +0 -347
  57. package/src/lib/deployment.ts +0 -244
  58. package/src/lib/update-server.ts +0 -262
@@ -1,17 +1,16 @@
1
+ import type { Command } from 'commander'
1
2
  import { existsSync, writeFileSync, mkdirSync, readFileSync } from 'node:fs'
2
- import { join, resolve, dirname } from 'node:path'
3
+ import { join } from 'node:path'
3
4
  import { execSync } from 'node:child_process'
4
5
  import { homedir } from 'node:os'
5
- import { fileURLToPath } from 'node:url'
6
6
  import chalk from 'chalk'
7
- import { updateServer } from '../lib/update-server'
8
7
 
9
- const getServerBinPath = (): string => {
10
- // Try to find the server binary relative to this module
11
- // This works both in development and when installed globally
12
- const currentFile = fileURLToPath(import.meta.url)
13
- const moduleRoot = resolve(dirname(currentFile), '../..')
14
- return join(moduleRoot, 'bin', 'server')
8
+ type PlatformKey = 'linux-x64' | 'linux-arm64' | 'darwin-arm64'
9
+
10
+ const PLATFORMS: Record<PlatformKey, string> = {
11
+ 'linux-x64': '@tothalex/nulljs-linux-x64',
12
+ 'linux-arm64': '@tothalex/nulljs-linux-arm64',
13
+ 'darwin-arm64': '@tothalex/nulljs-darwin-arm64'
15
14
  }
16
15
 
17
16
  type ProductionConfig = {
@@ -21,8 +20,23 @@ type ProductionConfig = {
21
20
  }
22
21
  }
23
22
 
23
+ const getPlatformKey = (): string => {
24
+ return `${process.platform}-${process.arch}`
25
+ }
26
+
27
+ const getServerBinPath = (): string => {
28
+ const platformKey = getPlatformKey()
29
+ const pkgName = PLATFORMS[platformKey as PlatformKey]
30
+
31
+ if (!pkgName) {
32
+ throw new Error(`Unsupported platform: ${platformKey}`)
33
+ }
34
+
35
+ return require.resolve(`${pkgName}/bin/server`)
36
+ }
37
+
24
38
  const generateProductionKeys = async (): Promise<{ privateKey: string; publicKey: string }> => {
25
- console.log(chalk.blue('🔑 Generating production keys...'))
39
+ console.log(chalk.blue('Generating production keys...'))
26
40
 
27
41
  const keyPair = await crypto.subtle.generateKey(
28
42
  {
@@ -51,60 +65,65 @@ const saveProductionConfig = (cloudPath: string, privateKey: string, publicKey:
51
65
  }
52
66
  }
53
67
 
54
- console.log(chalk.blue('💾 Saving production configuration...'))
68
+ console.log(chalk.blue('Saving production configuration...'))
55
69
  writeFileSync(configPath, JSON.stringify(config, null, 2))
56
70
 
57
71
  // Set proper permissions on the config file (readable only by owner)
58
72
  execSync(`chmod 600 ${configPath}`, { stdio: 'inherit' })
59
73
 
60
- console.log(chalk.green('Production configuration saved'))
74
+ console.log(chalk.green('Production configuration saved'))
61
75
  }
62
76
 
63
- const createSystemdService = (cloudPath: string, publicKey: string, serverBinPath: string, userName: string) => {
64
- const serviceContent = `
65
- [Unit]
66
- Description=NullJS Server
67
- After=network.target
68
- StartLimitIntervalSec=0
69
-
70
- [Service]
71
- Type=simple
72
- Restart=always
73
- RestartSec=1
74
- User=${userName}
75
- ExecStart=${serverBinPath}
76
- Environment=CLOUD_PATH=${cloudPath}
77
- Environment=PUBLIC_KEY=${publicKey}
78
- WorkingDirectory=${cloudPath}
79
-
80
- [Install]
81
- WantedBy=multi-user.target
77
+ const createSystemdService = (
78
+ cloudPath: string,
79
+ publicKey: string,
80
+ serverBinPath: string,
81
+ userName: string
82
+ ) => {
83
+ const serviceContent = `[Unit]
84
+ Description=NullJS Server
85
+ After=network.target
86
+ StartLimitIntervalSec=0
87
+
88
+ [Service]
89
+ Type=simple
90
+ Restart=always
91
+ RestartSec=1
92
+ User=${userName}
93
+ ExecStart=${serverBinPath}
94
+ Environment=CLOUD_PATH=${cloudPath}
95
+ Environment=PUBLIC_KEY=${publicKey}
96
+ WorkingDirectory=${cloudPath}
97
+
98
+ [Install]
99
+ WantedBy=multi-user.target
82
100
  `
83
101
 
84
102
  return serviceContent
85
103
  }
86
104
 
87
-
88
105
  const ensureCloudDirectory = (cloudPath: string) => {
89
106
  if (!existsSync(cloudPath)) {
90
- console.log(chalk.blue('🔧 Creating cloud directory...'))
107
+ console.log(chalk.blue('Creating cloud directory...'))
91
108
  try {
92
109
  mkdirSync(cloudPath, { recursive: true })
93
- console.log(chalk.green(`✅ Cloud directory created: ${cloudPath}`))
110
+ console.log(chalk.green(`Cloud directory created: ${cloudPath}`))
94
111
  } catch (error) {
95
- console.log(chalk.red('❌ Failed to create cloud directory:'), error.message)
112
+ console.log(
113
+ chalk.red('Failed to create cloud directory:'),
114
+ error instanceof Error ? error.message : String(error)
115
+ )
96
116
  throw error
97
117
  }
98
118
  } else {
99
- console.log(chalk.gray(`ℹ️ Cloud directory already exists: ${cloudPath}`))
119
+ console.log(chalk.gray(`Cloud directory already exists: ${cloudPath}`))
100
120
  }
101
121
  }
102
122
 
103
-
104
123
  const installSystemdService = (serviceContent: string) => {
105
124
  const servicePath = '/etc/systemd/system/nulljs.service'
106
125
 
107
- console.log(chalk.blue('🔧 Installing systemd service...'))
126
+ console.log(chalk.blue('Installing systemd service...'))
108
127
 
109
128
  // Write service file
110
129
  writeFileSync('/tmp/nulljs.service', serviceContent)
@@ -114,20 +133,20 @@ const installSystemdService = (serviceContent: string) => {
114
133
  execSync('sudo systemctl daemon-reload', { stdio: 'inherit' })
115
134
  execSync('sudo systemctl enable nulljs.service', { stdio: 'inherit' })
116
135
 
117
- console.log(chalk.green('Systemd service installed and enabled'))
136
+ console.log(chalk.green('Systemd service installed and enabled'))
118
137
  }
119
138
 
120
139
  const validateLinux = () => {
121
140
  if (process.platform !== 'linux') {
122
- console.log(chalk.red('This command only works on Linux'))
141
+ console.log(chalk.red('This command only works on Linux'))
123
142
  process.exit(1)
124
143
  }
125
144
  }
126
145
 
127
146
  const validateServerBinary = (serverBinPath: string) => {
128
147
  if (!existsSync(serverBinPath)) {
129
- console.log(chalk.red('Server binary not found at:'), serverBinPath)
130
- console.log(chalk.yellow(' Make sure you have built the server first'))
148
+ console.log(chalk.red('Server binary not found at:'), serverBinPath)
149
+ console.log(chalk.yellow('Make sure you have installed the package correctly'))
131
150
  process.exit(1)
132
151
  }
133
152
  }
@@ -145,7 +164,7 @@ const checkExistingProductionConfig = (cloudPath: string): ProductionConfig | nu
145
164
  return configContent as ProductionConfig
146
165
  }
147
166
  } catch {
148
- console.log(chalk.yellow('⚠️ Existing production config is invalid, will regenerate'))
167
+ console.log(chalk.yellow('Existing production config is invalid, will regenerate'))
149
168
  }
150
169
 
151
170
  return null
@@ -154,129 +173,65 @@ const checkExistingProductionConfig = (cloudPath: string): ProductionConfig | nu
154
173
  const unhost = async (options: { keepData?: boolean } = {}) => {
155
174
  validateLinux()
156
175
 
157
- console.log(chalk.blue('🧹 Removing NullJS production hosting...'))
176
+ console.log(chalk.blue('Removing NullJS production hosting...'))
158
177
 
159
178
  try {
160
179
  // Stop and disable the systemd service
161
- console.log(chalk.blue('🛑 Stopping NullJS service...'))
180
+ console.log(chalk.blue('Stopping NullJS service...'))
162
181
  try {
163
182
  execSync('sudo systemctl stop nulljs', { stdio: 'inherit' })
164
- console.log(chalk.green('Service stopped'))
183
+ console.log(chalk.green('Service stopped'))
165
184
  } catch {
166
- console.log(chalk.yellow('⚠️ Service was not running'))
185
+ console.log(chalk.yellow('Service was not running'))
167
186
  }
168
187
 
169
188
  try {
170
189
  execSync('sudo systemctl disable nulljs', { stdio: 'inherit' })
171
- console.log(chalk.green('Service disabled'))
190
+ console.log(chalk.green('Service disabled'))
172
191
  } catch {
173
- console.log(chalk.yellow('⚠️ Service was not enabled'))
192
+ console.log(chalk.yellow('Service was not enabled'))
174
193
  }
175
194
 
176
195
  // Remove the systemd service file
177
196
  const servicePath = '/etc/systemd/system/nulljs.service'
178
197
  if (existsSync(servicePath)) {
179
- console.log(chalk.blue('🗑️ Removing systemd service file...'))
198
+ console.log(chalk.blue('Removing systemd service file...'))
180
199
  execSync(`sudo rm ${servicePath}`, { stdio: 'inherit' })
181
200
  execSync('sudo systemctl daemon-reload', { stdio: 'inherit' })
182
- console.log(chalk.green('Systemd service file removed'))
201
+ console.log(chalk.green('Systemd service file removed'))
183
202
  }
184
203
 
185
204
  // Remove cloud directory (optional)
186
205
  if (!options.keepData) {
187
206
  const defaultCloudPath = join(homedir(), '.nulljs')
188
207
  if (existsSync(defaultCloudPath)) {
189
- console.log(chalk.blue('🗂️ Removing cloud directory...'))
208
+ console.log(chalk.blue('Removing cloud directory...'))
190
209
  execSync(`rm -rf ${defaultCloudPath}`, { stdio: 'inherit' })
191
- console.log(chalk.green('Cloud directory removed'))
210
+ console.log(chalk.green('Cloud directory removed'))
192
211
  } else {
193
- console.log(chalk.gray('ℹ️ Cloud directory does not exist'))
212
+ console.log(chalk.gray('Cloud directory does not exist'))
194
213
  }
195
214
  } else {
196
- console.log(chalk.gray('ℹ️ Keeping cloud data (--keep-data specified)'))
215
+ console.log(chalk.gray('Keeping cloud data (--keep-data specified)'))
197
216
  }
198
217
 
199
- console.log(chalk.green('🎉 NullJS hosting cleanup complete!'))
218
+ console.log(chalk.green('NullJS hosting cleanup complete!'))
200
219
  console.log('')
201
- console.log(chalk.blue('📋 Summary:'))
202
- console.log(chalk.gray(' Systemd service stopped and removed'))
220
+ console.log(chalk.blue('Summary:'))
221
+ console.log(chalk.gray(' - Systemd service stopped and removed'))
203
222
  if (!options.keepData) {
204
- console.log(chalk.gray(' Cloud directory removed'))
223
+ console.log(chalk.gray(' - Cloud directory removed'))
205
224
  }
206
225
  } catch (error) {
207
- console.log(chalk.red('❌ Failed to remove hosting setup:'), error.message)
226
+ console.log(
227
+ chalk.red('Failed to remove hosting setup:'),
228
+ error instanceof Error ? error.message : String(error)
229
+ )
208
230
  process.exit(1)
209
231
  }
210
232
  }
211
233
 
212
- const hostUpdate = async () => {
213
- validateLinux()
214
-
215
- console.log(chalk.blue('🔄 Updating NullJS server...'))
216
-
217
- // Check if service exists
218
- const serviceExists = existsSync('/etc/systemd/system/nulljs.service')
219
- if (!serviceExists) {
220
- console.log(chalk.red('❌ NullJS service is not installed. Run `nulljs host` first.'))
221
- process.exit(1)
222
- }
223
-
224
- let wasRunning = false
225
-
226
- try {
227
- // Check if service is running
228
- execSync('sudo systemctl is-active nulljs >/dev/null 2>&1')
229
- wasRunning = true
230
- console.log(chalk.blue('🛑 Stopping NullJS service...'))
231
- execSync('sudo systemctl stop nulljs', { stdio: 'inherit' })
232
- console.log(chalk.green('✅ Service stopped'))
233
- } catch {
234
- // Service wasn't running, that's fine
235
- console.log(chalk.gray('ℹ️ Service was not running'))
236
- }
237
-
238
- try {
239
- // Update the binary
240
- await updateServer()
241
-
242
- // Restart service if it was running before
243
- if (wasRunning) {
244
- console.log(chalk.blue('🚀 Starting NullJS service...'))
245
- execSync('sudo systemctl start nulljs', { stdio: 'inherit' })
246
- console.log(chalk.green('✅ Service restarted'))
247
- }
248
-
249
- console.log(chalk.green('🎉 NullJS server update complete!'))
250
- console.log('')
251
- console.log(chalk.blue('📋 Service management commands:'))
252
- console.log(chalk.gray(' Start: '), chalk.white('sudo systemctl start nulljs'))
253
- console.log(chalk.gray(' Stop: '), chalk.white('sudo systemctl stop nulljs'))
254
- console.log(chalk.gray(' Status: '), chalk.white('sudo systemctl status nulljs'))
255
- console.log(chalk.gray(' Logs: '), chalk.white('sudo journalctl -u nulljs -f'))
256
- } catch (error) {
257
- console.log(chalk.red('❌ Failed to update server:'), error.message)
258
-
259
- // Try to restart the service even if update failed
260
- if (wasRunning) {
261
- try {
262
- console.log(chalk.yellow('⚠️ Attempting to restart service with previous binary...'))
263
- execSync('sudo systemctl start nulljs', { stdio: 'inherit' })
264
- console.log(chalk.green('✅ Service restarted with previous binary'))
265
- } catch (restartError) {
266
- console.log(chalk.red('❌ Failed to restart service:'), restartError.message)
267
- }
268
- }
269
-
270
- process.exit(1)
271
- }
272
- }
273
-
274
- const host = async (cloudPath?: string, options: { update?: boolean } = {}) => {
275
- // Handle update flag
276
- if (options.update) {
277
- return hostUpdate()
278
- }
279
-
234
+ const host = async (cloudPath?: string) => {
280
235
  validateLinux()
281
236
 
282
237
  // Use provided cloud path or default to ~/.nulljs
@@ -286,7 +241,7 @@ const host = async (cloudPath?: string, options: { update?: boolean } = {}) => {
286
241
  : join(process.cwd(), cloudPath)
287
242
  : join(homedir(), '.nulljs')
288
243
 
289
- console.log(chalk.blue('🚀 Setting up NullJS production hosting...'))
244
+ console.log(chalk.blue('Setting up NullJS production hosting...'))
290
245
  console.log(chalk.gray(` Cloud path: ${resolvedCloudPath}`))
291
246
 
292
247
  const serverBinPath = getServerBinPath()
@@ -294,7 +249,7 @@ const host = async (cloudPath?: string, options: { update?: boolean } = {}) => {
294
249
 
295
250
  // Get current user
296
251
  const currentUser = process.env.USER || process.env.USERNAME || 'root'
297
- console.log(chalk.blue(`🔧 Setting up service to run as user: ${currentUser}`))
252
+ console.log(chalk.blue(`Setting up service to run as user: ${currentUser}`))
298
253
 
299
254
  // Ensure cloud directory exists
300
255
  ensureCloudDirectory(resolvedCloudPath)
@@ -307,12 +262,12 @@ const host = async (cloudPath?: string, options: { update?: boolean } = {}) => {
307
262
  saveProductionConfig(resolvedCloudPath, privateKey, publicKey)
308
263
  productionConfig = { key: { private: privateKey, public: publicKey } }
309
264
 
310
- console.log(chalk.green('🔑 Production keys generated'))
311
- console.log(chalk.blue('📋 Production Public Key:'))
312
- console.log(chalk.gray(publicKey))
265
+ console.log(chalk.green('Production keys generated'))
266
+ console.log(chalk.blue('Production Public Key:'))
267
+ console.log(chalk.gray(productionConfig.key.public))
313
268
  } else {
314
- console.log(chalk.green('🔑 Using existing production keys'))
315
- console.log(chalk.blue('📋 Production Public Key:'))
269
+ console.log(chalk.green('Using existing production keys'))
270
+ console.log(chalk.blue('Production Public Key:'))
316
271
  console.log(chalk.gray(productionConfig.key.public))
317
272
  }
318
273
 
@@ -325,15 +280,51 @@ const host = async (cloudPath?: string, options: { update?: boolean } = {}) => {
325
280
  )
326
281
  installSystemdService(serviceContent)
327
282
 
328
- console.log(chalk.green('🎉 NullJS hosting setup complete!'))
283
+ console.log(chalk.green('NullJS hosting setup complete!'))
329
284
  console.log('')
330
- console.log(chalk.blue('📋 Service management commands:'))
285
+ console.log(chalk.blue('Service management commands:'))
331
286
  console.log(chalk.gray(' Start: '), chalk.white('sudo systemctl start nulljs'))
332
287
  console.log(chalk.gray(' Stop: '), chalk.white('sudo systemctl stop nulljs'))
333
288
  console.log(chalk.gray(' Status: '), chalk.white('sudo systemctl status nulljs'))
334
289
  console.log(chalk.gray(' Logs: '), chalk.white('sudo journalctl -u nulljs -f'))
335
290
  console.log('')
336
- console.log(chalk.yellow('⚠️ Remember to start the service: sudo systemctl start nulljs'))
291
+ console.log(chalk.yellow('Remember to start the service: sudo systemctl start nulljs'))
337
292
  }
338
293
 
339
- export { host, unhost }
294
+ export const registerHostCommand = (program: Command) => {
295
+ const hostCmd = program
296
+ .command('host')
297
+ .description('Set up NullJS production hosting with systemd')
298
+ .argument('[cloud-path]', 'Path for cloud storage (default: ~/.nulljs)')
299
+ .action(async (cloudPath?: string) => {
300
+ await host(cloudPath)
301
+ })
302
+
303
+ hostCmd
304
+ .command('remove')
305
+ .description('Remove NullJS production hosting')
306
+ .option('--keep-data', 'Keep cloud data directory')
307
+ .action(async (options: { keepData?: boolean }) => {
308
+ await unhost(options)
309
+ })
310
+
311
+ hostCmd
312
+ .command('logs')
313
+ .description('View NullJS server logs')
314
+ .option('-n, --lines <number>', 'Number of lines to show', '100')
315
+ .option('--no-follow', 'Do not follow log output')
316
+ .action((options: { lines: string; follow: boolean }) => {
317
+ validateLinux()
318
+
319
+ const args = ['-u', 'nulljs', '-n', options.lines]
320
+ if (options.follow) {
321
+ args.push('-f')
322
+ }
323
+
324
+ try {
325
+ execSync(`journalctl ${args.join(' ')}`, { stdio: 'inherit' })
326
+ } catch {
327
+ // User likely pressed Ctrl+C, which is fine
328
+ }
329
+ })
330
+ }
@@ -1,8 +1,6 @@
1
- export * from './deploy'
2
- export * from './auth'
3
- export * from './secret'
4
- export * from './api'
5
- export * from './create'
6
- export * from './dev'
7
- export * from './host'
8
- export * from './profile'
1
+ export { registerDevCommand } from './dev'
2
+ export { registerDeployCommand } from './deploy'
3
+ export { registerConfigCommand } from './config'
4
+ export { registerStatusCommand } from './status'
5
+ export { registerSecretCommand } from './secret'
6
+ export { registerHostCommand } from './host'