Files
emdash-patch-imageupload/scripts/query-dumps/classify.mjs
kunthawat 2d1be52177 Emdash source with visual editor image upload fix
Fixes:
1. media.ts: wrap placeholder generation in try-catch
2. toolbar.ts: check r.ok, display error message in popover
2026-05-03 10:44:54 +07:00

174 lines
6.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env node
/**
* Analyse the per-route query dumps and classify each query.
*/
import { readFileSync, readdirSync, writeFileSync } from "node:fs";
import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url";
const __dirname = dirname(fileURLToPath(import.meta.url));
function classify(sql, params) {
const s = sql.replace(/\s+/g, " ").trim();
// Migrations / system
if (/pragma_table_info/i.test(s)) return "pragma_table_info";
if (/sqlite_master/i.test(s)) return "sqlite_master";
if (/PRAGMA/i.test(s)) return "pragma";
if (/from "kysely_migration"/i.test(s)) return "migrations_check";
if (/from "_emdash_migrations_lock"/i.test(s)) return "migrations_lock";
if (/from "_emdash_migrations"/i.test(s)) return "migrations_check";
if (/from "_emdash_collections"/i.test(s)) return "schema_collections";
if (/from "_emdash_fields"/i.test(s)) return "schema_fields";
if (/from "_emdash_setup_state"/i.test(s)) return "setup_check";
if (/from "_plugin_state"/i.test(s)) return "plugin_state";
if (/from "_emdash_cron_tasks"/i.test(s) || /_emdash_cron_tasks SET/i.test(s))
return "cron_recovery";
if (/_emdash_404_log/i.test(s)) return "404_log_migration";
if (/alter table/i.test(s)) return "ddl_alter";
if (/create table/i.test(s)) return "ddl_create";
if (/create.*index/i.test(s)) return "ddl_index";
if (/drop index/i.test(s)) return "ddl_drop_index";
if (/insert into "_emdash_migrations"/i.test(s)) return "migrations_record";
if (/delete from "options"/i.test(s)) return "options_delete";
if (/SELECT name FROM sqlite_master/i.test(s)) return "fts_table_check";
// Auth
if (/from "_emdash_sessions"/i.test(s)) return "auth_session";
if (/from "_emdash_users"/i.test(s)) return "auth_user_lookup";
if (/from "_emdash_passkeys"/i.test(s)) return "auth_passkey";
// Settings/options
if (/from "options"/i.test(s) && /LIKE/i.test(s)) {
const p0 = params?.[0];
if (typeof p0 === "string") return `options_prefix:${p0}`;
return "options_prefix";
}
if (/from "options"/i.test(s) && /"name" in/i.test(s)) {
return "options_in";
}
if (/from "options"/i.test(s) && /"name" = \?/i.test(s)) {
const p0 = params?.[0];
if (typeof p0 === "string") return `option:${p0}`;
return "option:single";
}
if (/from "options"/i.test(s)) return "option:other";
// Menus / widgets
if (/from "_emdash_menus"/i.test(s)) return "menu_lookup";
if (/from "_emdash_menu_items"/i.test(s)) return "menu_items";
if (/from "_emdash_widget_areas"/i.test(s)) {
const p0 = params?.[0];
return `widget_area:${p0 ?? ""}`;
}
if (/from "_emdash_widgets"/i.test(s)) return "widget";
// Bylines
if (/from "_emdash_content_bylines"/i.test(s)) return "byline_hydration";
if (/from "_emdash_bylines"/i.test(s)) return "byline_lookup";
// Taxonomies
if (/from "_emdash_taxonomy_defs"/i.test(s)) return "taxonomy_defs";
if (/from "content_taxonomies"/i.test(s) && /count\(/i.test(s)) return "taxonomy_counts";
if (/from "content_taxonomies"/i.test(s)) return "taxonomy_for_entries";
if (/from "taxonomies"/i.test(s)) return "taxonomy_terms";
// Author lookup (id, author_id)
if (/SELECT id, author_id FROM "ec_/i.test(s)) return "author_id_lookup";
// Content tables
const ecMatch = s.match(/from "ec_([a-z_]+)"/i) || s.match(/FROM "ec_([a-z_]+)"/);
if (ecMatch) {
const coll = ecMatch[1];
// detail vs list
if (/slug = \?/i.test(s) && /id = \?/i.test(s)) return `entry_by_slug:${coll}`;
if (/where "id" = \?/i.test(s)) return `entry_by_id:${coll}`;
if (/LIMIT \?/i.test(s)) return `collection_list:${coll}`;
if (/ORDER BY/i.test(s)) return `collection_list:${coll}`;
return `collection_other:${coll}`;
}
// Media
if (/from "_emdash_media"/i.test(s)) return "media_lookup";
// Plugins / pages dispatch
if (/from "_emdash_plugin/i.test(s)) return "plugin_lookup";
// SEO redirects
if (/from "_emdash_redirects"/i.test(s)) return "redirects";
// Comments
if (/from "_emdash_comments"/i.test(s)) return "comments";
// Default
return "other";
}
const targetArg = process.argv[2] || "sqlite";
const dir = resolve(__dirname, targetArg);
const files = readdirSync(dir).filter((f) => f.endsWith(".json") && f !== "_all.json");
// per-route classification table
const tables = {}; // { routePhase: { className: count } }
const allByClass = {}; // { className: count }
const totalDuration = {}; // { className: ms }
const routeOrder = [
"root",
"posts",
"posts_building_for_the_long_term",
"pages_about",
"category_development",
"tag_webdev",
"rss_xml",
"search",
];
const phases = ["cold", "warm"];
for (const f of files) {
const path = resolve(dir, f);
const events = JSON.parse(readFileSync(path, "utf8"));
const key = f.replace(/\.json$/, "");
tables[key] = {};
for (const e of events) {
const cls = classify(e.sql, e.params);
tables[key][cls] = (tables[key][cls] || 0) + 1;
allByClass[cls] = (allByClass[cls] || 0) + 1;
totalDuration[cls] = (totalDuration[cls] || 0) + (e.durationMs || 0);
}
}
// print table: rows = classes, cols = route.phase
const headerRoutes = [];
for (const r of routeOrder) for (const p of phases) headerRoutes.push(`${r}.${p}`);
const allClasses = Object.keys(allByClass).toSorted(
(a, b) => (allByClass[b] || 0) - (allByClass[a] || 0),
);
let out = `# Query classification (${targetArg})\n\n`;
out += `Total events: ${Object.values(allByClass).reduce((a, b) => a + b, 0)}\n\n`;
out += `## Top classes by total count\n\n`;
out += `| class | count | total_ms |\n|---|---:|---:|\n`;
for (const c of allClasses.slice(0, 20)) {
out += `| ${c} | ${allByClass[c]} | ${totalDuration[c].toFixed(2)} |\n`;
}
out += "\n";
out += `## Per-route × phase classification\n\n`;
out += `| class |`;
for (const h of headerRoutes) out += ` ${h} |`;
out += ` total |\n|---|`;
for (const _ of headerRoutes) out += "---:|";
out += "---:|\n";
for (const c of allClasses) {
out += `| ${c} |`;
let total = 0;
for (const h of headerRoutes) {
const v = tables[h]?.[c] || 0;
total += v;
out += ` ${v || ""} |`;
}
out += ` ${total} |\n`;
}
out += "\n";
out += `## Per-route totals\n\n`;
out += `| route.phase | count |\n|---|---:|\n`;
for (const h of headerRoutes) {
const total = Object.values(tables[h] || {}).reduce((a, b) => a + b, 0);
out += `| ${h} | ${total} |\n`;
}
writeFileSync(resolve(__dirname, `classification.${targetArg}.md`), out);
process.stdout.write(out);