desktop-storage-electron

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Electron Storage & Credentials

Electron存储与凭据管理

Quick Guide: Use
electron-store
for typed JSON preferences (small key-value config with schema validation, migrations, and file watching). Use
better-sqlite3
for structured/relational data or anything beyond simple key-value (synchronous, WAL mode, transactions). Use
safeStorage
for encrypting secrets like tokens and API keys via the OS keychain -- it replaces the deprecated
keytar
. All persistent data belongs under
app.getPath("userData")
. Never store secrets in plain JSON files.

<critical_requirements>
快速指南: 使用
electron-store
存储带类型的JSON偏好设置(支持模式验证、数据迁移和文件监听的小型键值配置)。使用
better-sqlite3
存储结构化/关系型数据或超出简单键值对范畴的数据(同步操作、WAL模式、事务支持)。使用
safeStorage
通过操作系统密钥链加密令牌和API密钥等机密信息——它已取代被弃用的
keytar
。所有持久化数据都应存储在
app.getPath("userData")
路径下。绝不要在纯JSON文件中存储机密信息。

<critical_requirements>

CRITICAL: Before Using This Skill

重要提示:使用本技能前须知

All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering,
import type
, named constants)
(You MUST use
safeStorage.encryptString()
/
safeStorage.decryptString()
for secrets -- never store tokens, API keys, or passwords in plain text or in electron-store without OS-level encryption)
(You MUST store all persistent data under
app.getPath("userData")
-- never write to the app installation directory, which is replaced on updates)
(You MUST enable WAL mode (
PRAGMA journal_mode = WAL
) when using better-sqlite3 -- it prevents readers from blocking writers and avoids SQLITE_BUSY errors in multi-window apps)
(You MUST call
safeStorage.isEncryptionAvailable()
before encrypting -- it returns false before the app
ready
event and on some Linux configurations)
(You MUST rebuild better-sqlite3 for Electron's Node.js version using
@electron/rebuild
-- mismatched native bindings crash the app)
</critical_requirements>

Auto-detection: electron-store, better-sqlite3, safeStorage, app.getPath, userData, encryptString, decryptString, isEncryptionAvailable, lowdb, JSONFilePreset, persistent storage, credential storage, keytar replacement, electron config, electron preferences, electron database
When to use:
  • Persisting user preferences and app configuration
  • Storing structured or relational data locally
  • Encrypting tokens, API keys, or other secrets
  • Choosing between storage solutions for an Electron app
  • Migrating stored data between app versions
  • Working with
    app.getPath()
    standard directories
When NOT to use:
  • Choosing a UI framework or styling for the renderer (separate skill)
  • IPC communication patterns between main and renderer (separate concern)
  • Packaging and distribution concerns (separate concern)
  • Server-side or cloud storage
Key patterns covered:
  • electron-store: typed config, schema validation, migrations, encryption, watching
  • better-sqlite3: WAL mode, prepared statements, transactions, native module rebuild
  • safeStorage: OS keychain encryption for secrets, replacing keytar
  • lowdb: lightweight JSON database for medium-complexity data
  • Storage path conventions using
    app.getPath()
  • Credential storage best practices

<philosophy>
所有代码必须遵循CLAUDE.md中的项目约定(短横线命名、命名导出、导入顺序、
import type
、命名常量)
(你必须使用
safeStorage.encryptString()
/
safeStorage.decryptString()
存储机密信息——绝不要以明文形式存储令牌、API密钥或密码,也不要在未经过操作系统级加密的情况下将其存入electron-store)
(你必须将所有持久化数据存储在
app.getPath("userData")
路径下——绝不要写入应用安装目录,该目录会在更新时被替换)
(使用better-sqlite3时必须启用WAL模式(
PRAGMA journal_mode = WAL
)——它可防止读取操作阻塞写入操作,避免多窗口应用中出现SQLITE_BUSY错误)
(加密前必须调用
safeStorage.isEncryptionAvailable()
——在应用
ready
事件触发前以及部分Linux配置下,它会返回false)
(必须使用
@electron/rebuild
针对Electron的Node.js版本重新构建better-sqlite3——不匹配的原生绑定会导致应用崩溃)
</critical_requirements>

