tandem-editor 0.2.9 → 0.2.10
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/CHANGELOG.md +16 -0
- package/dist/cli/index.js +1 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/client/assets/index-CfGlbY9B.js +297 -0
- package/dist/client/index.html +1 -1
- package/dist/server/index.js +27 -22
- package/dist/server/index.js.map +1 -1
- package/package.json +122 -122
- package/dist/client/assets/index-ChvL-huP.js +0 -297
package/dist/client/index.html
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
html, body, #root { height: 100%; }
|
|
10
10
|
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
|
|
11
11
|
</style>
|
|
12
|
-
<script type="module" crossorigin src="/assets/index-
|
|
12
|
+
<script type="module" crossorigin src="/assets/index-CfGlbY9B.js"></script>
|
|
13
13
|
</head>
|
|
14
14
|
<body>
|
|
15
15
|
<div id="root"></div>
|
package/dist/server/index.js
CHANGED
|
@@ -228,6 +228,25 @@ var init_platform = __esm({
|
|
|
228
228
|
import fs from "fs/promises";
|
|
229
229
|
import path2 from "path";
|
|
230
230
|
import * as Y2 from "yjs";
|
|
231
|
+
async function atomicWrite(sessionPath, content) {
|
|
232
|
+
const tmpPath = `${sessionPath}.tmp`;
|
|
233
|
+
await fs.writeFile(tmpPath, content, "utf-8");
|
|
234
|
+
for (let attempt = 0; attempt < RENAME_MAX_RETRIES; attempt++) {
|
|
235
|
+
try {
|
|
236
|
+
await fs.rename(tmpPath, sessionPath);
|
|
237
|
+
return;
|
|
238
|
+
} catch (err) {
|
|
239
|
+
const code = err.code;
|
|
240
|
+
if ((code === "EPERM" || code === "EACCES") && attempt < RENAME_MAX_RETRIES - 1) {
|
|
241
|
+
await new Promise((r) => setTimeout(r, RENAME_RETRY_BASE_MS * 2 ** attempt));
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
await fs.unlink(tmpPath).catch(() => {
|
|
245
|
+
});
|
|
246
|
+
throw err;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
231
250
|
function sessionKey(filePath) {
|
|
232
251
|
return encodeURIComponent(filePath.replace(/\\/g, "/"));
|
|
233
252
|
}
|
|
@@ -255,15 +274,7 @@ async function saveSession(filePath, format, doc) {
|
|
|
255
274
|
sessionDirReady = true;
|
|
256
275
|
}
|
|
257
276
|
const sessionPath = path2.join(SESSION_DIR, `${key}.json`);
|
|
258
|
-
|
|
259
|
-
await fs.writeFile(tmpPath, JSON.stringify(data), "utf-8");
|
|
260
|
-
try {
|
|
261
|
-
await fs.rename(tmpPath, sessionPath);
|
|
262
|
-
} catch (err) {
|
|
263
|
-
await fs.unlink(tmpPath).catch(() => {
|
|
264
|
-
});
|
|
265
|
-
throw err;
|
|
266
|
-
}
|
|
277
|
+
await atomicWrite(sessionPath, JSON.stringify(data));
|
|
267
278
|
}
|
|
268
279
|
async function loadSession(filePath) {
|
|
269
280
|
const key = sessionKey(filePath);
|
|
@@ -334,15 +345,7 @@ async function saveCtrlSession(doc) {
|
|
|
334
345
|
const ydocState = Buffer.from(state).toString("base64");
|
|
335
346
|
const data = { ydocState, lastAccessed: Date.now() };
|
|
336
347
|
const sessionPath = path2.join(SESSION_DIR, `${CTRL_SESSION_KEY}.json`);
|
|
337
|
-
|
|
338
|
-
await fs.writeFile(tmpPath, JSON.stringify(data), "utf-8");
|
|
339
|
-
try {
|
|
340
|
-
await fs.rename(tmpPath, sessionPath);
|
|
341
|
-
} catch (err) {
|
|
342
|
-
await fs.unlink(tmpPath).catch(() => {
|
|
343
|
-
});
|
|
344
|
-
throw err;
|
|
345
|
-
}
|
|
348
|
+
await atomicWrite(sessionPath, JSON.stringify(data));
|
|
346
349
|
}
|
|
347
350
|
async function loadCtrlSession() {
|
|
348
351
|
const sessionPath = path2.join(SESSION_DIR, `${CTRL_SESSION_KEY}.json`);
|
|
@@ -441,7 +444,7 @@ function stopAutoSave() {
|
|
|
441
444
|
}
|
|
442
445
|
autoSaveCallback = null;
|
|
443
446
|
}
|
|
444
|
-
var AUTO_SAVE_INTERVAL, sessionDirReady, CTRL_SESSION_KEY, autoSaveTimer, autoSaveCallback;
|
|
447
|
+
var AUTO_SAVE_INTERVAL, RENAME_MAX_RETRIES, RENAME_RETRY_BASE_MS, sessionDirReady, CTRL_SESSION_KEY, autoSaveTimer, autoSaveCallback;
|
|
445
448
|
var init_manager = __esm({
|
|
446
449
|
"src/server/session/manager.ts"() {
|
|
447
450
|
"use strict";
|
|
@@ -449,6 +452,8 @@ var init_manager = __esm({
|
|
|
449
452
|
init_constants();
|
|
450
453
|
init_queue();
|
|
451
454
|
AUTO_SAVE_INTERVAL = 60 * 1e3;
|
|
455
|
+
RENAME_MAX_RETRIES = 3;
|
|
456
|
+
RENAME_RETRY_BASE_MS = 50;
|
|
452
457
|
sessionDirReady = false;
|
|
453
458
|
CTRL_SESSION_KEY = CTRL_ROOM;
|
|
454
459
|
autoSaveTimer = null;
|
|
@@ -2681,7 +2686,7 @@ import path4 from "path";
|
|
|
2681
2686
|
function getAdapter(format) {
|
|
2682
2687
|
return adapters[format] ?? plaintextAdapter;
|
|
2683
2688
|
}
|
|
2684
|
-
async function
|
|
2689
|
+
async function atomicWrite2(filePath, content) {
|
|
2685
2690
|
const tempPath = path4.join(path4.dirname(filePath), `.tandem-tmp-${Date.now()}`);
|
|
2686
2691
|
await fs2.writeFile(tempPath, content, "utf-8");
|
|
2687
2692
|
await fs2.rename(tempPath, filePath);
|
|
@@ -3772,7 +3777,7 @@ async function convertToMarkdown(documentId, outputPath) {
|
|
|
3772
3777
|
resolvedOutput = path7.join(sourceDir, `${baseName}.md`);
|
|
3773
3778
|
}
|
|
3774
3779
|
resolvedOutput = await findAvailablePath(resolvedOutput);
|
|
3775
|
-
await
|
|
3780
|
+
await atomicWrite2(resolvedOutput, markdown);
|
|
3776
3781
|
try {
|
|
3777
3782
|
const openResult = await openFileByPath(resolvedOutput);
|
|
3778
3783
|
return {
|
|
@@ -4065,7 +4070,7 @@ function registerDocumentTools(server) {
|
|
|
4065
4070
|
});
|
|
4066
4071
|
}
|
|
4067
4072
|
const output = adapter.save(r.doc);
|
|
4068
|
-
await
|
|
4073
|
+
await atomicWrite2(r.filePath, output);
|
|
4069
4074
|
await saveSession(r.filePath, format, r.doc);
|
|
4070
4075
|
const meta = r.doc.getMap(Y_MAP_DOCUMENT_META);
|
|
4071
4076
|
r.doc.transact(() => meta.set(Y_MAP_SAVED_AT_VERSION, Date.now()), MCP_ORIGIN);
|