@jsbot-tellus/core 2.0.3 → 2.1.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/README.md +13 -17
- package/dist/app.js +12 -40
- package/dist/app.js.map +1 -1
- package/dist/core/config/schema.d.ts +0 -2
- package/dist/core/config/schema.d.ts.map +1 -1
- package/dist/core/config/schema.js +0 -1
- package/dist/core/config/schema.js.map +1 -1
- package/dist/core/dispatch/command.d.ts +24 -2
- package/dist/core/dispatch/command.d.ts.map +1 -1
- package/dist/core/dispatch/command.js +297 -60
- package/dist/core/dispatch/command.js.map +1 -1
- package/dist/core/dispatch/router.d.ts +17 -3
- package/dist/core/dispatch/router.d.ts.map +1 -1
- package/dist/core/dispatch/router.js +38 -2
- package/dist/core/dispatch/router.js.map +1 -1
- package/dist/core/index.d.ts +1 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/plugin/command-params.d.ts +15 -0
- package/dist/core/plugin/command-params.d.ts.map +1 -0
- package/dist/core/plugin/command-params.js +427 -0
- package/dist/core/plugin/command-params.js.map +1 -0
- package/dist/core/plugin/context.d.ts.map +1 -1
- package/dist/core/plugin/context.js +27 -0
- package/dist/core/plugin/context.js.map +1 -1
- package/dist/core/plugin/install-dir.d.ts +6 -9
- package/dist/core/plugin/install-dir.d.ts.map +1 -1
- package/dist/core/plugin/install-dir.js +99 -79
- package/dist/core/plugin/install-dir.js.map +1 -1
- package/dist/core/plugin/install-validate.d.ts +2 -1
- package/dist/core/plugin/install-validate.d.ts.map +1 -1
- package/dist/core/plugin/install-validate.js +25 -17
- package/dist/core/plugin/install-validate.js.map +1 -1
- package/dist/core/plugin/installer.d.ts +3 -3
- package/dist/core/plugin/installer.d.ts.map +1 -1
- package/dist/core/plugin/installer.js +143 -140
- package/dist/core/plugin/installer.js.map +1 -1
- package/dist/core/plugin/loader.d.ts +1 -0
- package/dist/core/plugin/loader.d.ts.map +1 -1
- package/dist/core/plugin/loader.js +24 -2
- package/dist/core/plugin/loader.js.map +1 -1
- package/dist/core/plugin/types.d.ts +11 -3
- package/dist/core/plugin/types.d.ts.map +1 -1
- package/dist/core/plugin/types.js.map +1 -1
- package/dist/plugins/admin/index.d.ts.map +1 -1
- package/dist/plugins/admin/index.js +119 -86
- package/dist/plugins/admin/index.js.map +1 -1
- package/dist/plugins/core/index.d.ts.map +1 -1
- package/dist/plugins/core/index.js +4 -1
- package/dist/plugins/core/index.js.map +1 -1
- package/docs/architecture.md +22 -24
- package/docs/configuration.md +19 -23
- package/docs/feature-parity.md +4 -4
- package/docs/migration.md +13 -13
- package/docs/plugin-api.md +156 -26
- package/docs/plugin-development.md +390 -0
- package/docs/release.md +2 -2
- package/examples/plugins/hello/README.md +10 -5
- package/examples/plugins/hello/index.js +3 -6
- package/package.json +2 -1
- package/src/app.ts +16 -55
- package/src/core/config/schema.ts +0 -1
- package/src/core/dispatch/command.ts +474 -70
- package/src/core/dispatch/router.ts +77 -3
- package/src/core/index.ts +2 -0
- package/src/core/plugin/command-params.ts +595 -0
- package/src/core/plugin/context.ts +47 -2
- package/src/core/plugin/install-dir.ts +117 -111
- package/src/core/plugin/install-validate.ts +36 -34
- package/src/core/plugin/installer.ts +228 -184
- package/src/core/plugin/loader.ts +36 -2
- package/src/core/plugin/types.ts +23 -3
- package/src/plugins/admin/index.ts +302 -233
- package/src/plugins/core/index.ts +5 -1
- package/dist/core/plugin/installed.d.ts +0 -9
- package/dist/core/plugin/installed.d.ts.map +0 -1
- package/dist/core/plugin/installed.js +0 -113
- package/dist/core/plugin/installed.js.map +0 -1
- package/src/core/plugin/installed.ts +0 -165
|
@@ -10,11 +10,10 @@ import {
|
|
|
10
10
|
symlink,
|
|
11
11
|
writeFile,
|
|
12
12
|
} from 'node:fs/promises'
|
|
13
|
-
import { dirname, join
|
|
13
|
+
import { dirname, join } from 'node:path'
|
|
14
14
|
import { fileURLToPath } from 'node:url'
|
|
15
15
|
|
|
16
16
|
export const CORE_PACKAGE_NAME = '@jsbot-tellus/core'
|
|
17
|
-
export const INSTALL_PACKAGE_NAME = '@jsbot-tellus/installed-plugins'
|
|
18
17
|
export const INSTALL_STATE_FILE = '.jsbot-installed.json'
|
|
19
18
|
export const VALIDATE_SCRIPT_FILE = 'validate.mjs'
|
|
20
19
|
|
|
@@ -24,10 +23,12 @@ export interface InstalledPackageRecord {
|
|
|
24
23
|
pluginIds: string[]
|
|
25
24
|
dependencies: Record<string, string[]>
|
|
26
25
|
installedAt: string
|
|
26
|
+
/** Directory name under the plugins root. */
|
|
27
|
+
dir: string
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
export interface InstallState {
|
|
30
|
-
version:
|
|
31
|
+
version: 2
|
|
31
32
|
packages: Record<string, InstalledPackageRecord>
|
|
32
33
|
}
|
|
33
34
|
|
|
@@ -38,25 +39,8 @@ export class InstallStateError extends Error {
|
|
|
38
39
|
}
|
|
39
40
|
}
|
|
40
41
|
|
|
41
|
-
|
|
42
|
-
name:
|
|
43
|
-
private: true,
|
|
44
|
-
version: '0.0.0',
|
|
45
|
-
type: 'module',
|
|
46
|
-
dependencies: {},
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export function resolveInstallDir(
|
|
50
|
-
config: { dir?: string | undefined },
|
|
51
|
-
dataDir: string
|
|
52
|
-
): string {
|
|
53
|
-
return config.dir
|
|
54
|
-
? resolve(process.cwd(), config.dir)
|
|
55
|
-
: join(dataDir, 'plugins')
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export function packageDir(dir: string, name: string): string {
|
|
59
|
-
return join(dir, 'node_modules', name)
|
|
42
|
+
export function packageDirectoryName(name: string): string {
|
|
43
|
+
return name.startsWith('@') ? name.replace('/', '__') : name
|
|
60
44
|
}
|
|
61
45
|
|
|
62
46
|
export async function readJsonFile(path: string): Promise<unknown> {
|
|
@@ -69,68 +53,12 @@ export async function readJsonFile(path: string): Promise<unknown> {
|
|
|
69
53
|
}
|
|
70
54
|
}
|
|
71
55
|
|
|
72
|
-
export async function
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
const manifestPath = join(dir, 'package.json')
|
|
76
|
-
const manifest = await readJsonFile(manifestPath)
|
|
77
|
-
|
|
78
|
-
if (manifest === undefined) {
|
|
79
|
-
await writeFile(
|
|
80
|
-
manifestPath,
|
|
81
|
-
`${JSON.stringify(INSTALL_MANIFEST, null, 4)}\n`
|
|
82
|
-
)
|
|
83
|
-
} else if (
|
|
84
|
-
typeof manifest !== 'object' ||
|
|
85
|
-
manifest === null ||
|
|
86
|
-
Array.isArray(manifest)
|
|
87
|
-
) {
|
|
88
|
-
throw new Error(`安装目录的 package.json 无效: ${manifestPath}`)
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
await mkdir(join(dir, '.jsbot'), { recursive: true })
|
|
92
|
-
await writeFile(
|
|
93
|
-
join(dir, '.jsbot', VALIDATE_SCRIPT_FILE),
|
|
94
|
-
VALIDATE_SCRIPT,
|
|
95
|
-
'utf8'
|
|
96
|
-
)
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
export async function readDependencies(
|
|
100
|
-
dir: string
|
|
101
|
-
): Promise<Record<string, string>> {
|
|
102
|
-
const manifest = await readJsonFile(join(dir, 'package.json'))
|
|
103
|
-
|
|
104
|
-
if (typeof manifest !== 'object' || manifest === null) return {}
|
|
105
|
-
|
|
106
|
-
const dependencies = (manifest as Record<string, unknown>)['dependencies']
|
|
107
|
-
|
|
108
|
-
if (
|
|
109
|
-
typeof dependencies !== 'object' ||
|
|
110
|
-
dependencies === null ||
|
|
111
|
-
Array.isArray(dependencies)
|
|
112
|
-
) {
|
|
113
|
-
return {}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
const result: Record<string, string> = {}
|
|
117
|
-
|
|
118
|
-
for (const [name, value] of Object.entries(dependencies)) {
|
|
119
|
-
if (typeof value === 'string') {
|
|
120
|
-
result[name] = value
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
return result
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
export async function readInstalledVersion(
|
|
128
|
-
dir: string,
|
|
129
|
-
name: string
|
|
56
|
+
export async function readPackageVersion(
|
|
57
|
+
directory: string
|
|
130
58
|
): Promise<string | undefined> {
|
|
131
|
-
const manifest = await readJsonFile(
|
|
132
|
-
|
|
133
|
-
)
|
|
59
|
+
const manifest = await readJsonFile(join(directory, 'package.json')).catch(
|
|
60
|
+
() => undefined
|
|
61
|
+
)
|
|
134
62
|
|
|
135
63
|
if (typeof manifest !== 'object' || manifest === null) return undefined
|
|
136
64
|
|
|
@@ -139,6 +67,16 @@ export async function readInstalledVersion(
|
|
|
139
67
|
return typeof version === 'string' ? version : undefined
|
|
140
68
|
}
|
|
141
69
|
|
|
70
|
+
function isSafeDirectoryName(value: string): boolean {
|
|
71
|
+
return (
|
|
72
|
+
value.length > 0 &&
|
|
73
|
+
value !== '.' &&
|
|
74
|
+
value !== '..' &&
|
|
75
|
+
!value.includes('/') &&
|
|
76
|
+
!value.includes('\\')
|
|
77
|
+
)
|
|
78
|
+
}
|
|
79
|
+
|
|
142
80
|
export async function readInstallState(dir: string): Promise<InstallState> {
|
|
143
81
|
const path = join(dir, INSTALL_STATE_FILE)
|
|
144
82
|
let value: unknown
|
|
@@ -152,14 +90,14 @@ export async function readInstallState(dir: string): Promise<InstallState> {
|
|
|
152
90
|
}
|
|
153
91
|
|
|
154
92
|
if (value === undefined) {
|
|
155
|
-
return { version:
|
|
93
|
+
return { version: 2, packages: {} }
|
|
156
94
|
}
|
|
157
95
|
|
|
158
96
|
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
159
97
|
throw new InstallStateError(`安装状态文件格式无效: ${path}`)
|
|
160
98
|
}
|
|
161
99
|
|
|
162
|
-
if ((value as Record<string, unknown>)['version'] !==
|
|
100
|
+
if ((value as Record<string, unknown>)['version'] !== 2) {
|
|
163
101
|
throw new InstallStateError(`不支持的安装状态版本,请检查 ${path}`)
|
|
164
102
|
}
|
|
165
103
|
|
|
@@ -174,6 +112,7 @@ export async function readInstallState(dir: string): Promise<InstallState> {
|
|
|
174
112
|
}
|
|
175
113
|
|
|
176
114
|
const result: Record<string, InstalledPackageRecord> = {}
|
|
115
|
+
const directories = new Set<string>()
|
|
177
116
|
|
|
178
117
|
for (const [name, rawRecord] of Object.entries(packages)) {
|
|
179
118
|
if (
|
|
@@ -187,17 +126,28 @@ export async function readInstallState(dir: string): Promise<InstallState> {
|
|
|
187
126
|
const record = rawRecord as Record<string, unknown>
|
|
188
127
|
const pluginIds = record['pluginIds']
|
|
189
128
|
const dependencies = record['dependencies']
|
|
129
|
+
const directory = record['dir']
|
|
190
130
|
|
|
191
131
|
if (
|
|
192
132
|
typeof record['spec'] !== 'string' ||
|
|
193
133
|
typeof record['version'] !== 'string' ||
|
|
194
134
|
typeof record['installedAt'] !== 'string' ||
|
|
135
|
+
typeof directory !== 'string' ||
|
|
136
|
+
!isSafeDirectoryName(directory) ||
|
|
195
137
|
!Array.isArray(pluginIds) ||
|
|
196
138
|
pluginIds.some((item) => typeof item !== 'string')
|
|
197
139
|
) {
|
|
198
140
|
throw new InstallStateError(`安装状态记录不完整: ${name}`)
|
|
199
141
|
}
|
|
200
142
|
|
|
143
|
+
if (directories.has(directory)) {
|
|
144
|
+
throw new InstallStateError(
|
|
145
|
+
`安装状态存在重复的插件目录: ${directory}`
|
|
146
|
+
)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
directories.add(directory)
|
|
150
|
+
|
|
201
151
|
if (
|
|
202
152
|
typeof dependencies !== 'object' ||
|
|
203
153
|
dependencies === null ||
|
|
@@ -227,17 +177,18 @@ export async function readInstallState(dir: string): Promise<InstallState> {
|
|
|
227
177
|
pluginIds: [...pluginIds],
|
|
228
178
|
dependencies: parsedDependencies,
|
|
229
179
|
installedAt: record['installedAt'],
|
|
180
|
+
dir: directory,
|
|
230
181
|
}
|
|
231
182
|
}
|
|
232
183
|
|
|
233
|
-
return { version:
|
|
184
|
+
return { version: 2, packages: result }
|
|
234
185
|
}
|
|
235
186
|
|
|
236
187
|
export async function writeInstallState(
|
|
237
188
|
dir: string,
|
|
238
189
|
state: InstallState
|
|
239
190
|
): Promise<void> {
|
|
240
|
-
const sorted: InstallState = { version:
|
|
191
|
+
const sorted: InstallState = { version: 2, packages: {} }
|
|
241
192
|
|
|
242
193
|
for (const name of Object.keys(state.packages).sort()) {
|
|
243
194
|
const record = state.packages[name]
|
|
@@ -312,10 +263,57 @@ export async function pathExists(path: string): Promise<boolean> {
|
|
|
312
263
|
return (await stat(path).catch(() => null)) !== null
|
|
313
264
|
}
|
|
314
265
|
|
|
315
|
-
|
|
266
|
+
export async function ensureInstallRoot(root: string): Promise<void> {
|
|
267
|
+
await mkdir(root, { recursive: true })
|
|
268
|
+
await mkdir(join(root, '.jsbot'), { recursive: true })
|
|
269
|
+
await writeFile(
|
|
270
|
+
join(root, '.jsbot', VALIDATE_SCRIPT_FILE),
|
|
271
|
+
VALIDATE_SCRIPT,
|
|
272
|
+
'utf8'
|
|
273
|
+
)
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
const VALIDATE_SCRIPT = `import { readdir } from 'node:fs/promises'
|
|
277
|
+
import { extname, join } from 'node:path'
|
|
278
|
+
import { pathToFileURL } from 'node:url'
|
|
279
|
+
|
|
280
|
+
const directory = process.argv[2]
|
|
281
|
+
|
|
282
|
+
if (!directory) {
|
|
283
|
+
console.error('missing plugin directory')
|
|
284
|
+
process.exit(2)
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
const extensions = new Set(['.js', '.mjs', '.cjs'])
|
|
288
|
+
|
|
289
|
+
async function filesIn(directory) {
|
|
290
|
+
const entries = await readdir(directory, { withFileTypes: true })
|
|
291
|
+
entries.sort((a, b) => a.name.localeCompare(b.name))
|
|
292
|
+
|
|
293
|
+
const files = []
|
|
294
|
+
|
|
295
|
+
for (const entry of entries) {
|
|
296
|
+
if (entry.name.startsWith('.')) continue
|
|
297
|
+
|
|
298
|
+
const path = join(directory, entry.name)
|
|
299
|
+
|
|
300
|
+
if (entry.isDirectory()) {
|
|
301
|
+
if (entry.name === 'node_modules') continue
|
|
302
|
+
files.push(...(await filesIn(path)))
|
|
303
|
+
continue
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
if (
|
|
307
|
+
entry.isFile() &&
|
|
308
|
+
extensions.has(extname(entry.name)) &&
|
|
309
|
+
!/\\.d\\.(ts|mts|cts)$/.test(entry.name)
|
|
310
|
+
) {
|
|
311
|
+
files.push(path)
|
|
312
|
+
}
|
|
313
|
+
}
|
|
316
314
|
|
|
317
|
-
|
|
318
|
-
|
|
315
|
+
return files
|
|
316
|
+
}
|
|
319
317
|
|
|
320
318
|
function isPlugin(value) {
|
|
321
319
|
if (typeof value !== 'object' || value === null) return false
|
|
@@ -367,40 +365,48 @@ function collect(value, result) {
|
|
|
367
365
|
}
|
|
368
366
|
}
|
|
369
367
|
|
|
370
|
-
|
|
371
|
-
console.error(
|
|
372
|
-
process.exit(
|
|
373
|
-
}
|
|
368
|
+
const files = await filesIn(directory).catch((error) => {
|
|
369
|
+
console.error(error instanceof Error ? error.message : String(error))
|
|
370
|
+
process.exit(3)
|
|
371
|
+
})
|
|
374
372
|
|
|
375
|
-
if (
|
|
376
|
-
console.error('
|
|
377
|
-
process.exit(
|
|
373
|
+
if (files.length === 0) {
|
|
374
|
+
console.error('插件 dist 目录中没有可加载的文件')
|
|
375
|
+
process.exit(4)
|
|
378
376
|
}
|
|
379
377
|
|
|
380
|
-
|
|
378
|
+
const found = []
|
|
379
|
+
const invalid = []
|
|
381
380
|
|
|
382
|
-
|
|
383
|
-
imported
|
|
384
|
-
} catch (error) {
|
|
385
|
-
console.error(error instanceof Error ? error.stack ?? error.message : String(error))
|
|
386
|
-
process.exit(3)
|
|
387
|
-
}
|
|
381
|
+
for (const file of files) {
|
|
382
|
+
let imported
|
|
388
383
|
|
|
389
|
-
|
|
384
|
+
try {
|
|
385
|
+
imported = await import(pathToFileURL(file).href)
|
|
386
|
+
} catch (error) {
|
|
387
|
+
invalid.push(
|
|
388
|
+
\`failed to import \${file}: \${error instanceof Error ? error.message : String(error)}\`
|
|
389
|
+
)
|
|
390
|
+
continue
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
if (typeof imported !== 'object' || imported === null) continue
|
|
390
394
|
|
|
391
|
-
if (typeof imported === 'object' && imported !== null) {
|
|
392
395
|
for (const candidate of [imported.default, imported.plugin, imported.plugins]) {
|
|
393
396
|
collect(candidate, found)
|
|
394
397
|
}
|
|
395
398
|
}
|
|
396
399
|
|
|
397
|
-
if (found.length === 0) {
|
|
398
|
-
console.error('package does not export a JsBot plugin (default / plugin / plugins)')
|
|
399
|
-
process.exit(4)
|
|
400
|
-
}
|
|
401
|
-
|
|
402
400
|
const unique = found.filter((plugin, index) => found.indexOf(plugin) === index)
|
|
403
401
|
|
|
402
|
+
if (unique.length === 0) {
|
|
403
|
+
console.error(
|
|
404
|
+
invalid[0] ??
|
|
405
|
+
'package does not export a JsBot plugin (default / plugin / plugins)'
|
|
406
|
+
)
|
|
407
|
+
process.exit(5)
|
|
408
|
+
}
|
|
409
|
+
|
|
404
410
|
console.log(
|
|
405
411
|
'__JSBOT_PLUGIN__' +
|
|
406
412
|
JSON.stringify(
|
|
@@ -1,11 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
VALIDATE_SCRIPT_FILE,
|
|
5
|
-
packageDir,
|
|
6
|
-
readJsonFile,
|
|
7
|
-
} from './install-dir.js'
|
|
8
|
-
import { packageEntry } from './installed.js'
|
|
1
|
+
import { stat } from 'node:fs/promises'
|
|
2
|
+
import { dirname, join, resolve } from 'node:path'
|
|
3
|
+
import { pathExists, VALIDATE_SCRIPT_FILE } from './install-dir.js'
|
|
9
4
|
import { runProcess } from './npm-runner.js'
|
|
10
5
|
|
|
11
6
|
export interface ValidatedPlugin {
|
|
@@ -17,10 +12,29 @@ export interface ValidatedPlugin {
|
|
|
17
12
|
export interface ValidateOptions {
|
|
18
13
|
timeoutMs: number
|
|
19
14
|
signal?: AbortSignal | undefined
|
|
15
|
+
script?: string | undefined
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async function findValidationScript(directory: string): Promise<string> {
|
|
19
|
+
let current = dirname(directory)
|
|
20
|
+
|
|
21
|
+
for (;;) {
|
|
22
|
+
const script = join(current, '.jsbot', VALIDATE_SCRIPT_FILE)
|
|
23
|
+
|
|
24
|
+
if (await pathExists(script)) return script
|
|
25
|
+
|
|
26
|
+
const parent = dirname(current)
|
|
27
|
+
|
|
28
|
+
if (parent === current) break
|
|
29
|
+
|
|
30
|
+
current = parent
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
throw new Error('找不到插件校验脚本,请重新执行安装')
|
|
20
34
|
}
|
|
21
35
|
|
|
22
36
|
export type PackageValidator = (
|
|
23
|
-
|
|
37
|
+
directory: string,
|
|
24
38
|
name: string,
|
|
25
39
|
options: ValidateOptions
|
|
26
40
|
) => Promise<ValidatedPlugin[]>
|
|
@@ -28,36 +42,24 @@ export type PackageValidator = (
|
|
|
28
42
|
export const PLUGIN_MARKER = '__JSBOT_PLUGIN__'
|
|
29
43
|
|
|
30
44
|
export const validateInstalledPackage: PackageValidator = async (
|
|
31
|
-
|
|
45
|
+
directory,
|
|
32
46
|
name,
|
|
33
47
|
options
|
|
34
48
|
) => {
|
|
35
|
-
const
|
|
36
|
-
const
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
if (
|
|
40
|
-
|
|
41
|
-
manifest === null ||
|
|
42
|
-
Array.isArray(manifest)
|
|
43
|
-
) {
|
|
44
|
-
throw new Error(`插件包缺少有效的 package.json: ${name}`)
|
|
49
|
+
const absolute = resolve(directory)
|
|
50
|
+
const dist = join(absolute, 'dist')
|
|
51
|
+
const info = await stat(dist).catch(() => null)
|
|
52
|
+
|
|
53
|
+
if (!info?.isDirectory()) {
|
|
54
|
+
throw new Error(`插件包缺少 dist 目录: ${name}`)
|
|
45
55
|
}
|
|
46
56
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
process.execPath,
|
|
54
|
-
[script, name, entry],
|
|
55
|
-
{
|
|
56
|
-
cwd: dir,
|
|
57
|
-
timeoutMs: options.timeoutMs,
|
|
58
|
-
...(options.signal !== undefined ? { signal: options.signal } : {}),
|
|
59
|
-
}
|
|
60
|
-
)
|
|
57
|
+
const script = options.script ?? (await findValidationScript(absolute))
|
|
58
|
+
const { stdout } = await runProcess(process.execPath, [script, absolute], {
|
|
59
|
+
cwd: absolute,
|
|
60
|
+
timeoutMs: options.timeoutMs,
|
|
61
|
+
...(options.signal !== undefined ? { signal: options.signal } : {}),
|
|
62
|
+
})
|
|
61
63
|
|
|
62
64
|
const line = stdout
|
|
63
65
|
.split('\n')
|