Syntax changes in front and back end dirs. These do not affect functionality and are purely intended to better capture and describe the app's functionality and purpose

This commit is contained in:
KS Jannette
2026-08-03 00:07:58 -04:00
parent 4a5e5d6612
commit 0352bdf516
42 changed files with 812 additions and 812 deletions

View File

@@ -1,9 +1,9 @@
import { describe, it, expect, vi, beforeEach, afterEach, type Mock } from 'vitest';
vi.mock('../stores/notebookStore.js', () => ({
getNotebook: vi.fn(),
vi.mock('../stores/sourceCorpusStore.js', () => ({
getSourceCorpus: vi.fn(),
getSources: vi.fn(),
getChunksForNotebook: vi.fn(),
getChunksForSourceCorpus: vi.fn(),
buildSourceGroups: vi.fn(),
}));
@@ -26,14 +26,14 @@ vi.mock('../logger.js', () => ({
}));
import { triggerPreGeneration } from './preGenerationService.js';
import * as notebookStore from '../stores/notebookStore.js';
import * as sourceCorpusStore from '../stores/sourceCorpusStore.js';
import * as documentCacheStore from '../stores/documentCacheStore.js';
import { generateStudyGuide, generateFaq, generateExecutiveBrief } from './documentService.js';
const mockedGetNotebook = notebookStore.getNotebook as Mock;
const mockedGetSources = notebookStore.getSources as Mock;
const mockedGetChunksForNotebook = notebookStore.getChunksForNotebook as Mock;
const mockedBuildSourceGroups = notebookStore.buildSourceGroups as Mock;
const mockedGetSourceCorpus = sourceCorpusStore.getSourceCorpus as Mock;
const mockedGetSources = sourceCorpusStore.getSources as Mock;
const mockedGetChunksForSourceCorpus = sourceCorpusStore.getChunksForSourceCorpus as Mock;
const mockedBuildSourceGroups = sourceCorpusStore.buildSourceGroups as Mock;
const mockedIsGenerating = documentCacheStore.isGenerating as Mock;
const mockedInvalidate = documentCacheStore.invalidate as Mock;
const mockedMarkGenerating = documentCacheStore.markGenerating as Mock;
@@ -43,10 +43,10 @@ const mockedGenerateStudyGuide = generateStudyGuide as Mock;
const mockedGenerateFaq = generateFaq as Mock;
const mockedGenerateExecutiveBrief = generateExecutiveBrief as Mock;
function setupNotebook(id: string): void {
mockedGetNotebook.mockReturnValue({ id });
function setupSourceCorpus(id: string): void {
mockedGetSourceCorpus.mockReturnValue({ id });
mockedGetSources.mockReturnValue([{ id: 's1' }, { id: 's2' }]);
mockedGetChunksForNotebook.mockReturnValue([{ id: 'c1', text: 'hello' }]);
mockedGetChunksForSourceCorpus.mockReturnValue([{ id: 'c1', text: 'hello' }]);
mockedBuildSourceGroups.mockReturnValue([{ docIndex: 1, name: 'Doc', chunks: [{ text: 'hello' }] }]);
}
@@ -66,14 +66,14 @@ afterEach(() => {
});
describe('triggerPreGeneration guards', () => {
it('does nothing if the notebook does not exist', () => {
mockedGetNotebook.mockReturnValue(null);
it('does nothing if the sourceCorpus does not exist', () => {
mockedGetSourceCorpus.mockReturnValue(null);
triggerPreGeneration('nb-missing');
expect(mockedGetSources).not.toHaveBeenCalled();
});
it('does nothing if fewer than 2 sources', () => {
mockedGetNotebook.mockReturnValue({ id: 'nb-few' });
mockedGetSourceCorpus.mockReturnValue({ id: 'nb-few' });
mockedGetSources.mockReturnValue([{ id: 's1' }]);
triggerPreGeneration('nb-few');
expect(mockedMarkGenerating).not.toHaveBeenCalled();
@@ -82,7 +82,7 @@ describe('triggerPreGeneration guards', () => {
describe('queuing when already generating', () => {
it('invalidates cache and skips runPreGeneration', () => {
setupNotebook('nb-q');
setupSourceCorpus('nb-q');
mockedIsGenerating.mockReturnValue(true);
triggerPreGeneration('nb-q');
@@ -94,7 +94,7 @@ describe('queuing when already generating', () => {
describe('debounce', () => {
it('does not run generation immediately', () => {
setupNotebook('nb-debounce');
setupSourceCorpus('nb-debounce');
mockedIsGenerating.mockReturnValue(false);
stubGeneratorsOk();
@@ -104,7 +104,7 @@ describe('debounce', () => {
});
it('runs generation after the debounce delay', async () => {
setupNotebook('nb-delay');
setupSourceCorpus('nb-delay');
mockedIsGenerating.mockReturnValue(false);
stubGeneratorsOk();
@@ -119,7 +119,7 @@ describe('debounce', () => {
});
it('resets the timer on repeated calls, running generation only once', async () => {
setupNotebook('nb-batch');
setupSourceCorpus('nb-batch');
mockedIsGenerating.mockReturnValue(false);
stubGeneratorsOk();
@@ -140,7 +140,7 @@ describe('debounce', () => {
describe('runPreGeneration (via triggerPreGeneration)', () => {
it('runs all three generators and caches results', async () => {
setupNotebook('nb-happy');
setupSourceCorpus('nb-happy');
mockedIsGenerating.mockReturnValue(false);
stubGeneratorsOk();
@@ -158,7 +158,7 @@ describe('runPreGeneration (via triggerPreGeneration)', () => {
});
it('marks generating before starting and clears it after', async () => {
setupNotebook('nb-flag');
setupSourceCorpus('nb-flag');
mockedIsGenerating.mockReturnValue(false);
stubGeneratorsOk();
@@ -173,7 +173,7 @@ describe('runPreGeneration (via triggerPreGeneration)', () => {
});
it('still clears generating flag when a generator fails', async () => {
setupNotebook('nb-partial');
setupSourceCorpus('nb-partial');
mockedIsGenerating.mockReturnValue(false);
mockedGenerateStudyGuide.mockRejectedValue(new Error('boom'));
mockedGenerateFaq.mockResolvedValue({ subject: 'ok' });
@@ -190,9 +190,9 @@ describe('runPreGeneration (via triggerPreGeneration)', () => {
});
});
describe('re-trigger for queued notebooks', () => {
describe('re-trigger for queued sourceCorpora', () => {
it('runs a second generation cycle after the first finishes', async () => {
setupNotebook('nb-re');
setupSourceCorpus('nb-re');
mockedIsGenerating.mockReturnValueOnce(false).mockReturnValueOnce(true);
let resolveFirst: (value: { title: string }) => void;

View File

@@ -1,7 +1,7 @@
'use strict';
import logger from '../logger.js';
import * as notebookStore from '../stores/notebookStore.js';
import * as sourceCorpusStore from '../stores/sourceCorpusStore.js';
import * as documentCacheStore from '../stores/documentCacheStore.js';
import {
generateStudyGuide,
@@ -29,45 +29,45 @@ const DEBOUNCE_MS = 5000;
const pendingReGen = new Set<string>();
const debounceTimers = new Map<string, ReturnType<typeof setTimeout>>();
export function triggerPreGeneration(notebookId: string): void {
const notebook = notebookStore.getNotebook(notebookId);
if (!notebook) return;
export function triggerPreGeneration(sourceCorpusId: string): void {
const sourceCorpus = sourceCorpusStore.getSourceCorpus(sourceCorpusId);
if (!sourceCorpus) return;
const sources = notebookStore.getSources(notebookId);
const sources = sourceCorpusStore.getSources(sourceCorpusId);
if (sources.length < MIN_SOURCES) return;
if (documentCacheStore.isGenerating(notebookId)) {
pendingReGen.add(notebookId);
documentCacheStore.invalidate(notebookId);
logger.debug({ notebookId }, 'pre-generation in progress, queued re-generation');
if (documentCacheStore.isGenerating(sourceCorpusId)) {
pendingReGen.add(sourceCorpusId);
documentCacheStore.invalidate(sourceCorpusId);
logger.debug({ sourceCorpusId }, 'pre-generation in progress, queued re-generation');
return;
}
const existingTimer = debounceTimers.get(notebookId);
const existingTimer = debounceTimers.get(sourceCorpusId);
if (existingTimer !== undefined) {
clearTimeout(existingTimer);
}
debounceTimers.set(
notebookId,
sourceCorpusId,
setTimeout(() => {
debounceTimers.delete(notebookId);
runPreGeneration(notebookId);
debounceTimers.delete(sourceCorpusId);
runPreGeneration(sourceCorpusId);
}, DEBOUNCE_MS)
);
logger.debug({ notebookId, debounceMs: DEBOUNCE_MS }, 'pre-generation debounced');
logger.debug({ sourceCorpusId, debounceMs: DEBOUNCE_MS }, 'pre-generation debounced');
}
function runPreGeneration(notebookId: string): void {
function runPreGeneration(sourceCorpusId: string): void {
try {
const sources = notebookStore.getSources(notebookId);
const chunks = notebookStore.getChunksForNotebook(notebookId);
const sourceGroups = notebookStore.buildSourceGroups(notebookId, chunks) as SourceGroup[];
const sources = sourceCorpusStore.getSources(sourceCorpusId);
const chunks = sourceCorpusStore.getChunksForSourceCorpus(sourceCorpusId);
const sourceGroups = sourceCorpusStore.buildSourceGroups(sourceCorpusId, chunks) as SourceGroup[];
documentCacheStore.invalidate(notebookId);
documentCacheStore.markGenerating(notebookId);
documentCacheStore.invalidate(sourceCorpusId);
documentCacheStore.markGenerating(sourceCorpusId);
logger.info(
{ notebookId, sourceCount: sources.length, chunkCount: chunks.length },
{ sourceCorpusId, sourceCount: sources.length, chunkCount: chunks.length },
'background pre-generation started'
);
@@ -75,31 +75,31 @@ function runPreGeneration(notebookId: string): void {
async ([type, generator]) => {
try {
const document = await generator(sourceGroups);
documentCacheStore.setCachedDocument(notebookId, type, document, sources);
logger.info({ notebookId, type }, 'background pre-generation complete for type');
documentCacheStore.setCachedDocument(sourceCorpusId, type, document, sources);
logger.info({ sourceCorpusId, type }, 'background pre-generation complete for type');
} catch (err) {
const error = err as Error;
logger.error({ notebookId, type, err: error.message }, 'background pre-generation failed for type');
logger.error({ sourceCorpusId, type, err: error.message }, 'background pre-generation failed for type');
}
}
);
Promise.all(jobs)
.then(() => {
logger.info({ notebookId }, 'all background pre-generation complete');
logger.info({ sourceCorpusId }, 'all background pre-generation complete');
})
.finally(() => {
documentCacheStore.clearGenerating(notebookId);
documentCacheStore.clearGenerating(sourceCorpusId);
if (pendingReGen.has(notebookId)) {
pendingReGen.delete(notebookId);
logger.info({ notebookId }, 're-triggering pre-generation for updated sources');
runPreGeneration(notebookId);
if (pendingReGen.has(sourceCorpusId)) {
pendingReGen.delete(sourceCorpusId);
logger.info({ sourceCorpusId }, 're-triggering pre-generation for updated sources');
runPreGeneration(sourceCorpusId);
}
});
} catch (err) {
const error = err as Error;
logger.error({ notebookId, err: error.message }, 'pre-generation setup failed');
documentCacheStore.clearGenerating(notebookId);
logger.error({ sourceCorpusId, err: error.message }, 'pre-generation setup failed');
documentCacheStore.clearGenerating(sourceCorpusId);
}
}

View File

@@ -2,7 +2,7 @@
import logger from '../logger.js';
import * as vectorStore from '../stores/vectorStore.js';
import * as notebookStore from '../stores/notebookStore.js';
import * as sourceCorpusStore from '../stores/sourceCorpusStore.js';
const VOYAGE_API_URL = 'https://api.voyageai.com/v1';
const EMBED_MODEL = 'voyage-3';
@@ -72,8 +72,8 @@ export function storeChunkEmbeddings(chunks: TextChunk[], embeddings: number[][]
logger.debug({ count: chunks.length }, 'stored chunk embeddings');
}
export function search(queryEmbedding: number[], notebookId: string, topK = 10): ScoredChunk[] {
const chunks = notebookStore.getChunksForNotebook(notebookId) as TextChunk[];
export function search(queryEmbedding: number[], sourceCorpusId: string, topK = 10): ScoredChunk[] {
const chunks = sourceCorpusStore.getChunksForSourceCorpus(sourceCorpusId) as TextChunk[];
if (chunks.length === 0) return [];
const scored: ScoredChunk[] = [];

View File

@@ -136,7 +136,7 @@ export async function parseUrl(url: string): Promise<string> {
const res = await fetch(safeUrl, {
headers: {
'User-Agent': 'Mozilla/5.0 (compatible; NotebookClone/1.0)',
'User-Agent': 'Mozilla/5.0 (compatible; SourceSentinel/1.0)',
Accept: 'text/html,application/xhtml+xml,text/plain',
},
signal: AbortSignal.timeout(15000),