Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
94 commits
Select commit Hold shift + click to select a range
df00e2c
fix: rm old files
lovestaco Nov 16, 2025
2a12ac6
fix: tldr to ssr
lovestaco Nov 16, 2025
5330689
fix: mcp to ssr
lovestaco Nov 16, 2025
b21799c
fix: svg to mcp
lovestaco Nov 16, 2025
2f27065
fix: png to ssr
lovestaco Nov 16, 2025
dded2ba
fix: man-pages to ssr
lovestaco Nov 16, 2025
51949bc
fix: build man-pages command
lovestaco Nov 16, 2025
8e93450
fix: cheatsheet to ssr
lovestaco Nov 16, 2025
34dfd90
fix: sitemaps
lovestaco Nov 16, 2025
7da9429
fix: serve commands
lovestaco Nov 16, 2025
c1ecca6
Merge branch 'main' of github.com:HexmosTech/FreeDevTools into feat/ssr
lovestaco Nov 17, 2025
666c2b7
fix: install onnxruntime node
lovestaco Nov 17, 2025
a13a95f
fix: emoji to ssr
lovestaco Nov 17, 2025
4d882ee
feat: ssr options, rm critical inline css, use node adaptor
lovestaco Nov 17, 2025
90a2aaa
fix: ssr scripts
lovestaco Nov 17, 2025
04e9e9c
fix: apple and discord emoji ssr
lovestaco Nov 17, 2025
c8373d2
fix: route collisions in png
lovestaco Nov 17, 2025
1f43ed9
fix: route collisions in svg
lovestaco Nov 17, 2025
c46cacc
fix: route collisions in mcp
lovestaco Nov 17, 2025
76521c1
fix: route collisions in tldr
lovestaco Nov 17, 2025
5e31c1b
fix: route collisions in cheatsheet
lovestaco Nov 17, 2025
8d932c7
fix: route collisions in tools
lovestaco Nov 17, 2025
2620e5d
fix: mandb
lovestaco Nov 19, 2025
50df567
fix: build only svg
lovestaco Nov 19, 2025
457e55f
rm compressor, streaming, build flags
lovestaco Nov 19, 2025
28b8561
fix: _ all except svg
lovestaco Nov 19, 2025
9ba8e30
fix: svg icon queries
lovestaco Nov 19, 2025
5b50850
Revert "fix: _ all except svg"
lovestaco Nov 19, 2025
d781f42
fix: _ all except png
lovestaco Nov 19, 2025
c71915d
fix: _t -> t
lovestaco Nov 19, 2025
02bcda4
fix: png icon queries
lovestaco Nov 19, 2025
f3ee01d
Revert "fix: _ all except png"
lovestaco Nov 19, 2025
43dbe0f
fix: man page merge conflicts
lovestaco Nov 19, 2025
6bb8795
Merge branch 'main' of github.com:HexmosTech/FreeDevTools into feat/ssr
lovestaco Nov 19, 2025
86a8eda
fix: make
lovestaco Nov 22, 2025
45f8366
fix: mergeconflics
lovestaco Nov 22, 2025
423ba28
fix: man-pages to ssr
lovestaco Nov 22, 2025
656a425
fix: man-page pagination
lovestaco Nov 22, 2025
db7f728
fix: man pages crawlable
lovestaco Nov 22, 2025
83fa336
fix: t
lovestaco Nov 22, 2025
1592208
fix: t
lovestaco Nov 22, 2025
6cc8e46
fix: emoji builder
lovestaco Nov 22, 2025
8c8e5d8
fix: man pages sitemap
lovestaco Nov 22, 2025
b612449
fix: rm unecessary files
lovestaco Nov 22, 2025
06beb17
fix: emoji normalize db
lovestaco Nov 22, 2025
1089943
fix: emoji discord and apple
lovestaco Nov 22, 2025
cc99952
fix: emoji sitemap
lovestaco Nov 22, 2025
80264cc
fix: emoji sitemap
lovestaco Nov 22, 2025
e4842d0
fix: optimize emoji query
lovestaco Nov 22, 2025
89ca0fd
fix: optimize emoji query with paginated
lovestaco Nov 22, 2025
76a03b3
fix: emoji sanity check and fix issues
lovestaco Nov 22, 2025
df731e3
fix: mmap for all dbs
lovestaco Nov 22, 2025
10b0510
fix: _ mcp, tldr, t, c
lovestaco Nov 22, 2025
fbee6cc
fix: svg icons node sqlite
lovestaco Nov 23, 2025
97dca5f
fix: svg icons cache
lovestaco Nov 23, 2025
1ee4b3b
fix: revert _
lovestaco Nov 23, 2025
f3158d9
fix: svg parallelize
lovestaco Nov 23, 2025
5862d48
fix: svg queries
lovestaco Nov 23, 2025
46dae5c
fix: svg queries use precomputed
lovestaco Nov 23, 2025
0d08276
fix: svg db indexes
lovestaco Nov 23, 2025
931eb6a
fix: update convo
lovestaco Nov 23, 2025
a81a662
fix: svg db queries
lovestaco Nov 23, 2025
01dc521
fix: cheatsheet hashing and conversion for ssr
LinceMathew Nov 29, 2025
f4e65a0
no db in git
LinceMathew Nov 29, 2025
ae77a24
fix: hashing for manpages
LinceMathew Nov 29, 2025
a823a76
refactor: migrate MCP data fetching from Astro Content Collections to…
LinceMathew Nov 30, 2025
2f9275c
Merge branch 'feat/ssr-new' of github.com:HexmosTech/freedevtools int…
LinceMathew Nov 30, 2025
5670024
Merge branch 'feat/ssr-new' of github.com:HexmosTech/freedevtools int…
LinceMathew Nov 30, 2025
8c1859c
fix: bun import, sqlite issues and refactoring
LinceMathew Nov 30, 2025
6132ef4
using bun for instead of node commands
LinceMathew Nov 30, 2025
c606f70
Merge branch 'feat/ssr-new' of github.com:HexmosTech/freedevtools int…
LinceMathew Nov 30, 2025
ce3494e
fix: worker pool for cheatsheets
LinceMathew Nov 30, 2025
cfca82b
fix: worker pool for manpages
LinceMathew Nov 30, 2025
4efa9e2
fix: worker pool for mcp
LinceMathew Dec 1, 2025
35c4e10
Merge branch 'feat/ssr-new' of github.com:HexmosTech/freedevtools int…
LinceMathew Dec 1, 2025
3acc725
Merge branch 'feat/ssr-new' of github.com:HexmosTech/freedevtools int…
LinceMathew Dec 2, 2025
64e148c
Merge branch 'feat/ssr-new' of github.com:HexmosTech/freedevtools int…
LinceMathew Dec 2, 2025
d6c0fa1
wip: mcp ssr, latency fix
LinceMathew Dec 4, 2025
420e5e3
feat: Implement category pagination and consolidate total counts into…
LinceMathew Dec 5, 2025
bdf5a18
Merge branch 'feat/ssr-new' of github.com:HexmosTech/freedevtools int…
LinceMathew Dec 5, 2025
344c039
query-fix, category hashing and sitemap fix
LinceMathew Dec 6, 2025
4ddee96
fix sitemap pagination urls for mcp
LinceMathew Dec 6, 2025
27f824e
feat: Add Makefile targets for cheatsheets and man pages testing, swi…
LinceMathew Dec 6, 2025
68a3c4c
Merge branch 'feat/ssr-new' of github.com:HexmosTech/freedevtools int…
LinceMathew Dec 6, 2025
c2f984a
cheatsheet-path fix
LinceMathew Dec 6, 2025
ba41de0
fix: various sitemap fixes
LinceMathew Dec 6, 2025
1a0533b
Merge branch 'feat/ssr-new' of github.com:HexmosTech/freedevtools int…
LinceMathew Dec 6, 2025
e1561a7
merge fixes
LinceMathew Dec 6, 2025
e88f605
merge fixes
LinceMathew Dec 6, 2025
229e139
feat: Implement paginated man page display and sitemap generation wit…
LinceMathew Dec 7, 2025
a085666
sitemap, schema fixes
LinceMathew Dec 7, 2025
4e574ec
Merge branch 'feat/ssr-new' of github.com:HexmosTech/freedevtools int…
LinceMathew Dec 8, 2025
5ffd6a2
fix: mapage path
LinceMathew Dec 8, 2025
6599653
Merge branch 'feat/ssr-new' of github.com:HexmosTech/freedevtools int…
LinceMathew Dec 9, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions frontend/.vscode/extensions.json

