TOES
This commit is contained in:
parent
7e0cde9ef5
commit
1ec012338f
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
node_modules/
|
||||||
|
|
||||||
25
TODO.txt
Normal file
25
TODO.txt
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
# toes
|
||||||
|
|
||||||
|
## server
|
||||||
|
|
||||||
|
[x] start toes server
|
||||||
|
[x] scans for apps/**/package.json, scripts.toes
|
||||||
|
[x] runs that for each, giving it a PORT
|
||||||
|
[x] has GET / page that shows all the running apps/status/port
|
||||||
|
[ ] watch each app and restart it on update
|
||||||
|
|
||||||
|
## cli
|
||||||
|
|
||||||
|
[ ] `toes --help`
|
||||||
|
[ ] `toes --version`
|
||||||
|
[ ] `toes list`
|
||||||
|
[ ] `toes new`
|
||||||
|
[ ] `toes pull`
|
||||||
|
[ ] `toes push`
|
||||||
|
[ ] `toes sync`
|
||||||
|
|
||||||
|
## webui
|
||||||
|
|
||||||
|
[ ] list projects
|
||||||
|
[ ] todo.txt
|
||||||
|
[ ] ...
|
||||||
37
apps/basic/bun.lock
Normal file
37
apps/basic/bun.lock
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
{
|
||||||
|
"lockfileVersion": 1,
|
||||||
|
"workspaces": {
|
||||||
|
"": {
|
||||||
|
"name": "toes-app",
|
||||||
|
"dependencies": {
|
||||||
|
"forge": "git+https://git.nose.space/defunkt/forge",
|
||||||
|
"hype": "git+https://git.nose.space/defunkt/hype",
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/bun": "latest",
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"typescript": "^5.9.2",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"packages": {
|
||||||
|
"@types/bun": ["@types/bun@1.3.6", "", { "dependencies": { "bun-types": "1.3.6" } }, "sha512-uWCv6FO/8LcpREhenN1d1b6fcspAB+cefwD7uti8C8VffIv0Um08TKMn98FynpTiU38+y2dUO55T11NgDt8VAA=="],
|
||||||
|
|
||||||
|
"@types/node": ["@types/node@25.0.10", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg=="],
|
||||||
|
|
||||||
|
"bun-types": ["bun-types@1.3.6", "", { "dependencies": { "@types/node": "*" } }, "sha512-OlFwHcnNV99r//9v5IIOgQ9Uk37gZqrNMCcqEaExdkVq3Avwqok1bJFmvGMCkCE0FqzdY8VMOZpfpR3lwI+CsQ=="],
|
||||||
|
|
||||||
|
"forge": ["forge@git+https://git.nose.space/defunkt/forge#9b6e1e91ec77d7e03589cac256d97fb9cd942184", { "peerDependencies": { "typescript": "^5" } }, "9b6e1e91ec77d7e03589cac256d97fb9cd942184"],
|
||||||
|
|
||||||
|
"hono": ["hono@4.11.7", "", {}, "sha512-l7qMiNee7t82bH3SeyUCt9UF15EVmaBvsppY2zQtrbIhl/yzBTny+YUxsVjSjQ6gaqaeVtZmGocom8TzBlA4Yw=="],
|
||||||
|
|
||||||
|
"hype": ["hype@git+https://git.nose.space/defunkt/hype#52086f4eb94cc36ecd9470d9a101ff01002687c8", { "dependencies": { "hono": "^4.10.4", "kleur": "^4.1.5" }, "peerDependencies": { "typescript": "^5" } }, "52086f4eb94cc36ecd9470d9a101ff01002687c8"],
|
||||||
|
|
||||||
|
"kleur": ["kleur@4.1.5", "", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="],
|
||||||
|
|
||||||
|
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
|
||||||
|
|
||||||
|
"undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="],
|
||||||
|
}
|
||||||
|
}
|
||||||
10
apps/basic/index.tsx
Normal file
10
apps/basic/index.tsx
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
import { Hype } from 'hype'
|
||||||
|
|
||||||
|
const app = new Hype
|
||||||
|
|
||||||
|
app.get('/', c => c.html(<h1>Hi there!</h1>))
|
||||||
|
|
||||||
|
const apps = () => {
|
||||||
|
}
|
||||||
|
|
||||||
|
export default app.defaults
|
||||||
21
apps/basic/package.json
Normal file
21
apps/basic/package.json
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
"name": "toes-app",
|
||||||
|
"module": "src/index.ts",
|
||||||
|
"type": "module",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"toes": "bun start",
|
||||||
|
"start": "bun run index.tsx",
|
||||||
|
"dev": "bun run --hot index.tsx"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/bun": "latest"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"typescript": "^5.9.2"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"hype": "git+https://git.nose.space/defunkt/hype",
|
||||||
|
"forge": "git+https://git.nose.space/defunkt/forge"
|
||||||
|
}
|
||||||
|
}
|
||||||
30
apps/basic/tsconfig.json
Normal file
30
apps/basic/tsconfig.json
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
// Environment setup & latest features
|
||||||
|
"lib": ["ESNext"],
|
||||||
|
"target": "ESNext",
|
||||||
|
"module": "Preserve",
|
||||||
|
"moduleDetection": "force",
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"jsxImportSource": "hono/jsx",
|
||||||
|
"allowJs": true,
|
||||||
|
|
||||||
|
// Bundler mode
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"verbatimModuleSyntax": true,
|
||||||
|
"noEmit": true,
|
||||||
|
|
||||||
|
// Best practices
|
||||||
|
"strict": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"noUncheckedIndexedAccess": true,
|
||||||
|
"noImplicitOverride": true,
|
||||||
|
|
||||||
|
// Some stricter flags (disabled by default)
|
||||||
|
"noUnusedLocals": false,
|
||||||
|
"noUnusedParameters": false,
|
||||||
|
"noPropertyAccessFromIndexSignature": false
|
||||||
|
}
|
||||||
|
}
|
||||||
1
apps/file.txt
Normal file
1
apps/file.txt
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
hi
|
||||||
37
apps/profile/bun.lock
Normal file
37
apps/profile/bun.lock
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
{
|
||||||
|
"lockfileVersion": 1,
|
||||||
|
"workspaces": {
|
||||||
|
"": {
|
||||||
|
"name": "toes-app",
|
||||||
|
"dependencies": {
|
||||||
|
"forge": "git+https://git.nose.space/defunkt/forge",
|
||||||
|
"hype": "git+https://git.nose.space/defunkt/hype",
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/bun": "latest",
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"typescript": "^5.9.2",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"packages": {
|
||||||
|
"@types/bun": ["@types/bun@1.3.6", "", { "dependencies": { "bun-types": "1.3.6" } }, "sha512-uWCv6FO/8LcpREhenN1d1b6fcspAB+cefwD7uti8C8VffIv0Um08TKMn98FynpTiU38+y2dUO55T11NgDt8VAA=="],
|
||||||
|
|
||||||
|
"@types/node": ["@types/node@25.0.10", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg=="],
|
||||||
|
|
||||||
|
"bun-types": ["bun-types@1.3.6", "", { "dependencies": { "@types/node": "*" } }, "sha512-OlFwHcnNV99r//9v5IIOgQ9Uk37gZqrNMCcqEaExdkVq3Avwqok1bJFmvGMCkCE0FqzdY8VMOZpfpR3lwI+CsQ=="],
|
||||||
|
|
||||||
|
"forge": ["forge@git+https://git.nose.space/defunkt/forge#9b6e1e91ec77d7e03589cac256d97fb9cd942184", { "peerDependencies": { "typescript": "^5" } }, "9b6e1e91ec77d7e03589cac256d97fb9cd942184"],
|
||||||
|
|
||||||
|
"hono": ["hono@4.11.7", "", {}, "sha512-l7qMiNee7t82bH3SeyUCt9UF15EVmaBvsppY2zQtrbIhl/yzBTny+YUxsVjSjQ6gaqaeVtZmGocom8TzBlA4Yw=="],
|
||||||
|
|
||||||
|
"hype": ["hype@git+https://git.nose.space/defunkt/hype#52086f4eb94cc36ecd9470d9a101ff01002687c8", { "dependencies": { "hono": "^4.10.4", "kleur": "^4.1.5" }, "peerDependencies": { "typescript": "^5" } }, "52086f4eb94cc36ecd9470d9a101ff01002687c8"],
|
||||||
|
|
||||||
|
"kleur": ["kleur@4.1.5", "", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="],
|
||||||
|
|
||||||
|
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
|
||||||
|
|
||||||
|
"undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="],
|
||||||
|
}
|
||||||
|
}
|
||||||
10
apps/profile/index.tsx
Normal file
10
apps/profile/index.tsx
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
import { Hype } from 'hype'
|
||||||
|
|
||||||
|
const app = new Hype
|
||||||
|
|
||||||
|
app.get('/', c => c.html(<h1>Hi there!</h1>))
|
||||||
|
|
||||||
|
const apps = () => {
|
||||||
|
}
|
||||||
|
|
||||||
|
export default app.defaults
|
||||||
21
apps/profile/package.json
Normal file
21
apps/profile/package.json
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
"name": "toes-app",
|
||||||
|
"module": "src/index.ts",
|
||||||
|
"type": "module",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"toes": "bun start",
|
||||||
|
"start": "bun run index.tsx",
|
||||||
|
"dev": "bun run --hot index.tsx"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/bun": "latest"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"typescript": "^5.9.2"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"hype": "git+https://git.nose.space/defunkt/hype",
|
||||||
|
"forge": "git+https://git.nose.space/defunkt/forge"
|
||||||
|
}
|
||||||
|
}
|
||||||
30
apps/profile/tsconfig.json
Normal file
30
apps/profile/tsconfig.json
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
// Environment setup & latest features
|
||||||
|
"lib": ["ESNext"],
|
||||||
|
"target": "ESNext",
|
||||||
|
"module": "Preserve",
|
||||||
|
"moduleDetection": "force",
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"jsxImportSource": "hono/jsx",
|
||||||
|
"allowJs": true,
|
||||||
|
|
||||||
|
// Bundler mode
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"verbatimModuleSyntax": true,
|
||||||
|
"noEmit": true,
|
||||||
|
|
||||||
|
// Best practices
|
||||||
|
"strict": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"noUncheckedIndexedAccess": true,
|
||||||
|
"noImplicitOverride": true,
|
||||||
|
|
||||||
|
// Some stricter flags (disabled by default)
|
||||||
|
"noUnusedLocals": false,
|
||||||
|
"noUnusedParameters": false,
|
||||||
|
"noPropertyAccessFromIndexSignature": false
|
||||||
|
}
|
||||||
|
}
|
||||||
1
apps/risk/package.json
Normal file
1
apps/risk/package.json
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
{}
|
||||||
37
bun.lock
Normal file
37
bun.lock
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
{
|
||||||
|
"lockfileVersion": 1,
|
||||||
|
"workspaces": {
|
||||||
|
"": {
|
||||||
|
"name": "toes",
|
||||||
|
"dependencies": {
|
||||||
|
"forge": "git+https://git.nose.space/defunkt/forge",
|
||||||
|
"hype": "git+https://git.nose.space/defunkt/hype",
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/bun": "latest",
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"typescript": "^5.9.2",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"packages": {
|
||||||
|
"@types/bun": ["@types/bun@1.3.6", "", { "dependencies": { "bun-types": "1.3.6" } }, "sha512-uWCv6FO/8LcpREhenN1d1b6fcspAB+cefwD7uti8C8VffIv0Um08TKMn98FynpTiU38+y2dUO55T11NgDt8VAA=="],
|
||||||
|
|
||||||
|
"@types/node": ["@types/node@25.0.10", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg=="],
|
||||||
|
|
||||||
|
"bun-types": ["bun-types@1.3.6", "", { "dependencies": { "@types/node": "*" } }, "sha512-OlFwHcnNV99r//9v5IIOgQ9Uk37gZqrNMCcqEaExdkVq3Avwqok1bJFmvGMCkCE0FqzdY8VMOZpfpR3lwI+CsQ=="],
|
||||||
|
|
||||||
|
"forge": ["forge@git+https://git.nose.space/defunkt/forge#9b6e1e91ec77d7e03589cac256d97fb9cd942184", { "peerDependencies": { "typescript": "^5" } }, "9b6e1e91ec77d7e03589cac256d97fb9cd942184"],
|
||||||
|
|
||||||
|
"hono": ["hono@4.11.7", "", {}, "sha512-l7qMiNee7t82bH3SeyUCt9UF15EVmaBvsppY2zQtrbIhl/yzBTny+YUxsVjSjQ6gaqaeVtZmGocom8TzBlA4Yw=="],
|
||||||
|
|
||||||
|
"hype": ["hype@git+https://git.nose.space/defunkt/hype#52086f4eb94cc36ecd9470d9a101ff01002687c8", { "dependencies": { "hono": "^4.10.4", "kleur": "^4.1.5" }, "peerDependencies": { "typescript": "^5" } }, "52086f4eb94cc36ecd9470d9a101ff01002687c8"],
|
||||||
|
|
||||||
|
"kleur": ["kleur@4.1.5", "", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="],
|
||||||
|
|
||||||
|
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
|
||||||
|
|
||||||
|
"undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="],
|
||||||
|
}
|
||||||
|
}
|
||||||
20
package.json
Normal file
20
package.json
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"name": "toes",
|
||||||
|
"module": "src/index.ts",
|
||||||
|
"type": "module",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"start": "bun run src/server/index.tsx",
|
||||||
|
"dev": "bun run --hot src/server/index.tsx"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/bun": "latest"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"typescript": "^5.9.2"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"hype": "git+https://git.nose.space/defunkt/hype",
|
||||||
|
"forge": "git+https://git.nose.space/defunkt/forge"
|
||||||
|
}
|
||||||
|
}
|
||||||
143
src/server/index.tsx
Normal file
143
src/server/index.tsx
Normal file
|
|
@ -0,0 +1,143 @@
|
||||||
|
import { Subprocess } from 'bun'
|
||||||
|
import { Hype } from 'hype'
|
||||||
|
import { readdirSync, readFileSync } from 'fs'
|
||||||
|
import { join } from 'path'
|
||||||
|
|
||||||
|
const APPS_DIR = join(process.env.DATA_DIR ?? '.', 'apps')
|
||||||
|
|
||||||
|
type RunningApp = {
|
||||||
|
name: string
|
||||||
|
port: number
|
||||||
|
proc: Subprocess
|
||||||
|
}
|
||||||
|
|
||||||
|
const runningApps = new Map<string, RunningApp>()
|
||||||
|
|
||||||
|
const err = (app: string, ...msg: string[]) =>
|
||||||
|
console.error('🐾', `${app}:`, ...msg)
|
||||||
|
|
||||||
|
const info = (app: string, ...msg: string[]) =>
|
||||||
|
console.log('🐾', `${app}:`, ...msg)
|
||||||
|
|
||||||
|
const log = (app: string, ...msg: string[]) =>
|
||||||
|
console.log('🐾', `${app}»`, ...msg)
|
||||||
|
|
||||||
|
const appNames = () => {
|
||||||
|
return readdirSync(APPS_DIR, { withFileTypes: true })
|
||||||
|
.filter(e => e.isDirectory())
|
||||||
|
.map(e => e.name)
|
||||||
|
.sort()
|
||||||
|
}
|
||||||
|
|
||||||
|
let NEXT_PORT = 3001
|
||||||
|
const getPort = () => NEXT_PORT++
|
||||||
|
|
||||||
|
const runApps = () => {
|
||||||
|
for (const dir of appNames()) {
|
||||||
|
if (!isApp(dir)) continue
|
||||||
|
const port = getPort()
|
||||||
|
runApp(dir, port)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const isApp = (dir: string): boolean => {
|
||||||
|
try {
|
||||||
|
const file = readFileSync(join(APPS_DIR, dir, 'package.json'), 'utf-8')
|
||||||
|
const json = JSON.parse(file)
|
||||||
|
return !!json.scripts?.toes
|
||||||
|
} catch (e) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const loadApp = (dir: string) => {
|
||||||
|
try {
|
||||||
|
const file = readFileSync(join(APPS_DIR, dir, 'package.json'), 'utf-8')
|
||||||
|
const json = JSON.parse(file)
|
||||||
|
|
||||||
|
if (json.scripts?.toes) {
|
||||||
|
return json
|
||||||
|
} else {
|
||||||
|
err(dir, 'No `bun toes` script in package.json')
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
err(dir, 'No package.json')
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const runApp = (dir: string, port: number) => {
|
||||||
|
const pkg = loadApp(dir)
|
||||||
|
if (!pkg.scripts?.toes) return
|
||||||
|
|
||||||
|
const cwd = join(APPS_DIR, dir)
|
||||||
|
const cmd = ['bun', 'run', 'toes']
|
||||||
|
|
||||||
|
info(dir, `Starting on port ${port}...`)
|
||||||
|
|
||||||
|
const proc = Bun.spawn(cmd, {
|
||||||
|
cwd,
|
||||||
|
env: { ...process.env, PORT: String(port) },
|
||||||
|
stdout: 'pipe',
|
||||||
|
stderr: 'pipe',
|
||||||
|
})
|
||||||
|
|
||||||
|
runningApps.set(dir, { name: dir, port, proc })
|
||||||
|
|
||||||
|
const streamOutput = async (stream: ReadableStream<Uint8Array> | null, isErr: boolean) => {
|
||||||
|
if (!stream) return
|
||||||
|
const reader = stream.getReader()
|
||||||
|
const decoder = new TextDecoder()
|
||||||
|
while (true) {
|
||||||
|
const { done, value } = await reader.read()
|
||||||
|
if (done) break
|
||||||
|
const text = decoder.decode(value).trimEnd()
|
||||||
|
if (text) {
|
||||||
|
//isErr ? err(dir, text) : info(dir, text)
|
||||||
|
log(dir, text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
streamOutput(proc.stdout, false)
|
||||||
|
streamOutput(proc.stderr, true)
|
||||||
|
|
||||||
|
// Handle process exit
|
||||||
|
proc.exited.then(code => {
|
||||||
|
if (code !== 0)
|
||||||
|
err(dir, `Exited with code ${code}`)
|
||||||
|
else
|
||||||
|
info(dir, 'Stopped')
|
||||||
|
runningApps.delete(dir)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const getRunningApps = () =>
|
||||||
|
Array.from(runningApps.values()).map(({ name, port }) => ({ name, port }))
|
||||||
|
|
||||||
|
const stopApp = (dir: string) => {
|
||||||
|
const app = runningApps.get(dir)
|
||||||
|
if (app) {
|
||||||
|
info(dir, 'Stopping...')
|
||||||
|
app.proc.kill()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('🐾 Toes!')
|
||||||
|
runApps()
|
||||||
|
|
||||||
|
|
||||||
|
const app = new Hype()
|
||||||
|
|
||||||
|
app.get('/', c => {
|
||||||
|
return c.html(
|
||||||
|
<>
|
||||||
|
<h1>🐾 Running Apps</h1>
|
||||||
|
{getRunningApps().map(app => <h2><a href={`http://localhost:${app.port}`}>{app.port}: {app.name}</a></h2>)}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
export { getRunningApps, stopApp }
|
||||||
|
export default app.defaults
|
||||||
30
tsconfig.json
Normal file
30
tsconfig.json
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
// Environment setup & latest features
|
||||||
|
"lib": ["ESNext"],
|
||||||
|
"target": "ESNext",
|
||||||
|
"module": "Preserve",
|
||||||
|
"moduleDetection": "force",
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"jsxImportSource": "hono/jsx",
|
||||||
|
"allowJs": true,
|
||||||
|
|
||||||
|
// Bundler mode
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"verbatimModuleSyntax": true,
|
||||||
|
"noEmit": true,
|
||||||
|
|
||||||
|
// Best practices
|
||||||
|
"strict": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"noUncheckedIndexedAccess": true,
|
||||||
|
"noImplicitOverride": true,
|
||||||
|
|
||||||
|
// Some stricter flags (disabled by default)
|
||||||
|
"noUnusedLocals": false,
|
||||||
|
"noUnusedParameters": false,
|
||||||
|
"noPropertyAccessFromIndexSignature": false
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user