diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 86da3c92d..abd43fcf6 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -36,7 +36,7 @@ Checklist - [ ] Did you write some relevant docs about this change (if it's a new feature)? - [ ] Did you write a regression test to reproduce the bug (if it's a bug fix)? - [ ] Did you write some tests for this change (if it's a new feature)? - - [ ] Did you run `deno task test-all` on your machine? + - [ ] Did you run `mise test` on your machine? Additional notes diff --git a/.hongdown.toml b/.hongdown.toml index 35622edf8..abe4c2764 100644 --- a/.hongdown.toml +++ b/.hongdown.toml @@ -6,6 +6,7 @@ exclude = [ "CLAUDE.md", "GEMINI.md", "WARP.md", + "packages/fedify/src/cfworkers/**", ] [heading] diff --git a/packages/amqp/src/mq.ts b/packages/amqp/src/mq.ts index c73111692..ff568c1e3 100644 --- a/packages/amqp/src/mq.ts +++ b/packages/amqp/src/mq.ts @@ -15,20 +15,20 @@ export interface AmqpMessageQueueOptions { * The name of the queue to use. Defaults to `"fedify_queue"`. * @default `"fedify_queue"` */ - queue?: string; + readonly queue?: string; /** * The prefix to use for the delayed queue. Defaults to `"fedify_delayed_"`. * Defaults to `"fedify_delayed_"`. * @default `"fedify_delayed_"` */ - delayedQueuePrefix?: string; + readonly delayedQueuePrefix?: string; /** * Whether the queue will survive a broker restart. Defaults to `true`. * @default `true` */ - durable?: boolean; + readonly durable?: boolean; /** * Whether to use native retrial mechanism. If set to `true`, the queue will @@ -45,7 +45,7 @@ export interface AmqpMessageQueueOptions { * @default `false` * @since 0.3.0 */ - nativeRetrial?: boolean; + readonly nativeRetrial?: boolean; } /** @@ -135,7 +135,7 @@ export class AmqpMessageQueue implements MessageQueue { async enqueueMany( // deno-lint-ignore no-explicit-any - messages: any[], + messages: readonly any[], options?: MessageQueueEnqueueOptions, ): Promise { const channel = await this.#getSenderChannel(); diff --git a/packages/cfworkers/src/mod.ts b/packages/cfworkers/src/mod.ts index c25c4713c..3097aa315 100644 --- a/packages/cfworkers/src/mod.ts +++ b/packages/cfworkers/src/mod.ts @@ -185,7 +185,7 @@ export class WorkersMessageQueue implements MessageQueue { enqueueMany( // deno-lint-ignore no-explicit-any - messages: any[], + messages: readonly any[], options?: MessageQueueEnqueueOptions, ): Promise { const requests: MessageSendRequest[] = messages.map((msg) => ({ diff --git a/packages/fedify/src/federation/collection.ts b/packages/fedify/src/federation/collection.ts index 174e0c849..764b63f1d 100644 --- a/packages/fedify/src/federation/collection.ts +++ b/packages/fedify/src/federation/collection.ts @@ -4,9 +4,9 @@ import { encodeHex } from "byte-encodings/hex"; * A page of items. */ export interface PageItems { - prevCursor?: string | null; - nextCursor?: string | null; - items: TItem[]; + readonly prevCursor?: string | null; + readonly nextCursor?: string | null; + readonly items: readonly TItem[]; } /** diff --git a/packages/fedify/src/federation/context.ts b/packages/fedify/src/federation/context.ts index b541c744c..013a4f343 100644 --- a/packages/fedify/src/federation/context.ts +++ b/packages/fedify/src/federation/context.ts @@ -570,7 +570,7 @@ export interface InboxContext extends Context { * inbox, it is `null`. * @since 1.2.0 */ - recipient: string | null; + readonly recipient: string | null; /** * Creates a new context with the same properties as this one, @@ -783,7 +783,7 @@ export interface SendActivityOptions { * * @since 0.9.0 */ - excludeBaseUris?: URL[]; + readonly excludeBaseUris?: readonly URL[]; } /** @@ -876,15 +876,15 @@ export interface ActorKeyPair extends CryptoKeyPair { /** * The URI of the public key, which is used for verifying HTTP Signatures. */ - keyId: URL; + readonly keyId: URL; /** * A {@link CryptographicKey} instance of the public key. */ - cryptographicKey: CryptographicKey; + readonly cryptographicKey: CryptographicKey; /** * A {@link Multikey} instance of the public key. */ - multikey: Multikey; + readonly multikey: Multikey; } diff --git a/packages/fedify/src/federation/handler.ts b/packages/fedify/src/federation/handler.ts index 7e975697e..15331df9f 100644 --- a/packages/fedify/src/federation/handler.ts +++ b/packages/fedify/src/federation/handler.ts @@ -480,7 +480,7 @@ export async function handleCollection< * @returns The filtered items as Objects, Links, or URLs. */ function filterCollectionItems( - items: TItem[], + items: readonly TItem[], collectionName: string, filterPredicate?: (item: TItem) => boolean, ): (Object | Link | URL)[] { @@ -1319,7 +1319,7 @@ class CustomCollectionHandler< * @param items The items to filter. * @returns The filtered items. */ - filterItems(items: TItem[]): (Object | Link | URL)[] { + filterItems(items: readonly TItem[]): (Object | Link | URL)[] { return filterCollectionItems(items, this.name, this.filterPredicate); } diff --git a/packages/fedify/src/federation/kv.ts b/packages/fedify/src/federation/kv.ts index e8d2147a9..d1a79b537 100644 --- a/packages/fedify/src/federation/kv.ts +++ b/packages/fedify/src/federation/kv.ts @@ -28,12 +28,12 @@ export interface KvStoreListEntry { /** * The key of the entry. */ - key: KvKey; + readonly key: KvKey; /** * The value of the entry. */ - value: unknown; + readonly value: unknown; } /** diff --git a/packages/fedify/src/federation/middleware.ts b/packages/fedify/src/federation/middleware.ts index 7659bec0f..058b91a48 100644 --- a/packages/fedify/src/federation/middleware.ts +++ b/packages/fedify/src/federation/middleware.ts @@ -128,13 +128,13 @@ export interface FederationQueueOptions { * The message queue for incoming activities. If not provided, incoming * activities will not be queued and will be processed immediately. */ - inbox?: MessageQueue; + readonly inbox?: MessageQueue; /** * The message queue for outgoing activities. If not provided, outgoing * activities will not be queued and will be sent immediately. */ - outbox?: MessageQueue; + readonly outbox?: MessageQueue; /** * The message queue for fanning out outgoing activities. If not provided, @@ -142,7 +142,7 @@ export interface FederationQueueOptions { * fanned out immediately, which causes slow response times on * {@link Context.sendActivity} calls. */ - fanout?: MessageQueue; + readonly fanout?: MessageQueue; } /** @@ -154,20 +154,20 @@ export interface FederationKvPrefixes { * processed or not. * @default `["_fedify", "activityIdempotence"]` */ - activityIdempotence: KvKey; + readonly activityIdempotence: KvKey; /** * The key prefix used for storing remote JSON-LD documents. * @default `["_fedify", "remoteDocument"]` */ - remoteDocument: KvKey; + readonly remoteDocument: KvKey; /** * The key prefix used for caching public keys. * @default `["_fedify", "publicKey"]` * @since 0.12.0 */ - publicKey: KvKey; + readonly publicKey: KvKey; /** * The key prefix used for caching HTTP Message Signatures specs. @@ -176,7 +176,7 @@ export interface FederationKvPrefixes { * @default `["_fedify", "httpMessageSignaturesSpec"]` * @since 1.6.0 */ - httpMessageSignaturesSpec: KvKey; + readonly httpMessageSignaturesSpec: KvKey; } /** diff --git a/packages/fedify/src/federation/mq.test.ts b/packages/fedify/src/federation/mq.test.ts index 4622419a5..1ae70de50 100644 --- a/packages/fedify/src/federation/mq.test.ts +++ b/packages/fedify/src/federation/mq.test.ts @@ -152,8 +152,8 @@ test("MessageQueue.nativeRetrial", async (t) => { return this.#queue.send(message); } - enqueueMany(messages: unknown[]): Promise { - return this.#queue.sendBatch(messages); + enqueueMany(messages: readonly unknown[]): Promise { + return this.#queue.sendBatch(messages as unknown[]); } listen(): Promise { diff --git a/packages/fedify/src/federation/mq.ts b/packages/fedify/src/federation/mq.ts index 106c2abec..14e749a9c 100644 --- a/packages/fedify/src/federation/mq.ts +++ b/packages/fedify/src/federation/mq.ts @@ -59,7 +59,7 @@ export interface MessageQueue { * @param options Additional options for enqueuing the messages. */ enqueueMany?: ( - messages: any[], + messages: readonly any[], options?: MessageQueueEnqueueOptions, ) => Promise; @@ -138,7 +138,7 @@ export class InProcessMessageQueue implements MessageQueue { } enqueueMany( - messages: any[], + messages: readonly any[], options?: MessageQueueEnqueueOptions, ): Promise { if (messages.length === 0) return Promise.resolve(); @@ -243,7 +243,7 @@ export class ParallelMessageQueue implements MessageQueue { } async enqueueMany( - messages: any[], + messages: readonly any[], options?: MessageQueueEnqueueOptions, ): Promise { if (this.queue.enqueueMany == null) { diff --git a/packages/fedify/src/federation/queue.ts b/packages/fedify/src/federation/queue.ts index 6538a2456..6d322afa9 100644 --- a/packages/fedify/src/federation/queue.ts +++ b/packages/fedify/src/federation/queue.ts @@ -1,6 +1,6 @@ export interface SenderKeyJwkPair { - keyId: string; - privateKey: JsonWebKey; + readonly keyId: string; + readonly privateKey: JsonWebKey; } /** @@ -15,41 +15,46 @@ export interface SenderKeyJwkPair { export type Message = FanoutMessage | OutboxMessage | InboxMessage; export interface FanoutMessage { - type: "fanout"; - id: ReturnType; - baseUrl: string; - keys: SenderKeyJwkPair[]; - inboxes: Record; - activity: unknown; - activityId?: string; - activityType: string; - collectionSync?: string; - traceContext: Record; + readonly type: "fanout"; + readonly id: ReturnType; + readonly baseUrl: string; + readonly keys: readonly SenderKeyJwkPair[]; + readonly inboxes: Readonly< + Record< + string, + { readonly actorIds: readonly string[]; readonly sharedInbox: boolean } + > + >; + readonly activity: unknown; + readonly activityId?: string; + readonly activityType: string; + readonly collectionSync?: string; + readonly traceContext: Readonly>; } export interface OutboxMessage { - type: "outbox"; - id: ReturnType; - baseUrl: string; - keys: SenderKeyJwkPair[]; - activity: unknown; - activityId?: string; - activityType: string; - inbox: string; - sharedInbox: boolean; - started: string; - attempt: number; - headers: Record; - traceContext: Record; + readonly type: "outbox"; + readonly id: ReturnType; + readonly baseUrl: string; + readonly keys: readonly SenderKeyJwkPair[]; + readonly activity: unknown; + readonly activityId?: string; + readonly activityType: string; + readonly inbox: string; + readonly sharedInbox: boolean; + readonly started: string; + readonly attempt: number; + readonly headers: Readonly>; + readonly traceContext: Readonly>; } export interface InboxMessage { - type: "inbox"; - id: ReturnType; - baseUrl: string; - activity: unknown; - started: string; - attempt: number; - identifier: string | null; - traceContext: Record; + readonly type: "inbox"; + readonly id: ReturnType; + readonly baseUrl: string; + readonly activity: unknown; + readonly started: string; + readonly attempt: number; + readonly identifier: string | null; + readonly traceContext: Readonly>; } diff --git a/packages/fedify/src/federation/send.ts b/packages/fedify/src/federation/send.ts index 866c9bb2e..a1c6a9b62 100644 --- a/packages/fedify/src/federation/send.ts +++ b/packages/fedify/src/federation/send.ts @@ -20,13 +20,13 @@ export interface ExtractInboxesParameters { /** * Actors to extract the inboxes from. */ - recipients: Recipient[]; + readonly recipients: readonly Recipient[]; /** * Whether to prefer the shared inbox over the personal inbox. * Defaults to `false`. */ - preferSharedInbox?: boolean; + readonly preferSharedInbox?: boolean; /** * The base URIs to exclude from the recipients' inboxes. It is useful @@ -36,7 +36,7 @@ export interface ExtractInboxesParameters { * * @since 0.9.0 */ - excludeBaseUris?: URL[]; + readonly excludeBaseUris?: readonly URL[]; } /** @@ -83,12 +83,12 @@ export interface SenderKeyPair { /** * The actor's private key to sign the request. */ - privateKey: CryptoKey; + readonly privateKey: CryptoKey; /** * The public key ID that corresponds to the private key. */ - keyId: URL; + readonly keyId: URL; } /** @@ -98,54 +98,54 @@ export interface SendActivityParameters { /** * The activity to send. */ - activity: unknown; + readonly activity: unknown; /** * The activity ID to send. * @since 1.0.0 */ - activityId?: string | null; + readonly activityId?: string | null; /** * The qualified URI of the activity type. * @since 1.3.0 */ - activityType?: string; + readonly activityType?: string; /** * The key pairs of the sender to sign the request. It must not be empty. * @since 0.10.0 */ - keys: SenderKeyPair[]; + readonly keys: readonly SenderKeyPair[]; /** * The inbox URL to send the activity to. */ - inbox: URL; + readonly inbox: URL; /** * Whether the inbox is a shared inbox. * @since 1.3.0 */ - sharedInbox?: boolean; + readonly sharedInbox?: boolean; /** * Additional headers to include in the request. */ - headers?: Headers; + readonly headers?: Headers; /** * The spec determiner to use for signing requests with double-knocking. * @since 1.6.0 */ - specDeterminer?: HttpMessageSignaturesSpecDeterminer; + readonly specDeterminer?: HttpMessageSignaturesSpecDeterminer; /** * The tracer provider for tracing the request. * If omitted, the global tracer provider is used. * @since 1.3.0 */ - tracerProvider?: TracerProvider; + readonly tracerProvider?: TracerProvider; } /** diff --git a/packages/fedify/src/federation/webfinger.ts b/packages/fedify/src/federation/webfinger.ts index 34a035571..14255cac6 100644 --- a/packages/fedify/src/federation/webfinger.ts +++ b/packages/fedify/src/federation/webfinger.ts @@ -223,12 +223,11 @@ async function handleWebFingerInternal( } for await (const image of actor.getIcons()) { if (image.url?.href == null) continue; - const link: Link = { + links.push({ rel: "http://webfinger.net/rel/avatar", href: image.url.href.toString(), - }; - if (image.mediaType != null) link.type = image.mediaType; - links.push(link); + ...(image.mediaType != null && { type: image.mediaType }), + }); } if (webFingerLinksDispatcher != null) { diff --git a/packages/fedify/src/nodeinfo/client.ts b/packages/fedify/src/nodeinfo/client.ts index 32b76c3b5..0d0955ddf 100644 --- a/packages/fedify/src/nodeinfo/client.ts +++ b/packages/fedify/src/nodeinfo/client.ts @@ -236,11 +236,14 @@ export function parseNodeInfo( metadata = Object.fromEntries(Object.entries(data.metadata)); } else if (!options.tryBestEffort) return null; } - const result: NodeInfo = { software, protocols, usage }; - if (services != null) result.services = services; - if (openRegistrations != null) result.openRegistrations = openRegistrations; - if (metadata != null) result.metadata = metadata; - return result; + return { + software, + protocols, + usage, + ...(services != null && { services }), + ...(openRegistrations != null && { openRegistrations }), + ...(metadata != null && { metadata }), + }; } export function parseSoftware( @@ -292,11 +295,12 @@ export function parseSoftware( if (!options.tryBestEffort) return null; } } - const result: Software = { name, version }; - if (repository != null) result.repository = repository; - if (homepage != null) result.homepage = homepage; - - return result; + return { + name, + version, + ...(repository != null && { repository }), + ...(homepage != null && { homepage }), + }; } export function parseProtocol(data: unknown): Protocol | null { @@ -333,10 +337,10 @@ export function parseServices( outbound = os.filter((o) => o != null) as OutboundService[]; if (os.length > outbound.length && !options.tryBestEffort) return null; } - const result: Services = {}; - if (inbound != null) result.inbound = inbound; - if (outbound != null) result.outbound = outbound; - return result; + return { + ...(inbound != null && { inbound }), + ...(outbound != null && { outbound }), + }; } export function parseInboundService(data: unknown): InboundService | null { @@ -377,44 +381,51 @@ export function parseUsage( options: ParseNodeInfoOptions = {}, ): Usage | null { if (typeof data !== "object" || data == null) return null; - const users: Usage["users"] = {}; + let total: number | undefined; + let activeHalfyear: number | undefined; + let activeMonth: number | undefined; if ("users" in data && typeof data.users === "object" && data.users != null) { if ("total" in data.users) { if (typeof data.users.total === "number") { - users.total = data.users.total; + total = data.users.total; } else { if (!options.tryBestEffort) return null; if (typeof data.users.total === "string") { const n = parseInt(data.users.total); - if (!isNaN(n)) users.total = n; + if (!isNaN(n)) total = n; } } } if ("activeHalfyear" in data.users) { if (typeof data.users.activeHalfyear === "number") { - users.activeHalfyear = data.users.activeHalfyear; + activeHalfyear = data.users.activeHalfyear; } else { if (!options.tryBestEffort) return null; if (typeof data.users.activeHalfyear === "string") { const n = parseInt(data.users.activeHalfyear); - if (!isNaN(n)) users.activeHalfyear = n; + if (!isNaN(n)) activeHalfyear = n; } } } if ("activeMonth" in data.users) { if (typeof data.users.activeMonth === "number") { - users.activeMonth = data.users.activeMonth; + activeMonth = data.users.activeMonth; } else { if (!options.tryBestEffort) return null; if (typeof data.users.activeMonth === "string") { const n = parseInt(data.users.activeMonth); - if (!isNaN(n)) users.activeMonth = n; + if (!isNaN(n)) activeMonth = n; } } } } else { if (!options.tryBestEffort) return null; } + const users: Usage["users"] = { + ...(total != null && { total }), + ...(activeHalfyear != null && { activeHalfyear }), + ...(activeMonth != null && { activeMonth }), + }; let localPosts = 0; if ("localPosts" in data) { if (typeof data.localPosts === "number") { diff --git a/packages/fedify/src/nodeinfo/types.ts b/packages/fedify/src/nodeinfo/types.ts index f92ccf473..c15f7cd5e 100644 --- a/packages/fedify/src/nodeinfo/types.ts +++ b/packages/fedify/src/nodeinfo/types.ts @@ -16,34 +16,34 @@ export interface NodeInfo { /** * Metadata about server software in use. */ - software: Software; + readonly software: Software; /** * The protocols supported on this server. At least one protocol must be * supported. */ - protocols: Protocol[]; + readonly protocols: readonly Protocol[]; /** * The third party sites this server can connect to via their application API. */ - services?: Services; + readonly services?: Services; /** * Whether this server allows open self-registration. Defaults to `false`. */ - openRegistrations?: boolean; + readonly openRegistrations?: boolean; /** * Usage statistics for this server. */ - usage: Usage; + readonly usage: Usage; /** * Free form key value pairs for software specific values. * Clients should not rely on any specific key present. */ - metadata?: Record; + readonly metadata?: Readonly>; } /** @@ -54,22 +54,22 @@ export interface Software { * The canonical name of this server software. This must comply with * pattern `/^[a-z0-9-]+$/`. */ - name: string; + readonly name: string; /** * The version of this server software. */ - version: string; + readonly version: string; /** * The URL of the source code repository of this server software. */ - repository?: URL; + readonly repository?: URL; /** * The URL of the homepage of this server software. */ - homepage?: URL; + readonly homepage?: URL; } /** @@ -96,13 +96,13 @@ export interface Services { * The third party sites this server can retrieve messages from for combined * display with regular traffic. */ - inbound?: InboundService[]; + readonly inbound?: readonly InboundService[]; /** * The third party sites this server can publish messages to on the behalf * of a user. */ - outbound?: OutboundService[]; + readonly outbound?: readonly OutboundService[]; } /** @@ -162,37 +162,37 @@ export interface Usage { /** * Statistics about the users of this server. */ - users: { + readonly users: { /** * The total amount of on this server registered users. This number * has to be an integer greater than or equal to zero. */ - total?: number; + readonly total?: number; /** * The amount of users that signed in at least once in the last 180 days. * This number has to be an integer greater than or equal to zero. */ - activeHalfyear?: number; + readonly activeHalfyear?: number; /** * The amount of users that signed in at least once in the last 30 days. * This number has to be an integer greater than or equal to zero. */ - activeMonth?: number; + readonly activeMonth?: number; }; /** * The amount of posts that were made by users that are registered on this * server. This number has to be an integer greater than or equal to zero. */ - localPosts: number; + readonly localPosts: number; /** * The amount of comments that were made by users that are registered on this * server. This number has to be an integer greater than or equal to zero. */ - localComments: number; + readonly localComments: number; } /** @@ -250,10 +250,12 @@ export function nodeInfoToJson(nodeInfo: NodeInfo): JsonValue { repository: nodeInfo.software.repository?.href, homepage: nodeInfo.software.homepage?.href, }, - protocols: nodeInfo.protocols, + protocols: [...nodeInfo.protocols], services: nodeInfo.services == null ? { inbound: [], outbound: [] } : { - inbound: nodeInfo.services.inbound ?? [], - outbound: nodeInfo.services.outbound ?? [], + inbound: nodeInfo.services.inbound ? [...nodeInfo.services.inbound] : [], + outbound: nodeInfo.services.outbound + ? [...nodeInfo.services.outbound] + : [], }, openRegistrations: nodeInfo.openRegistrations ?? false, usage: { diff --git a/packages/fedify/src/otel/exporter.ts b/packages/fedify/src/otel/exporter.ts index 071b9ab54..a59e44a35 100644 --- a/packages/fedify/src/otel/exporter.ts +++ b/packages/fedify/src/otel/exporter.ts @@ -19,17 +19,17 @@ export interface SignatureVerificationDetails { /** * Whether HTTP Signatures were verified. */ - httpSignaturesVerified: boolean; + readonly httpSignaturesVerified: boolean; /** * The key ID used for HTTP signature verification, if available. */ - httpSignaturesKeyId?: string; + readonly httpSignaturesKeyId?: string; /** * Whether Linked Data Signatures were verified. */ - ldSignaturesVerified: boolean; + readonly ldSignaturesVerified: boolean; } /** @@ -43,62 +43,62 @@ export interface TraceActivityRecord { /** * The trace ID from OpenTelemetry. */ - traceId: string; + readonly traceId: string; /** * The span ID from OpenTelemetry. */ - spanId: string; + readonly spanId: string; /** * The parent span ID, if any. */ - parentSpanId?: string; + readonly parentSpanId?: string; /** * Whether this is an inbound or outbound activity. */ - direction: ActivityDirection; + readonly direction: ActivityDirection; /** * The ActivityPub activity type (e.g., "Create", "Follow", "Like"). */ - activityType: string; + readonly activityType: string; /** * The activity's ID URL, if present. */ - activityId?: string; + readonly activityId?: string; /** * The actor ID URL (sender of the activity). */ - actorId?: string; + readonly actorId?: string; /** * The full JSON representation of the activity. */ - activityJson: string; + readonly activityJson: string; /** * Whether the activity was verified (for inbound activities). */ - verified?: boolean; + readonly verified?: boolean; /** * Detailed signature verification information (for inbound activities). */ - signatureDetails?: SignatureVerificationDetails; + readonly signatureDetails?: SignatureVerificationDetails; /** * The timestamp when this record was created (ISO 8601 format). */ - timestamp: string; + readonly timestamp: string; /** * The target inbox URL (for outbound activities). */ - inboxUrl?: string; + readonly inboxUrl?: string; } /** @@ -110,22 +110,22 @@ export interface TraceSummary { /** * The trace ID. */ - traceId: string; + readonly traceId: string; /** * The timestamp of the first activity in the trace. */ - timestamp: string; + readonly timestamp: string; /** * The number of activities in the trace. */ - activityCount: number; + readonly activityCount: number; /** * Activity types present in this trace. */ - activityTypes: string[]; + readonly activityTypes: readonly string[]; } /** @@ -139,13 +139,13 @@ export interface FedifySpanExporterOptions { * If not specified, data will be stored indefinitely * (or until manually deleted). */ - ttl?: Temporal.Duration; + readonly ttl?: Temporal.Duration; /** * The key prefix for storing trace data in the KvStore. * Defaults to `["fedify", "traces"]`. */ - keyPrefix?: KvKey; + readonly keyPrefix?: KvKey; } /** @@ -470,24 +470,18 @@ export class FedifySpanExporter implements SpanExporter { await this.#setWithCasRetry( summaryKey, (existing) => { - const summary: TraceSummary = existing != null - ? { - traceId: existing.traceId, - timestamp: existing.timestamp, - activityCount: existing.activityCount, - activityTypes: [...existing.activityTypes], - } - : { - traceId: record.traceId, - timestamp: record.timestamp, - activityCount: 0, - activityTypes: [], - }; - summary.activityCount += 1; - if (!summary.activityTypes.includes(record.activityType)) { - summary.activityTypes.push(record.activityType); - } - return summary; + const activityCount = existing != null ? existing.activityCount + 1 : 1; + const activityTypes = existing != null + ? existing.activityTypes.includes(record.activityType) + ? existing.activityTypes + : [...existing.activityTypes, record.activityType] + : [record.activityType]; + return { + traceId: existing?.traceId ?? record.traceId, + timestamp: existing?.timestamp ?? record.timestamp, + activityCount, + activityTypes, + }; }, options, ); diff --git a/packages/fedify/src/utils/kv-cache.ts b/packages/fedify/src/utils/kv-cache.ts index 4f2c3194d..3786f1304 100644 --- a/packages/fedify/src/utils/kv-cache.ts +++ b/packages/fedify/src/utils/kv-cache.ts @@ -55,18 +55,18 @@ export interface KvCacheParameters { /** * The document loader to decorate with a cache. */ - loader: DocumentLoader; + readonly loader: DocumentLoader; /** * The key–value store to use for backing the cache. */ - kv: KvStore; + readonly kv: KvStore; /** * The key prefix to use for namespacing the cache. * `["_fedify", "remoteDocument"]` by default. */ - prefix?: KvKey; + readonly prefix?: KvKey; /** * The per-URL cache rules in the array of `[urlPattern, duration]` pairs @@ -76,7 +76,7 @@ export interface KvCacheParameters { * * By default, 5 minutes for all URLs. */ - rules?: [ + readonly rules?: readonly [ string | URL | URLPattern, Temporal.Duration | Temporal.DurationLike, ][]; @@ -149,7 +149,7 @@ export function kvCache( function matchRule( url: string, - rules: [ + rules: readonly [ string | URL | URLPattern, Temporal.Duration | Temporal.DurationLike, ][], diff --git a/packages/postgres/src/kv.ts b/packages/postgres/src/kv.ts index a457e9d96..e289bca3a 100644 --- a/packages/postgres/src/kv.ts +++ b/packages/postgres/src/kv.ts @@ -19,13 +19,13 @@ export interface PostgresKvStoreOptions { * `"fedify_kv_v2"` by default. * @default `"fedify_kv_v2"` */ - tableName?: string; + readonly tableName?: string; /** * Whether the table has been initialized. `false` by default. * @default `false` */ - initialized?: boolean; + readonly initialized?: boolean; } /** diff --git a/packages/postgres/src/mq.ts b/packages/postgres/src/mq.ts index f3043d04d..53ca0b044 100644 --- a/packages/postgres/src/mq.ts +++ b/packages/postgres/src/mq.ts @@ -19,26 +19,26 @@ export interface PostgresMessageQueueOptions { * `"fedify_message_v2"` by default. * @default `"fedify_message_v2"` */ - tableName?: string; + readonly tableName?: string; /** * The channel name to use for the message queue. * `"fedify_channel"` by default. * @default `"fedify_channel"` */ - channelName?: string; + readonly channelName?: string; /** * Whether the table has been initialized. `false` by default. * @default `false` */ - initialized?: boolean; + readonly initialized?: boolean; /** * The poll interval for the message queue. 5 seconds by default. * @default `{ seconds: 5 }` */ - pollInterval?: Temporal.Duration | Temporal.DurationLike; + readonly pollInterval?: Temporal.Duration | Temporal.DurationLike; } /** @@ -113,7 +113,7 @@ export class PostgresMessageQueue implements MessageQueue { async enqueueMany( // deno-lint-ignore no-explicit-any - messages: any[], + messages: readonly any[], options?: MessageQueueEnqueueOptions, ): Promise { if (messages.length === 0) return; diff --git a/packages/redis/src/kv.ts b/packages/redis/src/kv.ts index 45615d8a5..758ee3bf4 100644 --- a/packages/redis/src/kv.ts +++ b/packages/redis/src/kv.ts @@ -16,13 +16,13 @@ export interface RedisKvStoreOptions { * The prefix to use for all keys in the key–value store in Redis. * Defaults to `"fedify::"`. */ - keyPrefix?: RedisKey; + readonly keyPrefix?: RedisKey; /** * The codec to use for encoding and decoding values in the key–value store. * Defaults to {@link JsonCodec}. */ - codec?: Codec; + readonly codec?: Codec; } /** diff --git a/packages/redis/src/mq.ts b/packages/redis/src/mq.ts index 0510e0087..2294e2271 100644 --- a/packages/redis/src/mq.ts +++ b/packages/redis/src/mq.ts @@ -20,40 +20,40 @@ export interface RedisMessageQueueOptions { * This is used to prevent multiple workers from processing the same message, * so it should be unique for each worker. */ - workerId?: string; + readonly workerId?: string; /** * The Pub/Sub channel key to use for the message queue. `"fedify_channel"` * by default. * @default `"fedify_channel"` */ - channelKey?: RedisKey; + readonly channelKey?: RedisKey; /** * The Sorted Set key to use for the delayed message queue. `"fedify_queue"` * by default. * @default `"fedify_queue"` */ - queueKey?: RedisKey; + readonly queueKey?: RedisKey; /** * The key to use for locking the message queue. `"fedify_lock"` by default. * @default `"fedify_lock"` */ - lockKey?: RedisKey; + readonly lockKey?: RedisKey; /** * The codec to use for encoding and decoding messages in the key–value store. * Defaults to {@link JsonCodec}. * @default {@link JsonCodec} */ - codec?: Codec; + readonly codec?: Codec; /** * The poll interval for the message queue. 5 seconds by default. * @default `{ seconds: 5 }` */ - pollInterval?: Temporal.Duration | Temporal.DurationLike; + readonly pollInterval?: Temporal.Duration | Temporal.DurationLike; } /** @@ -130,7 +130,7 @@ export class RedisMessageQueue implements MessageQueue, Disposable { } async enqueueMany( - messages: any[], + messages: readonly any[], options?: MessageQueueEnqueueOptions, ): Promise { if (messages.length === 0) return; diff --git a/packages/sqlite/src/kv.ts b/packages/sqlite/src/kv.ts index ad2e0f900..ce8bde4d5 100644 --- a/packages/sqlite/src/kv.ts +++ b/packages/sqlite/src/kv.ts @@ -21,13 +21,13 @@ export interface SqliteKvStoreOptions { * `"fedify_kv"` by default. * @default `"fedify_kv"` */ - tableName?: string; + readonly tableName?: string; /** * Whether the table has been initialized. `false` by default. * @default `false` */ - initialized?: boolean; + readonly initialized?: boolean; } /** diff --git a/packages/vocab/src/actor.ts b/packages/vocab/src/actor.ts index adf3341f1..c820ccb53 100644 --- a/packages/vocab/src/actor.ts +++ b/packages/vocab/src/actor.ts @@ -288,6 +288,6 @@ export interface Recipient { /** * The URI of the actor's shared inbox. */ - sharedInbox: URL | null; + readonly sharedInbox: URL | null; } | null; } diff --git a/packages/webfinger/src/jrd.ts b/packages/webfinger/src/jrd.ts index d9f22c17a..400d46287 100644 --- a/packages/webfinger/src/jrd.ts +++ b/packages/webfinger/src/jrd.ts @@ -6,22 +6,22 @@ export interface ResourceDescriptor { /** * A URI that identifies the entity that this descriptor describes. */ - subject?: string; + readonly subject?: string; /** * URIs that identify the same entity as the `subject`. */ - aliases?: string[]; + readonly aliases?: readonly string[]; /** * Conveys additional information about the `subject` of this descriptor. */ - properties?: Record; + readonly properties?: Readonly>; /** * Links to other resources. */ - links?: Link[]; + readonly links?: readonly Link[]; } /** @@ -33,29 +33,29 @@ export interface Link { * The link's relation type, which is either a URI or a registered relation * type (see [RFC 5988](https://datatracker.ietf.org/doc/html/rfc5988)). */ - rel: string; + readonly rel: string; /** * The media type of the target resource (see * [RFC 6838](https://datatracker.ietf.org/doc/html/rfc6838)). */ - type?: string; + readonly type?: string; /** * A URI pointing to the target resource. */ - href?: string; + readonly href?: string; /** * Human-readable titles describing the link relation. If the language is * unknown or unspecified, the key is `"und"`. */ - titles?: Record; + readonly titles?: Readonly>; /** * Conveys additional information about the link relation. */ - properties?: Record; + readonly properties?: Readonly>; /** * A URI Template (RFC 6570) that can be used to construct URIs by @@ -63,5 +63,5 @@ export interface Link { * where parameters like account URIs need to be dynamically inserted. * @since 1.9.0 */ - template?: string; + readonly template?: string; }