@platformatic/service 0.7.0 → 0.8.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/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  'use strict'
2
2
 
3
3
  const { start } = require('@fastify/restartable')
4
+ const autoload = require('@fastify/autoload')
4
5
  const sandbox = require('fastify-sandbox')
5
6
  const underPressure = require('@fastify/under-pressure')
6
7
  const { schema } = require('./lib/schema')
@@ -9,6 +10,9 @@ const { addLoggerToTheConfig, getJSPluginPath } = require('./lib/utils')
9
10
  const loadConfig = require('./lib/load-config')
10
11
  const { isKeyEnabled, deepmerge } = require('@platformatic/utils')
11
12
  const compiler = require('./lib/compile')
13
+ const { stat } = require('fs').promises
14
+ const { join } = require('path')
15
+ const wrapperPath = join(__dirname, 'lib', 'autoload-wrapper.js')
12
16
 
13
17
  function createServerConfig (config) {
14
18
  // convert the config file to a new structure
@@ -46,44 +50,15 @@ async function platformaticService (app, opts, toLoad = []) {
46
50
  }
47
51
  }
48
52
 
49
- if (opts.plugin) {
50
- let pluginOptions = opts.plugin
51
- /* c8 ignore next 4 */
52
- if (pluginOptions.typescript !== undefined) {
53
- const pluginPath = getJSPluginPath(pluginOptions.path, pluginOptions.typescript.outDir)
54
- pluginOptions = { ...pluginOptions, path: pluginPath }
55
- }
56
-
57
- app.log.debug({ plugin: opts.plugin }, 'loading plugin')
58
-
59
- // if not defined, we defaults to true (which can happen only if config is set programmatically,
60
- // that's why we ignore the coverage of the `undefined` case, which cannot be covered in cli tests)
61
- /* c8 ignore next */
62
- const hotReload = opts.plugin.watchOptions?.hotReload !== false
63
- const isWatchEnabled = opts.plugin.watch !== false
64
- /* c8 ignore next 13 */
65
- if (isWatchEnabled && hotReload) {
66
- await app.register(sandbox, {
67
- ...pluginOptions,
68
- customizeGlobalThis (_globalThis) {
69
- // Taken from https://github.com/nodejs/undici/blob/fa9fd9066569b6357acacffb806aa804b688c9d8/lib/global.js#L5
70
- const globalDispatcher = Symbol.for('undici.globalDispatcher.1')
71
- const dispatcher = globalThis[globalDispatcher]
72
- /* istanbul ignore else */
73
- if (dispatcher) {
74
- _globalThis[globalDispatcher] = dispatcher
75
- }
76
- }
77
- })
78
- // c8 fails in reporting the coverage of this else branch, so we ignore it
79
- /* c8 ignore next 7 */
80
- } else {
81
- let plugin = await import(`file://${pluginOptions.path}`)
82
- if (plugin.__esModule === true) {
83
- plugin = plugin.default
84
- }
85
- await app.register(plugin, pluginOptions.options)
53
+ // TODO apparently c8 is not able to mark
54
+ // this as covered even if it is
55
+ /* c8 ignore next 7 */
56
+ if (Array.isArray(opts.plugin)) {
57
+ for (const plugin of opts.plugin) {
58
+ await loadPlugin(app, opts, plugin)
86
59
  }
60
+ } else if (opts.plugin) {
61
+ await loadPlugin(app, opts, opts.plugin)
87
62
  }
88
63
 
89
64
  // Enable CORS
@@ -99,6 +74,58 @@ async function platformaticService (app, opts, toLoad = []) {
99
74
  }
100
75
  }
101
76
 
77
+ async function loadPlugin (app, config, pluginOptions) {
78
+ /* c8 ignore next 4 */
79
+ if (pluginOptions.typescript !== undefined) {
80
+ const pluginPath = getJSPluginPath(pluginOptions.path, pluginOptions.typescript.outDir)
81
+ pluginOptions = { ...pluginOptions, path: pluginPath }
82
+ }
83
+
84
+ app.log.debug({ plugin: pluginOptions }, 'loading plugin')
85
+
86
+ // if not defined, we defaults to true (which can happen only if config is set programmatically,
87
+ // that's why we ignore the coverage of the `undefined` case, which cannot be covered in cli tests)
88
+ /* c8 ignore next 35 */
89
+ const hotReload = pluginOptions.hotReload !== false
90
+ const isWatchEnabled = config.watch !== false
91
+ if (isWatchEnabled && hotReload) {
92
+ let options = pluginOptions
93
+ if ((await stat(pluginOptions.path)).isDirectory()) {
94
+ options = {
95
+ path: wrapperPath,
96
+ options: pluginOptions
97
+ }
98
+ }
99
+ await app.register(sandbox, {
100
+ ...options,
101
+ customizeGlobalThis (_globalThis) {
102
+ // Taken from https://github.com/nodejs/undici/blob/fa9fd9066569b6357acacffb806aa804b688c9d8/lib/global.js#L5
103
+ const globalDispatcher = Symbol.for('undici.globalDispatcher.1')
104
+ const dispatcher = globalThis[globalDispatcher]
105
+ /* istanbul ignore else */
106
+ if (dispatcher) {
107
+ _globalThis[globalDispatcher] = dispatcher
108
+ }
109
+ }
110
+ })
111
+ // c8 fails in reporting the coverage of this else branch, so we ignore it
112
+ } else {
113
+ if ((await stat(pluginOptions.path)).isDirectory()) {
114
+ const options = {
115
+ ...pluginOptions.options,
116
+ dir: pluginOptions.path
117
+ }
118
+ await app.register(autoload, options)
119
+ } else {
120
+ let plugin = await import(`file://${pluginOptions.path}`)
121
+ if (plugin.__esModule === true) {
122
+ plugin = plugin.default
123
+ }
124
+ await app.register(plugin, pluginOptions.options)
125
+ }
126
+ }
127
+ }
128
+
102
129
  platformaticService[Symbol.for('skip-override')] = true
103
130
 
104
131
  async function buildServer (options, app = platformaticService) {
@@ -109,7 +136,7 @@ async function buildServer (options, app = platformaticService) {
109
136
  schema
110
137
  })
111
138
  await cm.parseAndValidate()
112
- options = deepmerge({}, cm.current, options)
139
+ options = deepmerge({}, options, cm.current)
113
140
  options.configManager = cm
114
141
  }
