epos 1.2.5 â 1.3.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/eslint.config.js +19 -0
- package/package.json +9 -2
- package/src/kit/kit-server.js +95 -77
package/eslint.config.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import globals from 'globals'
|
|
2
|
+
import pluginJs from '@eslint/js'
|
|
3
|
+
|
|
4
|
+
/** @type {import('eslint').Linter.Config[]} */
|
|
5
|
+
export default [
|
|
6
|
+
{ languageOptions: { globals: globals.browser } },
|
|
7
|
+
pluginJs.configs.recommended,
|
|
8
|
+
{
|
|
9
|
+
languageOptions: {
|
|
10
|
+
globals: {
|
|
11
|
+
Self: false,
|
|
12
|
+
process: false,
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
rules: {
|
|
16
|
+
'no-unused-labels': 0,
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
]
|
package/package.json
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "epos",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"author": "imkost",
|
|
5
5
|
"description": "",
|
|
6
6
|
"keywords": [],
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"type": "module",
|
|
9
9
|
"scripts": {
|
|
10
|
-
"dev": "node ./src/kit/kit-bin.js"
|
|
10
|
+
"dev": "node ./src/kit/kit-bin.js",
|
|
11
|
+
"lint": "eslint ./src",
|
|
12
|
+
"publish": "npm publish --loglevel=error"
|
|
11
13
|
},
|
|
12
14
|
"bin": {
|
|
13
15
|
"epos": "src/kit/kit-bin.js"
|
|
@@ -27,5 +29,10 @@
|
|
|
27
29
|
"mime": "^4.0.6",
|
|
28
30
|
"prettier": "^3.4.2",
|
|
29
31
|
"ws": "^8.18.0"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@eslint/js": "^9.18.0",
|
|
35
|
+
"eslint": "^9.18.0",
|
|
36
|
+
"globals": "^15.14.0"
|
|
30
37
|
}
|
|
31
38
|
}
|
package/src/kit/kit-server.js
CHANGED
|
@@ -6,58 +6,86 @@ import $yaml from 'js-yaml'
|
|
|
6
6
|
import $chokidar from 'chokidar'
|
|
7
7
|
import * as $ws from 'ws'
|
|
8
8
|
|
|
9
|
-
// TODO: handle 'port in use' error
|
|
10
|
-
// TODO: epos-kit as separate package (?) but bin: epos
|
|
11
9
|
const $server = {
|
|
12
|
-
async init(dir
|
|
10
|
+
async init(dir) {
|
|
13
11
|
this._dir = dir
|
|
14
12
|
this._port = 4322
|
|
15
13
|
this._maxFiles = 10_000
|
|
14
|
+
|
|
16
15
|
this._pkgs = {} // { [path]: { name, dir, watcher } }
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
this._server = null
|
|
17
|
+
this._wss = null
|
|
18
|
+
|
|
19
|
+
await this._initServer()
|
|
20
|
+
await this._initWebSocket()
|
|
21
|
+
await this._startWatcher()
|
|
22
|
+
|
|
23
|
+
console.log('đĸ ready')
|
|
21
24
|
},
|
|
22
25
|
|
|
23
|
-
async
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
async _initServer() {
|
|
27
|
+
const ready = Promise.withResolvers()
|
|
28
|
+
|
|
29
|
+
this._server = $http.createServer(async (req, res) => {
|
|
30
|
+
try {
|
|
31
|
+
const { data, type } = await this._handleRequest(req)
|
|
32
|
+
res.writeHead(200, { 'Content-Type': type })
|
|
33
|
+
res.end(data)
|
|
34
|
+
} catch (e) {
|
|
35
|
+
if (e === 404) {
|
|
36
|
+
res.writeHead(404, { 'Content-Type': 'text/plain' })
|
|
37
|
+
res.end('404: File Not Found')
|
|
38
|
+
} else {
|
|
39
|
+
console.error(e)
|
|
40
|
+
res.writeHead(500, { 'Content-Type': 'text/plain' })
|
|
41
|
+
res.end('500: Internal Server Error')
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
this._server.listen(this._port, () => {
|
|
47
|
+
ready.resolve()
|
|
28
48
|
})
|
|
29
|
-
|
|
30
|
-
|
|
49
|
+
|
|
50
|
+
this._server.on('error', e => {
|
|
51
|
+
if (e.code === 'EADDRINUSE') {
|
|
52
|
+
console.log(`đ´ port ${this._port} is already in use`)
|
|
53
|
+
process.exit()
|
|
54
|
+
}
|
|
55
|
+
ready.reject(e)
|
|
31
56
|
})
|
|
32
|
-
|
|
57
|
+
|
|
58
|
+
await ready.promise
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
async _initWebSocket() {
|
|
62
|
+
this._wss = new $ws.WebSocketServer({ server: this._server })
|
|
33
63
|
},
|
|
34
64
|
|
|
35
|
-
async
|
|
36
|
-
const
|
|
65
|
+
async _startWatcher() {
|
|
66
|
+
const ready = Promise.withResolvers()
|
|
37
67
|
const watcher = $chokidar.watch(this._dir, { ignored: this._ignored })
|
|
38
68
|
|
|
39
69
|
// initial scan
|
|
40
|
-
let
|
|
41
|
-
let
|
|
42
|
-
watcher.on('
|
|
43
|
-
if (
|
|
44
|
-
|
|
45
|
-
if (
|
|
46
|
-
console.
|
|
47
|
-
console.
|
|
48
|
-
console.error(`Please point to a directory with less files.`)
|
|
70
|
+
let done = false
|
|
71
|
+
let files = 0
|
|
72
|
+
watcher.on('add', async () => {
|
|
73
|
+
if (done) return
|
|
74
|
+
files += 1
|
|
75
|
+
if (files < this._maxFiles) return
|
|
76
|
+
console.log(`đ´ too many files in ${this._dir}`)
|
|
77
|
+
console.log('âšī¸ select directory with fewer files')
|
|
49
78
|
process.exit()
|
|
50
79
|
})
|
|
51
80
|
watcher.on('ready', () => {
|
|
52
|
-
|
|
53
|
-
|
|
81
|
+
done = false
|
|
82
|
+
ready.resolve()
|
|
54
83
|
})
|
|
55
84
|
|
|
56
85
|
// watch added manifest files
|
|
57
86
|
const variants = new Set(['epos.json', 'epos.yaml', 'epos.yml'])
|
|
58
87
|
watcher.on('add', async path => {
|
|
59
|
-
|
|
60
|
-
if (!isEposManifest) return
|
|
88
|
+
if (!variants.has($path.basename(path))) return
|
|
61
89
|
const pkg = await this._createPkgWatcher(path)
|
|
62
90
|
if (!pkg) return
|
|
63
91
|
this._pkgs[path] = pkg
|
|
@@ -70,73 +98,63 @@ const $server = {
|
|
|
70
98
|
delete this._pkgs[path]
|
|
71
99
|
})
|
|
72
100
|
|
|
73
|
-
await
|
|
74
|
-
},
|
|
75
|
-
|
|
76
|
-
async _startHttpServer() {
|
|
77
|
-
const httpServerReady = Promise.withResolvers()
|
|
78
|
-
const httpServer = $http.createServer(async (req, res) => {
|
|
79
|
-
const pkgName = req.url.split('/')[1]
|
|
80
|
-
const pkg = Object.values(this._pkgs).find(p => p.name === pkgName)
|
|
81
|
-
if (!pkg) {
|
|
82
|
-
res.writeHead(404, { 'Content-Type': 'text/plain' })
|
|
83
|
-
res.end('404: File Not Found')
|
|
84
|
-
return
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
const filePath = $path.join(pkg.dir, req.url.split('/').slice(2).join('/'))
|
|
88
|
-
const contentType = $mime.getType(filePath) || 'application/octet-stream'
|
|
89
|
-
try {
|
|
90
|
-
const data = await $fs.readFile(filePath)
|
|
91
|
-
res.writeHead(200, { 'Content-Type': contentType })
|
|
92
|
-
res.end(data)
|
|
93
|
-
} catch (e) {
|
|
94
|
-
if (e.code === 'ENOENT') {
|
|
95
|
-
res.writeHead(404, { 'Content-Type': 'text/plain' })
|
|
96
|
-
res.end('404: File Not Found')
|
|
97
|
-
} else {
|
|
98
|
-
res.writeHead(500, { 'Content-Type': 'text/plain' })
|
|
99
|
-
res.end('500: Internal Server Error')
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
})
|
|
103
|
-
httpServer.listen(this._port, () => {
|
|
104
|
-
httpServerReady.resolve()
|
|
105
|
-
})
|
|
106
|
-
|
|
107
|
-
httpServer.on('error', e => {
|
|
108
|
-
console.warn(e)
|
|
109
|
-
})
|
|
110
|
-
|
|
111
|
-
await httpServerReady.promise
|
|
112
|
-
return httpServer
|
|
101
|
+
await ready.promise
|
|
113
102
|
},
|
|
114
103
|
|
|
115
104
|
async _createPkgWatcher(manifestPath) {
|
|
105
|
+
// read manifest
|
|
116
106
|
const isJson = manifestPath.endsWith('.json')
|
|
117
107
|
const content = await $fs.readFile(manifestPath, 'utf-8')
|
|
118
108
|
const manifest = isJson ? JSON.parse(content) : $yaml.load(content)
|
|
119
|
-
const pkgName = manifest.name
|
|
120
|
-
if (!pkgName) return null
|
|
121
109
|
|
|
110
|
+
// no name? -> ignore
|
|
111
|
+
const name = manifest.name
|
|
112
|
+
if (!name) return null
|
|
113
|
+
|
|
114
|
+
// create pkg dir watcher
|
|
122
115
|
const dir = $path.dirname(manifestPath)
|
|
123
116
|
const watcher = $chokidar.watch(dir, {
|
|
124
117
|
ignored: this._ignored,
|
|
125
118
|
ignoreInitial: true,
|
|
126
119
|
})
|
|
127
120
|
|
|
121
|
+
// broadcast changes
|
|
128
122
|
watcher.on('all', (event, path) => {
|
|
129
|
-
const data = JSON.stringify({ name
|
|
123
|
+
const data = JSON.stringify({ name, path: $path.relative(dir, path) })
|
|
130
124
|
for (const client of this._wss.clients) {
|
|
131
125
|
if (client.readyState !== 1) continue
|
|
132
126
|
client.send(data)
|
|
133
127
|
}
|
|
134
128
|
})
|
|
135
129
|
|
|
136
|
-
return {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
130
|
+
return { name, dir, watcher }
|
|
131
|
+
},
|
|
132
|
+
|
|
133
|
+
async _handleRequest(req) {
|
|
134
|
+
// /<pkgName>/some/path/to/file.jsx
|
|
135
|
+
const [pkgName, ...filePath] = req.url.split('/').slice(1)
|
|
136
|
+
|
|
137
|
+
// pkg not found? -> 404
|
|
138
|
+
const pkg = Object.values(this._pkgs).find(p => p.name === pkgName)
|
|
139
|
+
if (!pkg) throw 404
|
|
140
|
+
|
|
141
|
+
// file not found? -> 404
|
|
142
|
+
const path = $path.join(pkg.dir, ...filePath)
|
|
143
|
+
const exists = await this._fileExists(path)
|
|
144
|
+
if (!exists) throw 404
|
|
145
|
+
|
|
146
|
+
// respond file
|
|
147
|
+
const data = await $fs.readFile(path)
|
|
148
|
+
const type = $mime.getType(path) || 'application/octet-stream'
|
|
149
|
+
return { data, type }
|
|
150
|
+
},
|
|
151
|
+
|
|
152
|
+
async _fileExists(path) {
|
|
153
|
+
try {
|
|
154
|
+
await $fs.access(path, $fs.constants.F_OK)
|
|
155
|
+
return true
|
|
156
|
+
} catch {
|
|
157
|
+
return false
|
|
140
158
|
}
|
|
141
159
|
},
|
|
142
160
|
|