@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/dist/served.js ADDED
@@ -0,0 +1,251 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { realpath, stat } from 'node:fs/promises';
3
+ import { tmpdir } from 'node:os';
4
+ import { basename, dirname, isAbsolute } from 'node:path';
5
+ import { dialSocket } from "./bridge.js";
6
+ import { HeldError, LimitError, NoPeditError } from "./errors.js";
7
+ import { peditEnv } from "./resolve.js";
8
+ const MAX_FILES = 32;
9
+ const MAX_CLIENTS = 8;
10
+ const MAX_LINE = 4096;
11
+ const STDERR_KEEP = 1024;
12
+ const TERM_AFTER_MS = 5000;
13
+ async function inodeOf(real) {
14
+ const st = await stat(real);
15
+ if (!st.isFile())
16
+ throw new Error('not a regular file');
17
+ return `${st.dev}:${st.ino}`;
18
+ }
19
+ export class ServedFiles {
20
+ #options;
21
+ #byPath = new Map();
22
+ #byIno = new Map();
23
+ #closed = false;
24
+ constructor(options) {
25
+ if (process.platform === 'win32')
26
+ throw new Error('@pedit/serve does not support Windows');
27
+ if (!isAbsolute(options.bin))
28
+ throw new TypeError('bin must be an absolute path');
29
+ if (!options.app)
30
+ throw new TypeError('app is required');
31
+ this.#options = options;
32
+ }
33
+ /**
34
+ * Start or reuse the child serving `path` (absolute). Keyed by realpath and dev:ino, so a hard
35
+ * link or a case variant of one file never gets two children.
36
+ */
37
+ async acquire(path) {
38
+ if (!isAbsolute(path))
39
+ throw new TypeError('path must be absolute');
40
+ for (;;) {
41
+ if (this.#closed)
42
+ throw new Error('ServedFiles is closed');
43
+ const real = await realpath(path);
44
+ const ino = await inodeOf(real);
45
+ // An inode alias counts only while the other path still has that inode: an atomic save
46
+ // gives the served path a new one and splits a hard link into two files.
47
+ const alias = this.#byIno.get(ino);
48
+ const aliasHolds = alias !== undefined &&
49
+ alias.path !== real &&
50
+ (await inodeOf(alias.path).catch(() => '')) === ino;
51
+ if (this.#closed)
52
+ throw new Error('ServedFiles is closed');
53
+ let entry = this.#byPath.get(real) ?? (aliasHolds ? alias : undefined);
54
+ if (entry && entry.ino !== ino && entry.path === real)
55
+ this.#reindex(entry, ino);
56
+ if (entry?.stopping) {
57
+ await entry.exited;
58
+ continue;
59
+ }
60
+ if (!entry) {
61
+ if (this.#byPath.size >= MAX_FILES)
62
+ throw new LimitError('too many served files');
63
+ entry = this.#start(real, ino);
64
+ }
65
+ await entry.ready;
66
+ return entry.handle;
67
+ }
68
+ }
69
+ #reindex(entry, ino) {
70
+ if (this.#byIno.get(entry.ino) === entry)
71
+ this.#byIno.delete(entry.ino);
72
+ entry.ino = ino;
73
+ this.#byIno.set(ino, entry);
74
+ }
75
+ /** End every child: close stdin, wait, SIGTERM after 5 s. Never SIGKILL inside the write delay. */
76
+ async close() {
77
+ this.#closed = true;
78
+ const entries = [...this.#byPath.values()];
79
+ for (const e of entries)
80
+ this.#stop(e, 'shutdown');
81
+ await Promise.all(entries.map((e) => e.exited));
82
+ }
83
+ #emit(event) {
84
+ try {
85
+ this.#options.onEvent?.(event);
86
+ }
87
+ catch { }
88
+ }
89
+ #start(path, ino) {
90
+ const child = spawn(this.#options.bin, [
91
+ 'serve',
92
+ '--root',
93
+ dirname(path),
94
+ '--json',
95
+ '--app',
96
+ this.#options.app,
97
+ '--',
98
+ basename(path),
99
+ ], { env: peditEnv(), cwd: tmpdir(), stdio: ['pipe', 'pipe', 'pipe'], shell: false });
100
+ let onExit;
101
+ const exited = new Promise((r) => {
102
+ onExit = r;
103
+ });
104
+ let onReady;
105
+ let onFail;
106
+ const ready = new Promise((res, rej) => {
107
+ onReady = res;
108
+ onFail = rej;
109
+ });
110
+ ready.catch(() => { }); // a rejection nobody awaits is not an unhandled one
111
+ const entry = {
112
+ path,
113
+ ino,
114
+ child,
115
+ ready,
116
+ socket: undefined,
117
+ generation: '',
118
+ clients: 0,
119
+ idle: undefined,
120
+ stopping: undefined,
121
+ exited,
122
+ dead: false,
123
+ handle: {
124
+ path,
125
+ get generation() {
126
+ return entry.generation;
127
+ },
128
+ dial: (role) => this.#dial(entry, role),
129
+ },
130
+ };
131
+ this.#byPath.set(path, entry);
132
+ this.#byIno.set(ino, entry);
133
+ let held;
134
+ let errorMessage;
135
+ let missing = false;
136
+ let stderr = '';
137
+ let buf = '';
138
+ child.stdin.on('error', () => { });
139
+ child.stderr.setEncoding('utf8');
140
+ child.stderr.on('data', (d) => {
141
+ if (stderr.length < STDERR_KEEP)
142
+ stderr = (stderr + d).slice(0, STDERR_KEEP);
143
+ });
144
+ const onLine = (line) => {
145
+ let ev;
146
+ try {
147
+ ev = JSON.parse(line);
148
+ }
149
+ catch {
150
+ return;
151
+ }
152
+ if (ev.event === 'listening' && typeof ev.socket === 'string' && !entry.socket) {
153
+ if (typeof ev.generation !== 'string' || ev.generation === '') {
154
+ errorMessage = 'pedit did not report a generation; it is too old';
155
+ return child.kill('SIGTERM');
156
+ }
157
+ entry.socket = ev.socket;
158
+ entry.generation = ev.generation;
159
+ this.#emit({ type: 'started', path });
160
+ this.#armIdle(entry);
161
+ onReady(ev.socket);
162
+ }
163
+ else if (ev.event === 'held') {
164
+ held = { pid: Number(ev.pid), ...(typeof ev.app === 'string' ? { app: ev.app } : {}) };
165
+ }
166
+ else if (ev.event === 'error' && typeof ev.message === 'string') {
167
+ errorMessage = ev.message.slice(0, STDERR_KEEP);
168
+ }
169
+ };
170
+ child.stdout.setEncoding('utf8');
171
+ child.stdout.on('data', (d) => {
172
+ buf += d;
173
+ for (let i = buf.indexOf('\n'); i >= 0; i = buf.indexOf('\n')) {
174
+ const line = buf.slice(0, i);
175
+ buf = buf.slice(i + 1);
176
+ if (line.length > MAX_LINE)
177
+ return child.kill('SIGTERM');
178
+ onLine(line);
179
+ }
180
+ if (buf.length > MAX_LINE)
181
+ child.kill('SIGTERM');
182
+ });
183
+ child.on('error', (e) => {
184
+ if (e.code === 'ENOENT')
185
+ missing = true;
186
+ else
187
+ errorMessage = e.message;
188
+ });
189
+ child.on('close', (code) => {
190
+ entry.dead = true;
191
+ clearTimeout(entry.idle);
192
+ if (this.#byPath.get(path) === entry)
193
+ this.#byPath.delete(path);
194
+ if (this.#byIno.get(entry.ino) === entry)
195
+ this.#byIno.delete(entry.ino);
196
+ if (!entry.socket) {
197
+ if (missing)
198
+ onFail(new NoPeditError('the pedit binary cannot be run'));
199
+ else if (code === 3 && held)
200
+ onFail(new HeldError(held));
201
+ else
202
+ onFail(new Error(errorMessage ?? (stderr.trim() || `pedit exited with code ${code}`)));
203
+ if (!held)
204
+ this.#emit({ type: 'error', path, message: String(errorMessage ?? stderr) });
205
+ }
206
+ else {
207
+ if (stderr.trim() && code !== 0)
208
+ this.#emit({ type: 'error', path, message: stderr });
209
+ this.#emit({ type: 'stopped', path, reason: entry.stopping ?? 'exited' });
210
+ }
211
+ onExit();
212
+ });
213
+ return entry;
214
+ }
215
+ #armIdle(entry) {
216
+ clearTimeout(entry.idle);
217
+ if (entry.clients > 0 || entry.dead || entry.stopping)
218
+ return;
219
+ entry.idle = setTimeout(() => this.#stop(entry, 'idle'), this.#options.idleMs ?? 60_000);
220
+ entry.idle.unref();
221
+ }
222
+ #stop(entry, reason) {
223
+ if (entry.stopping || entry.dead)
224
+ return;
225
+ entry.stopping = reason;
226
+ clearTimeout(entry.idle);
227
+ entry.child.stdin?.end();
228
+ // Saving takes a write delay: ask nicely first, and never SIGKILL.
229
+ setTimeout(() => entry.dead || entry.child.kill('SIGTERM'), TERM_AFTER_MS).unref();
230
+ }
231
+ async #dial(entry, role) {
232
+ if (entry.dead || entry.stopping || !entry.socket)
233
+ throw new Error('the file is no longer served');
234
+ if (entry.clients >= MAX_CLIENTS)
235
+ throw new LimitError('too many clients for this file');
236
+ entry.clients++;
237
+ clearTimeout(entry.idle);
238
+ const release = () => {
239
+ entry.clients--;
240
+ this.#armIdle(entry);
241
+ };
242
+ try {
243
+ return await dialSocket(entry.socket, role, release);
244
+ }
245
+ catch (e) {
246
+ release();
247
+ throw e;
248
+ }
249
+ }
250
+ }
251
+ //# sourceMappingURL=served.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"served.js","sourceRoot":"","sources":["../src/served.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ /** `read` sends `X-Pedit-Role: read`, which pedit enforces. */
2
+ export type Role = 'edit' | 'read';
3
+ export type ServeEvent = {
4
+ type: 'started';
5
+ path: string;
6
+ } | {
7
+ type: 'stopped';
8
+ path: string;
9
+ reason: 'idle' | 'exited' | 'shutdown';
10
+ } | {
11
+ type: 'error';
12
+ path: string | null;
13
+ message: string;
14
+ };
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/package.json CHANGED
@@ -1,6 +1,88 @@
1
1
  {
2
2
  "name": "@pedit/serve",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.0",
4
+ "description": "Run pedit serve for a local app: the file's Yjs document over a Unix socket, with Hono routes and a browser client",
5
+ "keywords": [
6
+ "pedit",
7
+ "yjs",
8
+ "crdt",
9
+ "collaborative-editing",
10
+ "hono",
11
+ "local-first"
12
+ ],
13
+ "license": "MIT",
14
+ "homepage": "https://github.com/piconic-ai/pedit/tree/main/packages/serve#readme",
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "git+https://github.com/piconic-ai/pedit.git",
18
+ "directory": "packages/serve"
19
+ },
20
+ "type": "module",
21
+ "sideEffects": false,
22
+ "engines": {
23
+ "node": ">=22"
24
+ },
25
+ "files": [
26
+ "dist",
27
+ "README.md",
28
+ "LICENSE"
29
+ ],
30
+ "publishConfig": {
31
+ "access": "public",
32
+ "provenance": true
33
+ },
34
+ "scripts": {
35
+ "build": "rm -rf dist && tsc -p tsconfig.build.json",
36
+ "prepack": "cp ../../LICENSE LICENSE",
37
+ "postpack": "rm -f LICENSE",
38
+ "typecheck": "tsc -p .",
39
+ "test": "vitest run --passWithNoTests"
40
+ },
41
+ "dependencies": {
42
+ "ws": "^8.18.3"
43
+ },
44
+ "peerDependencies": {
45
+ "hono": "^4.9.0",
46
+ "y-protocols": "^1.0.7",
47
+ "y-websocket": "^3.0.0",
48
+ "yjs": "^13.6.33"
49
+ },
50
+ "peerDependenciesMeta": {
51
+ "hono": {
52
+ "optional": true
53
+ },
54
+ "y-protocols": {
55
+ "optional": true
56
+ },
57
+ "y-websocket": {
58
+ "optional": true
59
+ },
60
+ "yjs": {
61
+ "optional": true
62
+ }
63
+ },
64
+ "devDependencies": {
65
+ "@hono/node-server": "^1.19.5",
66
+ "@hono/node-ws": "^1.2.0",
67
+ "@types/node": "^22.0.0",
68
+ "@types/ws": "^8.18.1",
69
+ "hono": "^4.9.0",
70
+ "y-protocols": "^1.0.7",
71
+ "y-websocket": "^3.0.0",
72
+ "yjs": "^13.6.33"
73
+ },
74
+ "exports": {
75
+ ".": {
76
+ "types": "./dist/index.d.ts",
77
+ "import": "./dist/index.js"
78
+ },
79
+ "./hono": {
80
+ "types": "./dist/hono.d.ts",
81
+ "import": "./dist/hono.js"
82
+ },
83
+ "./client": {
84
+ "types": "./dist/client.d.ts",
85
+ "import": "./dist/client.js"
86
+ }
87
+ }
88
+ }