@zaclinley/dsh-mailharbor 0.7.1 → 0.8.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/lib/index.js CHANGED
@@ -225,6 +225,7 @@ var init_imap = __esm({
225
225
  mailboxCache = null;
226
226
  countCache = /* @__PURE__ */ new Map();
227
227
  pageCache = /* @__PURE__ */ new Map();
228
+ readCache = /* @__PURE__ */ new Map();
228
229
  keepalive = null;
229
230
  static MAILBOX_TTL_MS = 6e4;
230
231
  constructor(settings, credentials, options = {}) {
@@ -426,6 +427,9 @@ var init_imap = __esm({
426
427
  });
427
428
  }
428
429
  async read(mailbox, uid) {
430
+ const cacheKey = `${mailbox}:${String(uid)}`;
431
+ const cached = this.readCache.get(cacheKey);
432
+ if (cached !== void 0 && Date.now() - cached.at < 3e5) return cached.message;
429
433
  return this.run(async (client) => {
430
434
  const lock = await client.getMailboxLock(mailbox);
431
435
  try {
@@ -440,7 +444,7 @@ var init_imap = __esm({
440
444
  const parsed = await simpleParser2(message.source);
441
445
  const base = toMessage(message);
442
446
  const html = typeof parsed.html === "string" ? parsed.html : Buffer.isBuffer(parsed.html) ? parsed.html.toString("utf8") : null;
443
- return {
447
+ const detail = {
444
448
  ...base,
445
449
  text: typeof parsed.text === "string" ? parsed.text : "",
446
450
  html: html === "" ? null : html,
@@ -448,6 +452,12 @@ var init_imap = __esm({
448
452
  inReplyTo: normalizeId2(parsed.inReplyTo),
449
453
  references: String(parsed.references ?? "").split(/\s+/).map(normalizeId2).filter((item) => item !== "")
450
454
  };
455
+ this.readCache.set(cacheKey, { at: Date.now(), message: detail });
456
+ if (this.readCache.size > 40) {
457
+ const oldest = this.readCache.keys().next().value;
458
+ if (oldest !== void 0) this.readCache.delete(oldest);
459
+ }
460
+ return detail;
451
461
  } finally {
452
462
  lock.release();
453
463
  }
@@ -537,6 +547,7 @@ var init_imap = __esm({
537
547
  async dispose() {
538
548
  this.disposed = true;
539
549
  this.pageCache.clear();
550
+ this.readCache.clear();
540
551
  this.stopKeepalive();
541
552
  await this.reset("dispose");
542
553
  }
@@ -878,6 +889,27 @@ async function pickDirectory(defaultPath) {
878
889
  throw new Error("\u5F53\u524D\u73AF\u5883\u65E0\u6CD5\u6253\u5F00\u76EE\u5F55\u9009\u62E9\u5668\uFF0C\u8BF7\u624B\u52A8\u8F93\u5165\u8DEF\u5F84");
879
890
  }
880
891
  }
892
+ async function openPath(path) {
893
+ try {
894
+ const require2 = createRequire(import.meta.url);
895
+ const electron = require2("electron");
896
+ if (electron.shell !== void 0) {
897
+ const error = await electron.shell.openPath(path);
898
+ if (error !== "") throw new Error(error);
899
+ return;
900
+ }
901
+ } catch {
902
+ }
903
+ if (process.platform === "darwin") {
904
+ await run("open", [path]);
905
+ return;
906
+ }
907
+ if (process.platform === "win32") {
908
+ await run("explorer", [path]);
909
+ return;
910
+ }
911
+ await run("xdg-open", [path]);
912
+ }
881
913
 
882
914
  // src/host/backup.ts
883
915
  init_address();
@@ -1760,7 +1792,7 @@ var SEARCH_SYSTEM = `\u4F60\u662F\u90AE\u4EF6\u641C\u7D22\u52A9\u624B\u3002\u628
1760
1792
  var TASK_SYSTEM = `\u4F60\u662F\u4EFB\u52A1\u63D0\u53D6\u52A9\u624B\u3002\u4ECE\u90AE\u4EF6\u4E2D\u63D0\u53D6\u9700\u8981\u7528\u6237\u5904\u7406\u7684\u5177\u4F53\u4E8B\u9879\u3002
1761
1793
 
1762
1794
  \u53EA\u8F93\u51FA\u4E00\u4E2A JSON \u6570\u7EC4\uFF0C\u4E0D\u8981\u8F93\u51FA\u89E3\u91CA\u3001Markdown \u6216\u4EE3\u7801\u5757\u6807\u8BB0\u3002\u6BCF\u4E2A\u5143\u7D20\uFF1A
1763
- {"title": "\u4EFB\u52A1\u6807\u9898\uFF08\u52A8\u8BCD\u5F00\u5934\uFF0C\u5C3D\u91CF\u5177\u4F53\uFF09", "due": "YYYY-MM-DD \u6216\u7A7A\u5B57\u7B26\u4E32", "priority": 0 \u5230 9 \u7684\u6574\u6570, "owner": "\u8D1F\u8D23\u4EBA\u6216\u7A7A\u5B57\u7B26\u4E32", "notes": "\u4E00\u53E5\u6765\u6E90\u4F9D\u636E"}
1795
+ {"title": "\u4EFB\u52A1\u6807\u9898\uFF08\u52A8\u8BCD\u5F00\u5934\uFF0C\u5C3D\u91CF\u5177\u4F53\uFF09", "due": "YYYY-MM-DD \u6216\u7A7A\u5B57\u7B26\u4E32", "priority": 0 \u5230 9 \u7684\u6574\u6570, "owner": "\u8FD9\u4E2A\u4E8B\u8BE5\u8054\u7CFB\u8C01\uFF08\u90AE\u4EF6\u91CC\u7684\u8054\u7CFB\u4EBA\uFF09\u6216\u7A7A\u5B57\u7B26\u4E32", "notes": "\u4E00\u53E5\u6765\u6E90\u4F9D\u636E"}
1764
1796
 
1765
1797
  \u89C4\u5219\uFF1A
1766
1798
  - \u53EA\u63D0\u53D6\u90AE\u4EF6\u91CC\u660E\u786E\u8981\u6C42\u3001\u6216\u660E\u663E\u9700\u8981\u8DDF\u8FDB\u7684\u4E8B\u9879
@@ -2171,6 +2203,14 @@ var ProjectStore = class {
2171
2203
  this.flush();
2172
2204
  return project;
2173
2205
  }
2206
+ remove(id) {
2207
+ const data = this.load();
2208
+ const index = data.projects.findIndex((project) => project.id === id);
2209
+ if (index < 0) return false;
2210
+ data.projects.splice(index, 1);
2211
+ this.flush();
2212
+ return true;
2213
+ }
2174
2214
  addThread(id, thread) {
2175
2215
  const project = this.get(id);
2176
2216
  if (project === void 0) return void 0;
@@ -2309,7 +2349,7 @@ ${conversation.trim()}
2309
2349
  }
2310
2350
 
2311
2351
  // src/host/version.ts
2312
- var VERSION = "0.7.1";
2352
+ var VERSION = "0.8.0";
2313
2353
 
2314
2354
  // src/host/api.ts
2315
2355
  init_smtp();
@@ -2958,6 +2998,37 @@ function buildRouter(runtime) {
2958
2998
  });
2959
2999
  return { project: projectView(result.project), warnings: result.warnings };
2960
3000
  });
3001
+ router.patch("/projects/:id", async ({ params, body }) => {
3002
+ const payload = await body();
3003
+ const name2 = String(payload.name ?? "").trim();
3004
+ if (name2 === "") throw new HttpError(400, "\u9879\u76EE\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A");
3005
+ const project = runtime.projects().update(String(params.id ?? ""), { name: name2 });
3006
+ if (project === void 0) throw new HttpError(404, "\u9879\u76EE\u4E0D\u5B58\u5728");
3007
+ return { project: projectView(project) };
3008
+ });
3009
+ router.delete("/projects/:id", ({ params }) => {
3010
+ const id = String(params.id ?? "");
3011
+ const project = runtime.projects().get(id);
3012
+ if (project === void 0) throw new HttpError(404, "\u9879\u76EE\u4E0D\u5B58\u5728");
3013
+ const removed = runtime.projects().remove(id);
3014
+ const unlinked = runtime.tasks().clearProject(id);
3015
+ return {
3016
+ ok: removed,
3017
+ path: project.path,
3018
+ unlinkedTasks: unlinked,
3019
+ workspaceKept: project.workspaceId !== ""
3020
+ };
3021
+ });
3022
+ router.post("/projects/:id/open", async ({ params }) => {
3023
+ const project = runtime.projects().get(String(params.id ?? ""));
3024
+ if (project === void 0) throw new HttpError(404, "\u9879\u76EE\u4E0D\u5B58\u5728");
3025
+ try {
3026
+ await openPath(project.path);
3027
+ return { path: project.path };
3028
+ } catch (error) {
3029
+ throw new HttpError(400, error instanceof Error ? error.message : String(error));
3030
+ }
3031
+ });
2961
3032
  router.post("/projects/:id/messages", async ({ params, body }) => {
2962
3033
  const payload = await body();
2963
3034
  const account = accountFromPayload(runtime, payload);
@@ -3854,6 +3925,11 @@ var TaskStore = class {
3854
3925
  if (task === void 0) throw new Error("\u4EFB\u52A1\u66F4\u65B0\u5931\u8D25");
3855
3926
  return task;
3856
3927
  }
3928
+ /** Unlink tasks when their project is deleted. */
3929
+ clearProject(projectId) {
3930
+ const result = this.open().prepare("UPDATE tasks SET project_id = NULL, dirty = 1, updated_at = ? WHERE project_id = ?").run(now(), projectId);
3931
+ return Number(result.changes);
3932
+ }
3857
3933
  remove(id) {
3858
3934
  const current = this.get(id);
3859
3935
  if (current === void 0) return false;