wattpm 2.72.0 → 3.0.0-alpha.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/bin/wattpm.js CHANGED
@@ -4,6 +4,6 @@ import { checkNodeVersionForServices } from '@platformatic/utils'
4
4
 
5
5
  checkNodeVersionForServices()
6
6
 
7
- // Use await import here so that we can throw a proprer error on unsupported Node.js version
7
+ // Use await import here so that we can throw a proper error on unsupported Node.js version
8
8
  const { main } = await import('../index.js')
9
9
  await main()
package/index.js CHANGED
@@ -1,3 +1,4 @@
1
+ import * as colorette from 'colorette'
1
2
  import { bold } from 'colorette'
2
3
  import { adminCommand } from './lib/commands/admin.js'
3
4
  import { buildCommand, installCommand, updateCommand } from './lib/commands/build.js'
@@ -10,8 +11,11 @@ import { logsCommand } from './lib/commands/logs.js'
10
11
  import { configCommand, envCommand, psCommand, servicesCommand } from './lib/commands/management.js'
11
12
  import { metricsCommand } from './lib/commands/metrics.js'
12
13
  import { patchConfigCommand } from './lib/commands/patch-config.js'
14
+ import { getExecutableId } from './lib/embedding.js'
13
15
  import { version } from './lib/schema.js'
14
- import { createLogger, logFatalError, parseArgs, setVerbose } from './lib/utils.js'
16
+ import { createLogger, loadServicesCommands, logFatalError, parseArgs, setVerbose } from './lib/utils.js'
17
+
18
+ export * from './lib/embedding.js'
15
19
 
