Docs/Skills and code/src/
src/concept-store.js
import { ensureAnalysisSchema, db } from "./analysis-schema.js";
import { normalizeText } from "./metadata-utils.js";
ensureAnalysisSchema();
function findConcept(ecosystem, normalizedName, conceptType) {
return db.prepare(`
SELECT id, canonical_name, concept_type
FROM concepts
WHERE ecosystem = ?
AND normalized_name = ?
AND concept_type = ?
`).get(ecosystem, normalizedName, conceptType);
}
function findAlias(ecosystem, normalizedAlias) {
return db.prepare(`
SELECT c.id, c.canonical_name, c.concept_type
FROM concept_aliases a
JOIN concepts c ON c.id = a.concept_id
WHERE c.ecosystem = ?
AND a.normalized_alias = ?
LIMIT 1
`).get(ecosystem, normalizedAlias);
}
export function upsertConcept({
ecosystem,
name,
type = "theme",
description = null,
source = "extraction"
}) {
const canonicalName = String(name ?? "").trim();
const normalizedName = normalizeText(canonicalName);
if (!canonicalName || !normalizedName) return null;
const existing = findConcept(ecosystem, normalizedName, type) ??
findAlias(ecosystem, normalizedName);
const now = new Date().toISOString();
let concept = existing;
if (!concept) {
const result = db.prepare(`
INSERT INTO concepts (
ecosystem,
canonical_name,
normalized_name,
concept_type,
description,
created_at,
updated_at
)
VALUES (?, ?, ?, ?, ?, ?, ?)
`).run(
ecosystem,
canonicalName,
normalizedName,
type,
description,
now,
now
);
concept = {
id: Number(result.lastInsertRowid),
canonical_name: canonicalName,
concept_type: type
};
} else {
db.prepare(`
UPDATE concepts
SET updated_at = ?,
description = COALESCE(description, ?)
WHERE id = ?
`).run(now, description, concept.id);
}
db.prepare(`
INSERT OR IGNORE INTO concept_aliases (
concept_id,
alias,
normalized_alias,
source,
created_at
)
VALUES (?, ?, ?, ?, ?)
`).run(
concept.id,
canonicalName,
normalizedName,
source,
now
);
return concept;
}
export function attachConceptToChannel({
channelUrl,
conceptId,
source,
confidence = null
}) {
db.prepare(`
INSERT OR REPLACE INTO channel_concepts (
channel_url,
concept_id,
source,
confidence,
created_at
)
VALUES (?, ?, ?, ?, ?)
`).run(
channelUrl,
conceptId,
source,
confidence,
new Date().toISOString()
);
}
export function attachConceptToVideo({
videoId,
conceptId,
source,
confidence = null
}) {
db.prepare(`
INSERT OR REPLACE INTO video_concepts (
video_id,
concept_id,
source,
confidence,
created_at
)
VALUES (?, ?, ?, ?, ?)
`).run(
videoId,
conceptId,
source,
confidence,
new Date().toISOString()
);
}
Shown as it is in the repository at build time (2026-11-03); the home directory appears as <home>. If this page and the file differ, the file is right. Back to the catalogue.