@platformatic/basic 2.6.1 → 2.7.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/lib/base.js CHANGED
@@ -3,6 +3,7 @@ import { collectMetrics } from '@platformatic/metrics'
3
3
  import { parseCommandString } from 'execa'
4
4
  import { spawn } from 'node:child_process'
5
5
  import { once } from 'node:events'
6
+ import { workerData } from 'node:worker_threads'
6
7
  import { existsSync } from 'node:fs'
7
8
  import { platform } from 'node:os'
8
9
  import { pathToFileURL } from 'node:url'
@@ -34,6 +35,7 @@ export class BaseStackable {
34
35
  this.startHttpTimer = null
35
36
  this.endHttpTimer = null
36
37
  this.clientWs = null
38
+ this.runtimeConfig = workerData?.config ?? null
37
39
 
38
40
  // Setup the logger
39
41
  const pinoOptions = {
@@ -134,20 +136,12 @@ export class BaseStackable {
134
136
 
135
137
  this.logger.debug(`Executing "${command}" ...`)
136
138
 
139
+ const context = await this.#getChildManagerContext(basePath)
137
140
  this.childManager = new ChildManager({
138
141
  logger: this.logger,
139
142
  loader,
140
143
  scripts,
141
- context: {
142
- id: this.id,
143
- // Always use URL to avoid serialization problem in Windows
144
- root: pathToFileURL(this.root).toString(),
145
- basePath,
146
- logLevel: this.logger.level,
147
- /* c8 ignore next 2 */
148
- port: (this.isEntrypoint ? this.serverConfig?.port || 0 : undefined) ?? true,
149
- host: (this.isEntrypoint ? this.serverConfig?.hostname : undefined) ?? true
150
- }
144
+ context
151
145
  })
152
146
 
153
147
  try {
@@ -188,20 +182,12 @@ export class BaseStackable {
188
182
  async startWithCommand (command, loader) {
189
183
  const config = this.configManager.current
190
184
  const basePath = config.application?.basePath ? cleanBasePath(config.application?.basePath) : ''
185
+
186
+ const context = await this.#getChildManagerContext(basePath)
191
187
  this.childManager = new ChildManager({
192
188
  logger: this.logger,
193
189
  loader,
194
- context: {
195
- id: this.id,
196
- // Always use URL to avoid serialization problem in Windows
197
- root: pathToFileURL(this.root).toString(),
198
- basePath,
199
- logLevel: this.logger.level,
200
- /* c8 ignore next 2 */
201
- port: (this.isEntrypoint ? this.serverConfig?.port || 0 : undefined) ?? true,
202
- host: (this.isEntrypoint ? this.serverConfig?.hostname : undefined) ?? true,
203
- telemetry: this.telemetryConfig
204
- }
190
+ context
205
191
  })
206
192
 
207
193
  this.childManager.on('config', config => {
@@ -312,4 +298,31 @@ export class BaseStackable {
312
298
  ? await this.metricsRegistry.getMetricsAsJSON()
313
299
  : await this.metricsRegistry.metrics()
314
300
  }
301
+
302
+ getMeta () {
303
+ return {
304
+ composer: {
305
+ wantsAbsoluteUrls: false
306
+ }
307
+ }
308
+ }
309
+
310
+ async #getChildManagerContext (basePath) {
311
+ const meta = await this.getMeta()
312
+
313
+ return {
314
+ id: this.id,
315
+ // Always use URL to avoid serialization problem in Windows
316
+ root: pathToFileURL(this.root).toString(),
317
+ basePath,
318
+ logLevel: this.logger.level,
319
+ isEntrypoint: this.isEntrypoint,
320
+ runtimeBasePath: this.runtimeConfig?.basePath ?? null,
321
+ wantsAbsoluteUrls: meta.composer?.wantsAbsoluteUrls ?? false,
322
+ /* c8 ignore next 2 */
323
+ port: (this.isEntrypoint ? this.serverConfig?.port || 0 : undefined) ?? true,
324
+ host: (this.isEntrypoint ? this.serverConfig?.hostname : undefined) ?? true,
325
+ telemetry: this.telemetryConfig
326
+ }
327
+ }
315
328
  }
@@ -16,6 +16,8 @@ import { WebSocket } from 'ws'
16
16
  import { exitCodes } from '../errors.js'
17
17
  import { importFile } from '../utils.js'
18
18
  import { getSocketPath, isWindows } from './child-manager.js'
19
+ import diagnosticChannel from 'node:diagnostics_channel'
20
+ import { ServerResponse } from 'node:http'
19
21
 
20
22
  function createInterceptor (itc) {
21
23
  return function (dispatch) {
@@ -248,6 +250,11 @@ export class ChildProcess extends ITC {
248
250
  }
249
251
 
250
252
  tracingChannel('net.server.listen').subscribe(subscribers)
253
+
254
+ const { isEntrypoint, runtimeBasePath, wantsAbsoluteUrls } = globalThis.platformatic
255
+ if (isEntrypoint && runtimeBasePath && !wantsAbsoluteUrls) {
256
+ stripBasePath(runtimeBasePath)
257
+ }
251
258
  }
252
259
 
253
260
  #setupInterceptors () {
@@ -270,6 +277,56 @@ export class ChildProcess extends ITC {
270
277
  }
271
278
  }
272
279
 
280
+ function stripBasePath (basePath) {
281
+ const kBasePath = Symbol('kBasePath')
282
+
283
+ diagnosticChannel.subscribe('http.server.request.start', ({ request, response }) => {
284
+ if (request.url.startsWith(basePath)) {
285
+ request.url = request.url.slice(basePath.length)
286
+
287
+ if (request.url.charAt(0) !== '/') {
288
+ request.url = '/' + request.url
289
+ }
290
+
291
+ response[kBasePath] = basePath
292
+ }
293
+ })
294
+
295
+ const originWriteHead = ServerResponse.prototype.writeHead
296
+ const originSetHeader = ServerResponse.prototype.setHeader
297
+
298
+ ServerResponse.prototype.writeHead = function (statusCode, statusMessage, headers) {
299
+ if (this[kBasePath] !== undefined) {
300
+ if (headers === undefined && typeof statusMessage === 'object') {
301
+ headers = statusMessage
302
+ statusMessage = undefined
303
+ }
304
+
305
+ if (headers) {
306
+ for (const key in headers) {
307
+ if (
308
+ key.toLowerCase() === 'location' &&
309
+ !headers[key].startsWith(basePath)
310
+ ) {
311
+ headers[key] = basePath + headers[key]
312
+ }
313
+ }
314
+ }
315
+ }
316
+
317
+ return originWriteHead.call(this, statusCode, statusMessage, headers)
318
+ }
319
+
320
+ ServerResponse.prototype.setHeader = function (name, value) {
321
+ if (this[kBasePath]) {
322
+ if (name.toLowerCase() === 'location' && !value.startsWith(basePath)) {
323
+ value = basePath + value
324
+ }
325
+ }
326
+ originSetHeader.call(this, name, value)
327
+ }
328
+ }
329
+
273
330
  async function main () {
274
331
  const dataPath = resolve(tmpdir(), 'platformatic', 'runtimes', `${process.env.PLT_MANAGER_ID}.json`)
275
332
  const { data, loader, scripts } = JSON.parse(await readFile(dataPath))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platformatic/basic",
3
- "version": "2.6.1",
3
+ "version": "2.7.0",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -23,11 +23,11 @@
23
23
  "split2": "^4.2.0",
24
24
  "undici": "^6.19.5",
25
25
  "ws": "^8.18.0",
26
- "@platformatic/config": "2.6.1",
27
- "@platformatic/itc": "2.6.1",
28
- "@platformatic/telemetry": "2.6.1",
29
- "@platformatic/metrics": "2.6.1",
30
- "@platformatic/utils": "2.6.1"
26
+ "@platformatic/config": "2.7.0",
27
+ "@platformatic/itc": "2.7.0",
28
+ "@platformatic/telemetry": "2.7.0",
29
+ "@platformatic/metrics": "2.7.0",
30
+ "@platformatic/utils": "2.7.0"
31
31
  },
32
32
  "devDependencies": {
33
33
  "borp": "^0.18.0",
package/schema.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "$id": "https://schemas.platformatic.dev/@platformatic/basic/2.6.1.json",
2
+ "$id": "https://schemas.platformatic.dev/@platformatic/basic/2.7.0.json",
3
3
  "$schema": "http://json-schema.org/draft-07/schema#",
4
4
  "title": "Platformatic Stackable",
5
5
  "type": "object",