@platformatic/node 3.13.1 → 3.15.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
@@ -117,7 +117,20 @@ export interface PlatformaticNodeJsConfig {
117
117
  services?: {
118
118
  [k: string]: unknown;
119
119
  }[];
120
- workers?: number | string;
120
+ workers?:
121
+ | number
122
+ | string
123
+ | {
124
+ static?: number;
125
+ dynamic?: boolean;
126
+ minimum?: number;
127
+ maximum?: number;
128
+ total?: number;
129
+ maxMemory?: number;
130
+ cooldown?: number;
131
+ gracePeriod?: number;
132
+ [k: string]: unknown;
133
+ };
121
134
  workersRestartDelay?: number | string;
122
135
  logger?: {
123
136
  level: (
@@ -201,6 +214,7 @@ export interface PlatformaticNodeJsConfig {
201
214
  rejectUnauthorized?: boolean;
202
215
  };
203
216
  };
217
+ reuseTcpPorts?: boolean;
204
218
  startTimeout?: number;
205
219
  restartOnError?: boolean | number;
206
220
  exitOnUnhandledErrors?: boolean;
@@ -325,6 +339,37 @@ export interface PlatformaticNodeJsConfig {
325
339
  };
326
340
  plugins?: string[];
327
341
  timeout?: number | string;
342
+ /**
343
+ * Configuration for exporting metrics to an OTLP endpoint
344
+ */
345
+ otlpExporter?: {
346
+ /**
347
+ * Enable or disable OTLP metrics export
348
+ */
349
+ enabled?: boolean | string;
350
+ /**
351
+ * OTLP endpoint URL (e.g., http://collector:4318/v1/metrics)
352
+ */
353
+ endpoint: string;
354
+ /**
355
+ * Interval in milliseconds between metric pushes
356
+ */
357
+ interval?: number | string;
358
+ /**
359
+ * Additional HTTP headers for authentication
360
+ */
361
+ headers?: {
362
+ [k: string]: string;
363
+ };
364
+ /**
365
+ * Service name for OTLP resource attributes
366
+ */
367
+ serviceName?: string;
368
+ /**
369
+ * Service version for OTLP resource attributes
370
+ */
371
+ serviceVersion?: string;
372
+ };
328
373
  };
329
374
  telemetry?: {
330
375
  enabled?: boolean | string;
@@ -408,13 +453,28 @@ export interface PlatformaticNodeJsConfig {
408
453
  maxTotalMemory?: number;
409
454
  minWorkers?: number;
410
455
  maxWorkers?: number;
456
+ cooldownSec?: number;
457
+ gracePeriod?: number;
458
+ /**
459
+ * @deprecated
460
+ */
411
461
  scaleUpELU?: number;
462
+ /**
463
+ * @deprecated
464
+ */
412
465
  scaleDownELU?: number;
466
+ /**
467
+ * @deprecated
468
+ */
413
469
  timeWindowSec?: number;
470
+ /**
471
+ * @deprecated
472
+ */
414
473
  scaleDownTimeWindowSec?: number;
415
- cooldownSec?: number;
474
+ /**
475
+ * @deprecated
476
+ */
416
477
  scaleIntervalSec?: number;
417
- gracePeriod?: number;
418
478
  };
419
479
  inspectorOptions?: {
420
480
  host?: string;
package/lib/capability.js CHANGED
@@ -7,7 +7,6 @@ import {
7
7
  importFile,
8
8
  injectViaRequest
9
9
  } from '@platformatic/basic'
10
- import { features } from '@platformatic/foundation'
11
10
  import { Unpromise } from '@watchable/unpromise'
12
11
  import inject from 'light-my-request'
13
12
  import { once } from 'node:events'
@@ -100,6 +99,7 @@ export class NodeCapability extends BaseCapability {
100
99
  #isKoa
101
100
  #appClose
102
101
  #useHttpForDispatch
102
+ #factory
103
103
 
104
104
  constructor (root, config, context) {
105
105
  super('nodejs', version, root, config, context)
@@ -111,6 +111,8 @@ export class NodeCapability extends BaseCapability {
111
111
  return this.url
112
112
  }
113
113
 
114
+ await super._start({ listen })
115
+
114
116
  // Listen if entrypoint
115
117
  if (this.#app && listen) {
116
118
  await this._listen()
@@ -156,15 +158,15 @@ export class NodeCapability extends BaseCapability {
156
158
  this.#module = this.#module.default || this.#module
157
159
 
158
160
  // Deal with application
159
- const factory = ['build', 'create'].find(f => typeof this.#module[f] === 'function')
161
+ this.#factory = ['build', 'create'].find(f => typeof this.#module[f] === 'function')
160
162
  this.#appClose = this.#module['close']
161
163
 
162
164
  if (this.#hasServer()) {
163
- if (factory) {
165
+ if (this.#factory) {
164
166
  // We have build function, this Capability will not use HTTP unless it is the entrypoint
165
167
  serverPromise.cancel()
166
168
 
167
- this.#app = await this.#module[factory]()
169
+ this.#app = await this.#module[this.#factory]()
168
170
  this.#isFastify = isFastify(this.#app)
169
171
  this.#isKoa = isKoa(this.#app)
170
172
 
@@ -194,12 +196,21 @@ export class NodeCapability extends BaseCapability {
194
196
  }
195
197
 
196
198
  #hasServer () {
197
- return this.config.node?.hasServer !== false && this.#module.hasServer !== false
199
+ return this.config.node?.hasServer !== false && this.#module?.hasServer !== false
198
200
  }
199
201
 
200
202
  async stop () {
201
203
  await super.stop()
202
204
 
205
+ // Emit the close event so that an application can handle it
206
+ const closeHandled = globalThis.platformatic.events.emit('close')
207
+
208
+ if (!this.#isFastify && !this.#appClose && !closeHandled) {
209
+ this.logger.warn(
210
+ `Please export a "close" function or register a "close" event handler in globalThis.platformatic.events for application "${this.applicationId}" to make sure resources have been closed properly and avoid exit timeouts.`
211
+ )
212
+ }
213
+
203
214
  if (this.status === 'starting') {
204
215
  await Unpromise.race([once(this, 'started'), once(this, 'start:error')])
205
216
  }
@@ -208,9 +219,14 @@ export class NodeCapability extends BaseCapability {
208
219
  return this.stopCommand()
209
220
  }
210
221
 
211
- // for no-server apps, we support custom close method
212
- if (this.#appClose && !this.#hasServer()) {
213
- return this.#appClose()
222
+ // If we have a close function, always invoke it
223
+ if (this.#appClose) {
224
+ await this.#appClose()
225
+ }
226
+
227
+ // for no-server apps, nothing else to do
228
+ if (!this.#hasServer()) {
229
+ return
214
230
  }
215
231
 
216
232
  // This is needed if the capability was subclassed
@@ -326,10 +342,6 @@ export class NodeCapability extends BaseCapability {
326
342
  const serverOptions = this.serverConfig
327
343
  const listenOptions = { host: serverOptions?.hostname || '127.0.0.1', port: serverOptions?.port || 0 }
328
344
 
329
- if (this.isProduction && features.node.reusePort) {
330
- listenOptions.reusePort = true
331
- }
332
-
333
345
  if (this.#isFastify) {
334
346
  await this.#app.listen(listenOptions)
335
347
  this.url = getServerUrl(this.#app.server)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platformatic/node",
3
- "version": "3.13.1",
3
+ "version": "3.15.0",
4
4
  "description": "Platformatic Node.js Capability",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -18,9 +18,9 @@
18
18
  "@watchable/unpromise": "^1.0.2",
19
19
  "json5": "^2.2.3",
20
20
  "light-my-request": "^6.0.0",
21
- "@platformatic/basic": "3.13.1",
22
- "@platformatic/generators": "3.13.1",
23
- "@platformatic/foundation": "3.13.1"
21
+ "@platformatic/foundation": "3.15.0",
22
+ "@platformatic/basic": "3.15.0",
23
+ "@platformatic/generators": "3.15.0"
24
24
  },
25
25
  "devDependencies": {
26
26
  "cleaner-spec-reporter": "^0.5.0",
@@ -32,8 +32,8 @@
32
32
  "neostandard": "^0.12.0",
33
33
  "tsx": "^4.19.0",
34
34
  "typescript": "^5.5.4",
35
- "@platformatic/service": "3.13.1",
36
- "@platformatic/gateway": "3.13.1"
35
+ "@platformatic/gateway": "3.15.0",
36
+ "@platformatic/service": "3.15.0"
37
37
  },
38
38
  "engines": {
39
39
  "node": ">=22.19.0"
package/schema.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "$id": "https://schemas.platformatic.dev/@platformatic/node/3.13.1.json",
2
+ "$id": "https://schemas.platformatic.dev/@platformatic/node/3.15.0.json",
3
3
  "$schema": "http://json-schema.org/draft-07/schema#",
4
4
  "title": "Platformatic Node.js Config",
5
5
  "type": "object",
@@ -426,14 +426,34 @@
426
426
  "useHttp": {
427
427
  "type": "boolean"
428
428
  },
429
+ "reuseTcpPorts": {
430
+ "type": "boolean",
431
+ "default": true
432
+ },
429
433
  "workers": {
430
434
  "anyOf": [
431
435
  {
432
- "type": "number",
433
- "minimum": 1
436
+ "type": "number"
434
437
  },
435
438
  {
436
439
  "type": "string"
440
+ },
441
+ {
442
+ "type": "object",
443
+ "properties": {
444
+ "static": {
445
+ "type": "number",
446
+ "minimum": 1
447
+ },
448
+ "minimum": {
449
+ "type": "number",
450
+ "minimum": 1
451
+ },
452
+ "maximum": {
453
+ "type": "number",
454
+ "minimum": 0
455
+ }
456
+ }
437
457
  }
438
458
  ]
439
459
  },
@@ -654,6 +674,43 @@
654
674
  },
655
675
  {
656
676
  "type": "string"
677
+ },
678
+ {
679
+ "type": "object",
680
+ "properties": {
681
+ "static": {
682
+ "type": "number",
683
+ "minimum": 1
684
+ },
685
+ "dynamic": {
686
+ "type": "boolean",
687
+ "default": false
688
+ },
689
+ "minimum": {
690
+ "type": "number",
691
+ "minimum": 1
692
+ },
693
+ "maximum": {
694
+ "type": "number",
695
+ "minimum": 0
696
+ },
697
+ "total": {
698
+ "type": "number",
699
+ "minimum": 1
700
+ },
701
+ "maxMemory": {
702
+ "type": "number",
703
+ "minimum": 0
704
+ },
705
+ "cooldown": {
706
+ "type": "number",
707
+ "minimum": 0
708
+ },
709
+ "gracePeriod": {
710
+ "type": "number",
711
+ "minimum": 0
712
+ }
713
+ }
657
714
  }
658
715
  ]
659
716
  },
@@ -939,6 +996,10 @@
939
996
  },
940
997
  "additionalProperties": false
941
998
  },
999
+ "reuseTcpPorts": {
1000
+ "type": "boolean",
1001
+ "default": true
1002
+ },
942
1003
  "startTimeout": {
943
1004
  "default": 30000,
944
1005
  "type": "number",
@@ -1430,6 +1491,58 @@
1430
1491
  }
1431
1492
  ],
1432
1493
  "default": 10000
1494
+ },
1495
+ "otlpExporter": {
1496
+ "type": "object",
1497
+ "description": "Configuration for exporting metrics to an OTLP endpoint",
1498
+ "properties": {
1499
+ "enabled": {
1500
+ "anyOf": [
1501
+ {
1502
+ "type": "boolean"
1503
+ },
1504
+ {
1505
+ "type": "string"
1506
+ }
1507
+ ],
1508
+ "description": "Enable or disable OTLP metrics export"
1509
+ },
1510
+ "endpoint": {
1511
+ "type": "string",
1512
+ "description": "OTLP endpoint URL (e.g., http://collector:4318/v1/metrics)"
1513
+ },
1514
+ "interval": {
1515
+ "anyOf": [
1516
+ {
1517
+ "type": "integer"
1518
+ },
1519
+ {
1520
+ "type": "string"
1521
+ }
1522
+ ],
1523
+ "default": 60000,
1524
+ "description": "Interval in milliseconds between metric pushes"
1525
+ },
1526
+ "headers": {
1527
+ "type": "object",
1528
+ "additionalProperties": {
1529
+ "type": "string"
1530
+ },
1531
+ "description": "Additional HTTP headers for authentication"
1532
+ },
1533
+ "serviceName": {
1534
+ "type": "string",
1535
+ "description": "Service name for OTLP resource attributes"
1536
+ },
1537
+ "serviceVersion": {
1538
+ "type": "string",
1539
+ "description": "Service version for OTLP resource attributes"
1540
+ }
1541
+ },
1542
+ "required": [
1543
+ "endpoint"
1544
+ ],
1545
+ "additionalProperties": false
1433
1546
  }
1434
1547
  },
1435
1548
  "additionalProperties": false
@@ -1589,35 +1702,40 @@
1589
1702
  "type": "number",
1590
1703
  "minimum": 1
1591
1704
  },
1705
+ "cooldownSec": {
1706
+ "type": "number",
1707
+ "minimum": 0
1708
+ },
1709
+ "gracePeriod": {
1710
+ "type": "number",
1711
+ "minimum": 0
1712
+ },
1592
1713
  "scaleUpELU": {
1593
1714
  "type": "number",
1594
1715
  "minimum": 0,
1595
- "maximum": 1
1716
+ "maximum": 1,
1717
+ "deprecated": true
1596
1718
  },
1597
1719
  "scaleDownELU": {
1598
1720
  "type": "number",
1599
1721
  "minimum": 0,
1600
- "maximum": 1
1722
+ "maximum": 1,
1723
+ "deprecated": true
1601
1724
  },
1602
1725
  "timeWindowSec": {
1603
1726
  "type": "number",
1604
- "minimum": 0
1727
+ "minimum": 0,
1728
+ "deprecated": true
1605
1729
  },
1606
1730
  "scaleDownTimeWindowSec": {
1607
1731
  "type": "number",
1608
- "minimum": 0
1609
- },
1610
- "cooldownSec": {
1611
- "type": "number",
1612
- "minimum": 0
1732
+ "minimum": 0,
1733
+ "deprecated": true
1613
1734
  },
1614
1735
  "scaleIntervalSec": {
1615
1736
  "type": "number",
1616
- "minimum": 0
1617
- },
1618
- "gracePeriod": {
1619
- "type": "number",
1620
- "minimum": 0
1737
+ "minimum": 0,
1738
+ "deprecated": true
1621
1739
  }
1622
1740
  },
1623
1741
  "additionalProperties": false