From 2711d361a749d4e936d0db71b25fd5df30327895 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Sun, 4 Jan 2026 04:52:11 +0000 Subject: [PATCH 1/5] feat(desktop): store humans as markdown files with YAML frontmatter - Change human persister to store each human as humans/.md - Store structure info (user_id, created_at, name, email, etc.) as YAML frontmatter - Store memo field as markdown body content - Add automatic migration from humans.json to new format - Add cleanup of orphan human files when humans are deleted - Add yaml dependency for frontmatter parsing - Add 'text' operation type to persister utils for raw text file writes - Update tests for new markdown file format Co-Authored-By: yujonglee --- apps/desktop/package.json | 9 +- .../store/tinybase/persister/human/collect.ts | 59 +++ .../store/tinybase/persister/human/load.ts | 99 +++++ .../store/tinybase/persister/human/migrate.ts | 73 ++++ .../persister/human/persister.test.ts | 234 +++++++++-- .../tinybase/persister/human/persister.ts | 33 +- .../store/tinybase/persister/human/utils.ts | 43 ++ .../src/store/tinybase/persister/utils.ts | 17 +- pnpm-lock.yaml | 395 ++++-------------- 9 files changed, 602 insertions(+), 360 deletions(-) create mode 100644 apps/desktop/src/store/tinybase/persister/human/collect.ts create mode 100644 apps/desktop/src/store/tinybase/persister/human/load.ts create mode 100644 apps/desktop/src/store/tinybase/persister/human/migrate.ts create mode 100644 apps/desktop/src/store/tinybase/persister/human/utils.ts diff --git a/apps/desktop/package.json b/apps/desktop/package.json index 65deaedeea..c90b8c5a59 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -29,19 +29,20 @@ "@hypr/api-client": "workspace:*", "@hypr/codemirror": "workspace:^", "@hypr/plugin-analytics": "workspace:*", - "@hypr/plugin-audio-priority": "workspace:*", "@hypr/plugin-apple-calendar": "workspace:*", + "@hypr/plugin-audio-priority": "workspace:*", "@hypr/plugin-auth": "workspace:*", "@hypr/plugin-cli2": "workspace:*", "@hypr/plugin-db2": "workspace:*", "@hypr/plugin-deeplink2": "workspace:*", "@hypr/plugin-detect": "workspace:*", "@hypr/plugin-export": "workspace:*", - "@hypr/plugin-folder": "workspace:*", "@hypr/plugin-extensions": "workspace:*", + "@hypr/plugin-folder": "workspace:*", "@hypr/plugin-hooks": "workspace:*", "@hypr/plugin-icon": "workspace:*", "@hypr/plugin-importer": "workspace:*", + "@hypr/plugin-js": "workspace:*", "@hypr/plugin-listener": "workspace:*", "@hypr/plugin-listener2": "workspace:*", "@hypr/plugin-local-stt": "workspace:*", @@ -51,15 +52,14 @@ "@hypr/plugin-notify": "workspace:*", "@hypr/plugin-overlay": "workspace:*", "@hypr/plugin-path2": "workspace:*", - "@hypr/plugin-tantivy": "workspace:*", "@hypr/plugin-pdf": "workspace:*", "@hypr/plugin-permissions": "workspace:*", "@hypr/plugin-settings": "workspace:*", "@hypr/plugin-sfx": "workspace:*", + "@hypr/plugin-tantivy": "workspace:*", "@hypr/plugin-template": "workspace:*", "@hypr/plugin-updater2": "workspace:*", "@hypr/plugin-windows": "workspace:*", - "@hypr/plugin-js": "workspace:*", "@hypr/store": "workspace:*", "@hypr/tiptap": "workspace:^", "@hypr/ui": "workspace:^", @@ -125,6 +125,7 @@ "vfile": "^6.0.3", "wavesurfer.js": "^7.12.1", "xstate": "^5.25.0", + "yaml": "^2.8.2", "zod": "^4.2.1", "zustand": "^5.0.9" }, diff --git a/apps/desktop/src/store/tinybase/persister/human/collect.ts b/apps/desktop/src/store/tinybase/persister/human/collect.ts new file mode 100644 index 0000000000..4a5761a0ec --- /dev/null +++ b/apps/desktop/src/store/tinybase/persister/human/collect.ts @@ -0,0 +1,59 @@ +import type { MergeableStore, OptionalSchemas } from "tinybase/with-schemas"; + +import type { HumanStorage } from "@hypr/store"; + +import type { CollectorResult, TablesContent } from "../utils"; +import { + getHumanDir, + getHumanFilePath, + serializeMarkdownWithFrontmatter, +} from "./utils"; + +export interface HumanCollectorResult extends CollectorResult { + validHumanIds: Set; +} + +type HumansTable = Record; + +export function collectHumanWriteOps( + _store: MergeableStore, + tables: TablesContent, + dataDir: string, +): HumanCollectorResult { + const dirs = new Set(); + const operations: CollectorResult["operations"] = []; + const validHumanIds = new Set(); + + const humansDir = getHumanDir(dataDir); + dirs.add(humansDir); + + const humans = (tables as { humans?: HumansTable }).humans ?? {}; + + for (const [humanId, human] of Object.entries(humans)) { + validHumanIds.add(humanId); + + const { memo, ...frontmatterFields } = human; + + const frontmatter: Record = { + user_id: frontmatterFields.user_id ?? "", + created_at: frontmatterFields.created_at ?? "", + name: frontmatterFields.name ?? "", + email: frontmatterFields.email ?? "", + org_id: frontmatterFields.org_id ?? "", + job_title: frontmatterFields.job_title ?? "", + linkedin_username: frontmatterFields.linkedin_username ?? "", + }; + + const body = memo ?? ""; + const content = serializeMarkdownWithFrontmatter(frontmatter, body); + const filePath = getHumanFilePath(dataDir, humanId); + + operations.push({ + type: "text", + path: filePath, + content, + }); + } + + return { dirs, operations, validHumanIds }; +} diff --git a/apps/desktop/src/store/tinybase/persister/human/load.ts b/apps/desktop/src/store/tinybase/persister/human/load.ts new file mode 100644 index 0000000000..30ee7c1ae8 --- /dev/null +++ b/apps/desktop/src/store/tinybase/persister/human/load.ts @@ -0,0 +1,99 @@ +import { readDir, readTextFile, remove } from "@tauri-apps/plugin-fs"; + +import type { HumanStorage } from "@hypr/store"; + +import { isFileNotFoundError, isUUID } from "../utils"; +import { + getHumanDir, + getHumanFilePath, + parseMarkdownWithFrontmatter, +} from "./utils"; + +export async function loadAllHumans( + dataDir: string, +): Promise> { + const result: Record = {}; + const humansDir = getHumanDir(dataDir); + + let entries: { name: string; isDirectory: boolean }[]; + try { + entries = await readDir(humansDir); + } catch (error) { + if (isFileNotFoundError(error)) { + return result; + } + throw error; + } + + for (const entry of entries) { + if (entry.isDirectory) continue; + if (!entry.name.endsWith(".md")) continue; + + const humanId = entry.name.replace(/\.md$/, ""); + if (!isUUID(humanId)) { + console.warn(`[HumanPersister] Skipping non-UUID file: ${entry.name}`); + continue; + } + + try { + const filePath = getHumanFilePath(dataDir, humanId); + const content = await readTextFile(filePath); + const { frontmatter, body } = parseMarkdownWithFrontmatter(content); + + result[humanId] = { + user_id: String(frontmatter.user_id ?? ""), + created_at: String(frontmatter.created_at ?? ""), + name: String(frontmatter.name ?? ""), + email: String(frontmatter.email ?? ""), + org_id: String(frontmatter.org_id ?? ""), + job_title: String(frontmatter.job_title ?? ""), + linkedin_username: String(frontmatter.linkedin_username ?? ""), + memo: body, + }; + } catch (error) { + console.error(`[HumanPersister] Failed to load human ${humanId}:`, error); + continue; + } + } + + return result; +} + +export async function cleanupOrphanHumanFiles( + dataDir: string, + validHumanIds: Set, +): Promise { + const humansDir = getHumanDir(dataDir); + + let entries: { name: string; isDirectory: boolean }[]; + try { + entries = await readDir(humansDir); + } catch (error) { + if (isFileNotFoundError(error)) { + return; + } + throw error; + } + + for (const entry of entries) { + if (entry.isDirectory) continue; + if (!entry.name.endsWith(".md")) continue; + + const humanId = entry.name.replace(/\.md$/, ""); + if (!isUUID(humanId)) continue; + + if (!validHumanIds.has(humanId)) { + try { + const filePath = getHumanFilePath(dataDir, humanId); + await remove(filePath); + } catch (error) { + if (!isFileNotFoundError(error)) { + console.error( + `[HumanPersister] Failed to remove orphan file ${entry.name}:`, + error, + ); + } + } + } + } +} diff --git a/apps/desktop/src/store/tinybase/persister/human/migrate.ts b/apps/desktop/src/store/tinybase/persister/human/migrate.ts new file mode 100644 index 0000000000..a589ea5f19 --- /dev/null +++ b/apps/desktop/src/store/tinybase/persister/human/migrate.ts @@ -0,0 +1,73 @@ +import { sep } from "@tauri-apps/api/path"; +import { + exists, + mkdir, + readTextFile, + remove, + writeTextFile, +} from "@tauri-apps/plugin-fs"; + +import type { HumanStorage } from "@hypr/store"; + +import { isFileNotFoundError } from "../utils"; +import { + getHumanDir, + getHumanFilePath, + serializeMarkdownWithFrontmatter, +} from "./utils"; + +export async function migrateHumansJsonIfNeeded( + dataDir: string, +): Promise { + const humansJsonPath = [dataDir, "humans.json"].join(sep()); + const humansDir = getHumanDir(dataDir); + + const jsonExists = await exists(humansJsonPath); + if (!jsonExists) { + return; + } + + const dirExists = await exists(humansDir); + if (dirExists) { + return; + } + + console.log("[HumanPersister] Migrating from humans.json to humans/*.md"); + + try { + const content = await readTextFile(humansJsonPath); + const humans = JSON.parse(content) as Record; + + await mkdir(humansDir, { recursive: true }); + + for (const [humanId, human] of Object.entries(humans)) { + const { memo, ...frontmatterFields } = human; + + const frontmatter: Record = { + user_id: frontmatterFields.user_id ?? "", + created_at: frontmatterFields.created_at ?? "", + name: frontmatterFields.name ?? "", + email: frontmatterFields.email ?? "", + org_id: frontmatterFields.org_id ?? "", + job_title: frontmatterFields.job_title ?? "", + linkedin_username: frontmatterFields.linkedin_username ?? "", + }; + + const body = memo ?? ""; + const mdContent = serializeMarkdownWithFrontmatter(frontmatter, body); + const filePath = getHumanFilePath(dataDir, humanId); + + await writeTextFile(filePath, mdContent); + } + + await remove(humansJsonPath); + + console.log( + `[HumanPersister] Migration complete: ${Object.keys(humans).length} humans migrated`, + ); + } catch (error) { + if (!isFileNotFoundError(error)) { + console.error("[HumanPersister] Migration failed:", error); + } + } +} diff --git a/apps/desktop/src/store/tinybase/persister/human/persister.test.ts b/apps/desktop/src/store/tinybase/persister/human/persister.test.ts index e3e3d9c3a0..2ba24029d8 100644 --- a/apps/desktop/src/store/tinybase/persister/human/persister.test.ts +++ b/apps/desktop/src/store/tinybase/persister/human/persister.test.ts @@ -4,6 +4,10 @@ import { beforeEach, describe, expect, test, vi } from "vitest"; import { SCHEMA, type Schemas } from "@hypr/store"; import { createHumanPersister } from "./persister"; +import { + parseMarkdownWithFrontmatter, + serializeMarkdownWithFrontmatter, +} from "./utils"; vi.mock("@hypr/plugin-path2", () => ({ commands: { @@ -13,8 +17,11 @@ vi.mock("@hypr/plugin-path2", () => ({ vi.mock("@tauri-apps/plugin-fs", () => ({ mkdir: vi.fn().mockResolvedValue(undefined), + readDir: vi.fn(), readTextFile: vi.fn(), writeTextFile: vi.fn().mockResolvedValue(undefined), + exists: vi.fn(), + remove: vi.fn().mockResolvedValue(undefined), })); function createTestStore() { @@ -23,6 +30,9 @@ function createTestStore() { .setValuesSchema(SCHEMA.value); } +const HUMAN_UUID_1 = "550e8400-e29b-41d4-a716-446655440000"; +const HUMAN_UUID_2 = "550e8400-e29b-41d4-a716-446655440001"; + describe("createHumanPersister", () => { let store: ReturnType; @@ -41,11 +51,17 @@ describe("createHumanPersister", () => { }); describe("load", () => { - test("loads humans from json file", async () => { - const { readTextFile } = await import("@tauri-apps/plugin-fs"); + test("loads humans from markdown files", async () => { + const { readDir, readTextFile, exists } = + await import("@tauri-apps/plugin-fs"); + + vi.mocked(exists).mockResolvedValue(false); + vi.mocked(readDir).mockResolvedValue([ + { name: `${HUMAN_UUID_1}.md`, isDirectory: false }, + ]); - const mockData = { - "human-1": { + const mockMdContent = serializeMarkdownWithFrontmatter( + { user_id: "user-1", created_at: "2024-01-01T00:00:00Z", name: "John Doe", @@ -53,24 +69,34 @@ describe("createHumanPersister", () => { org_id: "org-1", job_title: "Engineer", linkedin_username: "johndoe", - memo: "Some notes", }, - }; - vi.mocked(readTextFile).mockResolvedValue(JSON.stringify(mockData)); + "Some notes", + ); + vi.mocked(readTextFile).mockResolvedValue(mockMdContent); const persister = createHumanPersister(store); await persister.load(); - expect(readTextFile).toHaveBeenCalledWith( - "/mock/data/dir/hyprnote/humans.json", - ); - expect(store.getTable("humans")).toEqual(mockData); + expect(readDir).toHaveBeenCalledWith("/mock/data/dir/hyprnote/humans"); + + const humans = store.getTable("humans"); + expect(humans[HUMAN_UUID_1]).toEqual({ + user_id: "user-1", + created_at: "2024-01-01T00:00:00Z", + name: "John Doe", + email: "john@example.com", + org_id: "org-1", + job_title: "Engineer", + linkedin_username: "johndoe", + memo: "Some notes", + }); }); - test("returns empty humans when file does not exist", async () => { - const { readTextFile } = await import("@tauri-apps/plugin-fs"); + test("returns empty humans when directory does not exist", async () => { + const { readDir, exists } = await import("@tauri-apps/plugin-fs"); - vi.mocked(readTextFile).mockRejectedValue( + vi.mocked(exists).mockResolvedValue(false); + vi.mocked(readDir).mockRejectedValue( new Error("No such file or directory"), ); @@ -79,13 +105,45 @@ describe("createHumanPersister", () => { expect(store.getTable("humans")).toEqual({}); }); + + test("skips non-UUID files", async () => { + const { readDir, readTextFile, exists } = + await import("@tauri-apps/plugin-fs"); + + vi.mocked(exists).mockResolvedValue(false); + vi.mocked(readDir).mockResolvedValue([ + { name: "not-a-uuid.md", isDirectory: false }, + { name: `${HUMAN_UUID_1}.md`, isDirectory: false }, + ]); + + const mockMdContent = serializeMarkdownWithFrontmatter( + { + user_id: "user-1", + created_at: "2024-01-01T00:00:00Z", + name: "John Doe", + email: "john@example.com", + org_id: "", + job_title: "", + linkedin_username: "", + }, + "", + ); + vi.mocked(readTextFile).mockResolvedValue(mockMdContent); + + const persister = createHumanPersister(store); + await persister.load(); + + const humans = store.getTable("humans"); + expect(Object.keys(humans)).toHaveLength(1); + expect(humans[HUMAN_UUID_1]).toBeDefined(); + }); }); describe("save", () => { - test("saves humans to json file", async () => { - const { writeTextFile } = await import("@tauri-apps/plugin-fs"); + test("saves humans to markdown files", async () => { + const { writeTextFile, mkdir } = await import("@tauri-apps/plugin-fs"); - store.setRow("humans", "human-1", { + store.setRow("humans", HUMAN_UUID_1, { user_id: "user-1", created_at: "2024-01-01T00:00:00Z", name: "John Doe", @@ -99,44 +157,44 @@ describe("createHumanPersister", () => { const persister = createHumanPersister(store); await persister.save(); + expect(mkdir).toHaveBeenCalledWith("/mock/data/dir/hyprnote/humans", { + recursive: true, + }); + expect(writeTextFile).toHaveBeenCalledWith( - "/mock/data/dir/hyprnote/humans.json", + `/mock/data/dir/hyprnote/humans/${HUMAN_UUID_1}.md`, expect.any(String), ); const writtenContent = vi.mocked(writeTextFile).mock.calls[0][1]; - const parsed = JSON.parse(writtenContent); + const { frontmatter, body } = + parseMarkdownWithFrontmatter(writtenContent); - expect(parsed).toEqual({ - "human-1": { - user_id: "user-1", - created_at: "2024-01-01T00:00:00Z", - name: "John Doe", - email: "john@example.com", - org_id: "org-1", - job_title: "Engineer", - linkedin_username: "johndoe", - memo: "Some notes", - }, + expect(frontmatter).toEqual({ + user_id: "user-1", + created_at: "2024-01-01T00:00:00Z", + name: "John Doe", + email: "john@example.com", + org_id: "org-1", + job_title: "Engineer", + linkedin_username: "johndoe", }); + expect(body).toBe("Some notes"); }); - test("writes empty object when no humans exist", async () => { + test("does not write when no humans exist", async () => { const { writeTextFile } = await import("@tauri-apps/plugin-fs"); const persister = createHumanPersister(store); await persister.save(); - expect(writeTextFile).toHaveBeenCalledWith( - "/mock/data/dir/hyprnote/humans.json", - "{}", - ); + expect(writeTextFile).not.toHaveBeenCalled(); }); - test("saves multiple humans", async () => { + test("saves multiple humans to separate files", async () => { const { writeTextFile } = await import("@tauri-apps/plugin-fs"); - store.setRow("humans", "human-1", { + store.setRow("humans", HUMAN_UUID_1, { user_id: "user-1", created_at: "2024-01-01T00:00:00Z", name: "John Doe", @@ -147,7 +205,7 @@ describe("createHumanPersister", () => { memo: "", }); - store.setRow("humans", "human-2", { + store.setRow("humans", HUMAN_UUID_2, { user_id: "user-1", created_at: "2024-01-02T00:00:00Z", name: "Jane Smith", @@ -161,12 +219,102 @@ describe("createHumanPersister", () => { const persister = createHumanPersister(store); await persister.save(); - const writtenContent = vi.mocked(writeTextFile).mock.calls[0][1]; - const parsed = JSON.parse(writtenContent); + expect(writeTextFile).toHaveBeenCalledTimes(2); + + const writtenPaths = vi + .mocked(writeTextFile) + .mock.calls.map((call) => call[0]); + expect(writtenPaths).toContain( + `/mock/data/dir/hyprnote/humans/${HUMAN_UUID_1}.md`, + ); + expect(writtenPaths).toContain( + `/mock/data/dir/hyprnote/humans/${HUMAN_UUID_2}.md`, + ); + }); + }); + + describe("migration", () => { + test("migrates from humans.json when it exists and humans dir does not", async () => { + const { exists, readTextFile, writeTextFile, mkdir, remove } = + await import("@tauri-apps/plugin-fs"); + + vi.mocked(exists).mockImplementation(async (path: string) => { + if (path.endsWith("humans.json")) return true; + if (path.endsWith("humans")) return false; + return false; + }); + + const mockJsonData = { + [HUMAN_UUID_1]: { + user_id: "user-1", + created_at: "2024-01-01T00:00:00Z", + name: "John Doe", + email: "john@example.com", + org_id: "org-1", + job_title: "Engineer", + linkedin_username: "johndoe", + memo: "Some notes", + }, + }; + vi.mocked(readTextFile).mockResolvedValue(JSON.stringify(mockJsonData)); + + const persister = createHumanPersister(store); + await persister.load(); + + expect(mkdir).toHaveBeenCalledWith("/mock/data/dir/hyprnote/humans", { + recursive: true, + }); + expect(writeTextFile).toHaveBeenCalledWith( + `/mock/data/dir/hyprnote/humans/${HUMAN_UUID_1}.md`, + expect.any(String), + ); + expect(remove).toHaveBeenCalledWith( + "/mock/data/dir/hyprnote/humans.json", + ); + }); + }); +}); + +describe("utils", () => { + describe("parseMarkdownWithFrontmatter", () => { + test("parses markdown with frontmatter", () => { + const content = `--- +name: John Doe +email: john@example.com +--- + +Some notes about John`; + + const { frontmatter, body } = parseMarkdownWithFrontmatter(content); + + expect(frontmatter).toEqual({ + name: "John Doe", + email: "john@example.com", + }); + expect(body).toBe("Some notes about John"); + }); + + test("returns empty frontmatter for content without frontmatter", () => { + const content = "Just some text without frontmatter"; + + const { frontmatter, body } = parseMarkdownWithFrontmatter(content); + + expect(frontmatter).toEqual({}); + expect(body).toBe("Just some text without frontmatter"); + }); + }); + + describe("serializeMarkdownWithFrontmatter", () => { + test("serializes frontmatter and body", () => { + const frontmatter = { name: "John Doe", email: "john@example.com" }; + const body = "Some notes"; + + const result = serializeMarkdownWithFrontmatter(frontmatter, body); - expect(Object.keys(parsed)).toHaveLength(2); - expect(parsed["human-1"].name).toBe("John Doe"); - expect(parsed["human-2"].name).toBe("Jane Smith"); + expect(result).toContain("---"); + expect(result).toContain("name: John Doe"); + expect(result).toContain("email: john@example.com"); + expect(result).toContain("Some notes"); }); }); }); diff --git a/apps/desktop/src/store/tinybase/persister/human/persister.ts b/apps/desktop/src/store/tinybase/persister/human/persister.ts index d7b26be10c..eee6d98031 100644 --- a/apps/desktop/src/store/tinybase/persister/human/persister.ts +++ b/apps/desktop/src/store/tinybase/persister/human/persister.ts @@ -1,13 +1,36 @@ -import type { MergeableStore, OptionalSchemas } from "tinybase/with-schemas"; +import type { + Content, + MergeableStore, + OptionalSchemas, +} from "tinybase/with-schemas"; -import { createSingleTablePersister } from "../utils"; +import { + asTableChanges, + createSessionDirPersister, + getDataDir, +} from "../utils"; +import { collectHumanWriteOps, type HumanCollectorResult } from "./collect"; +import { cleanupOrphanHumanFiles, loadAllHumans } from "./load"; +import { migrateHumansJsonIfNeeded } from "./migrate"; export function createHumanPersister( store: MergeableStore, ) { - return createSingleTablePersister(store, { - tableName: "humans", - filename: "humans.json", + return createSessionDirPersister(store, { label: "HumanPersister", + collect: collectHumanWriteOps, + load: async (): Promise | undefined> => { + const dataDir = await getDataDir(); + await migrateHumansJsonIfNeeded(dataDir); + const humans = await loadAllHumans(dataDir); + if (Object.keys(humans).length === 0) { + return undefined; + } + return asTableChanges("humans", humans) as Content; + }, + postSave: async (dataDir, result) => { + const { validHumanIds } = result as HumanCollectorResult; + await cleanupOrphanHumanFiles(dataDir, validHumanIds); + }, }); } diff --git a/apps/desktop/src/store/tinybase/persister/human/utils.ts b/apps/desktop/src/store/tinybase/persister/human/utils.ts new file mode 100644 index 0000000000..bab01c062f --- /dev/null +++ b/apps/desktop/src/store/tinybase/persister/human/utils.ts @@ -0,0 +1,43 @@ +import { sep } from "@tauri-apps/api/path"; +import { parse as parseYaml, stringify as stringifyYaml } from "yaml"; + +export interface ParsedMarkdown { + frontmatter: Record; + body: string; +} + +export function parseMarkdownWithFrontmatter(content: string): ParsedMarkdown { + const frontmatterRegex = /^---\n([\s\S]*?)\n---\n?([\s\S]*)$/; + const match = content.match(frontmatterRegex); + + if (!match) { + return { frontmatter: {}, body: content }; + } + + const [, yamlContent, body] = match; + + let frontmatter: Record = {}; + try { + frontmatter = parseYaml(yamlContent) ?? {}; + } catch { + frontmatter = {}; + } + + return { frontmatter, body: body.trim() }; +} + +export function serializeMarkdownWithFrontmatter( + frontmatter: Record, + body: string, +): string { + const yamlContent = stringifyYaml(frontmatter, { lineWidth: 0 }).trim(); + return `---\n${yamlContent}\n---\n\n${body}`; +} + +export function getHumanDir(dataDir: string): string { + return [dataDir, "humans"].join(sep()); +} + +export function getHumanFilePath(dataDir: string, humanId: string): string { + return [dataDir, "humans", `${humanId}.md`].join(sep()); +} diff --git a/apps/desktop/src/store/tinybase/persister/utils.ts b/apps/desktop/src/store/tinybase/persister/utils.ts index 812a017c59..57ed0d62e6 100644 --- a/apps/desktop/src/store/tinybase/persister/utils.ts +++ b/apps/desktop/src/store/tinybase/persister/utils.ts @@ -226,7 +226,8 @@ export function createModeAwarePersister( export type WriteOperation = | { type: "json"; path: string; content: unknown } - | { type: "md-batch"; items: Array<[ExportJsonValue, string]> }; + | { type: "md-batch"; items: Array<[ExportJsonValue, string]> } + | { type: "text"; path: string; content: string }; export type CollectorResult = { dirs: Set; @@ -263,12 +264,15 @@ export function createSessionDirPersister( const jsonBatchItems: Array<[ExportJsonValue, string]> = []; let mdBatchItems: Array<[ExportJsonValue, string]> = []; + const textItems: Array<{ path: string; content: string }> = []; for (const op of operations) { if (op.type === "json") { jsonBatchItems.push([op.content as ExportJsonValue, op.path]); } else if (op.type === "md-batch") { mdBatchItems = mdBatchItems.concat(op.items); + } else if (op.type === "text") { + textItems.push({ path: op.path, content: op.content }); } } @@ -294,6 +298,17 @@ export function createSessionDirPersister( } } + for (const item of textItems) { + try { + await writeTextFile(item.path, item.content); + } catch (e) { + console.error( + `[${options.label}] Failed to write text file ${item.path}:`, + e, + ); + } + } + if (options.postSave) { await options.postSave(dataDir, result); } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 88522022ba..8beb89751f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -175,7 +175,7 @@ importers: version: 19.2.3(@types/react@19.2.7) '@vitejs/plugin-react': specifier: ^4.7.0 - version: 4.7.0(vite@7.3.0(@types/node@24.10.4)(jiti@1.21.7)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)) + version: 4.7.0(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)) autoprefixer: specifier: ^10.4.23 version: 10.4.23(postcss@8.5.6) @@ -190,7 +190,7 @@ importers: version: 5.8.3 vite: specifier: ^7.3.0 - version: 7.3.0(@types/node@24.10.4)(jiti@1.21.7)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) + version: 7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) apps/desktop: dependencies: @@ -524,6 +524,9 @@ importers: xstate: specifier: ^5.25.0 version: 5.25.0 + yaml: + specifier: ^2.8.2 + version: 2.8.2 zod: specifier: ^4.2.1 version: 4.2.1 @@ -542,7 +545,7 @@ importers: version: 1.142.8(@tanstack/react-router@1.142.8(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(@tanstack/router-core@1.142.8)(csstype@3.2.3)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(solid-js@1.9.10) '@tanstack/router-plugin': specifier: ^1.142.8 - version: 1.142.8(@tanstack/react-router@1.142.8(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)) + version: 1.142.8(@tanstack/react-router@1.142.8(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(vite@7.3.0(@types/node@24.10.4)(jiti@1.21.7)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)) '@tauri-apps/cli': specifier: ^2.9.6 version: 2.9.6 @@ -569,7 +572,7 @@ importers: version: 2.0.3 '@vitejs/plugin-react': specifier: ^4.7.0 - version: 4.7.0(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)) + version: 4.7.0(vite@7.3.0(@types/node@24.10.4)(jiti@1.21.7)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)) autoprefixer: specifier: ^10.4.23 version: 10.4.23(postcss@8.5.6) @@ -590,10 +593,10 @@ importers: version: 5.8.3 vite: specifier: ^7.3.0 - version: 7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) + version: 7.3.0(@types/node@24.10.4)(jiti@1.21.7)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) vitest: specifier: ^3.2.4 - version: 3.2.4(@types/debug@4.1.12)(@types/node@24.10.4)(jiti@2.6.1)(jsdom@27.3.0)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) + version: 3.2.4(@types/debug@4.1.12)(@types/node@24.10.4)(jiti@1.21.7)(jsdom@27.3.0)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) apps/howto: dependencies: @@ -16553,14 +16556,14 @@ snapshots: '@apm-js-collab/tracing-hooks@0.3.1': dependencies: '@apm-js-collab/code-transformer': 0.8.2 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3(supports-color@10.2.2) module-details-from-path: 1.0.4 transitivePeerDependencies: - supports-color '@argos-ci/api-client@0.15.0': dependencies: - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3(supports-color@10.2.2) openapi-fetch: 0.15.0 transitivePeerDependencies: - supports-color @@ -16572,7 +16575,7 @@ snapshots: '@argos-ci/api-client': 0.15.0 '@argos-ci/util': 3.2.0 convict: 6.2.4 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3(supports-color@10.2.2) fast-glob: 3.3.3 mime-types: 3.0.2 sharp: 0.34.5 @@ -16586,7 +16589,7 @@ snapshots: '@argos-ci/core': 5.0.3 '@argos-ci/util': 3.2.0 chalk: 5.6.2 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3(supports-color@10.2.2) transitivePeerDependencies: - supports-color @@ -16663,7 +16666,7 @@ snapshots: dependencies: '@astrojs/internal-helpers': 0.7.5 '@astrojs/underscore-redirects': 1.0.0 - '@netlify/blobs': 10.5.0 + '@netlify/blobs': 10.5.0(supports-color@10.2.2) '@netlify/functions': 5.1.2 '@netlify/vite-plugin': 2.7.19(@netlify/api@14.0.12)(aws4fetch@1.0.20)(babel-plugin-macros@3.1.0)(ioredis@5.8.2)(rollup@4.54.0)(vite@6.4.1(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)) '@vercel/nft': 0.30.4(rollup@4.54.0) @@ -16739,7 +16742,7 @@ snapshots: '@astrojs/telemetry@3.3.0': dependencies: ci-info: 4.3.1 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3(supports-color@10.2.2) dlv: 1.1.3 dset: 3.1.4 is-docker: 3.0.0 @@ -16794,7 +16797,7 @@ snapshots: '@babel/types': 7.28.5 '@jridgewell/remapping': 2.3.5 convert-source-map: 2.0.0 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3(supports-color@10.2.2) gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -16962,7 +16965,7 @@ snapshots: '@babel/parser': 7.28.5 '@babel/template': 7.27.2 '@babel/types': 7.28.5 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3(supports-color@10.2.2) transitivePeerDependencies: - supports-color @@ -17464,7 +17467,7 @@ snapshots: '@esbuild-plugins/node-resolve@0.2.2(esbuild@0.25.12)': dependencies: '@types/resolve': 1.20.6 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3(supports-color@10.2.2) esbuild: 0.25.12 escape-string-regexp: 4.0.0 resolve: 1.22.11 @@ -17862,7 +17865,7 @@ snapshots: '@eslint/config-array@0.21.1': dependencies: '@eslint/object-schema': 2.1.7 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3(supports-color@10.2.2) minimatch: 3.1.2 transitivePeerDependencies: - supports-color @@ -17878,7 +17881,7 @@ snapshots: '@eslint/eslintrc@3.3.3': dependencies: ajv: 6.12.6 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3(supports-color@10.2.2) espree: 10.4.0 globals: 14.0.0 ignore: 5.3.2 @@ -18669,19 +18672,6 @@ snapshots: '@lukeed/ms@2.0.2': {} - '@mapbox/node-pre-gyp@2.0.3': - dependencies: - consola: 3.4.2 - detect-libc: 2.1.2 - https-proxy-agent: 7.0.6 - node-fetch: 2.7.0 - nopt: 8.1.0 - semver: 7.6.3 - tar: 7.5.2 - transitivePeerDependencies: - - encoding - - supports-color - '@mapbox/node-pre-gyp@2.0.3(supports-color@10.2.2)': dependencies: consola: 3.4.2 @@ -18902,14 +18892,6 @@ snapshots: '@netlify/dev-utils': 4.3.0 '@netlify/runtime-utils': 2.2.0 - '@netlify/blobs@10.5.0': - dependencies: - '@netlify/dev-utils': 4.3.3 - '@netlify/otel': 5.1.1 - '@netlify/runtime-utils': 2.2.1 - transitivePeerDependencies: - - supports-color - '@netlify/blobs@10.5.0(supports-color@10.2.2)': dependencies: '@netlify/dev-utils': 4.3.3 @@ -19121,7 +19103,7 @@ snapshots: '@netlify/dev@4.8.7(@netlify/api@14.0.12)(aws4fetch@1.0.20)(ioredis@5.8.2)(rollup@4.54.0)': dependencies: '@netlify/ai': 0.3.5(@netlify/api@14.0.12) - '@netlify/blobs': 10.5.0 + '@netlify/blobs': 10.5.0(supports-color@10.2.2) '@netlify/config': 24.2.0 '@netlify/dev-utils': 4.3.3 '@netlify/edge-functions-dev': 1.0.7 @@ -19227,10 +19209,10 @@ snapshots: '@netlify/functions-dev@1.1.7(rollup@4.54.0)': dependencies: - '@netlify/blobs': 10.5.0 + '@netlify/blobs': 10.5.0(supports-color@10.2.2) '@netlify/dev-utils': 4.3.3 '@netlify/functions': 5.1.2 - '@netlify/zip-it-and-ship-it': 14.1.17(rollup@4.54.0) + '@netlify/zip-it-and-ship-it': 14.1.17(rollup@4.54.0)(supports-color@10.2.2) cron-parser: 4.9.0 decache: 4.6.2 extract-zip: 2.0.1 @@ -19364,16 +19346,6 @@ snapshots: dependencies: '@opentelemetry/api': 1.8.0 - '@netlify/otel@5.1.1': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.203.0(@opentelemetry/api@1.9.0) - '@opentelemetry/resources': 1.30.1(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-trace-node': 1.30.1(@opentelemetry/api@1.9.0) - transitivePeerDependencies: - - supports-color - '@netlify/otel@5.1.1(supports-color@10.2.2)': dependencies: '@opentelemetry/api': 1.9.0 @@ -19411,7 +19383,7 @@ snapshots: '@netlify/runtime@4.1.13': dependencies: - '@netlify/blobs': 10.5.0 + '@netlify/blobs': 10.5.0(supports-color@10.2.2) '@netlify/cache': 3.3.4 '@netlify/runtime-utils': 2.2.1 '@netlify/types': 2.3.0 @@ -19527,47 +19499,6 @@ snapshots: - supports-color - uploadthing - '@netlify/zip-it-and-ship-it@14.1.14(rollup@4.54.0)': - dependencies: - '@babel/parser': 7.28.5 - '@babel/types': 7.28.5 - '@netlify/binary-info': 1.0.0 - '@netlify/serverless-functions-api': 2.8.2 - '@vercel/nft': 0.29.4(rollup@4.54.0) - archiver: 7.0.1 - common-path-prefix: 3.0.0 - copy-file: 11.1.0 - es-module-lexer: 1.7.0 - esbuild: 0.25.11 - execa: 8.0.1 - fast-glob: 3.3.3 - filter-obj: 6.1.0 - find-up: 7.0.0 - is-path-inside: 4.0.0 - junk: 4.0.1 - locate-path: 7.2.0 - merge-options: 3.0.4 - minimatch: 9.0.5 - normalize-path: 3.0.0 - p-map: 7.0.3 - path-exists: 5.0.0 - precinct: 12.2.0 - require-package-name: 2.0.1 - resolve: 2.0.0-next.5 - semver: 7.6.3 - tmp-promise: 3.0.3 - toml: 3.0.0 - unixify: 1.0.0 - urlpattern-polyfill: 8.0.2 - yargs: 17.7.2 - zod: 3.25.76 - transitivePeerDependencies: - - bare-abort-controller - - encoding - - react-native-b4a - - rollup - - supports-color - '@netlify/zip-it-and-ship-it@14.1.14(rollup@4.54.0)(supports-color@10.2.2)': dependencies: '@babel/parser': 7.28.5 @@ -19609,47 +19540,6 @@ snapshots: - rollup - supports-color - '@netlify/zip-it-and-ship-it@14.1.17(rollup@4.54.0)': - dependencies: - '@babel/parser': 7.28.5 - '@babel/types': 7.28.5 - '@netlify/binary-info': 1.0.0 - '@netlify/serverless-functions-api': 2.8.2 - '@vercel/nft': 0.29.4(rollup@4.54.0) - archiver: 7.0.1 - common-path-prefix: 3.0.0 - copy-file: 11.1.0 - es-module-lexer: 1.7.0 - esbuild: 0.27.2 - execa: 8.0.1 - fast-glob: 3.3.3 - filter-obj: 6.1.0 - find-up: 7.0.0 - is-path-inside: 4.0.0 - junk: 4.0.1 - locate-path: 7.2.0 - merge-options: 3.0.4 - minimatch: 9.0.5 - normalize-path: 3.0.0 - p-map: 7.0.3 - path-exists: 5.0.0 - precinct: 12.2.0 - require-package-name: 2.0.1 - resolve: 2.0.0-next.5 - semver: 7.6.3 - tmp-promise: 3.0.3 - toml: 3.0.0 - unixify: 1.0.0 - urlpattern-polyfill: 8.0.2 - yargs: 17.7.2 - zod: 3.25.76 - transitivePeerDependencies: - - bare-abort-controller - - encoding - - react-native-b4a - - rollup - - supports-color - '@netlify/zip-it-and-ship-it@14.1.17(rollup@4.54.0)(supports-color@10.2.2)': dependencies: '@babel/parser': 7.28.5 @@ -20395,15 +20285,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation@0.203.0(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/api-logs': 0.203.0 - import-in-the-middle: 1.15.0 - require-in-the-middle: 7.5.2 - transitivePeerDependencies: - - supports-color - '@opentelemetry/instrumentation@0.203.0(@opentelemetry/api@1.9.0)(supports-color@10.2.2)': dependencies: '@opentelemetry/api': 1.9.0 @@ -20428,7 +20309,7 @@ snapshots: '@opentelemetry/api-logs': 0.53.0 '@types/shimmer': 1.2.0 import-in-the-middle: 1.15.0 - require-in-the-middle: 7.5.2 + require-in-the-middle: 7.5.2(supports-color@10.2.2) semver: 7.6.3 shimmer: 1.2.1 transitivePeerDependencies: @@ -20440,7 +20321,7 @@ snapshots: '@opentelemetry/api-logs': 0.57.1 '@types/shimmer': 1.2.0 import-in-the-middle: 1.15.0 - require-in-the-middle: 7.5.2 + require-in-the-middle: 7.5.2(supports-color@10.2.2) semver: 7.6.3 shimmer: 1.2.1 transitivePeerDependencies: @@ -20452,7 +20333,7 @@ snapshots: '@opentelemetry/api-logs': 0.57.2 '@types/shimmer': 1.2.0 import-in-the-middle: 1.15.0 - require-in-the-middle: 7.5.2 + require-in-the-middle: 7.5.2(supports-color@10.2.2) semver: 7.6.3 shimmer: 1.2.1 transitivePeerDependencies: @@ -20760,7 +20641,7 @@ snapshots: '@pnpm/tabtab@0.5.4': dependencies: - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3(supports-color@10.2.2) enquirer: 2.4.1 minimist: 1.2.8 untildify: 4.0.0 @@ -20867,7 +20748,7 @@ snapshots: '@puppeteer/browsers@2.11.0': dependencies: - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3(supports-color@10.2.2) extract-zip: 2.0.1 progress: 2.0.3 proxy-agent: 6.5.0 @@ -20882,7 +20763,7 @@ snapshots: '@puppeteer/browsers@2.3.0': dependencies: - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3(supports-color@10.2.2) extract-zip: 2.0.1 progress: 2.0.3 proxy-agent: 6.5.0 @@ -23005,7 +22886,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@tanstack/router-plugin@1.142.8(@tanstack/react-router@1.142.8(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2))': + '@tanstack/router-plugin@1.142.8(@tanstack/react-router@1.142.8(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(vite@7.3.0(@types/node@24.10.4)(jiti@1.21.7)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2))': dependencies: '@babel/core': 7.28.5 '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.5) @@ -23023,7 +22904,7 @@ snapshots: zod: 3.25.76 optionalDependencies: '@tanstack/react-router': 1.142.8(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - vite: 7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) + vite: 7.3.0(@types/node@24.10.4)(jiti@1.21.7)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) transitivePeerDependencies: - supports-color @@ -23937,9 +23818,9 @@ snapshots: dependencies: '@typescript-eslint/scope-manager': 8.50.0 '@typescript-eslint/types': 8.50.0 - '@typescript-eslint/typescript-estree': 8.50.0(typescript@5.9.3) + '@typescript-eslint/typescript-estree': 8.50.0(supports-color@10.2.2)(typescript@5.9.3) '@typescript-eslint/visitor-keys': 8.50.0 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3(supports-color@10.2.2) eslint: 9.39.2(jiti@2.6.1) typescript: 5.9.3 transitivePeerDependencies: @@ -23954,15 +23835,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.50.0(typescript@5.9.3)': - dependencies: - '@typescript-eslint/tsconfig-utils': 8.50.0(typescript@5.9.3) - '@typescript-eslint/types': 8.50.0 - debug: 4.4.3(supports-color@8.1.1) - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - '@typescript-eslint/scope-manager@8.50.0': dependencies: '@typescript-eslint/types': 8.50.0 @@ -23975,9 +23847,9 @@ snapshots: '@typescript-eslint/type-utils@8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': dependencies: '@typescript-eslint/types': 8.50.0 - '@typescript-eslint/typescript-estree': 8.50.0(typescript@5.9.3) + '@typescript-eslint/typescript-estree': 8.50.0(supports-color@10.2.2)(typescript@5.9.3) '@typescript-eslint/utils': 8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3(supports-color@10.2.2) eslint: 9.39.2(jiti@2.6.1) ts-api-utils: 2.1.0(typescript@5.9.3) typescript: 5.9.3 @@ -24001,27 +23873,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/typescript-estree@8.50.0(typescript@5.9.3)': - dependencies: - '@typescript-eslint/project-service': 8.50.0(typescript@5.9.3) - '@typescript-eslint/tsconfig-utils': 8.50.0(typescript@5.9.3) - '@typescript-eslint/types': 8.50.0 - '@typescript-eslint/visitor-keys': 8.50.0 - debug: 4.4.3(supports-color@8.1.1) - minimatch: 9.0.5 - semver: 7.6.3 - tinyglobby: 0.2.15 - ts-api-utils: 2.1.0(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - '@typescript-eslint/utils@8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': dependencies: '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.2(jiti@2.6.1)) '@typescript-eslint/scope-manager': 8.50.0 '@typescript-eslint/types': 8.50.0 - '@typescript-eslint/typescript-estree': 8.50.0(typescript@5.9.3) + '@typescript-eslint/typescript-estree': 8.50.0(supports-color@10.2.2)(typescript@5.9.3) eslint: 9.39.2(jiti@2.6.1) typescript: 5.9.3 transitivePeerDependencies: @@ -24088,25 +23945,6 @@ snapshots: '@use-gesture/core': 10.3.1 react: 19.2.3 - '@vercel/nft@0.29.4(rollup@4.54.0)': - dependencies: - '@mapbox/node-pre-gyp': 2.0.3 - '@rollup/pluginutils': 5.3.0(rollup@4.54.0) - acorn: 8.15.0 - acorn-import-attributes: 1.9.5(acorn@8.15.0) - async-sema: 3.1.1 - bindings: 1.5.0 - estree-walker: 2.0.2 - glob: 10.5.0 - graceful-fs: 4.2.11 - node-gyp-build: 4.8.4 - picomatch: 4.0.3 - resolve-from: 5.0.0 - transitivePeerDependencies: - - encoding - - rollup - - supports-color - '@vercel/nft@0.29.4(rollup@4.54.0)(supports-color@10.2.2)': dependencies: '@mapbox/node-pre-gyp': 2.0.3(supports-color@10.2.2) @@ -24128,7 +23966,7 @@ snapshots: '@vercel/nft@0.30.4(rollup@4.54.0)': dependencies: - '@mapbox/node-pre-gyp': 2.0.3 + '@mapbox/node-pre-gyp': 2.0.3(supports-color@10.2.2) '@rollup/pluginutils': 5.3.0(rollup@4.54.0) acorn: 8.15.0 acorn-import-attributes: 1.9.5(acorn@8.15.0) @@ -24226,13 +24064,13 @@ snapshots: optionalDependencies: vite: 7.3.0(@types/node@22.19.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) - '@vitest/mocker@3.2.4(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2))': + '@vitest/mocker@3.2.4(vite@7.3.0(@types/node@24.10.4)(jiti@1.21.7)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2))': dependencies: '@vitest/spy': 3.2.4 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) + vite: 7.3.0(@types/node@24.10.4)(jiti@1.21.7)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) '@vitest/mocker@3.2.4(vite@7.3.0(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2))': dependencies: @@ -24921,7 +24759,7 @@ snapshots: common-ancestor-path: 1.0.1 cookie: 1.1.1 cssesc: 3.0.0 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3(supports-color@10.2.2) deterministic-object-hash: 2.0.2 devalue: 5.6.1 diff: 5.2.0 @@ -25191,7 +25029,7 @@ snapshots: dependencies: bytes: 3.1.2 content-type: 1.0.5 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3(supports-color@10.2.2) http-errors: 2.0.1 iconv-lite: 0.7.1 on-finished: 2.4.1 @@ -26184,15 +26022,6 @@ snapshots: transitivePeerDependencies: - supports-color - detective-typescript@14.0.0(typescript@5.9.3): - dependencies: - '@typescript-eslint/typescript-estree': 8.50.0(typescript@5.9.3) - ast-module-types: 6.0.1 - node-source-walk: 7.0.1 - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - detective-vue2@2.2.0(supports-color@10.2.2)(typescript@5.9.3): dependencies: '@dependents/detective-less': 5.0.1 @@ -26206,19 +26035,6 @@ snapshots: transitivePeerDependencies: - supports-color - detective-vue2@2.2.0(typescript@5.9.3): - dependencies: - '@dependents/detective-less': 5.0.1 - '@vue/compiler-sfc': 3.5.26 - detective-es6: 5.0.1 - detective-sass: 6.0.1 - detective-scss: 5.0.1 - detective-stylus: 5.0.1 - detective-typescript: 14.0.0(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - deterministic-object-hash@2.0.2: dependencies: base-64: 1.0.0 @@ -26333,7 +26149,7 @@ snapshots: edge-paths: 3.0.5 fast-xml-parser: 5.3.3 http-proxy-agent: 7.0.2 - https-proxy-agent: 7.0.6 + https-proxy-agent: 7.0.6(supports-color@10.2.2) which: 6.0.0 transitivePeerDependencies: - supports-color @@ -26458,7 +26274,7 @@ snapshots: esbuild-register@3.6.0(esbuild@0.25.12): dependencies: - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3(supports-color@10.2.2) esbuild: 0.25.12 transitivePeerDependencies: - supports-color @@ -26653,7 +26469,7 @@ snapshots: ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.6 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3(supports-color@10.2.2) escape-string-regexp: 4.0.0 eslint-scope: 8.4.0 eslint-visitor-keys: 4.2.1 @@ -26908,7 +26724,7 @@ snapshots: content-type: 1.0.5 cookie: 0.7.2 cookie-signature: 1.2.2 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3(supports-color@10.2.2) depd: 2.0.0 encodeurl: 2.0.0 escape-html: 1.0.3 @@ -26957,7 +26773,7 @@ snapshots: extract-zip@2.0.1: dependencies: - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3(supports-color@10.2.2) get-stream: 5.2.0 yauzl: 2.10.0 optionalDependencies: @@ -27129,7 +26945,7 @@ snapshots: finalhandler@2.1.1: dependencies: - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3(supports-color@10.2.2) encodeurl: 2.0.0 escape-html: 1.0.3 on-finished: 2.4.1 @@ -27187,7 +27003,7 @@ snapshots: follow-redirects@1.15.11(debug@4.4.3): optionalDependencies: - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3(supports-color@10.2.2) fontace@0.3.1: dependencies: @@ -27276,7 +27092,7 @@ snapshots: gaxios@7.1.3: dependencies: extend: 3.0.2 - https-proxy-agent: 7.0.6 + https-proxy-agent: 7.0.6(supports-color@10.2.2) node-fetch: 3.3.2 rimraf: 5.0.10 transitivePeerDependencies: @@ -27296,7 +27112,7 @@ snapshots: '@zip.js/zip.js': 2.8.11 decamelize: 6.0.1 http-proxy-agent: 7.0.2 - https-proxy-agent: 7.0.6 + https-proxy-agent: 7.0.6(supports-color@10.2.2) modern-tar: 0.7.3 transitivePeerDependencies: - supports-color @@ -27361,7 +27177,7 @@ snapshots: dependencies: basic-ftp: 5.0.5 data-uri-to-buffer: 6.0.2 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3(supports-color@10.2.2) transitivePeerDependencies: - supports-color @@ -27849,7 +27665,7 @@ snapshots: http-proxy-agent@7.0.2: dependencies: agent-base: 7.1.4 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3(supports-color@10.2.2) transitivePeerDependencies: - supports-color @@ -27878,13 +27694,6 @@ snapshots: quick-lru: 5.1.1 resolve-alpn: 1.2.1 - https-proxy-agent@7.0.6: - dependencies: - agent-base: 7.1.4 - debug: 4.4.3(supports-color@8.1.1) - transitivePeerDependencies: - - supports-color - https-proxy-agent@7.0.6(supports-color@10.2.2): dependencies: agent-base: 7.1.4 @@ -28034,7 +27843,7 @@ snapshots: dependencies: '@ioredis/commands': 1.4.0 cluster-key-slot: 1.1.2 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3(supports-color@10.2.2) denque: 2.1.0 lodash.defaults: 4.2.0 lodash.isarguments: 3.1.0 @@ -28384,7 +28193,7 @@ snapshots: decimal.js: 10.6.0 html-encoding-sniffer: 4.0.0 http-proxy-agent: 7.0.2 - https-proxy-agent: 7.0.6 + https-proxy-agent: 7.0.6(supports-color@10.2.2) is-potential-custom-element-name: 1.0.1 parse5: 8.0.0 saxes: 6.0.0 @@ -29838,7 +29647,7 @@ snapshots: micromark@3.2.0: dependencies: '@types/debug': 4.1.12 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3(supports-color@10.2.2) decode-named-character-reference: 1.2.0 micromark-core-commonmark: 1.1.0 micromark-factory-space: 1.1.0 @@ -29861,7 +29670,7 @@ snapshots: micromark@4.0.2: dependencies: '@types/debug': 4.1.12 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3(supports-color@10.2.2) decode-named-character-reference: 1.2.0 devlop: 1.1.0 micromark-core-commonmark: 2.0.3 @@ -30095,7 +29904,7 @@ snapshots: '@netlify/headers-parser': 9.0.2 '@netlify/local-functions-proxy': 2.0.3 '@netlify/redirect-parser': 15.0.3 - '@netlify/zip-it-and-ship-it': 14.1.14(rollup@4.54.0) + '@netlify/zip-it-and-ship-it': 14.1.14(rollup@4.54.0)(supports-color@10.2.2) '@octokit/rest': 22.0.0 '@opentelemetry/api': 1.8.0 '@pnpm/tabtab': 0.5.4 @@ -30113,7 +29922,7 @@ snapshots: content-type: 1.0.5 cookie: 1.0.2 cron-parser: 4.9.0 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3(supports-color@10.2.2) decache: 4.6.2 dot-prop: 9.0.0 dotenv: 17.2.3 @@ -30135,7 +29944,7 @@ snapshots: gitconfiglocal: 2.1.0 http-proxy: 1.18.1(debug@4.4.3) http-proxy-middleware: 2.0.9(debug@4.4.3) - https-proxy-agent: 7.0.6 + https-proxy-agent: 7.0.6(supports-color@10.2.2) inquirer: 8.2.7(@types/node@22.19.3) inquirer-autocomplete-prompt: 1.4.0(inquirer@8.2.7(@types/node@22.19.3)) ipx: 3.1.1(@netlify/blobs@10.1.0)(aws4fetch@1.0.20)(ioredis@5.8.2) @@ -30594,10 +30403,10 @@ snapshots: dependencies: '@tootallnate/quickjs-emscripten': 0.23.0 agent-base: 7.1.4 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3(supports-color@10.2.2) get-uri: 6.0.5 http-proxy-agent: 7.0.2 - https-proxy-agent: 7.0.6 + https-proxy-agent: 7.0.6(supports-color@10.2.2) pac-resolver: 7.0.1 socks-proxy-agent: 8.0.5 transitivePeerDependencies: @@ -31002,26 +30811,6 @@ snapshots: preact@10.28.0: {} - precinct@12.2.0: - dependencies: - '@dependents/detective-less': 5.0.1 - commander: 12.1.0 - detective-amd: 6.0.1 - detective-cjs: 6.0.1 - detective-es6: 5.0.1 - detective-postcss: 7.0.1(postcss@8.5.6) - detective-sass: 6.0.1 - detective-scss: 5.0.1 - detective-stylus: 5.0.1 - detective-typescript: 14.0.0(typescript@5.9.3) - detective-vue2: 2.2.0(typescript@5.9.3) - module-definition: 6.0.1 - node-source-walk: 7.0.1 - postcss: 8.5.6 - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - precinct@12.2.0(supports-color@10.2.2): dependencies: '@dependents/detective-less': 5.0.1 @@ -31243,9 +31032,9 @@ snapshots: proxy-agent@6.5.0: dependencies: agent-base: 7.1.4 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3(supports-color@10.2.2) http-proxy-agent: 7.0.2 - https-proxy-agent: 7.0.6 + https-proxy-agent: 7.0.6(supports-color@10.2.2) lru-cache: 7.18.3 pac-proxy-agent: 7.2.0 proxy-from-env: 1.1.0 @@ -31279,7 +31068,7 @@ snapshots: dependencies: '@puppeteer/browsers': 2.3.0 chromium-bidi: 0.6.3(devtools-protocol@0.0.1312386) - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3(supports-color@10.2.2) devtools-protocol: 0.0.1312386 ws: 8.18.3 transitivePeerDependencies: @@ -32015,14 +31804,6 @@ snapshots: require-from-string@2.0.2: {} - require-in-the-middle@7.5.2: - dependencies: - debug: 4.4.3(supports-color@8.1.1) - module-details-from-path: 1.0.4 - resolve: 1.22.11 - transitivePeerDependencies: - - supports-color - require-in-the-middle@7.5.2(supports-color@10.2.2): dependencies: debug: 4.4.3(supports-color@10.2.2) @@ -32033,7 +31814,7 @@ snapshots: require-in-the-middle@8.0.1: dependencies: - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3(supports-color@10.2.2) module-details-from-path: 1.0.4 transitivePeerDependencies: - supports-color @@ -32189,7 +31970,7 @@ snapshots: router@2.2.0: dependencies: - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3(supports-color@10.2.2) depd: 2.0.0 is-promise: 4.0.0 parseurl: 1.3.3 @@ -32305,7 +32086,7 @@ snapshots: send@1.2.1: dependencies: - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3(supports-color@10.2.2) encodeurl: 2.0.0 escape-html: 1.0.3 etag: 1.8.1 @@ -32538,7 +32319,7 @@ snapshots: socks-proxy-agent@8.0.5: dependencies: agent-base: 7.1.4 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3(supports-color@10.2.2) socks: 2.8.7 transitivePeerDependencies: - supports-color @@ -33208,7 +32989,7 @@ snapshots: dependencies: '@typescript-eslint/eslint-plugin': 8.50.0(@typescript-eslint/parser@8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) '@typescript-eslint/parser': 8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/typescript-estree': 8.50.0(typescript@5.9.3) + '@typescript-eslint/typescript-estree': 8.50.0(supports-color@10.2.2)(typescript@5.9.3) '@typescript-eslint/utils': 8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) eslint: 9.39.2(jiti@2.6.1) typescript: 5.9.3 @@ -33443,7 +33224,7 @@ snapshots: ofetch: 1.5.1 ufo: 1.6.1 optionalDependencies: - '@netlify/blobs': 10.5.0 + '@netlify/blobs': 10.5.0(supports-color@10.2.2) aws4fetch: 1.0.20 ioredis: 5.8.2 @@ -33610,7 +33391,7 @@ snapshots: vite-node@2.1.9(@types/node@20.19.27)(lightningcss@1.30.2): dependencies: cac: 6.7.14 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3(supports-color@10.2.2) es-module-lexer: 1.7.0 pathe: 1.1.2 vite: 5.4.21(@types/node@20.19.27)(lightningcss@1.30.2) @@ -33628,7 +33409,7 @@ snapshots: vite-node@3.2.4(@types/node@22.19.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2): dependencies: cac: 6.7.14 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3(supports-color@10.2.2) es-module-lexer: 1.7.0 pathe: 2.0.3 vite: 7.3.0(@types/node@22.19.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) @@ -33646,13 +33427,13 @@ snapshots: - tsx - yaml - vite-node@3.2.4(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2): + vite-node@3.2.4(@types/node@24.10.4)(jiti@1.21.7)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2): dependencies: cac: 6.7.14 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3(supports-color@10.2.2) es-module-lexer: 1.7.0 pathe: 2.0.3 - vite: 7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) + vite: 7.3.0(@types/node@24.10.4)(jiti@1.21.7)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) transitivePeerDependencies: - '@types/node' - jiti @@ -33670,7 +33451,7 @@ snapshots: vite-node@3.2.4(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2): dependencies: cac: 6.7.14 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3(supports-color@10.2.2) es-module-lexer: 1.7.0 pathe: 2.0.3 vite: 7.3.0(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) @@ -33690,7 +33471,7 @@ snapshots: vite-tsconfig-paths@5.1.4(typescript@5.9.3)(vite@7.3.0(@types/node@22.19.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)): dependencies: - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3(supports-color@10.2.2) globrex: 0.1.2 tsconfck: 3.1.6(typescript@5.9.3) optionalDependencies: @@ -33807,7 +33588,7 @@ snapshots: '@vitest/spy': 2.1.9 '@vitest/utils': 2.1.9 chai: 5.3.3 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3(supports-color@10.2.2) expect-type: 1.3.0 magic-string: 0.30.21 pathe: 1.1.2 @@ -33844,7 +33625,7 @@ snapshots: '@vitest/spy': 3.2.4 '@vitest/utils': 3.2.4 chai: 5.3.3 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3(supports-color@10.2.2) expect-type: 1.3.0 magic-string: 0.30.21 pathe: 2.0.3 @@ -33876,18 +33657,18 @@ snapshots: - tsx - yaml - vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.10.4)(jiti@2.6.1)(jsdom@27.3.0)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2): + vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.10.4)(jiti@1.21.7)(jsdom@27.3.0)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2): dependencies: '@types/chai': 5.2.3 '@vitest/expect': 3.2.4 - '@vitest/mocker': 3.2.4(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)) + '@vitest/mocker': 3.2.4(vite@7.3.0(@types/node@24.10.4)(jiti@1.21.7)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)) '@vitest/pretty-format': 3.2.4 '@vitest/runner': 3.2.4 '@vitest/snapshot': 3.2.4 '@vitest/spy': 3.2.4 '@vitest/utils': 3.2.4 chai: 5.3.3 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3(supports-color@10.2.2) expect-type: 1.3.0 magic-string: 0.30.21 pathe: 2.0.3 @@ -33898,8 +33679,8 @@ snapshots: tinyglobby: 0.2.15 tinypool: 1.1.1 tinyrainbow: 2.0.0 - vite: 7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) - vite-node: 3.2.4(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) + vite: 7.3.0(@types/node@24.10.4)(jiti@1.21.7)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) + vite-node: 3.2.4(@types/node@24.10.4)(jiti@1.21.7)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) why-is-node-running: 2.3.0 optionalDependencies: '@types/debug': 4.1.12 @@ -33930,7 +33711,7 @@ snapshots: '@vitest/spy': 3.2.4 '@vitest/utils': 3.2.4 chai: 5.3.3 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3(supports-color@10.2.2) expect-type: 1.3.0 magic-string: 0.30.21 pathe: 2.0.3 @@ -33989,7 +33770,7 @@ snapshots: dependencies: chalk: 4.1.2 commander: 9.5.0 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3(supports-color@10.2.2) transitivePeerDependencies: - supports-color @@ -34017,7 +33798,7 @@ snapshots: '@wdio/types': 9.20.0 '@wdio/utils': 9.21.0 deepmerge-ts: 7.1.5 - https-proxy-agent: 7.0.6 + https-proxy-agent: 7.0.6(supports-color@10.2.2) undici: 6.22.0 ws: 8.18.3 transitivePeerDependencies: From d6bb4e7ce9267135945d0a80411ed2b129ee51f7 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Sun, 4 Jan 2026 04:57:28 +0000 Subject: [PATCH 2/5] fix: resolve TypeScript errors in human persister - Fix Content return type using 'as unknown as' pattern - Add missing DirEntry properties (isFile, isSymlink) in test mocks - Fix exists mock to accept string | URL parameter type - Remove unused asTableChanges import Co-Authored-By: yujonglee --- .../persister/human/persister.test.ts | 28 +++++++++++++++---- .../tinybase/persister/human/persister.ts | 8 ++---- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/apps/desktop/src/store/tinybase/persister/human/persister.test.ts b/apps/desktop/src/store/tinybase/persister/human/persister.test.ts index 2ba24029d8..a962880130 100644 --- a/apps/desktop/src/store/tinybase/persister/human/persister.test.ts +++ b/apps/desktop/src/store/tinybase/persister/human/persister.test.ts @@ -57,7 +57,12 @@ describe("createHumanPersister", () => { vi.mocked(exists).mockResolvedValue(false); vi.mocked(readDir).mockResolvedValue([ - { name: `${HUMAN_UUID_1}.md`, isDirectory: false }, + { + name: `${HUMAN_UUID_1}.md`, + isDirectory: false, + isFile: true, + isSymlink: false, + }, ]); const mockMdContent = serializeMarkdownWithFrontmatter( @@ -112,8 +117,18 @@ describe("createHumanPersister", () => { vi.mocked(exists).mockResolvedValue(false); vi.mocked(readDir).mockResolvedValue([ - { name: "not-a-uuid.md", isDirectory: false }, - { name: `${HUMAN_UUID_1}.md`, isDirectory: false }, + { + name: "not-a-uuid.md", + isDirectory: false, + isFile: true, + isSymlink: false, + }, + { + name: `${HUMAN_UUID_1}.md`, + isDirectory: false, + isFile: true, + isSymlink: false, + }, ]); const mockMdContent = serializeMarkdownWithFrontmatter( @@ -238,9 +253,10 @@ describe("createHumanPersister", () => { const { exists, readTextFile, writeTextFile, mkdir, remove } = await import("@tauri-apps/plugin-fs"); - vi.mocked(exists).mockImplementation(async (path: string) => { - if (path.endsWith("humans.json")) return true; - if (path.endsWith("humans")) return false; + vi.mocked(exists).mockImplementation(async (path: string | URL) => { + const p = typeof path === "string" ? path : path.toString(); + if (p.endsWith("humans.json")) return true; + if (p.endsWith("humans")) return false; return false; }); diff --git a/apps/desktop/src/store/tinybase/persister/human/persister.ts b/apps/desktop/src/store/tinybase/persister/human/persister.ts index eee6d98031..1e8788d9ab 100644 --- a/apps/desktop/src/store/tinybase/persister/human/persister.ts +++ b/apps/desktop/src/store/tinybase/persister/human/persister.ts @@ -4,11 +4,7 @@ import type { OptionalSchemas, } from "tinybase/with-schemas"; -import { - asTableChanges, - createSessionDirPersister, - getDataDir, -} from "../utils"; +import { createSessionDirPersister, getDataDir } from "../utils"; import { collectHumanWriteOps, type HumanCollectorResult } from "./collect"; import { cleanupOrphanHumanFiles, loadAllHumans } from "./load"; import { migrateHumansJsonIfNeeded } from "./migrate"; @@ -26,7 +22,7 @@ export function createHumanPersister( if (Object.keys(humans).length === 0) { return undefined; } - return asTableChanges("humans", humans) as Content; + return [{ humans }, {}] as unknown as Content; }, postSave: async (dataDir, result) => { const { validHumanIds } = result as HumanCollectorResult; From 86c2996dfb0a79955bd31da961260260af6dbfdf Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Sun, 4 Jan 2026 06:19:59 +0000 Subject: [PATCH 3/5] refactor: use frontmatter crate via export plugin for human persister - Add frontmatter crate as dependency to export plugin - Add parse_frontmatter, serialize_frontmatter, export_frontmatter, and export_frontmatter_batch commands - Update human persister to use plugin commands instead of yaml npm package - Add frontmatter-batch operation type to createSessionDirPersister - Remove yaml npm dependency from desktop package.json - Update tests to mock new plugin commands Co-Authored-By: yujonglee --- Cargo.lock | 1 + apps/desktop/package.json | 1 - .../store/tinybase/persister/human/collect.ts | 21 +- .../store/tinybase/persister/human/load.ts | 2 +- .../store/tinybase/persister/human/migrate.ts | 33 ++-- .../persister/human/persister.test.ts | 185 ++++++++++++------ .../store/tinybase/persister/human/utils.ts | 36 ++-- .../src/store/tinybase/persister/utils.ts | 18 +- plugins/export/Cargo.toml | 1 + plugins/export/js/bindings.gen.ts | 48 +++++ plugins/export/src/commands.rs | 79 ++++++++ plugins/export/src/lib.rs | 4 + 12 files changed, 316 insertions(+), 113 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a0efbd425a..aa36c3270b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -18020,6 +18020,7 @@ name = "tauri-plugin-export" version = "0.1.0" dependencies = [ "criterion", + "frontmatter", "insta", "markdown", "mdast_util_to_markdown", diff --git a/apps/desktop/package.json b/apps/desktop/package.json index c90b8c5a59..be92273676 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -125,7 +125,6 @@ "vfile": "^6.0.3", "wavesurfer.js": "^7.12.1", "xstate": "^5.25.0", - "yaml": "^2.8.2", "zod": "^4.2.1", "zustand": "^5.0.9" }, diff --git a/apps/desktop/src/store/tinybase/persister/human/collect.ts b/apps/desktop/src/store/tinybase/persister/human/collect.ts index 4a5761a0ec..2177adad1a 100644 --- a/apps/desktop/src/store/tinybase/persister/human/collect.ts +++ b/apps/desktop/src/store/tinybase/persister/human/collect.ts @@ -1,13 +1,10 @@ import type { MergeableStore, OptionalSchemas } from "tinybase/with-schemas"; +import type { FrontmatterInput, JsonValue } from "@hypr/plugin-export"; import type { HumanStorage } from "@hypr/store"; import type { CollectorResult, TablesContent } from "../utils"; -import { - getHumanDir, - getHumanFilePath, - serializeMarkdownWithFrontmatter, -} from "./utils"; +import { getHumanDir, getHumanFilePath } from "./utils"; export interface HumanCollectorResult extends CollectorResult { validHumanIds: Set; @@ -29,12 +26,14 @@ export function collectHumanWriteOps( const humans = (tables as { humans?: HumansTable }).humans ?? {}; + const frontmatterItems: [FrontmatterInput, string][] = []; + for (const [humanId, human] of Object.entries(humans)) { validHumanIds.add(humanId); const { memo, ...frontmatterFields } = human; - const frontmatter: Record = { + const frontmatter: Record = { user_id: frontmatterFields.user_id ?? "", created_at: frontmatterFields.created_at ?? "", name: frontmatterFields.name ?? "", @@ -45,13 +44,15 @@ export function collectHumanWriteOps( }; const body = memo ?? ""; - const content = serializeMarkdownWithFrontmatter(frontmatter, body); const filePath = getHumanFilePath(dataDir, humanId); + frontmatterItems.push([{ frontmatter, content: body }, filePath]); + } + + if (frontmatterItems.length > 0) { operations.push({ - type: "text", - path: filePath, - content, + type: "frontmatter-batch", + items: frontmatterItems, }); } diff --git a/apps/desktop/src/store/tinybase/persister/human/load.ts b/apps/desktop/src/store/tinybase/persister/human/load.ts index 30ee7c1ae8..bdaa28d0b5 100644 --- a/apps/desktop/src/store/tinybase/persister/human/load.ts +++ b/apps/desktop/src/store/tinybase/persister/human/load.ts @@ -38,7 +38,7 @@ export async function loadAllHumans( try { const filePath = getHumanFilePath(dataDir, humanId); const content = await readTextFile(filePath); - const { frontmatter, body } = parseMarkdownWithFrontmatter(content); + const { frontmatter, body } = await parseMarkdownWithFrontmatter(content); result[humanId] = { user_id: String(frontmatter.user_id ?? ""), diff --git a/apps/desktop/src/store/tinybase/persister/human/migrate.ts b/apps/desktop/src/store/tinybase/persister/human/migrate.ts index a589ea5f19..14d48dc5eb 100644 --- a/apps/desktop/src/store/tinybase/persister/human/migrate.ts +++ b/apps/desktop/src/store/tinybase/persister/human/migrate.ts @@ -1,20 +1,15 @@ import { sep } from "@tauri-apps/api/path"; -import { - exists, - mkdir, - readTextFile, - remove, - writeTextFile, -} from "@tauri-apps/plugin-fs"; +import { exists, mkdir, readTextFile, remove } from "@tauri-apps/plugin-fs"; +import { + commands as exportCommands, + type FrontmatterInput, + type JsonValue, +} from "@hypr/plugin-export"; import type { HumanStorage } from "@hypr/store"; import { isFileNotFoundError } from "../utils"; -import { - getHumanDir, - getHumanFilePath, - serializeMarkdownWithFrontmatter, -} from "./utils"; +import { getHumanDir, getHumanFilePath } from "./utils"; export async function migrateHumansJsonIfNeeded( dataDir: string, @@ -40,10 +35,12 @@ export async function migrateHumansJsonIfNeeded( await mkdir(humansDir, { recursive: true }); + const batchItems: [FrontmatterInput, string][] = []; + for (const [humanId, human] of Object.entries(humans)) { const { memo, ...frontmatterFields } = human; - const frontmatter: Record = { + const frontmatter: Record = { user_id: frontmatterFields.user_id ?? "", created_at: frontmatterFields.created_at ?? "", name: frontmatterFields.name ?? "", @@ -54,10 +51,16 @@ export async function migrateHumansJsonIfNeeded( }; const body = memo ?? ""; - const mdContent = serializeMarkdownWithFrontmatter(frontmatter, body); const filePath = getHumanFilePath(dataDir, humanId); - await writeTextFile(filePath, mdContent); + batchItems.push([{ frontmatter, content: body }, filePath]); + } + + if (batchItems.length > 0) { + const result = await exportCommands.exportFrontmatterBatch(batchItems); + if (result.status === "error") { + throw new Error(`Failed to export migrated humans: ${result.error}`); + } } await remove(humansJsonPath); diff --git a/apps/desktop/src/store/tinybase/persister/human/persister.test.ts b/apps/desktop/src/store/tinybase/persister/human/persister.test.ts index a962880130..6e3647e4be 100644 --- a/apps/desktop/src/store/tinybase/persister/human/persister.test.ts +++ b/apps/desktop/src/store/tinybase/persister/human/persister.test.ts @@ -4,10 +4,7 @@ import { beforeEach, describe, expect, test, vi } from "vitest"; import { SCHEMA, type Schemas } from "@hypr/store"; import { createHumanPersister } from "./persister"; -import { - parseMarkdownWithFrontmatter, - serializeMarkdownWithFrontmatter, -} from "./utils"; +import { parseMarkdownWithFrontmatter } from "./utils"; vi.mock("@hypr/plugin-path2", () => ({ commands: { @@ -15,6 +12,13 @@ vi.mock("@hypr/plugin-path2", () => ({ }, })); +vi.mock("@hypr/plugin-export", () => ({ + commands: { + parseFrontmatter: vi.fn(), + exportFrontmatterBatch: vi.fn().mockResolvedValue({ status: "ok", data: null }), + }, +})); + vi.mock("@tauri-apps/plugin-fs", () => ({ mkdir: vi.fn().mockResolvedValue(undefined), readDir: vi.fn(), @@ -24,6 +28,14 @@ vi.mock("@tauri-apps/plugin-fs", () => ({ remove: vi.fn().mockResolvedValue(undefined), })); +function serializeFrontmatterSync( + frontmatter: Record, + body: string, +): string { + const lines = Object.entries(frontmatter).map(([k, v]) => `${k}: ${v}`); + return `---\n${lines.join("\n")}\n---\n\n${body}`; +} + function createTestStore() { return createMergeableStore() .setTablesSchema(SCHEMA.table) @@ -54,6 +66,7 @@ describe("createHumanPersister", () => { test("loads humans from markdown files", async () => { const { readDir, readTextFile, exists } = await import("@tauri-apps/plugin-fs"); + const { commands: exportCommands } = await import("@hypr/plugin-export"); vi.mocked(exists).mockResolvedValue(false); vi.mocked(readDir).mockResolvedValue([ @@ -65,7 +78,7 @@ describe("createHumanPersister", () => { }, ]); - const mockMdContent = serializeMarkdownWithFrontmatter( + const mockMdContent = serializeFrontmatterSync( { user_id: "user-1", created_at: "2024-01-01T00:00:00Z", @@ -78,6 +91,21 @@ describe("createHumanPersister", () => { "Some notes", ); vi.mocked(readTextFile).mockResolvedValue(mockMdContent); + vi.mocked(exportCommands.parseFrontmatter).mockResolvedValue({ + status: "ok", + data: { + frontmatter: { + user_id: "user-1", + created_at: "2024-01-01T00:00:00Z", + name: "John Doe", + email: "john@example.com", + org_id: "org-1", + job_title: "Engineer", + linkedin_username: "johndoe", + }, + content: "Some notes", + }, + }); const persister = createHumanPersister(store); await persister.load(); @@ -114,6 +142,7 @@ describe("createHumanPersister", () => { test("skips non-UUID files", async () => { const { readDir, readTextFile, exists } = await import("@tauri-apps/plugin-fs"); + const { commands: exportCommands } = await import("@hypr/plugin-export"); vi.mocked(exists).mockResolvedValue(false); vi.mocked(readDir).mockResolvedValue([ @@ -131,7 +160,7 @@ describe("createHumanPersister", () => { }, ]); - const mockMdContent = serializeMarkdownWithFrontmatter( + const mockMdContent = serializeFrontmatterSync( { user_id: "user-1", created_at: "2024-01-01T00:00:00Z", @@ -144,6 +173,21 @@ describe("createHumanPersister", () => { "", ); vi.mocked(readTextFile).mockResolvedValue(mockMdContent); + vi.mocked(exportCommands.parseFrontmatter).mockResolvedValue({ + status: "ok", + data: { + frontmatter: { + user_id: "user-1", + created_at: "2024-01-01T00:00:00Z", + name: "John Doe", + email: "john@example.com", + org_id: "", + job_title: "", + linkedin_username: "", + }, + content: "", + }, + }); const persister = createHumanPersister(store); await persister.load(); @@ -155,8 +199,9 @@ describe("createHumanPersister", () => { }); describe("save", () => { - test("saves humans to markdown files", async () => { - const { writeTextFile, mkdir } = await import("@tauri-apps/plugin-fs"); + test("saves humans to markdown files via export plugin", async () => { + const { mkdir } = await import("@tauri-apps/plugin-fs"); + const { commands: exportCommands } = await import("@hypr/plugin-export"); store.setRow("humans", HUMAN_UUID_1, { user_id: "user-1", @@ -176,38 +221,36 @@ describe("createHumanPersister", () => { recursive: true, }); - expect(writeTextFile).toHaveBeenCalledWith( - `/mock/data/dir/hyprnote/humans/${HUMAN_UUID_1}.md`, - expect.any(String), - ); - - const writtenContent = vi.mocked(writeTextFile).mock.calls[0][1]; - const { frontmatter, body } = - parseMarkdownWithFrontmatter(writtenContent); - - expect(frontmatter).toEqual({ - user_id: "user-1", - created_at: "2024-01-01T00:00:00Z", - name: "John Doe", - email: "john@example.com", - org_id: "org-1", - job_title: "Engineer", - linkedin_username: "johndoe", - }); - expect(body).toBe("Some notes"); + expect(exportCommands.exportFrontmatterBatch).toHaveBeenCalledWith([ + [ + { + frontmatter: { + user_id: "user-1", + created_at: "2024-01-01T00:00:00Z", + name: "John Doe", + email: "john@example.com", + org_id: "org-1", + job_title: "Engineer", + linkedin_username: "johndoe", + }, + content: "Some notes", + }, + `/mock/data/dir/hyprnote/humans/${HUMAN_UUID_1}.md`, + ], + ]); }); test("does not write when no humans exist", async () => { - const { writeTextFile } = await import("@tauri-apps/plugin-fs"); + const { commands: exportCommands } = await import("@hypr/plugin-export"); const persister = createHumanPersister(store); await persister.save(); - expect(writeTextFile).not.toHaveBeenCalled(); + expect(exportCommands.exportFrontmatterBatch).not.toHaveBeenCalled(); }); - test("saves multiple humans to separate files", async () => { - const { writeTextFile } = await import("@tauri-apps/plugin-fs"); + test("saves multiple humans in single batch call", async () => { + const { commands: exportCommands } = await import("@hypr/plugin-export"); store.setRow("humans", HUMAN_UUID_1, { user_id: "user-1", @@ -234,15 +277,17 @@ describe("createHumanPersister", () => { const persister = createHumanPersister(store); await persister.save(); - expect(writeTextFile).toHaveBeenCalledTimes(2); + expect(exportCommands.exportFrontmatterBatch).toHaveBeenCalledTimes(1); + + const batchItems = vi.mocked(exportCommands.exportFrontmatterBatch).mock + .calls[0][0]; + expect(batchItems).toHaveLength(2); - const writtenPaths = vi - .mocked(writeTextFile) - .mock.calls.map((call) => call[0]); - expect(writtenPaths).toContain( + const paths = batchItems.map((item: [unknown, string]) => item[1]); + expect(paths).toContain( `/mock/data/dir/hyprnote/humans/${HUMAN_UUID_1}.md`, ); - expect(writtenPaths).toContain( + expect(paths).toContain( `/mock/data/dir/hyprnote/humans/${HUMAN_UUID_2}.md`, ); }); @@ -250,8 +295,9 @@ describe("createHumanPersister", () => { describe("migration", () => { test("migrates from humans.json when it exists and humans dir does not", async () => { - const { exists, readTextFile, writeTextFile, mkdir, remove } = + const { exists, readTextFile, mkdir, remove } = await import("@tauri-apps/plugin-fs"); + const { commands: exportCommands } = await import("@hypr/plugin-export"); vi.mocked(exists).mockImplementation(async (path: string | URL) => { const p = typeof path === "string" ? path : path.toString(); @@ -280,10 +326,23 @@ describe("createHumanPersister", () => { expect(mkdir).toHaveBeenCalledWith("/mock/data/dir/hyprnote/humans", { recursive: true, }); - expect(writeTextFile).toHaveBeenCalledWith( - `/mock/data/dir/hyprnote/humans/${HUMAN_UUID_1}.md`, - expect.any(String), - ); + expect(exportCommands.exportFrontmatterBatch).toHaveBeenCalledWith([ + [ + { + frontmatter: { + user_id: "user-1", + created_at: "2024-01-01T00:00:00Z", + name: "John Doe", + email: "john@example.com", + org_id: "org-1", + job_title: "Engineer", + linkedin_username: "johndoe", + }, + content: "Some notes", + }, + `/mock/data/dir/hyprnote/humans/${HUMAN_UUID_1}.md`, + ], + ]); expect(remove).toHaveBeenCalledWith( "/mock/data/dir/hyprnote/humans.json", ); @@ -293,7 +352,20 @@ describe("createHumanPersister", () => { describe("utils", () => { describe("parseMarkdownWithFrontmatter", () => { - test("parses markdown with frontmatter", () => { + test("parses markdown with frontmatter via plugin", async () => { + const { commands: exportCommands } = await import("@hypr/plugin-export"); + + vi.mocked(exportCommands.parseFrontmatter).mockResolvedValue({ + status: "ok", + data: { + frontmatter: { + name: "John Doe", + email: "john@example.com", + }, + content: "Some notes about John", + }, + }); + const content = `--- name: John Doe email: john@example.com @@ -301,7 +373,7 @@ email: john@example.com Some notes about John`; - const { frontmatter, body } = parseMarkdownWithFrontmatter(content); + const { frontmatter, body } = await parseMarkdownWithFrontmatter(content); expect(frontmatter).toEqual({ name: "John Doe", @@ -310,27 +382,20 @@ Some notes about John`; expect(body).toBe("Some notes about John"); }); - test("returns empty frontmatter for content without frontmatter", () => { + test("returns empty frontmatter when plugin returns error", async () => { + const { commands: exportCommands } = await import("@hypr/plugin-export"); + + vi.mocked(exportCommands.parseFrontmatter).mockResolvedValue({ + status: "error", + error: "Parse error", + }); + const content = "Just some text without frontmatter"; - const { frontmatter, body } = parseMarkdownWithFrontmatter(content); + const { frontmatter, body } = await parseMarkdownWithFrontmatter(content); expect(frontmatter).toEqual({}); expect(body).toBe("Just some text without frontmatter"); }); }); - - describe("serializeMarkdownWithFrontmatter", () => { - test("serializes frontmatter and body", () => { - const frontmatter = { name: "John Doe", email: "john@example.com" }; - const body = "Some notes"; - - const result = serializeMarkdownWithFrontmatter(frontmatter, body); - - expect(result).toContain("---"); - expect(result).toContain("name: John Doe"); - expect(result).toContain("email: john@example.com"); - expect(result).toContain("Some notes"); - }); - }); }); diff --git a/apps/desktop/src/store/tinybase/persister/human/utils.ts b/apps/desktop/src/store/tinybase/persister/human/utils.ts index bab01c062f..dc1129377b 100644 --- a/apps/desktop/src/store/tinybase/persister/human/utils.ts +++ b/apps/desktop/src/store/tinybase/persister/human/utils.ts @@ -1,37 +1,23 @@ import { sep } from "@tauri-apps/api/path"; -import { parse as parseYaml, stringify as stringifyYaml } from "yaml"; +import { commands } from "@hypr/plugin-export"; export interface ParsedMarkdown { frontmatter: Record; body: string; } -export function parseMarkdownWithFrontmatter(content: string): ParsedMarkdown { - const frontmatterRegex = /^---\n([\s\S]*?)\n---\n?([\s\S]*)$/; - const match = content.match(frontmatterRegex); - - if (!match) { +export async function parseMarkdownWithFrontmatter( + content: string, +): Promise { + const result = await commands.parseFrontmatter(content); + if (result.status === "error") { + console.error("[HumanPersister] Failed to parse frontmatter:", result.error); return { frontmatter: {}, body: content }; } - - const [, yamlContent, body] = match; - - let frontmatter: Record = {}; - try { - frontmatter = parseYaml(yamlContent) ?? {}; - } catch { - frontmatter = {}; - } - - return { frontmatter, body: body.trim() }; -} - -export function serializeMarkdownWithFrontmatter( - frontmatter: Record, - body: string, -): string { - const yamlContent = stringifyYaml(frontmatter, { lineWidth: 0 }).trim(); - return `---\n${yamlContent}\n---\n\n${body}`; + return { + frontmatter: result.data.frontmatter as Record, + body: result.data.content.trim(), + }; } export function getHumanDir(dataDir: string): string { diff --git a/apps/desktop/src/store/tinybase/persister/utils.ts b/apps/desktop/src/store/tinybase/persister/utils.ts index 57ed0d62e6..8e2244b04f 100644 --- a/apps/desktop/src/store/tinybase/persister/utils.ts +++ b/apps/desktop/src/store/tinybase/persister/utils.ts @@ -9,6 +9,7 @@ import type { import { commands as exportCommands, + type FrontmatterInput, type JsonValue as ExportJsonValue, } from "@hypr/plugin-export"; import { events as notifyEvents } from "@hypr/plugin-notify"; @@ -227,7 +228,8 @@ export function createModeAwarePersister( export type WriteOperation = | { type: "json"; path: string; content: unknown } | { type: "md-batch"; items: Array<[ExportJsonValue, string]> } - | { type: "text"; path: string; content: string }; + | { type: "text"; path: string; content: string } + | { type: "frontmatter-batch"; items: Array<[FrontmatterInput, string]> }; export type CollectorResult = { dirs: Set; @@ -264,6 +266,7 @@ export function createSessionDirPersister( const jsonBatchItems: Array<[ExportJsonValue, string]> = []; let mdBatchItems: Array<[ExportJsonValue, string]> = []; + let frontmatterBatchItems: Array<[FrontmatterInput, string]> = []; const textItems: Array<{ path: string; content: string }> = []; for (const op of operations) { @@ -271,6 +274,8 @@ export function createSessionDirPersister( jsonBatchItems.push([op.content as ExportJsonValue, op.path]); } else if (op.type === "md-batch") { mdBatchItems = mdBatchItems.concat(op.items); + } else if (op.type === "frontmatter-batch") { + frontmatterBatchItems = frontmatterBatchItems.concat(op.items); } else if (op.type === "text") { textItems.push({ path: op.path, content: op.content }); } @@ -298,6 +303,17 @@ export function createSessionDirPersister( } } + if (frontmatterBatchItems.length > 0) { + const exportResult = + await exportCommands.exportFrontmatterBatch(frontmatterBatchItems); + if (exportResult.status === "error") { + console.error( + `[${options.label}] Failed to export frontmatter batch:`, + exportResult.error, + ); + } + } + for (const item of textItems) { try { await writeTextFile(item.path, item.content); diff --git a/plugins/export/Cargo.toml b/plugins/export/Cargo.toml index 6519858a09..33e83ae279 100644 --- a/plugins/export/Cargo.toml +++ b/plugins/export/Cargo.toml @@ -21,6 +21,7 @@ specta-typescript = { workspace = true } tokio = { workspace = true, features = ["macros"] } [dependencies] +frontmatter = { path = "../../crates/frontmatter" } markdown = { workspace = true } mdast_util_to_markdown = { workspace = true } diff --git a/plugins/export/js/bindings.gen.ts b/plugins/export/js/bindings.gen.ts index 7cab2ad85e..5ef1a2f26a 100644 --- a/plugins/export/js/bindings.gen.ts +++ b/plugins/export/js/bindings.gen.ts @@ -37,6 +37,52 @@ async exportTiptapJsonToMdBatch(items: ([JsonValue, string])[]) : Promise> { + try { + return { status: "ok", data: await TAURI_INVOKE("plugin:export|parse_frontmatter", { markdown }) }; +} catch (e) { + if(e instanceof Error) throw e; + else return { status: "error", error: e as any }; +} +}, +/** + * Serialize frontmatter and content into markdown with YAML frontmatter. + */ +async serializeFrontmatter(input: FrontmatterInput) : Promise> { + try { + return { status: "ok", data: await TAURI_INVOKE("plugin:export|serialize_frontmatter", { input }) }; +} catch (e) { + if(e instanceof Error) throw e; + else return { status: "error", error: e as any }; +} +}, +/** + * Write markdown with frontmatter to a file. + */ +async exportFrontmatter(input: FrontmatterInput, path: string) : Promise> { + try { + return { status: "ok", data: await TAURI_INVOKE("plugin:export|export_frontmatter", { input, path }) }; +} catch (e) { + if(e instanceof Error) throw e; + else return { status: "error", error: e as any }; +} +}, +/** + * Batch write multiple markdown files with frontmatter. + */ +async exportFrontmatterBatch(items: ([FrontmatterInput, string])[]) : Promise> { + try { + return { status: "ok", data: await TAURI_INVOKE("plugin:export|export_frontmatter_batch", { items }) }; +} catch (e) { + if(e instanceof Error) throw e; + else return { status: "error", error: e as any }; +} } } @@ -50,7 +96,9 @@ async exportTiptapJsonToMdBatch(items: ([JsonValue, string])[]) : Promise; content: string } export type JsonValue = null | boolean | number | string | JsonValue[] | Partial<{ [key in string]: JsonValue }> +export type ParsedFrontmatter = { frontmatter: Partial<{ [key in string]: JsonValue }>; content: string } /** tauri-specta globals **/ diff --git a/plugins/export/src/commands.rs b/plugins/export/src/commands.rs index 9f5f8980ef..f39cf94298 100644 --- a/plugins/export/src/commands.rs +++ b/plugins/export/src/commands.rs @@ -1,5 +1,8 @@ +use frontmatter::Document; use rayon::prelude::*; +use serde::{Deserialize, Serialize}; use serde_json::Value; +use std::collections::HashMap; /// For many small files with frequent writes, sync I/O with rayon parallelism /// is more efficient than async I/O (avoids per-file async task overhead). @@ -12,6 +15,18 @@ macro_rules! spawn_blocking { }; } +#[derive(Debug, Clone, Serialize, Deserialize, specta::Type)] +pub struct ParsedFrontmatter { + pub frontmatter: HashMap, + pub content: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize, specta::Type)] +pub struct FrontmatterInput { + pub frontmatter: HashMap, + pub content: String, +} + #[tauri::command] #[specta::specta] pub(crate) async fn export_json(json: Value, path: String) -> Result<(), String> { @@ -56,3 +71,67 @@ pub(crate) async fn export_tiptap_json_to_md_batch( }) }) } + +/// Parse markdown with YAML frontmatter into structured data. +/// Returns frontmatter as a HashMap and the content body. +/// If the markdown has no frontmatter, returns empty frontmatter and the full content as body. +#[tauri::command] +#[specta::specta] +pub(crate) async fn parse_frontmatter(markdown: String) -> Result { + spawn_blocking!({ + match Document::>::from_str(&markdown) { + Ok(doc) => Ok(ParsedFrontmatter { + frontmatter: doc.frontmatter, + content: doc.content, + }), + Err(frontmatter::Error::MissingOpeningDelimiter) + | Err(frontmatter::Error::MissingClosingDelimiter) => { + // No frontmatter, treat entire content as body + Ok(ParsedFrontmatter { + frontmatter: HashMap::new(), + content: markdown, + }) + } + Err(e) => Err(e.to_string()), + } + }) +} + +/// Serialize frontmatter and content into markdown with YAML frontmatter. +#[tauri::command] +#[specta::specta] +pub(crate) async fn serialize_frontmatter(input: FrontmatterInput) -> Result { + spawn_blocking!({ + let doc = Document::new(input.frontmatter, input.content); + doc.to_string().map_err(|e| e.to_string()) + }) +} + +/// Write markdown with frontmatter to a file. +#[tauri::command] +#[specta::specta] +pub(crate) async fn export_frontmatter( + input: FrontmatterInput, + path: String, +) -> Result<(), String> { + spawn_blocking!({ + let doc = Document::new(input.frontmatter, input.content); + let content = doc.to_string().map_err(|e| e.to_string())?; + std::fs::write(path, content).map_err(|e| e.to_string()) + }) +} + +/// Batch write multiple markdown files with frontmatter. +#[tauri::command] +#[specta::specta] +pub(crate) async fn export_frontmatter_batch( + items: Vec<(FrontmatterInput, String)>, +) -> Result<(), String> { + spawn_blocking!({ + items.into_par_iter().try_for_each(|(input, path)| { + let doc = Document::new(input.frontmatter, input.content); + let content = doc.to_string().map_err(|e| e.to_string())?; + std::fs::write(path, content).map_err(|e| e.to_string()) + }) + }) +} diff --git a/plugins/export/src/lib.rs b/plugins/export/src/lib.rs index 3423d31d8b..5b43d902a5 100644 --- a/plugins/export/src/lib.rs +++ b/plugins/export/src/lib.rs @@ -18,6 +18,10 @@ fn make_specta_builder() -> tauri_specta::Builder { commands::export_json_batch, commands::export_tiptap_json_to_md, commands::export_tiptap_json_to_md_batch, + commands::parse_frontmatter, + commands::serialize_frontmatter, + commands::export_frontmatter, + commands::export_frontmatter_batch, ]) .error_handling(tauri_specta::ErrorHandlingMode::Result) } From 9e63339d8bf5cc705505ea1bfaf32868ecd75a32 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Sun, 4 Jan 2026 06:21:37 +0000 Subject: [PATCH 4/5] chore: update pnpm-lock.yaml after removing yaml dependency Co-Authored-By: yujonglee --- pnpm-lock.yaml | 395 ++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 307 insertions(+), 88 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8beb89751f..88522022ba 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -175,7 +175,7 @@ importers: version: 19.2.3(@types/react@19.2.7) '@vitejs/plugin-react': specifier: ^4.7.0 - version: 4.7.0(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)) + version: 4.7.0(vite@7.3.0(@types/node@24.10.4)(jiti@1.21.7)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)) autoprefixer: specifier: ^10.4.23 version: 10.4.23(postcss@8.5.6) @@ -190,7 +190,7 @@ importers: version: 5.8.3 vite: specifier: ^7.3.0 - version: 7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) + version: 7.3.0(@types/node@24.10.4)(jiti@1.21.7)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) apps/desktop: dependencies: @@ -524,9 +524,6 @@ importers: xstate: specifier: ^5.25.0 version: 5.25.0 - yaml: - specifier: ^2.8.2 - version: 2.8.2 zod: specifier: ^4.2.1 version: 4.2.1 @@ -545,7 +542,7 @@ importers: version: 1.142.8(@tanstack/react-router@1.142.8(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(@tanstack/router-core@1.142.8)(csstype@3.2.3)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(solid-js@1.9.10) '@tanstack/router-plugin': specifier: ^1.142.8 - version: 1.142.8(@tanstack/react-router@1.142.8(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(vite@7.3.0(@types/node@24.10.4)(jiti@1.21.7)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)) + version: 1.142.8(@tanstack/react-router@1.142.8(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)) '@tauri-apps/cli': specifier: ^2.9.6 version: 2.9.6 @@ -572,7 +569,7 @@ importers: version: 2.0.3 '@vitejs/plugin-react': specifier: ^4.7.0 - version: 4.7.0(vite@7.3.0(@types/node@24.10.4)(jiti@1.21.7)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)) + version: 4.7.0(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)) autoprefixer: specifier: ^10.4.23 version: 10.4.23(postcss@8.5.6) @@ -593,10 +590,10 @@ importers: version: 5.8.3 vite: specifier: ^7.3.0 - version: 7.3.0(@types/node@24.10.4)(jiti@1.21.7)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) + version: 7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) vitest: specifier: ^3.2.4 - version: 3.2.4(@types/debug@4.1.12)(@types/node@24.10.4)(jiti@1.21.7)(jsdom@27.3.0)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) + version: 3.2.4(@types/debug@4.1.12)(@types/node@24.10.4)(jiti@2.6.1)(jsdom@27.3.0)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) apps/howto: dependencies: @@ -16556,14 +16553,14 @@ snapshots: '@apm-js-collab/tracing-hooks@0.3.1': dependencies: '@apm-js-collab/code-transformer': 0.8.2 - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3(supports-color@8.1.1) module-details-from-path: 1.0.4 transitivePeerDependencies: - supports-color '@argos-ci/api-client@0.15.0': dependencies: - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3(supports-color@8.1.1) openapi-fetch: 0.15.0 transitivePeerDependencies: - supports-color @@ -16575,7 +16572,7 @@ snapshots: '@argos-ci/api-client': 0.15.0 '@argos-ci/util': 3.2.0 convict: 6.2.4 - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3(supports-color@8.1.1) fast-glob: 3.3.3 mime-types: 3.0.2 sharp: 0.34.5 @@ -16589,7 +16586,7 @@ snapshots: '@argos-ci/core': 5.0.3 '@argos-ci/util': 3.2.0 chalk: 5.6.2 - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -16666,7 +16663,7 @@ snapshots: dependencies: '@astrojs/internal-helpers': 0.7.5 '@astrojs/underscore-redirects': 1.0.0 - '@netlify/blobs': 10.5.0(supports-color@10.2.2) + '@netlify/blobs': 10.5.0 '@netlify/functions': 5.1.2 '@netlify/vite-plugin': 2.7.19(@netlify/api@14.0.12)(aws4fetch@1.0.20)(babel-plugin-macros@3.1.0)(ioredis@5.8.2)(rollup@4.54.0)(vite@6.4.1(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)) '@vercel/nft': 0.30.4(rollup@4.54.0) @@ -16742,7 +16739,7 @@ snapshots: '@astrojs/telemetry@3.3.0': dependencies: ci-info: 4.3.1 - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3(supports-color@8.1.1) dlv: 1.1.3 dset: 3.1.4 is-docker: 3.0.0 @@ -16797,7 +16794,7 @@ snapshots: '@babel/types': 7.28.5 '@jridgewell/remapping': 2.3.5 convert-source-map: 2.0.0 - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3(supports-color@8.1.1) gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -16965,7 +16962,7 @@ snapshots: '@babel/parser': 7.28.5 '@babel/template': 7.27.2 '@babel/types': 7.28.5 - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -17467,7 +17464,7 @@ snapshots: '@esbuild-plugins/node-resolve@0.2.2(esbuild@0.25.12)': dependencies: '@types/resolve': 1.20.6 - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3(supports-color@8.1.1) esbuild: 0.25.12 escape-string-regexp: 4.0.0 resolve: 1.22.11 @@ -17865,7 +17862,7 @@ snapshots: '@eslint/config-array@0.21.1': dependencies: '@eslint/object-schema': 2.1.7 - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3(supports-color@8.1.1) minimatch: 3.1.2 transitivePeerDependencies: - supports-color @@ -17881,7 +17878,7 @@ snapshots: '@eslint/eslintrc@3.3.3': dependencies: ajv: 6.12.6 - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3(supports-color@8.1.1) espree: 10.4.0 globals: 14.0.0 ignore: 5.3.2 @@ -18672,6 +18669,19 @@ snapshots: '@lukeed/ms@2.0.2': {} + '@mapbox/node-pre-gyp@2.0.3': + dependencies: + consola: 3.4.2 + detect-libc: 2.1.2 + https-proxy-agent: 7.0.6 + node-fetch: 2.7.0 + nopt: 8.1.0 + semver: 7.6.3 + tar: 7.5.2 + transitivePeerDependencies: + - encoding + - supports-color + '@mapbox/node-pre-gyp@2.0.3(supports-color@10.2.2)': dependencies: consola: 3.4.2 @@ -18892,6 +18902,14 @@ snapshots: '@netlify/dev-utils': 4.3.0 '@netlify/runtime-utils': 2.2.0 + '@netlify/blobs@10.5.0': + dependencies: + '@netlify/dev-utils': 4.3.3 + '@netlify/otel': 5.1.1 + '@netlify/runtime-utils': 2.2.1 + transitivePeerDependencies: + - supports-color + '@netlify/blobs@10.5.0(supports-color@10.2.2)': dependencies: '@netlify/dev-utils': 4.3.3 @@ -19103,7 +19121,7 @@ snapshots: '@netlify/dev@4.8.7(@netlify/api@14.0.12)(aws4fetch@1.0.20)(ioredis@5.8.2)(rollup@4.54.0)': dependencies: '@netlify/ai': 0.3.5(@netlify/api@14.0.12) - '@netlify/blobs': 10.5.0(supports-color@10.2.2) + '@netlify/blobs': 10.5.0 '@netlify/config': 24.2.0 '@netlify/dev-utils': 4.3.3 '@netlify/edge-functions-dev': 1.0.7 @@ -19209,10 +19227,10 @@ snapshots: '@netlify/functions-dev@1.1.7(rollup@4.54.0)': dependencies: - '@netlify/blobs': 10.5.0(supports-color@10.2.2) + '@netlify/blobs': 10.5.0 '@netlify/dev-utils': 4.3.3 '@netlify/functions': 5.1.2 - '@netlify/zip-it-and-ship-it': 14.1.17(rollup@4.54.0)(supports-color@10.2.2) + '@netlify/zip-it-and-ship-it': 14.1.17(rollup@4.54.0) cron-parser: 4.9.0 decache: 4.6.2 extract-zip: 2.0.1 @@ -19346,6 +19364,16 @@ snapshots: dependencies: '@opentelemetry/api': 1.8.0 + '@netlify/otel@5.1.1': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.203.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-node': 1.30.1(@opentelemetry/api@1.9.0) + transitivePeerDependencies: + - supports-color + '@netlify/otel@5.1.1(supports-color@10.2.2)': dependencies: '@opentelemetry/api': 1.9.0 @@ -19383,7 +19411,7 @@ snapshots: '@netlify/runtime@4.1.13': dependencies: - '@netlify/blobs': 10.5.0(supports-color@10.2.2) + '@netlify/blobs': 10.5.0 '@netlify/cache': 3.3.4 '@netlify/runtime-utils': 2.2.1 '@netlify/types': 2.3.0 @@ -19499,6 +19527,47 @@ snapshots: - supports-color - uploadthing + '@netlify/zip-it-and-ship-it@14.1.14(rollup@4.54.0)': + dependencies: + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 + '@netlify/binary-info': 1.0.0 + '@netlify/serverless-functions-api': 2.8.2 + '@vercel/nft': 0.29.4(rollup@4.54.0) + archiver: 7.0.1 + common-path-prefix: 3.0.0 + copy-file: 11.1.0 + es-module-lexer: 1.7.0 + esbuild: 0.25.11 + execa: 8.0.1 + fast-glob: 3.3.3 + filter-obj: 6.1.0 + find-up: 7.0.0 + is-path-inside: 4.0.0 + junk: 4.0.1 + locate-path: 7.2.0 + merge-options: 3.0.4 + minimatch: 9.0.5 + normalize-path: 3.0.0 + p-map: 7.0.3 + path-exists: 5.0.0 + precinct: 12.2.0 + require-package-name: 2.0.1 + resolve: 2.0.0-next.5 + semver: 7.6.3 + tmp-promise: 3.0.3 + toml: 3.0.0 + unixify: 1.0.0 + urlpattern-polyfill: 8.0.2 + yargs: 17.7.2 + zod: 3.25.76 + transitivePeerDependencies: + - bare-abort-controller + - encoding + - react-native-b4a + - rollup + - supports-color + '@netlify/zip-it-and-ship-it@14.1.14(rollup@4.54.0)(supports-color@10.2.2)': dependencies: '@babel/parser': 7.28.5 @@ -19540,6 +19609,47 @@ snapshots: - rollup - supports-color + '@netlify/zip-it-and-ship-it@14.1.17(rollup@4.54.0)': + dependencies: + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 + '@netlify/binary-info': 1.0.0 + '@netlify/serverless-functions-api': 2.8.2 + '@vercel/nft': 0.29.4(rollup@4.54.0) + archiver: 7.0.1 + common-path-prefix: 3.0.0 + copy-file: 11.1.0 + es-module-lexer: 1.7.0 + esbuild: 0.27.2 + execa: 8.0.1 + fast-glob: 3.3.3 + filter-obj: 6.1.0 + find-up: 7.0.0 + is-path-inside: 4.0.0 + junk: 4.0.1 + locate-path: 7.2.0 + merge-options: 3.0.4 + minimatch: 9.0.5 + normalize-path: 3.0.0 + p-map: 7.0.3 + path-exists: 5.0.0 + precinct: 12.2.0 + require-package-name: 2.0.1 + resolve: 2.0.0-next.5 + semver: 7.6.3 + tmp-promise: 3.0.3 + toml: 3.0.0 + unixify: 1.0.0 + urlpattern-polyfill: 8.0.2 + yargs: 17.7.2 + zod: 3.25.76 + transitivePeerDependencies: + - bare-abort-controller + - encoding + - react-native-b4a + - rollup + - supports-color + '@netlify/zip-it-and-ship-it@14.1.17(rollup@4.54.0)(supports-color@10.2.2)': dependencies: '@babel/parser': 7.28.5 @@ -20285,6 +20395,15 @@ snapshots: transitivePeerDependencies: - supports-color + '@opentelemetry/instrumentation@0.203.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/api-logs': 0.203.0 + import-in-the-middle: 1.15.0 + require-in-the-middle: 7.5.2 + transitivePeerDependencies: + - supports-color + '@opentelemetry/instrumentation@0.203.0(@opentelemetry/api@1.9.0)(supports-color@10.2.2)': dependencies: '@opentelemetry/api': 1.9.0 @@ -20309,7 +20428,7 @@ snapshots: '@opentelemetry/api-logs': 0.53.0 '@types/shimmer': 1.2.0 import-in-the-middle: 1.15.0 - require-in-the-middle: 7.5.2(supports-color@10.2.2) + require-in-the-middle: 7.5.2 semver: 7.6.3 shimmer: 1.2.1 transitivePeerDependencies: @@ -20321,7 +20440,7 @@ snapshots: '@opentelemetry/api-logs': 0.57.1 '@types/shimmer': 1.2.0 import-in-the-middle: 1.15.0 - require-in-the-middle: 7.5.2(supports-color@10.2.2) + require-in-the-middle: 7.5.2 semver: 7.6.3 shimmer: 1.2.1 transitivePeerDependencies: @@ -20333,7 +20452,7 @@ snapshots: '@opentelemetry/api-logs': 0.57.2 '@types/shimmer': 1.2.0 import-in-the-middle: 1.15.0 - require-in-the-middle: 7.5.2(supports-color@10.2.2) + require-in-the-middle: 7.5.2 semver: 7.6.3 shimmer: 1.2.1 transitivePeerDependencies: @@ -20641,7 +20760,7 @@ snapshots: '@pnpm/tabtab@0.5.4': dependencies: - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3(supports-color@8.1.1) enquirer: 2.4.1 minimist: 1.2.8 untildify: 4.0.0 @@ -20748,7 +20867,7 @@ snapshots: '@puppeteer/browsers@2.11.0': dependencies: - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3(supports-color@8.1.1) extract-zip: 2.0.1 progress: 2.0.3 proxy-agent: 6.5.0 @@ -20763,7 +20882,7 @@ snapshots: '@puppeteer/browsers@2.3.0': dependencies: - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3(supports-color@8.1.1) extract-zip: 2.0.1 progress: 2.0.3 proxy-agent: 6.5.0 @@ -22886,7 +23005,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@tanstack/router-plugin@1.142.8(@tanstack/react-router@1.142.8(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(vite@7.3.0(@types/node@24.10.4)(jiti@1.21.7)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2))': + '@tanstack/router-plugin@1.142.8(@tanstack/react-router@1.142.8(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2))': dependencies: '@babel/core': 7.28.5 '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.5) @@ -22904,7 +23023,7 @@ snapshots: zod: 3.25.76 optionalDependencies: '@tanstack/react-router': 1.142.8(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - vite: 7.3.0(@types/node@24.10.4)(jiti@1.21.7)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) + vite: 7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) transitivePeerDependencies: - supports-color @@ -23818,9 +23937,9 @@ snapshots: dependencies: '@typescript-eslint/scope-manager': 8.50.0 '@typescript-eslint/types': 8.50.0 - '@typescript-eslint/typescript-estree': 8.50.0(supports-color@10.2.2)(typescript@5.9.3) + '@typescript-eslint/typescript-estree': 8.50.0(typescript@5.9.3) '@typescript-eslint/visitor-keys': 8.50.0 - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3(supports-color@8.1.1) eslint: 9.39.2(jiti@2.6.1) typescript: 5.9.3 transitivePeerDependencies: @@ -23835,6 +23954,15 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/project-service@8.50.0(typescript@5.9.3)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.50.0(typescript@5.9.3) + '@typescript-eslint/types': 8.50.0 + debug: 4.4.3(supports-color@8.1.1) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/scope-manager@8.50.0': dependencies: '@typescript-eslint/types': 8.50.0 @@ -23847,9 +23975,9 @@ snapshots: '@typescript-eslint/type-utils@8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': dependencies: '@typescript-eslint/types': 8.50.0 - '@typescript-eslint/typescript-estree': 8.50.0(supports-color@10.2.2)(typescript@5.9.3) + '@typescript-eslint/typescript-estree': 8.50.0(typescript@5.9.3) '@typescript-eslint/utils': 8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3(supports-color@8.1.1) eslint: 9.39.2(jiti@2.6.1) ts-api-utils: 2.1.0(typescript@5.9.3) typescript: 5.9.3 @@ -23873,12 +24001,27 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/typescript-estree@8.50.0(typescript@5.9.3)': + dependencies: + '@typescript-eslint/project-service': 8.50.0(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.50.0(typescript@5.9.3) + '@typescript-eslint/types': 8.50.0 + '@typescript-eslint/visitor-keys': 8.50.0 + debug: 4.4.3(supports-color@8.1.1) + minimatch: 9.0.5 + semver: 7.6.3 + tinyglobby: 0.2.15 + ts-api-utils: 2.1.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/utils@8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': dependencies: '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.2(jiti@2.6.1)) '@typescript-eslint/scope-manager': 8.50.0 '@typescript-eslint/types': 8.50.0 - '@typescript-eslint/typescript-estree': 8.50.0(supports-color@10.2.2)(typescript@5.9.3) + '@typescript-eslint/typescript-estree': 8.50.0(typescript@5.9.3) eslint: 9.39.2(jiti@2.6.1) typescript: 5.9.3 transitivePeerDependencies: @@ -23945,6 +24088,25 @@ snapshots: '@use-gesture/core': 10.3.1 react: 19.2.3 + '@vercel/nft@0.29.4(rollup@4.54.0)': + dependencies: + '@mapbox/node-pre-gyp': 2.0.3 + '@rollup/pluginutils': 5.3.0(rollup@4.54.0) + acorn: 8.15.0 + acorn-import-attributes: 1.9.5(acorn@8.15.0) + async-sema: 3.1.1 + bindings: 1.5.0 + estree-walker: 2.0.2 + glob: 10.5.0 + graceful-fs: 4.2.11 + node-gyp-build: 4.8.4 + picomatch: 4.0.3 + resolve-from: 5.0.0 + transitivePeerDependencies: + - encoding + - rollup + - supports-color + '@vercel/nft@0.29.4(rollup@4.54.0)(supports-color@10.2.2)': dependencies: '@mapbox/node-pre-gyp': 2.0.3(supports-color@10.2.2) @@ -23966,7 +24128,7 @@ snapshots: '@vercel/nft@0.30.4(rollup@4.54.0)': dependencies: - '@mapbox/node-pre-gyp': 2.0.3(supports-color@10.2.2) + '@mapbox/node-pre-gyp': 2.0.3 '@rollup/pluginutils': 5.3.0(rollup@4.54.0) acorn: 8.15.0 acorn-import-attributes: 1.9.5(acorn@8.15.0) @@ -24064,13 +24226,13 @@ snapshots: optionalDependencies: vite: 7.3.0(@types/node@22.19.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) - '@vitest/mocker@3.2.4(vite@7.3.0(@types/node@24.10.4)(jiti@1.21.7)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2))': + '@vitest/mocker@3.2.4(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2))': dependencies: '@vitest/spy': 3.2.4 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 7.3.0(@types/node@24.10.4)(jiti@1.21.7)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) + vite: 7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) '@vitest/mocker@3.2.4(vite@7.3.0(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2))': dependencies: @@ -24759,7 +24921,7 @@ snapshots: common-ancestor-path: 1.0.1 cookie: 1.1.1 cssesc: 3.0.0 - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3(supports-color@8.1.1) deterministic-object-hash: 2.0.2 devalue: 5.6.1 diff: 5.2.0 @@ -25029,7 +25191,7 @@ snapshots: dependencies: bytes: 3.1.2 content-type: 1.0.5 - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3(supports-color@8.1.1) http-errors: 2.0.1 iconv-lite: 0.7.1 on-finished: 2.4.1 @@ -26022,6 +26184,15 @@ snapshots: transitivePeerDependencies: - supports-color + detective-typescript@14.0.0(typescript@5.9.3): + dependencies: + '@typescript-eslint/typescript-estree': 8.50.0(typescript@5.9.3) + ast-module-types: 6.0.1 + node-source-walk: 7.0.1 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + detective-vue2@2.2.0(supports-color@10.2.2)(typescript@5.9.3): dependencies: '@dependents/detective-less': 5.0.1 @@ -26035,6 +26206,19 @@ snapshots: transitivePeerDependencies: - supports-color + detective-vue2@2.2.0(typescript@5.9.3): + dependencies: + '@dependents/detective-less': 5.0.1 + '@vue/compiler-sfc': 3.5.26 + detective-es6: 5.0.1 + detective-sass: 6.0.1 + detective-scss: 5.0.1 + detective-stylus: 5.0.1 + detective-typescript: 14.0.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + deterministic-object-hash@2.0.2: dependencies: base-64: 1.0.0 @@ -26149,7 +26333,7 @@ snapshots: edge-paths: 3.0.5 fast-xml-parser: 5.3.3 http-proxy-agent: 7.0.2 - https-proxy-agent: 7.0.6(supports-color@10.2.2) + https-proxy-agent: 7.0.6 which: 6.0.0 transitivePeerDependencies: - supports-color @@ -26274,7 +26458,7 @@ snapshots: esbuild-register@3.6.0(esbuild@0.25.12): dependencies: - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3(supports-color@8.1.1) esbuild: 0.25.12 transitivePeerDependencies: - supports-color @@ -26469,7 +26653,7 @@ snapshots: ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.6 - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3(supports-color@8.1.1) escape-string-regexp: 4.0.0 eslint-scope: 8.4.0 eslint-visitor-keys: 4.2.1 @@ -26724,7 +26908,7 @@ snapshots: content-type: 1.0.5 cookie: 0.7.2 cookie-signature: 1.2.2 - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3(supports-color@8.1.1) depd: 2.0.0 encodeurl: 2.0.0 escape-html: 1.0.3 @@ -26773,7 +26957,7 @@ snapshots: extract-zip@2.0.1: dependencies: - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3(supports-color@8.1.1) get-stream: 5.2.0 yauzl: 2.10.0 optionalDependencies: @@ -26945,7 +27129,7 @@ snapshots: finalhandler@2.1.1: dependencies: - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3(supports-color@8.1.1) encodeurl: 2.0.0 escape-html: 1.0.3 on-finished: 2.4.1 @@ -27003,7 +27187,7 @@ snapshots: follow-redirects@1.15.11(debug@4.4.3): optionalDependencies: - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3(supports-color@8.1.1) fontace@0.3.1: dependencies: @@ -27092,7 +27276,7 @@ snapshots: gaxios@7.1.3: dependencies: extend: 3.0.2 - https-proxy-agent: 7.0.6(supports-color@10.2.2) + https-proxy-agent: 7.0.6 node-fetch: 3.3.2 rimraf: 5.0.10 transitivePeerDependencies: @@ -27112,7 +27296,7 @@ snapshots: '@zip.js/zip.js': 2.8.11 decamelize: 6.0.1 http-proxy-agent: 7.0.2 - https-proxy-agent: 7.0.6(supports-color@10.2.2) + https-proxy-agent: 7.0.6 modern-tar: 0.7.3 transitivePeerDependencies: - supports-color @@ -27177,7 +27361,7 @@ snapshots: dependencies: basic-ftp: 5.0.5 data-uri-to-buffer: 6.0.2 - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -27665,7 +27849,7 @@ snapshots: http-proxy-agent@7.0.2: dependencies: agent-base: 7.1.4 - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -27694,6 +27878,13 @@ snapshots: quick-lru: 5.1.1 resolve-alpn: 1.2.1 + https-proxy-agent@7.0.6: + dependencies: + agent-base: 7.1.4 + debug: 4.4.3(supports-color@8.1.1) + transitivePeerDependencies: + - supports-color + https-proxy-agent@7.0.6(supports-color@10.2.2): dependencies: agent-base: 7.1.4 @@ -27843,7 +28034,7 @@ snapshots: dependencies: '@ioredis/commands': 1.4.0 cluster-key-slot: 1.1.2 - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3(supports-color@8.1.1) denque: 2.1.0 lodash.defaults: 4.2.0 lodash.isarguments: 3.1.0 @@ -28193,7 +28384,7 @@ snapshots: decimal.js: 10.6.0 html-encoding-sniffer: 4.0.0 http-proxy-agent: 7.0.2 - https-proxy-agent: 7.0.6(supports-color@10.2.2) + https-proxy-agent: 7.0.6 is-potential-custom-element-name: 1.0.1 parse5: 8.0.0 saxes: 6.0.0 @@ -29647,7 +29838,7 @@ snapshots: micromark@3.2.0: dependencies: '@types/debug': 4.1.12 - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3(supports-color@8.1.1) decode-named-character-reference: 1.2.0 micromark-core-commonmark: 1.1.0 micromark-factory-space: 1.1.0 @@ -29670,7 +29861,7 @@ snapshots: micromark@4.0.2: dependencies: '@types/debug': 4.1.12 - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3(supports-color@8.1.1) decode-named-character-reference: 1.2.0 devlop: 1.1.0 micromark-core-commonmark: 2.0.3 @@ -29904,7 +30095,7 @@ snapshots: '@netlify/headers-parser': 9.0.2 '@netlify/local-functions-proxy': 2.0.3 '@netlify/redirect-parser': 15.0.3 - '@netlify/zip-it-and-ship-it': 14.1.14(rollup@4.54.0)(supports-color@10.2.2) + '@netlify/zip-it-and-ship-it': 14.1.14(rollup@4.54.0) '@octokit/rest': 22.0.0 '@opentelemetry/api': 1.8.0 '@pnpm/tabtab': 0.5.4 @@ -29922,7 +30113,7 @@ snapshots: content-type: 1.0.5 cookie: 1.0.2 cron-parser: 4.9.0 - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3(supports-color@8.1.1) decache: 4.6.2 dot-prop: 9.0.0 dotenv: 17.2.3 @@ -29944,7 +30135,7 @@ snapshots: gitconfiglocal: 2.1.0 http-proxy: 1.18.1(debug@4.4.3) http-proxy-middleware: 2.0.9(debug@4.4.3) - https-proxy-agent: 7.0.6(supports-color@10.2.2) + https-proxy-agent: 7.0.6 inquirer: 8.2.7(@types/node@22.19.3) inquirer-autocomplete-prompt: 1.4.0(inquirer@8.2.7(@types/node@22.19.3)) ipx: 3.1.1(@netlify/blobs@10.1.0)(aws4fetch@1.0.20)(ioredis@5.8.2) @@ -30403,10 +30594,10 @@ snapshots: dependencies: '@tootallnate/quickjs-emscripten': 0.23.0 agent-base: 7.1.4 - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3(supports-color@8.1.1) get-uri: 6.0.5 http-proxy-agent: 7.0.2 - https-proxy-agent: 7.0.6(supports-color@10.2.2) + https-proxy-agent: 7.0.6 pac-resolver: 7.0.1 socks-proxy-agent: 8.0.5 transitivePeerDependencies: @@ -30811,6 +31002,26 @@ snapshots: preact@10.28.0: {} + precinct@12.2.0: + dependencies: + '@dependents/detective-less': 5.0.1 + commander: 12.1.0 + detective-amd: 6.0.1 + detective-cjs: 6.0.1 + detective-es6: 5.0.1 + detective-postcss: 7.0.1(postcss@8.5.6) + detective-sass: 6.0.1 + detective-scss: 5.0.1 + detective-stylus: 5.0.1 + detective-typescript: 14.0.0(typescript@5.9.3) + detective-vue2: 2.2.0(typescript@5.9.3) + module-definition: 6.0.1 + node-source-walk: 7.0.1 + postcss: 8.5.6 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + precinct@12.2.0(supports-color@10.2.2): dependencies: '@dependents/detective-less': 5.0.1 @@ -31032,9 +31243,9 @@ snapshots: proxy-agent@6.5.0: dependencies: agent-base: 7.1.4 - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3(supports-color@8.1.1) http-proxy-agent: 7.0.2 - https-proxy-agent: 7.0.6(supports-color@10.2.2) + https-proxy-agent: 7.0.6 lru-cache: 7.18.3 pac-proxy-agent: 7.2.0 proxy-from-env: 1.1.0 @@ -31068,7 +31279,7 @@ snapshots: dependencies: '@puppeteer/browsers': 2.3.0 chromium-bidi: 0.6.3(devtools-protocol@0.0.1312386) - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3(supports-color@8.1.1) devtools-protocol: 0.0.1312386 ws: 8.18.3 transitivePeerDependencies: @@ -31804,6 +32015,14 @@ snapshots: require-from-string@2.0.2: {} + require-in-the-middle@7.5.2: + dependencies: + debug: 4.4.3(supports-color@8.1.1) + module-details-from-path: 1.0.4 + resolve: 1.22.11 + transitivePeerDependencies: + - supports-color + require-in-the-middle@7.5.2(supports-color@10.2.2): dependencies: debug: 4.4.3(supports-color@10.2.2) @@ -31814,7 +32033,7 @@ snapshots: require-in-the-middle@8.0.1: dependencies: - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3(supports-color@8.1.1) module-details-from-path: 1.0.4 transitivePeerDependencies: - supports-color @@ -31970,7 +32189,7 @@ snapshots: router@2.2.0: dependencies: - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3(supports-color@8.1.1) depd: 2.0.0 is-promise: 4.0.0 parseurl: 1.3.3 @@ -32086,7 +32305,7 @@ snapshots: send@1.2.1: dependencies: - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3(supports-color@8.1.1) encodeurl: 2.0.0 escape-html: 1.0.3 etag: 1.8.1 @@ -32319,7 +32538,7 @@ snapshots: socks-proxy-agent@8.0.5: dependencies: agent-base: 7.1.4 - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3(supports-color@8.1.1) socks: 2.8.7 transitivePeerDependencies: - supports-color @@ -32989,7 +33208,7 @@ snapshots: dependencies: '@typescript-eslint/eslint-plugin': 8.50.0(@typescript-eslint/parser@8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) '@typescript-eslint/parser': 8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/typescript-estree': 8.50.0(supports-color@10.2.2)(typescript@5.9.3) + '@typescript-eslint/typescript-estree': 8.50.0(typescript@5.9.3) '@typescript-eslint/utils': 8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) eslint: 9.39.2(jiti@2.6.1) typescript: 5.9.3 @@ -33224,7 +33443,7 @@ snapshots: ofetch: 1.5.1 ufo: 1.6.1 optionalDependencies: - '@netlify/blobs': 10.5.0(supports-color@10.2.2) + '@netlify/blobs': 10.5.0 aws4fetch: 1.0.20 ioredis: 5.8.2 @@ -33391,7 +33610,7 @@ snapshots: vite-node@2.1.9(@types/node@20.19.27)(lightningcss@1.30.2): dependencies: cac: 6.7.14 - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3(supports-color@8.1.1) es-module-lexer: 1.7.0 pathe: 1.1.2 vite: 5.4.21(@types/node@20.19.27)(lightningcss@1.30.2) @@ -33409,7 +33628,7 @@ snapshots: vite-node@3.2.4(@types/node@22.19.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2): dependencies: cac: 6.7.14 - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3(supports-color@8.1.1) es-module-lexer: 1.7.0 pathe: 2.0.3 vite: 7.3.0(@types/node@22.19.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) @@ -33427,13 +33646,13 @@ snapshots: - tsx - yaml - vite-node@3.2.4(@types/node@24.10.4)(jiti@1.21.7)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2): + vite-node@3.2.4(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2): dependencies: cac: 6.7.14 - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3(supports-color@8.1.1) es-module-lexer: 1.7.0 pathe: 2.0.3 - vite: 7.3.0(@types/node@24.10.4)(jiti@1.21.7)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) + vite: 7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) transitivePeerDependencies: - '@types/node' - jiti @@ -33451,7 +33670,7 @@ snapshots: vite-node@3.2.4(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2): dependencies: cac: 6.7.14 - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3(supports-color@8.1.1) es-module-lexer: 1.7.0 pathe: 2.0.3 vite: 7.3.0(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) @@ -33471,7 +33690,7 @@ snapshots: vite-tsconfig-paths@5.1.4(typescript@5.9.3)(vite@7.3.0(@types/node@22.19.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)): dependencies: - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3(supports-color@8.1.1) globrex: 0.1.2 tsconfck: 3.1.6(typescript@5.9.3) optionalDependencies: @@ -33588,7 +33807,7 @@ snapshots: '@vitest/spy': 2.1.9 '@vitest/utils': 2.1.9 chai: 5.3.3 - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3(supports-color@8.1.1) expect-type: 1.3.0 magic-string: 0.30.21 pathe: 1.1.2 @@ -33625,7 +33844,7 @@ snapshots: '@vitest/spy': 3.2.4 '@vitest/utils': 3.2.4 chai: 5.3.3 - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3(supports-color@8.1.1) expect-type: 1.3.0 magic-string: 0.30.21 pathe: 2.0.3 @@ -33657,18 +33876,18 @@ snapshots: - tsx - yaml - vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.10.4)(jiti@1.21.7)(jsdom@27.3.0)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2): + vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.10.4)(jiti@2.6.1)(jsdom@27.3.0)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2): dependencies: '@types/chai': 5.2.3 '@vitest/expect': 3.2.4 - '@vitest/mocker': 3.2.4(vite@7.3.0(@types/node@24.10.4)(jiti@1.21.7)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)) + '@vitest/mocker': 3.2.4(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2)) '@vitest/pretty-format': 3.2.4 '@vitest/runner': 3.2.4 '@vitest/snapshot': 3.2.4 '@vitest/spy': 3.2.4 '@vitest/utils': 3.2.4 chai: 5.3.3 - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3(supports-color@8.1.1) expect-type: 1.3.0 magic-string: 0.30.21 pathe: 2.0.3 @@ -33679,8 +33898,8 @@ snapshots: tinyglobby: 0.2.15 tinypool: 1.1.1 tinyrainbow: 2.0.0 - vite: 7.3.0(@types/node@24.10.4)(jiti@1.21.7)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) - vite-node: 3.2.4(@types/node@24.10.4)(jiti@1.21.7)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) + vite: 7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) + vite-node: 3.2.4(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.2) why-is-node-running: 2.3.0 optionalDependencies: '@types/debug': 4.1.12 @@ -33711,7 +33930,7 @@ snapshots: '@vitest/spy': 3.2.4 '@vitest/utils': 3.2.4 chai: 5.3.3 - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3(supports-color@8.1.1) expect-type: 1.3.0 magic-string: 0.30.21 pathe: 2.0.3 @@ -33770,7 +33989,7 @@ snapshots: dependencies: chalk: 4.1.2 commander: 9.5.0 - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -33798,7 +34017,7 @@ snapshots: '@wdio/types': 9.20.0 '@wdio/utils': 9.21.0 deepmerge-ts: 7.1.5 - https-proxy-agent: 7.0.6(supports-color@10.2.2) + https-proxy-agent: 7.0.6 undici: 6.22.0 ws: 8.18.3 transitivePeerDependencies: From c44f5d92f93e9b4855aa2a627bb2cfc9a266b101 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Sun, 4 Jan 2026 06:27:26 +0000 Subject: [PATCH 5/5] style: fix dprint formatting issues Co-Authored-By: yujonglee --- .../src/store/tinybase/persister/human/persister.test.ts | 4 +++- apps/desktop/src/store/tinybase/persister/human/utils.ts | 6 +++++- apps/desktop/src/store/tinybase/persister/utils.ts | 7 ++++--- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/apps/desktop/src/store/tinybase/persister/human/persister.test.ts b/apps/desktop/src/store/tinybase/persister/human/persister.test.ts index 6e3647e4be..28cd9a7886 100644 --- a/apps/desktop/src/store/tinybase/persister/human/persister.test.ts +++ b/apps/desktop/src/store/tinybase/persister/human/persister.test.ts @@ -15,7 +15,9 @@ vi.mock("@hypr/plugin-path2", () => ({ vi.mock("@hypr/plugin-export", () => ({ commands: { parseFrontmatter: vi.fn(), - exportFrontmatterBatch: vi.fn().mockResolvedValue({ status: "ok", data: null }), + exportFrontmatterBatch: vi + .fn() + .mockResolvedValue({ status: "ok", data: null }), }, })); diff --git a/apps/desktop/src/store/tinybase/persister/human/utils.ts b/apps/desktop/src/store/tinybase/persister/human/utils.ts index dc1129377b..2f88b4b2d6 100644 --- a/apps/desktop/src/store/tinybase/persister/human/utils.ts +++ b/apps/desktop/src/store/tinybase/persister/human/utils.ts @@ -1,4 +1,5 @@ import { sep } from "@tauri-apps/api/path"; + import { commands } from "@hypr/plugin-export"; export interface ParsedMarkdown { @@ -11,7 +12,10 @@ export async function parseMarkdownWithFrontmatter( ): Promise { const result = await commands.parseFrontmatter(content); if (result.status === "error") { - console.error("[HumanPersister] Failed to parse frontmatter:", result.error); + console.error( + "[HumanPersister] Failed to parse frontmatter:", + result.error, + ); return { frontmatter: {}, body: content }; } return { diff --git a/apps/desktop/src/store/tinybase/persister/utils.ts b/apps/desktop/src/store/tinybase/persister/utils.ts index 8e2244b04f..aea0589ef9 100644 --- a/apps/desktop/src/store/tinybase/persister/utils.ts +++ b/apps/desktop/src/store/tinybase/persister/utils.ts @@ -9,8 +9,8 @@ import type { import { commands as exportCommands, - type FrontmatterInput, type JsonValue as ExportJsonValue, + type FrontmatterInput, } from "@hypr/plugin-export"; import { events as notifyEvents } from "@hypr/plugin-notify"; import { commands as path2Commands } from "@hypr/plugin-path2"; @@ -304,8 +304,9 @@ export function createSessionDirPersister( } if (frontmatterBatchItems.length > 0) { - const exportResult = - await exportCommands.exportFrontmatterBatch(frontmatterBatchItems); + const exportResult = await exportCommands.exportFrontmatterBatch( + frontmatterBatchItems, + ); if (exportResult.status === "error") { console.error( `[${options.label}] Failed to export frontmatter batch:`,