mirror of
https://github.com/juherr/kill-the-news.git
synced 2026-06-21 06:13:48 +00:00
ad196f1761
Address five modeling tensions in one pass: - Encapsulation: the Feed aggregate no longer exposes raw config/metadata (a shallow Readonly still leaked mutable arrays). It now offers intention-revealing accessors that return copies, plus toConfigSnapshot/toMetadataSnapshot for the repository and summary() for the global registry. - feeds:list consistency: FeedRepository.save/saveConfig upsert the registry entry from feed.summary(), so services no longer mirror title/description/ expiry by hand (the old add/updateInList footgun is gone). - domain/feed.ts: drop the dead applySenderPolicy, internalise resolveExpiresAt and trimToByteBudget into the aggregate; feed.ts keeps only the shared isExpired predicate used by the read-model routes. - Single edit path: remove editDetails; edit(patch, deps) is the sole config mutation, with a systematic expired guard. Renaming an expired feed now 403s. - FeedId flows through the application and infrastructure signatures; fromTrusted/parse happen once at the edge, .value only at the serialisation boundaries (urls, feed-generator, feed-keys, logs, JSON). 347 tests green, tsc clean, Worker bundle builds. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
50 lines
1.5 KiB
TypeScript
50 lines
1.5 KiB
TypeScript
import { Env, WebSubSubscription } from "../types";
|
|
import { feedKeys } from "../domain/feed-keys";
|
|
import { FeedId } from "../domain/value-objects/feed-id";
|
|
import { logger } from "./logger";
|
|
|
|
/**
|
|
* KV access for per-feed WebSub subscriber lists (`websub:subs:<feedId>`).
|
|
*/
|
|
export class WebSubSubscriptionRepository {
|
|
constructor(private readonly kv: KVNamespace) {}
|
|
|
|
static from(env: Env): WebSubSubscriptionRepository {
|
|
return new WebSubSubscriptionRepository(env.EMAIL_STORAGE);
|
|
}
|
|
|
|
async get(feedId: FeedId): Promise<WebSubSubscription[]> {
|
|
const raw = await this.kv.get(feedKeys.websub(feedId.value), "json");
|
|
return (raw as WebSubSubscription[] | null) ?? [];
|
|
}
|
|
|
|
async save(
|
|
feedId: FeedId,
|
|
subscriptions: WebSubSubscription[],
|
|
): Promise<void> {
|
|
await this.kv.put(
|
|
feedKeys.websub(feedId.value),
|
|
JSON.stringify(subscriptions),
|
|
);
|
|
}
|
|
|
|
/** Number of feeds that currently hold at least one WebSub subscription. */
|
|
async countKeys(): Promise<number> {
|
|
const prefix = feedKeys.websubPrefix();
|
|
let total = 0;
|
|
let cursor: string | undefined;
|
|
try {
|
|
do {
|
|
const listed = await this.kv.list({ prefix, cursor, limit: 1000 });
|
|
total += listed.keys.length;
|
|
cursor = listed.list_complete ? undefined : listed.cursor;
|
|
} while (cursor);
|
|
} catch (error) {
|
|
logger.error("Error counting subscription keys", {
|
|
error: String(error),
|
|
});
|
|
}
|
|
return total;
|
|
}
|
|
}
|