wattpm 3.4.1 → 3.5.1

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 ADDED
@@ -0,0 +1,46 @@
1
+ ![The Platformatic logo](https://github.com/platformatic/platformatic/raw/HEAD/assets/banner-light.png)
2
+
3
+ # Watt, the Application Server for Node.js
4
+
5
+ [![npm](https://img.shields.io/npm/v/wattpm)](https://www.npmjs.com/package/wattpm)
6
+ [![CI](https://github.com/platformatic/platformatic/actions/workflows/ci.yml/badge.svg)](https://github.com/platformatic/platformatic/actions/workflows/ci.yml)
7
+ [![NPM version](https://img.shields.io/npm/v/platformatic.svg?style=flat)](https://www.npmjs.com/package/platformatic)
8
+ [![Discord](https://img.shields.io/discord/1011258196905689118)](https://discord.gg/platformatic)
9
+
10
+ Watt, Platformatic's Node.js application server, allows you to run multiple Node.js applications that are centrally managed.
11
+
12
+ By using Watt, you gain access to a virtual mesh network, fast logging via [Pino](https://getpino.io/),
13
+ monitoring through [Prometheus](https://prometheus.io/), and [OpenTelemetry](https://opentelemetry.io/) integrations.
14
+
15
+ Watt supports the stacks you love most, including [Next.js](https://nextjs.org/), [Astro](https://astro.build/),
16
+ [Express](https://expressjs.com/), and [Fastify](https://fastify.dev/).
17
+
18
+ ## Install
19
+
20
+ ```bash
21
+ # Create a new application
22
+ npx wattpm@latest init
23
+
24
+ # Or install manually:
25
+ npm install wattpm
26
+ ```
27
+
28
+ Follow our [Quick Start Guide](https://docs.platformatic.dev/docs/getting-started/quick-start)
29
+ guide to get up and running with Platformatic.
30
+
31
+ ## Documentation
32
+
33
+ - [Getting Started](https://docs.platformatic.dev/docs/getting-started/quick-start)
34
+ - [Reference](https://docs.platformatic.dev/docs/reference/wattpm/overview)
35
+ - [Guides](https://docs.platformatic.dev/docs/guides/build-modular-monolith)
36
+
37
+ Check out our full documentation at [platformatic.dev](https://platformatic.dev).
38
+
39
+ ## Support
40
+
41
+ If you run into a bug, issues or have a suggestion for improvement, please raise an
42
+ [issue on GitHub](https://github.com/platformatic/platformatic/issues/new) or join our [Discord feedback](https://discord.gg/platformatic) channel.
43
+
44
+ ## License
45
+
46
+ [Apache 2.0](../../LICENSE)
package/bin/cli.js ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { checkNodeVersionForApplications, setExecutableId, setExecutableName } from '@platformatic/foundation'
4
+
5
+ checkNodeVersionForApplications()
6
+ setExecutableId('wattpm')
7
+ setExecutableName('Watt')
8
+
9
+ // Use await import here so that we can throw a proper error on unsupported Node.js version
10
+ const { main } = await import('../index.js')
11
+ await main()
package/index.js CHANGED
@@ -1,19 +1,25 @@
1
+ import { createCliLogger, getExecutableId, logFatalError, parseArgs, setVerbose } from '@platformatic/foundation'
2
+ import { loadApplicationsCommands } from '@platformatic/runtime'
3
+ import * as colorette from 'colorette'
1
4
  import { bold } from 'colorette'
5
+ import { adminCommand } from './lib/commands/admin.js'
2
6
  import { buildCommand } from './lib/commands/build.js'
7
+ import { createCommand } from './lib/commands/create.js'
3
8
  import { devCommand, reloadCommand, restartCommand, startCommand, stopCommand } from './lib/commands/execution.js'
4
- import { importCommand, resolveCommand } from './lib/commands/external.js'
5
9
  import { helpCommand } from './lib/commands/help.js'
6
- import { initCommand } from './lib/commands/init.js'
7
10
  import { injectCommand } from './lib/commands/inject.js'
8
11
  import { logsCommand } from './lib/commands/logs.js'
9
- import { configCommand, envCommand, psCommand, servicesCommand } from './lib/commands/management.js'
12
+ import { applicationsCommand, configCommand, envCommand, psCommand } from './lib/commands/management.js'
13
+ import { metricsCommand } from './lib/commands/metrics.js'
14
+ import { pprofCommand } from './lib/commands/pprof.js'
10
15
  import { version } from './lib/schema.js'
11
- import { createLogger, overrideFatal, parseArgs, setVerbose } from './lib/utils.js'
16
+
17
+ export * from './lib/schema.js'
12
18
 
13
19
  export async function main () {
14
- const logger = createLogger('info')
20
+ globalThis.platformatic = { executable: getExecutableId() }
15
21
 
16
- overrideFatal(logger)
22
+ const logger = createCliLogger('info')
17
23
 
18
24
  const options = {
19
25
  verbose: {
@@ -25,6 +31,7 @@ export async function main () {
25
31
  type: 'boolean'
26
32
  },
27
33
  help: {
34
+ short: 'h',
28
35
  type: 'boolean'
29
36
  }
30
37
  }
@@ -37,7 +44,10 @@ export async function main () {
37
44
  }
38
45
 
39
46
  if (values.help) {
40
- helpCommand([])
47
+ helpCommand(logger, [])
48
+ return
49
+ } else if (unparsed.includes('-h') || unparsed.includes('--help')) {
50
+ helpCommand(logger, unparsed)
41
51
  return
42
52
  }
43
53
 
@@ -46,10 +56,9 @@ export async function main () {
46
56
  }
47
57
 
48
58
  let command
49
- switch (unparsed[0]) {
50
- case 'init':
51
- command = initCommand
52
- break
59
+ const requestedCommand = unparsed[0] || 'help'
60
+ let applicationCommandContext
61
+ switch (requestedCommand) {
53
62
  case 'build':
54
63
  command = buildCommand
55
64
  break
@@ -71,8 +80,8 @@ export async function main () {
71
80
  case 'ps':
72
81
  command = psCommand
73
82
  break
74
- case 'services':
75
- command = servicesCommand
83
+ case 'applications':
84
+ command = applicationsCommand
76
85
  break
77
86
  case 'config':
78
87
  command = configCommand
@@ -86,23 +95,51 @@ export async function main () {
86
95
  case 'inject':
87
96
  command = injectCommand
88
97
  break
89
- case 'import':
90
- command = importCommand
98
+ case 'metrics':
99
+ command = metricsCommand
91
100
  break
92
- case 'resolve':
93
- command = resolveCommand
101
+ case 'pprof':
102
+ command = pprofCommand
103
+ break
104
+ case 'admin':
105
+ command = adminCommand
106
+ break
107
+ /* c8 ignore next 2 - aliases */
108
+ case 'init':
109
+ case 'add':
110
+ case 'create':
111
+ command = createCommand
94
112
  break
95
113
  case 'help':
96
114
  command = helpCommand
97
115
  break
98
116
  default:
99
- logger.fatal(
100
- `Unknown command ${bold(unparsed[0])}. Please run ${bold("'wattpm help'")} to see available commands.`
101
- )
117
+ if (requestedCommand) {
118
+ const applicationsCommands = await loadApplicationsCommands()
119
+ const applicationCommand = applicationsCommands.commands[requestedCommand]
120
+
121
+ if (applicationCommand) {
122
+ applicationCommandContext = applicationsCommands.applications[requestedCommand]
123
+ command = applicationCommand
124
+ }
125
+ }
126
+
102
127
  break
103
128
  }
104
129
 
105
- await command(logger, unparsed.slice(1))
106
- }
130
+ if (!command) {
131
+ logFatalError(
132
+ logger,
133
+ `Unknown command ${bold(requestedCommand)}. Please run ${bold(`"${getExecutableId()} help"`)} to see available commands.`
134
+ )
107
135
 
108
- export * from './lib/schema.js'
136
+ return
137
+ }
138
+
139
+ if (applicationCommandContext) {
140
+ process.chdir(applicationCommandContext.path)
141
+ return command(logger, applicationCommandContext.config, unparsed.slice(1), { colorette, parseArgs, logFatalError })
142
+ } else {
143
+ await command(logger, unparsed.slice(1))
144
+ }
145
+ }
@@ -0,0 +1,40 @@
1
+ import { parseArgs } from '@platformatic/foundation'
2
+ import { runDelegatedCommand } from './create.js'
3
+
4
+ export async function adminCommand (logger, args) {
5
+ const {
6
+ values: { latest, 'package-manager': packageManager }
7
+ } = parseArgs(
8
+ args,
9
+ {
10
+ latest: {
11
+ type: 'boolean',
12
+ short: 'l'
13
+ },
14
+ 'package-manager': {
15
+ type: 'string',
16
+ short: 'P'
17
+ }
18
+ },
19
+ false
20
+ )
21
+
22
+ return runDelegatedCommand(logger, packageManager, ['@platformatic/watt-admin' + (latest ? '@latest' : '')])
23
+ }
24
+
25
+ export const help = {
26
+ admin: {
27
+ usage: 'admin',
28
+ description: 'Start the admin interface',
29
+ options: [
30
+ {
31
+ usage: '-l --latest',
32
+ description: 'Use the latest version of @platformatic/watt-admin from'
33
+ },
34
+ {
35
+ usage: 'P, --package-manager <executable>',
36
+ description: 'Use an alternative package manager (the default is to autodetect it)'
37
+ }
38
+ ]
39
+ }
40
+ }
@@ -1,53 +1,95 @@
1
- import { ensureLoggableError } from '@platformatic/utils'
1
+ import {
2
+ ensureLoggableError,
3
+ findRuntimeConfigurationFile,
4
+ getRoot,
5
+ logFatalError,
6
+ parseArgs
7
+ } from '@platformatic/foundation'
8
+ import { create } from '@platformatic/runtime'
2
9
  import { bold } from 'colorette'
3
- import { resolve } from 'node:path'
4
- import { buildRuntime, findConfigurationFile, overrideFatal, parseArgs } from '../utils.js'
5
10
 
6
11
  export async function buildCommand (logger, args) {
7
- const { positionals } = parseArgs(args, {}, false)
8
- /* c8 ignore next */
9
- const root = resolve(process.cwd(), positionals[0] ?? '')
12
+ let runtime
13
+ let configurationFile
10
14
 
11
- const configurationFile = await findConfigurationFile(logger, root)
15
+ try {
16
+ const {
17
+ values: { config },
18
+ positionals
19
+ } = parseArgs(
20
+ args,
21
+ {
22
+ config: {
23
+ type: 'string',
24
+ short: 'c'
25
+ }
26
+ },
27
+ false
28
+ )
29
+ const root = getRoot(positionals)
12
30
 
13
- const runtime = await buildRuntime(logger, configurationFile)
14
- // Gather informations for all services before starting
15
- const { services } = await runtime.getServices()
31
+ configurationFile = await findRuntimeConfigurationFile(logger, root, config)
16
32
 
17
- for (const { id } of services) {
18
- const currentLogger = logger.child({ name: id })
19
- currentLogger.debug(`Building service ${bold(id)} ...`)
20
- overrideFatal(currentLogger)
33
+ /* c8 ignore next 3 - Hard to test */
34
+ if (!configurationFile) {
35
+ return
36
+ }
21
37
 
22
38
  try {
23
- await runtime.buildService(id)
39
+ runtime = await create(configurationFile)
40
+ await runtime.init()
41
+ /* c8 ignore next 4 - Hard to test */
24
42
  } catch (error) {
25
- if (error.code === 'PLT_BASIC_NON_ZERO_EXIT_CODE') {
26
- currentLogger.error(`Building service "${id}" has failed with exit code ${error.exitCode}.`)
27
- /* c8 ignore next 6 */
28
- } else {
29
- currentLogger.error(
30
- { err: ensureLoggableError(error) },
31
- `Building service "${id}" has throw an exception: ${error.message}`
32
- )
33
- }
43
+ logFatalError(logger, { err: ensureLoggableError(error) }, `Cannot load the runtime: ${error.message}`)
44
+ return
45
+ }
46
+
47
+ // Gather informations for all applications before starting
48
+ const { applications } = await runtime.getApplications()
49
+
50
+ for (const { id } of applications) {
51
+ const currentLogger = logger.child({ name: id })
52
+ currentLogger.debug(`Building application ${bold(id)} ...`)
34
53
 
35
- process.exit(1)
54
+ try {
55
+ await runtime.buildApplication(id)
56
+ } catch (error) {
57
+ if (error.code === 'PLT_BASIC_NON_ZERO_EXIT_CODE') {
58
+ logFatalError(currentLogger, `Building application "${id}" has failed with exit code ${error.exitCode}.`)
59
+ /* c8 ignore next 6 */
60
+ } else {
61
+ logFatalError(
62
+ currentLogger,
63
+ { err: ensureLoggableError(error) },
64
+ `Building application "${id}" has throw an exception: ${error.message}`
65
+ )
66
+ /* c8 ignore next 3 - Mistakenly reported as uncovered by C8 */
67
+ }
68
+
69
+ return
70
+ }
36
71
  }
37
- }
38
72
 
39
- logger.done('All services have been built.')
40
- await runtime.close(false, true)
73
+ logger.done('All applications have been built.')
74
+ } finally {
75
+ await runtime?.close?.(true)
76
+ }
41
77
  }
42
78
 
43
79
  export const help = {
44
80
  build: {
45
81
  usage: 'build [root]',
46
- description: 'Builds all services of an application',
82
+ description: 'Builds all applications of the project',
47
83
  args: [
48
84
  {
49
85
  name: 'root',
50
- description: 'The directory containing the application (the default is the current directory)'
86
+ description: 'The directory containing the project (the default is the current directory)'
87
+ }
88
+ ],
89
+ options: [
90
+ {
91
+ usage: '-c, --config <config>',
92
+ description: 'Name of the configuration file to use (the default is to autodetect it)'
51
93
  }
52
94
  ]
53
95
  }
@@ -0,0 +1,139 @@
1
+ import { getExecutableName, getPackageManager, parseArgs } from '@platformatic/foundation'
2
+ import { bold } from 'colorette'
3
+ import { spawn } from 'node:child_process'
4
+ import { platform } from 'node:os'
5
+
6
+ export async function runDelegatedCommand (logger, packageManager, args) {
7
+ if (!packageManager) {
8
+ packageManager = await getPackageManager(process.cwd())
9
+ }
10
+
11
+ let runner = 'npx'
12
+ if (packageManager === 'pnpm') {
13
+ runner = 'pnpx'
14
+ } else {
15
+ args.unshift('-y')
16
+ }
17
+
18
+ logger.info(`Running ${bold(runner)} ${bold(args.join(' '))} ...`)
19
+
20
+ const options = { stdio: 'inherit' }
21
+
22
+ if (platform() === 'win32') {
23
+ options.shell = true
24
+ options.windowsVerbatimArguments = true
25
+ }
26
+
27
+ const proc = spawn(runner, args, options)
28
+
29
+ proc.on('exit', code => {
30
+ process.exit(code)
31
+ })
32
+ }
33
+
34
+ export async function createCommand (logger, args) {
35
+ const {
36
+ values: {
37
+ latest,
38
+ config,
39
+ 'package-manager': packageManager,
40
+ 'skip-dependencies': skipDependencies,
41
+ module: modules
42
+ }
43
+ } = parseArgs(
44
+ args,
45
+ {
46
+ latest: {
47
+ type: 'boolean',
48
+ short: 'l'
49
+ },
50
+ // Keep following options in sync with the create command from wattpm-utils
51
+ config: {
52
+ type: 'string',
53
+ short: 'c',
54
+ default: 'watt.json'
55
+ },
56
+ 'package-manager': {
57
+ type: 'string',
58
+ short: 'P'
59
+ },
60
+ 'skip-dependencies': {
61
+ type: 'boolean',
62
+ short: 's',
63
+ default: false
64
+ },
65
+ module: {
66
+ short: 'M',
67
+ type: 'string',
68
+ multiple: true,
69
+ default: []
70
+ }
71
+ },
72
+ false,
73
+ false
74
+ )
75
+
76
+ const runArgs = ['wattpm-utils' + (latest ? '@latest' : ''), '--', 'create']
77
+
78
+ runArgs.push('-c', config)
79
+
80
+ if (packageManager) {
81
+ runArgs.push('-P', packageManager)
82
+ }
83
+
84
+ if (skipDependencies) {
85
+ runArgs.push('-s')
86
+ }
87
+
88
+ if (modules.length > 0) {
89
+ for (const m of modules) {
90
+ runArgs.push('-M', m)
91
+ }
92
+ }
93
+
94
+ return runDelegatedCommand(logger, packageManager, runArgs)
95
+ }
96
+
97
+ const createHelp = {
98
+ description () {
99
+ return `Creates a new ${getExecutableName()} project`
100
+ },
101
+ options: [
102
+ {
103
+ usage: '-l --latest',
104
+ description: 'Use the latest version of @platformatic/watt-admin from'
105
+ },
106
+ {
107
+ usage: '-c, --config <config>',
108
+ description: 'Name of the configuration file to use (the default is watt.json)'
109
+ },
110
+ {
111
+ usage: '-s, --skip-dependencies',
112
+ description: 'Do not install dependencies after creating the files'
113
+ },
114
+ {
115
+ usage: '-P, --package-manager <executable>',
116
+ description: 'Use an alternative package manager (the default is to autodetect it)'
117
+ },
118
+ {
119
+ usage: '-M, --module <name>',
120
+ description:
121
+ 'An additional module (or a comma separated list of modules) to use as application generator (it can be used multiple times)'
122
+ }
123
+ ]
124
+ }
125
+
126
+ export const help = {
127
+ init: {
128
+ usage: 'init',
129
+ ...createHelp
130
+ },
131
+ create: {
132
+ usage: 'create',
133
+ ...createHelp
134
+ },
135
+ add: {
136
+ usage: 'add',
137
+ ...createHelp
138
+ }
139
+ }