自动检测关键词: electron-store, better-sqlite3, safeStorage, app.getPath, userData, encryptString, decryptString, isEncryptionAvailable, lowdb, JSONFilePreset, persistent storage, credential storage, keytar replacement, electron config, electron preferences, electron database
适用场景:
  • 持久化用户偏好设置和应用配置
  • 本地存储结构化或关系型数据
  • 加密令牌、API密钥或其他机密信息
  • 为Electron应用选择存储方案
  • 在应用版本间迁移存储的数据
  • 使用
    app.getPath()
    标准目录
不适用场景:
  • 为渲染进程选择UI框架或样式(属于独立技能范畴)
  • 主进程与渲染进程间的IPC通信模式(属于独立关注点)
  • 打包与分发相关问题(属于独立关注点)
  • 服务器端或云存储
涵盖的核心模式:
  • electron-store:带类型的配置、模式验证、数据迁移、加密、监听
  • better-sqlite3:WAL模式、预编译语句、事务、原生模块重构
  • safeStorage:基于操作系统密钥链的机密信息加密、替代keytar
  • lowdb:适用于中等复杂度数据的轻量级JSON数据库
  • 使用
    app.getPath()
    的存储路径约定
  • 凭据存储最佳实践

<philosophy>

Philosophy

设计理念

Electron apps have access to the full filesystem but should store data in OS-designated locations. The right storage solution depends on data shape and sensitivity:
Preferences and small config (theme, window bounds, feature flags):
electron-store
writes a single JSON file atomically. It is read and written in full on every change, so it is only appropriate for small data (under ~1MB).
Structured or queryable data (chat history, project metadata, analytics):
better-sqlite3
provides a synchronous SQLite database with ACID transactions. It handles concurrent reads via WAL mode and scales to gigabytes.
Secrets (OAuth tokens, API keys, passwords):
safeStorage
uses the OS keychain (macOS Keychain, Windows DPAPI, Linux secret service) to encrypt strings. The encrypted buffer can be stored in electron-store or a file -- only your app can decrypt it on the same machine and user account.
Medium-complexity JSON data (todo lists, small document stores):
lowdb
provides a file-backed JavaScript object with native array methods. Simpler than SQLite for JSON-shaped data that does not need relational queries.
Key principle: Storage runs in the main process. Renderers request data via IPC. Never give renderers direct filesystem or database access.
</philosophy>
<patterns>
Electron应用可访问完整文件系统,但应将数据存储在操作系统指定的位置。合适的存储方案取决于数据形态和敏感度:
偏好设置与小型配置(主题、窗口尺寸、功能开关):
electron-store
以原子方式写入单个JSON文件。每次变更时都会完整读写整个文件,因此仅适用于小型数据(约1MB以下)。
结构化或可查询数据(聊天记录、项目元数据、分析数据):
better-sqlite3
提供支持ACID事务的同步SQLite数据库。它通过WAL模式处理并发读取,可扩展至千兆字节级数据。
机密信息(OAuth令牌、API密钥、密码):
safeStorage
使用操作系统密钥链(macOS Keychain、Windows DPAPI、Linux秘密服务)加密字符串。加密后的缓冲区可存储在electron-store或文件中——只有同一台机器上的同一用户账户下的你的应用才能解密。
中等复杂度JSON数据(待办事项列表、小型文档存储):
lowdb
提供基于文件的JavaScript对象,支持原生数组方法。对于不需要关系查询的JSON形态数据,它比SQLite更简单。
核心原则: 存储操作运行在主进程中。渲染进程通过IPC请求数据。绝不要让渲染进程直接访问文件系统或数据库。
</philosophy>
<patterns>

Core Patterns

核心模式

Pattern 1: electron-store -- Typed Preferences

模式1:electron-store——带类型的偏好设置

Use for small key-value configuration that persists across sessions. Supports schema validation, defaults, and migrations.
typescript
import Store from "electron-store";

interface AppSettings {
  theme: "light" | "dark" | "system";
  windowBounds: { width: number; height: number; x?: number; y?: number };
  recentFiles: string[];
  fontSize: number;
}

const DEFAULT_WIDTH = 1200;
const DEFAULT_HEIGHT = 800;
const MIN_FONT_SIZE = 8;
const MAX_FONT_SIZE = 72;
const DEFAULT_FONT_SIZE = 14;

