@platformatic/basic 3.0.0-alpha.1 → 3.0.0-alpha.2

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/lib/base.js CHANGED
@@ -1,5 +1,5 @@
1
- import { client, collectMetrics } from '@platformatic/metrics'
2
- import { buildPinoOptions, deepmerge, executeWithTimeout, kMetadata, kTimeout } from '@platformatic/utils'
1
+ import { buildPinoOptions, deepmerge, executeWithTimeout, kMetadata, kTimeout } from '@platformatic/foundation'
2
+ import { client, collectMetrics, ensureMetricsGroup } from '@platformatic/metrics'
3
3
  import { parseCommandString } from 'execa'
4
4
  import { spawn } from 'node:child_process'
5
5
  import EventEmitter, { once } from 'node:events'
@@ -42,7 +42,6 @@ export class BaseStackable extends EventEmitter {
42
42
  this.basePath = null
43
43
  this.isEntrypoint = this.context.isEntrypoint
44
44
  this.isProduction = this.context.isProduction
45
- this.metricsRegistry = new client.Registry()
46
45
  this.#metricsCollected = false
47
46
  this.customHealthCheck = null
48
47
  this.customReadinessCheck = null
@@ -68,20 +67,26 @@ export class BaseStackable extends EventEmitter {
68
67
  setBasePath: this.setBasePath.bind(this),
69
68
  runtimeBasePath: this.runtimeConfig?.basePath ?? null,
70
69
  invalidateHttpCache: this.#invalidateHttpCache.bind(this),
71
- prometheus: { client, registry: this.metricsRegistry },
72
70
  setCustomHealthCheck: this.setCustomHealthCheck.bind(this),
73
71
  setCustomReadinessCheck: this.setCustomReadinessCheck.bind(this),
74
72
  notifyConfig: this.notifyConfig.bind(this),
75
73
  logger: this.logger
76
74
  })
75
+
76
+ if (globalThis.platformatic.prometheus) {
77
+ this.metricsRegistry = globalThis.platformatic.prometheus.registry
78
+ } else {
79
+ this.metricsRegistry = new client.Registry()
80
+ this.registerGlobals({ prometheus: { client, registry: this.metricsRegistry } })
81
+ }
77
82
  }
78
83
 
79
84
  init () {
80
85
  return this.updateContext()
81
86
  }
82
87
 
