@pedit/serve 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +107 -2
- package/dist/bridge.d.ts +13 -0
- package/dist/bridge.js +87 -0
- package/dist/bridge.js.map +1 -0
- package/dist/client.d.ts +33 -0
- package/dist/client.js +176 -0
- package/dist/client.js.map +1 -0
- package/dist/errors.d.ts +19 -0
- package/dist/errors.js +24 -0
- package/dist/errors.js.map +1 -0
- package/dist/hono.d.ts +43 -0
- package/dist/hono.js +292 -0
- package/dist/hono.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/resolve.d.ts +20 -0
- package/dist/resolve.js +88 -0
- package/dist/resolve.js.map +1 -0
- package/dist/served.d.ts +29 -0
- package/dist/served.js +251 -0
- package/dist/served.js.map +1 -0
- package/dist/types.d.ts +14 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +86 -4
package/dist/hono.js
ADDED
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
import { createHash, randomBytes } from 'node:crypto';
|
|
2
|
+
import { realpathSync, statSync } from 'node:fs';
|
|
3
|
+
import { realpath, stat } from 'node:fs/promises';
|
|
4
|
+
import { isAbsolute, relative, sep } from 'node:path';
|
|
5
|
+
import { Hono } from 'hono';
|
|
6
|
+
import { HTTPException } from 'hono/http-exception';
|
|
7
|
+
import { HeldError } from "./errors.js";
|
|
8
|
+
import { resolvePedit } from "./resolve.js";
|
|
9
|
+
import { ServedFiles } from "./served.js";
|
|
10
|
+
const ID = /^[A-Za-z0-9_-]{1,128}$/;
|
|
11
|
+
const TOKEN_TTL_MS = 60_000;
|
|
12
|
+
const MAX_TOKENS = 1024;
|
|
13
|
+
const RATE_PER_SECOND = 10;
|
|
14
|
+
const MAX_PAGE_BUFFER = 16 * 1024 * 1024;
|
|
15
|
+
/** True for 127.0.0.0/8, ::1 and their IPv4-mapped forms. */
|
|
16
|
+
export function isLoopback(address) {
|
|
17
|
+
if (!address)
|
|
18
|
+
return false;
|
|
19
|
+
const a = address.toLowerCase().replace(/^::ffff:/, '');
|
|
20
|
+
return a === '::1' || /^127(\.\d{1,3}){3}$/.test(a);
|
|
21
|
+
}
|
|
22
|
+
function peerAddress(c) {
|
|
23
|
+
const env = c.env;
|
|
24
|
+
return env?.incoming?.socket?.remoteAddress ?? env?.requestIP?.(c.req.raw)?.address;
|
|
25
|
+
}
|
|
26
|
+
function validate(options) {
|
|
27
|
+
if (typeof options.upgradeWebSocket !== 'function')
|
|
28
|
+
throw new TypeError('upgradeWebSocket is required');
|
|
29
|
+
if (typeof options.resolve !== 'function')
|
|
30
|
+
throw new TypeError('resolve is required');
|
|
31
|
+
if (!options.app)
|
|
32
|
+
throw new TypeError('app must not be empty');
|
|
33
|
+
if (!Array.isArray(options.origins) || options.origins.length === 0) {
|
|
34
|
+
throw new TypeError('origins must list at least one origin');
|
|
35
|
+
}
|
|
36
|
+
const hosts = new Set();
|
|
37
|
+
for (const o of options.origins) {
|
|
38
|
+
let url;
|
|
39
|
+
try {
|
|
40
|
+
url = new URL(o);
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
throw new TypeError(`bad origin: ${o}`);
|
|
44
|
+
}
|
|
45
|
+
if (!/^https?:$/.test(url.protocol) || url.origin !== o) {
|
|
46
|
+
throw new TypeError(`origins must be exact scheme://host[:port]: ${o}`);
|
|
47
|
+
}
|
|
48
|
+
hosts.add(url.host.toLowerCase());
|
|
49
|
+
}
|
|
50
|
+
let root;
|
|
51
|
+
try {
|
|
52
|
+
root = realpathSync(options.root);
|
|
53
|
+
if (!statSync(root).isDirectory())
|
|
54
|
+
throw new Error();
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
throw new TypeError('root must be an existing directory');
|
|
58
|
+
}
|
|
59
|
+
return { root, hosts, origins: new Set(options.origins) };
|
|
60
|
+
}
|
|
61
|
+
/** Component-wise: `root-private/x` is not under `root`. */
|
|
62
|
+
function inside(root, path) {
|
|
63
|
+
const rel = relative(root, path);
|
|
64
|
+
return rel !== '' && rel !== '..' && !rel.startsWith(`..${sep}`) && !isAbsolute(rel);
|
|
65
|
+
}
|
|
66
|
+
export function peditRoutes(options) {
|
|
67
|
+
const { root, hosts, origins } = validate(options);
|
|
68
|
+
const idByPath = new Map();
|
|
69
|
+
const emit = (event) => {
|
|
70
|
+
try {
|
|
71
|
+
options.onEvent?.(event);
|
|
72
|
+
}
|
|
73
|
+
catch { }
|
|
74
|
+
};
|
|
75
|
+
let filesP;
|
|
76
|
+
// Resolved on first use, so a missing binary is a 503 and an event, not a crash at startup.
|
|
77
|
+
const getFiles = () => {
|
|
78
|
+
filesP ??= resolvePedit({ bin: options.bin }).then(({ bin }) => new ServedFiles({
|
|
79
|
+
bin,
|
|
80
|
+
app: options.app,
|
|
81
|
+
idleMs: options.idleMs,
|
|
82
|
+
onEvent(e) {
|
|
83
|
+
const id = (e.path !== null && idByPath.get(e.path)) || null;
|
|
84
|
+
if (e.type === 'error')
|
|
85
|
+
emit({ type: 'error', id, message: e.message });
|
|
86
|
+
else if (id !== null) {
|
|
87
|
+
emit(e.type === 'started'
|
|
88
|
+
? { type: 'started', id, path: e.path }
|
|
89
|
+
: { type: 'stopped', id, path: e.path, reason: e.reason });
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
}), (e) => {
|
|
93
|
+
filesP = undefined;
|
|
94
|
+
throw e;
|
|
95
|
+
});
|
|
96
|
+
return filesP;
|
|
97
|
+
};
|
|
98
|
+
const tokens = new Map();
|
|
99
|
+
const hits = new Map();
|
|
100
|
+
const json = (c, status, error) => c.json({ error }, status, { 'Cache-Control': 'no-store' });
|
|
101
|
+
function rateLimited(key) {
|
|
102
|
+
const now = Date.now();
|
|
103
|
+
if (hits.size > 10_000) {
|
|
104
|
+
for (const [k, v] of hits)
|
|
105
|
+
if (now - (v.at(-1) ?? 0) > 1000)
|
|
106
|
+
hits.delete(k);
|
|
107
|
+
}
|
|
108
|
+
const recent = (hits.get(key) ?? []).filter((t) => now - t < 1000);
|
|
109
|
+
recent.push(now);
|
|
110
|
+
hits.set(key, recent);
|
|
111
|
+
return recent.length > RATE_PER_SECOND;
|
|
112
|
+
}
|
|
113
|
+
async function lookup(id, c) {
|
|
114
|
+
const found = await options.resolve(id, c);
|
|
115
|
+
if (!found || (found.role !== 'edit' && found.role !== 'read'))
|
|
116
|
+
return null;
|
|
117
|
+
if (typeof found.path !== 'string' || !isAbsolute(found.path))
|
|
118
|
+
return null;
|
|
119
|
+
try {
|
|
120
|
+
const real = await realpath(found.path);
|
|
121
|
+
if (!inside(root, real) || !(await stat(real)).isFile())
|
|
122
|
+
return null;
|
|
123
|
+
return { path: found.path, real, role: found.role };
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
async function open(id, found) {
|
|
130
|
+
idByPath.set(found.real, id);
|
|
131
|
+
return (await getFiles()).acquire(found.real);
|
|
132
|
+
}
|
|
133
|
+
const failure = (c, e, role, id) => {
|
|
134
|
+
if (e instanceof HeldError && role === 'edit')
|
|
135
|
+
return json(c, 409, 'held');
|
|
136
|
+
if (!(e instanceof HeldError))
|
|
137
|
+
emit({ type: 'error', id, message: String(e?.message) });
|
|
138
|
+
return json(c, 503, 'busy');
|
|
139
|
+
};
|
|
140
|
+
const app = new Hono();
|
|
141
|
+
const guard = async (c, next) => {
|
|
142
|
+
const id = c.req.param('id');
|
|
143
|
+
if (!id || !ID.test(id))
|
|
144
|
+
return c.notFound();
|
|
145
|
+
if (!hosts.has((c.req.header('host') ?? '').toLowerCase()))
|
|
146
|
+
return c.notFound();
|
|
147
|
+
if (!options.allowRemote && !isLoopback(peerAddress(c)))
|
|
148
|
+
return json(c, 403, 'forbidden');
|
|
149
|
+
await next();
|
|
150
|
+
};
|
|
151
|
+
app.use('/:id', guard);
|
|
152
|
+
app.use('/:id/*', guard);
|
|
153
|
+
app.post('/:id/session', async (c) => {
|
|
154
|
+
const id = c.req.param('id');
|
|
155
|
+
const origin = c.req.header('origin');
|
|
156
|
+
const sameOrigin = c.req.header('sec-fetch-site') === 'same-origin';
|
|
157
|
+
if (!(origin !== undefined && origins.has(origin)) && !sameOrigin) {
|
|
158
|
+
return json(c, 403, 'forbidden');
|
|
159
|
+
}
|
|
160
|
+
const who = createHash('sha256')
|
|
161
|
+
.update(c.req.header('cookie') ?? peerAddress(c) ?? '')
|
|
162
|
+
.digest('hex');
|
|
163
|
+
if (rateLimited(`${who}:${id}`))
|
|
164
|
+
return json(c, 429, 'rate');
|
|
165
|
+
let found;
|
|
166
|
+
try {
|
|
167
|
+
found = await lookup(id, c);
|
|
168
|
+
}
|
|
169
|
+
catch (e) {
|
|
170
|
+
if (e instanceof HTTPException)
|
|
171
|
+
throw e;
|
|
172
|
+
emit({ type: 'error', id, message: String(e?.message) });
|
|
173
|
+
return json(c, 500, 'internal');
|
|
174
|
+
}
|
|
175
|
+
if (!found)
|
|
176
|
+
return json(c, 404, 'not-found');
|
|
177
|
+
let generation;
|
|
178
|
+
try {
|
|
179
|
+
generation = (await open(id, found)).generation;
|
|
180
|
+
}
|
|
181
|
+
catch (e) {
|
|
182
|
+
return failure(c, e, found.role, id);
|
|
183
|
+
}
|
|
184
|
+
const now = Date.now();
|
|
185
|
+
if (tokens.size >= MAX_TOKENS) {
|
|
186
|
+
for (const [k, g] of tokens)
|
|
187
|
+
if (g.expires <= now)
|
|
188
|
+
tokens.delete(k);
|
|
189
|
+
if (tokens.size >= MAX_TOKENS)
|
|
190
|
+
return json(c, 429, 'rate');
|
|
191
|
+
}
|
|
192
|
+
const token = randomBytes(32).toString('hex');
|
|
193
|
+
tokens.set(token, {
|
|
194
|
+
id,
|
|
195
|
+
role: found.role,
|
|
196
|
+
path: found.path,
|
|
197
|
+
generation,
|
|
198
|
+
expires: now + TOKEN_TTL_MS,
|
|
199
|
+
});
|
|
200
|
+
return c.json({ token, role: found.role, generation }, 200, { 'Cache-Control': 'no-store' });
|
|
201
|
+
});
|
|
202
|
+
app.get('/:id/doc', async (c, next) => {
|
|
203
|
+
const id = c.req.param('id');
|
|
204
|
+
const origin = c.req.header('origin');
|
|
205
|
+
if (c.req.header('upgrade')?.toLowerCase() !== 'websocket')
|
|
206
|
+
return c.notFound();
|
|
207
|
+
if (origin === undefined || !origins.has(origin))
|
|
208
|
+
return json(c, 403, 'forbidden');
|
|
209
|
+
// Get and delete before any await: a raced second upgrade finds nothing.
|
|
210
|
+
const token = c.req.query('token') ?? '';
|
|
211
|
+
const grant = tokens.get(token);
|
|
212
|
+
tokens.delete(token);
|
|
213
|
+
if (!grant || grant.id !== id || grant.expires <= Date.now())
|
|
214
|
+
return json(c, 403, 'forbidden');
|
|
215
|
+
let found;
|
|
216
|
+
try {
|
|
217
|
+
found = await lookup(id, c);
|
|
218
|
+
}
|
|
219
|
+
catch (e) {
|
|
220
|
+
if (e instanceof HTTPException)
|
|
221
|
+
throw e;
|
|
222
|
+
return json(c, 500, 'internal');
|
|
223
|
+
}
|
|
224
|
+
if (!found || found.role !== grant.role || found.path !== grant.path) {
|
|
225
|
+
return json(c, 403, 'forbidden');
|
|
226
|
+
}
|
|
227
|
+
let bridge;
|
|
228
|
+
try {
|
|
229
|
+
const served = await open(id, found);
|
|
230
|
+
// The child may have restarted since the session: the page's copy is of another document.
|
|
231
|
+
if (served.generation !== grant.generation)
|
|
232
|
+
return json(c, 403, 'forbidden');
|
|
233
|
+
bridge = await served.dial(found.role);
|
|
234
|
+
}
|
|
235
|
+
catch (e) {
|
|
236
|
+
return failure(c, e, found.role, id);
|
|
237
|
+
}
|
|
238
|
+
// If the upgrade never completes, do not keep the slot.
|
|
239
|
+
const abandoned = setTimeout(() => bridge.close(), 10_000);
|
|
240
|
+
abandoned.unref();
|
|
241
|
+
// The child can close while the browser's upgrade is pending: keep that and close the
|
|
242
|
+
// browser as soon as it opens.
|
|
243
|
+
let sock;
|
|
244
|
+
let ended;
|
|
245
|
+
const closeSock = (ws, reason, code) => {
|
|
246
|
+
const ok = code !== undefined && (code === 1000 || (code >= 3000 && code < 5000));
|
|
247
|
+
if (ok)
|
|
248
|
+
ws.close(code, Buffer.from(reason).subarray(0, 120).toString());
|
|
249
|
+
// The child is gone: the page re-issues a session and compares generations.
|
|
250
|
+
else
|
|
251
|
+
ws.close(4001, 'document restarted');
|
|
252
|
+
};
|
|
253
|
+
bridge.onclose = (reason, code) => {
|
|
254
|
+
ended = { reason, code };
|
|
255
|
+
clearTimeout(abandoned);
|
|
256
|
+
if (sock)
|
|
257
|
+
closeSock(sock, reason, code);
|
|
258
|
+
};
|
|
259
|
+
const upgrade = options.upgradeWebSocket(() => ({
|
|
260
|
+
onOpen(_evt, ws) {
|
|
261
|
+
clearTimeout(abandoned);
|
|
262
|
+
if (ended)
|
|
263
|
+
return closeSock(ws, ended.reason, ended.code);
|
|
264
|
+
sock = ws;
|
|
265
|
+
bridge.onframe = (frame) => {
|
|
266
|
+
const raw = ws.raw;
|
|
267
|
+
if ((raw?.bufferedAmount ?? 0) > MAX_PAGE_BUFFER)
|
|
268
|
+
return ws.close(1013, 'too slow');
|
|
269
|
+
ws.send(frame);
|
|
270
|
+
};
|
|
271
|
+
},
|
|
272
|
+
onMessage(evt, ws) {
|
|
273
|
+
if (typeof evt.data === 'string')
|
|
274
|
+
return ws.close(1003, 'binary frames only');
|
|
275
|
+
if (evt.data instanceof ArrayBuffer)
|
|
276
|
+
bridge.send(new Uint8Array(evt.data));
|
|
277
|
+
},
|
|
278
|
+
onClose() {
|
|
279
|
+
clearTimeout(abandoned);
|
|
280
|
+
bridge.close();
|
|
281
|
+
},
|
|
282
|
+
}));
|
|
283
|
+
return upgrade(c, next);
|
|
284
|
+
});
|
|
285
|
+
app.notFound((c) => c.body(null, 404));
|
|
286
|
+
return Object.assign(app, {
|
|
287
|
+
close: async () => {
|
|
288
|
+
await (await filesP?.catch(() => undefined))?.close();
|
|
289
|
+
},
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
//# sourceMappingURL=hono.js.map
|
package/dist/hono.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hono.js","sourceRoot":"","sources":["../src/hono.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export type { Bridge } from './bridge.ts';
|
|
2
|
+
export { HeldError, LimitError, NoPeditError } from './errors.ts';
|
|
3
|
+
export { MIN_PEDIT_VERSION, peditVersionOk, resolvePedit } from './resolve.ts';
|
|
4
|
+
export { type ServedFile, ServedFiles, type ServedFilesOptions } from './served.ts';
|
|
5
|
+
export type { Role, ServeEvent } from './types.ts';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AACjE,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAC9E,OAAO,EAAmB,WAAW,EAA2B,MAAM,aAAa,CAAA"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/** The first pedit release with `pedit serve --root`, `generation` in `listening` and `X-Pedit-Role`. */
|
|
2
|
+
export declare const MIN_PEDIT_VERSION = "0.4.0";
|
|
3
|
+
/**
|
|
4
|
+
* True when `output` (what `pedit --version` prints) is at least MIN_PEDIT_VERSION. A source build
|
|
5
|
+
* prints `dev` or a Go pseudo-version such as `v0.3.1-0.20261031060942-98b75f74184d`; its base
|
|
6
|
+
* says nothing about the commit, so those are accepted and left to the feature checks.
|
|
7
|
+
*/
|
|
8
|
+
export declare function peditVersionOk(output: string, min?: string): boolean;
|
|
9
|
+
/** The only environment a pedit child gets. */
|
|
10
|
+
export declare function peditEnv(): NodeJS.ProcessEnv;
|
|
11
|
+
/**
|
|
12
|
+
* Which pedit binary: `options.bin` (absolute) or `pedit` on the PATH. Config only, never request data.
|
|
13
|
+
* The file must be a regular file owned by this user or root, and not writable by group or others.
|
|
14
|
+
*/
|
|
15
|
+
export declare function resolvePedit(options?: {
|
|
16
|
+
bin?: string;
|
|
17
|
+
}): Promise<{
|
|
18
|
+
bin: string;
|
|
19
|
+
version: string;
|
|
20
|
+
}>;
|
package/dist/resolve.js
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { execFile } from 'node:child_process';
|
|
2
|
+
import { constants } from 'node:fs';
|
|
3
|
+
import { access, realpath, stat } from 'node:fs/promises';
|
|
4
|
+
import { delimiter, isAbsolute, join } from 'node:path';
|
|
5
|
+
import { NoPeditError } from "./errors.js";
|
|
6
|
+
/** The first pedit release with `pedit serve --root`, `generation` in `listening` and `X-Pedit-Role`. */
|
|
7
|
+
export const MIN_PEDIT_VERSION = '0.4.0';
|
|
8
|
+
const UPDATE_HINT = 'update with Homebrew (brew upgrade pedit), the install script (https://edit.piconic.ai/install.sh) or GitHub releases (https://github.com/piconic-ai/pedit/releases)';
|
|
9
|
+
/**
|
|
10
|
+
* True when `output` (what `pedit --version` prints) is at least MIN_PEDIT_VERSION. A source build
|
|
11
|
+
* prints `dev` or a Go pseudo-version such as `v0.3.1-0.20261031060942-98b75f74184d`; its base
|
|
12
|
+
* says nothing about the commit, so those are accepted and left to the feature checks.
|
|
13
|
+
*/
|
|
14
|
+
export function peditVersionOk(output, min = MIN_PEDIT_VERSION) {
|
|
15
|
+
const v = output.trim().replace(/^pedit\s+/, '');
|
|
16
|
+
if (v === 'dev' || /-(0\.)?\d{14}-[0-9a-f]{12}(\+.*)?$/.test(v))
|
|
17
|
+
return true;
|
|
18
|
+
const parse = (s) => s.replace(/^v/, '').split(/[-+]/)[0]?.split('.').map(Number) ?? [];
|
|
19
|
+
const [got, want] = [parse(v), parse(min)];
|
|
20
|
+
if (got.length !== 3 || got.some(Number.isNaN))
|
|
21
|
+
return false;
|
|
22
|
+
for (let i = 0; i < 3; i++) {
|
|
23
|
+
if (got[i] !== want[i])
|
|
24
|
+
return (got[i] ?? 0) > (want[i] ?? 0);
|
|
25
|
+
}
|
|
26
|
+
return !/^v?\d+\.\d+\.\d+-/.test(v); // 0.4.0-rc1 is older than 0.4.0
|
|
27
|
+
}
|
|
28
|
+
/** The only environment a pedit child gets. */
|
|
29
|
+
export function peditEnv() {
|
|
30
|
+
const env = {};
|
|
31
|
+
for (const k of ['PATH', 'HOME', 'XDG_CONFIG_HOME', 'TMPDIR']) {
|
|
32
|
+
const v = process.env[k];
|
|
33
|
+
if (v !== undefined)
|
|
34
|
+
env[k] = v;
|
|
35
|
+
}
|
|
36
|
+
return env;
|
|
37
|
+
}
|
|
38
|
+
async function findOnPath() {
|
|
39
|
+
for (const dir of (process.env.PATH ?? '').split(delimiter)) {
|
|
40
|
+
if (!dir || !isAbsolute(dir))
|
|
41
|
+
continue;
|
|
42
|
+
const candidate = join(dir, 'pedit');
|
|
43
|
+
try {
|
|
44
|
+
await access(candidate, constants.X_OK);
|
|
45
|
+
return candidate;
|
|
46
|
+
}
|
|
47
|
+
catch { }
|
|
48
|
+
}
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Which pedit binary: `options.bin` (absolute) or `pedit` on the PATH. Config only, never request data.
|
|
53
|
+
* The file must be a regular file owned by this user or root, and not writable by group or others.
|
|
54
|
+
*/
|
|
55
|
+
export async function resolvePedit(options = {}) {
|
|
56
|
+
if (process.platform === 'win32')
|
|
57
|
+
throw new Error('@pedit/serve does not support Windows');
|
|
58
|
+
const given = options.bin ?? (await findOnPath());
|
|
59
|
+
if (given === undefined)
|
|
60
|
+
throw new NoPeditError('pedit was not found on the PATH');
|
|
61
|
+
if (!isAbsolute(given))
|
|
62
|
+
throw new NoPeditError('the pedit binary must be an absolute path');
|
|
63
|
+
let bin;
|
|
64
|
+
try {
|
|
65
|
+
bin = await realpath(given);
|
|
66
|
+
const st = await stat(bin);
|
|
67
|
+
if (!st.isFile())
|
|
68
|
+
throw new NoPeditError('the pedit binary is not a regular file');
|
|
69
|
+
if (st.uid !== process.getuid?.() && st.uid !== 0) {
|
|
70
|
+
throw new NoPeditError('the pedit binary has another owner');
|
|
71
|
+
}
|
|
72
|
+
if (st.mode & 0o022)
|
|
73
|
+
throw new NoPeditError('the pedit binary is writable by others');
|
|
74
|
+
}
|
|
75
|
+
catch (e) {
|
|
76
|
+
if (e instanceof NoPeditError)
|
|
77
|
+
throw e;
|
|
78
|
+
throw new NoPeditError('the pedit binary cannot be used');
|
|
79
|
+
}
|
|
80
|
+
const version = await new Promise((resolve, reject) => {
|
|
81
|
+
execFile(bin, ['--version'], { env: peditEnv(), timeout: 5000, cwd: '/' }, (err, stdout) => err ? reject(new NoPeditError('pedit --version failed')) : resolve(stdout.trim()));
|
|
82
|
+
});
|
|
83
|
+
if (!peditVersionOk(version)) {
|
|
84
|
+
throw new NoPeditError(`pedit ${version} is too old: @pedit/serve needs ${MIN_PEDIT_VERSION} or later; ${UPDATE_HINT}`);
|
|
85
|
+
}
|
|
86
|
+
return { bin, version };
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=resolve.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve.js","sourceRoot":"","sources":["../src/resolve.ts"],"names":[],"mappings":"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"}
|
package/dist/served.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type Bridge } from './bridge.ts';
|
|
2
|
+
import type { Role, ServeEvent } from './types.ts';
|
|
3
|
+
export interface ServedFilesOptions {
|
|
4
|
+
bin: string;
|
|
5
|
+
/** Shown by another pedit's refusal. */
|
|
6
|
+
app: string;
|
|
7
|
+
/** Stop the child this long after the last client left. Default 60 s. */
|
|
8
|
+
idleMs?: number;
|
|
9
|
+
/** This layer knows paths, not IDs. */
|
|
10
|
+
onEvent?: (event: ServeEvent) => void;
|
|
11
|
+
}
|
|
12
|
+
export interface ServedFile {
|
|
13
|
+
readonly path: string;
|
|
14
|
+
/** Names the document: the same after a restart that restored it, new when it started afresh. */
|
|
15
|
+
readonly generation: string;
|
|
16
|
+
/** One connection to the child's socket; at most 8 per file (LimitError). */
|
|
17
|
+
dial(role: Role): Promise<Bridge>;
|
|
18
|
+
}
|
|
19
|
+
export declare class ServedFiles {
|
|
20
|
+
#private;
|
|
21
|
+
constructor(options: ServedFilesOptions);
|
|
22
|
+
/**
|
|
23
|
+
* Start or reuse the child serving `path` (absolute). Keyed by realpath and dev:ino, so a hard
|
|
24
|
+
* link or a case variant of one file never gets two children.
|
|
25
|
+
*/
|
|
26
|
+
acquire(path: string): Promise<ServedFile>;
|
|
27
|
+
/** End every child: close stdin, wait, SIGTERM after 5 s. Never SIGKILL inside the write delay. */
|
|
28
|
+
close(): Promise<void>;
|
|
29
|
+
}
|