@platformatic/basic 2.71.1-alpha.0 → 3.0.0-alpha.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/config.d.ts +1 -1
- package/index.d.ts +89 -0
- package/index.js +3 -140
- package/lib/base.js +90 -113
- package/lib/config.js +81 -0
- package/lib/creation.js +9 -0
- package/lib/modules.js +101 -0
- package/lib/schema.js +2 -1
- package/lib/worker/child-manager.js +1 -1
- package/lib/worker/child-process.js +1 -63
- package/lib/worker/listeners.js +3 -3
- package/package.json +8 -9
- package/schema.json +2 -2
package/config.d.ts
CHANGED
package/index.d.ts
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
export interface StartOptions {
|
|
2
|
+
listen?: boolean
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export interface Dependency {
|
|
6
|
+
id: string
|
|
7
|
+
url?: string
|
|
8
|
+
local: boolean
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export type BaseContext = Partial<{
|
|
12
|
+
serviceId: string
|
|
13
|
+
isEntrypoint: boolean
|
|
14
|
+
isProduction: boolean
|
|
15
|
+
isStandalone: boolean
|
|
16
|
+
directory: string
|
|
17
|
+
telemetryConfig: object
|
|
18
|
+
metricsConfig: object
|
|
19
|
+
serverConfig: object
|
|
20
|
+
hasManagementApi: boolean
|
|
21
|
+
localServiceEnvVars: Map<string, string>
|
|
22
|
+
}>
|
|
23
|
+
|
|
24
|
+
export interface BaseOptions<Context = BaseContext> {
|
|
25
|
+
context: Context
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export declare const schemaOptions: Partial<Record<string, unknown>>
|
|
29
|
+
|
|
30
|
+
export class BaseStackable<Config = Record<string, any>, Options = BaseOptions> {
|
|
31
|
+
basePath: string
|
|
32
|
+
constructor (
|
|
33
|
+
type: string,
|
|
34
|
+
version: string,
|
|
35
|
+
root: string,
|
|
36
|
+
config: object,
|
|
37
|
+
standardStreams?: Record<string, NodeJS.WritableStream>
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
init (): Promise<void>
|
|
41
|
+
start (options: StartOptions): Promise<void>
|
|
42
|
+
stop (): Promise<void>
|
|
43
|
+
build (): Promise<void>
|
|
44
|
+
getUrl (): string
|
|
45
|
+
updateContext (context: Partial<BaseContext>): Promise<void>
|
|
46
|
+
getConfig (includeMeta?: boolean): Promise<object>
|
|
47
|
+
getInfo (): Promise<{ type: string; version: string }>
|
|
48
|
+
getDispatchFunc (): Promise<Function>
|
|
49
|
+
getDispatchTarget (): Promise<Function | string>
|
|
50
|
+
getOpenapiSchema (): Promise<object>
|
|
51
|
+
getGraphqlSchema (): Promise<string>
|
|
52
|
+
setConnectionStatus (status: string): Promise<void>
|
|
53
|
+
setOpenapiSchema (schema: object): Promise<void>
|
|
54
|
+
setGraphqlSchema (schema: string): Promise<void>
|
|
55
|
+
setCustomHealthCheck (
|
|
56
|
+
healthCheck: () =>
|
|
57
|
+
| boolean
|
|
58
|
+
| Promise<boolean>
|
|
59
|
+
| { status: boolean; statusCode?: number; body?: string }
|
|
60
|
+
| Promise<{ status: boolean; statusCode?: number; body?: string }>
|
|
61
|
+
): Promise<void>
|
|
62
|
+
setCustomReadinessCheck (
|
|
63
|
+
readinessCheck: () =>
|
|
64
|
+
| boolean
|
|
65
|
+
| Promise<boolean>
|
|
66
|
+
| { status: boolean; statusCode?: number; body?: string }
|
|
67
|
+
| Promise<{ status: boolean; statusCode?: number; body?: string }>
|
|
68
|
+
): Promise<void>
|
|
69
|
+
collectMetrics (): Promise<any>
|
|
70
|
+
getMetrics ({ format: string }): Promise<string | Array<object>>
|
|
71
|
+
getMeta (): Promise<object>
|
|
72
|
+
inject (injectParams: string | object): Promise<{
|
|
73
|
+
statusCode: number
|
|
74
|
+
statusMessage: string
|
|
75
|
+
headers: object
|
|
76
|
+
body: object
|
|
77
|
+
}>
|
|
78
|
+
log (options: { message: string; level: string }): Promise<void>
|
|
79
|
+
getBootstrapDependencies (): Promise<Dependency[]>
|
|
80
|
+
getWatchConfig (): Promise<{
|
|
81
|
+
enabled: boolean
|
|
82
|
+
path: string
|
|
83
|
+
allow?: string[]
|
|
84
|
+
ignore?: string[]
|
|
85
|
+
}>
|
|
86
|
+
|
|
87
|
+
_initializeLogger (options: object): Promise<void>
|
|
88
|
+
_collectMetrics (): Promise<void>
|
|
89
|
+
}
|
package/index.js
CHANGED
|
@@ -1,145 +1,8 @@
|
|
|
1
|
-
import { ConfigManager } from '@platformatic/config'
|
|
2
|
-
import { detectApplicationType } from '@platformatic/utils'
|
|
3
|
-
import jsonPatch from 'fast-json-patch'
|
|
4
|
-
import { readFile } from 'node:fs/promises'
|
|
5
|
-
import { createRequire } from 'node:module'
|
|
6
|
-
import { relative, resolve } from 'node:path'
|
|
7
|
-
import { workerData } from 'node:worker_threads'
|
|
8
|
-
import pino from 'pino'
|
|
9
|
-
import { packageJson, schema } from './lib/schema.js'
|
|
10
|
-
import { importFile } from './lib/utils.js'
|
|
11
|
-
|
|
12
|
-
const importStackablePackageMarker = '__pltImportStackablePackage.js'
|
|
13
|
-
|
|
14
|
-
function isImportFailedError (error, pkg) {
|
|
15
|
-
if (error.code !== 'ERR_MODULE_NOT_FOUND' && error.code !== 'MODULE_NOT_FOUND') {
|
|
16
|
-
return false
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const match = error.message.match(/Cannot find package '(.+)' imported from (.+)/)
|
|
20
|
-
|
|
21
|
-
return match?.[1] === pkg || error.requireStack?.[0].endsWith(importStackablePackageMarker)
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
async function importStackablePackage (directory, pkg) {
|
|
25
|
-
try {
|
|
26
|
-
try {
|
|
27
|
-
// Try regular import
|
|
28
|
-
return await import(pkg)
|
|
29
|
-
} catch (e) {
|
|
30
|
-
if (!isImportFailedError(e, pkg)) {
|
|
31
|
-
throw e
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// Scope to the service
|
|
35
|
-
const require = createRequire(resolve(directory, importStackablePackageMarker))
|
|
36
|
-
const imported = require.resolve(pkg)
|
|
37
|
-
return await importFile(imported)
|
|
38
|
-
}
|
|
39
|
-
} catch (e) {
|
|
40
|
-
if (!isImportFailedError(e, pkg)) {
|
|
41
|
-
throw e
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const serviceDirectory = workerData ? relative(workerData.dirname, directory) : directory
|
|
45
|
-
throw new Error(
|
|
46
|
-
`Unable to import package '${pkg}'. Please add it as a dependency in the package.json file in the folder ${serviceDirectory}.`
|
|
47
|
-
)
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export async function importStackableAndConfig (root, config, context) {
|
|
52
|
-
let rootPackageJson
|
|
53
|
-
try {
|
|
54
|
-
rootPackageJson = JSON.parse(await readFile(resolve(root, 'package.json'), 'utf-8'))
|
|
55
|
-
} catch {
|
|
56
|
-
rootPackageJson = {}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const hadConfig = !!config
|
|
60
|
-
|
|
61
|
-
if (!config) {
|
|
62
|
-
config = await ConfigManager.findConfigFile(root, 'application')
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const appType = await detectApplicationType(root, rootPackageJson)
|
|
66
|
-
|
|
67
|
-
if (!appType) {
|
|
68
|
-
throw new Error(`Unable to detect application type in ${root}.`)
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const { label, name: moduleName } = appType
|
|
72
|
-
|
|
73
|
-
if (context) {
|
|
74
|
-
const serviceRoot = relative(process.cwd(), root)
|
|
75
|
-
|
|
76
|
-
if (!hadConfig && context.serviceId && !(await ConfigManager.findConfigFile(root)) && context.worker?.index === 0) {
|
|
77
|
-
const autodetectDescription =
|
|
78
|
-
moduleName === '@platformatic/node' ? 'is a generic Node.js application' : `is using ${label}`
|
|
79
|
-
|
|
80
|
-
const logger = pino({ level: context.serverConfig?.logger?.level ?? 'warn', name: context.serviceId })
|
|
81
|
-
|
|
82
|
-
logger.warn(`We have auto-detected that service "${context.serviceId}" ${autodetectDescription}.`)
|
|
83
|
-
logger.warn(
|
|
84
|
-
`We suggest you create a watt.json or a platformatic.json file in the folder ${serviceRoot} with the "$schema" property set to "https://schemas.platformatic.dev/${moduleName}/${packageJson.version}.json".`
|
|
85
|
-
)
|
|
86
|
-
logger.warn(`Also don't forget to add "${moduleName}" to the service dependencies.`)
|
|
87
|
-
logger.warn('You can also run "wattpm import" to do this automatically.\n')
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
const stackable = await importStackablePackage(root, moduleName)
|
|
92
|
-
|
|
93
|
-
return {
|
|
94
|
-
stackable,
|
|
95
|
-
config,
|
|
96
|
-
autodetectDescription:
|
|
97
|
-
moduleName === '@platformatic/node' ? 'is a generic Node.js application' : `is using ${label}`,
|
|
98
|
-
moduleName
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
async function buildStackable (opts) {
|
|
103
|
-
const hadConfig = !!opts.config
|
|
104
|
-
const { stackable, config } = await importStackableAndConfig(opts.context.directory, opts.config, opts.context)
|
|
105
|
-
opts.config = config
|
|
106
|
-
|
|
107
|
-
if (!hadConfig && typeof stackable.createDefaultConfig === 'function') {
|
|
108
|
-
opts.config = await stackable.createDefaultConfig?.(opts)
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
return stackable.buildStackable(opts)
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/* c8 ignore next 3 */
|
|
115
|
-
export async function transformConfig () {
|
|
116
|
-
const patch = workerData?.serviceConfig?.configPatch
|
|
117
|
-
|
|
118
|
-
if (Array.isArray(patch)) {
|
|
119
|
-
this.current = jsonPatch.applyPatch(this.current, patch).newDocument
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
export const schemaOptions = {
|
|
124
|
-
useDefaults: true,
|
|
125
|
-
coerceTypes: true,
|
|
126
|
-
allErrors: true,
|
|
127
|
-
strict: false
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
export default {
|
|
131
|
-
configType: 'nodejs',
|
|
132
|
-
configManagerConfig: {
|
|
133
|
-
schemaOptions,
|
|
134
|
-
transformConfig
|
|
135
|
-
},
|
|
136
|
-
buildStackable,
|
|
137
|
-
schema,
|
|
138
|
-
version: packageJson.version
|
|
139
|
-
}
|
|
140
|
-
|
|
141
1
|
export * from './lib/base.js'
|
|
2
|
+
export * from './lib/config.js'
|
|
3
|
+
export * from './lib/creation.js'
|
|
142
4
|
export * as errors from './lib/errors.js'
|
|
5
|
+
export * from './lib/modules.js'
|
|
143
6
|
export { schema, schemaComponents } from './lib/schema.js'
|
|
144
7
|
export * from './lib/utils.js'
|
|
145
8
|
export * from './lib/worker/child-manager.js'
|
package/lib/base.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { client, collectMetrics } from '@platformatic/metrics'
|
|
2
|
-
import { buildPinoOptions, deepmerge, executeWithTimeout, kTimeout } from '@platformatic/utils'
|
|
2
|
+
import { buildPinoOptions, deepmerge, executeWithTimeout, kMetadata, kTimeout } from '@platformatic/utils'
|
|
3
3
|
import { parseCommandString } from 'execa'
|
|
4
4
|
import { spawn } from 'node:child_process'
|
|
5
5
|
import EventEmitter, { once } from 'node:events'
|
|
@@ -11,7 +11,6 @@ import pino from 'pino'
|
|
|
11
11
|
import { NonZeroExitCode } from './errors.js'
|
|
12
12
|
import { cleanBasePath } from './utils.js'
|
|
13
13
|
import { ChildManager } from './worker/child-manager.js'
|
|
14
|
-
|
|
15
14
|
const kITC = Symbol.for('plt.runtime.itc')
|
|
16
15
|
|
|
17
16
|
export class BaseStackable extends EventEmitter {
|
|
@@ -22,47 +21,39 @@ export class BaseStackable extends EventEmitter {
|
|
|
22
21
|
#subprocessStarted
|
|
23
22
|
#metricsCollected
|
|
24
23
|
|
|
25
|
-
constructor (type, version,
|
|
24
|
+
constructor (type, version, root, config, context, standardStreams = {}) {
|
|
26
25
|
super()
|
|
27
26
|
|
|
28
|
-
options.context.worker ??= { count: 1, index: 0 }
|
|
29
|
-
|
|
30
27
|
this.type = type
|
|
31
28
|
this.version = version
|
|
32
|
-
this.serviceId = options.context.serviceId
|
|
33
|
-
this.workerId = options.context.worker.count > 1 ? options.context.worker.index : undefined
|
|
34
|
-
this.telemetryConfig = options.context.telemetryConfig
|
|
35
|
-
this.options = options
|
|
36
29
|
this.root = root
|
|
37
|
-
this.
|
|
38
|
-
this.
|
|
30
|
+
this.config = config
|
|
31
|
+
this.context = context ?? {}
|
|
32
|
+
this.context.worker ??= { count: 1, index: 0 }
|
|
33
|
+
this.standardStreams = standardStreams
|
|
34
|
+
|
|
35
|
+
this.serviceId = this.context.serviceId
|
|
36
|
+
this.workerId = this.context.worker.count > 1 ? this.context.worker.index : undefined
|
|
37
|
+
this.telemetryConfig = this.context.telemetryConfig
|
|
38
|
+
this.serverConfig = deepmerge(this.context.serverConfig ?? {}, config.server ?? {})
|
|
39
39
|
this.openapiSchema = null
|
|
40
40
|
this.graphqlSchema = null
|
|
41
41
|
this.connectionString = null
|
|
42
42
|
this.basePath = null
|
|
43
|
-
this.isEntrypoint =
|
|
44
|
-
this.isProduction =
|
|
43
|
+
this.isEntrypoint = this.context.isEntrypoint
|
|
44
|
+
this.isProduction = this.context.isProduction
|
|
45
45
|
this.metricsRegistry = new client.Registry()
|
|
46
46
|
this.#metricsCollected = false
|
|
47
47
|
this.customHealthCheck = null
|
|
48
48
|
this.customReadinessCheck = null
|
|
49
49
|
this.clientWs = null
|
|
50
|
-
this.runtimeConfig = deepmerge(
|
|
50
|
+
this.runtimeConfig = deepmerge(this.context?.runtimeConfig ?? {}, workerData?.config ?? {})
|
|
51
51
|
this.stdout = standardStreams?.stdout ?? process.stdout
|
|
52
52
|
this.stderr = standardStreams?.stderr ?? process.stderr
|
|
53
53
|
this.subprocessForceClose = false
|
|
54
54
|
this.subprocessTerminationSignal = 'SIGINT'
|
|
55
55
|
|
|
56
|
-
|
|
57
|
-
const pinoOptions = buildPinoOptions(
|
|
58
|
-
loggerOptions,
|
|
59
|
-
this.serverConfig?.logger,
|
|
60
|
-
this.serviceId,
|
|
61
|
-
this.workerId,
|
|
62
|
-
options,
|
|
63
|
-
this.root
|
|
64
|
-
)
|
|
65
|
-
this.logger = pino(pinoOptions, standardStreams?.stdout)
|
|
56
|
+
this.logger = this._initializeLogger()
|
|
66
57
|
|
|
67
58
|
// Setup globals
|
|
68
59
|
this.registerGlobals({
|
|
@@ -85,20 +76,50 @@ export class BaseStackable extends EventEmitter {
|
|
|
85
76
|
})
|
|
86
77
|
}
|
|
87
78
|
|
|
79
|
+
init () {
|
|
80
|
+
return this.updateContext()
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
updateContext (context) {
|
|
84
|
+
// No-op
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
start () {
|
|
88
|
+
throw new Error('BaseStackable.start must be overriden by the subclasses')
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
stop () {
|
|
92
|
+
throw new Error('BaseStackable.stop must be overriden by the subclasses')
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Alias for stop
|
|
96
|
+
close () {
|
|
97
|
+
return this.stop()
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
inject () {
|
|
101
|
+
throw new Error('BaseStackable.inject must be overriden by the subclasses')
|
|
102
|
+
}
|
|
103
|
+
|
|
88
104
|
getUrl () {
|
|
89
105
|
return this.url
|
|
90
106
|
}
|
|
91
107
|
|
|
92
|
-
async getConfig () {
|
|
93
|
-
|
|
108
|
+
async getConfig (includeMeta = false) {
|
|
109
|
+
if (includeMeta) {
|
|
110
|
+
return this.config
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const { [kMetadata]: _, ...config } = this.config
|
|
114
|
+
return config
|
|
94
115
|
}
|
|
95
116
|
|
|
96
117
|
async getEnv () {
|
|
97
|
-
return this.
|
|
118
|
+
return this.config[kMetadata].env
|
|
98
119
|
}
|
|
99
120
|
|
|
100
121
|
async getWatchConfig () {
|
|
101
|
-
const config = this.
|
|
122
|
+
const config = this.config
|
|
102
123
|
|
|
103
124
|
const enabled = config.watch?.enabled !== false
|
|
104
125
|
|
|
@@ -123,7 +144,7 @@ export class BaseStackable extends EventEmitter {
|
|
|
123
144
|
}
|
|
124
145
|
|
|
125
146
|
async getDispatchTarget () {
|
|
126
|
-
return this.getUrl() ?? this.getDispatchFunc()
|
|
147
|
+
return this.getUrl() ?? (await this.getDispatchFunc())
|
|
127
148
|
}
|
|
128
149
|
|
|
129
150
|
getMeta () {
|
|
@@ -242,7 +263,7 @@ export class BaseStackable extends EventEmitter {
|
|
|
242
263
|
}
|
|
243
264
|
|
|
244
265
|
async startWithCommand (command, loader, scripts) {
|
|
245
|
-
const config = this.
|
|
266
|
+
const config = this.config
|
|
246
267
|
const basePath = config.application?.basePath ? cleanBasePath(config.application?.basePath) : ''
|
|
247
268
|
|
|
248
269
|
const context = await this.getChildManagerContext(basePath)
|
|
@@ -342,7 +363,7 @@ export class BaseStackable extends EventEmitter {
|
|
|
342
363
|
|
|
343
364
|
return {
|
|
344
365
|
id: this.id,
|
|
345
|
-
config: this.
|
|
366
|
+
config: this.config,
|
|
346
367
|
serviceId: this.serviceId,
|
|
347
368
|
workerId: this.workerId,
|
|
348
369
|
// Always use URL to avoid serialization problem in Windows
|
|
@@ -388,46 +409,62 @@ export class BaseStackable extends EventEmitter {
|
|
|
388
409
|
this.emit('config', config)
|
|
389
410
|
}
|
|
390
411
|
|
|
412
|
+
_initializeLogger () {
|
|
413
|
+
const loggerOptions = deepmerge(this.runtimeConfig?.logger ?? {}, this.config?.logger ?? {})
|
|
414
|
+
const pinoOptions = buildPinoOptions(
|
|
415
|
+
loggerOptions,
|
|
416
|
+
this.serverConfig?.logger,
|
|
417
|
+
this.serviceId,
|
|
418
|
+
this.workerId,
|
|
419
|
+
this.context,
|
|
420
|
+
this.root
|
|
421
|
+
)
|
|
422
|
+
|
|
423
|
+
return pino(pinoOptions, this.standardStreams?.stdout)
|
|
424
|
+
}
|
|
425
|
+
|
|
391
426
|
async _collectMetrics () {
|
|
392
427
|
if (this.#metricsCollected) {
|
|
393
428
|
return
|
|
394
429
|
}
|
|
395
430
|
|
|
396
431
|
this.#metricsCollected = true
|
|
432
|
+
|
|
433
|
+
if (this.context.metricsConfig === false) {
|
|
434
|
+
return
|
|
435
|
+
}
|
|
436
|
+
|
|
397
437
|
await this.#collectMetrics()
|
|
398
438
|
this.#setHttpCacheMetrics()
|
|
399
439
|
}
|
|
400
440
|
|
|
401
441
|
async #collectMetrics () {
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
...metricsConfig
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
if (this.childManager && this.clientWs) {
|
|
411
|
-
await this.childManager.send(this.clientWs, 'collectMetrics', {
|
|
412
|
-
serviceId: this.serviceId,
|
|
413
|
-
workerId: this.workerId,
|
|
414
|
-
metricsConfig
|
|
415
|
-
})
|
|
416
|
-
return
|
|
417
|
-
}
|
|
442
|
+
const metricsConfig = {
|
|
443
|
+
defaultMetrics: true,
|
|
444
|
+
httpMetrics: true,
|
|
445
|
+
...this.context.metricsConfig
|
|
446
|
+
}
|
|
418
447
|
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
this.
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
)
|
|
448
|
+
if (this.childManager && this.clientWs) {
|
|
449
|
+
await this.childManager.send(this.clientWs, 'collectMetrics', {
|
|
450
|
+
serviceId: this.serviceId,
|
|
451
|
+
workerId: this.workerId,
|
|
452
|
+
metricsConfig
|
|
453
|
+
})
|
|
454
|
+
return
|
|
425
455
|
}
|
|
456
|
+
|
|
457
|
+
await collectMetrics(this.serviceId, this.workerId, metricsConfig, this.metricsRegistry)
|
|
426
458
|
}
|
|
427
459
|
|
|
428
460
|
#setHttpCacheMetrics () {
|
|
429
461
|
const { client, registry } = globalThis.platformatic.prometheus
|
|
430
462
|
|
|
463
|
+
// Metrics already registered, no need to register them again
|
|
464
|
+
if (registry.getSingleMetric('http_cache_hit_count') || registry.getSingleMetric('http_cache_miss_count')) {
|
|
465
|
+
return
|
|
466
|
+
}
|
|
467
|
+
|
|
431
468
|
const cacheHitMetric = new client.Counter({
|
|
432
469
|
name: 'http_cache_hit_count',
|
|
433
470
|
help: 'Number of http cache hits',
|
|
@@ -446,66 +483,6 @@ export class BaseStackable extends EventEmitter {
|
|
|
446
483
|
globalThis.platformatic.onHttpCacheMiss = () => {
|
|
447
484
|
cacheMissMetric.inc()
|
|
448
485
|
}
|
|
449
|
-
|
|
450
|
-
const httpStatsFreeMetric = new client.Gauge({
|
|
451
|
-
name: 'http_client_stats_free',
|
|
452
|
-
help: 'Number of free (idle) http clients (sockets)',
|
|
453
|
-
labelNames: ['dispatcher_stats_url'],
|
|
454
|
-
registers: [registry]
|
|
455
|
-
})
|
|
456
|
-
globalThis.platformatic.onHttpStatsFree = (url, val) => {
|
|
457
|
-
httpStatsFreeMetric.set({ dispatcher_stats_url: url }, val)
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
const httpStatsConnectedMetric = new client.Gauge({
|
|
461
|
-
name: 'http_client_stats_connected',
|
|
462
|
-
help: 'Number of open socket connections',
|
|
463
|
-
labelNames: ['dispatcher_stats_url'],
|
|
464
|
-
registers: [registry]
|
|
465
|
-
})
|
|
466
|
-
globalThis.platformatic.onHttpStatsConnected = (url, val) => {
|
|
467
|
-
httpStatsConnectedMetric.set({ dispatcher_stats_url: url }, val)
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
const httpStatsPendingMetric = new client.Gauge({
|
|
471
|
-
name: 'http_client_stats_pending',
|
|
472
|
-
help: 'Number of pending requests across all clients',
|
|
473
|
-
labelNames: ['dispatcher_stats_url'],
|
|
474
|
-
registers: [registry]
|
|
475
|
-
})
|
|
476
|
-
globalThis.platformatic.onHttpStatsPending = (url, val) => {
|
|
477
|
-
httpStatsPendingMetric.set({ dispatcher_stats_url: url }, val)
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
const httpStatsQueuedMetric = new client.Gauge({
|
|
481
|
-
name: 'http_client_stats_queued',
|
|
482
|
-
help: 'Number of queued requests across all clients',
|
|
483
|
-
labelNames: ['dispatcher_stats_url'],
|
|
484
|
-
registers: [registry]
|
|
485
|
-
})
|
|
486
|
-
globalThis.platformatic.onHttpStatsQueued = (url, val) => {
|
|
487
|
-
httpStatsQueuedMetric.set({ dispatcher_stats_url: url }, val)
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
const httpStatsRunningMetric = new client.Gauge({
|
|
491
|
-
name: 'http_client_stats_running',
|
|
492
|
-
help: 'Number of currently active requests across all clients',
|
|
493
|
-
labelNames: ['dispatcher_stats_url'],
|
|
494
|
-
registers: [registry]
|
|
495
|
-
})
|
|
496
|
-
globalThis.platformatic.onHttpStatsRunning = (url, val) => {
|
|
497
|
-
httpStatsRunningMetric.set({ dispatcher_stats_url: url }, val)
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
const httpStatsSizeMetric = new client.Gauge({
|
|
501
|
-
name: 'http_client_stats_size',
|
|
502
|
-
help: 'Number of active, pending, or queued requests across all clients',
|
|
503
|
-
labelNames: ['dispatcher_stats_url'],
|
|
504
|
-
registers: [registry]
|
|
505
|
-
})
|
|
506
|
-
globalThis.platformatic.onHttpStatsSize = (url, val) => {
|
|
507
|
-
httpStatsSizeMetric.set({ dispatcher_stats_url: url }, val)
|
|
508
|
-
}
|
|
509
486
|
}
|
|
510
487
|
|
|
511
488
|
async #invalidateHttpCache (opts = {}) {
|
package/lib/config.js
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import {
|
|
2
|
+
listRecognizedConfigurationFiles,
|
|
3
|
+
NoConfigFileFoundError,
|
|
4
|
+
findConfigurationFile as utilsFindConfigurationFile
|
|
5
|
+
} from '@platformatic/utils'
|
|
6
|
+
import jsonPatch from 'fast-json-patch'
|
|
7
|
+
import { stat } from 'node:fs/promises'
|
|
8
|
+
import { dirname, resolve as resolvePath } from 'node:path'
|
|
9
|
+
import { workerData } from 'node:worker_threads'
|
|
10
|
+
|
|
11
|
+
export async function findConfigurationFile (root, suffixes) {
|
|
12
|
+
const file = await utilsFindConfigurationFile(root, suffixes)
|
|
13
|
+
|
|
14
|
+
if (!file) {
|
|
15
|
+
const err = new NoConfigFileFoundError()
|
|
16
|
+
err.message = `No config file found in the directory ${root}. Please create one of the following files: ${listRecognizedConfigurationFiles(suffixes, ['json']).join(', ')}`
|
|
17
|
+
|
|
18
|
+
throw err
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return resolvePath(root, file)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export async function resolve (fileOrDirectory, sourceOrConfig, suffixes) {
|
|
25
|
+
if (sourceOrConfig && typeof sourceOrConfig !== 'string') {
|
|
26
|
+
return {
|
|
27
|
+
root: fileOrDirectory,
|
|
28
|
+
source: sourceOrConfig
|
|
29
|
+
}
|
|
30
|
+
} else if (typeof fileOrDirectory === 'string' && typeof sourceOrConfig === 'string') {
|
|
31
|
+
return {
|
|
32
|
+
root: fileOrDirectory,
|
|
33
|
+
source: resolvePath(fileOrDirectory, sourceOrConfig)
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
const fileInfo = await stat(fileOrDirectory)
|
|
39
|
+
|
|
40
|
+
if (fileInfo.isFile()) {
|
|
41
|
+
return {
|
|
42
|
+
root: dirname(fileOrDirectory),
|
|
43
|
+
source: fileOrDirectory
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
} catch {
|
|
47
|
+
// No-op
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
root: fileOrDirectory,
|
|
52
|
+
source: await findConfigurationFile(fileOrDirectory, suffixes)
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export async function transform (config) {
|
|
57
|
+
const patch = workerData?.serviceConfig?.configPatch
|
|
58
|
+
|
|
59
|
+
if (!config) {
|
|
60
|
+
return config
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (Array.isArray(patch)) {
|
|
64
|
+
config = jsonPatch.applyPatch(config, patch).newDocument
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (config.watch === undefined) {
|
|
68
|
+
config.watch = { enabled: workerData?.config?.watch ?? false }
|
|
69
|
+
} else if (typeof config.watch !== 'object') {
|
|
70
|
+
config.watch = { enabled: config.watch || false }
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return config
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export const validationOptions = {
|
|
77
|
+
useDefaults: true,
|
|
78
|
+
coerceTypes: true,
|
|
79
|
+
allErrors: true,
|
|
80
|
+
strict: false
|
|
81
|
+
}
|
package/lib/creation.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { resolve } from './config.js'
|
|
2
|
+
import { importStackableAndConfig } from './modules.js'
|
|
3
|
+
|
|
4
|
+
export async function create (fileOrDirectory, sourceOrConfig, context) {
|
|
5
|
+
const { root, source } = await resolve(fileOrDirectory, sourceOrConfig)
|
|
6
|
+
const { stackable } = await importStackableAndConfig(root, source, context)
|
|
7
|
+
|
|
8
|
+
return stackable.create(root, source, context)
|
|
9
|
+
}
|
package/lib/modules.js
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { detectApplicationType, findConfigurationFile } from '@platformatic/utils'
|
|
2
|
+
import { readFile } from 'node:fs/promises'
|
|
3
|
+
import { createRequire } from 'node:module'
|
|
4
|
+
import { relative, resolve } from 'node:path'
|
|
5
|
+
import { workerData } from 'node:worker_threads'
|
|
6
|
+
import pino from 'pino'
|
|
7
|
+
import { packageJson } from './schema.js'
|
|
8
|
+
import { importFile } from './utils.js'
|
|
9
|
+
|
|
10
|
+
const importStackablePackageMarker = '__pltImportStackablePackage.js'
|
|
11
|
+
|
|
12
|
+
export function isImportFailedError (error, pkg) {
|
|
13
|
+
if (error.code !== 'ERR_MODULE_NOT_FOUND' && error.code !== 'MODULE_NOT_FOUND') {
|
|
14
|
+
return false
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const match = error.message.match(/Cannot find package '(.+)' imported from (.+)/)
|
|
18
|
+
|
|
19
|
+
return match?.[1] === pkg || error.requireStack?.[0].endsWith(importStackablePackageMarker)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export async function importStackablePackage (directory, pkg) {
|
|
23
|
+
let imported
|
|
24
|
+
try {
|
|
25
|
+
try {
|
|
26
|
+
// Try regular import
|
|
27
|
+
imported = await import(pkg)
|
|
28
|
+
} catch (e) {
|
|
29
|
+
if (!isImportFailedError(e, pkg)) {
|
|
30
|
+
throw e
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Scope to the service
|
|
34
|
+
const require = createRequire(resolve(directory, importStackablePackageMarker))
|
|
35
|
+
const toImport = require.resolve(pkg)
|
|
36
|
+
imported = await importFile(toImport)
|
|
37
|
+
}
|
|
38
|
+
} catch (e) {
|
|
39
|
+
if (!isImportFailedError(e, pkg)) {
|
|
40
|
+
throw e
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const serviceDirectory = workerData ? relative(workerData.dirname, directory) : directory
|
|
44
|
+
throw new Error(
|
|
45
|
+
`Unable to import package '${pkg}'. Please add it as a dependency in the package.json file in the folder ${serviceDirectory}.`
|
|
46
|
+
)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return imported.default ?? imported
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export async function importStackableAndConfig (root, config, context) {
|
|
53
|
+
let rootPackageJson
|
|
54
|
+
try {
|
|
55
|
+
rootPackageJson = JSON.parse(await readFile(resolve(root, 'package.json'), 'utf-8'))
|
|
56
|
+
} catch {
|
|
57
|
+
rootPackageJson = {}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const hadConfig = !!config
|
|
61
|
+
|
|
62
|
+
if (!config) {
|
|
63
|
+
config = await findConfigurationFile(root, 'application')
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const appType = await detectApplicationType(root, rootPackageJson)
|
|
67
|
+
|
|
68
|
+
if (!appType) {
|
|
69
|
+
throw new Error(`Unable to detect application type in ${root}.`)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const { label, name: moduleName } = appType
|
|
73
|
+
|
|
74
|
+
if (context) {
|
|
75
|
+
const serviceRoot = relative(process.cwd(), root)
|
|
76
|
+
|
|
77
|
+
if (!hadConfig && context.serviceId && !(await findConfigurationFile(root)) && context.worker?.index === 0) {
|
|
78
|
+
const autodetectDescription =
|
|
79
|
+
moduleName === '@platformatic/node' ? 'is a generic Node.js application' : `is using ${label}`
|
|
80
|
+
|
|
81
|
+
const logger = pino({ level: context.serverConfig?.logger?.level ?? 'warn', name: context.serviceId })
|
|
82
|
+
|
|
83
|
+
logger.warn(`We have auto-detected that service "${context.serviceId}" ${autodetectDescription}.`)
|
|
84
|
+
logger.warn(
|
|
85
|
+
`We suggest you create a watt.json or a platformatic.json file in the folder ${serviceRoot} with the "$schema" property set to "https://schemas.platformatic.dev/${moduleName}/${packageJson.version}.json".`
|
|
86
|
+
)
|
|
87
|
+
logger.warn(`Also don't forget to add "${moduleName}" to the service dependencies.`)
|
|
88
|
+
logger.warn('You can also run "wattpm import" to do this automatically.\n')
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const stackable = await importStackablePackage(root, moduleName)
|
|
93
|
+
|
|
94
|
+
return {
|
|
95
|
+
stackable,
|
|
96
|
+
config,
|
|
97
|
+
autodetectDescription:
|
|
98
|
+
moduleName === '@platformatic/node' ? 'is a generic Node.js application' : `is using ${label}`,
|
|
99
|
+
moduleName
|
|
100
|
+
}
|
|
101
|
+
}
|
package/lib/schema.js
CHANGED
|
@@ -2,6 +2,7 @@ import { schemaComponents as utilsSchemaComponents } from '@platformatic/utils'
|
|
|
2
2
|
import { readFileSync } from 'node:fs'
|
|
3
3
|
|
|
4
4
|
export const packageJson = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf-8'))
|
|
5
|
+
export const version = packageJson.version
|
|
5
6
|
|
|
6
7
|
const application = {
|
|
7
8
|
type: 'object',
|
|
@@ -64,7 +65,7 @@ export const schemaComponents = { application, watch }
|
|
|
64
65
|
export const schema = {
|
|
65
66
|
$id: `https://schemas.platformatic.dev/@platformatic/basic/${packageJson.version}.json`,
|
|
66
67
|
$schema: 'http://json-schema.org/draft-07/schema#',
|
|
67
|
-
title: 'Platformatic
|
|
68
|
+
title: 'Platformatic Basic Config',
|
|
68
69
|
type: 'object',
|
|
69
70
|
properties: {
|
|
70
71
|
$schema: {
|
|
@@ -162,7 +162,7 @@ export class ChildManager extends ITC {
|
|
|
162
162
|
const childProcessInclude = `--import="${new URL('./child-process.js', import.meta.url)}"`
|
|
163
163
|
|
|
164
164
|
let telemetryInclude = ''
|
|
165
|
-
if (this.#context.telemetryConfig
|
|
165
|
+
if (this.#context.telemetryConfig) {
|
|
166
166
|
const require = createRequire(import.meta.url)
|
|
167
167
|
const telemetryPath = require.resolve('@platformatic/telemetry')
|
|
168
168
|
const openTelemetrySetupPath = join(telemetryPath, '..', 'lib', 'node-telemetry.js')
|
|
@@ -4,7 +4,6 @@ import {
|
|
|
4
4
|
buildPinoFormatters,
|
|
5
5
|
buildPinoTimestamp,
|
|
6
6
|
disablePinoDirectWrite,
|
|
7
|
-
ensureFlushedWorkerStdio,
|
|
8
7
|
ensureLoggableError,
|
|
9
8
|
features
|
|
10
9
|
} from '@platformatic/utils'
|
|
@@ -83,7 +82,7 @@ export class ChildProcess extends ITC {
|
|
|
83
82
|
getMetrics: (...args) => {
|
|
84
83
|
return this.#getMetrics(...args)
|
|
85
84
|
},
|
|
86
|
-
close:
|
|
85
|
+
close: signal => {
|
|
87
86
|
let handled = false
|
|
88
87
|
|
|
89
88
|
try {
|
|
@@ -223,66 +222,6 @@ export class ChildProcess extends ITC {
|
|
|
223
222
|
globalThis.platformatic.onHttpCacheMiss = () => {
|
|
224
223
|
cacheMissMetric.inc()
|
|
225
224
|
}
|
|
226
|
-
|
|
227
|
-
const httpStatsFreeMetric = new client.Gauge({
|
|
228
|
-
name: 'http_client_stats_free',
|
|
229
|
-
help: 'Number of free (idle) http clients (sockets)',
|
|
230
|
-
labelNames: ['dispatcher_stats_url'],
|
|
231
|
-
registers: [registry]
|
|
232
|
-
})
|
|
233
|
-
globalThis.platformatic.onHttpStatsFree = (url, val) => {
|
|
234
|
-
httpStatsFreeMetric.set({ dispatcher_stats_url: url }, val)
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
const httpStatsConnectedMetric = new client.Gauge({
|
|
238
|
-
name: 'http_client_stats_connected',
|
|
239
|
-
help: 'Number of open socket connections',
|
|
240
|
-
labelNames: ['dispatcher_stats_url'],
|
|
241
|
-
registers: [registry]
|
|
242
|
-
})
|
|
243
|
-
globalThis.platformatic.onHttpStatsConnected = (url, val) => {
|
|
244
|
-
httpStatsConnectedMetric.set({ dispatcher_stats_url: url }, val)
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
const httpStatsPendingMetric = new client.Gauge({
|
|
248
|
-
name: 'http_client_stats_pending',
|
|
249
|
-
help: 'Number of pending requests across all clients',
|
|
250
|
-
labelNames: ['dispatcher_stats_url'],
|
|
251
|
-
registers: [registry]
|
|
252
|
-
})
|
|
253
|
-
globalThis.platformatic.onHttpStatsPending = (url, val) => {
|
|
254
|
-
httpStatsPendingMetric.set({ dispatcher_stats_url: url }, val)
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
const httpStatsQueuedMetric = new client.Gauge({
|
|
258
|
-
name: 'http_client_stats_queued',
|
|
259
|
-
help: 'Number of queued requests across all clients',
|
|
260
|
-
labelNames: ['dispatcher_stats_url'],
|
|
261
|
-
registers: [registry]
|
|
262
|
-
})
|
|
263
|
-
globalThis.platformatic.onHttpStatsQueued = (url, val) => {
|
|
264
|
-
httpStatsQueuedMetric.set({ dispatcher_stats_url: url }, val)
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
const httpStatsRunningMetric = new client.Gauge({
|
|
268
|
-
name: 'http_client_stats_running',
|
|
269
|
-
help: 'Number of currently active requests across all clients',
|
|
270
|
-
labelNames: ['dispatcher_stats_url'],
|
|
271
|
-
registers: [registry]
|
|
272
|
-
})
|
|
273
|
-
globalThis.platformatic.onHttpStatsRunning = (url, val) => {
|
|
274
|
-
httpStatsRunningMetric.set({ dispatcher_stats_url: url }, val)
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
const httpStatsSizeMetric = new client.Gauge({
|
|
278
|
-
name: 'http_client_stats_size',
|
|
279
|
-
help: 'Number of active, pending, or queued requests across all clients',
|
|
280
|
-
labelNames: ['dispatcher_stats_url'],
|
|
281
|
-
registers: [registry]
|
|
282
|
-
})
|
|
283
|
-
globalThis.platformatic.onHttpStatsSize = (url, val) => {
|
|
284
|
-
httpStatsSizeMetric.set({ dispatcher_stats_url: url }, val)
|
|
285
|
-
}
|
|
286
225
|
}
|
|
287
226
|
|
|
288
227
|
async #getMetrics ({ format } = {}) {
|
|
@@ -294,7 +233,6 @@ export class ChildProcess extends ITC {
|
|
|
294
233
|
|
|
295
234
|
#setupLogger () {
|
|
296
235
|
disablePinoDirectWrite()
|
|
297
|
-
ensureFlushedWorkerStdio()
|
|
298
236
|
|
|
299
237
|
// Since this is executed by user code, make sure we only override this in the main thread
|
|
300
238
|
// The rest will be intercepted by the BaseStackable.
|
package/lib/worker/listeners.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { features
|
|
1
|
+
import { features } from '@platformatic/utils'
|
|
2
2
|
import { subscribe, tracingChannel, unsubscribe } from 'node:diagnostics_channel'
|
|
3
3
|
|
|
4
4
|
export function createServerListener (overridePort = true, overrideHost) {
|
|
5
|
-
const { promise, resolve, reject } = withResolvers()
|
|
5
|
+
const { promise, resolve, reject } = Promise.withResolvers()
|
|
6
6
|
|
|
7
7
|
const subscribers = {
|
|
8
8
|
asyncStart ({ options }) {
|
|
@@ -48,7 +48,7 @@ export function createServerListener (overridePort = true, overrideHost) {
|
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
export function createChildProcessListener () {
|
|
51
|
-
const { promise, resolve } = withResolvers()
|
|
51
|
+
const { promise, resolve } = Promise.withResolvers()
|
|
52
52
|
|
|
53
53
|
const handler = ({ process: child }) => {
|
|
54
54
|
unsubscribe('child_process', handler)
|
package/package.json
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@platformatic/basic",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0-alpha.1",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "index.js",
|
|
6
|
+
"types": "index.d.ts",
|
|
6
7
|
"type": "module",
|
|
7
8
|
"repository": {
|
|
8
9
|
"type": "git",
|
|
@@ -24,11 +25,10 @@
|
|
|
24
25
|
"split2": "^4.2.0",
|
|
25
26
|
"undici": "^7.0.0",
|
|
26
27
|
"ws": "^8.18.0",
|
|
27
|
-
"@platformatic/
|
|
28
|
-
"@platformatic/metrics": "
|
|
29
|
-
"@platformatic/
|
|
30
|
-
"@platformatic/utils": "
|
|
31
|
-
"@platformatic/telemetry": "2.71.1-alpha.0"
|
|
28
|
+
"@platformatic/itc": "3.0.0-alpha.1",
|
|
29
|
+
"@platformatic/metrics": "3.0.0-alpha.1",
|
|
30
|
+
"@platformatic/telemetry": "3.0.0-alpha.1",
|
|
31
|
+
"@platformatic/utils": "3.0.0-alpha.1"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
34
|
"borp": "^0.20.0",
|
|
@@ -37,13 +37,12 @@
|
|
|
37
37
|
"fastify": "^5.0.0",
|
|
38
38
|
"get-port": "^7.1.0",
|
|
39
39
|
"json-schema-to-typescript": "^15.0.0",
|
|
40
|
-
"minimatch": "^10.0.3",
|
|
41
40
|
"neostandard": "^0.12.0",
|
|
42
41
|
"typescript": "^5.5.4"
|
|
43
42
|
},
|
|
44
43
|
"scripts": {
|
|
45
|
-
"test": "npm run lint && borp --concurrency=1 --
|
|
46
|
-
"coverage": "npm run lint && borp -C -X test -X test/fixtures --concurrency=1 --
|
|
44
|
+
"test": "npm run lint && borp --concurrency=1 --timeout 1200000",
|
|
45
|
+
"coverage": "npm run lint && borp -C -X test -X test/fixtures --concurrency=1 --timeout 1200000",
|
|
47
46
|
"gen-schema": "node lib/schema.js > schema.json",
|
|
48
47
|
"gen-types": "json2ts > config.d.ts < schema.json",
|
|
49
48
|
"build": "pnpm run gen-schema && pnpm run gen-types",
|
package/schema.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
|
-
"$id": "https://schemas.platformatic.dev/@platformatic/basic/
|
|
2
|
+
"$id": "https://schemas.platformatic.dev/@platformatic/basic/3.0.0-alpha.1.json",
|
|
3
3
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
4
|
-
"title": "Platformatic
|
|
4
|
+
"title": "Platformatic Basic Config",
|
|
5
5
|
"type": "object",
|
|
6
6
|
"properties": {
|
|
7
7
|
"$schema": {
|