@platformatic/runtime 3.4.1 → 3.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/config.d.ts +224 -77
- package/eslint.config.js +3 -5
- package/index.d.ts +73 -24
- package/index.js +173 -29
- package/lib/config.js +279 -197
- package/lib/errors.js +126 -34
- package/lib/generator.js +640 -0
- package/lib/logger.js +43 -41
- package/lib/management-api.js +109 -118
- package/lib/prom-server.js +202 -16
- package/lib/runtime.js +1963 -585
- package/lib/scheduler.js +119 -0
- package/lib/schema.js +22 -234
- package/lib/shared-http-cache.js +43 -0
- package/lib/upgrade.js +6 -8
- package/lib/utils.js +6 -61
- package/lib/version.js +7 -0
- package/lib/versions/v1.36.0.js +2 -4
- package/lib/versions/v1.5.0.js +2 -4
- package/lib/versions/v2.0.0.js +3 -5
- package/lib/versions/v3.0.0.js +16 -0
- package/lib/worker/controller.js +302 -0
- package/lib/worker/http-cache.js +171 -0
- package/lib/worker/interceptors.js +190 -10
- package/lib/worker/itc.js +146 -59
- package/lib/worker/main.js +220 -81
- package/lib/worker/messaging.js +182 -0
- package/lib/worker/round-robin-map.js +62 -0
- package/lib/worker/shared-context.js +22 -0
- package/lib/worker/symbols.js +14 -5
- package/package.json +47 -38
- package/schema.json +1383 -55
- package/help/compile.txt +0 -8
- package/help/help.txt +0 -5
- package/help/start.txt +0 -21
- package/index.test-d.ts +0 -41
- package/lib/build-server.js +0 -69
- package/lib/compile.js +0 -98
- package/lib/dependencies.js +0 -59
- package/lib/generator/README.md +0 -32
- package/lib/generator/errors.js +0 -10
- package/lib/generator/runtime-generator.d.ts +0 -37
- package/lib/generator/runtime-generator.js +0 -498
- package/lib/start.js +0 -190
- package/lib/worker/app.js +0 -278
- package/lib/worker/default-stackable.js +0 -33
- package/lib/worker/metrics.js +0 -122
- package/runtime.mjs +0 -54
package/lib/worker/app.js
DELETED
|
@@ -1,278 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
const { existsSync } = require('node:fs')
|
|
4
|
-
const { EventEmitter } = require('node:events')
|
|
5
|
-
const { resolve } = require('node:path')
|
|
6
|
-
const { ConfigManager } = require('@platformatic/config')
|
|
7
|
-
const { FileWatcher } = require('@platformatic/utils')
|
|
8
|
-
const { getGlobalDispatcher, setGlobalDispatcher } = require('undici')
|
|
9
|
-
const debounce = require('debounce')
|
|
10
|
-
|
|
11
|
-
const errors = require('../errors')
|
|
12
|
-
const defaultStackable = require('./default-stackable')
|
|
13
|
-
const { collectMetrics } = require('./metrics')
|
|
14
|
-
const { getServiceUrl, loadConfig, loadEmptyConfig } = require('../utils')
|
|
15
|
-
|
|
16
|
-
class PlatformaticApp extends EventEmitter {
|
|
17
|
-
#starting
|
|
18
|
-
#started
|
|
19
|
-
#listening
|
|
20
|
-
#watch
|
|
21
|
-
#fileWatcher
|
|
22
|
-
#metricsRegistry
|
|
23
|
-
#debouncedRestart
|
|
24
|
-
#context
|
|
25
|
-
|
|
26
|
-
constructor (appConfig, telemetryConfig, loggerConfig, serverConfig, metricsConfig, hasManagementApi, watch) {
|
|
27
|
-
super()
|
|
28
|
-
this.appConfig = appConfig
|
|
29
|
-
this.#watch = watch
|
|
30
|
-
this.#starting = false
|
|
31
|
-
this.#started = false
|
|
32
|
-
this.#listening = false
|
|
33
|
-
this.stackable = null
|
|
34
|
-
this.#fileWatcher = null
|
|
35
|
-
this.#metricsRegistry = null
|
|
36
|
-
|
|
37
|
-
this.#context = {
|
|
38
|
-
serviceId: this.appConfig.id,
|
|
39
|
-
directory: this.appConfig.path,
|
|
40
|
-
isEntrypoint: this.appConfig.entrypoint,
|
|
41
|
-
isProduction: this.appConfig.isProduction,
|
|
42
|
-
telemetryConfig,
|
|
43
|
-
metricsConfig,
|
|
44
|
-
loggerConfig,
|
|
45
|
-
serverConfig,
|
|
46
|
-
hasManagementApi: !!hasManagementApi,
|
|
47
|
-
localServiceEnvVars: this.appConfig.localServiceEnvVars
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
getStatus () {
|
|
52
|
-
if (this.#starting) return 'starting'
|
|
53
|
-
if (this.#started) return 'started'
|
|
54
|
-
return 'stopped'
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
async updateContext (context) {
|
|
58
|
-
this.#context = { ...this.#context, ...context }
|
|
59
|
-
if (this.stackable) {
|
|
60
|
-
this.stackable.updateContext(context)
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
async getBootstrapDependencies () {
|
|
65
|
-
return this.stackable.getBootstrapDependencies()
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
async init () {
|
|
69
|
-
try {
|
|
70
|
-
const appConfig = this.appConfig
|
|
71
|
-
let loadedConfig
|
|
72
|
-
|
|
73
|
-
// Before returning the base application, check if there is any file we recognize
|
|
74
|
-
// and the user just forgot to specify in the configuration.
|
|
75
|
-
if (!appConfig.config) {
|
|
76
|
-
const candidate = ConfigManager.listConfigFiles().find(f => existsSync(resolve(appConfig.path, f)))
|
|
77
|
-
|
|
78
|
-
if (candidate) {
|
|
79
|
-
appConfig.config = resolve(appConfig.path, candidate)
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
if (!appConfig.config) {
|
|
84
|
-
loadedConfig = await loadEmptyConfig(
|
|
85
|
-
appConfig.path,
|
|
86
|
-
{
|
|
87
|
-
onMissingEnv: this.#fetchServiceUrl,
|
|
88
|
-
context: appConfig
|
|
89
|
-
},
|
|
90
|
-
true
|
|
91
|
-
)
|
|
92
|
-
} else {
|
|
93
|
-
loadedConfig = await loadConfig(
|
|
94
|
-
{},
|
|
95
|
-
['-c', appConfig.config],
|
|
96
|
-
{
|
|
97
|
-
onMissingEnv: this.#fetchServiceUrl,
|
|
98
|
-
context: appConfig
|
|
99
|
-
},
|
|
100
|
-
true
|
|
101
|
-
)
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
const app = loadedConfig.app
|
|
105
|
-
|
|
106
|
-
if (appConfig.isProduction && !process.env.NODE_ENV) {
|
|
107
|
-
process.env.NODE_ENV = 'production'
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
const stackable = await app.buildStackable({
|
|
111
|
-
onMissingEnv: this.#fetchServiceUrl,
|
|
112
|
-
config: this.appConfig.config,
|
|
113
|
-
context: this.#context
|
|
114
|
-
})
|
|
115
|
-
this.stackable = this.#wrapStackable(stackable)
|
|
116
|
-
|
|
117
|
-
const metricsConfig = this.#context.metricsConfig
|
|
118
|
-
if (metricsConfig !== false) {
|
|
119
|
-
this.#metricsRegistry = await collectMetrics(this.stackable, this.appConfig.id, metricsConfig)
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
this.#updateDispatcher()
|
|
123
|
-
} catch (err) {
|
|
124
|
-
if (err.validationErrors) {
|
|
125
|
-
console.error('Validation errors:', err.validationErrors)
|
|
126
|
-
process.exit(1)
|
|
127
|
-
} else {
|
|
128
|
-
this.#logAndExit(err)
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
async start () {
|
|
134
|
-
if (this.#starting || this.#started) {
|
|
135
|
-
throw new errors.ApplicationAlreadyStartedError()
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
this.#starting = true
|
|
139
|
-
|
|
140
|
-
try {
|
|
141
|
-
await this.stackable.init()
|
|
142
|
-
} catch (err) {
|
|
143
|
-
this.#logAndExit(err)
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
if (this.#watch) {
|
|
147
|
-
const watchConfig = await this.stackable.getWatchConfig()
|
|
148
|
-
if (watchConfig.enabled !== false) {
|
|
149
|
-
/* c8 ignore next 4 */
|
|
150
|
-
this.#debouncedRestart = debounce(() => {
|
|
151
|
-
this.stackable.log({ message: 'files changed', level: 'debug' })
|
|
152
|
-
this.emit('changed')
|
|
153
|
-
}, 100) // debounce restart for 100ms
|
|
154
|
-
|
|
155
|
-
this.#startFileWatching(watchConfig)
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
const listen = !!this.appConfig.useHttp
|
|
160
|
-
try {
|
|
161
|
-
await this.stackable.start({ listen })
|
|
162
|
-
this.#listening = listen
|
|
163
|
-
/* c8 ignore next 5 */
|
|
164
|
-
} catch (err) {
|
|
165
|
-
this.stackable.log({ message: err.message, level: 'debug' })
|
|
166
|
-
this.#starting = false
|
|
167
|
-
throw err
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
this.#started = true
|
|
171
|
-
this.#starting = false
|
|
172
|
-
this.emit('start')
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
async stop () {
|
|
176
|
-
if (!this.#started || this.#starting) {
|
|
177
|
-
throw new errors.ApplicationNotStartedError()
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
await this.#stopFileWatching()
|
|
181
|
-
await this.stackable.stop()
|
|
182
|
-
|
|
183
|
-
this.#started = false
|
|
184
|
-
this.#starting = false
|
|
185
|
-
this.#listening = false
|
|
186
|
-
this.emit('stop')
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
async listen () {
|
|
190
|
-
// This server is not an entrypoint or already listened in start. Behave as no-op.
|
|
191
|
-
if (!this.appConfig.entrypoint || this.appConfig.useHttp || this.#listening) {
|
|
192
|
-
return
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
await this.stackable.start({ listen: true })
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
async getMetrics ({ format }) {
|
|
199
|
-
if (!this.#metricsRegistry) return null
|
|
200
|
-
|
|
201
|
-
return format === 'json' ? this.#metricsRegistry.getMetricsAsJSON() : this.#metricsRegistry.metrics()
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
#fetchServiceUrl (key, { parent, context: service }) {
|
|
205
|
-
if (service.localServiceEnvVars.has(key)) {
|
|
206
|
-
return service.localServiceEnvVars.get(key)
|
|
207
|
-
} else if (!key.endsWith('_URL') || !parent.serviceId) {
|
|
208
|
-
return null
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
return getServiceUrl(parent.serviceId)
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
#startFileWatching (watch) {
|
|
215
|
-
if (this.#fileWatcher) {
|
|
216
|
-
return
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
const fileWatcher = new FileWatcher({
|
|
220
|
-
path: watch.path,
|
|
221
|
-
/* c8 ignore next 2 */
|
|
222
|
-
allowToWatch: watch?.allow,
|
|
223
|
-
watchIgnore: watch?.ignore || []
|
|
224
|
-
})
|
|
225
|
-
|
|
226
|
-
fileWatcher.on('update', this.#debouncedRestart)
|
|
227
|
-
|
|
228
|
-
fileWatcher.startWatching()
|
|
229
|
-
this.stackable.log({ message: 'start watching files', level: 'debug' })
|
|
230
|
-
this.#fileWatcher = fileWatcher
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
async #stopFileWatching () {
|
|
234
|
-
const watcher = this.#fileWatcher
|
|
235
|
-
|
|
236
|
-
if (watcher) {
|
|
237
|
-
this.stackable.log({ message: 'stop watching files', level: 'debug' })
|
|
238
|
-
await watcher.stopWatching()
|
|
239
|
-
this.#fileWatcher = null
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
#logAndExit (err) {
|
|
244
|
-
console.error(err)
|
|
245
|
-
process.exit(1)
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
#wrapStackable (stackable) {
|
|
249
|
-
const newStackable = {}
|
|
250
|
-
for (const method of Object.keys(defaultStackable)) {
|
|
251
|
-
newStackable[method] = stackable[method] ? stackable[method].bind(stackable) : defaultStackable[method]
|
|
252
|
-
}
|
|
253
|
-
return newStackable
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
#updateDispatcher () {
|
|
257
|
-
const telemetryConfig = this.#context.telemetryConfig
|
|
258
|
-
const telemetryId = telemetryConfig?.serviceName
|
|
259
|
-
|
|
260
|
-
const interceptor = dispatch => {
|
|
261
|
-
return function InterceptedDispatch (opts, handler) {
|
|
262
|
-
if (telemetryId) {
|
|
263
|
-
opts.headers = {
|
|
264
|
-
...opts.headers,
|
|
265
|
-
'x-plt-telemetry-id': telemetryId
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
return dispatch(opts, handler)
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
const dispatcher = getGlobalDispatcher().compose(interceptor)
|
|
273
|
-
|
|
274
|
-
setGlobalDispatcher(dispatcher)
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
module.exports = { PlatformaticApp }
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
const defaultStackable = {
|
|
4
|
-
init: () => {},
|
|
5
|
-
start: () => {
|
|
6
|
-
throw new Error('Stackable start not implemented')
|
|
7
|
-
},
|
|
8
|
-
stop: () => {},
|
|
9
|
-
build: () => {},
|
|
10
|
-
getUrl: () => null,
|
|
11
|
-
updateContext: () => {},
|
|
12
|
-
getConfig: () => null,
|
|
13
|
-
getEnv: () => null,
|
|
14
|
-
getInfo: () => null,
|
|
15
|
-
getDispatchFunc: () => null,
|
|
16
|
-
getOpenapiSchema: () => null,
|
|
17
|
-
getGraphqlSchema: () => null,
|
|
18
|
-
getMeta: () => ({}),
|
|
19
|
-
collectMetrics: () => ({
|
|
20
|
-
defaultMetrics: true,
|
|
21
|
-
httpMetrics: true
|
|
22
|
-
}),
|
|
23
|
-
inject: () => {
|
|
24
|
-
throw new Error('Stackable inject not implemented')
|
|
25
|
-
},
|
|
26
|
-
log: ({ message }) => {
|
|
27
|
-
console.log(message)
|
|
28
|
-
},
|
|
29
|
-
getBootstrapDependencies: () => [],
|
|
30
|
-
getWatchConfig: () => ({ enabled: false })
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
module.exports = defaultStackable
|
package/lib/worker/metrics.js
DELETED
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
const os = require('node:os')
|
|
4
|
-
const { eventLoopUtilization } = require('node:perf_hooks').performance
|
|
5
|
-
const { Registry, Gauge, collectDefaultMetrics } = require('prom-client')
|
|
6
|
-
const collectHttpMetrics = require('@platformatic/http-metrics')
|
|
7
|
-
|
|
8
|
-
async function collectMetrics (stackable, serviceId, opts = {}) {
|
|
9
|
-
const registry = new Registry()
|
|
10
|
-
|
|
11
|
-
const httpRequestCallbacks = []
|
|
12
|
-
const httpResponseCallbacks = []
|
|
13
|
-
|
|
14
|
-
const metricsConfig = await stackable.collectMetrics({
|
|
15
|
-
registry,
|
|
16
|
-
startHttpTimer: options => httpRequestCallbacks.forEach(cb => cb(options)),
|
|
17
|
-
endHttpTimer: options => httpResponseCallbacks.forEach(cb => cb(options))
|
|
18
|
-
})
|
|
19
|
-
|
|
20
|
-
const labels = opts.labels ?? {}
|
|
21
|
-
registry.setDefaultLabels({ ...labels, serviceId })
|
|
22
|
-
|
|
23
|
-
if (metricsConfig.defaultMetrics) {
|
|
24
|
-
collectDefaultMetrics({ register: registry })
|
|
25
|
-
collectEluMetric(registry)
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
if (metricsConfig.httpMetrics) {
|
|
29
|
-
{
|
|
30
|
-
const { startTimer, endTimer } = collectHttpMetrics(registry, {
|
|
31
|
-
customLabels: ['telemetry_id'],
|
|
32
|
-
getCustomLabels: (req) => {
|
|
33
|
-
const telemetryId = req.headers?.['x-plt-telemetry-id'] ?? 'unknown'
|
|
34
|
-
return { telemetry_id: telemetryId }
|
|
35
|
-
}
|
|
36
|
-
})
|
|
37
|
-
httpRequestCallbacks.push(startTimer)
|
|
38
|
-
httpResponseCallbacks.push(endTimer)
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
{
|
|
42
|
-
// TODO: check if it's a nodejs environment
|
|
43
|
-
// Needed for the Meraki metrics
|
|
44
|
-
const { startTimer, endTimer } = collectHttpMetrics(registry, {
|
|
45
|
-
customLabels: ['telemetry_id'],
|
|
46
|
-
getCustomLabels: (req) => {
|
|
47
|
-
const telemetryId = req.headers?.['x-plt-telemetry-id'] ?? 'unknown'
|
|
48
|
-
return { telemetry_id: telemetryId }
|
|
49
|
-
},
|
|
50
|
-
histogram: {
|
|
51
|
-
name: 'http_request_all_duration_seconds',
|
|
52
|
-
help: 'request duration in seconds summary for all requests',
|
|
53
|
-
collect: function () {
|
|
54
|
-
process.nextTick(() => this.reset())
|
|
55
|
-
},
|
|
56
|
-
},
|
|
57
|
-
summary: {
|
|
58
|
-
name: 'http_request_all_summary_seconds',
|
|
59
|
-
help: 'request duration in seconds histogram for all requests',
|
|
60
|
-
collect: function () {
|
|
61
|
-
process.nextTick(() => this.reset())
|
|
62
|
-
},
|
|
63
|
-
},
|
|
64
|
-
})
|
|
65
|
-
httpRequestCallbacks.push(startTimer)
|
|
66
|
-
httpResponseCallbacks.push(endTimer)
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
return registry
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
function collectEluMetric (register) {
|
|
74
|
-
let startELU = eventLoopUtilization()
|
|
75
|
-
const eluMetric = new Gauge({
|
|
76
|
-
name: 'nodejs_eventloop_utilization',
|
|
77
|
-
help: 'The event loop utilization as a fraction of the loop time. 1 is fully utilized, 0 is fully idle.',
|
|
78
|
-
collect: () => {
|
|
79
|
-
const endELU = eventLoopUtilization()
|
|
80
|
-
const result = eventLoopUtilization(endELU, startELU).utilization
|
|
81
|
-
eluMetric.set(result)
|
|
82
|
-
startELU = endELU
|
|
83
|
-
},
|
|
84
|
-
registers: [register],
|
|
85
|
-
})
|
|
86
|
-
register.registerMetric(eluMetric)
|
|
87
|
-
|
|
88
|
-
let previousIdleTime = 0
|
|
89
|
-
let previousTotalTime = 0
|
|
90
|
-
const cpuMetric = new Gauge({
|
|
91
|
-
name: 'process_cpu_percent_usage',
|
|
92
|
-
help: 'The process CPU percent usage.',
|
|
93
|
-
collect: () => {
|
|
94
|
-
const cpus = os.cpus()
|
|
95
|
-
let idleTime = 0
|
|
96
|
-
let totalTime = 0
|
|
97
|
-
|
|
98
|
-
cpus.forEach(cpu => {
|
|
99
|
-
for (const type in cpu.times) {
|
|
100
|
-
totalTime += cpu.times[type]
|
|
101
|
-
if (type === 'idle') {
|
|
102
|
-
idleTime += cpu.times[type]
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
})
|
|
106
|
-
|
|
107
|
-
const idleDiff = idleTime - previousIdleTime
|
|
108
|
-
const totalDiff = totalTime - previousTotalTime
|
|
109
|
-
|
|
110
|
-
const usagePercent = 100 - ((100 * idleDiff) / totalDiff)
|
|
111
|
-
const roundedUsage = Math.round(usagePercent * 100) / 100
|
|
112
|
-
cpuMetric.set(roundedUsage)
|
|
113
|
-
|
|
114
|
-
previousIdleTime = idleTime
|
|
115
|
-
previousTotalTime = totalTime
|
|
116
|
-
},
|
|
117
|
-
registers: [register],
|
|
118
|
-
})
|
|
119
|
-
register.registerMetric(cpuMetric)
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
module.exports = { collectMetrics }
|
package/runtime.mjs
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
#! /usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import { readFile } from 'node:fs/promises'
|
|
4
|
-
import commist from 'commist'
|
|
5
|
-
import { join } from 'desm'
|
|
6
|
-
import isMain from 'es-main'
|
|
7
|
-
import helpMe from 'help-me'
|
|
8
|
-
import parseArgs from 'minimist'
|
|
9
|
-
import { startCommand } from './index.js'
|
|
10
|
-
import { compile as compileCmd } from './lib/compile.js'
|
|
11
|
-
|
|
12
|
-
export const compile = compileCmd
|
|
13
|
-
|
|
14
|
-
const help = helpMe({
|
|
15
|
-
dir: join(import.meta.url, 'help'),
|
|
16
|
-
// the default
|
|
17
|
-
ext: '.txt',
|
|
18
|
-
})
|
|
19
|
-
|
|
20
|
-
const program = commist({ maxDistance: 2 })
|
|
21
|
-
|
|
22
|
-
program.register('help', help.toStdout)
|
|
23
|
-
program.register('help start', help.toStdout.bind(null, ['start']))
|
|
24
|
-
program.register('help compile', help.toStdout.bind(null, ['compile']))
|
|
25
|
-
program.register('start', startCommand)
|
|
26
|
-
program.register('compile', compile)
|
|
27
|
-
|
|
28
|
-
export async function run (argv) {
|
|
29
|
-
const args = parseArgs(argv, {
|
|
30
|
-
alias: {
|
|
31
|
-
v: 'version',
|
|
32
|
-
},
|
|
33
|
-
})
|
|
34
|
-
|
|
35
|
-
if (args.version) {
|
|
36
|
-
console.log('v' + JSON.parse(await readFile(join(import.meta.url, 'package.json'), 'utf-8')).version)
|
|
37
|
-
process.exit(0)
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/* c8 ignore next 4 */
|
|
41
|
-
return {
|
|
42
|
-
output: await program.parseAsync(argv),
|
|
43
|
-
help,
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
if (isMain(import.meta)) {
|
|
48
|
-
try {
|
|
49
|
-
await run(process.argv.slice(2))
|
|
50
|
-
} catch (err) {
|
|
51
|
-
console.error(err)
|
|
52
|
-
process.exit(1)
|
|
53
|
-
}
|
|
54
|
-
}
|