@platformatic/service 0.6.1 → 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
@@ -36,7 +40,7 @@ async function platformaticService (app, opts, toLoad = []) {
36
40
  {
37
41
  const fileWatcher = opts.fileWatcher
38
42
  const configManager = opts.configManager
39
-
43
+ /* c8 ignore next 4 */
40
44
  if (fileWatcher !== undefined) {
41
45
  app.platformatic.fileWatcher = fileWatcher
42
46
  }
@@ -46,37 +50,72 @@ 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 }
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)
55
59
  }
60
+ } else if (opts.plugin) {
61
+ await loadPlugin(app, opts, opts.plugin)
62
+ }
56
63
 
57
- app.log.debug({ plugin: opts.plugin }, 'loading plugin')
64
+ // Enable CORS
65
+ if (opts.cors) {
66
+ app.register(require('@fastify/cors'), opts.cors)
67
+ }
68
+ if (isKeyEnabled('healthCheck', opts)) {
69
+ app.register(underPressure, {
70
+ exposeStatusRoute: '/status',
71
+ healthCheckInterval: opts.healthCheck.interval !== undefined ? opts.healthCheck.interval : 5000,
72
+ healthCheck: opts.healthCheck.fn
73
+ })
74
+ }
75
+ }
58
76
 
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
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
+ }
64
83
 
65
- if (isWatchEnabled && hotReload) {
66
- await app.register(sandbox, {
67
- ...pluginOptions,
68
- customizeGlobalThis (_globalThis) {
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) {
69
102
  // 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
- }
103
+ const globalDispatcher = Symbol.for('undici.globalDispatcher.1')
104
+ const dispatcher = globalThis[globalDispatcher]
105
+ /* istanbul ignore else */
106
+ if (dispatcher) {
107
+ _globalThis[globalDispatcher] = dispatcher
76
108
  }
77
- })
109
+ }
110
+ })
78
111
  // c8 fails in reporting the coverage of this else branch, so we ignore it
79
- /* c8 ignore next 7 */
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)
80
119
  } else {
81
120
  let plugin = await import(`file://${pluginOptions.path}`)
82
121
  if (plugin.__esModule === true) {
@@ -85,18 +124,6 @@ async function platformaticService (app, opts, toLoad = []) {
85
124
  await app.register(plugin, pluginOptions.options)
86
125
  }
87
126
  }
88
-
89
- // Enable CORS
90
- if (opts.cors) {
91
- app.register(require('@fastify/cors'), opts.cors)
92
- }
93
- if (isKeyEnabled('healthCheck', opts)) {
94
- app.register(underPressure, {
95
- exposeStatusRoute: '/status',
96
- healthCheckInterval: opts.healthCheck.interval !== undefined ? opts.healthCheck.interval : 5000,
97
- healthCheck: opts.healthCheck.fn
98
- })
99
- }
100
127
  }
101
128
 
102
129
  platformaticService[Symbol.for('skip-override')] = true
@@ -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)
@@ -131,6 +158,7 @@ async function buildServer (options, app = platformaticService) {
131
158
 
132
159
  let debounce = null
133
160
  handler.restart = (opts) => {
161
+ /* c8 ignore next 3 */
134
162
  if (debounce) {
135
163
  return debounce
136
164
  }
@@ -168,3 +196,4 @@ module.exports.platformaticService = platformaticService
168
196
  module.exports.addLoggerToTheConfig = addLoggerToTheConfig
169
197
  module.exports.loadConfig = loadConfig
170
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,40 +153,18 @@ const plugin = {
122
153
  additionalProperties: false,
123
154
  required: ['outDir']
124
155
  },
125
- watch: {
156
+ fallback: {
126
157
  type: 'boolean'
127
158
  },
128
- watchOptions: {
129
- type: 'object',
130
- properties: {
131
- hotReload: {
132
- type: 'boolean',
133
- default: true
134
- },
135
- allow: {
136
- type: 'array',
137
- items: {
138
- type: 'string'
139
- },
140
- minItems: 1,
141
- nullable: true,
142
- default: null
143
- },
144
- ignore: {
145
- type: 'array',
146
- items: {
147
- type: 'string'
148
- },
149
- nullable: true,
150
- default: null
151
- }
152
- },
153
- additionalProperties: false
159
+ hotReload: {
160
+ type: 'boolean',
161
+ default: true
154
162
  },
155
163
  options: {
156
164
  type: 'object'
157
165
  }
158
166
  },
167
+ additionalProperties: false,
159
168
  required: ['path']
160
169
  }
161
170
 
@@ -186,12 +195,36 @@ const metrics = {
186
195
  const platformaticServiceSchema = {
187
196
  $id: 'https://schemas.platformatic.dev/db',
188
197
  type: 'object',
198
+ $defs: {
199
+ plugin
200
+ },
189
201
  properties: {
190
202
  server,
191
- 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
+ },
192
219
  metrics
193
220
  },
194
- additionalProperties: false,
221
+ additionalProperties: {
222
+ watch: {
223
+ anyOf: [watch, {
224
+ type: 'boolean'
225
+ }]
226
+ }
227
+ },
195
228
  required: ['server']
196
229
  }
197
230
 
@@ -200,3 +233,4 @@ module.exports.metrics = metrics
200
233
  module.exports.cors = cors
201
234
  module.exports.server = server
202
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,18 +1,18 @@
1
1
  {
2
2
  "name": "@platformatic/service",
3
- "version": "0.6.1",
3
+ "version": "0.8.0",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "author": "Matteo Collina <hello@matteocollina.com>",
7
7
  "repository": {
8
8
  "type": "git",
9
- "url": "git+https://github.com/plaformatic/platformatic.git"
9
+ "url": "git+https://github.com/platformatic/platformatic.git"
10
10
  },
11
11
  "license": "Apache-2.0",
12
12
  "bugs": {
13
- "url": "https://github.com/plaformatic/platformatic/issues"
13
+ "url": "https://github.com/platformatic/platformatic/issues"
14
14
  },
15
- "homepage": "https://github.com/plaformatic/platformatic#readme",
15
+ "homepage": "https://github.com/platformatic/platformatic#readme",
16
16
  "devDependencies": {
17
17
  "c8": "^7.11.0",
18
18
  "snazzy": "^9.0.0",
@@ -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.6.1",
38
- "@platformatic/utils": "0.6.1",
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
 
@@ -138,7 +136,7 @@ test('should not watch ignored file', async ({ teardown, equal }) => {
138
136
  equal(version, 'v1')
139
137
  })
140
138
 
141
- test('should not loop forever when doing ESM', async ({ comment, fail }) => {
139
+ test('should not loop forever when doing ESM', { skip: true }, async ({ comment, fail }) => {
142
140
  const tmpDir = await mkdtemp(join(os.tmpdir(), 'watch-esm-'))
143
141
  const pluginFilePath = join(tmpDir, 'plugin.mjs')
144
142
  const configFilePath = join(tmpDir, 'platformatic.service.json')
@@ -151,12 +149,11 @@ test('should not loop forever when doing ESM', async ({ comment, fail }) => {
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
+ }
@@ -201,7 +201,7 @@ test('load and reload with the fallback', async ({ teardown, equal, pass, same }
201
201
  }
202
202
  })
203
203
 
204
- test('load and reload ESM', async ({ teardown, equal, pass, same }) => {
204
+ test('load and reload ESM', { skip: true }, async ({ teardown, equal, pass, same }) => {
205
205
  const file = join(os.tmpdir(), `some-plugin-${process.pid}.mjs`)
206
206
 
207
207
  await writeFile(file, `
@@ -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