Commit Graph

4 Commits

Author SHA1 Message Date
fullex
f8f0e4b19a refactor(data-services): consolidate row-to-entity mapping utilities
Introduce shared `services/utils/rowMappers.ts` exporting three helpers:
`nullsToUndefined` (renamed from the previous `stripNulls`, with a
corrected type signature that preserves `notNull()` columns unchanged),
`timestampToISO` for guaranteed-present timestamps, and
`timestampToISOOrUndefined` for nullable ones.

Migrate 9 services off hand-rolled null/date handling:

- Eliminate the `stripNulls` duplicate in MiniAppService
- Replace 18 repetitive createdAt/updatedAt ternaries with helper calls
- Fix McpServerService misuse where an optional domain field was being
  forced to a synthesized "now" value; restore honest undefined semantics
- Simplify KnowledgeBaseService.rowToKnowledgeBase via the spread idiom,
  bypassing `clean` for the `T | null` embeddingModelId field

Document the paradigm in docs/references/data/data-api-in-main.md with
standard and advanced skeletons, plus an explicit "when NOT to spread"
list covering services with field renaming, custom fallbacks, computed
fields, or sensitive-data sanitization. Per-helper design decisions
(shallow vs. recursive, rejected alternatives) live in
services/utils/README.md.
2026-04-21 03:21:05 -07:00
fullex
40719bd77c fix(MessageService): map snake_case columns from raw SQL recursive CTEs
Drizzle's `casing: 'snake_case'` config only applies to the ORM channel.
Raw SQL via `db.all(sql`...`)` returns SQLite's native snake_case columns
with no runtime mapping — the TypeScript generic on `db.all<T>()` is a
compile-time assertion only. The recursive CTEs in `getTree`,
`getBranchMessages`, and `getPathToNode` used `SELECT *` and asserted
results to `messageTable.$inferSelect` (camelCase), so downstream code
silently read `undefined` from `parentId` and `siblingsGroupId`. This
broke multi-model message grouping whenever the renderer rebuilt
branches from the database (page refresh, topic switch).

Switch to the "CTE for IDs, ORM for rows" pattern: the recursive CTE
collects IDs only (single-word columns are casing-safe), then full rows
are fetched via `db.select().from(messageTable).where(inArray(...))`
where Drizzle applies camelCase mapping automatically. CTE order is
restored via a Map since SQL IN-list does not preserve order. Rename
`tree_depth` to `treeDepth` for naming consistency.

Document the pattern as the project standard in
`docs/references/data/database-patterns.md` and add regression tests
covering all three methods plus the multi-model siblings scenario.
2026-04-14 22:49:06 -07:00
fullex
3c140fc3be refactor(data): redesign database seeding architecture with SeedRunner and versioned seeders
Replace the ad-hoc seeding system with a journal-based architecture that
tracks seed versions via app_state table and skips unchanged seeds on startup.

- Introduce ISeeder interface with name/version/description/run() contract
- Add SeedRunner orchestrator with journal-based version tracking
- Rename ISeed -> ISeeder, migrate() -> run() (align with industry conventions)
- Rename *Seed -> *Seeder classes, *Seeding.ts -> *Seeder.ts files
- Move seeders into seeding/seeders/ subdirectory for better organization
- Add hashObject utility for auto-computing version from static data sources
- PreferenceSeeder/TranslateLanguageSeeder: auto checksum via hashObject()
- PresetProviderSeeder: lazy getter using RegistryLoader.getProvidersVersion()
- Simplify DbService.onInit() to single SeedRunner.runAll() call
- Add SeedRunner tests and PreferenceSeeder tests
- Add database-seeding-guide.md with version strategy documentation

Signed-off-by: fullex <0xfullex@gmail.com>
2026-04-10 06:29:11 -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