Commit Graph

8 Commits

Author SHA1 Message Date
fullex
9c1cbfae8e feat(concurrency): add createLatestReconciler and migrate ApiGateway (#16239) 2026-06-21 12:47:33 +08:00
fullex
3ad5eef096 feat(diagnostics): add opt-in CS_DIAGNOSTICS performance facility
Promote the temporary boot profiler into a permanent, opt-in diagnostics facility gated by CS_DIAGNOSTICS (off by default, zero overhead when unset). Move it to src/main/core/diagnostics.ts so the db and lifecycle layers no longer cross-import a lifecycle-internal file.

Probes, all gated by the same flag: per-service init timing, phase service spans, event-loop lag, and a whenReady V8 CPU profile (carried over); slow DB queries, now covering interactive-transaction interiors and batches (not just client.execute); slow IPC handlers (BaseService.ipcHandle); window construction + ready-to-show latency (WindowManager); and slow DataApi requests (ApiServer).

DataApi request duration is consolidated to a single monotonic performance.now() measurement in handleRequest, computed only when enabled; the redundant Date.now() duration in MiddlewareEngine is removed and metadata.duration is now optional.

Packaged-build safe: the CPU profile is written to the app logs directory (not process.cwd()) and a failed write can never break boot. Thresholds live in SLOW_THRESHOLD_MS; usage in docs/guides/diagnostics.md.

Also demote per-service stop/destroy logs to debug to quiet shutdown output.
2026-06-06 00:16:25 -07:00
fullex
16e0b44eb9 refactor(logger): move LoggerService to core/logger/
LoggerService is a preboot singleton already consumed by
core/preboot/ modules, so its previous home in src/main/services/
violated the core→services layering. src/main/core/README.md
already lists "Logging infrastructure" as belonging in core/; this
commit makes reality match the docs.

The relocation also exposed 13 files that bypassed the @logger
alias with relative paths or @main/services/LoggerService directly.
Normalize them to the canonical @logger form so any future move
only requires an alias update, not call-site changes.

Only the main-process @logger alias (tsconfig.node.json and the
main section of electron.vite.config.ts) is retargeted. The
renderer-side LoggerService and its separate renderer alias are
untouched.

Signed-off-by: fullex <0xfullex@gmail.com>
2026-04-11 20:10:52 -07:00
亢奋猫
a83f98fd24 docs: consolidate bilingual docs, add link checker and architecture overview (#14138)
Co-authored-by: fullex <106392080+0xfullex@users.noreply.github.com>
2026-04-09 16:01:40 +08:00
fullex
44b5edce54 feat(preboot): add core/preboot subsystem for userData location
Introduce a new `src/main/core/preboot/` subsystem that owns the
synchronous setup happening before `application.bootstrap()` runs. This
is the follow-up promised by c17dd033f to rewire v1's `initAppDataDir()`
to read from BootConfig instead of `~/.cherrystudio/config/config.json`.

## Startup phase vocabulary

The v2 main process now has three clearly-named startup phases, with the
definitions written into `src/main/core/README.md` as the authoritative
source:

  - preboot  — synchronous setup before the lifecycle container is
               built. BootConfig load, userData resolution, top-level
               Electron APIs. No DI, no services. Owned by core/preboot/.
  - bootstrap — the `application.bootstrap()` orchestration function.
                Freezes the path registry, runs Background/BeforeReady/
                WhenReady stages. NestJS/Spring-style term, the only
                meaning of "bootstrap" in v2 from this PR onward.
  - running  — steady state after bootstrap() returns.

v1's `src/main/bootstrap.ts` used "bootstrap" in the OS "early-boot
loader" sense, which clashes with the NestJS-style meaning v2's lifecycle
code has been using. "preboot" disambiguates without touching v2's
bootstrap terminology.

## userData location resolution

`resolveUserDataLocation()` is a single entry point that:

  1. Applies any pending relocation (see below).
  2. Reads `BootConfig.app.user_data_path[exe]` and calls setPath if
     valid.
  3. Falls back to portable-mode `<PORTABLE_EXECUTABLE_DIR>/data`.
  4. Otherwise leaves Electron's default userData.

Step 2 mirrors v1's `getAppDataPathFromConfig()` path-validity check
(existsSync + accessSync W_OK). Portable fallback mirrors v1's branch
character-for-character. Only the data source changes: BootConfig, not
v1 config.json. v1→v2 data migration is handled separately by
BootConfigMigrator (from c17dd033f).

## Pending relocation (new)

A new BootConfig field `temp.user_data_relocation` under a new top-level
`temp.*` namespace holds in-flight userData relocation state:

    { status: 'pending', from, to }
    { status: 'failed',  from, to, error, failedAt }
    null

The `temp.*` namespace is reserved for ephemeral runtime state that
should never be backed up or synced (restoring a stale temp.* entry
could re-execute a relocation that already happened).

When a pending request is present, `executePendingRelocation()`
synchronously copies the entire `from` tree to `to` with
`fs.cpSync({ recursive: true, force: true, verbatimSymlinks: true })`,
then atomically commits the new location to BootConfig
(`app.user_data_path[exe] = to` + clear `temp.user_data_relocation` +
flush via temp file + rename).

Pre-flight checks fail fast before any bytes are copied:
  - from !== to
  - to is not inside from (with path.sep guard against sibling-prefix
    false positives like /a vs /ab)
  - from exists
  - path.dirname(to) exists and is writable

On failure, the error is recorded as a `failed` state in BootConfig and
the function returns normally, letting Step 2 fall through to the
existing user_data_path. The `failed` record is the entry point for a
future renderer recovery UI (retry/abandon/investigate); this PR does
not implement the UI.

`failed` states are intentionally NOT auto-retried — that would turn a
one-off environmental failure into an infinite loop.

## v1 two-phase copy abandoned

v1's `copyOccupiedDirsInMainProcess()` worked around Windows file locks
on `occupiedDirs` (logs, Network, Partitions/webview/Network) by
splitting the copy into two phases (renderer copies the bulk, main
copies the locked dirs during the narrow "no renderer yet" window).

v2 abandons that distinction entirely: the previous process has fully
exited before preboot runs, so nothing is locked, and the entire
userData tree is copied as a single opaque unit. `occupiedDirs` has no
meaning in v2 and is deprecated in `packages/shared/config/constant.ts`
— it is kept on disk only because two v1-era call sites (bootstrap.ts
and BasicDataSettings.tsx) still reference it during the transition.

## Deprecations (code unchanged)

Two v1 files have their file-header @deprecated comments strengthened
but their code bodies untouched:

  - src/main/bootstrap.ts — no longer imported anywhere; the file stays
    on disk as reference for the follow-up cleanup PR.
  - src/main/utils/init.ts — initAppDataDir is dead code;
    updateAppDataConfig is still called by the v1 IPC handler at
    src/main/ipc.ts:300 (out of scope for this PR).

## Known limitation (pre-existing)

LoggerService opens its winston file transport at module-load time
pointing at `app.getPath('logs')`, which on Windows/Linux resolves to
`<userData>/logs`. This happens BEFORE preboot runs, so the winston
file handle is bound to the pre-setPath path. After preboot calls
setPath('userData', <new>), winston keeps writing to the old handle.
Post-relocation logs therefore appear at the old location until the
next restart. This is a pre-existing v2 issue (LoggerService doesn't
respond to setPath) and is documented in executePendingRelocation's
doc comment; a follow-up should either lazy-open winston transports
or teach LoggerService to rotate on userData change.

## Out of scope (follow-up cleanup PR)

  - App_SetAppDataPath IPC handler migration to write BootConfig
    pending_relocation instead of v1 argv protocol
  - BasicDataSettings.tsx renderer flow migration
  - src/main/bootstrap.ts and src/main/utils/init.ts deletion
  - occupiedDirs constant deletion
  - LoggerService setPath responsiveness

## Tests

25 unit tests in userDataLocation.test.ts covering:
  - getNormalizedExecutablePath (5 platform branches)
  - resolveUserDataLocation normal resolution (9 scenarios including
    AppImage/portable normalized key matching)
  - resolveUserDataLocation pending relocation (11 scenarios including
    pre-flight failure cases and the sibling-prefix regression guard)

Signed-off-by: fullex <0xfullex@gmail.com>
2026-04-08 01:03:45 -07:00
fullex
e8070115cf docs: move lifecycle/application docs to docs/en/references/lifecycle
Move detailed lifecycle and application documentation from source-adjacent
READMEs to docs/en/references/lifecycle/, following the same organizational
pattern as docs/en/references/data/. Source-adjacent READMEs are reduced to
lean pointers. Also add main process application.get() examples to data docs.

Signed-off-by: fullex <0xfullex@gmail.com>
2026-03-25 01:17:57 -07:00
fullex
6e3838389b feat(application): add Application class with lifecycle bootstrap orchestration
Signed-off-by: fullex <0xfullex@gmail.com>
2026-03-24 00:04:04 -07:00
fullex
b52b986311 docs(core): add README explaining core directory purpose
Signed-off-by: fullex <0xfullex@gmail.com>
2026-03-24 00:04:04 -07:00