83
- updateContext (context) {
84
- // No-op
88
+ updateContext (_context) {
89
+ // No-op by default
85
90
  }
86
91
 
87
92
  start () {
@@ -92,6 +97,10 @@ export class BaseStackable extends EventEmitter {
92
97
  throw new Error('BaseStackable.stop must be overriden by the subclasses')
93
98
  }
94
99
 
100
+ build () {
101
+ // No-op by default
102
+ }
103
+
95
104
  // Alias for stop
96
105
  close () {
97
106
  return this.stop()
@@ -295,6 +304,13 @@ export class BaseStackable extends EventEmitter {
295
304
  this.basePath = path
296
305
  })
297
306
 
307
+ // This is not really important for the URL but sometimes it also a sign
308
+ // that the process has been replaced and thus we need to update the client WebSocket
309
+ this.childManager.on('url', (url, clientWs) => {
310
+ this.url = url
311
+ this.clientWs = clientWs
312
+ })
313
+
298
314
  try {
299
315
  await this.childManager.inject()
300
316
  this.subprocess = await this.spawn(command)
@@ -461,7 +477,7 @@ export class BaseStackable extends EventEmitter {
461
477
  const { client, registry } = globalThis.platformatic.prometheus
462
478
 
463
479
  // Metrics already registered, no need to register them again
464
- if (registry.getSingleMetric('http_cache_hit_count') || registry.getSingleMetric('http_cache_miss_count')) {
480
+ if (ensureMetricsGroup(registry, 'http.cache')) {
465
481
  return
466
482
  }
467
483
 
@@ -483,6 +499,66 @@ export class BaseStackable extends EventEmitter {
483
499
  globalThis.platformatic.onHttpCacheMiss = () => {
484
500
  cacheMissMetric.inc()
485
501
  }
502
+
503
+ const httpStatsFreeMetric = new client.Gauge({
504
+ name: 'http_client_stats_free',
505
+ help: 'Number of free (idle) http clients (sockets)',
506
+ labelNames: ['dispatcher_stats_url'],
507
+ registers: [registry]
508
+ })
509
+ globalThis.platformatic.onHttpStatsFree = (url, val) => {
510
+ httpStatsFreeMetric.set({ dispatcher_stats_url: url }, val)
511
+ }
512
+
513
+ const httpStatsConnectedMetric = new client.Gauge({
514
+ name: 'http_client_stats_connected',
515
+ help: 'Number of open socket connections',
516
+ labelNames: ['dispatcher_stats_url'],
517
+ registers: [registry]
518
+ })
519
+ globalThis.platformatic.onHttpStatsConnected = (url, val) => {
520
+ httpStatsConnectedMetric.set({ dispatcher_stats_url: url }, val)
521
+ }
522
+
523
+ const httpStatsPendingMetric = new client.Gauge({
524
+ name: 'http_client_stats_pending',
525
+ help: 'Number of pending requests across all clients',
526
+ labelNames: ['dispatcher_stats_url'],
527
+ registers: [registry]
528
+ })
529
+ globalThis.platformatic.onHttpStatsPending = (url, val) => {
530
+ httpStatsPendingMetric.set({ dispatcher_stats_url: url }, val)
531
+ }
532
+
533
+ const httpStatsQueuedMetric = new client.Gauge({
534
+ name: 'http_client_stats_queued',
535
+ help: 'Number of queued requests across all clients',
536
+ labelNames: ['dispatcher_stats_url'],
537
+ registers: [registry]
538
+ })
539
+ globalThis.platformatic.onHttpStatsQueued = (url, val) => {
540
+ httpStatsQueuedMetric.set({ dispatcher_stats_url: url }, val)
541
+ }
542
+
543
+ const httpStatsRunningMetric = new client.Gauge({
544
+ name: 'http_client_stats_running',
545
+ help: 'Number of currently active requests across all clients',
546
+ labelNames: ['dispatcher_stats_url'],
547
+ registers: [registry]
548
+ })
549
+ globalThis.platformatic.onHttpStatsRunning = (url, val) => {
550
+ httpStatsRunningMetric.set({ dispatcher_stats_url: url }, val)
551
+ }
552
+
553
+ const httpStatsSizeMetric = new client.Gauge({
554
+ name: 'http_client_stats_size',
555
+ help: 'Number of active, pending, or queued requests across all clients',
556
+ labelNames: ['dispatcher_stats_url'],
557
+ registers: [registry]
558
+ })
559
+ globalThis.platformatic.onHttpStatsSize = (url, val) => {
560
+ httpStatsSizeMetric.set({ dispatcher_stats_url: url }, val)
561
+ }
486
562
  }
487
563
 
488
564
  async #invalidateHttpCache (opts = {}) {
package/lib/config.js CHANGED
@@ -2,7 +2,7 @@ import {
2
2
  listRecognizedConfigurationFiles,
3
3
  NoConfigFileFoundError,
4
4
  findConfigurationFile as utilsFindConfigurationFile
5
- } from '@platformatic/utils'
5
+ } from '@platformatic/foundation'
6
6
  import jsonPatch from 'fast-json-patch'
7
7
  import { stat } from 'node:fs/promises'
8
8
  import { dirname, resolve as resolvePath } from 'node:path'
package/lib/modules.js CHANGED
@@ -1,4 +1,4 @@
1
- import { detectApplicationType, findConfigurationFile } from '@platformatic/utils'
1
+ import { detectApplicationType, findConfigurationFile } from '@platformatic/foundation'
2
2
  import { readFile } from 'node:fs/promises'
3
3
  import { createRequire } from 'node:module'
4
4
  import { relative, resolve } from 'node:path'
package/lib/schema.js CHANGED
@@ -1,12 +1,27 @@
1
- import { schemaComponents as utilsSchemaComponents } from '@platformatic/utils'
1
+ import { schemaComponents as utilsSchemaComponents } from '@platformatic/foundation'
2
2
  import { readFileSync } from 'node:fs'
3
3
 
4
4
  export const packageJson = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf-8'))
5
5
  export const version = packageJson.version
6
6
 
7
+ // This is used by applications to have common properties.
7
8
  const application = {
9
+ type: 'object',
10
+ properties: {},
11
+ additionalProperties: false,
12
+ required: [],
13
+ default: {}
14
+ }
15
+
16
+ /*
17
+ For legacy purposes, when an application is buildable (like Astro), it should use for the application properties.
18
+ Make sure this always extends the `application` schema above.
19
+ */
20
+
21
+ const buildableApplication = {
8
22
  type: 'object',
9
23
  properties: {
24
+ ...application.properties,
10
25
  basePath: {
11
26
  type: 'string'
12
27
  },
@@ -45,7 +60,10 @@ const application = {
45
60
  }
46
61
  },
47
62
  additionalProperties: false,
48
- default: {}
63
+ required: [...application.required],
64
+ default: {
65
+ ...application.default
66
+ }
49
67
  }
50
68
 
51
69
  const watch = {
@@ -60,7 +78,7 @@ const watch = {
60
78
  ]
61
79
  }
62
80
 
63
- export const schemaComponents = { application, watch }
81
+ export const schemaComponents = { application, buildableApplication, watch }
64
82
 
65
83
  export const schema = {
66
84
  $id: `https://schemas.platformatic.dev/@platformatic/basic/${packageJson.version}.json`,
@@ -1,5 +1,5 @@
1
+ import { createDirectory, ensureLoggableError } from '@platformatic/foundation'
1
2
  import { ITC } from '@platformatic/itc'
2
- import { createDirectory, ensureLoggableError } from '@platformatic/utils'
3
3
  import { once } from 'node:events'
4
4
  import { rm, writeFile } from 'node:fs/promises'
5
5
  import { createServer } from 'node:http'
@@ -162,7 +162,7 @@ export class ChildManager extends ITC {
162
162
  const childProcessInclude = `--import="${new URL('./child-process.js', import.meta.url)}"`
163
163
 
164
164
  let telemetryInclude = ''
165
- if (this.#context.telemetryConfig) {
165
+ if (this.#context.telemetryConfig && this.#context.telemetryConfig.enabled !== false) {
166
166
  const require = createRequire(import.meta.url)
167
167
  const telemetryPath = require.resolve('@platformatic/telemetry')
168
168
  const openTelemetrySetupPath = join(telemetryPath, '..', 'lib', 'node-telemetry.js')
@@ -1,12 +1,12 @@
1
- import { ITC } from '@platformatic/itc'
2
- import { client, collectMetrics } from '@platformatic/metrics'
3
1
  import {
4
2
  buildPinoFormatters,
5
3
  buildPinoTimestamp,
6
4
  disablePinoDirectWrite,
7
5
  ensureLoggableError,
8
6
  features
9
- } from '@platformatic/utils'
7
+ } from '@platformatic/foundation'
8
+ import { ITC } from '@platformatic/itc'
9
+ import { client, collectMetrics } from '@platformatic/metrics'
10
10
  import diagnosticChannel, { tracingChannel } from 'node:diagnostics_channel'
11
11
  import { EventEmitter, once } from 'node:events'
12
12
  import { readFile } from 'node:fs/promises'
@@ -222,6 +222,66 @@ export class ChildProcess extends ITC {
222
222
  globalThis.platformatic.onHttpCacheMiss = () => {
223
223
  cacheMissMetric.inc()
224
224
  }
225
+
226
+ const httpStatsFreeMetric = new client.Gauge({
227
+ name: 'http_client_stats_free',
228
+ help: 'Number of free (idle) http clients (sockets)',
229
+ labelNames: ['dispatcher_stats_url'],
230
+ registers: [registry]
231
+ })
232
+ globalThis.platformatic.onHttpStatsFree = (url, val) => {
233
+ httpStatsFreeMetric.set({ dispatcher_stats_url: url }, val)
234
+ }
235
+
236
+ const httpStatsConnectedMetric = new client.Gauge({
237
+ name: 'http_client_stats_connected',
238
+ help: 'Number of open socket connections',
239
+ labelNames: ['dispatcher_stats_url'],
240
+ registers: [registry]
241
+ })
242
+ globalThis.platformatic.onHttpStatsConnected = (url, val) => {
243
+ httpStatsConnectedMetric.set({ dispatcher_stats_url: url }, val)
244
+ }
245
+
246
+ const httpStatsPendingMetric = new client.Gauge({
247
+ name: 'http_client_stats_pending',
248
+ help: 'Number of pending requests across all clients',
249
+ labelNames: ['dispatcher_stats_url'],
250
+ registers: [registry]
251
+ })
252
+ globalThis.platformatic.onHttpStatsPending = (url, val) => {
253
+ httpStatsPendingMetric.set({ dispatcher_stats_url: url }, val)
254
+ }
255
+
256
+ const httpStatsQueuedMetric = new client.Gauge({
257
+ name: 'http_client_stats_queued',
258
+ help: 'Number of queued requests across all clients',
259
+ labelNames: ['dispatcher_stats_url'],
260
+ registers: [registry]
261
+ })
262
+ globalThis.platformatic.onHttpStatsQueued = (url, val) => {
263
+ httpStatsQueuedMetric.set({ dispatcher_stats_url: url }, val)
264
+ }
265
+
266
+ const httpStatsRunningMetric = new client.Gauge({
267
+ name: 'http_client_stats_running',
268
+ help: 'Number of currently active requests across all clients',
269
+ labelNames: ['dispatcher_stats_url'],
270
+ registers: [registry]
271
+ })
272
+ globalThis.platformatic.onHttpStatsRunning = (url, val) => {
273
+ httpStatsRunningMetric.set({ dispatcher_stats_url: url }, val)
274
+ }
275
+
276
+ const httpStatsSizeMetric = new client.Gauge({
277
+ name: 'http_client_stats_size',
278
+ help: 'Number of active, pending, or queued requests across all clients',
279
+ labelNames: ['dispatcher_stats_url'],
280
+ registers: [registry]
281
+ })
282
+ globalThis.platformatic.onHttpStatsSize = (url, val) => {
283
+ httpStatsSizeMetric.set({ dispatcher_stats_url: url }, val)
284
+ }
225
285
  }
226
286
 
227
287
  async #getMetrics ({ format } = {}) {
@@ -1,5 +1,5 @@
1
+ import { ensureLoggableError } from '@platformatic/foundation'
1
2
  import { generateRequest, sanitize } from '@platformatic/itc'
2
- import { ensureLoggableError } from '@platformatic/utils'
3
3
  import { once } from 'node:events'
4
4
  import { platform } from 'node:os'
5
5
  import { workerData } from 'node:worker_threads'
@@ -1,4 +1,4 @@
1
- import { features } from '@platformatic/utils'
1
+ import { features } from '@platformatic/foundation'
2
2
  import { subscribe, tracingChannel, unsubscribe } from 'node:diagnostics_channel'
3
3
 
4
4
  export function createServerListener (overridePort = true, overrideHost) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platformatic/basic",
3
- "version": "3.0.0-alpha.1",
3
+ "version": "3.0.0-alpha.2",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -19,16 +19,16 @@
19
19
  "@fastify/error": "^4.0.0",
20
20
  "execa": "^9.3.1",
21
21
  "fast-json-patch": "^3.1.1",
22
- "pino": "^9.3.2",
22
+ "pino": "^9.9.0",
23
23
  "pino-abstract-transport": "^2.0.0",
24
24
  "semver": "^7.6.3",
25
25
  "split2": "^4.2.0",
26
26
  "undici": "^7.0.0",
27
27
  "ws": "^8.18.0",
28
- "@platformatic/itc": "3.0.0-alpha.1",
29
- "@platformatic/metrics": "3.0.0-alpha.1",
30
- "@platformatic/telemetry": "3.0.0-alpha.1",
31
- "@platformatic/utils": "3.0.0-alpha.1"
28
+ "@platformatic/itc": "3.0.0-alpha.2",
29
+ "@platformatic/metrics": "3.0.0-alpha.2",
30
+ "@platformatic/telemetry": "3.0.0-alpha.2",
31
+ "@platformatic/foundation": "3.0.0-alpha.2"
32
32
  },
33
33
  "devDependencies": {
34
34
  "borp": "^0.20.0",
@@ -40,6 +40,9 @@
40
40
  "neostandard": "^0.12.0",
41
41
  "typescript": "^5.5.4"
42
42
  },
43
+ "engines": {
44
+ "node": ">=22.18.0"
45
+ },
43
46
  "scripts": {
44
47
  "test": "npm run lint && borp --concurrency=1 --timeout 1200000",
45
48
  "coverage": "npm run lint && borp -C -X test -X test/fixtures --concurrency=1 --timeout 1200000",
package/schema.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "$id": "https://schemas.platformatic.dev/@platformatic/basic/3.0.0-alpha.1.json",
2
+ "$id": "https://schemas.platformatic.dev/@platformatic/basic/3.0.0-alpha.2.json",
3
3
  "$schema": "http://json-schema.org/draft-07/schema#",
4
4
  "title": "Platformatic Basic Config",
5
5
  "type": "object",