thebird 1.2.51 → 1.2.52
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/docs/preview-sw-client.js +20 -0
- package/docs/preview-sw.js +18 -9
- package/docs/shell-node.js +53 -0
- package/package.json +1 -1
|
@@ -22,3 +22,23 @@ export async function registerPreviewSW() {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
registerPreviewSW();
|
|
25
|
+
|
|
26
|
+
navigator.serviceWorker?.addEventListener('message', e => {
|
|
27
|
+
if (e.data?.type !== 'EXPRESS_REQUEST') return;
|
|
28
|
+
const { path, method } = e.data;
|
|
29
|
+
const replyPort = e.ports[0];
|
|
30
|
+
const handlers = window.__debug?.shell?.httpHandlers || {};
|
|
31
|
+
const app = Object.values(handlers)[0];
|
|
32
|
+
if (!app?.routes) { replyPort.postMessage({ status: 404, body: 'no express app' }); return; }
|
|
33
|
+
const routes = app.routes[method] || [];
|
|
34
|
+
const match = routes.find(r => r.path === '*' || r.path === path || path.startsWith(r.path));
|
|
35
|
+
if (!match) { replyPort.postMessage({ status: 404, body: 'no route for ' + path }); return; }
|
|
36
|
+
const res = {
|
|
37
|
+
_body: '', _status: 200, _ct: 'text/html',
|
|
38
|
+
send(b) { this._body = b; replyPort.postMessage({ status: this._status, body: this._body, contentType: this._ct }); },
|
|
39
|
+
json(o) { this._ct = 'application/json'; this.send(JSON.stringify(o)); },
|
|
40
|
+
status(n) { this._status = n; return this; },
|
|
41
|
+
};
|
|
42
|
+
const req = { method, path, query: {}, headers: {} };
|
|
43
|
+
try { match.fn(req, res); } catch (err) { replyPort.postMessage({ status: 500, body: err.message }); }
|
|
44
|
+
});
|
package/docs/preview-sw.js
CHANGED
|
@@ -36,13 +36,22 @@ self.addEventListener('fetch', e => {
|
|
|
36
36
|
const url = new URL(e.request.url);
|
|
37
37
|
const idx = url.pathname.indexOf('/preview/');
|
|
38
38
|
if (idx === -1) return;
|
|
39
|
-
const key = url.pathname.slice(idx + '/preview/'.length);
|
|
40
|
-
e.respondWith(
|
|
41
|
-
openIDB()
|
|
42
|
-
.then(db => getFS(db))
|
|
43
|
-
.then(fs => {
|
|
44
|
-
if (!(key in fs)) return new Response('not found: ' + key, { status: 404 });
|
|
45
|
-
return new Response(fs[key], { status: 200, headers: { 'Content-Type': getMime(key) } });
|
|
46
|
-
})
|
|
47
|
-
);
|
|
39
|
+
const key = url.pathname.slice(idx + '/preview/'.length) || 'index.html';
|
|
40
|
+
e.respondWith(handlePreview(key, e.request));
|
|
48
41
|
});
|
|
42
|
+
|
|
43
|
+
async function handlePreview(key, request) {
|
|
44
|
+
const db = await openIDB();
|
|
45
|
+
const fs = await getFS(db);
|
|
46
|
+
if (key in fs) return new Response(fs[key], { status: 200, headers: { 'Content-Type': getMime(key) } });
|
|
47
|
+
const clients = await self.clients.matchAll({ type: 'window' });
|
|
48
|
+
if (!clients.length) return new Response('not found: ' + key, { status: 404 });
|
|
49
|
+
const { port1, port2 } = new MessageChannel();
|
|
50
|
+
const result = await new Promise((res, rej) => {
|
|
51
|
+
const t = setTimeout(() => rej(new Error('express timeout')), 5000);
|
|
52
|
+
port1.onmessage = e => { clearTimeout(t); res(e.data); };
|
|
53
|
+
clients[0].postMessage({ type: 'EXPRESS_REQUEST', path: '/' + key, method: request.method }, [port2]);
|
|
54
|
+
});
|
|
55
|
+
if (!result || result.status === 404) return new Response('not found: ' + key, { status: 404 });
|
|
56
|
+
return new Response(result.body, { status: result.status || 200, headers: { 'Content-Type': result.contentType || 'text/html' } });
|
|
57
|
+
}
|
package/docs/shell-node.js
CHANGED
|
@@ -1,3 +1,44 @@
|
|
|
1
|
+
function serializeRoutes(routes) {
|
|
2
|
+
const out = {};
|
|
3
|
+
for (const [method, arr] of Object.entries(routes)) {
|
|
4
|
+
out[method] = arr.map(r => ({ path: r.path }));
|
|
5
|
+
}
|
|
6
|
+
return out;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const BUILTIN_MODULES = {
|
|
10
|
+
express: () => () => {
|
|
11
|
+
const routes = { GET: [], POST: [], USE: [] };
|
|
12
|
+
const app = {
|
|
13
|
+
get: (path, fn) => routes.GET.push({ path, fn }),
|
|
14
|
+
post: (path, fn) => routes.POST.push({ path, fn }),
|
|
15
|
+
use: (fn) => routes.USE.push({ path: '*', fn }),
|
|
16
|
+
listen: (port, cb) => {
|
|
17
|
+
window.__debug.shell.httpHandlers[port] = { routes };
|
|
18
|
+
navigator.serviceWorker?.controller?.postMessage({ type: 'REGISTER_ROUTES', port, routes: serializeRoutes(routes) });
|
|
19
|
+
term.write('Express listening on :' + port + '\r\n');
|
|
20
|
+
cb?.();
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
return app;
|
|
24
|
+
},
|
|
25
|
+
'better-sqlite3': () => class Database {
|
|
26
|
+
constructor(name) {
|
|
27
|
+
this._name = name;
|
|
28
|
+
if (!window.__sqlJs) throw new Error('sql.js not loaded — call await loadSql() first');
|
|
29
|
+
this._db = new window.__sqlJs.Database();
|
|
30
|
+
}
|
|
31
|
+
prepare(sql) {
|
|
32
|
+
const db = this._db;
|
|
33
|
+
return {
|
|
34
|
+
run: (...params) => { db.run(sql, params); return { changes: 1 }; },
|
|
35
|
+
get: (...params) => { const r = db.exec(sql, params); return r[0]?.values[0] ? Object.fromEntries(r[0].columns.map((c, i) => [c, r[0].values[0][i]])) : undefined; },
|
|
36
|
+
all: (...params) => { const r = db.exec(sql, params); if (!r[0]) return []; return r[0].values.map(row => Object.fromEntries(r[0].columns.map((c, i) => [c, row[i]]))); },
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
|
|
1
42
|
export function createNodeEnv({ ctx, term }) {
|
|
2
43
|
const scope = {
|
|
3
44
|
process: {
|
|
@@ -12,6 +53,7 @@ export function createNodeEnv({ ctx, term }) {
|
|
|
12
53
|
warn: (...a) => term.write('\x1b[33m' + a.map(String).join(' ') + '\x1b[0m\r\n'),
|
|
13
54
|
},
|
|
14
55
|
require: id => {
|
|
56
|
+
if (BUILTIN_MODULES[id]) return BUILTIN_MODULES[id]();
|
|
15
57
|
const key = 'node_modules/' + id + '/index.js';
|
|
16
58
|
const src = (window.__debug.idbSnapshot || {})[key];
|
|
17
59
|
if (src == null) throw new Error('module not found: ' + id);
|
|
@@ -19,6 +61,17 @@ export function createNodeEnv({ ctx, term }) {
|
|
|
19
61
|
new Function('module', 'exports', 'require', src)(mod, mod.exports, scope.require);
|
|
20
62
|
return mod.exports;
|
|
21
63
|
},
|
|
64
|
+
loadSql: async () => {
|
|
65
|
+
if (window.__sqlJs) return window.__sqlJs;
|
|
66
|
+
await new Promise((res, rej) => {
|
|
67
|
+
const s = document.createElement('script');
|
|
68
|
+
s.src = './vendor/sql-wasm.js';
|
|
69
|
+
s.onload = res; s.onerror = rej;
|
|
70
|
+
document.head.appendChild(s);
|
|
71
|
+
});
|
|
72
|
+
window.__sqlJs = await initSqlJs({ locateFile: f => './vendor/' + f });
|
|
73
|
+
return window.__sqlJs;
|
|
74
|
+
},
|
|
22
75
|
setTimeout, setInterval, clearTimeout, clearInterval, fetch,
|
|
23
76
|
Buffer: {
|
|
24
77
|
from: (s, enc) => enc === 'base64'
|
package/package.json
CHANGED