@platformatic/basic 3.4.1 → 3.5.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/config.d.ts +321 -1
- package/eslint.config.js +1 -3
- package/index.d.ts +81 -0
- package/index.js +4 -155
- package/lib/capability.js +734 -0
- package/lib/config.js +81 -0
- package/lib/creation.js +9 -0
- package/lib/errors.js +1 -1
- package/lib/modules.js +101 -0
- package/lib/schema.js +26 -6
- package/lib/utils.js +4 -2
- package/lib/worker/child-manager.js +82 -37
- package/lib/worker/child-process.js +320 -93
- package/lib/worker/listeners.js +10 -4
- package/package.json +19 -18
- package/schema.json +1277 -2
- package/lib/base.js +0 -276
- package/lib/worker/child-transport.js +0 -59
|
@@ -1,24 +1,44 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import {
|
|
2
|
+
buildPinoFormatters,
|
|
3
|
+
buildPinoTimestamp,
|
|
4
|
+
disablePinoDirectWrite,
|
|
5
|
+
ensureLoggableError,
|
|
6
|
+
features
|
|
7
|
+
} from '@platformatic/foundation'
|
|
8
|
+
import { ITC } from '@platformatic/itc/lib/index.js'
|
|
9
|
+
import { client, collectMetrics } from '@platformatic/metrics'
|
|
10
|
+
import diagnosticChannel, { tracingChannel } from 'node:diagnostics_channel'
|
|
11
|
+
import { EventEmitter, once } from 'node:events'
|
|
6
12
|
import { readFile } from 'node:fs/promises'
|
|
13
|
+
import { ServerResponse } from 'node:http'
|
|
7
14
|
import { register } from 'node:module'
|
|
8
|
-
import { platform, tmpdir } from 'node:os'
|
|
15
|
+
import { hostname, platform, tmpdir } from 'node:os'
|
|
9
16
|
import { basename, resolve } from 'node:path'
|
|
10
|
-
import { fileURLToPath } from 'node:url'
|
|
11
17
|
import { isMainThread } from 'node:worker_threads'
|
|
12
18
|
import pino from 'pino'
|
|
13
|
-
import {
|
|
19
|
+
import { Agent, Pool, setGlobalDispatcher } from 'undici'
|
|
14
20
|
import { WebSocket } from 'ws'
|
|
15
21
|
import { exitCodes } from '../errors.js'
|
|
16
22
|
import { importFile } from '../utils.js'
|
|
17
|
-
import { getSocketPath
|
|
23
|
+
import { getSocketPath } from './child-manager.js'
|
|
24
|
+
|
|
25
|
+
const windowsNpmExecutables = ['npm-prefix.js', 'npm-cli.js']
|
|
26
|
+
|
|
27
|
+
function createInterceptor () {
|
|
28
|
+
const pool = new Pool(
|
|
29
|
+
{
|
|
30
|
+
hostname: 'localhost',
|
|
31
|
+
protocol: 'http:'
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
socketPath: getSocketPath(process.env.PLT_MANAGER_ID),
|
|
35
|
+
keepAliveTimeout: 10,
|
|
36
|
+
keepAliveMaxTimeout: 10
|
|
37
|
+
}
|
|
38
|
+
)
|
|
18
39
|
|
|
19
|
-
function createInterceptor (itc) {
|
|
20
40
|
return function (dispatch) {
|
|
21
|
-
return
|
|
41
|
+
return (opts, handler) => {
|
|
22
42
|
let url = opts.origin
|
|
23
43
|
if (!(url instanceof URL)) {
|
|
24
44
|
url = new URL(opts.path, url)
|
|
@@ -29,49 +49,17 @@ function createInterceptor (itc) {
|
|
|
29
49
|
return dispatch(opts, handler)
|
|
30
50
|
}
|
|
31
51
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
const requestOpts = {
|
|
41
|
-
...opts,
|
|
42
|
-
headers
|
|
43
|
-
}
|
|
44
|
-
delete requestOpts.dispatcher
|
|
45
|
-
|
|
46
|
-
itc
|
|
47
|
-
.send('fetch', requestOpts)
|
|
48
|
-
.then(res => {
|
|
49
|
-
if (res.rawPayload && !Buffer.isBuffer(res.rawPayload)) {
|
|
50
|
-
res.rawPayload = Buffer.from(res.rawPayload.data)
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const headers = []
|
|
54
|
-
for (const [key, value] of Object.entries(res.headers)) {
|
|
55
|
-
if (Array.isArray(value)) {
|
|
56
|
-
for (const v of value) {
|
|
57
|
-
headers.push(key)
|
|
58
|
-
headers.push(v)
|
|
59
|
-
}
|
|
60
|
-
} else {
|
|
61
|
-
headers.push(key)
|
|
62
|
-
headers.push(value)
|
|
63
|
-
}
|
|
52
|
+
// Route the call via the UNIX socket
|
|
53
|
+
return pool.dispatch(
|
|
54
|
+
{
|
|
55
|
+
...opts,
|
|
56
|
+
headers: {
|
|
57
|
+
host: url.host,
|
|
58
|
+
...opts?.headers
|
|
64
59
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
handler.onComplete([])
|
|
69
|
-
})
|
|
70
|
-
.catch(e => {
|
|
71
|
-
handler.onError(new Error(e.message))
|
|
72
|
-
})
|
|
73
|
-
|
|
74
|
-
return true
|
|
60
|
+
},
|
|
61
|
+
handler
|
|
62
|
+
)
|
|
75
63
|
}
|
|
76
64
|
}
|
|
77
65
|
}
|
|
@@ -79,30 +67,90 @@ function createInterceptor (itc) {
|
|
|
79
67
|
export class ChildProcess extends ITC {
|
|
80
68
|
#listener
|
|
81
69
|
#socket
|
|
82
|
-
#child
|
|
83
70
|
#logger
|
|
71
|
+
#metricsRegistry
|
|
84
72
|
#pendingMessages
|
|
85
73
|
|
|
86
74
|
constructor () {
|
|
87
|
-
super({
|
|
75
|
+
super({
|
|
76
|
+
throwOnMissingHandler: false,
|
|
77
|
+
name: `${process.env.PLT_MANAGER_ID}-child-process`,
|
|
78
|
+
handlers: {
|
|
79
|
+
collectMetrics: (...args) => {
|
|
80
|
+
return this.#collectMetrics(...args)
|
|
81
|
+
},
|
|
82
|
+
getMetrics: (...args) => {
|
|
83
|
+
return this.#getMetrics(...args)
|
|
84
|
+
},
|
|
85
|
+
close: signal => {
|
|
86
|
+
let handled = false
|
|
87
|
+
|
|
88
|
+
try {
|
|
89
|
+
handled = globalThis.platformatic.events.emit('close', signal)
|
|
90
|
+
} catch (error) {
|
|
91
|
+
this.#logger.error({ err: ensureLoggableError(error) }, 'Error while handling close event.')
|
|
92
|
+
process.exitCode = 1
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (!handled) {
|
|
96
|
+
// No user event, just exit without errors
|
|
97
|
+
setImmediate(() => {
|
|
98
|
+
process.exit(process.exitCode ?? 0)
|
|
99
|
+
})
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return handled
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
})
|
|
88
106
|
|
|
89
107
|
/* c8 ignore next */
|
|
90
108
|
const protocol = platform() === 'win32' ? 'ws+unix:' : 'ws+unix://'
|
|
91
109
|
this.#socket = new WebSocket(`${protocol}${getSocketPath(process.env.PLT_MANAGER_ID)}`)
|
|
92
110
|
this.#pendingMessages = []
|
|
111
|
+
this.#metricsRegistry = new client.Registry()
|
|
93
112
|
|
|
94
113
|
this.listen()
|
|
95
114
|
this.#setupLogger()
|
|
96
|
-
|
|
97
|
-
|
|
115
|
+
|
|
116
|
+
if (globalThis.platformatic.exitOnUnhandledErrors) {
|
|
117
|
+
this.#setupHandlers()
|
|
118
|
+
}
|
|
119
|
+
|
|
98
120
|
this.#setupServer()
|
|
99
121
|
this.#setupInterceptors()
|
|
100
122
|
|
|
101
|
-
this.
|
|
102
|
-
|
|
123
|
+
this.registerGlobals({
|
|
124
|
+
logger: this.#logger,
|
|
125
|
+
setOpenapiSchema: this.setOpenapiSchema.bind(this),
|
|
126
|
+
setGraphqlSchema: this.setGraphqlSchema.bind(this),
|
|
127
|
+
setConnectionString: this.setConnectionString.bind(this),
|
|
128
|
+
setBasePath: this.setBasePath.bind(this),
|
|
129
|
+
prometheus: { client, registry: this.#metricsRegistry },
|
|
130
|
+
notifyConfig: this.#notifyConfig.bind(this)
|
|
103
131
|
})
|
|
104
132
|
}
|
|
105
133
|
|
|
134
|
+
registerGlobals (globals) {
|
|
135
|
+
globalThis.platformatic = Object.assign(globalThis.platformatic ?? {}, globals)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
setOpenapiSchema (schema) {
|
|
139
|
+
this.notify('openapiSchema', schema)
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
setGraphqlSchema (schema) {
|
|
143
|
+
this.notify('graphqlSchema', schema)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
setConnectionString (connectionString) {
|
|
147
|
+
this.notify('connectionString', connectionString)
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
setBasePath (basePath) {
|
|
151
|
+
this.notify('basePath', basePath)
|
|
152
|
+
}
|
|
153
|
+
|
|
106
154
|
_setupListener (listener) {
|
|
107
155
|
this.#listener = listener
|
|
108
156
|
|
|
@@ -152,31 +200,138 @@ export class ChildProcess extends ITC {
|
|
|
152
200
|
this.#socket.close()
|
|
153
201
|
}
|
|
154
202
|
|
|
155
|
-
#
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
203
|
+
async #collectMetrics ({ applicationId, workerId, metricsConfig }) {
|
|
204
|
+
await collectMetrics(applicationId, workerId, metricsConfig, this.#metricsRegistry)
|
|
205
|
+
this.#setHttpCacheMetrics()
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
#setHttpCacheMetrics () {
|
|
209
|
+
const { client, registry } = globalThis.platformatic.prometheus
|
|
210
|
+
|
|
211
|
+
const cacheHitMetric = new client.Counter({
|
|
212
|
+
name: 'http_cache_hit_count',
|
|
213
|
+
help: 'Number of http cache hits',
|
|
214
|
+
registers: [registry]
|
|
215
|
+
})
|
|
216
|
+
|
|
217
|
+
const cacheMissMetric = new client.Counter({
|
|
218
|
+
name: 'http_cache_miss_count',
|
|
219
|
+
help: 'Number of http cache misses',
|
|
220
|
+
registers: [registry]
|
|
221
|
+
})
|
|
222
|
+
|
|
223
|
+
globalThis.platformatic.onHttpCacheHit = () => {
|
|
224
|
+
cacheHitMetric.inc()
|
|
225
|
+
}
|
|
226
|
+
globalThis.platformatic.onHttpCacheMiss = () => {
|
|
227
|
+
cacheMissMetric.inc()
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const httpStatsFreeMetric = new client.Gauge({
|
|
231
|
+
name: 'http_client_stats_free',
|
|
232
|
+
help: 'Number of free (idle) http clients (sockets)',
|
|
233
|
+
labelNames: ['dispatcher_stats_url'],
|
|
234
|
+
registers: [registry]
|
|
235
|
+
})
|
|
236
|
+
globalThis.platformatic.onHttpStatsFree = (url, val) => {
|
|
237
|
+
httpStatsFreeMetric.set({ dispatcher_stats_url: url }, val)
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
const httpStatsConnectedMetric = new client.Gauge({
|
|
241
|
+
name: 'http_client_stats_connected',
|
|
242
|
+
help: 'Number of open socket connections',
|
|
243
|
+
labelNames: ['dispatcher_stats_url'],
|
|
244
|
+
registers: [registry]
|
|
245
|
+
})
|
|
246
|
+
globalThis.platformatic.onHttpStatsConnected = (url, val) => {
|
|
247
|
+
httpStatsConnectedMetric.set({ dispatcher_stats_url: url }, val)
|
|
248
|
+
}
|
|
167
249
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
250
|
+
const httpStatsPendingMetric = new client.Gauge({
|
|
251
|
+
name: 'http_client_stats_pending',
|
|
252
|
+
help: 'Number of pending requests across all clients',
|
|
253
|
+
labelNames: ['dispatcher_stats_url'],
|
|
254
|
+
registers: [registry]
|
|
255
|
+
})
|
|
256
|
+
globalThis.platformatic.onHttpStatsPending = (url, val) => {
|
|
257
|
+
httpStatsPendingMetric.set({ dispatcher_stats_url: url }, val)
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const httpStatsQueuedMetric = new client.Gauge({
|
|
261
|
+
name: 'http_client_stats_queued',
|
|
262
|
+
help: 'Number of queued requests across all clients',
|
|
263
|
+
labelNames: ['dispatcher_stats_url'],
|
|
264
|
+
registers: [registry]
|
|
265
|
+
})
|
|
266
|
+
globalThis.platformatic.onHttpStatsQueued = (url, val) => {
|
|
267
|
+
httpStatsQueuedMetric.set({ dispatcher_stats_url: url }, val)
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const httpStatsRunningMetric = new client.Gauge({
|
|
271
|
+
name: 'http_client_stats_running',
|
|
272
|
+
help: 'Number of currently active requests across all clients',
|
|
273
|
+
labelNames: ['dispatcher_stats_url'],
|
|
274
|
+
registers: [registry]
|
|
275
|
+
})
|
|
276
|
+
globalThis.platformatic.onHttpStatsRunning = (url, val) => {
|
|
277
|
+
httpStatsRunningMetric.set({ dispatcher_stats_url: url }, val)
|
|
172
278
|
}
|
|
279
|
+
|
|
280
|
+
const httpStatsSizeMetric = new client.Gauge({
|
|
281
|
+
name: 'http_client_stats_size',
|
|
282
|
+
help: 'Number of active, pending, or queued requests across all clients',
|
|
283
|
+
labelNames: ['dispatcher_stats_url'],
|
|
284
|
+
registers: [registry]
|
|
285
|
+
})
|
|
286
|
+
globalThis.platformatic.onHttpStatsSize = (url, val) => {
|
|
287
|
+
httpStatsSizeMetric.set({ dispatcher_stats_url: url }, val)
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
const activeResourcesEventLoopMetric = new client.Gauge({
|
|
291
|
+
name: 'active_resources_event_loop',
|
|
292
|
+
help: 'Number of active resources keeping the event loop alive',
|
|
293
|
+
registers: [registry]
|
|
294
|
+
})
|
|
295
|
+
globalThis.platformatic.onActiveResourcesEventLoop = val => activeResourcesEventLoopMetric.set(val)
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
async #getMetrics ({ format } = {}) {
|
|
299
|
+
const res =
|
|
300
|
+
format === 'json' ? await this.#metricsRegistry.getMetricsAsJSON() : await this.#metricsRegistry.metrics()
|
|
301
|
+
|
|
302
|
+
return res
|
|
173
303
|
}
|
|
174
304
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
305
|
+
#setupLogger () {
|
|
306
|
+
disablePinoDirectWrite()
|
|
307
|
+
|
|
308
|
+
// Since this is executed by user code, make sure we only override this in the main thread
|
|
309
|
+
// The rest will be intercepted by the BaseCapability.
|
|
310
|
+
const loggerOptions = globalThis.platformatic?.config?.logger ?? {}
|
|
311
|
+
const pinoOptions = {
|
|
312
|
+
...loggerOptions,
|
|
313
|
+
level: loggerOptions.level ?? 'info',
|
|
314
|
+
name: globalThis.platformatic.applicationId
|
|
315
|
+
}
|
|
316
|
+
if (loggerOptions.formatters) {
|
|
317
|
+
pinoOptions.formatters = buildPinoFormatters(loggerOptions.formatters)
|
|
318
|
+
}
|
|
319
|
+
if (loggerOptions.timestamp) {
|
|
320
|
+
pinoOptions.timestamp = buildPinoTimestamp(loggerOptions.timestamp)
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
if (loggerOptions.base !== null && typeof globalThis.platformatic.workerId !== 'undefined') {
|
|
324
|
+
pinoOptions.base = {
|
|
325
|
+
...(pinoOptions.base ?? {}),
|
|
326
|
+
pid: process.pid,
|
|
327
|
+
hostname: hostname(),
|
|
328
|
+
worker: parseInt(globalThis.platformatic.workerId)
|
|
329
|
+
}
|
|
330
|
+
} else if (loggerOptions.base === null) {
|
|
331
|
+
pinoOptions.base = undefined
|
|
179
332
|
}
|
|
333
|
+
|
|
334
|
+
this.#logger = pino(pinoOptions)
|
|
180
335
|
}
|
|
181
336
|
|
|
182
337
|
#setupServer () {
|
|
@@ -191,8 +346,14 @@ export class ChildProcess extends ITC {
|
|
|
191
346
|
const host = globalThis.platformatic.host
|
|
192
347
|
|
|
193
348
|
if (port !== false) {
|
|
194
|
-
|
|
349
|
+
const hasFixedPort = typeof port === 'number'
|
|
350
|
+
options.port = hasFixedPort ? port : 0
|
|
351
|
+
|
|
352
|
+
if (hasFixedPort && features.node.reusePort) {
|
|
353
|
+
options.reusePort = true
|
|
354
|
+
}
|
|
195
355
|
}
|
|
356
|
+
|
|
196
357
|
if (typeof host === 'string') {
|
|
197
358
|
options.host = host
|
|
198
359
|
}
|
|
@@ -220,18 +381,26 @@ export class ChildProcess extends ITC {
|
|
|
220
381
|
}
|
|
221
382
|
|
|
222
383
|
tracingChannel('net.server.listen').subscribe(subscribers)
|
|
384
|
+
|
|
385
|
+
const { isEntrypoint, runtimeBasePath, wantsAbsoluteUrls } = globalThis.platformatic
|
|
386
|
+
if (isEntrypoint && runtimeBasePath && !wantsAbsoluteUrls) {
|
|
387
|
+
stripBasePath(runtimeBasePath)
|
|
388
|
+
}
|
|
223
389
|
}
|
|
224
390
|
|
|
225
391
|
#setupInterceptors () {
|
|
226
|
-
|
|
392
|
+
const globalDispatcher = new Agent().compose(createInterceptor(this))
|
|
393
|
+
setGlobalDispatcher(globalDispatcher)
|
|
227
394
|
}
|
|
228
395
|
|
|
229
396
|
#setupHandlers () {
|
|
397
|
+
const errorLabel =
|
|
398
|
+
typeof globalThis.platformatic.workerId !== 'undefined'
|
|
399
|
+
? `worker ${globalThis.platformatic.workerId} of the application "${globalThis.platformatic.applicationId}"`
|
|
400
|
+
: `application "${globalThis.platformatic.applicationId}"`
|
|
401
|
+
|
|
230
402
|
function handleUnhandled (type, err) {
|
|
231
|
-
this.#logger.error(
|
|
232
|
-
{ err: ensureLoggableError(err) },
|
|
233
|
-
`Child process for service ${globalThis.platformatic.id} threw an ${type}.`
|
|
234
|
-
)
|
|
403
|
+
this.#logger.error({ err: ensureLoggableError(err) }, `Child process for the ${errorLabel} threw an ${type}.`)
|
|
235
404
|
|
|
236
405
|
// Give some time to the logger and ITC notifications to land before shutting down
|
|
237
406
|
setTimeout(() => process.exit(exitCodes.PROCESS_UNHANDLED_ERROR), 100)
|
|
@@ -239,18 +408,79 @@ export class ChildProcess extends ITC {
|
|
|
239
408
|
|
|
240
409
|
process.on('uncaughtException', handleUnhandled.bind(this, 'uncaught exception'))
|
|
241
410
|
process.on('unhandledRejection', handleUnhandled.bind(this, 'unhandled rejection'))
|
|
411
|
+
|
|
412
|
+
process.on('newListener', event => {
|
|
413
|
+
if (event === 'uncaughtException' || event === 'unhandledRejection') {
|
|
414
|
+
this.#logger.warn(
|
|
415
|
+
`A listener has been added for the "process.${event}" event. This listener will be never triggered as Watt default behavior will kill the process before.\n To disable this behavior, set "exitOnUnhandledErrors" to false in the runtime config.`
|
|
416
|
+
)
|
|
417
|
+
}
|
|
418
|
+
})
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
#notifyConfig (config) {
|
|
422
|
+
this.notify('config', config)
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
function stripBasePath (basePath) {
|
|
427
|
+
const kBasePath = Symbol('kBasePath')
|
|
428
|
+
|
|
429
|
+
diagnosticChannel.subscribe('http.server.request.start', ({ request, response }) => {
|
|
430
|
+
if (request.url.startsWith(basePath)) {
|
|
431
|
+
request.url = request.url.slice(basePath.length)
|
|
432
|
+
|
|
433
|
+
if (request.url.charAt(0) !== '/') {
|
|
434
|
+
request.url = '/' + request.url
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
response[kBasePath] = basePath
|
|
438
|
+
}
|
|
439
|
+
})
|
|
440
|
+
|
|
441
|
+
const originWriteHead = ServerResponse.prototype.writeHead
|
|
442
|
+
const originSetHeader = ServerResponse.prototype.setHeader
|
|
443
|
+
|
|
444
|
+
ServerResponse.prototype.writeHead = function (statusCode, statusMessage, headers) {
|
|
445
|
+
if (this[kBasePath] !== undefined) {
|
|
446
|
+
if (headers === undefined && typeof statusMessage === 'object') {
|
|
447
|
+
headers = statusMessage
|
|
448
|
+
statusMessage = undefined
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
if (headers) {
|
|
452
|
+
for (const key in headers) {
|
|
453
|
+
if (key.toLowerCase() === 'location' && !headers[key].startsWith(basePath)) {
|
|
454
|
+
headers[key] = basePath + headers[key]
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
return originWriteHead.call(this, statusCode, statusMessage, headers)
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
ServerResponse.prototype.setHeader = function (name, value) {
|
|
464
|
+
if (this[kBasePath]) {
|
|
465
|
+
if (name.toLowerCase() === 'location' && !value.startsWith(basePath)) {
|
|
466
|
+
value = basePath + value
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
originSetHeader.call(this, name, value)
|
|
242
470
|
}
|
|
243
471
|
}
|
|
244
472
|
|
|
245
473
|
async function main () {
|
|
474
|
+
const executable = basename(process.argv[1] ?? '')
|
|
475
|
+
if (!isMainThread || windowsNpmExecutables.includes(executable)) {
|
|
476
|
+
return
|
|
477
|
+
}
|
|
478
|
+
|
|
246
479
|
const dataPath = resolve(tmpdir(), 'platformatic', 'runtimes', `${process.env.PLT_MANAGER_ID}.json`)
|
|
247
480
|
const { data, loader, scripts } = JSON.parse(await readFile(dataPath))
|
|
248
481
|
|
|
249
482
|
globalThis.platformatic = data
|
|
250
|
-
|
|
251
|
-
if (data.root && isMainThread) {
|
|
252
|
-
process.chdir(fileURLToPath(data.root))
|
|
253
|
-
}
|
|
483
|
+
globalThis.platformatic.events = new EventEmitter()
|
|
254
484
|
|
|
255
485
|
if (loader) {
|
|
256
486
|
register(loader, { data })
|
|
@@ -263,7 +493,4 @@ async function main () {
|
|
|
263
493
|
globalThis[Symbol.for('plt.children.itc')] = new ChildProcess()
|
|
264
494
|
}
|
|
265
495
|
|
|
266
|
-
|
|
267
|
-
if (!isWindows || basename(process.argv.at(-1)) !== 'npm-prefix.js') {
|
|
268
|
-
await main()
|
|
269
|
-
}
|
|
496
|
+
await main()
|
package/lib/worker/listeners.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
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) {
|
|
5
|
-
const { promise, resolve, reject } = withResolvers()
|
|
5
|
+
const { promise, resolve, reject } = Promise.withResolvers()
|
|
6
6
|
|
|
7
7
|
const subscribers = {
|
|
8
8
|
asyncStart ({ options }) {
|
|
@@ -12,8 +12,14 @@ export function createServerListener (overridePort = true, overrideHost) {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
if (overridePort !== false) {
|
|
15
|
-
|
|
15
|
+
const hasFixedPort = typeof overridePort === 'number'
|
|
16
|
+
options.port = hasFixedPort ? overridePort : 0
|
|
17
|
+
|
|
18
|
+
if (hasFixedPort && features.node.reusePort) {
|
|
19
|
+
options.reusePort = true
|
|
20
|
+
}
|
|
16
21
|
}
|
|
22
|
+
|
|
17
23
|
if (typeof overrideHost === 'string') {
|
|
18
24
|
options.host = overrideHost
|
|
19
25
|
}
|
|
@@ -42,7 +48,7 @@ export function createServerListener (overridePort = true, overrideHost) {
|
|
|
42
48
|
}
|
|
43
49
|
|
|
44
50
|
export function createChildProcessListener () {
|
|
45
|
-
const { promise, resolve } = withResolvers()
|
|
51
|
+
const { promise, resolve } = Promise.withResolvers()
|
|
46
52
|
|
|
47
53
|
const handler = ({ process: child }) => {
|
|
48
54
|
unsubscribe('child_process', handler)
|
package/package.json
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@platformatic/basic",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.5.1",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "index.js",
|
|
6
|
+
"types": "index.d.ts",
|
|
6
7
|
"type": "module",
|
|
7
8
|
"repository": {
|
|
8
9
|
"type": "git",
|
|
9
10
|
"url": "git+https://github.com/platformatic/platformatic.git"
|
|
10
11
|
},
|
|
11
|
-
"author": "
|
|
12
|
+
"author": "Platformatic Inc. <oss@platformatic.dev> (https://platformatic.dev)",
|
|
12
13
|
"license": "Apache-2.0",
|
|
13
14
|
"bugs": {
|
|
14
15
|
"url": "https://github.com/platformatic/platformatic/issues"
|
|
@@ -17,36 +18,36 @@
|
|
|
17
18
|
"dependencies": {
|
|
18
19
|
"@fastify/error": "^4.0.0",
|
|
19
20
|
"execa": "^9.3.1",
|
|
20
|
-
"
|
|
21
|
+
"fast-json-patch": "^3.1.1",
|
|
22
|
+
"pino": "^9.9.0",
|
|
21
23
|
"pino-abstract-transport": "^2.0.0",
|
|
22
24
|
"semver": "^7.6.3",
|
|
23
25
|
"split2": "^4.2.0",
|
|
24
|
-
"undici": "^
|
|
26
|
+
"undici": "^7.0.0",
|
|
25
27
|
"ws": "^8.18.0",
|
|
26
|
-
"@platformatic/
|
|
27
|
-
"@platformatic/
|
|
28
|
-
"@platformatic/
|
|
29
|
-
"@platformatic/
|
|
28
|
+
"@platformatic/foundation": "3.5.1",
|
|
29
|
+
"@platformatic/telemetry": "3.5.1",
|
|
30
|
+
"@platformatic/metrics": "3.5.1",
|
|
31
|
+
"@platformatic/itc": "3.5.1"
|
|
30
32
|
},
|
|
31
33
|
"devDependencies": {
|
|
32
|
-
"
|
|
34
|
+
"cleaner-spec-reporter": "^0.5.0",
|
|
33
35
|
"eslint": "9",
|
|
34
36
|
"express": "^4.19.2",
|
|
35
37
|
"fastify": "^5.0.0",
|
|
38
|
+
"get-port": "^7.1.0",
|
|
36
39
|
"json-schema-to-typescript": "^15.0.0",
|
|
37
|
-
"neostandard": "^0.
|
|
38
|
-
"
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
"
|
|
42
|
-
"vite": "^5.4.0"
|
|
40
|
+
"neostandard": "^0.12.0",
|
|
41
|
+
"typescript": "^5.5.4"
|
|
42
|
+
},
|
|
43
|
+
"engines": {
|
|
44
|
+
"node": ">=22.19.0"
|
|
43
45
|
},
|
|
44
46
|
"scripts": {
|
|
45
|
-
"test": "
|
|
46
|
-
"coverage": "npm run lint && borp -C -X test -X test/fixtures --concurrency=1 --no-timeout",
|
|
47
|
+
"test": "node --test --test-reporter=cleaner-spec-reporter --test-concurrency=1 --test-timeout=2000000 test/*.test.js test/**/*.test.js",
|
|
47
48
|
"gen-schema": "node lib/schema.js > schema.json",
|
|
48
49
|
"gen-types": "json2ts > config.d.ts < schema.json",
|
|
49
|
-
"build": "
|
|
50
|
+
"build": "npm run gen-schema && npm run gen-types",
|
|
50
51
|
"lint": "eslint"
|
|
51
52
|
}
|
|
52
53
|
}
|