@platformatic/nest 3.71.1-alpha.0 → 4.0.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/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { BaseCapability, BaseContext, BaseOptions } from '@platformatic/basic'
1
+ import { ApplicationDefinition, BaseCapability, BaseContext, BaseOptions, CapabilityFactoryOptions, ConfigContext, DeferredApplicationDefinition } from '@platformatic/basic'
2
2
  import { Configuration, ConfigurationOptions } from '@platformatic/foundation'
3
3
  import { JSONSchemaType } from 'ajv'
4
4
  import type { PlatformaticNestJSConfig } from './config.d.ts'
@@ -36,3 +36,10 @@ export declare const schema: JSONSchemaType<PlatformaticNestJSConfig>
36
36
  export declare const schemaComponents: { nest: JSONSchemaType<object> }
37
37
  export declare const version: string
38
38
  export declare const supportedVersions: string
39
+
40
+ export type NestConfigOptions = CapabilityFactoryOptions<PlatformaticNestJSConfig, 'nest', never>
41
+
42
+ export declare function createNestConfig (options?: NestConfigOptions): ApplicationDefinition
43
+ export declare function createNestConfig (
44
+ callback: (context: ConfigContext) => NestConfigOptions | Promise<NestConfigOptions>
45
+ ): DeferredApplicationDefinition
package/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import { transform as basicTransform, resolve, validationOptions } from '@platformatic/basic'
2
- import { kMetadata, loadConfiguration as utilsLoadConfiguration } from '@platformatic/foundation'
1
+ import { loadCapabilityConfiguration, transform as basicTransform, validationOptions } from '@platformatic/basic'
2
+ import { kMetadata } from '@platformatic/foundation'
3
3
  import { NestCapability } from './lib/capability.js'
4
4
  import { schema } from './lib/schema.js'
5
5
 
@@ -11,14 +11,12 @@ export async function transform (config, schema, options) {
11
11
  }
12
12
 