const store = new Store<AppSettings>({
  defaults: {
    theme: "system",
    windowBounds: { width: DEFAULT_WIDTH, height: DEFAULT_HEIGHT },
    recentFiles: [],
    fontSize: DEFAULT_FONT_SIZE,
  },
  schema: {
    fontSize: {
      type: "number",
      minimum: MIN_FONT_SIZE,
      maximum: MAX_FONT_SIZE,
    },
  },
});
Why good: Type-safe generic parameter ensures
.get()
and
.set()
are checked at compile time, named constants for all limits, schema rejects invalid values at write time
See examples/core.md for migrations, file watching, dot-notation access, and renderer integration via IPC.

用于跨会话持久化的小型键值配置。支持模式验证、默认值和数据迁移。
typescript
import Store from "electron-store";

interface AppSettings {
  theme: "light" | "dark" | "system";
  windowBounds: { width: number; height: number; x?: number; y?: number };
  recentFiles: string[];
  fontSize: number;
}

const DEFAULT_WIDTH = 1200;
const DEFAULT_HEIGHT = 800;
const MIN_FONT_SIZE = 8;
const MAX_FONT_SIZE = 72;
const DEFAULT_FONT_SIZE = 14;

const store = new Store<AppSettings>({
  defaults: {
    theme: "system",
    windowBounds: { width: DEFAULT_WIDTH, height: DEFAULT_HEIGHT },
    recentFiles: [],
    fontSize: DEFAULT_FONT_SIZE,
  },
  schema: {
    fontSize: {
      type: "number",
      minimum: MIN_FONT_SIZE,
      maximum: MAX_FONT_SIZE,
    },
  },
});
优势: 类型安全的泛型参数确保
.get()
.set()
在编译时被校验,所有限制使用命名常量,模式在写入时拒绝无效值
查看examples/core.md了解数据迁移、文件监听、点符号访问以及通过IPC与渲染进程集成的内容。

Pattern 2: better-sqlite3 -- Local Database

模式2:better-sqlite3——本地数据库

Use for structured data that benefits from queries, indexes, or transactions. Always enable WAL mode.
typescript
import Database from "better-sqlite3";
import { app } from "electron";
import path from "node:path";

const DB_FILE = "app-data.db";

const db = new Database(path.join(app.getPath("userData"), DB_FILE));

// Performance pragmas -- set once at connection open
db.pragma("journal_mode = WAL");
db.pragma("synchronous = NORMAL");
db.pragma("foreign_keys = ON");
Why good: WAL mode allows concurrent reads during writes (essential for multi-window apps),
synchronous = NORMAL
balances safety and speed, foreign keys enforce referential integrity
See examples/sqlite.md for prepared statements, transactions, bulk inserts, and schema migrations.

用于受益于查询、索引或事务的结构化数据。始终启用WAL模式。
typescript
import Database from "better-sqlite3";
import { app } from "electron";
import path from "node:path";

const DB_FILE = "app-data.db";

const db = new Database(path.join(app.getPath("userData"), DB_FILE));

// 性能优化参数——连接打开时设置一次
db.pragma("journal_mode = WAL");
db.pragma("synchronous = NORMAL");
db.pragma("foreign_keys = ON");
优势: WAL模式允许写入期间进行并发读取(对多窗口应用至关重要),
synchronous = NORMAL
平衡安全性与速度,外键强制引用完整性
查看examples/sqlite.md了解预编译语句、事务、批量插入和模式迁移的内容。

Pattern 3: safeStorage -- OS Keychain Encryption

模式3:safeStorage——操作系统密钥链加密

Use for secrets (tokens, API keys, passwords). The encrypted buffer is opaque -- only your app on the same machine and user account can decrypt it.
typescript
import { safeStorage, app } from "electron";
import Store from "electron-store";

const credentialStore = new Store<Record<string, string>>({
  name: "credentials",
});

function saveSecret(key: string, plainText: string): void {
  if (!safeStorage.isEncryptionAvailable()) {
    throw new Error("OS encryption is not available");
  }
  const encrypted = safeStorage.encryptString(plainText);
  credentialStore.set(key, encrypted.toString("base64"));
}

