@platformatic/runtime 2.67.0-alpha.1 → 2.67.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 +1 -1
- package/lib/config.js +3 -3
- package/lib/errors.js +1 -0
- package/lib/generator/runtime-generator.js +62 -10
- package/lib/prom-server.js +25 -2
- package/lib/runtime.js +131 -14
- package/lib/worker/interceptors.js +197 -8
- package/lib/worker/itc.js +14 -1
- package/lib/worker/main.js +5 -88
- package/lib/worker/round-robin-map.js +6 -11
- package/lib/worker/symbols.js +3 -1
- package/package.json +16 -16
- package/schema.json +1 -1
- package/undefined +0 -5
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
|
|
8
|
+
export type HttpsSchemasPlatformaticDevPlatformaticRuntime2670Json = {
|
|
9
9
|
[k: string]: unknown;
|
|
10
10
|
} & {
|
|
11
11
|
$schema?: string;
|
package/lib/config.js
CHANGED
|
@@ -251,7 +251,7 @@ platformaticRuntime.configManagerConfig = {
|
|
|
251
251
|
upgrade
|
|
252
252
|
}
|
|
253
253
|
|
|
254
|
-
async function wrapConfigInRuntimeConfig ({ configManager, args }) {
|
|
254
|
+
async function wrapConfigInRuntimeConfig ({ configManager, args, opts }) {
|
|
255
255
|
let serviceId = 'main'
|
|
256
256
|
try {
|
|
257
257
|
const packageJson = join(configManager.dirname, 'package.json')
|
|
@@ -272,7 +272,7 @@ async function wrapConfigInRuntimeConfig ({ configManager, args }) {
|
|
|
272
272
|
const wrapperConfig = {
|
|
273
273
|
$schema: schema.$id,
|
|
274
274
|
server,
|
|
275
|
-
watch:
|
|
275
|
+
watch: !args?.production,
|
|
276
276
|
...omitProperties(configManager.current.runtime ?? {}, runtimeUnwrappablePropertiesList),
|
|
277
277
|
entrypoint: serviceId,
|
|
278
278
|
services: [
|
|
@@ -298,7 +298,7 @@ async function wrapConfigInRuntimeConfig ({ configManager, args }) {
|
|
|
298
298
|
}
|
|
299
299
|
})
|
|
300
300
|
|
|
301
|
-
await cm.parseAndValidate()
|
|
301
|
+
await cm.parseAndValidate(true, [], opts)
|
|
302
302
|
|
|
303
303
|
return cm
|
|
304
304
|
}
|
package/lib/errors.js
CHANGED
|
@@ -38,6 +38,7 @@ module.exports = {
|
|
|
38
38
|
FailedToUnlinkManagementApiSocket: createError(`${ERROR_PREFIX}_FAILED_TO_UNLINK_MANAGEMENT_API_SOCKET`, 'Failed to unlink management API socket "%s"'),
|
|
39
39
|
LogFileNotFound: createError(`${ERROR_PREFIX}_LOG_FILE_NOT_FOUND`, 'Log file with index %s not found', 404),
|
|
40
40
|
WorkerIsRequired: createError(`${ERROR_PREFIX}_REQUIRED_WORKER`, 'The worker parameter is required'),
|
|
41
|
+
InvalidArgumentError: createError(`${ERROR_PREFIX}_INVALID_ARGUMENT`, 'Invalid argument: "%s"'),
|
|
41
42
|
|
|
42
43
|
// TODO: should remove next one as it's not used anymore
|
|
43
44
|
CannotRemoveServiceOnUpdateError: createError(`${ERROR_PREFIX}_CANNOT_REMOVE_SERVICE_ON_UPDATE`, 'Cannot remove service "%s" when updating a Runtime'),
|
|
@@ -150,6 +150,9 @@ class RuntimeGenerator extends BaseGenerator {
|
|
|
150
150
|
this.config.port = configManager.env.PORT
|
|
151
151
|
this.entryPoint = configManager.current.services.find(svc => svc.entrypoint)
|
|
152
152
|
this.existingServices = configManager.current.services.map(s => s.id)
|
|
153
|
+
|
|
154
|
+
this.updateRuntimeConfig(this.existingConfigRaw)
|
|
155
|
+
this.updateRuntimeEnv(await readFile(join(this.targetDirectory, '.env'), 'utf-8'))
|
|
153
156
|
}
|
|
154
157
|
}
|
|
155
158
|
|
|
@@ -204,11 +207,7 @@ class RuntimeGenerator extends BaseGenerator {
|
|
|
204
207
|
...servicesEnv
|
|
205
208
|
})
|
|
206
209
|
|
|
207
|
-
this.
|
|
208
|
-
path: '',
|
|
209
|
-
file: '.env',
|
|
210
|
-
contents: envObjectToString(this.config.env)
|
|
211
|
-
})
|
|
210
|
+
this.updateRuntimeEnv(envObjectToString(this.config.env))
|
|
212
211
|
|
|
213
212
|
this.addFile({
|
|
214
213
|
path: '',
|
|
@@ -227,12 +226,21 @@ class RuntimeGenerator extends BaseGenerator {
|
|
|
227
226
|
}
|
|
228
227
|
|
|
229
228
|
async writeFiles () {
|
|
229
|
+
for (const { service } of this.services) {
|
|
230
|
+
await service._beforeWriteFiles?.(this)
|
|
231
|
+
}
|
|
232
|
+
|
|
230
233
|
await super.writeFiles()
|
|
234
|
+
|
|
231
235
|
if (!this.config.isUpdating) {
|
|
232
236
|
for (const { service } of this.services) {
|
|
233
237
|
await service.writeFiles()
|
|
234
238
|
}
|
|
235
239
|
}
|
|
240
|
+
|
|
241
|
+
for (const { service } of this.services) {
|
|
242
|
+
await service._afterWriteFiles?.(this)
|
|
243
|
+
}
|
|
236
244
|
}
|
|
237
245
|
|
|
238
246
|
async prepareQuestions () {
|
|
@@ -275,6 +283,7 @@ class RuntimeGenerator extends BaseGenerator {
|
|
|
275
283
|
} else {
|
|
276
284
|
basePath = join(this.targetDirectory, this.config.autoload || this.servicesFolder)
|
|
277
285
|
}
|
|
286
|
+
this.servicesBasePath = basePath
|
|
278
287
|
service.setTargetDirectory(join(basePath, service.config.serviceName))
|
|
279
288
|
})
|
|
280
289
|
}
|
|
@@ -496,16 +505,59 @@ class RuntimeGenerator extends BaseGenerator {
|
|
|
496
505
|
|
|
497
506
|
this.setEntryPoint(newEntrypoint)
|
|
498
507
|
runtimePkgConfigFileData.entrypoint = newEntrypoint
|
|
499
|
-
this.
|
|
500
|
-
path: '',
|
|
501
|
-
file: this.runtimeConfig,
|
|
502
|
-
contents: JSON.stringify(runtimePkgConfigFileData, null, 2)
|
|
503
|
-
})
|
|
508
|
+
this.updateRuntimeConfig(runtimePkgConfigFileData)
|
|
504
509
|
}
|
|
505
510
|
await this.writeFiles()
|
|
506
511
|
// save new env
|
|
507
512
|
await envTool.save()
|
|
508
513
|
}
|
|
514
|
+
|
|
515
|
+
async generateConfigFile () {
|
|
516
|
+
this.updateRuntimeConfig(await super.generateConfigFile())
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
async generateEnv () {
|
|
520
|
+
const serialized = await super.generateEnv()
|
|
521
|
+
|
|
522
|
+
if (serialized) {
|
|
523
|
+
this.updateRuntimeEnv(serialized)
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
getRuntimeConfigFileObject () {
|
|
528
|
+
return this.files.find(file => file.tags?.includes('runtime-config')) ?? null
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
getRuntimeEnvFileObject () {
|
|
532
|
+
return this.files.find(file => file.tags?.includes('runtime-env')) ?? null
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
updateRuntimeConfig (config) {
|
|
536
|
+
this.addFile({
|
|
537
|
+
path: '',
|
|
538
|
+
file: this.runtimeConfig,
|
|
539
|
+
contents: JSON.stringify(config, null, 2),
|
|
540
|
+
tags: ['runtime-config']
|
|
541
|
+
})
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
updateRuntimeEnv (contents) {
|
|
545
|
+
this.addFile({
|
|
546
|
+
path: '',
|
|
547
|
+
file: '.env',
|
|
548
|
+
contents,
|
|
549
|
+
tags: ['runtime-env']
|
|
550
|
+
})
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
updateConfigEntryPoint (entrypoint) {
|
|
554
|
+
// This can return null if the generator was not supposed to modify the config
|
|
555
|
+
const configObject = this.getRuntimeConfigFileObject()
|
|
556
|
+
const config = JSON.parse(configObject.contents)
|
|
557
|
+
config.entrypoint = entrypoint
|
|
558
|
+
|
|
559
|
+
this.updateRuntimeConfig(config)
|
|
560
|
+
}
|
|
509
561
|
}
|
|
510
562
|
|
|
511
563
|
class WrappedGenerator extends BaseGenerator {
|
package/lib/prom-server.js
CHANGED
|
@@ -93,6 +93,29 @@ async function startPrometheusServer (runtime, opts) {
|
|
|
93
93
|
onRequestHook = promServer.basicAuth
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
+
const readinessEndpoint = opts.readiness?.endpoint ?? DEFAULT_READINESS_ENDPOINT
|
|
97
|
+
const livenessEndpoint = opts.liveness?.endpoint ?? DEFAULT_LIVENESS_ENDPOINT
|
|
98
|
+
|
|
99
|
+
promServer.route({
|
|
100
|
+
url: '/',
|
|
101
|
+
method: 'GET',
|
|
102
|
+
logLevel: 'warn',
|
|
103
|
+
handler (req, reply) {
|
|
104
|
+
reply.type('text/plain')
|
|
105
|
+
let response = `Hello from Platformatic Prometheus Server!\nThe metrics are available at ${metricsEndpoint}.`
|
|
106
|
+
|
|
107
|
+
if (opts.readiness !== false) {
|
|
108
|
+
response += `\nThe readiness endpoint is available at ${readinessEndpoint}.`
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (opts.liveness !== false) {
|
|
112
|
+
response += `\nThe liveness endpoint is available at ${livenessEndpoint}.`
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return response
|
|
116
|
+
}
|
|
117
|
+
})
|
|
118
|
+
|
|
96
119
|
promServer.route({
|
|
97
120
|
url: metricsEndpoint,
|
|
98
121
|
method: 'GET',
|
|
@@ -112,7 +135,7 @@ async function startPrometheusServer (runtime, opts) {
|
|
|
112
135
|
const failBody = opts.readiness?.fail?.body ?? DEFAULT_READINESS_FAIL_BODY
|
|
113
136
|
|
|
114
137
|
promServer.route({
|
|
115
|
-
url:
|
|
138
|
+
url: readinessEndpoint,
|
|
116
139
|
method: 'GET',
|
|
117
140
|
logLevel: 'warn',
|
|
118
141
|
handler: async (req, reply) => {
|
|
@@ -151,7 +174,7 @@ async function startPrometheusServer (runtime, opts) {
|
|
|
151
174
|
const failBody = opts.liveness?.fail?.body ?? DEFAULT_LIVENESS_FAIL_BODY
|
|
152
175
|
|
|
153
176
|
promServer.route({
|
|
154
|
-
url:
|
|
177
|
+
url: livenessEndpoint,
|
|
155
178
|
method: 'GET',
|
|
156
179
|
logLevel: 'warn',
|
|
157
180
|
handler: async (req, reply) => {
|
package/lib/runtime.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const { ITC } = require('@platformatic/itc')
|
|
4
|
-
const { ensureLoggableError, executeWithTimeout, deepmerge } = require('@platformatic/utils')
|
|
4
|
+
const { features, ensureLoggableError, executeWithTimeout, deepmerge } = require('@platformatic/utils')
|
|
5
5
|
const { once, EventEmitter } = require('node:events')
|
|
6
6
|
const { createReadStream, watch, existsSync } = require('node:fs')
|
|
7
7
|
const { readdir, readFile, stat, access } = require('node:fs/promises')
|
|
@@ -50,6 +50,7 @@ const COLLECT_METRICS_TIMEOUT = 1000
|
|
|
50
50
|
|
|
51
51
|
const MAX_BOOTSTRAP_ATTEMPTS = 5
|
|
52
52
|
const IMMEDIATE_RESTART_MAX_THRESHOLD = 10
|
|
53
|
+
const MAX_WORKERS = 100
|
|
53
54
|
|
|
54
55
|
const telemetryPath = require.resolve('@platformatic/telemetry')
|
|
55
56
|
const openTelemetrySetupPath = join(telemetryPath, '..', 'lib', 'node-telemetry.js')
|
|
@@ -129,7 +130,19 @@ class Runtime extends EventEmitter {
|
|
|
129
130
|
|
|
130
131
|
this.#isProduction = this.#configManager.args?.production ?? false
|
|
131
132
|
this.#servicesIds = config.services.map(service => service.id)
|
|
132
|
-
|
|
133
|
+
|
|
134
|
+
const workersConfig = []
|
|
135
|
+
for (const service of config.services) {
|
|
136
|
+
const count = service.workers ?? this.#configManager.current.workers
|
|
137
|
+
if (count > 1 && service.entrypoint && !features.node.reusePort) {
|
|
138
|
+
this.logger.warn(`"${service.id}" is set as the entrypoint, but reusePort is not available in your OS; setting workers to 1 instead of ${count}`)
|
|
139
|
+
workersConfig.push({ id: service.id, workers: 1 })
|
|
140
|
+
} else {
|
|
141
|
+
workersConfig.push({ id: service.id, workers: count })
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
this.#workers.configure(workersConfig)
|
|
133
146
|
|
|
134
147
|
if (this.#isProduction) {
|
|
135
148
|
this.#env['PLT_DEV'] = 'false'
|
|
@@ -198,17 +211,7 @@ class Runtime extends EventEmitter {
|
|
|
198
211
|
await this.closeAndThrow(e)
|
|
199
212
|
}
|
|
200
213
|
|
|
201
|
-
|
|
202
|
-
const interceptors = [this.#meshInterceptor]
|
|
203
|
-
|
|
204
|
-
if (config.httpCache) {
|
|
205
|
-
this.#sharedHttpCache = await createSharedStore(this.#configManager.dirname, config.httpCache)
|
|
206
|
-
interceptors.push(
|
|
207
|
-
undiciInterceptors.cache({ store: this.#sharedHttpCache, methods: config.httpCache.methods ?? ['GET', 'HEAD'] })
|
|
208
|
-
)
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
this.#dispatcher = new Agent(dispatcherOpts).compose(interceptors)
|
|
214
|
+
await this.#setDispatcher(config.undici)
|
|
212
215
|
|
|
213
216
|
if (config.scheduler) {
|
|
214
217
|
this.#scheduler = startScheduler(config.scheduler, this.#dispatcher, logger)
|
|
@@ -477,6 +480,22 @@ class Runtime extends EventEmitter {
|
|
|
477
480
|
}
|
|
478
481
|
}
|
|
479
482
|
|
|
483
|
+
async updateUndiciInterceptors (undiciConfig) {
|
|
484
|
+
this.#configManager.current.undici = undiciConfig
|
|
485
|
+
|
|
486
|
+
const promises = []
|
|
487
|
+
for (const worker of this.#workers.values()) {
|
|
488
|
+
promises.push(sendViaITC(worker, 'updateUndiciInterceptors', undiciConfig))
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
const results = await Promise.allSettled(promises)
|
|
492
|
+
for (const result of results) {
|
|
493
|
+
if (result.status === 'rejected') {
|
|
494
|
+
throw result.reason
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
|
|
480
499
|
startCollectingMetrics () {
|
|
481
500
|
this.#metrics = []
|
|
482
501
|
this.#metricsTimeout = setInterval(async () => {
|
|
@@ -1041,6 +1060,24 @@ class Runtime extends EventEmitter {
|
|
|
1041
1060
|
return super.emit(event, payload)
|
|
1042
1061
|
}
|
|
1043
1062
|
|
|
1063
|
+
async #setDispatcher (undiciConfig) {
|
|
1064
|
+
const config = this.#configManager.current
|
|
1065
|
+
|
|
1066
|
+
const dispatcherOpts = { ...undiciConfig }
|
|
1067
|
+
const interceptors = [this.#meshInterceptor]
|
|
1068
|
+
|
|
1069
|
+
if (config.httpCache) {
|
|
1070
|
+
this.#sharedHttpCache = await createSharedStore(this.#configManager.dirname, config.httpCache)
|
|
1071
|
+
interceptors.push(
|
|
1072
|
+
undiciInterceptors.cache({
|
|
1073
|
+
store: this.#sharedHttpCache,
|
|
1074
|
+
methods: config.httpCache.methods ?? ['GET', 'HEAD']
|
|
1075
|
+
})
|
|
1076
|
+
)
|
|
1077
|
+
}
|
|
1078
|
+
this.#dispatcher = new Agent(dispatcherOpts).compose(interceptors)
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1044
1081
|
#updateStatus (status, args) {
|
|
1045
1082
|
this.#status = status
|
|
1046
1083
|
this.emit(status, args)
|
|
@@ -1279,7 +1316,7 @@ class Runtime extends EventEmitter {
|
|
|
1279
1316
|
}
|
|
1280
1317
|
|
|
1281
1318
|
// This must be done here as the dependencies are filled above
|
|
1282
|
-
worker[kConfig] = { ...serviceConfig, health }
|
|
1319
|
+
worker[kConfig] = { ...serviceConfig, health, workers: workersCount }
|
|
1283
1320
|
worker[kWorkerStatus] = 'init'
|
|
1284
1321
|
this.emit('service:worker:init', eventPayload)
|
|
1285
1322
|
|
|
@@ -1809,6 +1846,86 @@ class Runtime extends EventEmitter {
|
|
|
1809
1846
|
await immediate()
|
|
1810
1847
|
}
|
|
1811
1848
|
}
|
|
1849
|
+
|
|
1850
|
+
async getServiceResourcesInfo (id) {
|
|
1851
|
+
const workers = this.#workers.getCount(id)
|
|
1852
|
+
return { workers }
|
|
1853
|
+
}
|
|
1854
|
+
|
|
1855
|
+
async #updateWorkerCount (serviceId, workers) {
|
|
1856
|
+
this.#configManager.current.services.find(s => s.id === serviceId).workers = workers
|
|
1857
|
+
const service = await this.#getServiceById(serviceId)
|
|
1858
|
+
this.#workers.setCount(serviceId, workers)
|
|
1859
|
+
service[kConfig].workers = workers
|
|
1860
|
+
|
|
1861
|
+
const promises = []
|
|
1862
|
+
for (const [workerId, worker] of this.#workers.entries()) {
|
|
1863
|
+
if (workerId.startsWith(`${serviceId}:`)) {
|
|
1864
|
+
promises.push(sendViaITC(worker, 'updateWorkersCount', { serviceId, workers }))
|
|
1865
|
+
}
|
|
1866
|
+
}
|
|
1867
|
+
|
|
1868
|
+
const results = await Promise.allSettled(promises)
|
|
1869
|
+
for (const result of results) {
|
|
1870
|
+
if (result.status === 'rejected') {
|
|
1871
|
+
throw result.reason
|
|
1872
|
+
}
|
|
1873
|
+
}
|
|
1874
|
+
}
|
|
1875
|
+
|
|
1876
|
+
async updateServicesResources (updates) {
|
|
1877
|
+
if (this.#status === 'stopping' || this.#status === 'closed') return
|
|
1878
|
+
|
|
1879
|
+
if (!Array.isArray(updates)) {
|
|
1880
|
+
throw new errors.InvalidArgumentError('updates', 'must be an array')
|
|
1881
|
+
}
|
|
1882
|
+
if (updates.length === 0) {
|
|
1883
|
+
throw new errors.InvalidArgumentError('updates', 'must have at least one element')
|
|
1884
|
+
}
|
|
1885
|
+
|
|
1886
|
+
const config = this.#configManager.current
|
|
1887
|
+
|
|
1888
|
+
for (const update of updates) {
|
|
1889
|
+
const { service: serviceId, workers } = update
|
|
1890
|
+
|
|
1891
|
+
if (typeof workers !== 'number') {
|
|
1892
|
+
throw new errors.InvalidArgumentError('workers', 'must be a number')
|
|
1893
|
+
}
|
|
1894
|
+
if (!serviceId) {
|
|
1895
|
+
throw new errors.InvalidArgumentError('service', 'must be a string')
|
|
1896
|
+
}
|
|
1897
|
+
if (workers <= 0) {
|
|
1898
|
+
throw new errors.InvalidArgumentError('workers', 'must be greater than 0')
|
|
1899
|
+
}
|
|
1900
|
+
if (workers > MAX_WORKERS) {
|
|
1901
|
+
throw new errors.InvalidArgumentError('workers', `must be less than ${MAX_WORKERS}`)
|
|
1902
|
+
}
|
|
1903
|
+
const serviceConfig = config.services.find(s => s.id === serviceId)
|
|
1904
|
+
if (!serviceConfig) {
|
|
1905
|
+
throw new errors.ServiceNotFoundError(serviceId, Array.from(this.#servicesIds).join(', '))
|
|
1906
|
+
}
|
|
1907
|
+
|
|
1908
|
+
const { workers: currentWorkers } = await this.getServiceResourcesInfo(serviceId)
|
|
1909
|
+
if (currentWorkers === workers) {
|
|
1910
|
+
this.logger.warn({ serviceId, workers }, 'No change in the number of workers for service')
|
|
1911
|
+
continue
|
|
1912
|
+
}
|
|
1913
|
+
|
|
1914
|
+
if (currentWorkers < workers) {
|
|
1915
|
+
await this.#updateWorkerCount(serviceId, workers)
|
|
1916
|
+
for (let i = currentWorkers; i < workers; i++) {
|
|
1917
|
+
await this.#setupWorker(config, serviceConfig, workers, serviceId, i)
|
|
1918
|
+
await this.#startWorker(config, serviceConfig, workers, serviceId, i, false, 0)
|
|
1919
|
+
}
|
|
1920
|
+
} else {
|
|
1921
|
+
for (let i = currentWorkers - 1; i >= workers; i--) {
|
|
1922
|
+
// keep the current workers count until the workers are stopped
|
|
1923
|
+
await this.#stopWorker(currentWorkers, serviceId, i, false)
|
|
1924
|
+
}
|
|
1925
|
+
await this.#updateWorkerCount(serviceId, workers)
|
|
1926
|
+
}
|
|
1927
|
+
}
|
|
1928
|
+
}
|
|
1812
1929
|
}
|
|
1813
1930
|
|
|
1814
1931
|
module.exports = { Runtime }
|
|
@@ -1,19 +1,208 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const { join } = require('node:path')
|
|
4
|
+
const { workerData, parentPort } = require('node:worker_threads')
|
|
3
5
|
const { pathToFileURL } = require('node:url')
|
|
6
|
+
const { createRequire } = require('@platformatic/utils')
|
|
7
|
+
const { setGlobalDispatcher, Client, Pool, Agent } = require('undici')
|
|
8
|
+
const { wire } = require('undici-thread-interceptor')
|
|
9
|
+
const { createTelemetryThreadInterceptorHooks } = require('@platformatic/telemetry')
|
|
10
|
+
const { RemoteCacheStore, httpCacheInterceptor } = require('./http-cache')
|
|
11
|
+
const { kInterceptors } = require('./symbols')
|
|
4
12
|
|
|
5
|
-
async function
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
|
|
13
|
+
async function setDispatcher (runtimeConfig) {
|
|
14
|
+
const threadDispatcher = createThreadInterceptor(runtimeConfig)
|
|
15
|
+
const threadInterceptor = threadDispatcher.interceptor
|
|
16
|
+
|
|
17
|
+
let cacheInterceptor = null
|
|
18
|
+
if (runtimeConfig.httpCache) {
|
|
19
|
+
cacheInterceptor = createHttpCacheInterceptor(runtimeConfig)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
let userInterceptors = []
|
|
23
|
+
if (Array.isArray(runtimeConfig.undici?.interceptors)) {
|
|
24
|
+
const _require = createRequire(join(workerData.dirname, 'package.json'))
|
|
25
|
+
userInterceptors = await loadInterceptors(_require, runtimeConfig.undici.interceptors)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const dispatcherOpts = await getDispatcherOpts(runtimeConfig.undici)
|
|
29
|
+
|
|
30
|
+
setGlobalDispatcher(
|
|
31
|
+
new Agent(dispatcherOpts).compose(
|
|
32
|
+
[
|
|
33
|
+
threadInterceptor,
|
|
34
|
+
...userInterceptors,
|
|
35
|
+
cacheInterceptor
|
|
36
|
+
].filter(Boolean)
|
|
37
|
+
)
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
return { threadDispatcher }
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async function updateUndiciInterceptors (undiciConfig) {
|
|
44
|
+
const updatableInterceptors = globalThis[kInterceptors]
|
|
45
|
+
if (!updatableInterceptors) return
|
|
46
|
+
|
|
47
|
+
if (Array.isArray(undiciConfig?.interceptors)) {
|
|
48
|
+
for (const interceptorConfig of undiciConfig.interceptors) {
|
|
49
|
+
const { module, options } = interceptorConfig
|
|
50
|
+
|
|
51
|
+
const interceptorCtx = updatableInterceptors[module]
|
|
52
|
+
if (!interceptorCtx) continue
|
|
53
|
+
|
|
54
|
+
const { createInterceptor, updateInterceptor } = interceptorCtx
|
|
55
|
+
updateInterceptor(createInterceptor(options))
|
|
56
|
+
}
|
|
57
|
+
} else {
|
|
58
|
+
for (const key of ['Agent', 'Pool', 'Client']) {
|
|
59
|
+
const interceptorConfigs = undiciConfig.interceptors[key]
|
|
60
|
+
if (!interceptorConfigs) continue
|
|
61
|
+
|
|
62
|
+
for (const interceptorConfig of interceptorConfigs) {
|
|
63
|
+
const { module, options } = interceptorConfig
|
|
64
|
+
|
|
65
|
+
const interceptorCtx = updatableInterceptors[key][module]
|
|
66
|
+
if (!interceptorCtx) continue
|
|
67
|
+
|
|
68
|
+
const { createInterceptor, updateInterceptor } = interceptorCtx
|
|
69
|
+
updateInterceptor(createInterceptor(options))
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
9
73
|
}
|
|
10
74
|
|
|
11
|
-
function
|
|
75
|
+
function createUpdatableInterceptor (originInterceptor) {
|
|
76
|
+
let originalDispatcher = null
|
|
77
|
+
let originalDispatch = null
|
|
78
|
+
|
|
79
|
+
function updatableInterceptor (dispatch) {
|
|
80
|
+
originalDispatch = dispatch
|
|
81
|
+
originalDispatcher = originInterceptor(dispatch)
|
|
82
|
+
|
|
83
|
+
return function dispatcher (opts, handler) {
|
|
84
|
+
return originalDispatcher(opts, handler)
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function updateInterceptor (newInterceptor) {
|
|
89
|
+
originalDispatcher = newInterceptor(originalDispatch)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return { updatableInterceptor, updateInterceptor }
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async function loadInterceptors (_require, interceptorsConfigs, key) {
|
|
12
96
|
return Promise.all(
|
|
13
|
-
|
|
14
|
-
return loadInterceptor(_require,
|
|
97
|
+
interceptorsConfigs.map(async (interceptorConfig) => {
|
|
98
|
+
return loadInterceptor(_require, interceptorConfig, key)
|
|
15
99
|
})
|
|
16
100
|
)
|
|
17
101
|
}
|
|
18
102
|
|
|
19
|
-
|
|
103
|
+
async function loadInterceptor (_require, interceptorConfig, key) {
|
|
104
|
+
let updatableInterceptors = globalThis[kInterceptors]
|
|
105
|
+
if (!updatableInterceptors) {
|
|
106
|
+
updatableInterceptors = {}
|
|
107
|
+
globalThis[kInterceptors] = updatableInterceptors
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const { module, options } = interceptorConfig
|
|
111
|
+
|
|
112
|
+
const url = pathToFileURL(_require.resolve(module))
|
|
113
|
+
const createInterceptor = (await import(url)).default
|
|
114
|
+
const interceptor = createInterceptor(options)
|
|
115
|
+
|
|
116
|
+
const {
|
|
117
|
+
updatableInterceptor,
|
|
118
|
+
updateInterceptor
|
|
119
|
+
} = createUpdatableInterceptor(interceptor)
|
|
120
|
+
|
|
121
|
+
const interceptorCtx = { createInterceptor, updateInterceptor }
|
|
122
|
+
|
|
123
|
+
if (key !== undefined) {
|
|
124
|
+
if (!updatableInterceptors[key]) {
|
|
125
|
+
updatableInterceptors[key] = {}
|
|
126
|
+
}
|
|
127
|
+
updatableInterceptors[key][module] = interceptorCtx
|
|
128
|
+
} else {
|
|
129
|
+
updatableInterceptors[module] = interceptorCtx
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return updatableInterceptor
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
async function getDispatcherOpts (undiciConfig) {
|
|
136
|
+
const dispatcherOpts = { ...undiciConfig }
|
|
137
|
+
|
|
138
|
+
const interceptorsConfigs = undiciConfig?.interceptors
|
|
139
|
+
if (!interceptorsConfigs || Array.isArray(interceptorsConfigs)) {
|
|
140
|
+
return dispatcherOpts
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const _require = createRequire(join(workerData.dirname, 'package.json'))
|
|
144
|
+
|
|
145
|
+
const clientInterceptors = []
|
|
146
|
+
const poolInterceptors = []
|
|
147
|
+
|
|
148
|
+
for (const key of ['Agent', 'Pool', 'Client']) {
|
|
149
|
+
const interceptorConfig = undiciConfig.interceptors[key]
|
|
150
|
+
if (!interceptorConfig) continue
|
|
151
|
+
|
|
152
|
+
const interceptors = await loadInterceptors(_require, interceptorConfig, key)
|
|
153
|
+
if (key === 'Agent') {
|
|
154
|
+
clientInterceptors.push(...interceptors)
|
|
155
|
+
poolInterceptors.push(...interceptors)
|
|
156
|
+
}
|
|
157
|
+
if (key === 'Pool') {
|
|
158
|
+
poolInterceptors.push(...interceptors)
|
|
159
|
+
}
|
|
160
|
+
if (key === 'Client') {
|
|
161
|
+
clientInterceptors.push(...interceptors)
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
dispatcherOpts.factory = (origin, opts) => {
|
|
166
|
+
return opts && opts.connections === 1
|
|
167
|
+
? new Client(origin, opts).compose(clientInterceptors)
|
|
168
|
+
: new Pool(origin, opts).compose(poolInterceptors)
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return dispatcherOpts
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function createThreadInterceptor (runtimeConfig) {
|
|
175
|
+
const telemetry = runtimeConfig.telemetry
|
|
176
|
+
const hooks = telemetry ? createTelemetryThreadInterceptorHooks() : {}
|
|
177
|
+
const threadDispatcher = wire({
|
|
178
|
+
// Specifying the domain is critical to avoid flooding the DNS
|
|
179
|
+
// with requests for a domain that's never going to exist.
|
|
180
|
+
domain: '.plt.local',
|
|
181
|
+
port: parentPort,
|
|
182
|
+
timeout: runtimeConfig.serviceTimeout,
|
|
183
|
+
...hooks
|
|
184
|
+
})
|
|
185
|
+
return threadDispatcher
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function createHttpCacheInterceptor (runtimeConfig) {
|
|
189
|
+
const cacheInterceptor = httpCacheInterceptor({
|
|
190
|
+
store: new RemoteCacheStore({
|
|
191
|
+
onRequest: opts => {
|
|
192
|
+
globalThis.platformatic?.onHttpCacheRequest?.(opts)
|
|
193
|
+
},
|
|
194
|
+
onCacheHit: opts => {
|
|
195
|
+
globalThis.platformatic?.onHttpCacheHit?.(opts)
|
|
196
|
+
},
|
|
197
|
+
onCacheMiss: opts => {
|
|
198
|
+
globalThis.platformatic?.onHttpCacheMiss?.(opts)
|
|
199
|
+
},
|
|
200
|
+
logger: globalThis.platformatic.logger
|
|
201
|
+
}),
|
|
202
|
+
methods: runtimeConfig.httpCache.methods ?? ['GET', 'HEAD'],
|
|
203
|
+
logger: globalThis.platformatic.logger
|
|
204
|
+
})
|
|
205
|
+
return cacheInterceptor
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
module.exports = { setDispatcher, updateUndiciInterceptors }
|
package/lib/worker/itc.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const { once } = require('node:events')
|
|
4
|
-
const { parentPort } = require('node:worker_threads')
|
|
4
|
+
const { parentPort, workerData } = require('node:worker_threads')
|
|
5
5
|
|
|
6
6
|
const { ITC } = require('@platformatic/itc')
|
|
7
7
|
const { Unpromise } = require('@watchable/unpromise')
|
|
8
8
|
|
|
9
9
|
const errors = require('../errors')
|
|
10
|
+
const { updateUndiciInterceptors } = require('./interceptors')
|
|
10
11
|
const { kITC, kId, kServiceId, kWorkerId } = require('./symbols')
|
|
11
12
|
|
|
12
13
|
async function safeHandleInITC (worker, fn) {
|
|
@@ -109,6 +110,18 @@ function setupITC (app, service, dispatcher) {
|
|
|
109
110
|
return app.stackable.inject(injectParams)
|
|
110
111
|
},
|
|
111
112
|
|
|
113
|
+
async updateUndiciInterceptors (undiciConfig) {
|
|
114
|
+
await updateUndiciInterceptors(undiciConfig)
|
|
115
|
+
},
|
|
116
|
+
|
|
117
|
+
async updateWorkersCount (data) {
|
|
118
|
+
const { serviceId, workers } = data
|
|
119
|
+
const w = workerData.config.serviceMap.get(serviceId)
|
|
120
|
+
if (w) { w.workers = workers }
|
|
121
|
+
workerData.serviceConfig.workers = workers
|
|
122
|
+
workerData.worker.count = workers
|
|
123
|
+
},
|
|
124
|
+
|
|
112
125
|
getStatus () {
|
|
113
126
|
return app.getStatus()
|
|
114
127
|
},
|
package/lib/worker/main.js
CHANGED
|
@@ -2,16 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
const { EventEmitter } = require('node:events')
|
|
4
4
|
const { hostname } = require('node:os')
|
|
5
|
-
const {
|
|
6
|
-
const {
|
|
5
|
+
const { resolve } = require('node:path')
|
|
6
|
+
const { workerData, threadId } = require('node:worker_threads')
|
|
7
7
|
const { pathToFileURL } = require('node:url')
|
|
8
8
|
const inspector = require('node:inspector')
|
|
9
9
|
const diagnosticChannel = require('node:diagnostics_channel')
|
|
10
10
|
const { ServerResponse } = require('node:http')
|
|
11
11
|
|
|
12
|
-
const { createTelemetryThreadInterceptorHooks } = require('@platformatic/telemetry')
|
|
13
12
|
const {
|
|
14
|
-
createRequire,
|
|
15
13
|
disablePinoDirectWrite,
|
|
16
14
|
ensureFlushedWorkerStdio,
|
|
17
15
|
executeWithTimeout,
|
|
@@ -22,14 +20,11 @@ const {
|
|
|
22
20
|
} = require('@platformatic/utils')
|
|
23
21
|
const dotenv = require('dotenv')
|
|
24
22
|
const pino = require('pino')
|
|
25
|
-
const { fetch
|
|
26
|
-
const { wire } = require('undici-thread-interceptor')
|
|
27
|
-
const undici = require('undici')
|
|
23
|
+
const { fetch } = require('undici')
|
|
28
24
|
|
|
29
|
-
const { RemoteCacheStore, httpCacheInterceptor } = require('./http-cache')
|
|
30
25
|
const { PlatformaticApp } = require('./app')
|
|
31
26
|
const { setupITC } = require('./itc')
|
|
32
|
-
const {
|
|
27
|
+
const { setDispatcher } = require('./interceptors')
|
|
33
28
|
const { kId, kITC, kStderrMarker } = require('./symbols')
|
|
34
29
|
|
|
35
30
|
function handleUnhandled (app, type, err) {
|
|
@@ -138,85 +133,7 @@ async function main () {
|
|
|
138
133
|
Object.assign(process.env, service.env)
|
|
139
134
|
}
|
|
140
135
|
|
|
141
|
-
|
|
142
|
-
const interceptors = {}
|
|
143
|
-
const composedInterceptors = []
|
|
144
|
-
|
|
145
|
-
if (config.undici?.interceptors) {
|
|
146
|
-
const _require = createRequire(join(workerData.dirname, 'package.json'))
|
|
147
|
-
for (const key of ['Agent', 'Pool', 'Client']) {
|
|
148
|
-
if (config.undici.interceptors[key]) {
|
|
149
|
-
interceptors[key] = await loadInterceptors(_require, config.undici.interceptors[key])
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
if (Array.isArray(config.undici.interceptors)) {
|
|
154
|
-
composedInterceptors.push(...(await loadInterceptors(_require, config.undici.interceptors)))
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
const dispatcherOpts = { ...config.undici }
|
|
159
|
-
|
|
160
|
-
if (Object.keys(interceptors).length > 0) {
|
|
161
|
-
const clientInterceptors = []
|
|
162
|
-
const poolInterceptors = []
|
|
163
|
-
|
|
164
|
-
if (interceptors.Agent) {
|
|
165
|
-
clientInterceptors.push(...interceptors.Agent)
|
|
166
|
-
poolInterceptors.push(...interceptors.Agent)
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
if (interceptors.Pool) {
|
|
170
|
-
poolInterceptors.push(...interceptors.Pool)
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
if (interceptors.Client) {
|
|
174
|
-
clientInterceptors.push(...interceptors.Client)
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
dispatcherOpts.factory = (origin, opts) => {
|
|
178
|
-
return opts && opts.connections === 1
|
|
179
|
-
? new undici.Client(origin, opts).compose(clientInterceptors)
|
|
180
|
-
: new undici.Pool(origin, opts).compose(poolInterceptors)
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
setGlobalDispatcher(new Agent(dispatcherOpts))
|
|
185
|
-
|
|
186
|
-
const { telemetry } = service
|
|
187
|
-
const hooks = telemetry ? createTelemetryThreadInterceptorHooks() : {}
|
|
188
|
-
// Setup mesh networker
|
|
189
|
-
const threadDispatcher = wire({
|
|
190
|
-
// Specifying the domain is critical to avoid flooding the DNS
|
|
191
|
-
// with requests for a domain that's never going to exist.
|
|
192
|
-
domain: '.plt.local',
|
|
193
|
-
port: parentPort,
|
|
194
|
-
timeout: config.serviceTimeout,
|
|
195
|
-
...hooks
|
|
196
|
-
})
|
|
197
|
-
|
|
198
|
-
if (config.httpCache) {
|
|
199
|
-
const cacheInterceptor = httpCacheInterceptor({
|
|
200
|
-
store: new RemoteCacheStore({
|
|
201
|
-
onRequest: opts => {
|
|
202
|
-
globalThis.platformatic?.onHttpCacheRequest?.(opts)
|
|
203
|
-
},
|
|
204
|
-
onCacheHit: opts => {
|
|
205
|
-
globalThis.platformatic?.onHttpCacheHit?.(opts)
|
|
206
|
-
},
|
|
207
|
-
onCacheMiss: opts => {
|
|
208
|
-
globalThis.platformatic?.onHttpCacheMiss?.(opts)
|
|
209
|
-
},
|
|
210
|
-
logger: globalThis.platformatic.logger
|
|
211
|
-
}),
|
|
212
|
-
methods: config.httpCache.methods ?? ['GET', 'HEAD'],
|
|
213
|
-
logger: globalThis.platformatic.logger
|
|
214
|
-
})
|
|
215
|
-
composedInterceptors.push(cacheInterceptor)
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
const globalDispatcher = getGlobalDispatcher()
|
|
219
|
-
setGlobalDispatcher(globalDispatcher.compose(composedInterceptors))
|
|
136
|
+
const { threadDispatcher } = await setDispatcher(config)
|
|
220
137
|
|
|
221
138
|
// If the service is an entrypoint and runtime server config is defined, use it.
|
|
222
139
|
let serverConfig = null
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const { features } = require('@platformatic/utils')
|
|
4
|
-
|
|
5
3
|
class RoundRobinMap extends Map {
|
|
6
4
|
#instances
|
|
7
5
|
|
|
@@ -14,18 +12,11 @@ class RoundRobinMap extends Map {
|
|
|
14
12
|
return { ...this.#instances }
|
|
15
13
|
}
|
|
16
14
|
|
|
17
|
-
|
|
18
|
-
configure (services, defaultInstances, production) {
|
|
15
|
+
configure (services) {
|
|
19
16
|
this.#instances = {}
|
|
20
17
|
|
|
21
18
|
for (const service of services) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
if (!production || (service.entrypoint && !features.node.reusePort)) {
|
|
25
|
-
count = 1
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
this.#instances[service.id] = { next: 0, count }
|
|
19
|
+
this.#instances[service.id] = { next: 0, count: service.workers }
|
|
29
20
|
}
|
|
30
21
|
}
|
|
31
22
|
|
|
@@ -33,6 +24,10 @@ class RoundRobinMap extends Map {
|
|
|
33
24
|
return this.#instances[service].count
|
|
34
25
|
}
|
|
35
26
|
|
|
27
|
+
setCount (service, count) {
|
|
28
|
+
this.#instances[service].count = count
|
|
29
|
+
}
|
|
30
|
+
|
|
36
31
|
next (service) {
|
|
37
32
|
if (!this.#instances[service]) {
|
|
38
33
|
return undefined
|
package/lib/worker/symbols.js
CHANGED
|
@@ -8,6 +8,7 @@ const kWorkerId = Symbol.for('plt.runtime.worker.id')
|
|
|
8
8
|
const kITC = Symbol.for('plt.runtime.itc')
|
|
9
9
|
const kHealthCheckTimer = Symbol.for('plt.runtime.worker.healthCheckTimer')
|
|
10
10
|
const kWorkerStatus = Symbol('plt.runtime.worker.status')
|
|
11
|
+
const kInterceptors = Symbol.for('plt.runtime.worker.interceptors')
|
|
11
12
|
|
|
12
13
|
// This string marker should be safe to use since it belongs to Unicode private area
|
|
13
14
|
const kStderrMarker = '\ue002'
|
|
@@ -21,5 +22,6 @@ module.exports = {
|
|
|
21
22
|
kITC,
|
|
22
23
|
kHealthCheckTimer,
|
|
23
24
|
kWorkerStatus,
|
|
24
|
-
kStderrMarker
|
|
25
|
+
kStderrMarker,
|
|
26
|
+
kInterceptors
|
|
25
27
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@platformatic/runtime",
|
|
3
|
-
"version": "2.67.0
|
|
3
|
+
"version": "2.67.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -37,12 +37,12 @@
|
|
|
37
37
|
"typescript": "^5.5.4",
|
|
38
38
|
"undici-oidc-interceptor": "^0.5.0",
|
|
39
39
|
"why-is-node-running": "^2.2.2",
|
|
40
|
-
"@platformatic/composer": "2.67.0
|
|
41
|
-
"@platformatic/db": "2.67.0
|
|
42
|
-
"@platformatic/node": "2.67.0
|
|
43
|
-
"@platformatic/service": "2.67.0
|
|
44
|
-
"@platformatic/sql-
|
|
45
|
-
"@platformatic/sql-
|
|
40
|
+
"@platformatic/composer": "2.67.0",
|
|
41
|
+
"@platformatic/db": "2.67.0",
|
|
42
|
+
"@platformatic/node": "2.67.0",
|
|
43
|
+
"@platformatic/service": "2.67.0",
|
|
44
|
+
"@platformatic/sql-mapper": "2.67.0",
|
|
45
|
+
"@platformatic/sql-graphql": "2.67.0"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
48
|
"@fastify/accepts": "^5.0.0",
|
|
@@ -77,21 +77,21 @@
|
|
|
77
77
|
"undici": "^7.0.0",
|
|
78
78
|
"undici-thread-interceptor": "^0.13.1",
|
|
79
79
|
"ws": "^8.16.0",
|
|
80
|
-
"@platformatic/basic": "2.67.0
|
|
81
|
-
"@platformatic/config": "2.67.0
|
|
82
|
-
"@platformatic/generators": "2.67.0
|
|
83
|
-
"@platformatic/itc": "2.67.0
|
|
84
|
-
"@platformatic/telemetry": "2.67.0
|
|
85
|
-
"@platformatic/ts-compiler": "2.67.0
|
|
86
|
-
"@platformatic/utils": "2.67.0
|
|
80
|
+
"@platformatic/basic": "2.67.0",
|
|
81
|
+
"@platformatic/config": "2.67.0",
|
|
82
|
+
"@platformatic/generators": "2.67.0",
|
|
83
|
+
"@platformatic/itc": "2.67.0",
|
|
84
|
+
"@platformatic/telemetry": "2.67.0",
|
|
85
|
+
"@platformatic/ts-compiler": "2.67.0",
|
|
86
|
+
"@platformatic/utils": "2.67.0"
|
|
87
87
|
},
|
|
88
88
|
"scripts": {
|
|
89
|
-
"test": "pnpm run lint && borp --concurrency=1 --timeout=
|
|
89
|
+
"test": "pnpm run lint && borp --concurrency=1 --timeout=600000 && tsd",
|
|
90
90
|
"test:main": "borp --concurrency=1 --timeout=300000 test/*.test.js test/*.test.mjs test/versions/*.test.js test/versions/*.test.mjs",
|
|
91
91
|
"test:api": "borp --concurrency=1 --timeout=300000 test/api/*.test.js test/api/*.test.mjs test/management-api/*.test.js test/management-api/*.test.mjs",
|
|
92
92
|
"test:cli": "borp --concurrency=1 --timeout=300000 test/cli/*.test.js test/cli/*.test.mjs test/cli/**/*.test.js test/cli/**/*.test.mjs",
|
|
93
93
|
"test:start": "borp --concurrency=1 --timeout=300000 test/start/*.test.js test/start/*.test.mjs",
|
|
94
|
-
"test:multiple-workers": "borp --concurrency=1 --timeout=
|
|
94
|
+
"test:multiple-workers": "borp --concurrency=1 --timeout=600000 test/multiple-workers/*.test.js test/multiple-workers/*.test.mjs",
|
|
95
95
|
"test:types": "tsd",
|
|
96
96
|
"coverage": "pnpm run lint && borp -X fixtures -X test -C --concurrency=1 --timeout=300000 && tsd",
|
|
97
97
|
"gen-schema": "node lib/schema.js > schema.json",
|
package/schema.json
CHANGED
package/undefined
DELETED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
{"level":30,"time":1747909640025,"pid":84550,"hostname":"work","msg":"Starting the service \"service\"..."}
|
|
2
|
-
{"level":30,"time":1747909640055,"pid":84550,"hostname":"work","msg":"Started the service \"service\"..."}
|
|
3
|
-
{"level":30,"time":1747909640055,"pid":84550,"hostname":"work","msg":"Platformatic is now listening at http://127.0.0.1:41619"}
|
|
4
|
-
{"level":30,"time":1747909640066,"pid":84550,"hostname":"work","msg":"Stopping the service \"service\"..."}
|
|
5
|
-
{"level":30,"time":1747909640069,"pid":84550,"hostname":"work","msg":"Stopped the service \"service\"..."}
|