13
13
  export async function loadConfiguration (configOrRoot, sourceOrConfig, context) {
14
- const { root, source } = await resolve(configOrRoot, sourceOrConfig, 'application')
15
-
16
- return utilsLoadConfiguration(source, context?.schema ?? schema, {
14
+ return loadCapabilityConfiguration(configOrRoot, sourceOrConfig, context, {
15
+ schema,
16
+ scope: import.meta.filename,
17
+ suffixes: 'application',
17
18
  validationOptions,
18
- transform,
19
- replaceEnv: true,
20
- root,
21
- ...context
19
+ transform
22
20
  })
23
21
  }
24
22
 
@@ -29,3 +27,5 @@ export async function create (configOrRoot, sourceOrConfig, context) {
29
27
 
30
28
  export * from './lib/capability.js'
31
29
  export { packageJson, schema, schemaComponents, version } from './lib/schema.js'
30
+
31
+ export * from './lib/factory.js'
package/lib/capability.js CHANGED
@@ -30,6 +30,8 @@ export class NestCapability extends BaseCapability {
30
30
  #app
31
31
  #server
32
32
  #dispatcher
33
+ #managedDevelopment
34
+ #originalDevelopmentEnvironment
33
35
 
34
36
  constructor (root, config, context) {
35
37
  super('nest', version, root, config, context)
@@ -56,17 +58,10 @@ export class NestCapability extends BaseCapability {
56
58
  : undefined
57
59
 
58
60
  this.registerGlobals({ basePath: this.#basePath })
59
-
60
- this.subprocessForceClose = true
61
61
  }
62
62
 
63
- async start ({ listen }) {
64
- // Make this idempotent
65
- if (this.url) {
66
- return this.url
67
- }
68
-
69
- await super._start({ listen })
63
+ async _start () {
64
+ await super._start()
70
65
 
71
66
  const config = this.config
72
67
  const command = config.application.commands[this.isProduction ? 'production' : 'development']
@@ -77,7 +72,19 @@ export class NestCapability extends BaseCapability {
77
72
  })
78
73
 
79
74
  if (command || !this.isProduction) {
80
- await this.startWithCommand(command || `node ${this.#nestjsCli} start --watch --preserveWatchOutput`)
75
+ this.#managedDevelopment = !command && !this.isProduction
76
+
77
+ if (this.#managedDevelopment && typeof this.serverConfig?.port === 'undefined') {
78
+ this.#managedDevelopment = false
79
+ return
80
+ }
81
+
82
+ try {
83
+ await this.startWithCommand(command || `node ${this.#nestjsCli} start --watch --preserveWatchOutput`)
84
+ } finally {
85
+ this.#restoreDevelopmentEnvironment()
86
+ this.#managedDevelopment = false
87
+ }
81
88
 
82
89
  // We use url changing as a way to notify restarts
83
90
  this.childManager.on('url', () => {
@@ -85,12 +92,12 @@ export class NestCapability extends BaseCapability {
85
92
  events.emitAndNotify('url', this.url)
86
93
  })
87
94
  } else {
88
- return this.#startProduction(listen)
95
+ return this.#startProduction()
89
96
  }
90
97
  }
91
98
 
92
- async stop () {
93
- await super.stop()
99
+ async _stop () {
100
+ await super._stop()
94
101
 
95
102
  if (this.childManager) {
96
103
  return this.stopCommand()
@@ -167,15 +174,12 @@ export class NestCapability extends BaseCapability {
167
174
  getMeta () {
168
175
  const hasBasePath = this.basePath || this.#basePath
169
176
 
170
- return {
171
- gateway: {
172
- tcp: typeof this.url !== 'undefined',
173
- url: this.url,
174
- prefix: this.basePath ?? this.#basePath,
175
- wantsAbsoluteUrls: !!hasBasePath,
176
- needsRootTrailingSlash: false
177
- }
178
- }
177
+ return super.getMeta({
178
+ includeConnection: true,
179
+ prefix: this.basePath ?? this.#basePath,
180
+ wantsAbsoluteUrls: !!hasBasePath,
181
+ needsRootTrailingSlash: false
182
+ })
179
183
  }
180
184
 
181
185
  /* c8 ignore next 5 */
@@ -188,22 +192,42 @@ export class NestCapability extends BaseCapability {
188
192
  async getChildManagerContext (basePath) {
189
193
  const context = await super.getChildManagerContext(basePath)
190
194
 
191
- // In development mode we need to choose the random port in advance as the Nest CLI will start a new process
192
- // every time the code changes and thus we need to ensure that the port is always the same.
193
- if (!this.isProduction && (context.port === true || context.port === 0)) {
194
- context.port = await getPort()
195
+ // In managed development mode the Nest CLI starts the application in another process. Choose an ephemeral
196
+ // port once when needed and pass capability-owned listener settings through the environment inherited by
197
+ // that process. Applications started with custom commands remain responsible for their own listener.
198
+ if (this.#managedDevelopment) {
199
+ if (context.port === true || context.port === 0) {
200
+ const environmentPort = Number(process.env.PORT)
201
+ context.port = Number.isInteger(environmentPort) && environmentPort > 0 ? environmentPort : await getPort()
202
+ }
203
+
204
+ this.#originalDevelopmentEnvironment = new Map([
205
+ ['PORT', process.env.PORT],
206
+ ['HOST', process.env.HOST]
207
+ ])
208
+ process.env.PORT = context.port.toString()
209
+
210
+ if (typeof context.host === 'string') {
211
+ process.env.HOST = context.host
212
+ }
195
213
  }
196
214
 
197
215
  return context
198
216
  }
199
217
 
200
- async #startProduction (listen) {
201
- // Listen if entrypoint
202
- if (this.#app && listen) {
203
- await this.#listen()
204
- return this.url
218
+ #restoreDevelopmentEnvironment () {
219
+ for (const [key, value] of this.#originalDevelopmentEnvironment ?? []) {
220
+ if (typeof value === 'undefined') {
221
+ delete process.env[key]
222
+ } else {
223
+ process.env[key] = value
224
+ }
205
225
  }
206
226
 
227
+ this.#originalDevelopmentEnvironment = null
228
+ }
229
+
230
+ async #startProduction () {
207
231
  const outputDirectory = this.config.application.outputDirectory
208
232
  const { path, name } = this.config.nest.appModule
209
233
  this.verifyOutputDirectory(resolve(this.root, outputDirectory))
@@ -241,23 +265,22 @@ export class NestCapability extends BaseCapability {
241
265
  this.#dispatcher = this.#server.listeners('request')[0]
242
266
  }
243
267
 
244
- if (listen) {
245
- await this.#listen()
246
- }
268
+ await this.#listen()
247
269
 
248
270
  await this._collectMetrics()
249
271
  return this.url
250
272
  }
251
273
 
252
274
  async #listen () {
275
+ if (typeof this.serverConfig?.port === 'undefined') {
276
+ return
277
+ }
278
+
253
279
  const serverOptions = this.serverConfig
254
280
  const listenOptions = buildListenOptions(serverOptions)
281
+ const serverPromise = createServerListener()
255
282
 
256
- if (typeof serverOptions?.backlog === 'number') {
257
- createServerListener(false, false, { backlog: serverOptions.backlog })
258
- }
259
-
260
- await this.#app.listen(listenOptions)
283
+ await Promise.all([this.#app.listen(listenOptions), serverPromise])
261
284
  this.url = getServerUrl(this.#isFastify ? this.#server.server : this.#server)
262
285
 
263
286
  return this.url
package/lib/factory.js ADDED
@@ -0,0 +1,7 @@
1
+ import { defineCapabilityFactory } from '@platformatic/basic'
2
+ import { schema, version } from './schema.js'
3
+
4
+ export const createNestConfig = defineCapabilityFactory('@platformatic/nest', schema, {
5
+ version,
6
+ flatten: ['nest']
7
+ })
package/lib/schema.js CHANGED
@@ -1,11 +1,14 @@
1
- import { schemaComponents as basicSchemaComponents } from '@platformatic/basic'
2
- import { schemaComponents as utilsSchemaComponents } from '@platformatic/foundation'
1
+ import { schemaComponents as basicSchemaComponents } from '@platformatic/basic/schema'
2
+ import { schemaComponents as utilsSchemaComponents } from '@platformatic/foundation/schema'
3
3
  import { readFileSync } from 'node:fs'
4
4
  import { resolve } from 'node:path'
5
5
 
6
6
  export const packageJson = JSON.parse(readFileSync(resolve(import.meta.dirname, '../package.json'), 'utf8'))
7
7
  export const version = packageJson.version
8
8
 
9
+ // Production builds an in-thread application and awaits ready() before the port check.
10
+ export const servesWithoutPort = { development: false, production: true }
11
+
9
12
  const nest = {
10
13
  type: 'object',
11
14
  properties: {
package/package.json CHANGED
@@ -1,9 +1,15 @@
1
1
  {
2
2
  "name": "@platformatic/nest",
3
- "version": "3.71.1-alpha.0",
3
+ "version": "4.0.0-alpha.1",
4
4
  "description": "Platformatic Nest.js Capability",
5
5
  "main": "index.js",
6
6
  "type": "module",
7
+ "exports": {
8
+ ".": "./index.js",
9
+ "./schema": "./schema.js",
10
+ "./package.json": "./package.json",
11
+ "./*": "./*"
12
+ },
7
13
  "repository": {
8
14
  "type": "git",
9
15
  "url": "git+https://github.com/platformatic/platformatic.git"
@@ -18,10 +24,10 @@
18
24
  "get-port": "^7.1.0",
19
25
  "light-my-request": "^6.0.0",
20
26
  "pino-http": "^10.2.0",
21
- "@platformatic/basic": "3.71.1-alpha.0",
22
- "@platformatic/foundation": "3.71.1-alpha.0",
23
- "@platformatic/generators": "3.71.1-alpha.0",
24
- "@platformatic/globals": "3.71.1-alpha.0"
27
+ "@platformatic/basic": "4.0.0-alpha.1",
28
+ "@platformatic/foundation": "4.0.0-alpha.1",
29
+ "@platformatic/generators": "4.0.0-alpha.1",
30
+ "@platformatic/globals": "4.0.0-alpha.1"
25
31
  },
26
32
  "devDependencies": {
27
33
  "@nestjs/cli": "^11.0.7",
@@ -35,11 +41,11 @@
35
41
  "tstyche": "^7.0.0",
36
42
  "tsx": "^4.19.0",
37
43
  "typescript": "~6.0.3",
38
- "@platformatic/gateway": "3.71.1-alpha.0",
39
- "@platformatic/service": "3.71.1-alpha.0"
44
+ "@platformatic/gateway": "4.0.0-alpha.1",
45
+ "@platformatic/service": "4.0.0-alpha.1"
40
46
  },
41
47
  "engines": {
42
- "node": ">=22.19.0"
48
+ "node": ">=24.20.0"
43
49
  },
44
50
  "scripts": {
45
51
  "test": "npm run test:types && node --test --test-reporter=cleaner-spec-reporter --test-concurrency=1 --test-timeout=2000000 test/*.test.js test/**/*.test.js",
package/schema.js ADDED
@@ -0,0 +1,7 @@
1
+ // The light /schema subpath, part of the capability contract. It carries the schema and the
2
+ // package-level metadata main-side preparation needs, and imports nothing that would pull the
3
+ // capability's implementation in with it — boot resolves this rather than the package entry.
4
+ //
5
+ // Light is a statement about import cost, never about safety: this executes in the main process
6
+ // with full privileges, like any capability code.
7
+ export * from './lib/schema.js'