function loadSecret(key: string): string | null {
  const stored = credentialStore.get(key);
  if (!stored) return null;
  const buffer = Buffer.from(stored, "base64");
  return safeStorage.decryptString(buffer);
}
Why good: Secrets are encrypted via the OS keychain before being persisted, base64 encoding allows storing the buffer in JSON, explicit availability check prevents crashes on unsupported systems
See examples/core.md for the full credential manager pattern and async API usage.

用于存储机密信息(令牌、API密钥、密码)。加密后的缓冲区是不透明的——只有同一台机器上的同一用户账户下的你的应用才能解密。
typescript
import { safeStorage, app } from "electron";
import Store from "electron-store";

const credentialStore = new Store<Record<string, string>>({
  name: "credentials",
});

function saveSecret(key: string, plainText: string): void {
  if (!safeStorage.isEncryptionAvailable()) {
    throw new Error("操作系统加密不可用");
  }
  const encrypted = safeStorage.encryptString(plainText);
  credentialStore.set(key, encrypted.toString("base64"));
}

function loadSecret(key: string): string | null {
  const stored = credentialStore.get(key);
  if (!stored) return null;
  const buffer = Buffer.from(stored, "base64");
  return safeStorage.decryptString(buffer);
}
优势: 机密信息在持久化前通过操作系统密钥链加密,Base64编码允许将缓冲区存储在JSON中,显式的可用性检查可防止在不支持的系统上崩溃
查看examples/core.md了解完整的凭据管理器模式和异步API用法。

Pattern 4: Storage Path Conventions

模式4:存储路径约定

All persistent data belongs under
app.getPath("userData")
. Use other paths for specific purposes.
typescript
import { app } from "electron";

// User-specific persistent data (config, databases, credentials)
const userDataDir = app.getPath("userData");
//  macOS: ~/Library/Application Support/<AppName>
//  Windows: %APPDATA%/<AppName>
//  Linux: ~/.config/<AppName>

// Temporary files (cache, downloads in progress)
const tempDir = app.getPath("temp");

// Log files
const logsDir = app.getPath("logs");

// User's documents, downloads, desktop (for file save dialogs)
const documentsDir = app.getPath("documents");
const downloadsDir = app.getPath("downloads");
Key point: The
userData
directory survives app updates. The app installation directory does not -- writing data there causes data loss on update.

所有持久化数据都应存储在
app.getPath("userData")
路径下。其他路径用于特定用途。
typescript
import { app } from "electron";

// 用户专属持久化数据(配置、数据库、凭据)
const userDataDir = app.getPath("userData");
//  macOS: ~/Library/Application Support/<AppName>
//  Windows: %APPDATA%/<AppName>
//  Linux: ~/.config/<AppName>

// 临时文件(缓存、正在下载的文件)
const tempDir = app.getPath("temp");

// 日志文件
const logsDir = app.getPath("logs");

// 用户的文档、下载、桌面目录(用于文件保存对话框)
const documentsDir = app.getPath("documents");
const downloadsDir = app.getPath("downloads");
关键点:
userData
目录在应用更新后会保留。应用安装目录则不会——写入该目录的数据会在更新时丢失。

Pattern 5: lowdb -- Lightweight JSON Database

模式5:lowdb——轻量级JSON数据库

Use when data is JSON-shaped but too complex for flat key-value (nested arrays, document collections) and does not need relational queries.
typescript
import { JSONFilePreset } from "lowdb/node";
import { app } from "electron";
import path from "node:path";

interface ProjectData {
  projects: Array<{ id: string; name: string; lastOpened: string }>;
  settings: { sortBy: "name" | "lastOpened" };
}

const DB_FILE = "projects.json";
const defaultData: ProjectData = {
  projects: [],
  settings: { sortBy: "lastOpened" },
};

const db = await JSONFilePreset<ProjectData>(
  path.join(app.getPath("userData"), DB_FILE),
  defaultData,
);

// Read
const recent = db.data.projects.toSorted((a, b) =>
  b.lastOpened.localeCompare(a.lastOpened),
);

