Initial Commit

This commit is contained in:
2026-05-27 23:29:53 +01:00
commit fbc70f96e6
10000 changed files with 1102152 additions and 0 deletions

View File

@@ -0,0 +1,67 @@
import { readFile } from "node:fs/promises";
import path from "node:path";
import { describe, expect, it } from "@suaveplan/testing/runner";
const PKG_ROOT = path.resolve(import.meta.dirname, "..");
describe("api-plugin-sse — dependency graph and exports map", () => {
it("exports map advertises every feature subpath", {
timeout: 5000,
}, async () => {
const raw = await readFile(path.join(PKG_ROOT, "package.json"), "utf8");
const pkg = JSON.parse(raw) as {
exports: Record<string, unknown>;
};
const subpaths = Object.keys(pkg.exports)
.filter(
(k) =>
(k === "." || k.startsWith("./")) && k !== "./package.json"
)
.sort();
expect(subpaths).toEqual(
[
".",
"./plugin/plugin",
"./services/service",
"./middleware/sse-handler",
"./formatter/format",
"./formatter/parse",
"./buffer/ring-buffer",
"./fanout/in-process",
"./schemas/options",
"./schemas/event",
"./error-codes/error-codes",
"./otel/instruments",
"./types/types",
"./registry/service-registry",
].sort()
);
});
it("dependencies use only workspace and catalog refs", {
timeout: 5000,
}, async () => {
const raw = await readFile(path.join(PKG_ROOT, "package.json"), "utf8");
const pkg = JSON.parse(raw) as {
dependencies?: Record<string, string>;
devDependencies?: Record<string, string>;
};
const all = {
...(pkg.dependencies ?? {}),
...(pkg.devDependencies ?? {}),
};
for (const [name, version] of Object.entries(all)) {
// Allow exact versions for explicitly pinned testing deps.
if (name === "@opentelemetry/sdk-metrics") continue;
if (
!version.startsWith("workspace:") &&
!version.startsWith("catalog:")
) {
throw new Error(
`Dependency ${name} uses bare version '${version}' — must be workspace:/catalog:`
);
}
}
expect(true).toBe(true);
});
});

View File

