moved js files in public into js and css into css folder

This commit is contained in:
2026-09-22 21:55:02 +02:00
parent 99a3277e64
commit 08f5f3f8d9
40 changed files with 1772 additions and 1555 deletions
+52 -42
View File
@@ -1,9 +1,9 @@
import test from "node:test";
import assert from "node:assert/strict";
import {createServer} from "node:http";
import {createModelService} from "../src/services/modelService.js";
import {createApp} from "../src/app.js";
import {stream} from "../public/app/api.js";
import { createServer } from "node:http";
import { createModelService } from "../src/services/modelService.js";
import { createApp } from "../src/app.js";
import { stream } from "../public/js/api.js";
const config = {
modelApiBaseUrl: "http://fixture", modelRequestTimeoutMs: 20,
@@ -17,13 +17,15 @@ test("active tokens outlive the legacy total timeout and arrive before completio
let first;
const firstChunk = new Promise(resolve => { first = resolve; });
let interval;
const service = createModelService(config, {fetch: async () => new Response(new ReadableStream({
start(controller) {
interval = setInterval(() => controller.enqueue(line({message: {content: "x"}})), 15);
finish = () => { clearInterval(interval); controller.enqueue(line({done: true})); controller.close(); };
},
cancel() { clearInterval(interval); }
}))});
const service = createModelService(config, {
fetch: async () => new Response(new ReadableStream({
start(controller) {
interval = setInterval(() => controller.enqueue(line({ message: { content: "x" } })), 15);
finish = () => { clearInterval(interval); controller.enqueue(line({ done: true })); controller.close(); };
},
cancel() { clearInterval(interval); }
}))
});
let completed = false;
const pending = service.streamReply("qwen3:14b", [], () => first()).then(reply => { completed = true; return reply; });
try {
@@ -45,18 +47,20 @@ test("connection, inactivity and overall deadlines abort generation", async (t)
modelConnectTimeoutMs: kind === "connection" ? 25 : 1000,
modelInactivityTimeoutMs: kind === "inactivity" ? 25 : 1000,
modelMaxGenerationMs: kind === "overall" ? 70 : 1000
}, {fetch: async (_url, {signal}) => {
if (kind === "connection") return new Promise((_resolve, reject) => {
signal.addEventListener("abort", () => { cancelled = true; reject(new DOMException("Aborted", "AbortError")); });
});
return new Response(new ReadableStream({
start(controller) {
if (kind === "overall") interval = setInterval(() => controller.enqueue(line({message: {content: "x"}})), 10);
},
cancel() { cancelled = true; clearInterval(interval); }
}));
}});
await assert.rejects(service.streamReply("qwen3:14b", [], () => {}), {code: "MODEL_TIMEOUT"});
}, {
fetch: async (_url, { signal }) => {
if (kind === "connection") return new Promise((_resolve, reject) => {
signal.addEventListener("abort", () => { cancelled = true; reject(new DOMException("Aborted", "AbortError")); });
});
return new Response(new ReadableStream({
start(controller) {
if (kind === "overall") interval = setInterval(() => controller.enqueue(line({ message: { content: "x" } })), 10);
},
cancel() { cancelled = true; clearInterval(interval); }
}));
}
});
await assert.rejects(service.streamReply("qwen3:14b", [], () => { }), { code: "MODEL_TIMEOUT" });
assert.equal(cancelled, true);
});
});
@@ -68,11 +72,11 @@ test("stream validation rejects truncated output, upstream errors and invalid fi
'{"message":{"content":42},"done":true}\n',
'{broken}\n'
]) {
const service = createModelService(config, {fetch: async () => new Response(body)});
const service = createModelService(config, { fetch: async () => new Response(body) });
await assert.rejects(service.generateReply("qwen3:14b", []));
}
const service = createModelService(config, {fetch: async () => { throw new Error("Connection refused"); }});
await assert.rejects(service.generateReply("qwen3:14b", []), {code: "MODEL_UNAVAILABLE"});
const service = createModelService(config, { fetch: async () => { throw new Error("Connection refused"); } });
await assert.rejects(service.generateReply("qwen3:14b", []), { code: "MODEL_UNAVAILABLE" });
});
test("SSE EOF is a failure rather than successful completion", async (t) => {
@@ -80,7 +84,7 @@ test("SSE EOF is a failure rather than successful completion", async (t) => {
t.after(() => { globalThis.fetch = original; });
globalThis.fetch = async () => new Response('event: chunk\ndata: {"fullReply":"partial"}\n\n');
const chunks = [];
await assert.rejects(stream("/fixture", {}, {onChunk: data => chunks.push(data.fullReply)}), /interrupted/);
await assert.rejects(stream("/fixture", {}, { onChunk: data => chunks.push(data.fullReply) }), /interrupted/);
assert.deepEqual(chunks, ["partial"]);
});
@@ -88,17 +92,23 @@ test("browser disconnect aborts Ollama and incomplete output is not persisted",
let signal;
let releaseAbort;
const aborted = new Promise(resolve => { releaseAbort = resolve; });
const app = createApp({config: {
dbPath: ":memory:", testMode: true, sessionSecret: "test-only",
chatSummaryUpdateEveryMessages: 0, structuredRequestLoggingEnabled: false,
eventLoopLagMonitorEnabled: false, slowRequestLoggingEnabled: false
}, modelOverrides: {fetch: async (_url, options) => {
signal = options.signal;
signal.addEventListener("abort", releaseAbort, {once: true});
return new Response(new ReadableStream({start(controller) {
controller.enqueue(line({message: {content: "partial"}}));
}}));
}}});
const app = createApp({
config: {
dbPath: ":memory:", testMode: true, sessionSecret: "test-only",
chatSummaryUpdateEveryMessages: 0, structuredRequestLoggingEnabled: false,
eventLoopLagMonitorEnabled: false, slowRequestLoggingEnabled: false
}, modelOverrides: {
fetch: async (_url, options) => {
signal = options.signal;
signal.addEventListener("abort", releaseAbort, { once: true });
return new Response(new ReadableStream({
start(controller) {
controller.enqueue(line({ message: { content: "partial" } }));
}
}));
}
}
});
const server = createServer(app);
await new Promise(resolve => server.listen(0, "127.0.0.1", resolve));
t.after(async () => {
@@ -106,14 +116,14 @@ test("browser disconnect aborts Ollama and incomplete output is not persisted",
app.locals.close();
});
const base = `http://127.0.0.1:${server.address().port}`;
const registered = await fetch(base + "/register", {method: "POST", headers: {"Content-Type": "application/json"}, body: JSON.stringify({username: "disconnect", password: "test-password"})});
const registered = await fetch(base + "/register", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ username: "disconnect", password: "test-password" }) });
const cookie = registered.headers.get("set-cookie").split(";")[0];
const headers = {"Content-Type": "application/json", Cookie: cookie};
const chat = await (await fetch(base + "/chats", {method: "POST", headers, body: '{"title":"Disconnect"}'})).json();
const headers = { "Content-Type": "application/json", Cookie: cookie };
const chat = await (await fetch(base + "/chats", { method: "POST", headers, body: '{"title":"Disconnect"}' })).json();
const controller = new AbortController();
const response = await fetch(base + "/chat/stream", {
method: "POST", headers, signal: controller.signal,
body: JSON.stringify({chatId: chat.chat.id, message: "Hello"})
body: JSON.stringify({ chatId: chat.chat.id, message: "Hello" })
});
const reader = response.body.getReader();
let received = "";