// Write (mutate then persist)
db.data.projects.push({
  id: "abc",
  name: "New Project",
  lastOpened: new Date().toISOString(),
});
await db.write();
Why good: Plain JavaScript data access (no query language), type-safe with generics, file I/O only on explicit
.write()
call
When to prefer SQLite instead: Data exceeds ~10MB, you need indexes or joins, you need concurrent write safety, or you need partial reads (lowdb loads the entire file into memory).
</patterns>
<decision_framework>
当数据为JSON形态但过于复杂不适合扁平键值对(嵌套数组、文档集合)且不需要关系查询时使用。
typescript
import { JSONFilePreset } from "lowdb/node";
import { app } from "electron";
import path from "node:path";

interface ProjectData {
  projects: Array<{ id: string; name: string; lastOpened: string }>;
  settings: { sortBy: "name" | "lastOpened" };
}

const DB_FILE = "projects.json";
const defaultData: ProjectData = {
  projects: [],
  settings: { sortBy: "lastOpened" },
};

const db = await JSONFilePreset<ProjectData>(
  path.join(app.getPath("userData"), DB_FILE),
  defaultData,
);

// 读取
const recent = db.data.projects.toSorted((a, b) =>
  b.lastOpened.localeCompare(a.lastOpened),
);

// 写入(修改后持久化)
db.data.projects.push({
  id: "abc",
  name: "New Project",
  lastOpened: new Date().toISOString(),
});
await db.write();
优势: 纯JavaScript数据访问(无需查询语言),泛型支持类型安全,仅在显式调用
.write()
时才进行文件I/O
何时应选择SQLite: 数据超过约10MB、需要索引或连接、需要并发写入安全、或需要部分读取(lowdb会将整个文件加载到内存中)。
</patterns>
<decision_framework>

Decision Framework

决策框架

Choosing a Storage Solution

选择存储方案

What kind of data?
|
+-- User preferences / small config (theme, window size, feature flags)?
|   +-- electron-store (JSON file, schema validation, migrations)
|
+-- Secrets (tokens, API keys, passwords)?
|   +-- safeStorage + electron-store or file
|   +-- Never plain text, never unencrypted electron-store
|
+-- Structured / relational data (records, queries, indexes)?
|   +-- better-sqlite3 (WAL mode, transactions, scales to GB)
|
+-- JSON document collections (nested objects, no joins needed)?
|   +-- Small (<10MB) -> lowdb
|   +-- Large or concurrent writes -> better-sqlite3 with JSON columns
|
+-- Temporary / cache data?
|   +-- app.getPath("temp") + regular file I/O
|
+-- Session-only state (lost on quit)?
    +-- In-memory (no persistence needed)
数据类型是什么?
|
+-- 用户偏好设置 / 小型配置(主题、窗口尺寸、功能开关)?
|   +-- electron-store(JSON文件、模式验证、数据迁移)
|
+-- 机密信息(令牌、API密钥、密码)?
|   +-- safeStorage + electron-store 或 文件
|   +-- 绝不要明文存储,绝不要使用未加密的electron-store
|
+-- 结构化 / 关系型数据(记录、查询、索引)?
|   +-- better-sqlite3(WAL模式、事务、可扩展至GB级)
|
+-- JSON文档集合(嵌套对象、无需连接)?
|   +-- 小型数据(<10MB) -> lowdb
|   +-- 大型数据或并发写入 -> 带JSON列的better-sqlite3
|
+-- 临时 / 缓存数据?
|   +-- app.getPath("temp") + 常规文件I/O
|
+-- 仅会话状态(退出后丢失)?
    +-- 内存存储(无需持久化)

electron-store vs better-sqlite3

electron-store vs better-sqlite3

Criteriaelectron-storebetter-sqlite3
Data shapeFlat key-value, small JSONRelational, structured records
Data size< 1MBUp to several GB
Query capabilityGet by key, dot-notationFull SQL, indexes, joins
Concurrent accessSingle process onlyWAL mode supports multi-window
Schema evolutionMigrations by semverSQL ALTER TABLE / migration scripts
Setup complexityZero (pure JS)Native module rebuild required
Best forPreferences, feature flagsChat history, project data, logs
评估标准electron-storebetter-sqlite3
数据形态扁平键值对、小型JSON关系型、结构化记录
数据大小< 1MB可达数GB
查询能力按键获取、点符号访问完整SQL、索引、连接
并发访问仅支持单进程WAL模式支持多窗口
模式演进基于语义化版本的数据迁移SQL ALTER TABLE / 迁移脚本
配置复杂度零配置(纯JS)需要重构原生模块
最佳适用场景偏好设置、功能开关聊天记录、项目数据、日志