This file was deleted.

54 changes: 0 additions & 54 deletions frontend/.vscode/settings.json

This file was deleted.

Binary file removed frontend/bun.lockb
Binary file not shown.
43 changes: 43 additions & 0 deletions frontend/db/cheatsheets/cheatsheets-schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
export interface Cheatsheet {
hash_id: bigint;
category: string;
slug: string;
content: string;
title: string | null;
description: string;
keywords: string[]; // JSON array stored as TEXT
}

export interface Category {
id: number;
name: string;
slug: string;
description: string;
keywords: string[]; // JSON array
features: string[]; // JSON array
}

export interface Overview {
id: number;
total_count: number;
}

// Raw database row types (before JSON parsing)
export interface RawCheatsheetRow {
id: number;
category: string;
slug: string;
content: string;
title: string | null;
description: string;
keywords: string; // JSON string
}

export interface RawCategoryRow {
id: number;
name: string;
slug: string;
description: string;
keywords: string; // JSON string
features: string; // JSON string
}
40 changes: 40 additions & 0 deletions frontend/db/cheatsheets/cheatsheets-utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { query } from './cheatsheets-worker-pool';
import type {
Category,
Cheatsheet,
} from './cheatsheets-schema';

export interface CategoryWithPreviews extends Category {
cheatsheetCount: number;
previewCheatsheets: Array<{ slug: string }>;
}

