duto 0.260808.0 → 0.260915.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.
Files changed (98) hide show
  1. package/LICENSE +2 -2
  2. package/README.md +35 -13
  3. package/package.json +54 -13
  4. package/src/cli.ts +45 -0
  5. package/src/commands/new.ts +147 -0
  6. package/src/commands/templates/base/.editorconfig +21 -0
  7. package/src/commands/templates/base/.env.dev.example +2 -0
  8. package/src/commands/templates/base/.env.prod.example +2 -0
  9. package/src/commands/templates/base/README.md +29 -0
  10. package/src/commands/templates/base/_gitignore +100 -0
  11. package/src/commands/templates/base/actions/blog/index.get.ts +9 -0
  12. package/src/commands/templates/base/actions/blog/post/[id].get.ts +9 -0
  13. package/src/commands/templates/base/actions/index.get.ts +5 -0
  14. package/src/commands/templates/base/configs/duto.ts +11 -0
  15. package/src/commands/templates/base/configs/roles.ts +5 -0
  16. package/src/commands/templates/base/package.json +24 -0
  17. package/src/commands/templates/base/tsconfig.json +42 -0
  18. package/src/commands/templates/workerd/wrangler.toml +47 -0
  19. package/src/commands/upgrade.ts +30 -0
  20. package/src/enum.ts +2 -0
  21. package/src/index.d.ts +61 -0
  22. package/src/index.ts +19 -23
  23. package/src/middleware.ts +2 -0
  24. package/src/{cli → pages/cli}/commands/build.ts +13 -7
  25. package/src/{cli → pages/cli}/commands/dev.ts +11 -4
  26. package/src/{cli → pages/cli}/commands/preview.ts +6 -4
  27. package/src/pages/cli/index.ts +10 -0
  28. package/src/{cli → pages/cli}/utils.ts +4 -5
  29. package/src/{client → pages/client}/app.tsx +1 -1
  30. package/src/{client → pages/client}/router.ts +1 -1
  31. package/src/{compiler → pages/compiler}/file.ts +1 -1
  32. package/src/{compiler → pages/compiler}/index.ts +2 -4
  33. package/src/{compiler → pages/compiler}/meta.ts +2 -2
  34. package/src/{compiler → pages/compiler}/routes.ts +1 -1
  35. package/src/{compiler → pages/compiler}/types.ts +10 -0
  36. package/src/{compiler → pages/compiler}/vite-plugin.ts +20 -45
  37. package/src/{i18n → pages/i18n}/client.ts +2 -2
  38. package/src/{i18n → pages/i18n}/element.ts +1 -1
  39. package/src/{i18n → pages/i18n}/server.ts +1 -1
  40. package/src/pages/index.ts +28 -0
  41. package/src/{preact → pages/preact}/server.ts +1 -1
  42. package/src/server/adapter/aws.ts +4 -0
  43. package/src/server/adapter/workerd.ts +4 -0
  44. package/src/server/app.ts +91 -0
  45. package/src/server/auth/ability.ts +72 -0
  46. package/src/server/auth/authnz.ts +86 -0
  47. package/src/server/auth/index.ts +5 -0
  48. package/src/server/auth/token.ts +147 -0
  49. package/src/server/auth/types.ts +2 -0
  50. package/src/server/cli/commands/build.ts +41 -0
  51. package/src/server/cli/commands/deploy.ts +81 -0
  52. package/src/server/cli/commands/dev.ts +279 -0
  53. package/src/server/cli/commands/make.ts +98 -0
  54. package/src/server/cli/commands/migrate.ts +93 -0
  55. package/src/server/cli/commands/routes.ts +78 -0
  56. package/src/server/cli/index.ts +27 -0
  57. package/src/server/cli/stubs.ts +39 -0
  58. package/src/server/cli/types.ts +3 -0
  59. package/src/server/cli/utils.ts +437 -0
  60. package/src/server/config.ts +19 -0
  61. package/src/server/db/d1.d.ts +1 -0
  62. package/src/server/db/d1.ts +1 -0
  63. package/src/server/db/dynamodb.d.ts +1 -0
  64. package/src/server/db/dynamodb.ts +1 -0
  65. package/src/server/db/index.ts +1 -0
  66. package/src/server/entry.ts +19 -0
  67. package/src/server/handle.ts +30 -0
  68. package/src/server/middlewares/auth.ts +40 -0
  69. package/src/server/middlewares/logger.ts +30 -0
  70. package/src/server/open-api/register.ts +29 -0
  71. package/src/server/open-api/spec.ts +114 -0
  72. package/src/server/request.ts +265 -0
  73. package/src/server/response.ts +142 -0
  74. package/src/server/routes.ts +473 -0
  75. package/src/server/types.ts +142 -0
  76. package/src/server/utils/environment.ts +17 -0
  77. package/src/server/utils.ts +71 -0
  78. package/src/server/validator.ts +70 -0
  79. package/src/utils.ts +15 -0
  80. package/src/cli/commands/dev-old.ts +0 -213
  81. package/src/cli/index.ts +0 -28
  82. package/src/compiler/utils.ts +0 -5
  83. /package/src/{client → pages/client}/hmr.ts +0 -0
  84. /package/src/{client → pages/client}/island.ts +0 -0
  85. /package/src/{client → pages/client}/link.ts +0 -0
  86. /package/src/{client → pages/client}/storage.ts +0 -0
  87. /package/src/{compiler → pages/compiler}/aliases.ts +0 -0
  88. /package/src/{compiler → pages/compiler}/assets.ts +0 -0
  89. /package/src/{compiler → pages/compiler}/ast.ts +0 -0
  90. /package/src/{compiler → pages/compiler}/constants.ts +0 -0
  91. /package/src/{compiler → pages/compiler}/island.ts +0 -0
  92. /package/src/{components → pages/components}/link.tsx +0 -0
  93. /package/src/{preact → pages/preact}/client-dev.ts +0 -0
  94. /package/src/{preact → pages/preact}/client.ts +0 -0
  95. /package/src/{preact → pages/preact}/context.ts +0 -0
  96. /package/src/{preact → pages/preact}/signals.ts +0 -0
  97. /package/src/{preact → pages/preact}/static-html.ts +0 -0
  98. /package/src/{preact → pages/preact}/types.ts +0 -0