16
20
  export async function main () {
17
21
  globalThis.platformatic = { executable: 'watt' }
@@ -52,7 +56,9 @@ export async function main () {
52
56
  }
53
57
 
54
58
  let command
55
- switch (unparsed[0] || 'help') {
59
+ const requestedCommand = unparsed[0] || 'help'
60
+ let serviceCommandContext
61
+ switch (requestedCommand) {
56
62
  case 'build':
57
63
  command = buildCommand
58
64
  break
@@ -119,15 +125,34 @@ export async function main () {
119
125
  command = adminCommand
120
126
  break
121
127
  default:
122
- logFatalError(
123
- logger,
124
- `Unknown command ${bold(unparsed[0])}. Please run ${bold("'wattpm help'")} to see available commands.`
125
- )
128
+ if (requestedCommand) {
129
+ const servicesCommands = await loadServicesCommands()
130
+ const serviceCommand = servicesCommands.commands[requestedCommand]
131
+
132
+ if (serviceCommand) {
133
+ serviceCommandContext = servicesCommands.services[requestedCommand]
134
+ command = serviceCommand
135
+ }
136
+ }
126
137
 
127
138
  break
128
139
  }
129
140
 
130
- await command(logger, unparsed.slice(1))
141
+ if (!command) {
142
+ logFatalError(
143
+ logger,
144
+ `Unknown command ${bold(requestedCommand)}. Please run ${bold(`"${getExecutableId()} help"`)} to see available commands.`
145
+ )
146
+
147
+ return
148
+ }
149
+
150
+ if (serviceCommandContext) {
151
+ process.chdir(serviceCommandContext.path)
152
+ return command(logger, serviceCommandContext.config, unparsed.slice(1), { colorette, parseArgs, logFatalError })
153
+ } else {
154
+ await command(logger, unparsed.slice(1))
155
+ }
131
156
  }
132
157
 
133
158
  export * from './lib/schema.js'
@@ -1,6 +1,5 @@
1
1
  import { spawn } from 'node:child_process'
2
- import { parseArgs } from '../utils.js'
3
- import { getPackageManager } from '@platformatic/utils'
2
+ import { getPackageManager, parseArgs } from '../utils.js'
4
3
 
5
4
  export function adminCommand (logger, args) {
6
5
  let {
@@ -1,4 +1,5 @@
1
- import { ensureLoggableError, getPackageManager } from '@platformatic/utils'
1
+ import { loadConfiguration } from '@platformatic/runtime'
2
+ import { ensureLoggableError } from '@platformatic/utils'
2
3
  import { bold } from 'colorette'
3
4
  import { parse } from 'dotenv'
4
5
  import { execa } from 'execa'
@@ -11,8 +12,8 @@ import {
11
12
  buildRuntime,
12
13
  findRuntimeConfigurationFile,
13
14
  getPackageArgs,
15
+ getPackageManager,
14
16
  getRoot,
15
- loadRuntimeConfigurationFile,
16
17
  logFatalError,
17
18
  parseArgs
18
19
  } from '../utils.js'
@@ -39,7 +40,7 @@ async function executeCommand (root, ...args) {
39
40
 
40
41
  export async function installDependencies (logger, root, services, production, packageManager) {
41
42
  if (typeof services === 'string') {
42
- const config = await loadRuntimeConfigurationFile(logger, services)
43
+ const config = await loadConfiguration(services, null, { validate: false })
43
44
 
44
45
  if (!config) {
45
46
  return
@@ -180,7 +181,7 @@ export async function buildCommand (logger, args) {
180
181
  )
181
182
  const root = getRoot(positionals)
182
183
 
183
- configurationFile = await findRuntimeConfigurationFile(logger, root, config, true)
184
+ configurationFile = await findRuntimeConfigurationFile(logger, root, config)
184
185
 
185
186
  /* c8 ignore next 3 - Hard to test */
186
187
  if (!configurationFile) {
@@ -251,7 +252,7 @@ export async function installCommand (logger, args) {
251
252
  )
252
253
 
253
254
  const root = getRoot(positionals)
254
- const configurationFile = await findRuntimeConfigurationFile(logger, root, config, true)
255
+ const configurationFile = await findRuntimeConfigurationFile(logger, root, config)
255
256
 
256
257
  /* c8 ignore next 3 - Hard to test */
257
258
  if (!configurationFile) {
@@ -285,14 +286,14 @@ export async function updateCommand (logger, args) {
285
286
  )
286
287
 
287
288
  const root = getRoot(positionals)
288
- const configurationFile = await findRuntimeConfigurationFile(logger, root, config, true)
289
+ const configurationFile = await findRuntimeConfigurationFile(logger, root, config)
289
290
 
290
291
  /* c8 ignore next 3 - Hard to test */
291
292
  if (!configurationFile) {
292
293
  return
293
294
  }
294
295
 
295
- const configuration = await loadRuntimeConfigurationFile(logger, configurationFile)
296
+ const configuration = await loadConfiguration(configurationFile)
296
297
 
297
298
  if (!configuration) {
298
299
  return
@@ -373,7 +374,7 @@ export const help = {
373
374
  },
374
375
  update: {
375
376
  usage: 'update [root]',
376
- description: 'Updates all the Platformatic packages to the latest available version.',
377
+ description: 'Updates all the Platformatic packages to the latest available version',
377
378
  args: [
378
379
  {
379
380
  name: 'root',
@@ -1,7 +1,7 @@
1
- import { getPackageManager } from '@platformatic/utils'
2
1
  import { createApplication, getUsername, getVersion, say } from 'create-platformatic'
3
2
  import { resolve } from 'node:path'
4
- import { parseArgs } from '../utils.js'
3
+ import { getExecutableName } from '../embedding.js'
4
+ import { getPackageManager, parseArgs } from '../utils.js'
5
5
  import { installDependencies } from './build.js'
6
6
 
7
7
  export async function createCommand (logger, args) {
@@ -46,15 +46,16 @@ export async function createCommand (logger, args) {
46
46
  )
47
47
 
48
48
  if (!packageManager) {
49
- packageManager = getPackageManager(process.cwd(), null)
49
+ packageManager = getPackageManager(process.cwd())
50
50
  }
51
51
 
52
52
  const username = await getUsername()
53
53
  const version = await getVersion()
54
54
 
55
- /* c8 ignore next 2 - Ignoring else branches */
55
+ /* c8 ignore next 3 - Ignoring else branches */
56
+ const executableName = getExecutableName()
56
57
  const greeting = username ? `Hello ${username},` : 'Hello,'
57
- await say(`${greeting} welcome to ${version ? `Watt ${version}!` : 'Watt!'}`)
58
+ await say(`${greeting} welcome to ${version ? `${executableName} ${version}!` : `${executableName}!`}`)
58
59
 
59
60
  await createApplication(
60
61
  logger,
@@ -79,7 +80,9 @@ export async function createCommand (logger, args) {
79
80
  }
80
81
 
81
82
  const createHelp = {
82
- description: 'Creates a new Watt project',
83
+ description () {
84
+ return `Creates a new ${getExecutableName()} project`
85
+ },
83
86
  options: [
84
87
  {
85
88
  usage: '-c, --config <config>',
@@ -1,17 +1,10 @@
1
1
  import { RuntimeApiClient } from '@platformatic/control'
2
- import { startCommand as pltStartCommand } from '@platformatic/runtime'
2
+ import { create } from '@platformatic/runtime'
3
3
  import { ensureLoggableError, FileWatcher } from '@platformatic/utils'
4
4
  import { bold } from 'colorette'
5
5
  import { spawn } from 'node:child_process'
6
6
  import { on } from 'node:events'
7
- import {
8
- findRuntimeConfigurationFile,
9
- getMatchingRuntime,
10
- getRoot,
11
- handleRuntimeError,
12
- logFatalError,
13
- parseArgs
14
- } from '../utils.js'
7
+ import { findRuntimeConfigurationFile, getMatchingRuntime, getRoot, logFatalError, parseArgs } from '../utils.js'
15
8
 
16
9
  export async function devCommand (logger, args) {
17
10
  const {
@@ -29,33 +22,24 @@ export async function devCommand (logger, args) {
29
22
  )
30
23
  const root = getRoot(positionals)
31
24
 
32
- const configurationFile = await findRuntimeConfigurationFile(logger, root, config, true)
25
+ const configurationFile = await findRuntimeConfigurationFile(logger, root, config)
33
26
 
34
27
  /* c8 ignore next 3 - Hard to test */
35
28
  if (!configurationFile) {
36
29
  return
37
30
  }
38
31
 
39
- let runtime
40
- try {
41
- runtime = await pltStartCommand(['-c', configurationFile], true, true)
42
- } catch (error) {
43
- if (await handleRuntimeError(logger, configurationFile, error)) {
44
- return
45
- /* c8 ignore next 4 - Hard to test */
46
- }
47
-
48
- throw error
49
- }
32
+ let runtime = await create(root, configurationFile, { start: true })
50
33
 
51
34
  // Add a watcher on the configurationFile so that we can eventually restart the runtime
52
35
  const watcher = new FileWatcher({ path: configurationFile })
53
36
  watcher.startWatching()
37
+
54
38
  // eslint-disable-next-line no-unused-vars
55
39
  for await (const _ of on(watcher, 'update')) {
56
40
  runtime.logger.info('The configuration file has changed, reloading the application ...')
57
41
  await runtime.close()
58
- runtime = await pltStartCommand(['-c', configurationFile], true, true)
42
+ runtime = await create(root, configurationFile, { start: true })
59
43
  }
60
44
  /* c8 ignore next - Mistakenly reported as uncovered by C8 */
61
45
  }
@@ -80,28 +64,14 @@ export async function startCommand (logger, args) {
80
64
  )
81
65
 
82
66
  const root = getRoot(positionals)
83
- const configurationFile = await findRuntimeConfigurationFile(logger, root, config, true)
67
+ const configurationFile = await findRuntimeConfigurationFile(logger, root, config)
84
68
 
85
69
  /* c8 ignore next 3 - Hard to test */
86
70
  if (!configurationFile) {
87
71
  return
88
72
  }
89
73
 
90
- const cmd = ['--production', '-c', configurationFile]
91
- if (inspect) {
92
- cmd.push('--inspect')
93
- }
94
-
95
- try {
96
- await pltStartCommand(cmd, true)
97
- } catch (error) {
98
- if (await handleRuntimeError(logger, configurationFile, error)) {
99
- return
100
- /* c8 ignore next 4 - Hard to test */
101
- }
102
-
103
- throw error
104
- }
74
+ await create(root, configurationFile, { start: true, production: true, inspect })
105
75
  }
106
76
 
107
77
  export async function stopCommand (logger, args) {
@@ -1,24 +1,20 @@
1
+ import { loadConfiguration } from '@platformatic/runtime'
1
2
  import {
2
- ConfigManager,
3
+ detectApplicationType,
4
+ ensureLoggableError,
5
+ findConfigurationFile,
3
6
  loadConfigurationFile as loadRawConfigurationFile,
4
7
  saveConfigurationFile
5
- } from '@platformatic/config'
6
- import { detectApplicationType, ensureLoggableError } from '@platformatic/utils'
8
+ } from '@platformatic/utils'
7
9
  import { bold } from 'colorette'
8
10
  import { parse } from 'dotenv'
9
11
  import { execa } from 'execa'
10
12
  import { existsSync } from 'node:fs'
11
13
  import { readFile, writeFile } from 'node:fs/promises'
12
14
  import { basename, dirname, isAbsolute, join, relative, resolve } from 'node:path'
15
+ import { getExecutableId } from '../embedding.js'
13
16
  import { version } from '../schema.js'
14
- import {
15
- findRuntimeConfigurationFile,
16
- getRoot,
17
- loadRuntimeConfigurationFile,
18
- logFatalError,
19
- parseArgs,
20
- serviceToEnvVariable
21
- } from '../utils.js'
17
+ import { findRuntimeConfigurationFile, getRoot, logFatalError, parseArgs, serviceToEnvVariable } from '../utils.js'
22
18
  import { installDependencies } from './build.js'
23
19
 
24
20
  const originCandidates = ['origin', 'upstream']
@@ -75,7 +71,7 @@ export async function appendEnvVariable (envFile, key, value) {
75
71
  }
76
72
 
77
73
  async function fixConfiguration (logger, root, configOption, skipDependencies, packageManager) {
78
- const configurationFile = await findRuntimeConfigurationFile(logger, root, configOption, true)
74
+ const configurationFile = await findRuntimeConfigurationFile(logger, root, configOption, true, true, false)
79
75
 
80
76
  /* c8 ignore next 3 - Hard to test */
81
77
  if (!configurationFile) {
@@ -99,7 +95,7 @@ async function fixConfiguration (logger, root, configOption, skipDependencies, p
99
95
  const path = resolve(root)
100
96
  services = [{ path, id: basename(path) }]
101
97
  } else {
102
- const config = await loadRuntimeConfigurationFile(logger, configurationFile)
98
+ const config = await loadConfiguration(configurationFile)
103
99
 
104
100
  /* c8 ignore next 3 - Hard to test */
105
101
  if (!config) {
@@ -111,7 +107,7 @@ async function fixConfiguration (logger, root, configOption, skipDependencies, p
111
107
 
112
108
  // For each service, if there is no watt.json, create one and fix package dependencies
113
109
  for (const { path } of services) {
114
- const wattConfiguration = await ConfigManager.findConfigFile(path, 'application')
110
+ const wattConfiguration = await findConfigurationFile(path, 'application')
115
111
 
116
112
  const appType = await parseLocalFolder(resolve(root, path))
117
113
 
@@ -156,7 +152,7 @@ async function fixConfiguration (logger, root, configOption, skipDependencies, p
156
152
  }
157
153
 
158
154
  async function importService (logger, configurationFile, id, path, url, branch) {
159
- const config = await loadRuntimeConfigurationFile(logger, configurationFile)
155
+ const config = await loadConfiguration(configurationFile)
160
156
 
161
157
  /* c8 ignore next 3 - Hard to test */
162
158
  if (!config) {
@@ -271,7 +267,7 @@ async function importLocal (logger, root, configurationFile, path, overridenId)
271
267
  }
272
268
 
273
269
  // Check if there is any configuration file we recognize. If so, don't do anything
274
- const wattConfiguration = await ConfigManager.findConfigFile(path, 'application')
270
+ const wattConfiguration = await findConfigurationFile(path, 'application')
275
271
 
276
272
  if (wattConfiguration) {
277
273
  /* c8 ignore next */
@@ -305,7 +301,7 @@ export async function resolveServices (
305
301
  skipDependencies,
306
302
  packageManager
307
303
  ) {
308
- const config = await loadRuntimeConfigurationFile(logger, configurationFile)
304
+ const config = await loadConfiguration(configurationFile)
309
305
 
310
306
  if (!config) {
311
307
  return
@@ -465,7 +461,7 @@ export async function importCommand (logger, args) {
465
461
  rawUrl = positionals[1]
466
462
  }
467
463
 
468
- const configurationFile = await findRuntimeConfigurationFile(logger, root, config, true)
464
+ const configurationFile = await findRuntimeConfigurationFile(logger, root, config)
469
465
 
470
466
  /* c8 ignore next 3 - Hard to test */
471
467
  if (!configurationFile) {
@@ -520,7 +516,7 @@ export async function resolveCommand (logger, args) {
520
516
  )
521
517
 
522
518
  const root = getRoot(positionals)
523
- const configurationFile = await findRuntimeConfigurationFile(logger, root, config, true)
519
+ const configurationFile = await findRuntimeConfigurationFile(logger, root, config)
524
520
 
525
521
  /* c8 ignore next 3 - Hard to test */
526
522
  if (!configurationFile) {
@@ -614,11 +610,12 @@ export const help = {
614
610
  description: 'Use an alternative package manager (the default is to autodetect it)'
615
611
  }
616
612
  ],
617
- footer: `
618
- wattpm resolve command resolves runtime services that have the \`url\` in their configuration.
613
+ footer () {
614
+ return `
615
+ ${getExecutableId()} resolve command resolves runtime services that have the \`url\` in their configuration.
619
616
  To change the directory where a service is cloned, you can set the \`path\` property in the service configuration.
620
617
 
621
- After cloning the service, the resolve command will set the relative path to the service in the wattpm configuration file.
618
+ After cloning the service, the resolve command will set the relative path to the service in the Platformatic configuration file.
622
619
 
623
620
  Example of the runtime \`watt.json\` configuration file:
624
621
 
@@ -669,5 +666,6 @@ You can find more details about the configuration format here:
669
666
  * [Platformatic DB Configuration](https://docs.platformatic.dev/docs/db/configuration)
670
667
  * [Platformatic Service Configuration](https://docs.platformatic.dev/docs/service/configuration)
671
668
  `
669
+ }
672
670
  }
673
671
  }
@@ -1,6 +1,11 @@
1
- import { bold, isColorSupported } from 'colorette'
1
+ import { bold } from 'colorette'
2
+ import { getExecutableId, getExecutableName } from '../embedding.js'
2
3
  import { logo } from '../logo.js'
3
- import { logFatalError } from '../utils.js'
4
+ import { loadServicesCommands, logFatalError } from '../utils.js'
5
+
6
+ function sanitizeHelp (raw) {
7
+ return (typeof raw === 'function' ? raw() : raw).trim()
8
+ }
4
9
 
5
10
  async function loadCommands () {
6
11
  const commands = {}
@@ -26,36 +31,61 @@ async function loadCommands () {
26
31
  return commands
27
32
  }
28
33
 
29
- async function showGeneralHelp () {
34
+ export async function showGeneralHelp (logger) {
35
+ if (typeof logger !== 'function') {
36
+ logger = console.log
37
+ }
38
+
39
+ const executableId = getExecutableId()
30
40
  const commands = Object.values(await loadCommands())
41
+ const servicesCommands = Object.values((await loadServicesCommands()).help)
42
+
31
43
  const options = [
32
- { usage: '-V, --version', description: 'Show wattpm version' },
44
+ { usage: '-V, --version', description: `Show ${executableId} version` },
33
45
  { usage: '-v, --verbose', description: 'Show more information' },
34
46
  { usage: '--help', description: 'Show this help' }
35
47
  ]
36
48
 
37
- console.log('\nUsage: wattpm [options] [command]\n')
49
+ logger(logo())
50
+ logger(`\nUsage: ${executableId} [options] [command]\n`)
38
51
 
39
52
  // Compute the maximum length of options or commands
40
- const maximumLength = Math.max(...options.map(c => c.usage.length), ...commands.map(c => c.usage.length)) + 5
53
+ const maximumLength =
54
+ Math.max(
55
+ ...options.map(c => c.usage.length),
56
+ ...commands.map(c => c.usage.length),
57
+ ...servicesCommands.map(c => c.usage.length)
58
+ ) + 5
41
59
 
42
60
  // Print all options
43
- console.log('Options:\n')
61
+ logger('Options:\n')
44
62
  for (const { usage, description } of options) {
45
- console.log(` ${usage.padEnd(maximumLength, ' ')} ${description}`)
63
+ logger(` ${usage.padEnd(maximumLength, ' ')} ${sanitizeHelp(description)}`)
46
64
  }
47
- console.log('')
65
+ logger('')
48
66
 
49
67
  // Print all commands
50
- console.log('Commands:\n')
68
+ logger('Commands:\n')
51
69
  for (const { usage, description } of commands) {
52
- console.log(` ${usage.padEnd(maximumLength, ' ')} ${description}`)
70
+ logger(` ${usage.padEnd(maximumLength, ' ')} ${sanitizeHelp(description)}`)
71
+ }
72
+ logger('')
73
+
74
+ if (servicesCommands.length) {
75
+ logger('Services Commands:\n')
76
+ for (const { usage, description } of servicesCommands) {
77
+ logger(` ${usage.padEnd(maximumLength, ' ')} ${sanitizeHelp(description)})`)
78
+ }
79
+ logger('')
53
80
  }
54
- console.log('')
55
81
  }
56
82
 
57
- function showHelp (command) {
58
- console.log(`\nUsage: wattpm ${command.usage}\n\n${command.description}.\n`)
83
+ export function showHelp (command, logger) {
84
+ if (typeof logger !== 'function') {
85
+ logger = console.log
86
+ }
87
+
88
+ logger(`\nUsage: ${getExecutableId()} ${sanitizeHelp(command.usage)}\n\n${sanitizeHelp(command.description)}.\n`)
59
89
 
60
90
  let { options, args } = command
61
91
  options ??= []
@@ -66,24 +96,24 @@ function showHelp (command) {
66
96
 
67
97
  // Print all options
68
98
  if (options.length) {
69
- console.log('Options:\n')
99
+ logger('Options:\n')
70
100
  for (const { usage, description } of options) {
71
- console.log(` ${usage.padEnd(maximumLength, ' ')} ${description}`)
101
+ logger(` ${usage.padEnd(maximumLength, ' ')} ${sanitizeHelp(description)}`)
72
102
  }
73
- console.log('')
103
+ logger('')
74
104
  }
75
105
 
76
106
  // Print all arguments
77
107
  if (args.length) {
78
- console.log('Arguments:\n')
108
+ logger('Arguments:\n')
79
109
  for (const { name, description } of args) {
80
- console.log(` ${name.padEnd(maximumLength, ' ')} ${description}`)
110
+ logger(` ${name.padEnd(maximumLength, ' ')} ${sanitizeHelp(description)}`)
81
111
  }
82
- console.log('')
112
+ logger('')
83
113
  }
84
114
 
85
115
  if (command.footer) {
86
- console.log(command.footer.trim() + '\n')
116
+ logger(sanitizeHelp(command.footer) + '\n')
87
117
  }
88
118
  }
89
119
 
@@ -91,19 +121,21 @@ export async function helpCommand (logger, args) {
91
121
  const command = args?.[0]
92
122
 
93
123
  if (!command) {
94
- /* c8 ignore next 3 - Hard to test */
95
- if (isColorSupported && process.stdout.isTTY) {
96
- console.log(logo)
97
- }
98
-
99
124
  return showGeneralHelp()
100
125
  }
101
126
 
102
127
  const commands = await loadCommands()
103
128
  if (!commands[command]) {
129
+ const servicesCommands = (await loadServicesCommands()).help
130
+
131
+ if (servicesCommands[command]) {
132
+ // If the command is a service command, we show the help for that command
133
+ return showHelp(servicesCommands[command])
134
+ }
135
+
104
136
  return logFatalError(
105
137
  logger,
106
- `Unknown command ${bold(command)}. Please run ${bold("'wattpm help'")} to see available commands.`
138
+ `Unknown command ${bold(command)}. Please run ${bold(`"${getExecutableId()} help"`)} to see available commands.`
107
139
  )
108
140
  }
109
141
 
@@ -111,6 +143,16 @@ export async function helpCommand (logger, args) {
111
143
  }
112
144
 
113
145
  export const help = {
114
- help: { usage: 'help [command]', description: 'Show help about Watt or one of its commands' },
115
- version: { usage: 'version', description: 'Show current Watt version' }
146
+ help: {
147
+ usage: 'help [command]',
148
+ description () {
149
+ return `Show help about ${getExecutableName()} or one of its commands`
150
+ }
151
+ },
152
+ version: {
153
+ usage: 'version',
154
+ description () {
155
+ return `Show current ${getExecutableName()} version`
156
+ }
157
+ }
116
158
  }
@@ -114,6 +114,7 @@ export async function injectCommand (logger, args) {
114
114
  if (response.statusCode === 500) {
115
115
  const json = JSON.parse(responseBody)
116
116
 
117
+ /* c8 ignore next - else */
117
118
  if (json?.code === 'PLT_RUNTIME_SERVICE_NOT_FOUND' || json?.code === 'PLT_RUNTIME_SERVICE_WORKER_NOT_FOUND') {
118
119
  const error = new Error('Cannot find a service.')
119
120
  error.code = 'PLT_CTR_SERVICE_NOT_FOUND'
@@ -1,25 +1,21 @@
1
1
  import {
2
- ConfigManager,
2
+ ensureLoggableError,
3
+ extractModuleFromSchemaUrl,
3
4
  getParser,
4
5
  getStringifier,
6
+ listRecognizedConfigurationFiles,
5
7
  loadConfigurationFile,
8
+ loadModule,
9
+ safeRemove,
6
10
  saveConfigurationFile
7
- } from '@platformatic/config'
8
- import { ensureLoggableError, loadModule, safeRemove } from '@platformatic/utils'
11
+ } from '@platformatic/utils'
9
12
  import jsonPatch from 'fast-json-patch'
10
13
  import { existsSync } from 'node:fs'
11
14
  import { mkdtemp, readFile, writeFile } from 'node:fs/promises'
12
15
  import { createRequire } from 'node:module'
13
16
  import { tmpdir } from 'node:os'
14
17
  import { resolve } from 'node:path'
15
- import {
16
- buildRuntime,
17
- findRuntimeConfigurationFile,
18
- getRoot,
19
- loadConfigurationFileAsConfig,
20
- logFatalError,
21
- parseArgs
22
- } from '../utils.js'
18
+ import { buildRuntime, findRuntimeConfigurationFile, getRoot, logFatalError, parseArgs } from '../utils.js'
23
19
 
24
20
  async function patchFile (path, patch) {
25
21
  let config = getParser(path)(await readFile(path, 'utf-8'))
@@ -31,14 +27,15 @@ export async function patchConfig (logger, configurationFile, patchPath) {
31
27
  let runtime
32
28
  try {
33
29
  // Determine if the configuration file is for a service or a runtime
34
- const config = await loadConfigurationFileAsConfig(logger, configurationFile)
30
+ const config = await loadConfigurationFile(configurationFile)
35
31
 
36
32
  /* c8 ignore next 3 - Hard to test */
37
33
  if (!config) {
38
34
  return false
39
35
  }
40
36
 
41
- const isService = config.configType !== 'runtime'
37
+ const mod = extractModuleFromSchemaUrl(config)
38
+ const isService = mod.module !== '@platformatic/runtime'
42
39
 
43
40
  const patchFunction = await loadModule(createRequire(configurationFile), patchPath)
44
41
 
@@ -55,8 +52,9 @@ export async function patchConfig (logger, configurationFile, patchPath) {
55
52
  }
56
53
 
57
54
  // Prepare the structure for original and modified configurations files
55
+ const parser = getParser(configurationFile)
58
56
  const original = {
59
- runtime: getParser(configurationFile)(await readFile(configurationFile, 'utf-8')),
57
+ runtime: parser(await readFile(configurationFile, 'utf-8')),
60
58
  services: {}
61
59
  }
62
60
 
@@ -70,7 +68,7 @@ export async function patchConfig (logger, configurationFile, patchPath) {
70
68
  // Load configuration for all services
71
69
  for (const service of loaded.runtime.services) {
72
70
  if (!service.config) {
73
- const candidate = ConfigManager.listConfigFiles().find(f => existsSync(resolve(service.path, f)))
71
+ const candidate = listRecognizedConfigurationFiles().find(f => existsSync(resolve(service.path, f)))
74
72
 
75
73
  if (candidate) {
76
74
  service.config = resolve(service.path, candidate)
@@ -161,7 +159,7 @@ export async function patchConfigCommand (logger, args) {
161
159
  patch = positionals[1]
162
160
  }
163
161
 
164
- const configurationFile = await findRuntimeConfigurationFile(logger, root, config, true)
162
+ const configurationFile = await findRuntimeConfigurationFile(logger, root, config)
165
163
 
166
164
  /* c8 ignore next 3 */
167
165
  if (!configurationFile) {
@@ -186,7 +184,7 @@ export async function patchConfigCommand (logger, args) {
186
184
  export const help = {
187
185
  'patch-config': {
188
186
  usage: 'patch-config [root] [patch]',
189
- description: 'Applies a patch file to the runtime and services configurations.',
187
+ description: 'Applies a patch file to the runtime and services configurations',
190
188
  args: [
191
189
  {
192
190
  name: 'root',
@@ -0,0 +1,15 @@
1
+ let executableId = process.env.WATTPM_EXECUTABLE_ID ?? 'wattpm'
2
+ let executableName = process.env.WATTPM_EXECUTABLE_NAME ?? 'Watt'
3
+
4
+ export function getExecutableId () {
5
+ return executableId
6
+ }
7
+
8
+ export function getExecutableName () {
9
+ return executableName
10
+ }
11
+
12
+ export function setExecutableParameters (id, name) {
13
+ executableId = id
14
+ executableName = name
15
+ }
package/lib/logo.js CHANGED
@@ -1,6 +1,9 @@
1
- import { bold, green } from 'colorette'
1
+ import { bold, green, isColorSupported } from 'colorette'
2
+ import { getExecutableName } from './embedding.js'
2
3
 
3
- const str = `
4
+ export function logo () {
5
+ const executableName = isColorSupported ? bold(getExecutableName()) : getExecutableName()
6
+ const str = `
4
7
 
5
8
  //////
6
9
  /////////////
@@ -30,7 +33,9 @@ const str = `
30
33
  &&&&&&&&&&&&&
31
34
  &&&&&&
32
35
 
33
- Welcome to ${bold('Watt')}!
36
+ Welcome to ${executableName}!
34
37
  `
35
38
 
36
- export const logo = str.replace(/\//g, s => green(s))
39
+ /* c8 ignore next - else */
40
+ return isColorSupported ? str.replace(/\//g, s => green(s)) : str
41
+ }
package/lib/packages.js CHANGED
@@ -5,7 +5,6 @@ export const packages = [
5
5
  '@platformatic/client',
6
6
  '@platformatic/client-cli',
7
7
  '@platformatic/composer',
8
- '@platformatic/config',
9
8
  '@platformatic/control',
10
9
  '@platformatic/db',
11
10
  '@platformatic/db-authorization',
package/lib/utils.js CHANGED
@@ -1,28 +1,23 @@
1
- import {
2
- findConfigurationFile,
3
- loadConfig,
4
- loadConfigurationFile,
5
- saveConfigurationFile,
6
- Store
7
- } from '@platformatic/config'
8
1
  import { errors } from '@platformatic/control'
9
- import { platformaticRuntime, buildRuntime as pltBuildRuntime, wrapConfigInRuntimeConfig } from '@platformatic/runtime'
2
+ import { create, loadConfiguration } from '@platformatic/runtime'
10
3
  import {
4
+ abstractLogger,
11
5
  detectApplicationType,
12
- ensureLoggableError,
13
- escapeRegexp,
6
+ findConfigurationFileRecursive,
14
7
  hasJavascriptFiles,
15
- kFailedImport
8
+ loadConfigurationModule,
9
+ saveConfigurationFile,
10
+ loadConfiguration as utilsLoadConfiguration
16
11
  } from '@platformatic/utils'
17
12
  import { bgGreen, black, bold } from 'colorette'
13
+ import { existsSync } from 'node:fs'
18
14
  import { resolve } from 'node:path'
19
15
  import { parseArgs as nodeParseArgs } from 'node:util'
20
16
  import { pino } from 'pino'
21
17
  import pinoPretty from 'pino-pretty'
18
+ import { getExecutableName } from './embedding.js'
22
19
  import { version } from './schema.js'
23
20
 
24
- const autoGeneratedSchemaMatcher = `^${escapeRegexp('https://schemas.platformatic.dev/')}@/(.+)\\?autogenerated=true$`
25
-
26
21
  export let verbose = false
27
22
 
28
23
  export function setVerbose (value) {
@@ -94,6 +89,18 @@ export function parseArgs (args, options, stopAtFirstPositional = true) {
94
89
  }
95
90
  }
96
91
 
92
+ export function getPackageManager (root, defaultManager = 'npm') {
93
+ if (existsSync(resolve(root, 'pnpm-lock.yaml'))) {
94
+ return 'pnpm'
95
+ }
96
+
97
+ if (existsSync(resolve(root, 'yarn.lock'))) {
98
+ return 'yarn'
99
+ }
100
+
101
+ return defaultManager
102
+ }
103
+
97
104
  export function getPackageArgs (packageManager, production) {
98
105
  const args = ['install']
99
106
  if (production) {
@@ -155,87 +162,48 @@ export function serviceToEnvVariable (service) {
155
162
  return `PLT_SERVICE_${service.toUpperCase().replaceAll(/[^A-Z0-9_]/g, '_')}_PATH`
156
163
  }
157
164
 
158
- export async function findRuntimeConfigurationFile (logger, root, configurationFile, fallback = false) {
159
- let configFile = await findConfigurationFile(root, configurationFile, 'runtime')
165
+ export async function findRuntimeConfigurationFile (
166
+ logger,
167
+ root,
168
+ configurationFile,
169
+ fallback = true,
170
+ throwOnError = true,
171
+ verifyPackages = true
172
+ ) {
173
+ let configFile = await findConfigurationFileRecursive(root, configurationFile, '@platformatic/runtime')
160
174
 
161
175
  // If a runtime was not found, search for service file that we wrap in a runtime
162
176
  if (!configFile && !configurationFile) {
163
- configFile = await findConfigurationFile(root, configurationFile)
177
+ configFile = await findConfigurationFileRecursive(root, configurationFile)
164
178
  }
165
179
 
166
180
  if (!configFile) {
167
181
  if (fallback) {
168
- configurationFile = await fallbackToTemporaryConfigFile(logger, root)
182
+ configurationFile = await fallbackToTemporaryConfigFile(logger, root, verifyPackages)
169
183
 
170
- if (configurationFile) {
184
+ if (configurationFile || configurationFile === false) {
171
185
  return configurationFile
172
186
  }
173
187
  }
174
188
 
175
- return logFatalError(
176
- logger,
177
- `Cannot find a supported Watt configuration file (like ${bold('watt.json')}, a ${bold('wattpm.json')} or a ${bold(
178
- 'platformatic.json'
179
- )}) in ${bold(resolve(root))}.`
180
- )
189
+ if (throwOnError) {
190
+ return logFatalError(
191
+ logger,
192
+ `Cannot find a supported ${getExecutableName()} configuration file (like ${bold('watt.json')}, a ${bold('wattpm.json')} or a ${bold(
193
+ 'platformatic.json'
194
+ )}) in ${bold(resolve(root))}.`
195
+ )
196
+ }
181
197
  }
182
198
 
183
199
  return configFile
184
200
  }
185
201
 
186
202
  export async function loadConfigurationFileAsConfig (logger, configurationFile) {
187
- const store = new Store()
188
- store.add(platformaticRuntime)
189
-
190
- const args = ['-c', configurationFile]
191
- const options = { allowInvalid: true, transformOnValidationErrors: true }
192
-
193
- try {
194
- return await loadConfig(
195
- {},
196
- args,
197
- store,
198
- {
199
- /* c8 ignore next 3 */
200
- onMissingEnv () {
201
- return ''
202
- }
203
- },
204
- true,
205
- logger,
206
- options
207
- )
208
- } catch (error) {
209
- if (await handleRuntimeError(logger, configurationFile, error)) {
210
- return false
211
- /* c8 ignore next 4 - Hard to test */
212
- }
213
-
214
- throw error
215
- }
203
+ return loadConfiguration(configurationFile, null, { validate: false })
216
204
  }
217
205
 
218
- export async function loadRuntimeConfigurationFile (logger, configurationFile) {
219
- const config = await loadConfigurationFileAsConfig(logger, configurationFile)
220
-
221
- if (!config) {
222
- return false
223
- }
224
-
225
- const args = ['-c', configurationFile]
226
- const options = { allowInvalid: true, transformOnValidationErrors: true }
227
-
228
- if (config.configType !== 'runtime') {
229
- const configManager = await wrapConfigInRuntimeConfig(config, args, options)
230
- config.configManager = configManager
231
- }
232
-
233
- config.configManager.args = config.args
234
-
235
- return config.configManager.current
236
- }
237
-
238
- export async function fallbackToTemporaryConfigFile (logger, root) {
206
+ export async function fallbackToTemporaryConfigFile (logger, root, verifyPackages) {
239
207
  if (await hasJavascriptFiles(root)) {
240
208
  const { name, label } = await detectApplicationType(root)
241
209
 
@@ -251,63 +219,79 @@ export async function fallbackToTemporaryConfigFile (logger, root) {
251
219
  const configurationFile = resolve(root, 'watt.json')
252
220
  await saveConfigurationFile(configurationFile, { $schema: schema })
253
221
 
222
+ // Try to load the module, if it is missing, we will throw an error
223
+ if (verifyPackages) {
224
+ try {
225
+ await loadConfigurationModule(root, { $schema: schema })
226
+ } catch (error) {
227
+ logFatalError(logger, `Cannot load module ${bold(name)}. Please add it to your package.json and try again.`)
228
+ return false
229
+ }
230
+ }
231
+
254
232
  return configurationFile
255
233
  }
256
234
  }
257
235
 
258
236
  export async function buildRuntime (logger, configurationFile) {
259
- const store = new Store()
260
- store.add(platformaticRuntime)
261
-
262
- const args = ['-c', configurationFile]
263
-
264
- let config
265
-
237
+ let runtime
266
238
  try {
267
- config = await loadConfig({}, args, store, {}, true, logger)
239
+ runtime = await create(configurationFile)
240
+ await runtime.init()
241
+ /* c8 ignore next 3 - Hard to test */
268
242
  } catch (error) {
269
- if (await handleRuntimeError(logger, configurationFile, error)) {
270
- return false
271
- /* c8 ignore next 4 - Hard to test */
272
- }
243
+ await runtime.close()
273
244
 
274
- throw error
245
+ return false
275
246
  }
276
247
 
277
- if (config.configType !== 'runtime') {
278
- const configManager = await wrapConfigInRuntimeConfig(config, args)
279
- config.configManager = configManager
280
- }
248
+ return runtime
249
+ }
281
250
 
282
- config.configManager.args = config.args
251
+ export async function loadServicesCommands () {
252
+ const services = {}
253
+ const commands = {}
254
+ const help = {}
283
255
 
284
- let runtime
256
+ let config
285
257
  try {
286
- runtime = await pltBuildRuntime(config.configManager)
258
+ const file = await findRuntimeConfigurationFile(abstractLogger, process.cwd(), null, false, false)
259
+
287
260
  /* c8 ignore next 3 - Hard to test */
288
- } catch (error) {
289
- logFatalError(logger, { err: ensureLoggableError(error) }, 'Error creating the runtime')
290
- }
261
+ if (!file) {
262
+ throw new Error('No runtime configuration file found.')
263
+ }
291
264
 
292
- return runtime
293
- }
265
+ config = await loadConfiguration(file)
294
266
 
295
- export async function handleRuntimeError (logger, configurationFile, error) {
296
- const failedModule = error[kFailedImport]
267
+ /* c8 ignore next 3 - Hard to test */
268
+ if (!config) {
269
+ throw new Error('No runtime configuration file found.')
270
+ }
271
+ } catch {
272
+ return { services, commands, help }
273
+ }
297
274
 
298
- if (failedModule) {
299
- const config = await loadConfigurationFile(configurationFile)
275
+ for (const service of config.services) {
276
+ try {
277
+ const serviceConfig = await utilsLoadConfiguration(service.config)
278
+ const pkg = await loadConfigurationModule(service.path, serviceConfig)
300
279
 
301
- if (config.$schema.match(new RegExp(autoGeneratedSchemaMatcher.replace('@', escapeRegexp(failedModule))))) {
302
- logFatalError(
303
- logger,
304
- `Cannot load module ${bold(failedModule)}. Please add it to your package.json and try again.`
305
- )
280
+ if (pkg.createCommands) {
281
+ const definition = await pkg.createCommands(service.id)
282
+ process._rawDebug(definition)
283
+ for (const command of Object.keys(definition.commands)) {
284
+ services[command] = service
285
+ }
306
286
 
307
- return true
308
- /* c8 ignore next 4 - Hard to test */
287
+ Object.assign(commands, definition.commands)
288
+ Object.assign(help, definition.help)
289
+ }
290
+ /* c8 ignore next 3 - Hard to test */
291
+ } catch {
292
+ // No-op, ignore the service
309
293
  }
310
294
  }
311
295
 
312
- return false
296
+ return { services, commands, help }
313
297
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wattpm",
3
- "version": "2.72.0",
3
+ "version": "3.0.0-alpha.1",
4
4
  "description": "The Node.js Application Server",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -36,11 +36,10 @@
36
36
  "semver": "^7.7.0",
37
37
  "split2": "^4.2.0",
38
38
  "table": "^6.8.2",
39
- "@platformatic/config": "2.72.0",
40
- "@platformatic/control": "2.72.0",
41
- "@platformatic/runtime": "2.72.0",
42
- "@platformatic/utils": "2.72.0",
43
- "create-platformatic": "2.72.0"
39
+ "@platformatic/control": "3.0.0-alpha.1",
40
+ "@platformatic/runtime": "3.0.0-alpha.1",
41
+ "@platformatic/utils": "3.0.0-alpha.1",
42
+ "create-platformatic": "3.0.0-alpha.1"
44
43
  },
45
44
  "devDependencies": {
46
45
  "borp": "^0.20.0",
@@ -50,7 +49,7 @@
50
49
  "neostandard": "^0.12.0",
51
50
  "typescript": "^5.5.4",
52
51
  "undici": "^7.0.0",
53
- "@platformatic/node": "2.72.0"
52
+ "@platformatic/node": "3.0.0-alpha.1"
54
53
  },
55
54
  "scripts": {
56
55
  "test": "npm run lint && borp --concurrency=1 --timeout=1200000",
package/schema.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
- "$id": "https://schemas.platformatic.dev/wattpm/2.72.0.json",
2
+ "$id": "https://schemas.platformatic.dev/wattpm/3.0.0-alpha.1.json",
3
3
  "$schema": "http://json-schema.org/draft-07/schema#",
4
+ "title": "Platformatic Runtime Config",
4
5
  "type": "object",
5
6
  "properties": {
6
7
  "$schema": {