wattpm 2.66.0 → 2.67.0-alpha.0

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/index.js CHANGED
@@ -11,7 +11,7 @@ import { configCommand, envCommand, psCommand, servicesCommand } from './lib/com
11
11
  import { metricsCommand } from './lib/commands/metrics.js'
12
12
  import { patchConfigCommand } from './lib/commands/patch-config.js'
13
13
  import { version } from './lib/schema.js'
14
- import { createLogger, parseArgs, setVerbose } from './lib/utils.js'
14
+ import { createLogger, logFatalError, parseArgs, setVerbose } from './lib/utils.js'
15
15
 
16
16
  export async function main () {
17
17
  globalThis.platformatic = { executable: 'watt' }
@@ -52,7 +52,7 @@ export async function main () {
52
52
  }
53
53
 
54
54
  let command
55
- switch (unparsed[0]) {
55
+ switch (unparsed[0] || 'help') {
56
56
  case 'build':
57
57
  command = buildCommand
58
58
  break
@@ -118,12 +118,13 @@ export async function main () {
118
118
  case 'admin':
119
119
  command = adminCommand
120
120
  break
121
- /* c8 ignore next 5 */
122
121
  default:
123
- logger.fatal(
122
+ logFatalError(
123
+ logger,
124
124
  `Unknown command ${bold(unparsed[0])}. Please run ${bold("'wattpm help'")} to see available commands.`
125
125
  )
126
- return
126
+
127
+ break
127
128
  }
128
129
 
129
130
  await command(logger, unparsed.slice(1))
@@ -9,11 +9,11 @@ import { rsort, satisfies } from 'semver'
9
9
  import { packages } from '../packages.js'
10
10
  import {
11
11
  buildRuntime,
12
- findConfigurationFile,
12
+ findRuntimeConfigurationFile,
13
13
  getPackageArgs,
14
14
  getPackageManager,
15
15
  getRoot,
16
- loadConfigurationFile,
16
+ loadRuntimeConfigurationFile,
17
17
  logFatalError,
18
18
  parseArgs
19
19
  } from '../utils.js'
@@ -40,7 +40,7 @@ async function executeCommand (root, ...args) {
40
40
 
41
41
  export async function installDependencies (logger, root, services, production, packageManager) {
42
42
  if (typeof services === 'string') {
43
- const config = await loadConfigurationFile(logger, services)
43
+ const config = await loadRuntimeConfigurationFile(logger, services)
44
44
  services = config.services
45
45
  }
46
46
 
@@ -158,55 +158,68 @@ async function updateDependencies (logger, latest, availableVersions, path, targ
158
158
  }
159
159
 
160
160
  export async function buildCommand (logger, args) {
161
- const {
162
- values: { config },
163
- positionals
164
- } = parseArgs(
165
- args,
166
- {
167
- config: {
168
- type: 'string',
169
- short: 'c'
170
- }
171
- },
172
- false
173
- )
174
- const root = getRoot(positionals)
161
+ let runtime
175
162
 
176
- const configurationFile = await findConfigurationFile(logger, root, config)
163
+ try {
164
+ const {
165
+ values: { config },
166
+ positionals
167
+ } = parseArgs(
168
+ args,
169
+ {
170
+ config: {
171
+ type: 'string',
172
+ short: 'c'
173
+ }
174
+ },
175
+ false
176
+ )
177
+ const root = getRoot(positionals)
177
178
 
178
- /* c8 ignore next 3 - Hard to test */
179
- if (!configurationFile) {
180
- return
181
- }
179
+ const configurationFile = await findRuntimeConfigurationFile(logger, root, config)
182
180
 
183
- const runtime = await buildRuntime(logger, configurationFile)
184
- // Gather informations for all services before starting
185
- const { services } = await runtime.getServices()
181
+ /* c8 ignore next 3 - Hard to test */
182
+ if (!configurationFile) {
183
+ return
184
+ }
186
185
 
187
- for (const { id } of services) {
188
- const currentLogger = logger.child({ name: id })
189
- currentLogger.debug(`Building service ${bold(id)} ...`)
186
+ runtime = await buildRuntime(logger, configurationFile)
190
187
 
191
- try {
192
- await runtime.buildService(id)
193
- } catch (error) {
194
- if (error.code === 'PLT_BASIC_NON_ZERO_EXIT_CODE') {
195
- currentLogger.error(`Building service "${id}" has failed with exit code ${error.exitCode}.`)
196
- /* c8 ignore next 6 */
197
- } else {
198
- currentLogger.error(
199
- { err: ensureLoggableError(error) },
200
- `Building service "${id}" has throw an exception: ${error.message}`
201
- )
202
- }
188
+ /* c8 ignore next 3 - Hard to test */
189
+ if (!runtime) {
190
+ return
191
+ }
192
+
193
+ // Gather informations for all services before starting
194
+ const { services } = await runtime.getServices()
203
195
 
204
- process.exit(1)
196
+ for (const { id } of services) {
197
+ const currentLogger = logger.child({ name: id })
198
+ currentLogger.debug(`Building service ${bold(id)} ...`)
199
+
200
+ try {
201
+ await runtime.buildService(id)
202
+ } catch (error) {
203
+ if (error.code === 'PLT_BASIC_NON_ZERO_EXIT_CODE') {
204
+ logFatalError(currentLogger, `Building service "${id}" has failed with exit code ${error.exitCode}.`)
205
+ /* c8 ignore next 6 */
206
+ } else {
207
+ logFatalError(
208
+ currentLogger,
209
+ { err: ensureLoggableError(error) },
210
+ `Building service "${id}" has throw an exception: ${error.message}`
211
+ )
212
+ /* c8 ignore next 3 - Mistakenly reported as uncovered by C8 */
213
+ }
214
+
215
+ return
216
+ }
205
217
  }
206
- }
207
218
 
208
- logger.done('All services have been built.')
209
- await runtime.close(true)
219
+ logger.done('All services have been built.')
220
+ } finally {
221
+ await runtime?.close?.(true)
222
+ }
210
223
  }
211
224
 
212
225
  export async function installCommand (logger, args) {
@@ -234,7 +247,7 @@ export async function installCommand (logger, args) {
234
247
  )
235
248
 
236
249
  const root = getRoot(positionals)
237
- const configurationFile = await findConfigurationFile(logger, root, config)
250
+ const configurationFile = await findRuntimeConfigurationFile(logger, root, config)
238
251
 
239
252
  /* c8 ignore next 3 - Hard to test */
240
253
  if (!configurationFile) {
@@ -268,14 +281,14 @@ export async function updateCommand (logger, args) {
268
281
  )
269
282
 
270
283
  const root = getRoot(positionals)
271
- const configurationFile = await findConfigurationFile(logger, root, config)
284
+ const configurationFile = await findRuntimeConfigurationFile(logger, root, config)
272
285
 
273
286
  /* c8 ignore next 3 - Hard to test */
274
287
  if (!configurationFile) {
275
288
  return
276
289
  }
277
290
 
278
- const { services } = await loadConfigurationFile(logger, configurationFile)
291
+ const { services } = await loadRuntimeConfigurationFile(logger, configurationFile)
279
292
 
280
293
  // First of all, get all version from NPM for the runtime
281
294
  const selfInfoResponse = await fetch('https://registry.npmjs.org/@platformatic/runtime')
@@ -4,7 +4,7 @@ import { ensureLoggableError } from '@platformatic/utils'
4
4
  import { bold } from 'colorette'
5
5
  import { spawn } from 'node:child_process'
6
6
  import { watch } from 'node:fs/promises'
7
- import { findConfigurationFile, getMatchingRuntime, getRoot, logFatalError, parseArgs } from '../utils.js'
7
+ import { findRuntimeConfigurationFile, getMatchingRuntime, getRoot, logFatalError, parseArgs } from '../utils.js'
8
8
 
9
9
  export async function devCommand (logger, args) {
10
10
  const {
@@ -22,7 +22,7 @@ export async function devCommand (logger, args) {
22
22
  )
23
23
  const root = getRoot(positionals)
24
24
 
25
- const configurationFile = await findConfigurationFile(logger, root, config)
25
+ const configurationFile = await findRuntimeConfigurationFile(logger, root, config)
26
26
 
27
27
  /* c8 ignore next 3 - Hard to test */
28
28
  if (!configurationFile) {
@@ -62,7 +62,7 @@ export async function startCommand (logger, args) {
62
62
  )
63
63
 
64
64
  const root = getRoot(positionals)
65
- const configurationFile = await findConfigurationFile(logger, root, config)
65
+ const configurationFile = await findRuntimeConfigurationFile(logger, root, config)
66
66
 
67
67
  /* c8 ignore next 3 - Hard to test */
68
68
  if (!configurationFile) {
@@ -9,9 +9,9 @@ import { readFile, writeFile } from 'node:fs/promises'
9
9
  import { basename, dirname, isAbsolute, join, relative, resolve } from 'node:path'
10
10
  import { version } from '../schema.js'
11
11
  import {
12
- findConfigurationFile,
12
+ findRuntimeConfigurationFile,
13
13
  getRoot,
14
- loadConfigurationFile,
14
+ loadRuntimeConfigurationFile,
15
15
  logFatalError,
16
16
  parseArgs,
17
17
  serviceToEnvVariable
@@ -89,20 +89,19 @@ export async function appendEnvVariable (envFile, key, value) {
89
89
  }
90
90
 
91
91
  async function fixConfiguration (logger, root, configOption) {
92
- const configurationFile = await findConfigurationFile(logger, root, configOption)
92
+ const configurationFile = await findRuntimeConfigurationFile(logger, root, configOption)
93
93
 
94
94
  /* c8 ignore next 3 - Hard to test */
95
95
  if (!configurationFile) {
96
96
  return
97
97
  }
98
98
 
99
- const config = await loadConfigurationFile(logger, configurationFile)
99
+ const config = await loadRuntimeConfigurationFile(logger, configurationFile)
100
100
 
101
101
  // For each service, if there is no watt.json, create one and fix package dependencies
102
102
  for (const { path } of config.services) {
103
103
  const wattConfiguration = await findExistingConfiguration(root, path)
104
104
 
105
- /* c8 ignore next 3 - Hard to test */
106
105
  if (wattConfiguration) {
107
106
  continue
108
107
  }
@@ -124,7 +123,7 @@ async function fixConfiguration (logger, root, configOption) {
124
123
  }
125
124
 
126
125
  async function importService (logger, configurationFile, id, path, url, branch) {
127
- const config = await loadConfigurationFile(logger, configurationFile)
126
+ const config = await loadRuntimeConfigurationFile(logger, configurationFile)
128
127
  const rawConfig = await loadRawConfigurationFile(configurationFile)
129
128
  const root = dirname(configurationFile)
130
129
  const envFile = resolve(root, '.env')
@@ -260,7 +259,7 @@ export async function resolveServices (
260
259
  skipDependencies,
261
260
  packageManager
262
261
  ) {
263
- const config = await loadConfigurationFile(logger, configurationFile)
262
+ const config = await loadRuntimeConfigurationFile(logger, configurationFile)
264
263
 
265
264
  // The services which might be to be resolved are the one that have a URL and either
266
265
  // no path defined (which means no environment variable set) or a non-existing path (which means not resolved yet)
@@ -407,7 +406,7 @@ export async function importCommand (logger, args) {
407
406
  rawUrl = positionals[1]
408
407
  }
409
408
 
410
- const configurationFile = await findConfigurationFile(logger, root, config)
409
+ const configurationFile = await findRuntimeConfigurationFile(logger, root, config)
411
410
 
412
411
  /* c8 ignore next 3 - Hard to test */
413
412
  if (!configurationFile) {
@@ -462,7 +461,7 @@ export async function resolveCommand (logger, args) {
462
461
  )
463
462
 
464
463
  const root = getRoot(positionals)
465
- const configurationFile = await findConfigurationFile(logger, root, config)
464
+ const configurationFile = await findRuntimeConfigurationFile(logger, root, config)
466
465
 
467
466
  /* c8 ignore next 3 - Hard to test */
468
467
  if (!configurationFile) {
@@ -1,11 +1,25 @@
1
- import { ConfigManager, getParser, getStringifier } from '@platformatic/config'
2
- import { ensureLoggableError, loadModule } from '@platformatic/utils'
1
+ import {
2
+ ConfigManager,
3
+ getParser,
4
+ getStringifier,
5
+ loadConfigurationFile,
6
+ saveConfigurationFile
7
+ } from '@platformatic/config'
8
+ import { ensureLoggableError, loadModule, safeRemove } from '@platformatic/utils'
3
9
  import jsonPatch from 'fast-json-patch'
4
10
  import { existsSync } from 'node:fs'
5
- import { readFile, writeFile } from 'node:fs/promises'
11
+ import { mkdtemp, readFile, writeFile } from 'node:fs/promises'
6
12
  import { createRequire } from 'node:module'
13
+ import { tmpdir } from 'node:os'
7
14
  import { resolve } from 'node:path'
8
- import { buildRuntime, findConfigurationFile, getRoot, logFatalError, parseArgs } from '../utils.js'
15
+ import {
16
+ buildRuntime,
17
+ findRuntimeConfigurationFile,
18
+ getRoot,
19
+ loadConfigurationFileAsConfig,
20
+ logFatalError,
21
+ parseArgs
22
+ } from '../utils.js'
9
23
 
10
24
  async function patchFile (path, patch) {
11
25
  let config = getParser(path)(await readFile(path, 'utf-8'))
@@ -16,7 +30,11 @@ async function patchFile (path, patch) {
16
30
  export async function patchConfig (logger, configurationFile, patchPath) {
17
31
  let runtime
18
32
  try {
19
- const patchFunction = await loadModule(createRequire(import.meta.url), patchPath)
33
+ // Determine if the configuration file is for a service or a runtime
34
+ const config = await loadConfigurationFileAsConfig(logger, configurationFile)
35
+ const isService = config.configType !== 'runtime'
36
+
37
+ const patchFunction = await loadModule(createRequire(configurationFile), patchPath)
20
38
 
21
39
  if (typeof patchFunction !== 'function') {
22
40
  throw new Error('Patch file must export a function.')
@@ -25,6 +43,11 @@ export async function patchConfig (logger, configurationFile, patchPath) {
25
43
  // Create the runtime
26
44
  runtime = await buildRuntime(logger, configurationFile)
27
45
 
46
+ /* c8 ignore next 3 - Hard to test */
47
+ if (!runtime) {
48
+ return
49
+ }
50
+
28
51
  // Prepare the structure for original and modified configurations files
29
52
  const original = {
30
53
  runtime: getParser(configurationFile)(await readFile(configurationFile, 'utf-8')),
@@ -64,7 +87,24 @@ export async function patchConfig (logger, configurationFile, patchPath) {
64
87
  }
65
88
 
66
89
  if (Array.isArray(patches.runtime)) {
67
- await patchFile(configurationFile, patches.runtime)
90
+ if (isService) {
91
+ // Create a temporary file with existing configuration
92
+ const temporaryDir = await mkdtemp(resolve(tmpdir(), 'wattpm-patch-'))
93
+ const temporaryFile = resolve(temporaryDir, 'watt.json')
94
+
95
+ try {
96
+ /* c8 ignore next - Else branch */
97
+ await saveConfigurationFile(temporaryFile, original.runtime.runtime ?? {})
98
+ await patchFile(temporaryFile, patches.runtime)
99
+ await patchFile(configurationFile, [
100
+ { op: 'replace', path: '/runtime', value: await loadConfigurationFile(temporaryFile) }
101
+ ])
102
+ } finally {
103
+ await safeRemove(temporaryDir)
104
+ }
105
+ } else {
106
+ await patchFile(configurationFile, patches.runtime)
107
+ }
68
108
  }
69
109
 
70
110
  if (typeof patches.services === 'object') {
@@ -77,7 +117,7 @@ export async function patchConfig (logger, configurationFile, patchPath) {
77
117
  }
78
118
  }
79
119
  } finally {
80
- await runtime?.close?.(false, true)
120
+ await runtime?.close?.(true)
81
121
  }
82
122
  }
83
123
 
@@ -113,7 +153,7 @@ export async function patchConfigCommand (logger, args) {
113
153
  patch = positionals[1]
114
154
  }
115
155
 
116
- const configurationFile = await findConfigurationFile(logger, root, config)
156
+ const configurationFile = await findRuntimeConfigurationFile(logger, root, config)
117
157
 
118
158
  /* c8 ignore next 3 */
119
159
  if (!configurationFile) {
package/lib/logo.js CHANGED
@@ -30,7 +30,7 @@ const str = `
30
30
  &&&&&&&&&&&&&
31
31
  &&&&&&
32
32
 
33
- Welcome to ${bold('Watt')}!
33
+ Welcome to ${bold('Watt')}!
34
34
  `
35
35
 
36
36
  export const logo = str.replace(/\//g, s => green(s))
package/lib/utils.js CHANGED
@@ -1,10 +1,7 @@
1
- import {
2
- findConfigurationFile as findRawConfigurationFile,
3
- loadConfig as pltConfigLoadConfig,
4
- Store
5
- } from '@platformatic/config'
1
+ import { findConfigurationFile, loadConfig, Store } from '@platformatic/config'
6
2
  import { errors } from '@platformatic/control'
7
- import { platformaticRuntime, buildRuntime as pltBuildRuntime } from '@platformatic/runtime'
3
+ import { platformaticRuntime, buildRuntime as pltBuildRuntime, wrapConfigInRuntimeConfig } from '@platformatic/runtime'
4
+ import { ensureLoggableError } from '@platformatic/utils'
8
5
  import { bgGreen, black, bold } from 'colorette'
9
6
  import { existsSync } from 'node:fs'
10
7
  import { resolve } from 'node:path'
@@ -156,8 +153,13 @@ export function serviceToEnvVariable (service) {
156
153
  return `PLT_SERVICE_${service.toUpperCase().replaceAll(/[^A-Z0-9_]/g, '_')}_PATH`
157
154
  }
158
155
 
159
- export async function findConfigurationFile (logger, root, configurationFile, schemas = 'runtime') {
160
- const configFile = await findRawConfigurationFile(root, configurationFile, schemas)
156
+ export async function findRuntimeConfigurationFile (logger, root, configurationFile, schemas = 'runtime') {
157
+ let configFile = await findConfigurationFile(root, configurationFile, schemas)
158
+
159
+ // If a runtime was not found, search for service file that we wrap in a runtime
160
+ if (schemas === 'runtime' && !configFile && !configurationFile) {
161
+ configFile = await findConfigurationFile(root, configurationFile)
162
+ }
161
163
 
162
164
  if (!configFile) {
163
165
  return logFatalError(
@@ -171,39 +173,64 @@ export async function findConfigurationFile (logger, root, configurationFile, sc
171
173
  return configFile
172
174
  }
173
175
 
174
- export async function loadConfigurationFile (logger, configurationFile) {
175
- const store = new Store({
176
- cwd: process.cwd(),
177
- logger
178
- })
176
+ export async function loadConfigurationFileAsConfig (logger, configurationFile) {
177
+ const store = new Store()
179
178
  store.add(platformaticRuntime)
180
179
 
181
- const { configManager } = await store.loadConfig({
182
- config: configurationFile,
183
- overrides: {
180
+ const args = ['-c', configurationFile]
181
+ const options = { allowInvalid: true, transformOnValidationErrors: true }
182
+ return loadConfig(
183
+ {},
184
+ args,
185
+ store,
186
+ {
184
187
  /* c8 ignore next 3 */
185
- onMissingEnv (key) {
188
+ onMissingEnv () {
186
189
  return ''
187
190
  }
188
- }
189
- })
191
+ },
192
+ true,
193
+ logger,
194
+ options
195
+ )
196
+ }
190
197
 
191
- await configManager.parse(true, [], { transformOnValidationErrors: true })
192
- return configManager.current
198
+ export async function loadRuntimeConfigurationFile (logger, configurationFile) {
199
+ const config = await loadConfigurationFileAsConfig(logger, configurationFile)
200
+ const args = ['-c', configurationFile]
201
+ const options = { allowInvalid: true, transformOnValidationErrors: true }
202
+
203
+ if (config.configType !== 'runtime') {
204
+ const configManager = await wrapConfigInRuntimeConfig(config, args, options)
205
+ config.configManager = configManager
206
+ }
207
+
208
+ config.configManager.args = config.args
209
+
210
+ return config.configManager.current
193
211
  }
194
212
 
195
213
  export async function buildRuntime (logger, configurationFile) {
196
214
  const store = new Store()
197
215
  store.add(platformaticRuntime)
198
216
 
199
- const config = await pltConfigLoadConfig({}, ['-c', configurationFile], store, {}, true, logger)
217
+ const args = ['-c', configurationFile]
218
+ const config = await loadConfig({}, args, store, {}, true, logger)
219
+
220
+ if (config.configType !== 'runtime') {
221
+ const configManager = await wrapConfigInRuntimeConfig(config, args)
222
+ config.configManager = configManager
223
+ }
224
+
200
225
  config.configManager.args = config.args
201
226
 
202
- const runtimeConfig = config.configManager
227
+ let runtime
203
228
  try {
204
- return await pltBuildRuntime(runtimeConfig)
229
+ runtime = await pltBuildRuntime(config.configManager)
205
230
  /* c8 ignore next 3 - Hard to test */
206
- } catch (e) {
207
- process.exit(1)
231
+ } catch (error) {
232
+ logFatalError(logger, { err: ensureLoggableError(error) }, 'Error creating the runtime')
208
233
  }
234
+
235
+ return runtime
209
236
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wattpm",
3
- "version": "2.66.0",
3
+ "version": "2.67.0-alpha.0",
4
4
  "description": "The Node.js Application Server",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -36,12 +36,12 @@
36
36
  "semver": "^7.7.0",
37
37
  "split2": "^4.2.0",
38
38
  "table": "^6.8.2",
39
- "@platformatic/basic": "2.66.0",
40
- "@platformatic/config": "2.66.0",
41
- "@platformatic/runtime": "2.66.0",
42
- "@platformatic/utils": "2.66.0",
43
- "create-platformatic": "2.66.0",
44
- "@platformatic/control": "2.66.0"
39
+ "@platformatic/basic": "2.67.0-alpha.0",
40
+ "@platformatic/config": "2.67.0-alpha.0",
41
+ "@platformatic/control": "2.67.0-alpha.0",
42
+ "@platformatic/runtime": "2.67.0-alpha.0",
43
+ "@platformatic/utils": "2.67.0-alpha.0",
44
+ "create-platformatic": "2.67.0-alpha.0"
45
45
  },
46
46
  "devDependencies": {
47
47
  "borp": "^0.20.0",
@@ -51,7 +51,7 @@
51
51
  "neostandard": "^0.12.0",
52
52
  "typescript": "^5.5.4",
53
53
  "undici": "^7.0.0",
54
- "@platformatic/node": "2.66.0"
54
+ "@platformatic/node": "2.67.0-alpha.0"
55
55
  },
56
56
  "scripts": {
57
57
  "test": "npm run lint && borp --concurrency=1 --timeout=300000",
package/schema.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "$id": "https://schemas.platformatic.dev/wattpm/2.66.0.json",
2
+ "$id": "https://schemas.platformatic.dev/wattpm/2.67.0-alpha.0.json",
3
3
  "$schema": "http://json-schema.org/draft-07/schema#",
4
4
  "type": "object",
5
5
  "properties": {