@platformatic/runtime 0.28.1 → 0.30.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/fixtures/dbApp/platformatic.db.json +5 -0
- package/fixtures/dbApp/plugin.js +12 -0
- package/fixtures/monorepo/serviceApp/plugin.js +4 -0
- package/fixtures/start-command-in-runtime.js +2 -1
- package/fixtures/typescript/platformatic.runtime.json +12 -0
- package/fixtures/typescript/services/composer/platformatic.composer.json +28 -0
- package/fixtures/typescript/services/movies/global.d.ts +24 -0
- package/fixtures/typescript/services/movies/migrations/001.do.sql +6 -0
- package/fixtures/typescript/services/movies/migrations/001.undo.sql +3 -0
- package/fixtures/typescript/services/movies/platformatic.db.json +33 -0
- package/fixtures/typescript/services/movies/plugin.ts +5 -0
- package/fixtures/typescript/services/movies/tsconfig.json +21 -0
- package/fixtures/typescript/services/movies/types/Movie.d.ts +9 -0
- package/fixtures/typescript/services/movies/types/index.d.ts +7 -0
- package/fixtures/typescript/services/titles/client/client.d.ts +141 -0
- package/fixtures/typescript/services/titles/client/client.openapi.json +630 -0
- package/fixtures/typescript/services/titles/client/package.json +4 -0
- package/fixtures/typescript/services/titles/platformatic.service.json +31 -0
- package/fixtures/typescript/services/titles/plugins/example.ts +6 -0
- package/fixtures/typescript/services/titles/routes/root.ts +21 -0
- package/fixtures/typescript/services/titles/tsconfig.json +21 -0
- package/help/compile.txt +8 -0
- package/index.js +7 -7
- package/lib/api-client.js +91 -0
- package/lib/api.js +26 -77
- package/lib/app.js +6 -2
- package/lib/compile.js +43 -0
- package/lib/config.js +77 -14
- package/lib/message-port-writable.js +42 -0
- package/lib/start.js +38 -19
- package/lib/unified-api.js +2 -0
- package/lib/worker.js +25 -26
- package/package.json +12 -8
- package/runtime.mjs +4 -0
- package/test/api.test.js +12 -1
- package/test/app.test.js +1 -1
- package/test/cli/compile.test.mjs +65 -0
- package/test/cli/start.test.mjs +56 -0
- package/test/cli/validations.test.mjs +2 -1
- package/test/cli/watch.test.mjs +15 -12
- package/test/config.test.js +137 -1
- package/test/start.test.js +57 -0
package/lib/worker.js
CHANGED
|
@@ -1,36 +1,33 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const inspector = require('node:inspector')
|
|
4
|
+
const { isatty } = require('node:tty')
|
|
3
5
|
const { parentPort, workerData } = require('node:worker_threads')
|
|
4
|
-
const
|
|
5
|
-
const { Agent, setGlobalDispatcher } = require('undici')
|
|
6
|
-
const { PlatformaticApp } = require('./app')
|
|
7
|
-
const { RuntimeApi } = require('./api')
|
|
8
|
-
|
|
9
|
-
const loaderPort = globalThis.LOADER_PORT // Added by loader.mjs.
|
|
10
|
-
const globalAgent = new Agent()
|
|
11
|
-
const globalDispatcher = new FastifyUndiciDispatcher({
|
|
12
|
-
dispatcher: globalAgent,
|
|
13
|
-
// setting the domain here allows for fail-fast scenarios
|
|
14
|
-
domain: '.plt.local'
|
|
15
|
-
})
|
|
6
|
+
const undici = require('undici')
|
|
16
7
|
const pino = require('pino')
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
const
|
|
8
|
+
const RuntimeApi = require('./api')
|
|
9
|
+
const { MessagePortWritable } = require('./message-port-writable')
|
|
10
|
+
const loaderPort = globalThis.LOADER_PORT // Added by loader.mjs.
|
|
20
11
|
|
|
12
|
+
globalThis.fetch = undici.fetch
|
|
21
13
|
delete globalThis.LOADER_PORT
|
|
22
|
-
setGlobalDispatcher(globalDispatcher)
|
|
23
14
|
|
|
24
15
|
let transport
|
|
16
|
+
let destination
|
|
25
17
|
|
|
26
|
-
/* c8 ignore next
|
|
27
|
-
if (
|
|
18
|
+
/* c8 ignore next 10 */
|
|
19
|
+
if (workerData.config.loggingPort) {
|
|
20
|
+
destination = new MessagePortWritable({
|
|
21
|
+
metadata: workerData.config.loggingMetadata,
|
|
22
|
+
port: workerData.config.loggingPort
|
|
23
|
+
})
|
|
24
|
+
} else if (isatty(1)) {
|
|
28
25
|
transport = pino.transport({
|
|
29
26
|
target: 'pino-pretty'
|
|
30
27
|
})
|
|
31
28
|
}
|
|
32
29
|
|
|
33
|
-
const logger = pino(transport)
|
|
30
|
+
const logger = pino(transport, destination)
|
|
34
31
|
|
|
35
32
|
/* c8 ignore next 4 */
|
|
36
33
|
process.once('uncaughtException', (err) => {
|
|
@@ -45,17 +42,19 @@ process.once('unhandledRejection', (err) => {
|
|
|
45
42
|
throw err
|
|
46
43
|
})
|
|
47
44
|
|
|
48
|
-
|
|
49
|
-
const {
|
|
45
|
+
function main () {
|
|
46
|
+
const { inspectorOptions } = workerData.config
|
|
50
47
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
48
|
+
if (inspectorOptions) {
|
|
49
|
+
/* c8 ignore next 6 */
|
|
50
|
+
if (inspectorOptions.hotReloadDisabled) {
|
|
51
|
+
logger.info('debugging flags were detected. hot reloading has been disabled')
|
|
52
|
+
}
|
|
54
53
|
|
|
55
|
-
|
|
54
|
+
inspector.open(inspectorOptions.port, inspectorOptions.host, inspectorOptions.breakFirstLine)
|
|
56
55
|
}
|
|
57
56
|
|
|
58
|
-
const runtime = new RuntimeApi(
|
|
57
|
+
const runtime = new RuntimeApi(workerData.config, logger, loaderPort)
|
|
59
58
|
runtime.startListening(parentPort)
|
|
60
59
|
|
|
61
60
|
parentPort.postMessage('plt:init')
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@platformatic/runtime",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.30.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -22,7 +22,10 @@
|
|
|
22
22
|
"snazzy": "^9.0.0",
|
|
23
23
|
"split2": "^4.2.0",
|
|
24
24
|
"standard": "^17.1.0",
|
|
25
|
-
"tsd": "^0.28.1"
|
|
25
|
+
"tsd": "^0.28.1",
|
|
26
|
+
"typescript": "^5.1.6",
|
|
27
|
+
"@platformatic/sql-mapper": "0.30.0",
|
|
28
|
+
"@platformatic/sql-graphql": "0.30.0"
|
|
26
29
|
},
|
|
27
30
|
"dependencies": {
|
|
28
31
|
"@hapi/topo": "^6.0.2",
|
|
@@ -37,15 +40,16 @@
|
|
|
37
40
|
"pino": "^8.14.1",
|
|
38
41
|
"pino-pretty": "^10.0.0",
|
|
39
42
|
"undici": "^5.22.1",
|
|
40
|
-
"@platformatic/composer": "0.
|
|
41
|
-
"@platformatic/config": "0.
|
|
42
|
-
"@platformatic/db": "0.
|
|
43
|
-
"@platformatic/service": "0.
|
|
44
|
-
"@platformatic/utils": "0.
|
|
43
|
+
"@platformatic/composer": "0.30.0",
|
|
44
|
+
"@platformatic/config": "0.30.0",
|
|
45
|
+
"@platformatic/db": "0.30.0",
|
|
46
|
+
"@platformatic/service": "0.30.0",
|
|
47
|
+
"@platformatic/utils": "0.30.0"
|
|
45
48
|
},
|
|
46
49
|
"standard": {
|
|
47
50
|
"ignore": [
|
|
48
|
-
"**/dist/*"
|
|
51
|
+
"**/dist/*",
|
|
52
|
+
"**/test/tmp"
|
|
49
53
|
]
|
|
50
54
|
},
|
|
51
55
|
"scripts": {
|
package/runtime.mjs
CHANGED
|
@@ -7,6 +7,7 @@ import isMain from 'es-main'
|
|
|
7
7
|
import helpMe from 'help-me'
|
|
8
8
|
import parseArgs from 'minimist'
|
|
9
9
|
import { start } from './lib/start.js'
|
|
10
|
+
import { compile } from './lib/compile.js'
|
|
10
11
|
|
|
11
12
|
const help = helpMe({
|
|
12
13
|
dir: join(import.meta.url, 'help'),
|
|
@@ -18,7 +19,9 @@ const program = commist({ maxDistance: 2 })
|
|
|
18
19
|
|
|
19
20
|
program.register('help', help.toStdout)
|
|
20
21
|
program.register('help start', help.toStdout.bind(null, ['start']))
|
|
22
|
+
program.register('help compile', help.toStdout.bind(null, ['compile']))
|
|
21
23
|
program.register('start', start)
|
|
24
|
+
program.register('compile', compile)
|
|
22
25
|
|
|
23
26
|
export async function run (argv) {
|
|
24
27
|
const args = parseArgs(argv, {
|
|
@@ -32,6 +35,7 @@ export async function run (argv) {
|
|
|
32
35
|
process.exit(0)
|
|
33
36
|
}
|
|
34
37
|
|
|
38
|
+
/* c8 ignore next 4 */
|
|
35
39
|
return {
|
|
36
40
|
output: await program.parseAsync(argv),
|
|
37
41
|
help
|
package/test/api.test.js
CHANGED
|
@@ -45,9 +45,10 @@ test('should get service config', async (t) => {
|
|
|
45
45
|
|
|
46
46
|
const serviceConfig = await app.getServiceConfig('with-logger')
|
|
47
47
|
|
|
48
|
+
delete serviceConfig.$schema
|
|
49
|
+
|
|
48
50
|
// TODO: should return correct logger config
|
|
49
51
|
assert.deepStrictEqual(serviceConfig, {
|
|
50
|
-
$schema: 'https://platformatic.dev/schemas/v0.27.0/service',
|
|
51
52
|
server: {
|
|
52
53
|
hostname: '127.0.0.1',
|
|
53
54
|
port: 0,
|
|
@@ -274,6 +275,16 @@ test('should fail inject request is service is not started', async (t) => {
|
|
|
274
275
|
}
|
|
275
276
|
})
|
|
276
277
|
|
|
278
|
+
test('does not wait forever if worker exits during api operation', async (t) => {
|
|
279
|
+
const configFile = join(fixturesDir, 'configs', 'service-throws-on-start.json')
|
|
280
|
+
const config = await loadConfig({}, ['-c', configFile], platformaticRuntime)
|
|
281
|
+
const app = await buildServer(config.configManager.current)
|
|
282
|
+
|
|
283
|
+
await assert.rejects(async () => {
|
|
284
|
+
await app.start()
|
|
285
|
+
}, /The runtime exited before the operation completed/)
|
|
286
|
+
})
|
|
287
|
+
|
|
277
288
|
test('should handle a lot of runtime api requests', async (t) => {
|
|
278
289
|
const configFile = join(fixturesDir, 'configs', 'monorepo.json')
|
|
279
290
|
const config = await loadConfig({}, ['-c', configFile], platformaticRuntime)
|
package/test/app.test.js
CHANGED
|
@@ -268,7 +268,7 @@ test('supports configuration overrides', async (t) => {
|
|
|
268
268
|
})
|
|
269
269
|
})
|
|
270
270
|
|
|
271
|
-
test('restarts on config change without overriding the configManager',
|
|
271
|
+
test('restarts on config change without overriding the configManager', async (t) => {
|
|
272
272
|
const { logger, stream } = getLoggerAndStream()
|
|
273
273
|
const appPath = join(fixturesDir, 'monorepo', 'serviceApp')
|
|
274
274
|
const configFile = join(appPath, 'platformatic.service.json')
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { test } from 'node:test'
|
|
2
|
+
import assert from 'node:assert'
|
|
3
|
+
import { join } from 'desm'
|
|
4
|
+
import path from 'node:path'
|
|
5
|
+
import { cliPath } from './helper.mjs'
|
|
6
|
+
import { execa } from 'execa'
|
|
7
|
+
import { mkdtemp, rm, cp, mkdir } from 'node:fs/promises'
|
|
8
|
+
import { setTimeout as sleep } from 'node:timers/promises'
|
|
9
|
+
|
|
10
|
+
const base = join(import.meta.url, '..', 'tmp')
|
|
11
|
+
|
|
12
|
+
try {
|
|
13
|
+
await mkdir(base, { recursive: true })
|
|
14
|
+
} catch {
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
test('compile without tsconfigs', async () => {
|
|
18
|
+
const config = join(import.meta.url, '..', '..', 'fixtures', 'configs', 'monorepo.json')
|
|
19
|
+
await execa(cliPath, ['compile', '-c', config])
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
test('compile with tsconfig', async (t) => {
|
|
23
|
+
const tmpDir = await mkdtemp(path.join(base, 'test-runtime-compile-'))
|
|
24
|
+
const prev = process.cwd()
|
|
25
|
+
process.chdir(tmpDir)
|
|
26
|
+
t.after(() => {
|
|
27
|
+
process.chdir(prev)
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
t.after(async () => {
|
|
31
|
+
// We give up after 10s.
|
|
32
|
+
// This is because on Windows, it's very hard to delete files if the file
|
|
33
|
+
// system is not collaborating.
|
|
34
|
+
for (let i = 0; i < 10; i++) {
|
|
35
|
+
try {
|
|
36
|
+
await rm(tmpDir, { recursive: true, force: true })
|
|
37
|
+
break
|
|
38
|
+
} catch (err) {
|
|
39
|
+
if (err.code === 'EBUSY') {
|
|
40
|
+
await sleep(1000)
|
|
41
|
+
continue
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
const folder = join(import.meta.url, '..', '..', 'fixtures', 'typescript')
|
|
48
|
+
await cp(folder, tmpDir, { recursive: true })
|
|
49
|
+
|
|
50
|
+
const { stdout } = await execa(cliPath, ['compile'])
|
|
51
|
+
|
|
52
|
+
const lines = stdout.split('\n').map(JSON.parse)
|
|
53
|
+
const expected = [{
|
|
54
|
+
name: 'movies',
|
|
55
|
+
msg: 'Typescript compilation completed successfully.'
|
|
56
|
+
}, {
|
|
57
|
+
name: 'titles',
|
|
58
|
+
msg: 'Typescript compilation completed successfully.'
|
|
59
|
+
}]
|
|
60
|
+
|
|
61
|
+
for (let i = 0; i < expected.length; i++) {
|
|
62
|
+
assert.deepStrictEqual(lines[i].name, expected[i].name)
|
|
63
|
+
assert.deepStrictEqual(lines[i].msg, expected[i].msg)
|
|
64
|
+
}
|
|
65
|
+
})
|
package/test/cli/start.test.mjs
CHANGED
|
@@ -59,3 +59,59 @@ test('exits on error', async () => {
|
|
|
59
59
|
assert.strictEqual(res.statusCode, 200)
|
|
60
60
|
assert.strictEqual(exitCode, 1)
|
|
61
61
|
})
|
|
62
|
+
|
|
63
|
+
test('does not start if node inspector flags are provided', async (t) => {
|
|
64
|
+
const { execa } = await import('execa')
|
|
65
|
+
const config = join(import.meta.url, '..', '..', 'fixtures', 'configs', 'monorepo.json')
|
|
66
|
+
const child = execa(process.execPath, [cliPath, 'start', '-c', config], {
|
|
67
|
+
env: { NODE_OPTIONS: '--inspect' },
|
|
68
|
+
encoding: 'utf8'
|
|
69
|
+
})
|
|
70
|
+
let stderr = ''
|
|
71
|
+
let found = false
|
|
72
|
+
|
|
73
|
+
for await (const messages of on(child.stderr, 'data')) {
|
|
74
|
+
for (const message of messages) {
|
|
75
|
+
stderr += message
|
|
76
|
+
|
|
77
|
+
if (/Error: The Node.js inspector flags are not supported/.test(stderr)) {
|
|
78
|
+
found = true
|
|
79
|
+
break
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (found) {
|
|
84
|
+
break
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
assert(found)
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
test('starts the inspector', async (t) => {
|
|
92
|
+
const { execa } = await import('execa')
|
|
93
|
+
const config = join(import.meta.url, '..', '..', 'fixtures', 'configs', 'monorepo.json')
|
|
94
|
+
const child = execa(process.execPath, [cliPath, 'start', '-c', config, '--inspect'], {
|
|
95
|
+
encoding: 'utf8'
|
|
96
|
+
})
|
|
97
|
+
let stderr = ''
|
|
98
|
+
let found = false
|
|
99
|
+
|
|
100
|
+
for await (const messages of on(child.stderr, 'data')) {
|
|
101
|
+
for (const message of messages) {
|
|
102
|
+
stderr += message
|
|
103
|
+
|
|
104
|
+
if (/Debugger listening on ws:\/\/127\.0\.0\.1:9229/.test(stderr)) {
|
|
105
|
+
found = true
|
|
106
|
+
break
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (found) {
|
|
111
|
+
break
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
assert(found)
|
|
116
|
+
child.kill('SIGINT')
|
|
117
|
+
})
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import assert from 'node:assert'
|
|
2
2
|
import { readFile } from 'node:fs/promises'
|
|
3
3
|
import { test } from 'node:test'
|
|
4
|
+
import { stripVTControlCharacters } from 'node:util'
|
|
4
5
|
import { join } from 'desm'
|
|
5
6
|
import { execa } from 'execa'
|
|
6
7
|
import { cliPath } from './helper.mjs'
|
|
@@ -42,7 +43,7 @@ test('print validation errors', async () => {
|
|
|
42
43
|
|
|
43
44
|
assert(error)
|
|
44
45
|
assert.strictEqual(error.exitCode, 1)
|
|
45
|
-
assert.strictEqual(error.stdout, `
|
|
46
|
+
assert.strictEqual(stripVTControlCharacters(error.stdout), `
|
|
46
47
|
┌─────────┬─────────────┬─────────────────────────────────────────────────────────────────┐
|
|
47
48
|
│ (index) │ path │ message │
|
|
48
49
|
├─────────┼─────────────┼─────────────────────────────────────────────────────────────────┤
|
package/test/cli/watch.test.mjs
CHANGED
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
import assert from 'node:assert'
|
|
2
|
-
import { cp, writeFile, mkdtemp } from 'node:fs/promises'
|
|
3
|
-
import { tmpdir } from 'node:os'
|
|
2
|
+
import { cp, writeFile, mkdtemp, mkdir, rm } from 'node:fs/promises'
|
|
4
3
|
import { join } from 'node:path'
|
|
5
4
|
import { test } from 'node:test'
|
|
6
5
|
import { setTimeout as sleep } from 'node:timers/promises'
|
|
7
6
|
import desm from 'desm'
|
|
8
7
|
import { request } from 'undici'
|
|
9
8
|
import { start } from './helper.mjs'
|
|
9
|
+
|
|
10
10
|
const fixturesDir = join(desm(import.meta.url), '..', '..', 'fixtures')
|
|
11
|
-
|
|
11
|
+
|
|
12
|
+
const base = join(desm(import.meta.url), '..', 'tmp')
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
await mkdir(base, { recursive: true })
|
|
16
|
+
} catch {
|
|
17
|
+
}
|
|
12
18
|
|
|
13
19
|
function createCjsLoggingPlugin (text, reloaded) {
|
|
14
20
|
return `\
|
|
@@ -42,7 +48,8 @@ function createEsmLoggingPlugin (text, reloaded) {
|
|
|
42
48
|
}
|
|
43
49
|
|
|
44
50
|
test('watches CommonJS files', async (t) => {
|
|
45
|
-
const tmpDir = await mkdtemp(join(
|
|
51
|
+
const tmpDir = await mkdtemp(join(base, 'watch-'))
|
|
52
|
+
t.after(() => rm(tmpDir, { recursive: true, force: true }))
|
|
46
53
|
t.diagnostic(`using ${tmpDir}`)
|
|
47
54
|
const configFileSrc = join(fixturesDir, 'configs', 'monorepo.json')
|
|
48
55
|
const configFileDst = join(tmpDir, 'configs', 'monorepo.json')
|
|
@@ -55,8 +62,6 @@ test('watches CommonJS files', async (t) => {
|
|
|
55
62
|
cp(appSrc, appDst, { recursive: true })
|
|
56
63
|
])
|
|
57
64
|
|
|
58
|
-
await cp(undiciPath, join(appDst, 'serviceApp', 'node_modules', 'undici'), { recursive: true })
|
|
59
|
-
|
|
60
65
|
await writeFile(cjsPluginFilePath, createCjsLoggingPlugin('v1', false))
|
|
61
66
|
const { child } = await start('-c', configFileDst)
|
|
62
67
|
t.after(() => child.kill('SIGINT'))
|
|
@@ -73,7 +78,8 @@ test('watches CommonJS files', async (t) => {
|
|
|
73
78
|
})
|
|
74
79
|
|
|
75
80
|
test('watches ESM files', async (t) => {
|
|
76
|
-
const tmpDir = await mkdtemp(join(
|
|
81
|
+
const tmpDir = await mkdtemp(join(base, 'watch-'))
|
|
82
|
+
t.after(() => rm(tmpDir, { recursive: true, force: true }))
|
|
77
83
|
t.diagnostic(`using ${tmpDir}`)
|
|
78
84
|
const configFileSrc = join(fixturesDir, 'configs', 'monorepo.json')
|
|
79
85
|
const configFileDst = join(tmpDir, 'configs', 'monorepo.json')
|
|
@@ -86,8 +92,6 @@ test('watches ESM files', async (t) => {
|
|
|
86
92
|
cp(appSrc, appDst, { recursive: true })
|
|
87
93
|
])
|
|
88
94
|
|
|
89
|
-
await cp(undiciPath, join(appDst, 'serviceApp', 'node_modules', 'undici'), { recursive: true })
|
|
90
|
-
|
|
91
95
|
await writeFile(esmPluginFilePath, createEsmLoggingPlugin('v1', false))
|
|
92
96
|
const { child } = await start('-c', configFileDst)
|
|
93
97
|
t.after(() => child.kill('SIGINT'))
|
|
@@ -103,7 +107,8 @@ test('watches ESM files', async (t) => {
|
|
|
103
107
|
})
|
|
104
108
|
|
|
105
109
|
test('should not hot reload files with `--hot-reload false', async (t) => {
|
|
106
|
-
const tmpDir = await mkdtemp(join(
|
|
110
|
+
const tmpDir = await mkdtemp(join(base, 'watch-'))
|
|
111
|
+
t.after(() => rm(tmpDir, { recursive: true, force: true }))
|
|
107
112
|
t.diagnostic(`using ${tmpDir}`)
|
|
108
113
|
const configFileSrc = join(fixturesDir, 'configs', 'monorepo.json')
|
|
109
114
|
const configFileDst = join(tmpDir, 'configs', 'monorepo.json')
|
|
@@ -116,8 +121,6 @@ test('should not hot reload files with `--hot-reload false', async (t) => {
|
|
|
116
121
|
cp(appSrc, appDst, { recursive: true })
|
|
117
122
|
])
|
|
118
123
|
|
|
119
|
-
await cp(undiciPath, join(appDst, 'serviceApp', 'node_modules', 'undici'), { recursive: true })
|
|
120
|
-
|
|
121
124
|
await writeFile(cjsPluginFilePath, createCjsLoggingPlugin('v1', false))
|
|
122
125
|
const { child, url } = await start('-c', configFileDst, '--hot-reload', 'false')
|
|
123
126
|
t.after(() => child.kill('SIGINT'))
|
package/test/config.test.js
CHANGED
|
@@ -4,7 +4,7 @@ const assert = require('node:assert')
|
|
|
4
4
|
const { join } = require('node:path')
|
|
5
5
|
const { test } = require('node:test')
|
|
6
6
|
const { loadConfig } = require('@platformatic/service')
|
|
7
|
-
const { platformaticRuntime } = require('../lib/config')
|
|
7
|
+
const { parseInspectorOptions, platformaticRuntime } = require('../lib/config')
|
|
8
8
|
const fixturesDir = join(__dirname, '..', 'fixtures')
|
|
9
9
|
|
|
10
10
|
test('throws if no entrypoint is found', async (t) => {
|
|
@@ -65,3 +65,139 @@ test('can resolve service id from client package.json if not provided', async ()
|
|
|
65
65
|
assert.strictEqual(entry.dependencies.length, 1)
|
|
66
66
|
assert.strictEqual(entry.dependencies[0].id, 'with-logger')
|
|
67
67
|
})
|
|
68
|
+
|
|
69
|
+
test('parseInspectorOptions()', async (t) => {
|
|
70
|
+
await t.test('throws if --inspect and --inspect-brk are both used', () => {
|
|
71
|
+
assert.throws(() => {
|
|
72
|
+
const cm = {
|
|
73
|
+
args: { inspect: '', 'inspect-brk': '' },
|
|
74
|
+
current: {}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
parseInspectorOptions(cm)
|
|
78
|
+
}, /--inspect and --inspect-brk cannot be used together/)
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
await t.test('--inspect default settings', () => {
|
|
82
|
+
const cm = {
|
|
83
|
+
args: { inspect: '' },
|
|
84
|
+
current: {}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
parseInspectorOptions(cm)
|
|
88
|
+
assert.deepStrictEqual(cm.current.inspectorOptions, {
|
|
89
|
+
host: '127.0.0.1',
|
|
90
|
+
port: 9229,
|
|
91
|
+
breakFirstLine: false,
|
|
92
|
+
hotReloadDisabled: false
|
|
93
|
+
})
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
await t.test('--inspect-brk default settings', () => {
|
|
97
|
+
const cm = {
|
|
98
|
+
args: { 'inspect-brk': '' },
|
|
99
|
+
current: {}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
parseInspectorOptions(cm)
|
|
103
|
+
assert.deepStrictEqual(cm.current.inspectorOptions, {
|
|
104
|
+
host: '127.0.0.1',
|
|
105
|
+
port: 9229,
|
|
106
|
+
breakFirstLine: true,
|
|
107
|
+
hotReloadDisabled: false
|
|
108
|
+
})
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
await t.test('hot reloading is disabled if the inspector is used', () => {
|
|
112
|
+
const cm1 = {
|
|
113
|
+
args: { 'inspect-brk': '' },
|
|
114
|
+
current: { hotReload: true }
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
parseInspectorOptions(cm1)
|
|
118
|
+
assert.strictEqual(cm1.current.hotReload, false)
|
|
119
|
+
|
|
120
|
+
const cm2 = {
|
|
121
|
+
args: {},
|
|
122
|
+
current: { hotReload: true }
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
parseInspectorOptions(cm2)
|
|
126
|
+
assert.strictEqual(cm2.current.hotReload, true)
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
await t.test('sets port to a custom value', () => {
|
|
130
|
+
const cm = {
|
|
131
|
+
args: { inspect: '6666' },
|
|
132
|
+
current: {}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
parseInspectorOptions(cm)
|
|
136
|
+
assert.deepStrictEqual(cm.current.inspectorOptions, {
|
|
137
|
+
host: '127.0.0.1',
|
|
138
|
+
port: 6666,
|
|
139
|
+
breakFirstLine: false,
|
|
140
|
+
hotReloadDisabled: false
|
|
141
|
+
})
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
await t.test('sets host and port to custom values', () => {
|
|
145
|
+
const cm = {
|
|
146
|
+
args: { inspect: '0.0.0.0:6666' },
|
|
147
|
+
current: {}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
parseInspectorOptions(cm)
|
|
151
|
+
assert.deepStrictEqual(cm.current.inspectorOptions, {
|
|
152
|
+
host: '0.0.0.0',
|
|
153
|
+
port: 6666,
|
|
154
|
+
breakFirstLine: false,
|
|
155
|
+
hotReloadDisabled: false
|
|
156
|
+
})
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
await t.test('throws if the host is empty', () => {
|
|
160
|
+
assert.throws(() => {
|
|
161
|
+
const cm = {
|
|
162
|
+
args: { inspect: ':9229' },
|
|
163
|
+
current: {}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
parseInspectorOptions(cm)
|
|
167
|
+
}, /inspector host cannot be empty/)
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
await t.test('differentiates valid and invalid ports', () => {
|
|
171
|
+
['127.0.0.1:', 'foo', '1', '-1', '1023', '65536'].forEach((inspectFlag) => {
|
|
172
|
+
assert.throws(() => {
|
|
173
|
+
const cm = {
|
|
174
|
+
args: { inspect: inspectFlag },
|
|
175
|
+
current: {}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
parseInspectorOptions(cm)
|
|
179
|
+
}, /inspector port must be 0 or in range 1024 to 65535/)
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
const cm = {
|
|
183
|
+
args: {},
|
|
184
|
+
current: {}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
cm.args.inspect = '0'
|
|
188
|
+
parseInspectorOptions(cm)
|
|
189
|
+
assert.strictEqual(cm.current.inspectorOptions.port, 0)
|
|
190
|
+
cm.args.inspect = '1024'
|
|
191
|
+
parseInspectorOptions(cm)
|
|
192
|
+
assert.strictEqual(cm.current.inspectorOptions.port, 1024)
|
|
193
|
+
cm.args.inspect = '1025'
|
|
194
|
+
parseInspectorOptions(cm)
|
|
195
|
+
assert.strictEqual(cm.current.inspectorOptions.port, 1025)
|
|
196
|
+
cm.args.inspect = '65534'
|
|
197
|
+
parseInspectorOptions(cm)
|
|
198
|
+
assert.strictEqual(cm.current.inspectorOptions.port, 65534)
|
|
199
|
+
cm.args.inspect = '65535'
|
|
200
|
+
parseInspectorOptions(cm)
|
|
201
|
+
assert.strictEqual(cm.current.inspectorOptions.port, 65535)
|
|
202
|
+
})
|
|
203
|
+
})
|
package/test/start.test.js
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
const assert = require('node:assert')
|
|
3
|
+
const { spawn } = require('node:child_process')
|
|
4
|
+
const { once } = require('node:events')
|
|
3
5
|
const { join } = require('node:path')
|
|
4
6
|
const { test } = require('node:test')
|
|
7
|
+
const { MessageChannel } = require('node:worker_threads')
|
|
5
8
|
const { request } = require('undici')
|
|
6
9
|
const { loadConfig } = require('@platformatic/service')
|
|
7
10
|
const { buildServer, platformaticRuntime } = require('..')
|
|
11
|
+
const { startWithConfig } = require('../lib/start')
|
|
8
12
|
const fixturesDir = join(__dirname, '..', 'fixtures')
|
|
9
13
|
|
|
10
14
|
test('can start applications programmatically from object', async (t) => {
|
|
@@ -99,3 +103,56 @@ test('can restart the runtime apps', async (t) => {
|
|
|
99
103
|
assert.deepStrictEqual(await res.body.json(), { hello: 'world' })
|
|
100
104
|
}
|
|
101
105
|
})
|
|
106
|
+
|
|
107
|
+
test('supports logging via message port', async (t) => {
|
|
108
|
+
const configFile = join(fixturesDir, 'configs', 'monorepo.json')
|
|
109
|
+
const config = await loadConfig({}, ['-c', configFile], platformaticRuntime)
|
|
110
|
+
const { port1, port2 } = new MessageChannel()
|
|
111
|
+
config.configManager.current.loggingPort = port2
|
|
112
|
+
config.configManager.current.loggingMetadata = { foo: 1, bar: 2 }
|
|
113
|
+
const app = await buildServer(config.configManager.current)
|
|
114
|
+
await app.start()
|
|
115
|
+
|
|
116
|
+
t.after(async () => {
|
|
117
|
+
await app.close()
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
const [msg] = await once(port1, 'message')
|
|
121
|
+
|
|
122
|
+
assert.deepStrictEqual(msg.metadata, { foo: 1, bar: 2 })
|
|
123
|
+
assert(Array.isArray(msg.logs))
|
|
124
|
+
assert(msg.logs.length > 0)
|
|
125
|
+
|
|
126
|
+
for (let i = 0; i < msg.logs.length; ++i) {
|
|
127
|
+
// Verify that each log is valid JSON.
|
|
128
|
+
JSON.parse(msg.logs[i])
|
|
129
|
+
}
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
test('can start with a custom environment', async (t) => {
|
|
133
|
+
const configFile = join(fixturesDir, 'configs', 'monorepo.json')
|
|
134
|
+
const config = await loadConfig({}, ['-c', configFile], platformaticRuntime)
|
|
135
|
+
const app = await startWithConfig(config.configManager, { A_CUSTOM_ENV_VAR: 'foobar' })
|
|
136
|
+
|
|
137
|
+
t.after(async () => {
|
|
138
|
+
await app.close()
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
const entryUrl = await app.start()
|
|
142
|
+
const res = await request(entryUrl + '/env')
|
|
143
|
+
|
|
144
|
+
assert.strictEqual(res.statusCode, 200)
|
|
145
|
+
assert.deepStrictEqual(await res.body.json(), { A_CUSTOM_ENV_VAR: 'foobar' })
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
test('handles uncaught exceptions with db app', async (t) => {
|
|
149
|
+
// Test for https://github.com/platformatic/platformatic/issues/1193
|
|
150
|
+
const scriptFile = join(fixturesDir, 'start-command-in-runtime.js')
|
|
151
|
+
const configFile = join(fixturesDir, 'dbApp', 'platformatic.db.json')
|
|
152
|
+
const child = spawn(process.execPath, [scriptFile, configFile, '/async_crash'])
|
|
153
|
+
child.stdout.pipe(process.stdout)
|
|
154
|
+
child.stderr.pipe(process.stderr)
|
|
155
|
+
const [exitCode] = await once(child, 'exit')
|
|
156
|
+
|
|
157
|
+
assert.strictEqual(exitCode, 42)
|
|
158
|
+
})
|