115
142
  const serverConfig = createServerConfig(options)
@@ -169,3 +196,4 @@ module.exports.platformaticService = platformaticService
169
196
  module.exports.addLoggerToTheConfig = addLoggerToTheConfig
170
197
  module.exports.loadConfig = loadConfig
171
198
  module.exports.tsCompiler = compiler
199
+ module.exports.ConfigManager = ConfigManager
@@ -0,0 +1,11 @@
1
+ 'use strict'
2
+
3
+ const fp = require('fastify-plugin')
4
+ const autoload = require('@fastify/autoload')
5
+
6
+ module.exports = fp(async function (app, opts) {
7
+ app.register(autoload, {
8
+ dir: opts.path,
9
+ ...opts
10
+ })
11
+ })
package/lib/config.js CHANGED
@@ -9,11 +9,12 @@ class ServiceConfigManager extends ConfigManager {
9
9
  constructor (opts) {
10
10
  super({
11
11
  ...opts,
12
- schema,
12
+ schema: opts.schema || schema,
13
13
  schemaOptions: {
14
14
  useDefaults: true,
15
15
  coerceTypes: true,
16
- allErrors: true
16
+ allErrors: true,
17
+ strict: false
17
18
  },
18
19
  allowToWatch: ['.env'],
19
20
  envWhitelist: ['PORT', ...(opts.envWhitelist || [])]
@@ -21,10 +22,22 @@ class ServiceConfigManager extends ConfigManager {
21
22
  }
22
23
 
23
24
  _transformConfig () {
25
+ const fixPluginPath = (plugin) => {
26
+ // for some reasons c8 does not detect these
27
+ /* c8 ignore next 3 */
28
+ if (typeof plugin === 'string') {
29
+ plugin = { path: plugin }
30
+ }
31
+ plugin.path = this._fixRelativePath(plugin.path)
32
+ return plugin
33
+ }
34
+
24
35
  // relative-to-absolute plugin path
25
36
  /* c8 ignore next 3 */
26
- if (this.current.plugin) {
27
- this.current.plugin.path = this._fixRelativePath(this.current.plugin.path)
37
+ if (Array.isArray(this.current.plugin)) {
38
+ this.current.plugin = this.current.plugin.map(fixPluginPath)
39
+ } else if (this.current.plugin) {
40
+ this.current.plugin = fixPluginPath(this.current.plugin)
28
41
  }
29
42
  }
30
43
 
@@ -32,10 +45,22 @@ class ServiceConfigManager extends ConfigManager {
32
45
  const sanitizedConfig = clone(this.current)
33
46
  const dirOfConfig = dirname(this.fullPath)
34
47
 
48
+ const fixPluginPath = (plugin) => {
49
+ // for some reasons c8 does not detect these
50
+ /* c8 ignore next 3 */
51
+ if (typeof plugin === 'string') {
52
+ plugin = { path: plugin }
53
+ }
54
+ plugin.path = relative(dirOfConfig, plugin.path)
55
+ return plugin
56
+ }
57
+
35
58
  // relative-to-absolute plugin path
36
- /* c8 ignore next 3 */
37
- if (this.current.plugin) {
38
- sanitizedConfig.plugin.path = relative(dirOfConfig, this.current.plugin.path)
59
+ /* c8 ignore next 6 */
60
+ if (Array.isArray(this.current.plugin)) {
61
+ sanitizedConfig.plugin = sanitizedConfig.plugin.map(fixPluginPath)
62
+ } else if (this.current.plugin) {
63
+ sanitizedConfig.plugin = fixPluginPath(sanitizedConfig.plugin)
39
64
  }
40
65
 
41
66
  return sanitizedConfig
package/lib/schema.js CHANGED
@@ -84,6 +84,9 @@ const server = {
84
84
  { type: 'string' }
85
85
  ]
86
86
  },
87
+ pluginTimeout: {
88
+ type: 'integer'
89
+ },
87
90
  healthCheck: {
88
91
  anyOf: [
89
92
  { type: 'boolean' },
@@ -102,6 +105,34 @@ const server = {
102
105
  required: ['hostname', 'port']
103
106
  }
104
107
 
108
+ const watch = {
109
+ $id: 'https://schemas.platformatic.dev/service/watch',
110
+ type: 'object',
111
+ properties: {
112
+ type: 'object',
113
+ properties: {
114
+ allow: {
115
+ type: 'array',
116
+ items: {
117
+ type: 'string'
118
+ },
119
+ minItems: 1,
120
+ nullable: true,
121
+ default: null
122
+ },
123
+ ignore: {
124
+ type: 'array',
125
+ items: {
126
+ type: 'string'
127
+ },
128
+ nullable: true,
129
+ default: null
130
+ }
131
+ },
132
+ additionalProperties: false
133
+ }
134
+ }
135
+
105
136
  const plugin = {
106
137
  $id: 'https://schemas.platformatic.dev/service/plugin',
107
138
  type: 'object',
@@ -122,38 +153,12 @@ const plugin = {
122
153
  additionalProperties: false,
123
154
  required: ['outDir']
124
155
  },
125
- watch: {
126
- type: 'boolean'
127
- },
128
156
  fallback: {
129
157
  type: 'boolean'
130
158
  },
131
- watchOptions: {
132
- type: 'object',
133
- properties: {
134
- hotReload: {
135
- type: 'boolean',
136
- default: true
137
- },
138
- allow: {
139
- type: 'array',
140
- items: {
141
- type: 'string'
142
- },
143
- minItems: 1,
144
- nullable: true,
145
- default: null
146
- },
147
- ignore: {
148
- type: 'array',
149
- items: {
150
- type: 'string'
151
- },
152
- nullable: true,
153
- default: null
154
- }
155
- },
156
- additionalProperties: false
159
+ hotReload: {
160
+ type: 'boolean',
161
+ default: true
157
162
  },
158
163
  options: {
159
164
  type: 'object'
@@ -190,12 +195,36 @@ const metrics = {
190
195
  const platformaticServiceSchema = {
191
196
  $id: 'https://schemas.platformatic.dev/db',
192
197
  type: 'object',
198
+ $defs: {
199
+ plugin
200
+ },
193
201
  properties: {
194
202
  server,
195
- plugin,
203
+ plugin: {
204
+ anyOf: [{
205
+ type: 'array',
206
+ items: {
207
+ anyOf: [{
208
+ $ref: '#/$defs/plugin'
209
+ }, {
210
+ type: 'string'
211
+ }]
212
+ }
213
+ }, {
214
+ $ref: '#/$defs/plugin'
215
+ }, {
216
+ type: 'string'
217
+ }]
218
+ },
196
219
  metrics
197
220
  },
198
- additionalProperties: false,
221
+ additionalProperties: {
222
+ watch: {
223
+ anyOf: [watch, {
224
+ type: 'boolean'
225
+ }]
226
+ }
227
+ },
199
228
  required: ['server']
200
229
  }
201
230
 
@@ -204,3 +233,4 @@ module.exports.metrics = metrics
204
233
  module.exports.cors = cors
205
234
  module.exports.server = server
206
235
  module.exports.plugin = plugin
236
+ module.exports.watch = watch
package/lib/start.mjs CHANGED
@@ -40,7 +40,7 @@ async function start (_args) {
40
40
 
41
41
  if (
42
42
  config.plugin !== undefined &&
43
- config.plugin.watch !== false
43
+ config.watch !== false
44
44
  ) {
45
45
  await startFileWatching(server)
46
46
  }
@@ -92,8 +92,8 @@ async function startFileWatching (server) {
92
92
 
93
93
  const fileWatcher = new FileWatcher({
94
94
  path: dirname(configManager.fullPath),
95
- allowToWatch: config.plugin.watchOptions?.allow,
96
- watchIgnore: config.plugin.watchOptions?.ignore
95
+ allowToWatch: config.watch?.allow,
96
+ watchIgnore: config.watch?.ignore
97
97
  })
98
98
  fileWatcher.on('update', () => {
99
99
  onFilesUpdated(server)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platformatic/service",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "author": "Matteo Collina <hello@matteocollina.com>",
@@ -27,6 +27,7 @@
27
27
  },
28
28
  "dependencies": {
29
29
  "@fastify/accepts": "^4.0.1",
30
+ "@fastify/autoload": "^5.5.0",
30
31
  "@fastify/basic-auth": "^4.0.0",
31
32
  "@fastify/cors": "^8.0.0",
32
33
  "@fastify/deepmerge": "^1.1.0",
@@ -34,8 +35,8 @@
34
35
  "@fastify/static": "^6.5.0",
35
36
  "@fastify/swagger": "^8.0.0",
36
37
  "@fastify/under-pressure": "^8.0.0",
37
- "@platformatic/config": "0.7.0",
38
- "@platformatic/utils": "0.7.0",
38
+ "@platformatic/config": "0.8.0",
39
+ "@platformatic/utils": "0.8.0",
39
40
  "close-with-grace": "^1.1.0",
40
41
  "commist": "^3.1.2",
41
42
  "desm": "^1.2.0",
@@ -45,7 +46,7 @@
45
46
  "fastify": "^4.6.0",
46
47
  "fastify-metrics": "^10.0.0",
47
48
  "fastify-plugin": "^4.1.0",
48
- "fastify-sandbox": "^0.10.0",
49
+ "fastify-sandbox": "^0.11.0",
49
50
  "graphql": "^16.6.0",
50
51
  "help-me": "^4.1.0",
51
52
  "mercurius": "^11.3.0",
@@ -0,0 +1,346 @@
1
+ 'use strict'
2
+
3
+ require('./helper')
4
+ const { test } = require('tap')
5
+ const { buildServer } = require('..')
6
+ const { request } = require('undici')
7
+ const { join } = require('path')
8
+
9
+ test('autoload & filesystem based routing / watch disabled', async ({ teardown, equal }) => {
10
+ const config = {
11
+ server: {
12
+ hostname: '127.0.0.1',
13
+ port: 0
14
+ },
15
+ plugin: {
16
+ path: join(__dirname, 'fixtures', 'directories', 'routes')
17
+ },
18
+ watch: false,
19
+ metrics: false
20
+ }
21
+
22
+ const server = await buildServer(config)
23
+ teardown(server.stop)
24
+ await server.listen()
25
+
26
+ {
27
+ const res = await request(`${server.url}/`)
28
+ equal(res.statusCode, 200, 'status code')
29
+ const body = await res.body.json()
30
+ equal(body.hello, 'from root', 'body')
31
+ }
32
+
33
+ {
34
+ const res = await request(`${server.url}/foo/bar`)
35
+ equal(res.statusCode, 200, 'status code')
36
+ const body = await res.body.json()
37
+ equal(body.hello, 'from bar', 'body')
38
+ }
39
+
40
+ {
41
+ const res = await request(`${server.url}/foo/baz`)
42
+ equal(res.statusCode, 200, 'status code')
43
+ const body = await res.body.json()
44
+ equal(body.hello, 'from baz', 'body')
45
+ }
46
+ })
47
+
48
+ test('autoload & filesystem based routing / watch enabled', async ({ teardown, equal }) => {
49
+ const config = {
50
+ server: {
51
+ hostname: '127.0.0.1',
52
+ port: 0
53
+ },
54
+ plugin: {
55
+ path: join(__dirname, 'fixtures', 'directories', 'routes')
56
+ },
57
+ watch: true,
58
+ metrics: false
59
+ }
60
+
61
+ const server = await buildServer(config)
62
+ teardown(server.stop)
63
+ await server.listen()
64
+
65
+ {
66
+ const res = await request(`${server.url}/`)
67
+ equal(res.statusCode, 200, 'status code')
68
+ const body = await res.body.json()
69
+ equal(body.hello, 'from root', 'body')
70
+ }
71
+
72
+ {
73
+ const res = await request(`${server.url}/foo/bar`)
74
+ equal(res.statusCode, 200, 'status code')
75
+ const body = await res.body.json()
76
+ equal(body.hello, 'from bar', 'body')
77
+ }
78
+
79
+ {
80
+ const res = await request(`${server.url}/foo/baz`)
81
+ equal(res.statusCode, 200, 'status code')
82
+ const body = await res.body.json()
83
+ equal(body.hello, 'from baz', 'body')
84
+ }
85
+ })
86
+
87
+ test('multiple files', async ({ teardown, equal }) => {
88
+ const config = {
89
+ server: {
90
+ hostname: '127.0.0.1',
91
+ port: 0
92
+ },
93
+ plugin: [{
94
+ path: join(__dirname, 'fixtures', 'directories', 'plugins')
95
+ }, {
96
+ path: join(__dirname, 'fixtures', 'directories', 'routes')
97
+ }],
98
+ watch: true,
99
+ metrics: false
100
+ }
101
+
102
+ const server = await buildServer(config)
103
+ teardown(server.stop)
104
+ await server.listen()
105
+
106
+ {
107
+ const res = await request(`${server.url}/`)
108
+ equal(res.statusCode, 200, 'status code')
109
+ const body = await res.body.json()
110
+ equal(body.hello, 'from root', 'body')
111
+ }
112
+
113
+ {
114
+ const res = await request(`${server.url}/foo/bar`)
115
+ equal(res.statusCode, 200, 'status code')
116
+ const body = await res.body.json()
117
+ equal(body.hello, 'from bar', 'body')
118
+ }
119
+
120
+ {
121
+ const res = await request(`${server.url}/foo/baz`)
122
+ equal(res.statusCode, 200, 'status code')
123
+ const body = await res.body.json()
124
+ equal(body.hello, 'from baz', 'body')
125
+ }
126
+
127
+ {
128
+ const res = await request(`${server.url}/foo/with-decorator`)
129
+ equal(res.statusCode, 200, 'status code')
130
+ const body = await res.body.json()
131
+ equal(body.hello, 'bar', 'body')
132
+ }
133
+ })
134
+
135
+ test('multiple files / watch false', async ({ teardown, equal }) => {
136
+ const config = {
137
+ server: {
138
+ hostname: '127.0.0.1',
139
+ port: 0
140
+ },
141
+ plugin: [{
142
+ path: join(__dirname, 'fixtures', 'directories', 'plugins')
143
+ }, {
144
+ path: join(__dirname, 'fixtures', 'directories', 'routes')
145
+ }],
146
+ watch: false,
147
+ metrics: false
148
+ }
149
+
150
+ const server = await buildServer(config)
151
+ teardown(server.stop)
152
+ await server.listen()
153
+
154
+ {
155
+ const res = await request(`${server.url}/`)
156
+ equal(res.statusCode, 200, 'status code')
157
+ const body = await res.body.json()
158
+ equal(body.hello, 'from root', 'body')
159
+ }
160
+
161
+ {
162
+ const res = await request(`${server.url}/foo/bar`)
163
+ equal(res.statusCode, 200, 'status code')
164
+ const body = await res.body.json()
165
+ equal(body.hello, 'from bar', 'body')
166
+ }
167
+
168
+ {
169
+ const res = await request(`${server.url}/foo/baz`)
170
+ equal(res.statusCode, 200, 'status code')
171
+ const body = await res.body.json()
172
+ equal(body.hello, 'from baz', 'body')
173
+ }
174
+
175
+ {
176
+ const res = await request(`${server.url}/foo/with-decorator`)
177
+ equal(res.statusCode, 200, 'status code')
178
+ const body = await res.body.json()
179
+ equal(body.hello, 'bar', 'body')
180
+ }
181
+ })
182
+
183
+ test('autoload & filesystem based routing / watch disabled / no object', async ({ teardown, equal }) => {
184
+ const config = {
185
+ server: {
186
+ hostname: '127.0.0.1',
187
+ port: 0
188
+ },
189
+ plugin: join(__dirname, 'fixtures', 'directories', 'routes'),
190
+ watch: false,
191
+ metrics: false
192
+ }
193
+
194
+ const server = await buildServer(config)
195
+ teardown(server.stop)
196
+ await server.listen()
197
+
198
+ {
199
+ const res = await request(`${server.url}/`)
200
+ equal(res.statusCode, 200, 'status code')
201
+ const body = await res.body.json()
202
+ equal(body.hello, 'from root', 'body')
203
+ }
204
+
205
+ {
206
+ const res = await request(`${server.url}/foo/bar`)
207
+ equal(res.statusCode, 200, 'status code')
208
+ const body = await res.body.json()
209
+ equal(body.hello, 'from bar', 'body')
210
+ }
211
+
212
+ {
213
+ const res = await request(`${server.url}/foo/baz`)
214
+ equal(res.statusCode, 200, 'status code')
215
+ const body = await res.body.json()
216
+ equal(body.hello, 'from baz', 'body')
217
+ }
218
+ })
219
+
220
+ test('autoload & filesystem based routing / watch enabled / no object', async ({ teardown, equal }) => {
221
+ const config = {
222
+ server: {
223
+ hostname: '127.0.0.1',
224
+ port: 0
225
+ },
226
+ plugin: join(__dirname, 'fixtures', 'directories', 'routes'),
227
+ watch: true,
228
+ metrics: false
229
+ }
230
+
231
+ const server = await buildServer(config)
232
+ teardown(server.stop)
233
+ await server.listen()
234
+
235
+ {
236
+ const res = await request(`${server.url}/`)
237
+ equal(res.statusCode, 200, 'status code')
238
+ const body = await res.body.json()
239
+ equal(body.hello, 'from root', 'body')
240
+ }
241
+
242
+ {
243
+ const res = await request(`${server.url}/foo/bar`)
244
+ equal(res.statusCode, 200, 'status code')
245
+ const body = await res.body.json()
246
+ equal(body.hello, 'from bar', 'body')
247
+ }
248
+
249
+ {
250
+ const res = await request(`${server.url}/foo/baz`)
251
+ equal(res.statusCode, 200, 'status code')
252
+ const body = await res.body.json()
253
+ equal(body.hello, 'from baz', 'body')
254
+ }
255
+ })
256
+
257
+ test('multiple files / no object', async ({ teardown, equal }) => {
258
+ const config = {
259
+ server: {
260
+ hostname: '127.0.0.1',
261
+ port: 0
262
+ },
263
+ plugin: [join(__dirname, 'fixtures', 'directories', 'plugins'), join(__dirname, 'fixtures', 'directories', 'routes')],
264
+ watch: true,
265
+ metrics: false
266
+ }
267
+
268
+ const server = await buildServer(config)
269
+ teardown(server.stop)
270
+ await server.listen()
271
+
272
+ {
273
+ const res = await request(`${server.url}/`)
274
+ equal(res.statusCode, 200, 'status code')
275
+ const body = await res.body.json()
276
+ equal(body.hello, 'from root', 'body')
277
+ }
278
+
279
+ {
280
+ const res = await request(`${server.url}/foo/bar`)
281
+ equal(res.statusCode, 200, 'status code')
282
+ const body = await res.body.json()
283
+ equal(body.hello, 'from bar', 'body')
284
+ }
285
+
286
+ {
287
+ const res = await request(`${server.url}/foo/baz`)
288
+ equal(res.statusCode, 200, 'status code')
289
+ const body = await res.body.json()
290
+ equal(body.hello, 'from baz', 'body')
291
+ }
292
+
293
+ {
294
+ const res = await request(`${server.url}/foo/with-decorator`)
295
+ equal(res.statusCode, 200, 'status code')
296
+ const body = await res.body.json()
297
+ equal(body.hello, 'bar', 'body')
298
+ }
299
+ })
300
+
301
+ test('multiple files / watch false / no object', async ({ teardown, equal }) => {
302
+ const config = {
303
+ server: {
304
+ hostname: '127.0.0.1',
305
+ port: 0
306
+ },
307
+ plugin: [
308
+ join(__dirname, 'fixtures', 'directories', 'plugins'),
309
+ join(__dirname, 'fixtures', 'directories', 'routes')
310
+ ],
311
+ watch: false,
312
+ metrics: false
313
+ }
314
+
315
+ const server = await buildServer(config)
316
+ teardown(server.stop)
317
+ await server.listen()
318
+
319
+ {
320
+ const res = await request(`${server.url}/`)
321
+ equal(res.statusCode, 200, 'status code')
322
+ const body = await res.body.json()
323
+ equal(body.hello, 'from root', 'body')
324
+ }
325
+
326
+ {
327
+ const res = await request(`${server.url}/foo/bar`)
328
+ equal(res.statusCode, 200, 'status code')
329
+ const body = await res.body.json()
330
+ equal(body.hello, 'from bar', 'body')
331
+ }
332
+
333
+ {
334
+ const res = await request(`${server.url}/foo/baz`)
335
+ equal(res.statusCode, 200, 'status code')
336
+ const body = await res.body.json()
337
+ equal(body.hello, 'from baz', 'body')
338
+ }
339
+
340
+ {
341
+ const res = await request(`${server.url}/foo/with-decorator`)
342
+ equal(res.statusCode, 200, 'status code')
343
+ const body = await res.body.json()
344
+ equal(body.hello, 'bar', 'body')
345
+ }
346
+ })
@@ -1,7 +1,8 @@
1
- import { start } from './helper.mjs'
1
+ import { start, cliPath } from './helper.mjs'
2
2
  import { test } from 'tap'
3
3
  import { join } from 'desm'
4
4
  import { request } from 'undici'
5
+ import { execa } from 'execa'
5
6
 
6
7
  test('autostart', async ({ equal, same, match, teardown }) => {
7
8
  const { child, url } = await start('-c', join(import.meta.url, '..', '..', 'fixtures', 'hello', 'platformatic.service.json'))
@@ -47,3 +48,7 @@ test('plugin options', async ({ equal, same, match, teardown }) => {
47
48
 
48
49
  child.kill('SIGINT')
49
50
  })
51
+
52
+ test('not load', async ({ rejects }) => {
53
+ await rejects(execa('node', [cliPath, 'start', '-c', join(import.meta.url, '..', 'fixtures', 'not-load.service.json')]))
54
+ })
@@ -29,9 +29,9 @@ test('should watch js files by default', async ({ equal, teardown }) => {
29
29
  hostname: '127.0.0.1',
30
30
  port: 0
31
31
  },
32
+ watch: true,
32
33
  plugin: {
33
- path: pluginFilePath,
34
- watch: true
34
+ path: pluginFilePath
35
35
  }
36
36
  }
37
37
 
@@ -66,12 +66,11 @@ test('should watch allowed file', async ({ comment, teardown }) => {
66
66
  hostname: '127.0.0.1',
67
67
  port: 0
68
68
  },
69
+ watch: {
70
+ allow: ['*.js', '*.json']
71
+ },
69
72
  plugin: {
70
- path: pluginFilePath,
71
- watch: true,
72
- watchOptions: {
73
- allow: ['*.js', '*.json']
74
- }
73
+ path: pluginFilePath
75
74
  }
76
75
  }
77
76
 
@@ -113,12 +112,11 @@ test('should not watch ignored file', async ({ teardown, equal }) => {
113
112
  hostname: '127.0.0.1',
114
113
  port: 0
115
114
  },
115
+ watch: {
116
+ ignore: [basename(pluginFilePath)]
117
+ },
116
118
  plugin: {
117
- path: pluginFilePath,
118
- watch: true,
119
- watchOptions: {
120
- ignore: [basename(pluginFilePath)]
121
- }
119
+ path: pluginFilePath
122
120
  }
123
121
  }
124
122
 
@@ -151,12 +149,11 @@ test('should not loop forever when doing ESM', { skip: true }, async ({ comment,
151
149
  hostname: '127.0.0.1',
152
150
  port: 0
153
151
  },
152
+ watch: {
153
+ ignore: [basename(pluginFilePath)]
154
+ },
154
155
  plugin: {
155
- path: pluginFilePath,
156
- watch: true,
157
- watchOptions: {
158
- ignore: [basename(pluginFilePath)]
159
- }
156
+ path: pluginFilePath
160
157
  }
161
158
  }
162
159
 
@@ -196,12 +193,11 @@ test('should watch config file', async ({ comment, teardown }) => {
196
193
  hostname: '127.0.0.1',
197
194
  port: 0
198
195
  },
196
+ watch: {
197
+ allow: ['*.js', '*.json']
198
+ },
199
199
  plugin: {
200
- path: pluginFilePath,
201
- watch: true,
202
- watchOptions: {
203
- allow: ['*.js', '*.json']
204
- }
200
+ path: pluginFilePath
205
201
  }
206
202
  }
207
203
 
@@ -8,9 +8,9 @@
8
8
  },
9
9
  "plugin": {
10
10
  "path": "plugin.ts",
11
- "watch": true,
12
11
  "typescript": {
13
12
  "outDir": "dist"
14
13
  }
15
- }
14
+ },
15
+ "watch": true
16
16
  }
@@ -0,0 +1,13 @@
1
+ {
2
+ "server": {
3
+ "hostname": "127.0.0.1",
4
+ "port": 0,
5
+ "logger": {
6
+ "level": "info"
7
+ }
8
+ },
9
+ "plugin": {
10
+ "path": "./routes"
11
+ },
12
+ "metrics": false
13
+ }
@@ -0,0 +1,7 @@
1
+ 'use strict'
2
+
3
+ const fp = require('fastify-plugin')
4
+
5
+ module.exports = fp(async function (app) {
6
+ app.decorate('foo', 'bar')
7
+ })
@@ -0,0 +1,11 @@
1
+ 'use strict'
2
+
3
+ module.exports = async function (fastify, opts) {
4
+ fastify.get('/bar', async function (request, reply) {
5
+ return { hello: 'from bar' }
6
+ })
7
+
8
+ fastify.get('/with-decorator', async function (request, reply) {
9
+ return { hello: fastify.foo }
10
+ })
11
+ }
@@ -0,0 +1,7 @@
1
+ 'use strict'
2
+
3
+ module.exports = async function (fastify, opts) {
4
+ fastify.get('/', async function (request, reply) {
5
+ return { hello: 'from baz' }
6
+ })
7
+ }
@@ -0,0 +1,7 @@
1
+ 'use strict'
2
+
3
+ module.exports = async function (fastify, opts) {
4
+ fastify.get('/', async function (request, reply) {
5
+ return { hello: 'from root' }
6
+ })
7
+ }
@@ -0,0 +1,8 @@
1
+ 'use strict'
2
+
3
+ const { promisify } = require('util')
4
+ const sleep = promisify(setTimeout)
5
+
6
+ module.exports = async function (app) {
7
+ await sleep(60000)
8
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "server": {
3
+ "logger": {
4
+ "level": "info"
5
+ },
6
+ "hostname": "127.0.0.1",
7
+ "port": "3042",
8
+ "pluginTimeout": "10"
9
+ },
10
+ "plugin": {
11
+ "path": "./not-load.js",
12
+ "watch": false
13
+ }
14
+ }
@@ -303,10 +303,7 @@ test('hot reload disabled, CommonJS', async ({ teardown, equal, pass, same }) =>
303
303
  },
304
304
  plugin: {
305
305
  path: file,
306
- watch: true,
307
- watchOptions: {
308
- hotReload: false
309
- }
306
+ hotReload: false
310
307
  }
311
308
  })
312
309
  teardown(server.stop)
@@ -359,11 +356,9 @@ test('hot reload disabled, ESM', async ({ teardown, equal, pass, same }) => {
359
356
  plugin: {
360
357
  path: file,
361
358
  stopTimeout: 1000,
362
- watch: true,
363
- watchOptions: {
364
- hotReload: false
365
- }
359
+ hotReload: false
366
360
  },
361
+ watch: true,
367
362
  metrics: false
368
363
  })
369
364
  teardown(server.stop)
@@ -416,11 +411,9 @@ test('hot reload disabled, with default export', async ({ teardown, equal, pass,
416
411
  plugin: {
417
412
  path: file,
418
413
  stopTimeout: 1000,
419
- watch: true,
420
- watchOptions: {
421
- hotReload: false
422
- }
414
+ hotReload: false
423
415
  },
416
+ watch: true,
424
417
  metrics: false
425
418
  })
426
419
  teardown(server.stop)
@@ -430,7 +423,6 @@ test('hot reload disabled, with default export', async ({ teardown, equal, pass,
430
423
  const res = await request(`${server.url}/test`, {
431
424
  method: 'GET'
432
425
  })
433
- equal(res.statusCode, 200)
434
426
  same(await res.body.json(), { res: 'plugin, version 1' }, 'get rest plugin')
435
427
  }
436
428