export async function getTotalCheatsheets(): Promise<number> {
return query.getTotalCheatsheets();
}

export async function getTotalCategories(): Promise<number> {
return query.getTotalCategories();
}

export async function getAllCategories(
page: number = 1,
itemsPerPage: number = 30
): Promise<CategoryWithPreviews[]> {
return query.getAllCategories(page, itemsPerPage);
}

export async function getCheatsheetsByCategory(categorySlug: string): Promise<Cheatsheet[]> {
return query.getCheatsheetsByCategory(categorySlug);
}

export async function getCategoryBySlug(slug: string): Promise<Category | null> {
return query.getCategoryBySlug(slug);
}

export async function getCheatsheetByCategoryAndSlug(
categorySlug: string,
cheatsheetSlug: string
): Promise<Cheatsheet | null> {
return query.getCheatsheetByCategoryAndSlug(categorySlug, cheatsheetSlug);
}
230 changes: 230 additions & 0 deletions frontend/db/cheatsheets/cheatsheets-worker-pool.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
/**
* Worker pool manager for SQLite queries using bun:sqlite
* Manages 2 worker threads with round-robin query distribution
*/

import { existsSync } from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
import { Worker } from 'worker_threads';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

const WORKER_COUNT = 2;
let workers: Worker[] = [];
let workerIndex = 0;
let initPromise: Promise<void> | null = null;
const pendingQueries = new Map<string, { resolve: (value: any) => void; reject: (reason?: any) => void }>();

interface QueryMessage {
id: string;
type: string;
params: any;
}

interface QueryResponse {
id: string;
result?: any;
error?: string;
}

function getDbPath(): string {
return path.resolve(process.cwd(), 'db/all_dbs/cheatsheets-db.db');
}

