@platformatic/runtime 2.65.0 → 2.66.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/config.d.ts CHANGED
@@ -5,7 +5,7 @@
5
5
  * and run json-schema-to-typescript to regenerate this file.
6
6
  */
7
7
 
8
- export type HttpsSchemasPlatformaticDevPlatformaticRuntime2650Json = {
8
+ export type HttpsSchemasPlatformaticDevPlatformaticRuntime2660Json = {
9
9
  [k: string]: unknown;
10
10
  } & {
11
11
  $schema?: string;
@@ -85,6 +85,14 @@ export type HttpsSchemasPlatformaticDevPlatformaticRuntime2650Json = {
85
85
  paths: string[];
86
86
  censor?: string;
87
87
  };
88
+ base?: {
89
+ [k: string]: unknown;
90
+ } | null;
91
+ messageKey?: string;
92
+ customLevels?: {
93
+ [k: string]: unknown;
94
+ };
95
+ captureStdio?: boolean;
88
96
  [k: string]: unknown;
89
97
  };
90
98
  server?: {
@@ -140,11 +148,35 @@ export type HttpsSchemasPlatformaticDevPlatformaticRuntime2650Json = {
140
148
  [k: string]: unknown;
141
149
  };
142
150
  interceptors?:
143
- | UndiciInterceptor[]
144
151
  | {
145
- Client?: UndiciInterceptor[];
146
- Pool?: UndiciInterceptor[];
147
- Agent?: UndiciInterceptor[];
152
+ module: string;
153
+ options: {
154
+ [k: string]: unknown;
155
+ };
156
+ [k: string]: unknown;
157
+ }[]
158
+ | {
159
+ Client?: {
160
+ module: string;
161
+ options: {
162
+ [k: string]: unknown;
163
+ };
164
+ [k: string]: unknown;
165
+ }[];
166
+ Pool?: {
167
+ module: string;
168
+ options: {
169
+ [k: string]: unknown;
170
+ };
171
+ [k: string]: unknown;
172
+ }[];
173
+ Agent?: {
174
+ module: string;
175
+ options: {
176
+ [k: string]: unknown;
177
+ };
178
+ [k: string]: unknown;
179
+ }[];
148
180
  [k: string]: unknown;
149
181
  };
150
182
  [k: string]: unknown;
@@ -215,7 +247,82 @@ export type HttpsSchemasPlatformaticDevPlatformaticRuntime2650Json = {
215
247
  additionalProperties?: never;
216
248
  [k: string]: unknown;
217
249
  };
218
- telemetry?: OpenTelemetry;
250
+ telemetry?: {
251
+ enabled?: boolean | string;
252
+ /**
253
+ * The name of the service. Defaults to the folder name if not specified.
254
+ */
255
+ serviceName: string;
256
+ /**
257
+ * The version of the service (optional)
258
+ */
259
+ version?: string;
260
+ /**
261
+ * An array of paths to skip when creating spans. Useful for health checks and other endpoints that do not need to be traced.
262
+ */
263
+ skip?: {
264
+ /**
265
+ * The path to skip. Can be a string or a regex.
266
+ */
267
+ path?: string;
268
+ /**
269
+ * HTTP method to skip
270
+ */
271
+ method?: "GET" | "POST" | "PUT" | "DELETE" | "PATCH" | "HEAD" | "OPTIONS";
272
+ [k: string]: unknown;
273
+ }[];
274
+ exporter?:
275
+ | {
276
+ type?: "console" | "otlp" | "zipkin" | "memory" | "file";
277
+ /**
278
+ * Options for the exporter. These are passed directly to the exporter.
279
+ */
280
+ options?: {
281
+ /**
282
+ * The URL to send the traces to. Not used for console or memory exporters.
283
+ */
284
+ url?: string;
285
+ /**
286
+ * Headers to send to the exporter. Not used for console or memory exporters.
287
+ */
288
+ headers?: {
289
+ [k: string]: unknown;
290
+ };
291
+ /**
292
+ * The path to write the traces to. Only for file exporter.
293
+ */
294
+ path?: string;
295
+ [k: string]: unknown;
296
+ };
297
+ additionalProperties?: never;
298
+ [k: string]: unknown;
299
+ }[]
300
+ | {
301
+ type?: "console" | "otlp" | "zipkin" | "memory" | "file";
302
+ /**
303
+ * Options for the exporter. These are passed directly to the exporter.
304
+ */
305
+ options?: {
306
+ /**
307
+ * The URL to send the traces to. Not used for console or memory exporters.
308
+ */
309
+ url?: string;
310
+ /**
311
+ * Headers to send to the exporter. Not used for console or memory exporters.
312
+ */
313
+ headers?: {
314
+ [k: string]: unknown;
315
+ };
316
+ /**
317
+ * The path to write the traces to. Only for file exporter.
318
+ */
319
+ path?: string;
320
+ [k: string]: unknown;
321
+ };
322
+ additionalProperties?: never;
323
+ [k: string]: unknown;
324
+ };
325
+ };
219
326
  inspectorOptions?: {
220
327
  host?: string;
221
328
  port?: number;
@@ -247,87 +354,3 @@ export type HttpsSchemasPlatformaticDevPlatformaticRuntime2650Json = {
247
354
  [k: string]: unknown;
248
355
  }[];
249
356
  };
250
-
251
- export interface UndiciInterceptor {
252
- module: string;
253
- options: {
254
- [k: string]: unknown;
255
- };
256
- [k: string]: unknown;
257
- }
258
- export interface OpenTelemetry {
259
- enabled?: boolean | string;
260
- /**
261
- * The name of the service. Defaults to the folder name if not specified.
262
- */
263
- serviceName: string;
264
- /**
265
- * The version of the service (optional)
266
- */
267
- version?: string;
268
- /**
269
- * An array of paths to skip when creating spans. Useful for health checks and other endpoints that do not need to be traced.
270
- */
271
- skip?: {
272
- /**
273
- * The path to skip. Can be a string or a regex.
274
- */
275
- path?: string;
276
- /**
277
- * HTTP method to skip
278
- */
279
- method?: "GET" | "POST" | "PUT" | "DELETE" | "PATCH" | "HEAD" | "OPTIONS";
280
- [k: string]: unknown;
281
- }[];
282
- exporter?:
283
- | {
284
- type?: "console" | "otlp" | "zipkin" | "memory" | "file";
285
- /**
286
- * Options for the exporter. These are passed directly to the exporter.
287
- */
288
- options?: {
289
- /**
290
- * The URL to send the traces to. Not used for console or memory exporters.
291
- */
292
- url?: string;
293
- /**
294
- * Headers to send to the exporter. Not used for console or memory exporters.
295
- */
296
- headers?: {
297
- [k: string]: unknown;
298
- };
299
- /**
300
- * The path to write the traces to. Only for file exporter.
301
- */
302
- path?: string;
303
- [k: string]: unknown;
304
- };
305
- additionalProperties?: never;
306
- [k: string]: unknown;
307
- }[]
308
- | {
309
- type?: "console" | "otlp" | "zipkin" | "memory" | "file";
310
- /**
311
- * Options for the exporter. These are passed directly to the exporter.
312
- */
313
- options?: {
314
- /**
315
- * The URL to send the traces to. Not used for console or memory exporters.
316
- */
317
- url?: string;
318
- /**
319
- * Headers to send to the exporter. Not used for console or memory exporters.
320
- */
321
- headers?: {
322
- [k: string]: unknown;
323
- };
324
- /**
325
- * The path to write the traces to. Only for file exporter.
326
- */
327
- path?: string;
328
- [k: string]: unknown;
329
- };
330
- additionalProperties?: never;
331
- [k: string]: unknown;
332
- };
333
- }
package/index.js CHANGED
@@ -4,7 +4,7 @@ const { buildServer } = require('./lib/build-server')
4
4
  const { compile } = require('./lib/compile')
5
5
  const errors = require('./lib/errors')
6
6
  const { platformaticRuntime, wrapConfigInRuntimeConfig } = require('./lib/config')
7
- const RuntimeGenerator = require('./lib/generator/runtime-generator')
7
+ const { RuntimeGenerator, WrappedGenerator } = require('./lib/generator/runtime-generator')
8
8
  const { Runtime } = require('./lib/runtime')
9
9
  const { buildRuntime, start, startCommand } = require('./lib/start')
10
10
  const symbols = require('./lib/worker/symbols')
@@ -17,6 +17,7 @@ module.exports.buildRuntime = buildRuntime
17
17
  module.exports.compile = compile
18
18
  module.exports.errors = errors
19
19
  module.exports.Generator = RuntimeGenerator
20
+ module.exports.WrappedGenerator = WrappedGenerator
20
21
  module.exports.getRuntimeLogsDir = getRuntimeLogsDir
21
22
  module.exports.loadConfig = loadConfig
22
23
  module.exports.platformaticRuntime = platformaticRuntime
package/lib/config.js CHANGED
@@ -2,8 +2,12 @@
2
2
 
3
3
  const { readdir } = require('node:fs/promises')
4
4
  const { join, resolve: pathResolve, isAbsolute } = require('node:path')
5
-
6
- const { createRequire, loadModule } = require('@platformatic/utils')
5
+ const {
6
+ createRequire,
7
+ loadModule,
8
+ omitProperties,
9
+ schemaComponents: { runtimeUnwrappablePropertiesList }
10
+ } = require('@platformatic/utils')
7
11
  const ConfigManager = require('@platformatic/config')
8
12
  const { Store } = require('@platformatic/config')
9
13
 
@@ -259,11 +263,13 @@ async function wrapConfigInRuntimeConfig ({ configManager, args }) {
259
263
  // on purpose, the package.json might be missing
260
264
  }
261
265
 
266
+ // Important: do not change the order of the properties in this object
262
267
  /* c8 ignore next */
263
268
  const wrapperConfig = {
264
269
  $schema: schema.$id,
265
- entrypoint: serviceId,
266
270
  watch: true,
271
+ ...omitProperties(configManager.current.runtime ?? {}, runtimeUnwrappablePropertiesList),
272
+ entrypoint: serviceId,
267
273
  services: [
268
274
  {
269
275
  id: serviceId,
@@ -272,6 +278,7 @@ async function wrapConfigInRuntimeConfig ({ configManager, args }) {
272
278
  }
273
279
  ]
274
280
  }
281
+
275
282
  const cm = new ConfigManager({
276
283
  source: wrapperConfig,
277
284
  schema,
@@ -287,6 +294,7 @@ async function wrapConfigInRuntimeConfig ({ configManager, args }) {
287
294
  })
288
295
 
289
296
  await cm.parseAndValidate()
297
+
290
298
  return cm
291
299
  }
292
300
 
@@ -3,7 +3,7 @@
3
3
  const { BaseGenerator } = require('@platformatic/generators')
4
4
  const { NoEntryPointError, NoServiceNamedError } = require('./errors')
5
5
  const { existsSync } = require('node:fs')
6
- const { join } = require('node:path')
6
+ const { join, basename } = require('node:path')
7
7
  const { envObjectToString } = require('@platformatic/generators/lib/utils')
8
8
  const { readFile, readdir, stat } = require('node:fs/promises')
9
9
  const { ConfigManager } = require('@platformatic/config')
@@ -14,6 +14,30 @@ const { getArrayDifference } = require('../utils')
14
14
  const { pathToFileURL } = require('node:url')
15
15
  const { createRequire, safeRemove, generateDashedName } = require('@platformatic/utils')
16
16
 
17
+ const wrappableProperties = {
18
+ logger: {
19
+ level: '{PLT_SERVER_LOGGER_LEVEL}'
20
+ },
21
+ server: {
22
+ hostname: '{PLT_SERVER_HOSTNAME}',
23
+ port: '{PORT}'
24
+ },
25
+ managementApi: '{PLT_MANAGEMENT_API}'
26
+ }
27
+
28
+ const engines = {
29
+ node: '^18.8.0 || >=20.6.0'
30
+ }
31
+
32
+ function getRuntimeBaseEnvVars (config) {
33
+ return {
34
+ PLT_SERVER_HOSTNAME: '127.0.0.1',
35
+ PORT: config.port || 3042,
36
+ PLT_SERVER_LOGGER_LEVEL: config.logLevel || 'info',
37
+ PLT_MANAGEMENT_API: true
38
+ }
39
+ }
40
+
17
41
  class RuntimeGenerator extends BaseGenerator {
18
42
  constructor (opts) {
19
43
  super({
@@ -62,7 +86,9 @@ class RuntimeGenerator extends BaseGenerator {
62
86
  name: `${this.runtimeName}`,
63
87
  workspaces: [this.servicesFolder + '/*'],
64
88
  scripts: {
65
- start: 'platformatic start'
89
+ dev: this.config.devCommand,
90
+ build: this.config.buildCommand,
91
+ start: this.config.startCommand ?? 'platformatic start'
66
92
  },
67
93
  devDependencies: {
68
94
  fastify: `^${this.fastifyVersion}`,
@@ -74,9 +100,7 @@ class RuntimeGenerator extends BaseGenerator {
74
100
  wattpm: `^${this.platformaticVersion}`,
75
101
  ...this.config.dependencies
76
102
  },
77
- engines: {
78
- node: '^18.8.0 || >=20.6.0'
79
- }
103
+ engines
80
104
  }
81
105
  if (this.config.typescript) {
82
106
  const typescriptVersion = JSON.parse(await readFile(join(__dirname, '..', '..', 'package.json'), 'utf-8'))
@@ -93,15 +117,7 @@ class RuntimeGenerator extends BaseGenerator {
93
117
  this.setServicesConfigValues()
94
118
  this.addServicesDependencies()
95
119
 
96
- this.addEnvVars(
97
- {
98
- PLT_SERVER_HOSTNAME: '127.0.0.1',
99
- PORT: this.config.port || 3042,
100
- PLT_SERVER_LOGGER_LEVEL: this.config.logLevel || 'info',
101
- PLT_MANAGEMENT_API: true
102
- },
103
- { overwrite: false, default: true }
104
- )
120
+ this.addEnvVars(getRuntimeBaseEnvVars(this.config), { overwrite: false, default: true })
105
121
  }
106
122
 
107
123
  addServicesDependencies () {
@@ -119,7 +135,8 @@ class RuntimeGenerator extends BaseGenerator {
119
135
  return
120
136
  }
121
137
  this._hasCheckedForExistingConfig = true
122
- const existingConfigFile = this.runtimeConfig ?? await ConfigManager.findConfigFile(this.targetDirectory, 'runtime')
138
+ const existingConfigFile =
139
+ this.runtimeConfig ?? (await ConfigManager.findConfigFile(this.targetDirectory, 'runtime'))
123
140
  if (existingConfigFile && existsSync(join(this.targetDirectory, existingConfigFile))) {
124
141
  const configManager = new ConfigManager({
125
142
  ...platformaticRuntime.configManagerConfig,
@@ -170,14 +187,7 @@ class RuntimeGenerator extends BaseGenerator {
170
187
  path: this.config.autoload || this.servicesFolder,
171
188
  exclude: ['docs']
172
189
  },
173
- logger: {
174
- level: '{PLT_SERVER_LOGGER_LEVEL}'
175
- },
176
- server: {
177
- hostname: '{PLT_SERVER_HOSTNAME}',
178
- port: '{PORT}'
179
- },
180
- managementApi: '{PLT_MANAGEMENT_API}'
190
+ ...wrappableProperties
181
191
  }
182
192
 
183
193
  return config
@@ -326,9 +336,7 @@ class RuntimeGenerator extends BaseGenerator {
326
336
  const output = {
327
337
  services: []
328
338
  }
329
- const runtimePkgConfigFileData = JSON.parse(
330
- await readFile(join(this.targetDirectory, this.runtimeConfig), 'utf-8')
331
- )
339
+ const runtimePkgConfigFileData = JSON.parse(await readFile(join(this.targetDirectory, this.runtimeConfig), 'utf-8'))
332
340
  const servicesPath = join(this.targetDirectory, runtimePkgConfigFileData.autoload.path)
333
341
 
334
342
  // load all services
@@ -388,9 +396,7 @@ class RuntimeGenerator extends BaseGenerator {
388
396
  // delete dependencies
389
397
  const servicePath = join(this.targetDirectory, this.servicesFolder, s.name)
390
398
  const configFile = await ConfigManager.findConfigFile(servicePath)
391
- const servicePackageJson = JSON.parse(
392
- await readFile(join(servicePath, configFile), 'utf-8')
393
- )
399
+ const servicePackageJson = JSON.parse(await readFile(join(servicePath, configFile), 'utf-8'))
394
400
  if (servicePackageJson.plugins && servicePackageJson.plugins.packages) {
395
401
  servicePackageJson.plugins.packages.forEach(p => {
396
402
  delete currrentPackageJson.dependencies[p.name]
@@ -502,5 +508,96 @@ class RuntimeGenerator extends BaseGenerator {
502
508
  }
503
509
  }
504
510
 
505
- module.exports = RuntimeGenerator
506
- module.exports.RuntimeGenerator = RuntimeGenerator
511
+ class WrappedGenerator extends BaseGenerator {
512
+ async prepare () {
513
+ await this.getPlatformaticVersion()
514
+ await this.#updateEnvironment()
515
+ await this.#updatePackageJson()
516
+ await this.#createConfigFile()
517
+ }
518
+
519
+ async #updateEnvironment () {
520
+ this.addEnvVars(getRuntimeBaseEnvVars(this.config), { overwrite: false, default: true })
521
+
522
+ this.addFile({
523
+ path: '',
524
+ file: '.env',
525
+ contents: (await this.#readExistingFile('.env', '', '\n')) + envObjectToString(this.config.env)
526
+ })
527
+
528
+ this.addFile({
529
+ path: '',
530
+ file: '.env.sample',
531
+ contents: (await this.#readExistingFile('.env.sample', '', '\n')) + envObjectToString(this.config.env)
532
+ })
533
+ }
534
+
535
+ async #updatePackageJson () {
536
+ // Manipulate the package.json, if any
537
+ const packageJson = JSON.parse(await this.#readExistingFile('package.json', '{}'))
538
+ let { name, dependencies, devDependencies, scripts, engines: packageJsonEngines, ...rest } = packageJson
539
+
540
+ // Add the dependencies
541
+ dependencies = {
542
+ ...dependencies,
543
+ [this.module]: `^${this.platformaticVersion}`,
544
+ platformatic: `^${this.platformaticVersion}`,
545
+ wattpm: `^${this.platformaticVersion}`
546
+ }
547
+
548
+ // For easier readbility, sort dependencies and devDependencies by name
549
+ dependencies = Object.fromEntries(Object.entries(dependencies).sort(([a], [b]) => a.localeCompare(b)))
550
+ devDependencies = Object.fromEntries(Object.entries(devDependencies ?? {}).sort(([a], [b]) => a.localeCompare(b)))
551
+
552
+ scripts ??= {}
553
+ scripts.dev ??= this.config.devCommand
554
+ scripts.build ??= this.config.buildCommand
555
+ scripts.start ??= this.config.startCommand ?? 'platformatic start'
556
+
557
+ this.addFile({
558
+ path: '',
559
+ file: 'package.json',
560
+ contents: JSON.stringify(
561
+ {
562
+ name: name ?? this.projectName ?? this.runtimeName ?? basename(this.targetDirectory),
563
+ scripts,
564
+ dependencies,
565
+ devDependencies,
566
+ ...rest,
567
+ engines: { ...packageJsonEngines, ...engines }
568
+ },
569
+ null,
570
+ 2
571
+ )
572
+ })
573
+ }
574
+
575
+ async #createConfigFile () {
576
+ const config = {
577
+ $schema: `https://schemas.platformatic.dev/${this.module}/${this.platformaticVersion}.json`,
578
+ runtime: wrappableProperties
579
+ }
580
+
581
+ this.addFile({
582
+ path: '',
583
+ file: 'watt.json',
584
+ contents: JSON.stringify(config, null, 2)
585
+ })
586
+ }
587
+
588
+ async #readExistingFile (path, emptyContents = '', suffix = '') {
589
+ const filePath = join(this.targetDirectory, path)
590
+
591
+ if (!existsSync(filePath)) {
592
+ return emptyContents
593
+ }
594
+
595
+ const contents = await readFile(filePath, 'utf-8')
596
+ return contents + suffix
597
+ }
598
+ }
599
+
600
+ module.exports = {
601
+ RuntimeGenerator,
602
+ WrappedGenerator
603
+ }
package/lib/logger.js CHANGED
@@ -20,15 +20,22 @@ const customPrettifiers = {
20
20
 
21
21
  // Create the runtime logger
22
22
  async function createLogger (config, runtimeLogsDir) {
23
- const loggerConfig = { ...config.logger }
23
+ const loggerConfig = { ...config.logger, transport: undefined }
24
+ if (config.logger.base === null) {
25
+ loggerConfig.base = undefined
26
+ }
24
27
 
25
28
  // PLT_RUNTIME_LOGGER_STDOUT is used in test to reduce verbosity
26
- const cliStream = process.env.PLT_RUNTIME_LOGGER_STDOUT
29
+ let cliStream = process.env.PLT_RUNTIME_LOGGER_STDOUT
27
30
  ? pino.destination(process.env.PLT_RUNTIME_LOGGER_STDOUT)
28
31
  : isatty(1)
29
32
  ? pretty({ customPrettifiers })
30
33
  : pino.destination(1)
31
34
 
35
+ if (config.logger.transport) {
36
+ cliStream = pino.transport(config.logger.transport)
37
+ }
38
+
32
39
  if (loggerConfig.formatters) {
33
40
  loggerConfig.formatters = buildPinoFormatters(loggerConfig.formatters)
34
41
  }
@@ -40,43 +47,36 @@ async function createLogger (config, runtimeLogsDir) {
40
47
  return [pino(loggerConfig, cliStream), cliStream]
41
48
  }
42
49
 
43
- const multiStream = pino.multistream([{ stream: cliStream, level: loggerConfig.level || 'info' }])
44
-
45
- if (loggerConfig.transport) {
46
- const transport = pino.transport(loggerConfig.transport)
47
- multiStream.add({ level: loggerConfig.level || 'info', stream: transport })
48
- }
50
+ const multiStream = pino.multistream([{ stream: cliStream, level: loggerConfig.level }])
49
51
 
50
- if (config.managementApi) {
51
- const logsFileMb = 5
52
- const logsLimitMb = config.managementApi?.logs?.maxSize || 200
52
+ const logsFileMb = 5
53
+ const logsLimitMb = config.managementApi?.logs?.maxSize || 200
53
54
 
54
- let logsLimitCount = Math.ceil(logsLimitMb / logsFileMb) - 1
55
- if (logsLimitCount < 1) {
56
- logsLimitCount = 1
57
- }
55
+ let logsLimitCount = Math.ceil(logsLimitMb / logsFileMb) - 1
56
+ if (logsLimitCount < 1) {
57
+ logsLimitCount = 1
58
+ }
58
59
 
59
- const pinoRoll = pino.transport({
60
- target: 'pino-roll',
61
- options: {
62
- file: join(runtimeLogsDir, 'logs'),
63
- mode: 0o600,
64
- size: logsFileMb + 'm',
65
- mkdir: true,
66
- fsync: true,
67
- limit: {
68
- count: logsLimitCount
69
- }
60
+ const pinoRoll = pino.transport({
61
+ target: 'pino-roll',
62
+ options: {
63
+ file: join(runtimeLogsDir, 'logs'),
64
+ mode: 0o600,
65
+ size: logsFileMb + 'm',
66
+ mkdir: true,
67
+ fsync: true,
68
+ limit: {
69
+ count: logsLimitCount
70
70
  }
71
- })
71
+ }
72
+ })
72
73
 
73
- multiStream.add({ level: 'trace', stream: pinoRoll })
74
+ multiStream.add({ level: loggerConfig.level, stream: pinoRoll })
74
75
 
75
- // Make sure there is a file before continuing otherwise the management API log endpoint might bail out
76
- await once(pinoRoll, 'ready')
77
- }
76
+ // Make sure there is a file before continuing otherwise the management API log endpoint might bail out
77
+ await once(pinoRoll, 'ready')
78
78
 
79
- return [pino({ level: 'trace' }, multiStream), multiStream]
79
+ return [pino(loggerConfig, multiStream), multiStream]
80
80
  }
81
81
 
82
82
  module.exports = { createLogger }