safeStorage vs electron-store encryptionKey

safeStorage vs electron-store encryptionKey

FeaturesafeStorageelectron-store encryptionKey
Security levelOS keychain (strong)Obfuscation only (weak)
Key managementOS manages keysKey embedded in source code
Use for secretsYesNo -- not actual encryption
Use for obfuscationOverkillYes -- prevents casual file reading
Platform supportmacOS, Windows, Linux (varies)All platforms
</decision_framework>

Detailed Resources:
  • examples/core.md - electron-store setup, migrations, watching, safeStorage credential manager, lowdb, storage paths
  • examples/sqlite.md - better-sqlite3 setup, WAL mode, prepared statements, transactions, migrations, native rebuild
  • reference.md - API quick-reference tables, path directory map, security checklist

<red_flags>
特性safeStorageelectron-store encryptionKey
安全级别操作系统密钥链(高)仅混淆(低)
密钥管理操作系统管理密钥密钥嵌入源代码中
是否适用于机密信息否——并非真正的加密
是否适用于混淆大材小用是——防止随意读取文件
平台支持macOS、Windows、Linux(差异较大)所有平台
</decision_framework>

详细资源:
  • examples/core.md - electron-store配置、数据迁移、监听、safeStorage凭据管理器、lowdb、存储路径
  • examples/sqlite.md - better-sqlite3配置、WAL模式、预编译语句、事务、数据迁移、原生模块重构
  • reference.md - API快速参考表、路径目录映射、安全检查清单

<red_flags>

RED FLAGS

警示事项

Critical Security Issues:
  • Storing tokens, API keys, or passwords in plain text (electron-store without safeStorage)
  • Using
    electron-store
    's
    encryptionKey
    option for actual secrets -- it is obfuscation, not encryption. The key is in your source code.
  • Writing persistent data to the app installation directory -- it is deleted on update
  • Giving renderer processes direct filesystem or database access -- route through IPC
Architecture Issues:
  • Not enabling WAL mode with better-sqlite3 -- causes
    SQLITE_BUSY
    errors when reading and writing concurrently
  • Using better-sqlite3 without
    @electron/rebuild
    -- native module version mismatch crashes the app at startup
  • Using electron-store for large datasets (>1MB) -- the entire file is read and written on every change
  • Running database operations in the renderer process instead of the main process
  • Not checking
    safeStorage.isEncryptionAvailable()
    before encrypting -- crashes on Linux without a secret service
Common Mistakes:
  • Calling
    safeStorage
    methods before
    app.whenReady()
    -- encryption is unavailable until the app is ready
  • Forgetting to
    db.close()
    on
    before-quit
    -- risks WAL file corruption
  • Using async functions inside
    better-sqlite3
    transactions -- the transaction commits at the first
    await
    , not at function end
  • Not using
    asarUnpack
    for better-sqlite3 in packaged builds -- the native binary fails to load from inside ASAR archives
  • Storing
    Buffer
    objects directly in electron-store -- they serialize incorrectly. Convert to base64 strings.
Gotchas & Edge Cases:
  • electron-store
    requires Electron 30+ and is ESM-only (no CommonJS)
  • safeStorage
    on Windows (DPAPI) protects data per-user but not per-app -- another app running as the same user could theoretically decrypt
  • safeStorage
    on Linux depends on the desktop environment's secret service (gnome-keyring, KWallet) -- falls back to plaintext if none is available
  • electron-store
    's
    schema
    validation uses JSON Schema draft-2020-12 via ajv -- not Zod
  • Object.groupBy
    on
    better-sqlite3
    result rows works but rows are plain objects with a null prototype -- use
    Object.hasOwn()
    not
    hasOwnProperty
</red_flags>

