@platformatic/runtime 2.67.0-alpha.0 → 2.67.0-alpha.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 CHANGED
@@ -5,7 +5,7 @@
5
5
  * and run json-schema-to-typescript to regenerate this file.
6
6
  */
7
7
 
8
- export type HttpsSchemasPlatformaticDevPlatformaticRuntime2670Alpha0Json = {
8
+ export type HttpsSchemasPlatformaticDevPlatformaticRuntime2670Alpha1Json = {
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, opts }) {
254
+ async function wrapConfigInRuntimeConfig ({ configManager, args }) {
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, opts }) {
272
272
  const wrapperConfig = {
273
273
  $schema: schema.$id,
274
274
  server,
275
- watch: !args?.production,
275
+ watch: true,
276
276
  ...omitProperties(configManager.current.runtime ?? {}, runtimeUnwrappablePropertiesList),
277
277
  entrypoint: serviceId,
278
278
  services: [
@@ -298,7 +298,7 @@ async function wrapConfigInRuntimeConfig ({ configManager, args, opts }) {
298
298
  }
299
299
  })
300
300
 
301
- await cm.parseAndValidate(true, [], opts)
301
+ await cm.parseAndValidate()
302
302
 
303
303
  return cm
304
304
  }
@@ -93,29 +93,6 @@ 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
-
119
96
  promServer.route({
120
97
  url: metricsEndpoint,
121
98
  method: 'GET',
@@ -135,7 +112,7 @@ async function startPrometheusServer (runtime, opts) {
135
112
  const failBody = opts.readiness?.fail?.body ?? DEFAULT_READINESS_FAIL_BODY
136
113
 
137
114
  promServer.route({
138
- url: readinessEndpoint,
115
+ url: opts.readiness?.endpoint ?? DEFAULT_READINESS_ENDPOINT,
139
116
  method: 'GET',
140
117
  logLevel: 'warn',
141
118
  handler: async (req, reply) => {
@@ -174,7 +151,7 @@ async function startPrometheusServer (runtime, opts) {
174
151
  const failBody = opts.liveness?.fail?.body ?? DEFAULT_LIVENESS_FAIL_BODY
175
152
 
176
153
  promServer.route({
177
- url: livenessEndpoint,
154
+ url: opts.liveness?.endpoint ?? DEFAULT_LIVENESS_ENDPOINT,
178
155
  method: 'GET',
179
156
  logLevel: 'warn',
180
157
  handler: async (req, reply) => {
package/lib/runtime.js CHANGED
@@ -198,7 +198,17 @@ class Runtime extends EventEmitter {
198
198
  await this.closeAndThrow(e)
199
199
  }
200
200
 
201
- await this.#setDispatcher(config.undici)
201
+ const dispatcherOpts = { ...config.undici }
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)
202
212
 
203
213
  if (config.scheduler) {
204
214
  this.#scheduler = startScheduler(config.scheduler, this.#dispatcher, logger)
@@ -467,24 +477,6 @@ class Runtime extends EventEmitter {
467
477
  }
468
478
  }
469
479
 
470
- async updateUndiciConfig (undiciConfig) {
471
- this.#configManager.current.undici = undiciConfig
472
-
473
- await this.#setDispatcher(undiciConfig)
474
-
475
- const promises = []
476
- for (const worker of this.#workers.values()) {
477
- promises.push(sendViaITC(worker, 'updateUndiciConfig', undiciConfig))
478
- }
479
-
480
- const results = await Promise.allSettled(promises)
481
- for (const result of results) {
482
- if (result.status === 'rejected') {
483
- throw result.reason
484
- }
485
- }
486
- }
487
-
488
480
  startCollectingMetrics () {
489
481
  this.#metrics = []
490
482
  this.#metricsTimeout = setInterval(async () => {
@@ -1049,24 +1041,6 @@ class Runtime extends EventEmitter {
1049
1041
  return super.emit(event, payload)
1050
1042
  }
1051
1043
 
1052
- async #setDispatcher (undiciConfig) {
1053
- const config = this.#configManager.current
1054
-
1055
- const dispatcherOpts = { ...undiciConfig }
1056
- const interceptors = [this.#meshInterceptor]
1057
-
1058
- if (config.httpCache) {
1059
- this.#sharedHttpCache = await createSharedStore(this.#configManager.dirname, config.httpCache)
1060
- interceptors.push(
1061
- undiciInterceptors.cache({
1062
- store: this.#sharedHttpCache,
1063
- methods: config.httpCache.methods ?? ['GET', 'HEAD']
1064
- })
1065
- )
1066
- }
1067
- this.#dispatcher = new Agent(dispatcherOpts).compose(interceptors)
1068
- }
1069
-
1070
1044
  #updateStatus (status, args) {
1071
1045
  this.#status = status
1072
1046
  this.emit(status, args)
@@ -1,128 +1,6 @@
1
1
  'use strict'
2
2
 
3
- const { join } = require('node:path')
4
- const { workerData, parentPort } = require('node:worker_threads')
5
3
  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')
12
-
13
- async function setDispatcher (runtimeConfig) {
14
- const dispatcherOpts = await getDespatcherOpts(runtimeConfig.undici)
15
-
16
- let interceptors = globalThis[kInterceptors]
17
- if (!interceptors) {
18
- const threadDispatcher = createThreadInterceptor(runtimeConfig)
19
- const threadInterceptor = threadDispatcher.interceptor
20
-
21
- let cacheInterceptor = null
22
- if (runtimeConfig.httpCache) {
23
- cacheInterceptor = createHttpCacheInterceptor(runtimeConfig)
24
- }
25
-
26
- interceptors = {
27
- threadDispatcher,
28
- threadInterceptor,
29
- cacheInterceptor
30
- }
31
-
32
- globalThis[kInterceptors] = interceptors
33
- }
34
-
35
- let userInterceptors = []
36
- if (Array.isArray(runtimeConfig.undici?.interceptors)) {
37
- const _require = createRequire(join(workerData.dirname, 'package.json'))
38
- userInterceptors = await loadInterceptors(_require, runtimeConfig.undici.interceptors)
39
- }
40
-
41
- setGlobalDispatcher(
42
- new Agent(dispatcherOpts).compose(
43
- [
44
- interceptors.threadInterceptor,
45
- interceptors.cacheInterceptor,
46
- ...userInterceptors
47
- ].filter(Boolean)
48
- )
49
- )
50
-
51
- return interceptors
52
- }
53
-
54
- async function getDespatcherOpts (undiciConfig) {
55
- const dispatcherOpts = { ...undiciConfig }
56
-
57
- const interceptorsConfigs = undiciConfig?.interceptors
58
- if (!interceptorsConfigs || Array.isArray(interceptorsConfigs)) {
59
- return dispatcherOpts
60
- }
61
-
62
- const _require = createRequire(join(workerData.dirname, 'package.json'))
63
-
64
- const clientInterceptors = []
65
- const poolInterceptors = []
66
-
67
- for (const key of ['Agent', 'Pool', 'Client']) {
68
- const interceptorConfig = undiciConfig.interceptors[key]
69
- if (!interceptorConfig) continue
70
-
71
- const interceptors = await loadInterceptors(_require, interceptorConfig)
72
- if (key === 'Agent') {
73
- clientInterceptors.push(...interceptors)
74
- poolInterceptors.push(...interceptors)
75
- }
76
- if (key === 'Pool') {
77
- poolInterceptors.push(...interceptors)
78
- }
79
- if (key === 'Client') {
80
- clientInterceptors.push(...interceptors)
81
- }
82
- }
83
-
84
- dispatcherOpts.factory = (origin, opts) => {
85
- return opts && opts.connections === 1
86
- ? new Client(origin, opts).compose(clientInterceptors)
87
- : new Pool(origin, opts).compose(poolInterceptors)
88
- }
89
-
90
- return dispatcherOpts
91
- }
92
-
93
- function createThreadInterceptor (runtimeConfig) {
94
- const telemetry = runtimeConfig.telemetry
95
- const hooks = telemetry ? createTelemetryThreadInterceptorHooks() : {}
96
- const threadDispatcher = wire({
97
- // Specifying the domain is critical to avoid flooding the DNS
98
- // with requests for a domain that's never going to exist.
99
- domain: '.plt.local',
100
- port: parentPort,
101
- timeout: runtimeConfig.serviceTimeout,
102
- ...hooks
103
- })
104
- return threadDispatcher
105
- }
106
-
107
- function createHttpCacheInterceptor (runtimeConfig) {
108
- const cacheInterceptor = httpCacheInterceptor({
109
- store: new RemoteCacheStore({
110
- onRequest: opts => {
111
- globalThis.platformatic?.onHttpCacheRequest?.(opts)
112
- },
113
- onCacheHit: opts => {
114
- globalThis.platformatic?.onHttpCacheHit?.(opts)
115
- },
116
- onCacheMiss: opts => {
117
- globalThis.platformatic?.onHttpCacheMiss?.(opts)
118
- },
119
- logger: globalThis.platformatic.logger
120
- }),
121
- methods: runtimeConfig.httpCache.methods ?? ['GET', 'HEAD'],
122
- logger: globalThis.platformatic.logger
123
- })
124
- return cacheInterceptor
125
- }
126
4
 
127
5
  async function loadInterceptor (_require, module, options) {
128
6
  const url = pathToFileURL(_require.resolve(module))
@@ -138,4 +16,4 @@ function loadInterceptors (_require, interceptors) {
138
16
  )
139
17
  }
140
18
 
141
- module.exports = { setDispatcher }
19
+ module.exports = { loadInterceptors }
package/lib/worker/itc.js CHANGED
@@ -7,7 +7,6 @@ const { ITC } = require('@platformatic/itc')
7
7
  const { Unpromise } = require('@watchable/unpromise')
8
8
 
9
9
  const errors = require('../errors')
10
- const { setDispatcher } = require('./interceptors')
11
10
  const { kITC, kId, kServiceId, kWorkerId } = require('./symbols')
12
11
 
13
12
  async function safeHandleInITC (worker, fn) {
@@ -110,11 +109,6 @@ function setupITC (app, service, dispatcher) {
110
109
  return app.stackable.inject(injectParams)
111
110
  },
112
111
 
113
- async updateUndiciConfig (undiciConfig) {
114
- const config = await app.stackable.getConfig()
115
- await setDispatcher({ ...config, undici: undiciConfig })
116
- },
117
-
118
112
  getStatus () {
119
113
  return app.getStatus()
120
114
  },
@@ -2,14 +2,16 @@
2
2
 
3
3
  const { EventEmitter } = require('node:events')
4
4
  const { hostname } = require('node:os')
5
- const { resolve } = require('node:path')
6
- const { workerData, threadId } = require('node:worker_threads')
5
+ const { join, resolve } = require('node:path')
6
+ const { parentPort, 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')
12
13
  const {
14
+ createRequire,
13
15
  disablePinoDirectWrite,
14
16
  ensureFlushedWorkerStdio,
15
17
  executeWithTimeout,
@@ -20,11 +22,14 @@ const {
20
22
  } = require('@platformatic/utils')
21
23
  const dotenv = require('dotenv')
22
24
  const pino = require('pino')
23
- const { fetch } = require('undici')
25
+ const { fetch, setGlobalDispatcher, getGlobalDispatcher, Agent } = require('undici')
26
+ const { wire } = require('undici-thread-interceptor')
27
+ const undici = require('undici')
24
28
 
29
+ const { RemoteCacheStore, httpCacheInterceptor } = require('./http-cache')
25
30
  const { PlatformaticApp } = require('./app')
26
31
  const { setupITC } = require('./itc')
27
- const { setDispatcher } = require('./interceptors')
32
+ const { loadInterceptors } = require('./interceptors')
28
33
  const { kId, kITC, kStderrMarker } = require('./symbols')
29
34
 
30
35
  function handleUnhandled (app, type, err) {
@@ -133,7 +138,85 @@ async function main () {
133
138
  Object.assign(process.env, service.env)
134
139
  }
135
140
 
136
- const { threadDispatcher } = await setDispatcher(config)
141
+ // Setup undici
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))
137
220
 
138
221
  // If the service is an entrypoint and runtime server config is defined, use it.
139
222
  let serverConfig = null
@@ -8,7 +8,6 @@ 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')
12
11
 
13
12
  // This string marker should be safe to use since it belongs to Unicode private area
14
13
  const kStderrMarker = '\ue002'
@@ -22,6 +21,5 @@ module.exports = {
22
21
  kITC,
23
22
  kHealthCheckTimer,
24
23
  kWorkerStatus,
25
- kStderrMarker,
26
- kInterceptors
24
+ kStderrMarker
27
25
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platformatic/runtime",
3
- "version": "2.67.0-alpha.0",
3
+ "version": "2.67.0-alpha.1",
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-alpha.0",
41
- "@platformatic/db": "2.67.0-alpha.0",
42
- "@platformatic/node": "2.67.0-alpha.0",
43
- "@platformatic/service": "2.67.0-alpha.0",
44
- "@platformatic/sql-graphql": "2.67.0-alpha.0",
45
- "@platformatic/sql-mapper": "2.67.0-alpha.0"
40
+ "@platformatic/composer": "2.67.0-alpha.1",
41
+ "@platformatic/db": "2.67.0-alpha.1",
42
+ "@platformatic/node": "2.67.0-alpha.1",
43
+ "@platformatic/service": "2.67.0-alpha.1",
44
+ "@platformatic/sql-graphql": "2.67.0-alpha.1",
45
+ "@platformatic/sql-mapper": "2.67.0-alpha.1"
46
46
  },
47
47
  "dependencies": {
48
48
  "@fastify/accepts": "^5.0.0",
@@ -77,13 +77,13 @@
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-alpha.0",
81
- "@platformatic/config": "2.67.0-alpha.0",
82
- "@platformatic/generators": "2.67.0-alpha.0",
83
- "@platformatic/itc": "2.67.0-alpha.0",
84
- "@platformatic/telemetry": "2.67.0-alpha.0",
85
- "@platformatic/utils": "2.67.0-alpha.0",
86
- "@platformatic/ts-compiler": "2.67.0-alpha.0"
80
+ "@platformatic/basic": "2.67.0-alpha.1",
81
+ "@platformatic/config": "2.67.0-alpha.1",
82
+ "@platformatic/generators": "2.67.0-alpha.1",
83
+ "@platformatic/itc": "2.67.0-alpha.1",
84
+ "@platformatic/telemetry": "2.67.0-alpha.1",
85
+ "@platformatic/ts-compiler": "2.67.0-alpha.1",
86
+ "@platformatic/utils": "2.67.0-alpha.1"
87
87
  },
88
88
  "scripts": {
89
89
  "test": "pnpm run lint && borp --concurrency=1 --timeout=300000 && tsd",
package/schema.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "$id": "https://schemas.platformatic.dev/@platformatic/runtime/2.67.0-alpha.0.json",
2
+ "$id": "https://schemas.platformatic.dev/@platformatic/runtime/2.67.0-alpha.1.json",
3
3
  "$schema": "http://json-schema.org/draft-07/schema#",
4
4
  "type": "object",
5
5
  "properties": {
package/undefined ADDED
@@ -0,0 +1,5 @@
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\"..."}