Files
platform/scripts/rebuild-soulseek-tree.ts
pastilhasandClaude Opus 5 68f2c55ecf one directory per feature: schema.ts and queries.ts together
src/databases/officer_db/src/<feature>/{schema.ts,queries.ts}, replacing the
parallel schema/ and queries/ trees. 24 feature directories, 46 files moved with
git mv so history follows.

The parallel trees had drifted, which is what the restructure is really fixing:

  four features were named differently on each side — app-store/sidecar-installs,
  email/email-accounts, server/server-config

  operations had a schema and NO query file: its task_logs is reached directly
  from src/servers/api/task-logger.ts, bypassing this package's own boundary

  integrations had queries and NO schema, because it spans two features'
  tables — server_integrations and user_integrations

Both lopsided cases survive as directories holding one file, which states the
problem instead of hiding it across two trees.

Nothing outside the package changed how it imports. `officerdb`, `officerdb/types`
and `officerdb/db` resolve exactly as before; index.ts absorbed the path changes.
Added `"./*": "./src/*"` so the new layout is reachable — `officerdb/soulseek/schema`
— which one script needed, because soulseek is a plugin and therefore commented
out of the aggregator.

schema/index.ts became src/schema.ts, keeping the core/plugin split from earlier
tonight. drizzle.config.ts and the package's "./schema" export follow it.

Verified rather than assumed: all 52 files in the package parse, every relative
import resolves against the new layout (checked by walking each specifier to a
real file, since parsing does not check paths), and everything in the tree
importing officerdb still parses. Not typechecked — empty node_modules, frozen
installs.

One rewrite bug worth recording: the rule mapping a query module's sibling import
also matched the './schema' this pass had just written, turning it into
'../schema/queries' in 22 files. Caught by the resolver check, not by parsing —
both spellings parse fine.

Also corrects every path reference the move invalidated: src/databases/CLAUDE.md's
layout diagram, the root CLAUDE.md data section, three docs, and seven sidecar
comments naming queries/<x>.ts.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-13 01:34:04 +00:00

53 lines
2.1 KiB
TypeScript

// Re-derive the stored tree shape for every cached share snapshot, from the folder rows already in
// Postgres — no peer contact, so it works with everyone offline and is deterministic.
//
// Written for the backfill when the tree columns were added, but kept because it's the answer to any
// future change in tree shape: it runs the SAME buildTree + finishSoulseekBrowse the sidecar's ingest
// runs, so there's no second implementation to drift.
//
// bun scripts/rebuild-soulseek-tree.ts
import type { BrowsedFile } from 'officerdb';
import { eq, asc } from 'drizzle-orm';
import { db, finishSoulseekBrowse } from 'officerdb';
// soulseek is a plugin, so its tables are commented out of officerdb's schema aggregator —
// import them from the feature directly.
import { soulseekBrowseSnapshots, soulseekBrowseDirs } from 'officerdb/soulseek/schema';
import { buildTree } from '../src/servers/sidecar/slskd/browse';
const snapshots = await db
.select({
id: soulseekBrowseSnapshots.id,
userId: soulseekBrowseSnapshots.userId,
username: soulseekBrowseSnapshots.username,
status: soulseekBrowseSnapshots.status,
})
.from(soulseekBrowseSnapshots)
.orderBy(asc(soulseekBrowseSnapshots.id));
console.log(`${snapshots.length} snapshot(s) to rebuild`);
for (const snap of snapshots) {
const started = Date.now();
const rows = await db
.select({ name: soulseekBrowseDirs.name, files: soulseekBrowseDirs.files })
.from(soulseekBrowseDirs)
.where(eq(soulseekBrowseDirs.snapshotId, snap.id));
if (!rows.length) {
console.log(` #${snap.id} ${snap.username}: no folder rows, skipped`);
continue;
}
const tree = buildTree(rows.map((r) => ({ name: r.name, files: r.files as BrowsedFile[] })));
await finishSoulseekBrowse(snap.id, tree);
const roots = tree.filter((t) => t.parentPath === null);
console.log(
` #${snap.id} ${snap.username}: ${rows.length} -> ${tree.length} rows ` +
`(+${tree.length - rows.length} synthesized), ${roots.length} root(s), ` +
`${roots.reduce((n, r) => n + r.subtreeFileCount, 0)} files, ${Date.now() - started}ms`,
);
}
process.exit(0);