<critical_reminders>
严重安全问题:
  • 将令牌、API密钥或密码以明文形式存储(未使用safeStorage的electron-store)
  • 使用
    electron-store
    encryptionKey
    选项存储实际机密信息——这只是混淆,并非加密。密钥会暴露在源代码中。
  • 将持久化数据写入应用安装目录——该目录会在更新时被删除
  • 让渲染进程直接访问文件系统或数据库——应通过IPC路由
架构问题:
  • 使用better-sqlite3时未启用WAL模式——并发读写时会导致
    SQLITE_BUSY
    错误
  • 使用better-sqlite3时未使用
    @electron/rebuild
    ——原生模块版本不匹配会导致应用启动时崩溃
  • 使用electron-store存储大型数据集(>1MB)——每次变更都会完整读写整个文件
  • 在渲染进程中运行数据库操作而非主进程
  • 加密前未检查
    safeStorage.isEncryptionAvailable()
    ——在没有秘密服务的Linux系统上会崩溃
常见错误:
  • app.whenReady()
    之前调用
    safeStorage
    方法——应用就绪前加密不可用
  • before-quit
    事件中忘记调用
    db.close()
    ——可能导致WAL文件损坏
  • better-sqlite3
    事务中使用异步函数——事务会在第一个
    await
    时提交,而非函数结束时
  • 打包构建时未为better-sqlite3使用
    asarUnpack
    ——原生二进制文件无法从ASAR归档中加载
  • Buffer
    对象直接存储在electron-store中——序列化会出错。需转换为Base64字符串。
陷阱与边缘情况:
  • electron-store
    要求Electron 30+版本,且仅支持ESM(不支持CommonJS)
  • Windows上的
    safeStorage
    (DPAPI)按用户保护数据,但不按应用保护——同一用户运行的其他理论上可以解密
  • Linux上的
    safeStorage
    依赖桌面环境的秘密服务(gnome-keyring、KWallet)——如果没有则会回退到明文存储
  • electron-store
    schema
    验证通过ajv使用JSON Schema draft-2020-12——不支持Zod
  • better-sqlite3
    结果行上使用
    Object.groupBy
    可行,但行是带有null原型的普通对象——使用
    Object.hasOwn()
    而非
    hasOwnProperty
</red_flags>

<critical_reminders>

CRITICAL REMINDERS

重要提醒

All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering,
import type
, named constants)
(You MUST use
safeStorage.encryptString()
/
safeStorage.decryptString()
for secrets -- never store tokens, API keys, or passwords in plain text or in electron-store without OS-level encryption)
(You MUST store all persistent data under
app.getPath("userData")
-- never write to the app installation directory, which is replaced on updates)
(You MUST enable WAL mode (
PRAGMA journal_mode = WAL
) when using better-sqlite3 -- it prevents readers from blocking writers and avoids SQLITE_BUSY errors in multi-window apps)
(You MUST call
safeStorage.isEncryptionAvailable()
before encrypting -- it returns false before the app
ready
event and on some Linux configurations)
(You MUST rebuild better-sqlite3 for Electron's Node.js version using
@electron/rebuild
-- mismatched native bindings crash the app)
Failure to follow these rules will cause data loss, security vulnerabilities, or application crashes.
</critical_reminders>
所有代码必须遵循CLAUDE.md中的项目约定(短横线命名、命名导出、导入顺序、
import type
、命名常量)
(你必须使用
safeStorage.encryptString()
/
safeStorage.decryptString()
存储机密信息——绝不要以明文形式存储令牌、API密钥或密码,也不要在未经过操作系统级加密的情况下将其存入electron-store)
(你必须将所有持久化数据存储在
app.getPath("userData")
路径下——绝不要写入应用安装目录,该目录会在更新时被替换)
(使用better-sqlite3时必须启用WAL模式(
PRAGMA journal_mode = WAL
)——它可防止读取操作阻塞写入操作,避免多窗口应用中出现SQLITE_BUSY错误)
(加密前必须调用
safeStorage.isEncryptionAvailable()
——在应用
ready
事件触发前以及部分Linux配置下,它会返回false)
(必须使用
@electron/rebuild
针对Electron的Node.js版本重新构建better-sqlite3——不匹配的原生绑定会导致应用崩溃)
不遵循这些规则会导致数据丢失、安全漏洞或应用崩溃。
</critical_reminders>