@@ -0,0 +1,167 @@
import { describe, expect, it } from "@suaveplan/testing/runner";
import { Hono } from "hono";
import { createRingBuffer } from "../../src/buffer/ring-buffer.js";
import { createInProcessFanout } from "../../src/fanout/in-process.js";
import { parseSseStream } from "../../src/formatter/parse.js";
import { createSseRoute } from "../../src/middleware/sse-handler.js";
import { createSseService } from "../../src/services/service.js";
/**
* End-to-end integration coverage. A real Hono app, a real fetch
* round-trip (via `app.fetch` over a `Request`), and a real ReadableStream
* client reader that decodes the SSE wire format using `parseSseStream`.
*/
describe("api-plugin-sse — full-stream integration", () => {
it("end-to-end fetch subscriber receives three ordered events", {
timeout: 10_000,
}, async () => {
let n = 0;
const service = createSseService({
buffer: createRingBuffer(100),
fanout: createInProcessFanout(),
idGenerator: () => `evt-${++n}`,
maxConnectionsPerPrincipal: 16,
maxTotalConnections: 100,
});
const app = new Hono();
app.get(
"/sse/:channel",
createSseRoute(service, {
heartbeatIntervalMs: 60_000,
queueHighWaterMark: 64,
overflowPolicy: "drop-oldest",
})
);
const controller = new AbortController();
const res = await app.request(
new Request("http://test.local/sse/notifications", {
signal: controller.signal,
})
);
expect(res.status).toBe(200);
// Broadcast on a small delay so the subscriber has installed.
setTimeout(() => {
service.broadcast("notifications", { data: { n: 1 } });
service.broadcast("notifications", { data: { n: 2 } });
service.broadcast("notifications", { data: { n: 3 } });
}, 10);
const body = res.body;
if (body === null) throw new Error("no body");
const reader = body.getReader();
const collected = [] as { id: string; data: unknown }[];
for await (const event of parseSseStream(reader)) {
collected.push({
id: event.id,
data: JSON.parse(event.data as string),
});
if (collected.length >= 3) break;
}
controller.abort();
await reader.cancel().catch(() => undefined);
// service.subscribe consumes one id for the connection handle
// (evt-1), then broadcasts consume evt-2/3/4 for the events.
expect(collected.map((c) => c.id)).toEqual(["evt-2", "evt-3", "evt-4"]);
expect(collected.map((c) => (c.data as { n: number }).n)).toEqual([
1, 2, 3,
]);
await service.close();
});
it("reconnect with Last-Event-ID skips delivered events", {
timeout: 10_000,
}, async () => {
let n = 0;
const service = createSseService({
buffer: createRingBuffer(100),
fanout: createInProcessFanout(),
idGenerator: () => `evt-${++n}`,
maxConnectionsPerPrincipal: 16,
maxTotalConnections: 100,
});
const app = new Hono();
app.get(
"/sse/:channel",
createSseRoute(service, {
heartbeatIntervalMs: 60_000,
queueHighWaterMark: 64,
overflowPolicy: "drop-oldest",
})
);
// Pre-load three broadcasts into the ring buffer.
service.broadcast("c", { data: { n: 1 } });
service.broadcast("c", { data: { n: 2 } });
service.broadcast("c", { data: { n: 3 } });
const controller = new AbortController();
const res = await app.request(
new Request("http://test.local/sse/c", {
headers: { "Last-Event-ID": "evt-2" },
signal: controller.signal,
})
);
const body = res.body;
if (body === null) throw new Error("no body");
const reader = body.getReader();
const collected = [] as { id: string }[];
for await (const event of parseSseStream(reader)) {
collected.push({ id: event.id });
if (collected.length >= 1) break;
}
controller.abort();
await reader.cancel().catch(() => undefined);
expect(collected.map((c) => c.id)).toEqual(["evt-3"]);
await service.close();
});
it("heartbeat tick via injected interval triggers a comment frame", {
timeout: 10_000,
}, async () => {
const service = createSseService({
buffer: createRingBuffer(10),
fanout: createInProcessFanout(),
idGenerator: () => "x",
maxConnectionsPerPrincipal: 16,
maxTotalConnections: 100,
});
const app = new Hono();
let fire: (() => void) | undefined;
app.get(
"/sse/:channel",
createSseRoute(service, {
heartbeatIntervalMs: 30_000,
queueHighWaterMark: 64,
overflowPolicy: "drop-oldest",
setIntervalImpl: (fn) => {
fire = fn;
return { mock: true };
},
clearIntervalImpl: () => {
fire = undefined;
},
})
);
const controller = new AbortController();
const res = await app.request(
new Request("http://t/sse/c", { signal: controller.signal })
);
const body = res.body;
if (body === null) throw new Error("no body");
const reader = body.getReader();
// Allow stream install
await new Promise((r) => setTimeout(r, 10));
fire?.();
const { value } = await reader.read();
const decoder = new TextDecoder();
const text = decoder.decode(value ?? new Uint8Array());
expect(text).toContain(": heartbeat");
controller.abort();
await reader.cancel().catch(() => undefined);
await service.close();
});
});

View File

@@ -0,0 +1,33 @@
/**
* Test setup for @suaveplan/api-plugin-sse.
*
* Installs a real in-memory OpenTelemetry MeterProvider so OTel
* instruments resolved on first call bind against an exporter the
* tests can flush + read.
*/
import { metrics } from "@opentelemetry/api";
import {
AggregationTemporality,
InMemoryMetricExporter,
MeterProvider,
PeriodicExportingMetricReader,
} from "@opentelemetry/sdk-metrics";
export const testInMemoryMetricExporter = new InMemoryMetricExporter(
AggregationTemporality.CUMULATIVE
);
const reader = new PeriodicExportingMetricReader({
exporter: testInMemoryMetricExporter,
exportIntervalMillis: 60_000,
});
export const testMeterProvider = new MeterProvider({ readers: [reader] });
metrics.setGlobalMeterProvider(testMeterProvider);
export async function flushTestMetrics() {
await testMeterProvider.forceFlush();
const collected = testInMemoryMetricExporter.getMetrics();
testInMemoryMetricExporter.reset();
return collected;
}