@platformatic/node 2.0.0-alpha.8 → 2.0.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 CHANGED
@@ -7,83 +7,48 @@
7
7
 
8
8
  export interface PlatformaticNodeJsStackable {
9
9
  $schema?: string;
10
- server?: {
11
- hostname?: string;
12
- port?: number | string;
13
- pluginTimeout?: number;
14
- healthCheck?:
15
- | boolean
10
+ logger?: {
11
+ level: (
12
+ | ("fatal" | "error" | "warn" | "info" | "debug" | "trace" | "silent")
16
13
  | {
17
- enabled?: boolean;
18
- interval?: number;
19
14
  [k: string]: unknown;
20
- };
21
- ignoreTrailingSlash?: boolean;
22
- ignoreDuplicateSlashes?: boolean;
23
- connectionTimeout?: number;
24
- keepAliveTimeout?: number;
25
- maxRequestsPerSocket?: number;
26
- forceCloseConnections?: boolean | string;
27
- requestTimeout?: number;
28
- bodyLimit?: number;
29
- maxParamLength?: number;
30
- disableRequestLogging?: boolean;
31
- exposeHeadRoutes?: boolean;
32
- logger?:
33
- | boolean
15
+ }
16
+ ) &
17
+ string;
18
+ transport?:
34
19
  | {
35
- level?: string;
36
- transport?:
37
- | {
38
- target?: string;
39
- options?: {
40
- [k: string]: unknown;
41
- };
42
- }
43
- | {
44
- targets?: {
45
- target?: string;
46
- options?: {
47
- [k: string]: unknown;
48
- };
49
- level?: string;
50
- additionalProperties?: never;
51
- [k: string]: unknown;
52
- }[];
53
- options?: {
54
- [k: string]: unknown;
55
- };
56
- };
57
- pipeline?: {
20
+ target?: string;
21
+ options?: {
22
+ [k: string]: unknown;
23
+ };
24
+ }
25
+ | {
26
+ targets?: {
58
27
  target?: string;
59
28
  options?: {
60
29
  [k: string]: unknown;
61
30
  };
31
+ level?: string;
32
+ additionalProperties?: never;
33
+ [k: string]: unknown;
34
+ }[];
35
+ options?: {
36
+ [k: string]: unknown;
62
37
  };
63
- [k: string]: unknown;
64
38
  };
65
- serializerOpts?: {
66
- schema?: {
67
- [k: string]: unknown;
68
- };
69
- ajv?: {
39
+ pipeline?: {
40
+ target?: string;
41
+ options?: {
70
42
  [k: string]: unknown;
71
43
  };
72
- rounding?: "floor" | "ceil" | "round" | "trunc";
73
- debugMode?: boolean;
74
- mode?: "debug" | "standalone";
75
- largeArraySize?: number | string;
76
- largeArrayMechanism?: "default" | "json-stringify";
77
- [k: string]: unknown;
78
44
  };
79
- caseSensitive?: boolean;
80
- requestIdHeader?: string | false;
81
- requestIdLogLabel?: string;
82
- jsonShorthand?: boolean;
83
- trustProxy?: boolean | string | string[] | number;
45
+ [k: string]: unknown;
46
+ };
47
+ server?: {
48
+ hostname?: string;
49
+ port?: number | string;
84
50
  http2?: boolean;
85
51
  https?: {
86
- allowHTTP1?: boolean;
87
52
  key:
88
53
  | string
89
54
  | {
@@ -106,37 +71,6 @@ export interface PlatformaticNodeJsStackable {
106
71
  path?: string;
107
72
  }
108
73
  )[];
109
- requestCert?: boolean;
110
- rejectUnauthorized?: boolean;
111
- };
112
- cors?: {
113
- origin?:
114
- | boolean
115
- | string
116
- | (
117
- | string
118
- | {
119
- regexp: string;
120
- [k: string]: unknown;
121
- }
122
- )[]
123
- | {
124
- regexp: string;
125
- [k: string]: unknown;
126
- };
127
- methods?: string[];
128
- /**
129
- * Comma separated string of allowed headers.
130
- */
131
- allowedHeaders?: string;
132
- exposedHeaders?: string[] | string;
133
- credentials?: boolean;
134
- maxAge?: number;
135
- preflightContinue?: boolean;
136
- optionsSuccessStatus?: number;
137
- preflight?: boolean;
138
- strictPreflight?: boolean;
139
- hideOptionsRoute?: boolean;
140
74
  };
141
75
  };
142
76
  watch?:
@@ -162,6 +96,10 @@ export interface PlatformaticNodeJsStackable {
162
96
  };
163
97
  };
164
98
  node?: {
165
- entrypoint?: string;
99
+ main?: string;
100
+ /**
101
+ * This Node.js application requires the Absolute URL from the Composer
102
+ */
103
+ absoluteUrl?: boolean;
166
104
  };
167
105
  }
package/index.js CHANGED
@@ -10,6 +10,7 @@ import {
10
10
  transformConfig
11
11
  } from '@platformatic/basic'
12
12
  import { ConfigManager } from '@platformatic/config'
13
+ import { setupNodeHTTPTelemetry } from '@platformatic/telemetry'
13
14
  import inject from 'light-my-request'
14
15
  import { existsSync } from 'node:fs'
15
16
  import { readFile } from 'node:fs/promises'
@@ -39,12 +40,17 @@ function isFastify (app) {
39
40
  return Object.getOwnPropertySymbols(app).some(s => s.description === 'fastify.state')
40
41
  }
41
42
 
43
+ function isKoa (app) {
44
+ return typeof app.callback === 'function'
45
+ }
46
+
42
47
  export class NodeStackable extends BaseStackable {
43
48
  #module
44
49
  #app
45
50
  #server
46
51
  #dispatcher
47
52
  #isFastify
53
+ #isKoa
48
54
 
49
55
  constructor (options, root, configManager) {
50
56
  super('nodejs', packageJson.version, options, root, configManager)
@@ -91,19 +97,29 @@ export class NodeStackable extends BaseStackable {
91
97
  // at all. Otherwise there is chance we miss the listen event.
92
98
  const serverOptions = this.serverConfig
93
99
  const serverPromise = createServerListener((this.isEntrypoint ? serverOptions?.port : undefined) ?? true)
100
+ // If telemetry is set, configure it
101
+ const telemetryConfig = this.telemetryConfig
102
+ if (telemetryConfig) {
103
+ setupNodeHTTPTelemetry(telemetryConfig, this.logger)
104
+ }
94
105
  this.#module = await importFile(finalEntrypoint)
95
106
  this.#module = this.#module.default || this.#module
96
107
 
97
108
  // Deal with application
98
- if (typeof this.#module.build === 'function') {
109
+ const factory = ['build', 'create'].find(f => typeof this.#module[f] === 'function')
110
+
111
+ if (factory) {
99
112
  // We have build function, this Stackable will not use HTTP unless it is the entrypoint
100
113
  serverPromise.cancel()
101
114
 
102
- this.#app = await this.#module.build()
115
+ this.#app = await this.#module[factory]()
103
116
  this.#isFastify = isFastify(this.#app)
117
+ this.#isKoa = isKoa(this.#app)
104
118
 
105
119
  if (this.#isFastify) {
106
120
  await this.#app.ready()
121
+ } else if (this.#isKoa) {
122
+ this.#dispatcher = this.#app.callback()
107
123
  } else if (this.#app instanceof Server) {
108
124
  this.#server = this.#app
109
125
  this.#dispatcher = this.#server.listeners('request')[0]
@@ -154,7 +170,7 @@ export class NodeStackable extends BaseStackable {
154
170
  const hasBuildScript = await this.#hasBuildScript()
155
171
 
156
172
  if (!hasBuildScript) {
157
- this.logger.warn(
173
+ this.logger.debug(
158
174
  'No "application.commands.build" configuration value specified and no build script found in package.json. Skipping build ...'
159
175
  )
160
176
  return
@@ -167,10 +183,13 @@ export class NodeStackable extends BaseStackable {
167
183
  let res
168
184
 
169
185
  if (this.url) {
186
+ this.logger.trace({ injectParams, url: this.url }, 'injecting via request')
170
187
  res = await injectViaRequest(this.url, injectParams, onInject)
171
188
  } else if (this.#isFastify) {
189
+ this.logger.trace({ injectParams }, 'injecting via fastify')
172
190
  res = await this.#app.inject(injectParams, onInject)
173
191
  } else {
192
+ this.logger.trace({ injectParams }, 'injecting via light-my-request')
174
193
  res = await inject(this.#dispatcher ?? this.#app, injectParams, onInject)
175
194
  }
176
195
 
@@ -185,9 +204,18 @@ export class NodeStackable extends BaseStackable {
185
204
  return { statusCode, headers, body, payload, rawPayload }
186
205
  }
187
206
 
207
+ _getWantsAbsoluteUrls () {
208
+ const config = this.configManager.current
209
+ return config.node.absoluteUrl
210
+ }
211
+
188
212
  getMeta () {
189
213
  const config = this.configManager.current
190
- let composer = { prefix: this.servicePrefix, wantsAbsoluteUrls: true, needsRootRedirect: true }
214
+ let composer = {
215
+ prefix: this.servicePrefix,
216
+ wantsAbsoluteUrls: this._getWantsAbsoluteUrls(),
217
+ needsRootRedirect: true
218
+ }
191
219
 
192
220
  if (this.url) {
193
221
  composer = {
@@ -196,7 +224,7 @@ export class NodeStackable extends BaseStackable {
196
224
  prefix: config.application?.basePath
197
225
  ? ensureTrailingSlash(cleanBasePath(config.application?.basePath))
198
226
  : this.servicePrefix,
199
- wantsAbsoluteUrls: true,
227
+ wantsAbsoluteUrls: this._getWantsAbsoluteUrls(),
200
228
  needsRootRedirect: true
201
229
  }
202
230
  }
@@ -211,7 +239,7 @@ export class NodeStackable extends BaseStackable {
211
239
  await this.#app.listen({ host: serverOptions?.hostname || '127.0.0.1', port: serverOptions?.port || 0 })
212
240
  this.url = getServerUrl(this.#app.server)
213
241
  } else {
214
- // Express / Node
242
+ // Express / Node / Koa
215
243
  this.#server = await new Promise((resolve, reject) => {
216
244
  return this.#app
217
245
  .listen({ host: serverOptions?.hostname || '127.0.0.1', port: serverOptions?.port || 0 }, function () {
@@ -234,8 +262,8 @@ export class NodeStackable extends BaseStackable {
234
262
  const config = this.configManager.current
235
263
  const outputRoot = resolve(this.root, config.application.outputDirectory)
236
264
 
237
- if (config.node.entrypoint) {
238
- return pathResolve(this.root, config.node.entrypoint)
265
+ if (config.node.main) {
266
+ return pathResolve(this.root, config.node.main)
239
267
  }
240
268
 
241
269
  const { entrypoint, hadEntrypointField } = await getEntrypointInformation(this.root)
@@ -333,7 +361,13 @@ async function getEntrypointInformation (root) {
333
361
  export async function buildStackable (opts) {
334
362
  const root = opts.context.directory
335
363
 
336
- const configManager = new ConfigManager({ schema, source: opts.config ?? {}, schemaOptions, transformConfig })
364
+ const configManager = new ConfigManager({
365
+ schema,
366
+ source: opts.config ?? {},
367
+ schemaOptions,
368
+ transformConfig,
369
+ dirname: root
370
+ })
337
371
  await configManager.parseAndValidate()
338
372
 
339
373
  return new NodeStackable(opts, root, configManager)
package/lib/schema.js CHANGED
@@ -7,8 +7,13 @@ export const packageJson = JSON.parse(readFileSync(new URL('../package.json', im
7
7
  const node = {
8
8
  type: 'object',
9
9
  properties: {
10
- entrypoint: {
11
- type: 'string'
10
+ main: {
11
+ type: 'string',
12
+ },
13
+ absoluteUrl: {
14
+ description: 'This Node.js application requires the Absolute URL from the Composer',
15
+ type: 'boolean',
16
+ default: false,
12
17
  }
13
18
  },
14
19
  default: {},
@@ -18,7 +23,7 @@ const node = {
18
23
  export const schemaComponents = { node }
19
24
 
20
25
  export const schema = {
21
- $id: `https://schemas.platformatic.dev/@platformatic/vite/${packageJson.version}.json`,
26
+ $id: `https://schemas.platformatic.dev/@platformatic/node/${packageJson.version}.json`,
22
27
  $schema: 'http://json-schema.org/draft-07/schema#',
23
28
  title: 'Platformatic Node.js Stackable',
24
29
  type: 'object',
@@ -26,6 +31,7 @@ export const schema = {
26
31
  $schema: {
27
32
  type: 'string'
28
33
  },
34
+ logger: utilsSchemaComponents.logger,
29
35
  server: utilsSchemaComponents.server,
30
36
  watch: basicSchemaComponents.watch,
31
37
  application: basicSchemaComponents.application,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platformatic/node",
3
- "version": "2.0.0-alpha.8",
3
+ "version": "2.0.0",
4
4
  "description": "Platformatic Node.js Stackable",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -16,21 +16,23 @@
16
16
  "homepage": "https://github.com/platformatic/platformatic#readme",
17
17
  "dependencies": {
18
18
  "light-my-request": "^6.0.0",
19
- "@platformatic/basic": "2.0.0-alpha.8",
20
- "@platformatic/config": "2.0.0-alpha.8",
21
- "@platformatic/utils": "2.0.0-alpha.8"
19
+ "@platformatic/basic": "2.0.0",
20
+ "@platformatic/config": "2.0.0",
21
+ "@platformatic/utils": "2.0.0",
22
+ "@platformatic/telemetry": "2.0.0"
22
23
  },
23
24
  "devDependencies": {
24
25
  "borp": "^0.17.0",
25
26
  "express": "^4.19.2",
26
27
  "eslint": "9",
27
- "fastify": "^4.28.1",
28
+ "fastify": "^5.0.0",
28
29
  "json-schema-to-typescript": "^15.0.1",
30
+ "koa": "^2.15.3",
29
31
  "neostandard": "^0.11.1",
30
32
  "tsx": "^4.19.0",
31
33
  "typescript": "^5.5.4",
32
- "@platformatic/service": "2.0.0-alpha.8",
33
- "@platformatic/composer": "2.0.0-alpha.8"
34
+ "@platformatic/composer": "2.0.0",
35
+ "@platformatic/service": "2.0.0"
34
36
  },
35
37
  "scripts": {
36
38
  "test": "npm run lint && borp --concurrency=1 --no-timeout",
package/schema.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "$id": "https://schemas.platformatic.dev/@platformatic/vite/2.0.0-alpha.8.json",
2
+ "$id": "https://schemas.platformatic.dev/@platformatic/node/2.0.0.json",
3
3
  "$schema": "http://json-schema.org/draft-07/schema#",
4
4
  "title": "Platformatic Node.js Stackable",
5
5
  "type": "object",
@@ -7,246 +7,108 @@
7
7
  "$schema": {
8
8
  "type": "string"
9
9
  },
10
- "server": {
10
+ "logger": {
11
11
  "type": "object",
12
12
  "properties": {
13
- "hostname": {
14
- "type": "string"
15
- },
16
- "port": {
17
- "anyOf": [
13
+ "level": {
14
+ "type": "string",
15
+ "default": "info",
16
+ "oneOf": [
18
17
  {
19
- "type": "integer"
18
+ "enum": [
19
+ "fatal",
20
+ "error",
21
+ "warn",
22
+ "info",
23
+ "debug",
24
+ "trace",
25
+ "silent"
26
+ ]
20
27
  },
21
28
  {
22
- "type": "string"
29
+ "pattern": "^\\{.+\\}$"
23
30
  }
24
31
  ]
25
32
  },
26
- "pluginTimeout": {
27
- "type": "integer"
28
- },
29
- "healthCheck": {
33
+ "transport": {
30
34
  "anyOf": [
31
- {
32
- "type": "boolean"
33
- },
34
35
  {
35
36
  "type": "object",
36
37
  "properties": {
37
- "enabled": {
38
- "type": "boolean"
38
+ "target": {
39
+ "type": "string",
40
+ "resolveModule": true
39
41
  },
40
- "interval": {
41
- "type": "integer"
42
+ "options": {
43
+ "type": "object"
42
44
  }
43
45
  },
44
- "additionalProperties": true
45
- }
46
- ]
47
- },
48
- "ignoreTrailingSlash": {
49
- "type": "boolean"
50
- },
51
- "ignoreDuplicateSlashes": {
52
- "type": "boolean"
53
- },
54
- "connectionTimeout": {
55
- "type": "integer"
56
- },
57
- "keepAliveTimeout": {
58
- "type": "integer",
59
- "default": 5000
60
- },
61
- "maxRequestsPerSocket": {
62
- "type": "integer"
63
- },
64
- "forceCloseConnections": {
65
- "anyOf": [
66
- {
67
- "type": "boolean"
68
- },
69
- {
70
- "type": "string",
71
- "pattern": "^idle$"
72
- }
73
- ]
74
- },
75
- "requestTimeout": {
76
- "type": "integer"
77
- },
78
- "bodyLimit": {
79
- "type": "integer"
80
- },
81
- "maxParamLength": {
82
- "type": "integer"
83
- },
84
- "disableRequestLogging": {
85
- "type": "boolean"
86
- },
87
- "exposeHeadRoutes": {
88
- "type": "boolean"
89
- },
90
- "logger": {
91
- "anyOf": [
92
- {
93
- "type": "boolean"
46
+ "additionalProperties": false
94
47
  },
95
48
  {
96
49
  "type": "object",
97
50
  "properties": {
98
- "level": {
99
- "type": "string"
100
- },
101
- "transport": {
102
- "anyOf": [
103
- {
104
- "type": "object",
105
- "properties": {
106
- "target": {
107
- "type": "string",
108
- "resolveModule": true
109
- },
110
- "options": {
111
- "type": "object"
112
- }
51
+ "targets": {
52
+ "type": "array",
53
+ "items": {
54
+ "type": "object",
55
+ "properties": {
56
+ "target": {
57
+ "type": "string",
58
+ "resolveModule": true
113
59
  },
114
- "additionalProperties": false
115
- },
116
- {
117
- "type": "object",
118
- "properties": {
119
- "targets": {
120
- "type": "array",
121
- "items": {
122
- "type": "object",
123
- "properties": {
124
- "target": {
125
- "type": "string",
126
- "resolveModule": true
127
- },
128
- "options": {
129
- "type": "object"
130
- },
131
- "level": {
132
- "type": "string"
133
- },
134
- "additionalProperties": false
135
- }
136
- }
137
- },
138
- "options": {
139
- "type": "object"
140
- }
60
+ "options": {
61
+ "type": "object"
62
+ },
63
+ "level": {
64
+ "type": "string"
141
65
  },
142
66
  "additionalProperties": false
143
67
  }
144
- ]
68
+ }
145
69
  },
146
- "pipeline": {
147
- "type": "object",
148
- "properties": {
149
- "target": {
150
- "type": "string",
151
- "resolveModule": true
152
- },
153
- "options": {
154
- "type": "object"
155
- }
156
- },
157
- "additionalProperties": false
70
+ "options": {
71
+ "type": "object"
158
72
  }
159
73
  },
160
- "additionalProperties": true
74
+ "additionalProperties": false
161
75
  }
162
76
  ]
163
77
  },
164
- "serializerOpts": {
78
+ "pipeline": {
165
79
  "type": "object",
166
80
  "properties": {
167
- "schema": {
168
- "type": "object"
169
- },
170
- "ajv": {
171
- "type": "object"
172
- },
173
- "rounding": {
81
+ "target": {
174
82
  "type": "string",
175
- "enum": [
176
- "floor",
177
- "ceil",
178
- "round",
179
- "trunc"
180
- ],
181
- "default": "trunc"
182
- },
183
- "debugMode": {
184
- "type": "boolean"
83
+ "resolveModule": true
185
84
  },
186
- "mode": {
187
- "type": "string",
188
- "enum": [
189
- "debug",
190
- "standalone"
191
- ]
192
- },
193
- "largeArraySize": {
194
- "anyOf": [
195
- {
196
- "type": "integer"
197
- },
198
- {
199
- "type": "string"
200
- }
201
- ],
202
- "default": 20000
203
- },
204
- "largeArrayMechanism": {
205
- "type": "string",
206
- "enum": [
207
- "default",
208
- "json-stringify"
209
- ],
210
- "default": "default"
211
- }
212
- }
213
- },
214
- "caseSensitive": {
215
- "type": "boolean"
216
- },
217
- "requestIdHeader": {
218
- "anyOf": [
219
- {
220
- "type": "string"
221
- },
222
- {
223
- "type": "boolean",
224
- "const": false
85
+ "options": {
86
+ "type": "object"
225
87
  }
226
- ]
227
- },
228
- "requestIdLogLabel": {
229
- "type": "string"
230
- },
231
- "jsonShorthand": {
232
- "type": "boolean"
88
+ },
89
+ "additionalProperties": false
90
+ }
91
+ },
92
+ "required": [
93
+ "level"
94
+ ],
95
+ "default": {},
96
+ "additionalProperties": true
97
+ },
98
+ "server": {
99
+ "type": "object",
100
+ "properties": {
101
+ "hostname": {
102
+ "type": "string",
103
+ "default": "127.0.0.1"
233
104
  },
234
- "trustProxy": {
105
+ "port": {
235
106
  "anyOf": [
236
107
  {
237
- "type": "boolean"
108
+ "type": "integer"
238
109
  },
239
110
  {
240
111
  "type": "string"
241
- },
242
- {
243
- "type": "array",
244
- "items": {
245
- "type": "string"
246
- }
247
- },
248
- {
249
- "type": "integer"
250
112
  }
251
113
  ]
252
114
  },
@@ -256,9 +118,6 @@
256
118
  "https": {
257
119
  "type": "object",
258
120
  "properties": {
259
- "allowHTTP1": {
260
- "type": "boolean"
261
- },
262
121
  "key": {
263
122
  "anyOf": [
264
123
  {
@@ -332,12 +191,6 @@
332
191
  }
333
192
  }
334
193
  ]
335
- },
336
- "requestCert": {
337
- "type": "boolean"
338
- },
339
- "rejectUnauthorized": {
340
- "type": "boolean"
341
194
  }
342
195
  },
343
196
  "additionalProperties": false,
@@ -345,105 +198,6 @@
345
198
  "key",
346
199
  "cert"
347
200
  ]
348
- },
349
- "cors": {
350
- "type": "object",
351
- "$comment": "See https://github.com/fastify/fastify-cors",
352
- "properties": {
353
- "origin": {
354
- "anyOf": [
355
- {
356
- "type": "boolean"
357
- },
358
- {
359
- "type": "string"
360
- },
361
- {
362
- "type": "array",
363
- "items": {
364
- "anyOf": [
365
- {
366
- "type": "string"
367
- },
368
- {
369
- "type": "object",
370
- "properties": {
371
- "regexp": {
372
- "type": "string"
373
- }
374
- },
375
- "required": [
376
- "regexp"
377
- ]
378
- }
379
- ]
380
- }
381
- },
382
- {
383
- "type": "object",
384
- "properties": {
385
- "regexp": {
386
- "type": "string"
387
- }
388
- },
389
- "required": [
390
- "regexp"
391
- ]
392
- }
393
- ]
394
- },
395
- "methods": {
396
- "type": "array",
397
- "items": {
398
- "type": "string"
399
- }
400
- },
401
- "allowedHeaders": {
402
- "type": "string",
403
- "description": "Comma separated string of allowed headers."
404
- },
405
- "exposedHeaders": {
406
- "anyOf": [
407
- {
408
- "type": "array",
409
- "items": {
410
- "type": "string"
411
- }
412
- },
413
- {
414
- "type": "string",
415
- "description": "Comma separated string of exposed headers."
416
- }
417
- ]
418
- },
419
- "credentials": {
420
- "type": "boolean"
421
- },
422
- "maxAge": {
423
- "type": "integer"
424
- },
425
- "preflightContinue": {
426
- "type": "boolean",
427
- "default": false
428
- },
429
- "optionsSuccessStatus": {
430
- "type": "integer",
431
- "default": 204
432
- },
433
- "preflight": {
434
- "type": "boolean",
435
- "default": true
436
- },
437
- "strictPreflight": {
438
- "type": "boolean",
439
- "default": true
440
- },
441
- "hideOptionsRoute": {
442
- "type": "boolean",
443
- "default": true
444
- }
445
- },
446
- "additionalProperties": false
447
201
  }
448
202
  },
449
203
  "additionalProperties": false
@@ -538,8 +292,13 @@
538
292
  "node": {
539
293
  "type": "object",
540
294
  "properties": {
541
- "entrypoint": {
295
+ "main": {
542
296
  "type": "string"
297
+ },
298
+ "absoluteUrl": {
299
+ "description": "This Node.js application requires the Absolute URL from the Composer",
300
+ "type": "boolean",
301
+ "default": false
543
302
  }
544
303
  },
545
304
  "default": {},