epos 1.1.9 → 1.2.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/package.json +9 -4
- package/src/bridge/bridge-mobx-react.js +1 -1
- package/src/bridge/bridge-mobx.js +1 -1
- package/src/bridge/bridge-react-dom-client.js +1 -1
- package/src/bridge/bridge-react-dom.js +1 -1
- package/src/bridge/bridge-react-jsx-runtime.js +1 -1
- package/src/bridge/bridge-react.js +1 -1
- package/src/kit/kit-bin.js +5 -103
- package/src/kit/kit-server.js +131 -0
- package/src/types/types.d.ts +54 -96
package/package.json
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "epos",
|
|
3
|
-
"version": "1.1
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"author": "imkost",
|
|
5
5
|
"description": "",
|
|
6
6
|
"keywords": [],
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"type": "module",
|
|
9
|
+
"scripts": {
|
|
10
|
+
"dev": "node ./src/kit/kit-bin.js"
|
|
11
|
+
},
|
|
12
|
+
"bin": {
|
|
13
|
+
"epos": "./src/kit/kit-bin.js"
|
|
14
|
+
},
|
|
9
15
|
"exports": {
|
|
10
16
|
".": {
|
|
11
17
|
"types": "./src/types/types.d.ts"
|
|
@@ -13,11 +19,10 @@
|
|
|
13
19
|
"./plugin-vite": "./src/plugin-vite.js",
|
|
14
20
|
"./plugin-esbuild": "./src/plugin-esbuild.js"
|
|
15
21
|
},
|
|
16
|
-
"bin": {
|
|
17
|
-
"epos": "./src/kit/kit-bin.js"
|
|
18
|
-
},
|
|
19
22
|
"devDependencies": {
|
|
20
23
|
"chokidar": "^4.0.3",
|
|
24
|
+
"glob": "^11.0.1",
|
|
25
|
+
"ignore": "^7.0.3",
|
|
21
26
|
"js-yaml": "^4.1.0",
|
|
22
27
|
"mime": "^4.0.6",
|
|
23
28
|
"prettier": "^3.4.2",
|
package/src/kit/kit-bin.js
CHANGED
|
@@ -1,106 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import $
|
|
4
|
-
import $path from 'path'
|
|
5
|
-
import $http from 'node:http'
|
|
6
|
-
import $mime from 'mime'
|
|
7
|
-
import $yaml from 'js-yaml'
|
|
8
|
-
import $chokidar from 'chokidar'
|
|
9
|
-
import * as $ws from 'ws'
|
|
3
|
+
import $server from './kit-server.js'
|
|
10
4
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
await this._start(dirs)
|
|
16
|
-
|
|
17
|
-
const server = $http.createServer(async (req, res) => {
|
|
18
|
-
const pkgName = req.url.split('/')[1]
|
|
19
|
-
const dir = dirs.find(({ name }) => name === pkgName)
|
|
20
|
-
if (!dir) {
|
|
21
|
-
res.writeHead(404, { 'Content-Type': 'text/plain' })
|
|
22
|
-
res.end('404: File Not Found')
|
|
23
|
-
return
|
|
24
|
-
}
|
|
25
|
-
const path = req.url.split('/').slice(2).join('/')
|
|
26
|
-
const filePath = $path.join(dir.dir, path)
|
|
27
|
-
const contentType = $mime.getType(filePath) || 'application/octet-stream'
|
|
28
|
-
|
|
29
|
-
try {
|
|
30
|
-
const data = await $fs.readFile(filePath)
|
|
31
|
-
res.writeHead(200, { 'Content-Type': contentType })
|
|
32
|
-
res.end(data)
|
|
33
|
-
} catch (e) {
|
|
34
|
-
if (e.code === 'ENOENT') {
|
|
35
|
-
res.writeHead(404, { 'Content-Type': 'text/plain' })
|
|
36
|
-
res.end('404: File Not Found')
|
|
37
|
-
} else {
|
|
38
|
-
res.writeHead(500, { 'Content-Type': 'text/plain' })
|
|
39
|
-
res.end('500: Internal Server Error')
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
})
|
|
43
|
-
|
|
44
|
-
const PORT = 3098
|
|
45
|
-
server.listen(PORT, () => {
|
|
46
|
-
console.log(`Server running at http://localhost:${PORT}/`)
|
|
47
|
-
})
|
|
48
|
-
},
|
|
49
|
-
|
|
50
|
-
async _findManifestDirs(dir) {
|
|
51
|
-
const results = []
|
|
52
|
-
const variants = ['epos.json', 'epos.yaml', 'epos.yml']
|
|
53
|
-
const entries = await $fs.readdir(dir, { withFileTypes: true })
|
|
54
|
-
|
|
55
|
-
const manifestEntry = entries.find(e => e.isFile() && variants.includes(e.name))
|
|
56
|
-
if (manifestEntry) {
|
|
57
|
-
const path = $path.join(dir, manifestEntry.name)
|
|
58
|
-
const content = await $fs.readFile(path, 'utf-8')
|
|
59
|
-
const isJson = path.endsWith('.json')
|
|
60
|
-
const manifest = isJson ? JSON.parse(content) : $yaml.load(content)
|
|
61
|
-
if (!manifest.name) throw new Error(`no name in ${path}`)
|
|
62
|
-
results.push({ name: manifest.name, dir })
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
for (const entry of entries) {
|
|
66
|
-
if (!entry.isDirectory()) continue
|
|
67
|
-
if (entry.name.startsWith('.')) continue
|
|
68
|
-
if (entry.name === 'node_modules') continue
|
|
69
|
-
const subDir = $path.join(dir, entry.name)
|
|
70
|
-
const subResults = await this._findManifestDirs(subDir)
|
|
71
|
-
results.push(...subResults)
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
return results
|
|
75
|
-
},
|
|
76
|
-
|
|
77
|
-
async _start(dirs, port = 3099) {
|
|
78
|
-
const wss = new $ws.WebSocketServer({ port }, () => {
|
|
79
|
-
console.log(`WebSocket server started on ws://localhost:${port}`)
|
|
80
|
-
})
|
|
81
|
-
|
|
82
|
-
const manifestDirs = dirs
|
|
83
|
-
|
|
84
|
-
manifestDirs.forEach(({ dir, name }) => {
|
|
85
|
-
const watcher = $chokidar.watch(dir, {
|
|
86
|
-
ignored: /(node_modules|\.git)/,
|
|
87
|
-
ignoreInitial: true,
|
|
88
|
-
})
|
|
89
|
-
|
|
90
|
-
const broadcast = path => {
|
|
91
|
-
console.log('change', path)
|
|
92
|
-
const data = JSON.stringify({ name, path: $path.relative(dir, path) })
|
|
93
|
-
for (const client of wss.clients) {
|
|
94
|
-
if (client.readyState !== 1) continue
|
|
95
|
-
client.send(data)
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
watcher.on('add', p => broadcast(p))
|
|
100
|
-
watcher.on('change', p => broadcast(p))
|
|
101
|
-
watcher.on('unlink', p => broadcast(p))
|
|
102
|
-
})
|
|
103
|
-
},
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
$server.init()
|
|
5
|
+
run: (async () => {
|
|
6
|
+
const dir = process.argv[2] || process.cwd()
|
|
7
|
+
await $server.init(dir)
|
|
8
|
+
})()
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import $fs from 'node:fs/promises'
|
|
2
|
+
import $path from 'path'
|
|
3
|
+
import $http from 'node:http'
|
|
4
|
+
import $mime from 'mime'
|
|
5
|
+
import $yaml from 'js-yaml'
|
|
6
|
+
import $chokidar from 'chokidar'
|
|
7
|
+
import * as $ws from 'ws'
|
|
8
|
+
|
|
9
|
+
const $server = {
|
|
10
|
+
async init(dir = '/Users/imkost/z/epos') {
|
|
11
|
+
this._dir = dir
|
|
12
|
+
this._wsPort = 2217
|
|
13
|
+
this._httpPort = 2218
|
|
14
|
+
this._maxFiles = 10_000
|
|
15
|
+
this._pkgs = {} // { [path]: { name, dir, watcher } }
|
|
16
|
+
await this._start()
|
|
17
|
+
},
|
|
18
|
+
|
|
19
|
+
async _start() {
|
|
20
|
+
const watcherReady = Promise.withResolvers()
|
|
21
|
+
const httpServerReady = Promise.withResolvers()
|
|
22
|
+
|
|
23
|
+
const wss = new $ws.WebSocketServer({ port: this._wsPort })
|
|
24
|
+
const watcher = $chokidar.watch(this._dir, { ignored: this._ignored })
|
|
25
|
+
|
|
26
|
+
// initial scan
|
|
27
|
+
let isInitialScan = true
|
|
28
|
+
let initialFileCount = 0
|
|
29
|
+
watcher.on('all', async (event, path) => {
|
|
30
|
+
if (!isInitialScan) return
|
|
31
|
+
initialFileCount += 1
|
|
32
|
+
if (initialFileCount < this._maxFiles) return
|
|
33
|
+
console.error('⛔ too many files')
|
|
34
|
+
console.error(`More than ${this._maxFiles} files in the directory.`)
|
|
35
|
+
console.error(`Please point to a directory with less files.`)
|
|
36
|
+
process.exit()
|
|
37
|
+
})
|
|
38
|
+
watcher.on('ready', () => {
|
|
39
|
+
isInitialScan = false
|
|
40
|
+
watcherReady.resolve()
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
// watch added manifest files
|
|
44
|
+
const variants = new Set(['epos.json', 'epos.yaml', 'epos.yml'])
|
|
45
|
+
watcher.on('add', async path => {
|
|
46
|
+
const isEposManifest = variants.has($path.basename(path))
|
|
47
|
+
if (!isEposManifest) return
|
|
48
|
+
const pkg = await this._createPkgWatcher(path, wss)
|
|
49
|
+
if (!pkg) return
|
|
50
|
+
this._pkgs[path] = pkg
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
// watch removed manifest files
|
|
54
|
+
watcher.on('unlink', path => {
|
|
55
|
+
if (!this._pkgs[path]) return
|
|
56
|
+
this._pkgs[path].watcher.close()
|
|
57
|
+
delete this._pkgs[path]
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
// static server
|
|
61
|
+
const httpServer = $http.createServer(async (req, res) => {
|
|
62
|
+
const pkgName = req.url.split('/')[1]
|
|
63
|
+
const pkg = Object.values(this._pkgs).find(p => p.name === pkgName)
|
|
64
|
+
if (!pkg) {
|
|
65
|
+
res.writeHead(404, { 'Content-Type': 'text/plain' })
|
|
66
|
+
res.end('404: File Not Found')
|
|
67
|
+
return
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const filePath = $path.join(pkg.dir, req.url.split('/').slice(2).join('/'))
|
|
71
|
+
const contentType = $mime.getType(filePath) || 'application/octet-stream'
|
|
72
|
+
try {
|
|
73
|
+
const data = await $fs.readFile(filePath)
|
|
74
|
+
res.writeHead(200, { 'Content-Type': contentType })
|
|
75
|
+
res.end(data)
|
|
76
|
+
} catch (e) {
|
|
77
|
+
if (e.code === 'ENOENT') {
|
|
78
|
+
res.writeHead(404, { 'Content-Type': 'text/plain' })
|
|
79
|
+
res.end('404: File Not Found')
|
|
80
|
+
} else {
|
|
81
|
+
res.writeHead(500, { 'Content-Type': 'text/plain' })
|
|
82
|
+
res.end('500: Internal Server Error')
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
})
|
|
86
|
+
httpServer.listen(this._httpPort, () => {
|
|
87
|
+
httpServerReady.resolve()
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
await watcherReady.promise
|
|
91
|
+
await httpServerReady.promise
|
|
92
|
+
|
|
93
|
+
console.log('⚡ running')
|
|
94
|
+
},
|
|
95
|
+
|
|
96
|
+
async _createPkgWatcher(manifestPath, wss) {
|
|
97
|
+
const isJson = manifestPath.endsWith('.json')
|
|
98
|
+
const content = await $fs.readFile(manifestPath, 'utf-8')
|
|
99
|
+
const manifest = isJson ? JSON.parse(content) : $yaml.load(content)
|
|
100
|
+
const pkgName = manifest.name
|
|
101
|
+
if (!pkgName) return null
|
|
102
|
+
|
|
103
|
+
const dir = $path.dirname(manifestPath)
|
|
104
|
+
const watcher = $chokidar.watch(dir, {
|
|
105
|
+
ignored: this._ignored,
|
|
106
|
+
ignoreInitial: true,
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
watcher.on('all', (event, path) => {
|
|
110
|
+
const data = JSON.stringify({ name: pkgName, path: $path.relative(dir, path) })
|
|
111
|
+
for (const client of wss.clients) {
|
|
112
|
+
if (client.readyState !== 1) continue
|
|
113
|
+
client.send(data)
|
|
114
|
+
}
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
return {
|
|
118
|
+
watcher,
|
|
119
|
+
name: pkgName,
|
|
120
|
+
dir,
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
|
|
124
|
+
_ignored(path) {
|
|
125
|
+
if (path.includes('node_modules')) return true
|
|
126
|
+
if ($path.basename(path).startsWith('.')) return true
|
|
127
|
+
return false
|
|
128
|
+
},
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export default $server
|
package/src/types/types.d.ts
CHANGED
|
@@ -1,103 +1,61 @@
|
|
|
1
|
-
declare var
|
|
2
|
-
|
|
1
|
+
declare var self: EposSelf
|
|
2
|
+
declare var Self: EposSelf
|
|
3
|
+
|
|
4
|
+
interface EposSelf {
|
|
3
5
|
name: string
|
|
4
|
-
state:
|
|
5
|
-
|
|
6
|
+
state: Record<string, any>
|
|
7
|
+
tabId: number
|
|
8
|
+
browser: Record<string, any>
|
|
9
|
+
root: HTMLElement
|
|
10
|
+
shadow: ShadowRoot
|
|
11
|
+
|
|
6
12
|
on: (event: string, callback: (...args: any[]) => any) => void
|
|
7
13
|
off: (event: string, callback?: (...args: any[]) => any) => void
|
|
8
14
|
send: (event: string, ...args: any[]) => Promise<any>
|
|
9
15
|
call: (event: string, ...args: any[]) => Promise<any>
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
is: { tab: boolean; popup: boolean; sidePanel: boolean; background: boolean }
|
|
13
|
-
use: (packageName: string) => any
|
|
14
|
-
|
|
15
|
-
// ui
|
|
16
|
+
use: (pkgName: string) => any
|
|
17
|
+
fetch: (url: string, opts?: object) => Promise<object>
|
|
16
18
|
render: (what: any, container?: HTMLElement | ShadowRoot) => void
|
|
17
|
-
portal: (vnode:
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
19
|
+
portal: (vnode: object, container: HTMLElement | ShadowRoot) => void
|
|
20
|
+
|
|
21
|
+
unit<T extends Record<string, unknown> & { create?: (...args: any[]) => any }>(
|
|
22
|
+
name: string,
|
|
23
|
+
schema: T,
|
|
24
|
+
): T['create'] extends (...args: infer P) => any ? (...args: P) => T : () => T
|
|
25
|
+
|
|
26
|
+
is: {
|
|
27
|
+
tab: boolean
|
|
28
|
+
popup: boolean
|
|
29
|
+
sidePanel: boolean
|
|
30
|
+
background: boolean
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
component: {
|
|
34
|
+
(render: Function): any
|
|
35
|
+
(name: string, render: Function): any
|
|
36
|
+
(schema: Record<string, any>): any
|
|
37
|
+
(name: string, schema: Record<string, any>): any
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
storage: {
|
|
41
|
+
get(key: string): Promise<any>
|
|
42
|
+
set(key: string, value: any): Promise<void>
|
|
43
|
+
keys(): Promise<string[]>
|
|
44
|
+
remove(key: string): Promise<void>
|
|
45
|
+
clear(): Promise<void>
|
|
46
|
+
connect(name: string): Omit<EposSelf['storage'], 'connect'>
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
files: {
|
|
50
|
+
get: (name: string) => Promise<Blob>
|
|
51
|
+
url: (name: string) => Promise<string>
|
|
52
|
+
text: (name: string) => Promise<string>
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
react: object
|
|
56
|
+
reactJsxRuntime: object
|
|
57
|
+
reactDom: object
|
|
58
|
+
reactDomClient: object
|
|
59
|
+
mobx: object
|
|
60
|
+
mobxReact: object
|
|
35
61
|
}
|
|
36
|
-
|
|
37
|
-
// base
|
|
38
|
-
declare var state: typeof Self.state
|
|
39
|
-
declare var storage: typeof Self.storage
|
|
40
|
-
declare var on: typeof Self.on
|
|
41
|
-
declare var off: typeof Self.off
|
|
42
|
-
declare var send: typeof Self.send
|
|
43
|
-
declare var call: typeof Self.call
|
|
44
|
-
declare var is: typeof Self.is
|
|
45
|
-
declare var tabId: typeof Self.tabId
|
|
46
|
-
declare var browser: typeof Self.browser
|
|
47
|
-
declare var use: typeof Self.use
|
|
48
|
-
|
|
49
|
-
// ui
|
|
50
|
-
declare var render: typeof Self.render
|
|
51
|
-
declare var portal: typeof Self.portal
|
|
52
|
-
declare var component: typeof Self.component
|
|
53
|
-
declare var root: typeof Self.root
|
|
54
|
-
declare var shadow: typeof Self.shadow
|
|
55
|
-
|
|
56
|
-
// unit
|
|
57
|
-
declare var unit: typeof Self.unit
|
|
58
|
-
|
|
59
|
-
// files
|
|
60
|
-
declare var files: typeof Self.files
|
|
61
|
-
|
|
62
|
-
// libs
|
|
63
|
-
declare var react: typeof Self.react
|
|
64
|
-
declare var reactJsxRuntime: typeof Self.reactJsxRuntime
|
|
65
|
-
declare var reactDom: typeof Self.reactDom
|
|
66
|
-
declare var reactDomClient: typeof Self.reactDomClient
|
|
67
|
-
declare var mobx: typeof Self.mobx
|
|
68
|
-
declare var mobxReact: typeof Self.mobxReact
|
|
69
|
-
|
|
70
|
-
type EposStorage = EposStorageBase & {
|
|
71
|
-
connect: (name: string) => EposStorageBase
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
type EposStorageBase = {
|
|
75
|
-
get: (key: string) => Promise<any>
|
|
76
|
-
set: (key: string, value: any) => Promise<void>
|
|
77
|
-
keys: () => Promise<string[]>
|
|
78
|
-
remove: (key: string) => Promise<void>
|
|
79
|
-
clear: () => Promise<void>
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// type EposComponent = {
|
|
83
|
-
// (name: number, schema: EposComponentSchema): any
|
|
84
|
-
// (name: number, render: Function): any
|
|
85
|
-
// (schema: EposComponentSchema): any
|
|
86
|
-
// (render: Function): any
|
|
87
|
-
// }
|
|
88
|
-
type EposComponent = (name: number) => any
|
|
89
|
-
|
|
90
|
-
type EposComponentSchema = {}
|
|
91
|
-
|
|
92
|
-
type EposUnitSchema = {}
|
|
93
|
-
|
|
94
|
-
type EposFiles = {
|
|
95
|
-
get: (name: string) => Promise<Blob>
|
|
96
|
-
url: (name: string) => Promise<string>
|
|
97
|
-
text: (name: string) => Promise<string>
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// declare function unit<T extends { constructor: (...args: any[]) => void }>(
|
|
101
|
-
// name: string,
|
|
102
|
-
// definition: T & ThisType<T>,
|
|
103
|
-
// ): (...args: Parameters<T['constructor']>) => T
|