/**
* Initialize the worker pool
*/
async function initWorkers(): Promise<void> {
if (workers.length > 0) {
return;
}

if (initPromise) {
return initPromise;
}

const initStartTime = Date.now();
console.log(`[CHEATSHEETS_DB] Initializing worker pool with ${WORKER_COUNT} workers...`);

initPromise = new Promise((resolve, reject) => {
// Resolve worker path - try multiple locations
const projectRoot = process.cwd();
const sourceWorkerPath = path.join(projectRoot, 'db', 'cheatsheets', 'cheatsheets-worker');
const distWorkerPath = path.join(projectRoot, 'dist', 'server', 'chunks', 'db', 'cheatsheets', 'cheatsheets-worker');
const relativeWorkerPath = path.join(__dirname, 'cheatsheets-worker');

// Try .js first (for compiled output), then .ts (for development/TypeScript)
// Priority: dist (built) > source (dev) > relative (fallback)
let workerPath: string | null = null;

// Check dist directory first (production build)
if (existsSync(`${distWorkerPath}.js`)) {
workerPath = `${distWorkerPath}.js`;
} else if (existsSync(`${distWorkerPath}.ts`)) {
workerPath = `${distWorkerPath}.ts`;
}
// Check source directory (development)
else if (existsSync(`${sourceWorkerPath}.js`)) {
workerPath = `${sourceWorkerPath}.js`;
} else if (existsSync(`${sourceWorkerPath}.ts`)) {
workerPath = `${sourceWorkerPath}.ts`;
}
// Fallback: try relative to current file location
else if (existsSync(`${relativeWorkerPath}.js`)) {
workerPath = `${relativeWorkerPath}.js`;
} else if (existsSync(`${relativeWorkerPath}.ts`)) {
workerPath = `${relativeWorkerPath}.ts`;
}

if (!workerPath) {
const error = new Error(
`Worker file not found. Checked:\n` +
` - ${distWorkerPath}.js (production)\n` +
` - ${sourceWorkerPath}.ts (development)\n` +
` - ${relativeWorkerPath}.ts (fallback)\n` +
`Make sure the db/cheatsheets/cheatsheets-worker.ts file exists and is copied during build.`
);
reject(error);
return;
}

const dbPath = getDbPath();

const pendingWorkers: Worker[] = [];
let initializedCount = 0;

for (let i = 0; i < WORKER_COUNT; i++) {
const worker = new Worker(workerPath, {
workerData: {
dbPath,
workerId: i,
},
});

// Increase max listeners to prevent memory leak warnings with concurrent queries
worker.setMaxListeners(100);

worker.on('message', (msg) => {
if (msg.ready) {
initializedCount++;
if (initializedCount === WORKER_COUNT) {
workers = pendingWorkers;
const initEndTime = Date.now();
console.log(`[CHEATSHEETS_DB] Worker pool initialized in ${initEndTime - initStartTime}ms`);
initPromise = null;
resolve();
}
} else if (msg.id) {
const { resolve, reject } = pendingQueries.get(msg.id) || {};
if (resolve && reject) {
if (msg.error) {
reject(new Error(msg.error));
} else {
resolve(msg.result);
}
pendingQueries.delete(msg.id);
}
}
});

worker.on('error', (err) => {
console.error(`[CHEATSHEETS_DB] Worker ${i} error:`, err);
initPromise = null;
reject(err);
});

worker.on('exit', (code) => {
if (code !== 0) {
console.error(`[CHEATSHEETS_DB] Worker ${i} exited with code ${code}`);
}
});

pendingWorkers.push(worker);
}
});

return initPromise;
}

/**
* Execute a query using the worker pool
*/
async function executeQuery(type: string, params: any): Promise<any> {
await initWorkers();

// Round-robin worker selection
const worker = workers[workerIndex % workers.length];
workerIndex = (workerIndex + 1) % workers.length;

const startTime = new Date();
// console.log(`[CHEATSHEETS_DB][${startTime.toISOString()}] Dispatching ${type}`);
return new Promise((resolve, reject) => {
const queryId = `${Date.now()}-${Math.random()}`;
const timeout = setTimeout(() => {
pendingQueries.delete(queryId);
reject(new Error(`Query timeout: ${type}`));
}, 30000); // 30 second timeout

pendingQueries.set(queryId, {
resolve: (val) => {
clearTimeout(timeout);
resolve(val);
},
reject: (err) => {
clearTimeout(timeout);
reject(err);
}
});

const message: QueryMessage = {
id: queryId,
type,
params,
};

worker.postMessage(message);
});
}

/**
* Cleanup workers (for graceful shutdown)
*/
export function cleanupWorkers(): Promise<void> {
return Promise.all(
workers.map(
(worker) =>
new Promise<void>((resolve) => {
worker.terminate().then(() => resolve());
})
)
).then(() => {
workers = [];
workerIndex = 0;
initPromise = null;
});
}

// Export query functions
export const query = {
getTotalCheatsheets: () => executeQuery('getTotalCheatsheets', {}),
getTotalCategories: () => executeQuery('getTotalCategories', {}),
getCheatsheetsByCategory: (categorySlug: string) =>
executeQuery('getCheatsheetsByCategory', { categorySlug }),
getAllCategories: (
page: number,
itemsPerPage: number
) =>
executeQuery('getAllCategories', {
page,
itemsPerPage,
}),
getCategoryBySlug: (slug: string) => executeQuery('getCategoryBySlug', { slug }),
getCheatsheetByCategoryAndSlug: (categorySlug: string, cheatsheetSlug: string) =>
executeQuery('getCheatsheetByCategoryAndSlug', { categorySlug, cheatsheetSlug }),
};

void initWorkers().catch((err) => {
console.error('[CHEATSHEETS_DB] Failed to warm worker pool:', err);
});
Loading