@@ -8,7 +8,7 @@ import { getContext, incrementIslandId } from './context'
8
8
  import { restoreSignalsOnProps, serializeSignals } from './signals'
9
9
  import StaticHtml from './static-html'
10
10
  import type { IslandAttrs } from './types'
11
- import type { IslandMeta } from '@/types'
11
+ import type { IslandMeta } from '#/compiler/types'
12
12
  // import type { AstroPreactAttrs, RendererContext } from './types.js'
13
13
  // import { createFilter } from '@astrojs/internal-helpers/create-filter'
14
14
 
@@ -0,0 +1,4 @@
1
+ import { handle } from 'hono/aws-lambda'
2
+ import { app } from '$/entry'
3
+
4
+ export const handler = handle(app)
@@ -0,0 +1,4 @@
1
+ import { app } from '$/entry'
2
+ export default app
3
+
4
+ // export { app as default } from '$/entry'
@@ -0,0 +1,91 @@
1
+ import { Hono } from 'hono'
2
+ import type {
3
+ Env, Context, Next,
4
+ HTTPResponseError,
5
+ ServerOptions, Route
6
+ } from './types'
7
+ import request from './request'
8
+ import response from './response'
9
+ import { isDev } from './utils/environment'
10
+ import { getVerb } from './utils'
11
+ import { registerMiddleware, resolveHandle, resolveMw } from './handle'
12
+
13
+ async function EHandler(e: Error | HTTPResponseError) {
14
+ console.error(e)
15
+
16
+ switch (true) {
17
+ // case 'status' in e && e.status === 401:
18
+ case e?.status === 401:
19
+ return response.unauthorized()
20
+
21
+ // case 'status' in e && e.status === 400: // @ts-ignore
22
+ case e?.status === 400:
23
+ return response.badRequest(null, e?.message)
24
+
25
+ default:
26
+ return response.internalError(
27
+ // @ts-ignore
28
+ isDev() // TODO: remover ou migrar para um middleware / melhorar
29
+ ? e.stack?.split('\n').map(line =>
30
+ line.replace(
31
+ /at (.+ )?\(?([^)]+)\)?/g,
32
+ (match, method, path) => {
33
+ if (!path) return match
34
+
35
+ const nodeModulesIndex = path.indexOf('node_modules')
36
+ if (nodeModulesIndex > -1)
37
+ return `${method || ''}(node_modules${path.slice(nodeModulesIndex + 'node_modules'.length)})`
38
+
39
+ const projectRoot = process.cwd()
40
+ const relativePath = path.startsWith(projectRoot) ? path.slice(projectRoot.length + 1) : path
41
+ return `${method || ''}(${relativePath})`
42
+ }
43
+ ).trim()
44
+ )
45
+ : undefined,
46
+ e.message || 'Internal Error'
47
+ )
48
+ }
49
+
50
+ // return json.internalError(
51
+ // // @ts-ignore
52
+ // isDev() ? e.stack?.split('\n at ').map() : undefined,
53
+ // e.message || 'Internal Error'
54
+ // )
55
+ // error: e.message,
56
+ // cause: e.cause || '???',
57
+ // stack: isDev (? e.stack : undefined
58
+ }
59
+
60
+ export default function createApp<E extends Env>(options?: ServerOptions<E>) {
61
+ // const root = options?.root ?? '/'
62
+ const app = options?.app ?? new Hono<E>()
63
+
64
+ app.use(async (c: Context, next: Next) => {
65
+ await request.toContext(c)
66
+ await next()
67
+ })
68
+
69
+ const middlewares = options?.middlewares || []
70
+ for (const mw of middlewares) { // @ts-ignore
71
+ registerMiddleware(mw[2], mw[1]) // @ts-ignore
72
+ mw[0] ? app.use(String(mw[0]), resolveMw(mw[1])) : app.use(resolveMw(mw[1]))
73
+ }
74
+
75
+ // @ts-ignore
76
+ app.onError(options?.onError || EHandler)
77
+ app.notFound(options?.notFound || response.notFound)
78
+
79
+ if (options?.init) options.init(app)
80
+
81
+ const routes = options?.routes || [] // @ts-ignore
82
+ const routeRegister = options?.routeRegister ? options.routeRegister : (_: Hono, route: Route) => {
83
+ // @ts-ignore
84
+ _[getVerb[route[0]]](route[1], ...resolveHandle(route[2], route[3], route[4]))
85
+ }
86
+
87
+ for (const route of routes)
88
+ routeRegister(app, route)
89
+
90
+ return app
91
+ }
@@ -0,0 +1,72 @@
1
+ import { verbAlias } from '$/utils'
2
+ import type { IRequest, Routes } from '$/types'
3
+ import type { Roles, Abilities } from './types'
4
+
5
+ export class Ability {
6
+ static #roles: Roles = {}
7
+ static #abilities: Abilities = []
8
+ static #actions: Record<string, string> = {}
9
+
10
+ static empty() {
11
+ this.#roles = {}
12
+ this.#abilities = []
13
+ }
14
+
15
+ static fromRoutes(routes: Routes) {
16
+ if (!routes?.length) return
17
+
18
+ const actions: Record<string, string> = {}
19
+ const abilities = new Set()
20
+ for (const route of routes) {
21
+ const isArr = Array.isArray(route)
22
+ const name = isArr ? route[route.length-1] : route.name
23
+ if (!name || abilities.has(name)) continue
24
+
25
+ abilities.add(name)
26
+ actions[(isArr ? route[1] : route.path) +'$'+ verbAlias[isArr ? route[0] : route.method]] = name
27
+ const formatted = this.format(name)
28
+
29
+ if (formatted)
30
+ this.#abilities.push(formatted)
31
+ }
32
+
33
+ this.#actions = actions
34
+ }
35
+
36
+ static fromRequest(req: IRequest) {
37
+ const ability = this.#actions[`${req.routePath}$` + verbAlias[req.method.toLowerCase()]]
38
+ return ability ? this.format(ability) : ''
39
+ }
40
+
41
+ static fromAction(target: any): string {
42
+ return !target ? '' : this.format(typeof target === 'string' ? target : (target.name.length > 3 ? target.name : (target?.p || '')))
43
+ }
44
+
45
+ static format(path: string) {
46
+ return path === '/'
47
+ ? 'index'
48
+ : path.normalize('NFD')
49
+ .replace(/[\u0300-\u036f]/g, '')
50
+ .replace(/^\/*/, '')
51
+ .replace(/([a-z])([A-Z])/g, '$1-$2')
52
+ .replace(/[^a-zA-Z0-9/]|[\s\-.]/g, '_')
53
+ .replace(/_+/g, '_')
54
+ .replace(/\//g, '.')
55
+ .replace(/\._/g, '.')
56
+ .replace(/^[._-]+/, '')
57
+ .replace(/[._-]+$/, '')
58
+ .toLowerCase()
59
+ }
60
+
61
+ static get abilities() {
62
+ return this.#abilities
63
+ }
64
+
65
+ static get roles() {
66
+ return this.#roles
67
+ }
68
+
69
+ static set roles(roles: Roles) {
70
+ this.#roles = roles
71
+ }
72
+ }
@@ -0,0 +1,86 @@
1
+ import { Ability } from './ability'
2
+ import type { Token } from 'cripta/token'
3
+
4
+ export class Authnz<T extends object> {
5
+ #abilities: string[]
6
+ #roles: string[]
7
+ #data: T
8
+ #token: Token
9
+
10
+ constructor(token: Token, data: T, abilities: string[], roles: string[]) {
11
+ this.#abilities = abilities
12
+ this.#roles = roles
13
+ this.#token = token
14
+ this.#data = data
15
+ }
16
+
17
+ can(...abilities: string[]): boolean {
18
+ if (this.#abilities.includes('*')) return true
19
+
20
+ return abilities.flat().every(ability => {
21
+ if (this.#roles.includes(ability)) return true
22
+ return this.#abilities.some(rule => this.#match(rule, ability))
23
+ })
24
+ }
25
+ cant(...abilities: string[]): boolean {
26
+ return !this.can(...abilities)
27
+ }
28
+
29
+ hasRole(...roles: string[]): boolean {
30
+ return roles.flat().every(role => this.#roles.includes(role))
31
+ }
32
+
33
+ // has(prop: string, value: any = null): boolean {
34
+ // return this.#token?.hasValue(prop, value) || false
35
+ // }
36
+ // hasValue(prop: string, value: any = null): boolean {
37
+ // return this.has(prop, value)
38
+ // }
39
+
40
+ static fromToken<T extends object>(token: Token): Authnz<T> | null {
41
+ if (!token || !token?.valid) return null
42
+ const user = token.body
43
+ const roles = [...(user?.role ? [user.role] : []), ...(user?.roles ?? [])]
44
+
45
+ const combined = [...(user?.perms ?? []), ...roles.flatMap(role => {
46
+ const perms = Ability.roles[role]
47
+ if (!perms) return []
48
+ return perms === '*' ? ['*'] : perms
49
+ })]
50
+
51
+ const abilities = combined.includes('*') ? ['*'] : Array.from(new Set(combined))
52
+
53
+ return new Authnz<T>(token, user, abilities, roles)
54
+ }
55
+
56
+ #match(rule: string, ability: string): boolean {
57
+ if (rule === ability) return true
58
+ if (
59
+ this.#wildcardMatch(rule, ability, '_*')
60
+ || this.#wildcardMatch(rule, ability, '-*')
61
+ || this.#wildcardMatch(rule, ability, '.*')
62
+ ) return true
63
+
64
+ return false
65
+ }
66
+
67
+ #wildcardMatch(rule: string, ability: string, suffix: string) {
68
+ return rule.endsWith(suffix)
69
+ && (ability.startsWith(rule.slice(0, -2) + suffix[0]) || ability === rule.slice(0, -2))
70
+ }
71
+
72
+ get abilities() {
73
+ return this.#abilities
74
+ }
75
+
76
+ get roles() {
77
+ return this.#roles
78
+ }
79
+
80
+ get data() {
81
+ return this.#data
82
+ }
83
+ get user() {
84
+ return this.#data
85
+ }
86
+ }
@@ -0,0 +1,5 @@
1
+ export { Ability } from './ability'
2
+ export { Authnz } from './authnz'
3
+ export { Token } from './token'
4
+
5
+ export * from './types'
@@ -0,0 +1,147 @@
1
+ import { bytesToHex, getEnv, parseUA } from 't0n'
2
+ import { sha256, type Config } from 'cripta'
3
+ import { config, create, parse } from 'cripta/token'
4
+ import type { IRequest } from '$/types'
5
+
6
+ const STABLE_HEADERS = [
7
+ 'host',
8
+ 'connection',
9
+ 'sec-ch-ua',
10
+ 'sec-ch-ua-mobile',
11
+ 'sec-ch-ua-platform',
12
+ 'upgrade-insecure-requests',
13
+ 'user-agent',
14
+ 'accept',
15
+ 'sec-fetch-site',
16
+ 'sec-fetch-mode',
17
+ 'sec-fetch-user',
18
+ 'sec-fetch-dest',
19
+ 'accept-encoding',
20
+ 'accept-language',
21
+ ]
22
+
23
+ function headerOrdering(headers: Record<string, string>) {
24
+ const order: string[] = []
25
+
26
+ for (const name in headers) {
27
+ const key = name.toLowerCase()
28
+
29
+ if (STABLE_HEADERS.includes(key))
30
+ order.push(key)
31
+ }
32
+
33
+ return order
34
+ }
35
+
36
+ export class Token {
37
+ static #cookieName: string = '__auth_'
38
+ static #name: string = 'Authorization'
39
+ static #prefix: string = 'bearer'
40
+ static #cripta: Config
41
+
42
+ static async fromRequest(req: IRequest) {
43
+ const token = this.fromCookie(req) || this.fromHeader(req)
44
+ return token ? await this.parse(req, token) : null
45
+ }
46
+
47
+ static fromHeader(req: IRequest): string | null {
48
+ const header = req.header(this.#name) || req.header('HTTP_AUTHORIZATION') || req.header('REDIRECT_HTTP_AUTHORIZATION') || null
49
+
50
+ if (header) {
51
+ const pos = header.toLowerCase().indexOf(this.#prefix.toLowerCase())
52
+ if (pos < 0) return header
53
+
54
+ let token = header.slice(pos + this.#prefix.length).trim()
55
+ const commaPos = token.indexOf(',')
56
+ if (commaPos > -1) token = token.slice(0, commaPos).trim()
57
+
58
+ return token
59
+ }
60
+
61
+ return null
62
+ }
63
+
64
+ static fromCookie(req: IRequest): string | null {
65
+ const uid = req.header('uid')
66
+
67
+ if (uid) {
68
+ const auth = req.cookie.get(this.#cookieName + uid)
69
+ return auth ? auth : null
70
+ }
71
+
72
+ return null
73
+ }
74
+
75
+ static async cripta() {
76
+ if (this.#cripta)
77
+ return this.#cripta
78
+
79
+ return this.#cripta = await config({ key: getEnv('KEY') })
80
+ }
81
+
82
+ static async parse(req: IRequest, token: string) {
83
+ const host = this.host(req)
84
+ const serveHost = getEnv('FLOW_SERVER', host) as string
85
+
86
+ return parse(await this.cripta(), token, {
87
+ iss: serveHost,
88
+ aud: [host],
89
+ '_': await this.fingerprint(req),
90
+ })
91
+ }
92
+
93
+ static async create(req: IRequest, user: any, exp: number = 7200) {
94
+ const time = Math.floor(Date.now() / 1000)
95
+ const host = this.host(req)
96
+
97
+ return create(await this.cripta(), user, {
98
+ iss: host,
99
+ aud: [host],
100
+ iat: time,
101
+ exp: time + exp,
102
+ '_': await this.fingerprint(req),
103
+ })
104
+ }
105
+
106
+ static setPrefix(prefix: string): void {
107
+ this.#prefix = prefix
108
+ }
109
+
110
+ static setName(name: string): void {
111
+ this.#name = name
112
+ }
113
+
114
+ static host(req: IRequest): string {
115
+ const url = req.url || req.header('host')
116
+ if (!url) return ''
117
+
118
+ let formattedUrl = String(url)
119
+ if (!formattedUrl.startsWith('http'))
120
+ formattedUrl = 'http://' + formattedUrl
121
+
122
+ try {
123
+ const parsedUrl = new URL(formattedUrl)
124
+ return parsedUrl.host
125
+ } catch {
126
+ return ''
127
+ }
128
+ }
129
+
130
+ static async fingerprint(req: IRequest) {
131
+ const ua = parseUA(req.header('user-agent'))
132
+
133
+ const id = bytesToHex(await sha256(
134
+ // (req.header('accept-language') || '')
135
+ this.ip(req)
136
+ + ua.browser.name + ua.browser.version.split('.')[0]
137
+ + ua.os.type + ua.os.name
138
+ + headerOrdering(req.header()).join('')
139
+ ))
140
+
141
+ return id.substring(0, 8) + id.substring(56, 64)
142
+ }
143
+
144
+ static ip(req: IRequest) {
145
+ return req?.ip || 0
146
+ }
147
+ }
@@ -0,0 +1,2 @@
1
+ export type Roles = Record<string, string[] | '*'>
2
+ export type Abilities = string[]
@@ -0,0 +1,41 @@
1
+ import { getEnv } from 't0n'
2
+ import { command } from 't0n/cli'
3
+ import { gray } from 't0n/color'
4
+ import { wait, error, rn } from 't0n/log'
5
+ import { platforms, build, normalizePlatform, platformError } from '../utils'
6
+
7
+ export default command({
8
+ meta: {
9
+ name: 'build',
10
+ description: '🗂️ Perform the build\n',
11
+ },
12
+ args: {
13
+ platform: {
14
+ alias: 'p',
15
+ description: 'Environment platform',
16
+ type: 'enum',
17
+ options: platforms,
18
+ },
19
+ minify: {
20
+ description: 'Minify the result',
21
+ type: 'boolean',
22
+ default: true,
23
+ },
24
+ },
25
+ async run({ args }) {
26
+ const platform = normalizePlatform(args.p || args.platform || getEnv('duto.platform') || args._[0] || 'node')
27
+ if (!platform)
28
+ return platformError()
29
+
30
+ wait('Building for platform: '+ gray(platform))
31
+
32
+ try { // @ts-ignore
33
+ await build({ platform, minify: args.minify })
34
+ } catch (e: any) {
35
+ error('Build failed:\n\n', e)
36
+ process.exit(0)
37
+ } finally {
38
+ rn()
39
+ }
40
+ },
41
+ })
@@ -0,0 +1,81 @@
1
+ import { spawn } from 'node:child_process'
2
+ import { command } from 't0n/cli'
3
+ import { error } from 't0n/log'
4
+ import { getRuntime } from 't0n/cli'
5
+
6
+ import { build, platforms, normalizePlatform, platformError } from '../utils'
7
+ import { _root } from '@/utils'
8
+
9
+ // import build from './build'
10
+
11
+ export default command({
12
+ meta: {
13
+ name: 'deploy',
14
+ description: '☁️ Perform the build and execute deploy\n',
15
+ },
16
+ args: {
17
+ platform: {
18
+ alias: 'p',
19
+ description: 'Environment platform',
20
+ type: 'enum',
21
+ options: platforms,
22
+ },
23
+ minify: {
24
+ description: 'Minify the result',
25
+ type: 'boolean',
26
+ default: true,
27
+ },
28
+ },
29
+ async run({ args }) { // @ts-ignore
30
+ const platform = normalizePlatform(args.p || args.platform || args._[0] || getEnv('duto.platform'))
31
+ if (!platform)
32
+ return platformError()
33
+
34
+ // @ts-ignore
35
+ await build({ platform, minify: args.minify })
36
+ const isBun = getRuntime() === 'bun'
37
+
38
+ switch (platform) {
39
+ case 'aws':
40
+ // TODO: perform aws deploy
41
+ return error('Platform not yet implemented, contact the webmaster')
42
+ case 'workerd':
43
+ const child = spawn(
44
+ isBun ? 'bunx' : 'npx',
45
+ ['wrangler', 'deploy'],
46
+ {
47
+ stdio: 'inherit',
48
+ cwd: _root,
49
+ }
50
+ )
51
+
52
+ child.on('exit', code => process.exit(code ?? 0))
53
+ .on('message', msg => {
54
+ process.send && process.send(msg)
55
+ }).on('disconnect', () => {
56
+ process.disconnect && process.disconnect()
57
+ })
58
+
59
+ return
60
+ case 'vercel':
61
+ return error('Platform not yet implemented, contact the webmaster')
62
+ const vchild = spawn(
63
+ isBun ? 'bunx' : 'npx',
64
+ ['vercel', 'deploy'],
65
+ {
66
+ stdio: 'inherit',
67
+ cwd: _root,
68
+ }
69
+ )
70
+
71
+ vchild.on('exit', code => process.exit(code ?? 0))
72
+ .on('message', msg => {
73
+ process.send && process.send(msg)
74
+ }).on('disconnect', () => {
75
+ process.disconnect && process.disconnect()
76
+ })
77
+
78
+ return
79
+ }
80
+ },
81
+ })