@sdxc/activitypub 0.0.0-pre.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/LICENSE.md +21 -0
  2. package/README.md +711 -0
  3. package/dist/collections.d.ts +67 -0
  4. package/dist/collections.js +38 -0
  5. package/dist/conformance.d.ts +80 -0
  6. package/dist/conformance.js +313 -0
  7. package/dist/discovery.d.ts +46 -0
  8. package/dist/discovery.js +105 -0
  9. package/dist/errors.d.ts +115 -0
  10. package/dist/errors.js +124 -0
  11. package/dist/federation.d.ts +281 -0
  12. package/dist/federation.js +513 -0
  13. package/dist/fixtures/gotosocial.d.ts +114 -0
  14. package/dist/fixtures/gotosocial.js +115 -0
  15. package/dist/fixtures/index.d.ts +15 -0
  16. package/dist/fixtures/index.js +50 -0
  17. package/dist/fixtures/mastodon.d.ts +489 -0
  18. package/dist/fixtures/mastodon.js +334 -0
  19. package/dist/fixtures/misskey.d.ts +254 -0
  20. package/dist/fixtures/misskey.js +163 -0
  21. package/dist/fixtures/pixelfed.d.ts +118 -0
  22. package/dist/fixtures/pixelfed.js +126 -0
  23. package/dist/fixtures/shared.d.ts +14 -0
  24. package/dist/fixtures/shared.js +14 -0
  25. package/dist/fixtures/threads.d.ts +82 -0
  26. package/dist/fixtures/threads.js +88 -0
  27. package/dist/index.d.ts +33 -0
  28. package/dist/index.js +22 -0
  29. package/dist/keys.d.ts +100 -0
  30. package/dist/keys.js +147 -0
  31. package/dist/lib/availability.d.ts +37 -0
  32. package/dist/lib/availability.js +52 -0
  33. package/dist/lib/compact.d.ts +145 -0
  34. package/dist/lib/compact.js +514 -0
  35. package/dist/lib/constants.d.ts +35 -0
  36. package/dist/lib/constants.js +65 -0
  37. package/dist/lib/delivery-fan-out.d.ts +72 -0
  38. package/dist/lib/delivery-fan-out.js +202 -0
  39. package/dist/lib/delivery-post.d.ts +108 -0
  40. package/dist/lib/delivery-post.js +318 -0
  41. package/dist/lib/fetch.d.ts +64 -0
  42. package/dist/lib/fetch.js +103 -0
  43. package/dist/lib/inbox-error.d.ts +45 -0
  44. package/dist/lib/inbox-error.js +74 -0
  45. package/dist/lib/inbox-handle.d.ts +119 -0
  46. package/dist/lib/inbox-handle.js +587 -0
  47. package/dist/lib/inbox-receive.d.ts +117 -0
  48. package/dist/lib/inbox-receive.js +233 -0
  49. package/dist/lib/inbox-signature.d.ts +61 -0
  50. package/dist/lib/inbox-signature.js +156 -0
  51. package/dist/lib/inbox-summarize.d.ts +41 -0
  52. package/dist/lib/inbox-summarize.js +97 -0
  53. package/dist/lib/messages.d.ts +36 -0
  54. package/dist/lib/messages.js +35 -0
  55. package/dist/lib/parse.d.ts +42 -0
  56. package/dist/lib/parse.js +348 -0
  57. package/dist/lib/response.d.ts +45 -0
  58. package/dist/lib/response.js +59 -0
  59. package/dist/lib/stringify.d.ts +51 -0
  60. package/dist/lib/stringify.js +122 -0
  61. package/dist/lib/tombstone.d.ts +24 -0
  62. package/dist/lib/tombstone.js +15 -0
  63. package/dist/lib/types.d.ts +249 -0
  64. package/dist/lib/types.js +0 -0
  65. package/dist/memory.d.ts +77 -0
  66. package/dist/memory.js +146 -0
  67. package/dist/remote.d.ts +105 -0
  68. package/dist/remote.js +262 -0
  69. package/dist/store.d.ts +78 -0
  70. package/dist/store.js +8 -0
  71. package/dist/testing.d.ts +12 -0
  72. package/dist/testing.js +10 -0
  73. package/package.json +54 -0
@@ -0,0 +1,51 @@
1
+ import type { ActivityPub } from "./types.js";
2
+ /**
3
+ * The extension terms this package writes, defined the way Mastodon defines them, so a
4
+ * JSON-LD processor on the other side expands each to the IRI its own writer uses.
5
+ */
6
+ export declare const EXTENSION_CONTEXT: {
7
+ readonly manuallyApprovesFollowers: "as:manuallyApprovesFollowers";
8
+ readonly sensitive: "as:sensitive";
9
+ readonly Hashtag: "as:Hashtag";
10
+ readonly alsoKnownAs: {
11
+ readonly "@id": "as:alsoKnownAs";
12
+ readonly "@type": "@id";
13
+ };
14
+ readonly movedTo: {
15
+ readonly "@id": "as:movedTo";
16
+ readonly "@type": "@id";
17
+ };
18
+ readonly toot: "http://joinmastodon.org/ns#";
19
+ readonly featured: {
20
+ readonly "@id": "toot:featured";
21
+ readonly "@type": "@id";
22
+ };
23
+ readonly discoverable: "toot:discoverable";
24
+ readonly indexable: "toot:indexable";
25
+ readonly Emoji: "toot:Emoji";
26
+ readonly blurhash: "toot:blurhash";
27
+ readonly focalPoint: {
28
+ readonly "@container": "@list";
29
+ readonly "@id": "toot:focalPoint";
30
+ };
31
+ readonly schema: "http://schema.org#";
32
+ readonly PropertyValue: "schema:PropertyValue";
33
+ readonly value: "schema:value";
34
+ readonly misskey: "https://misskey-hub.net/ns#";
35
+ readonly _misskey_quote: "misskey:_misskey_quote";
36
+ readonly quote: {
37
+ readonly "@id": "https://w3id.org/fep/044f#quote";
38
+ readonly "@type": "@id";
39
+ };
40
+ readonly quoteUri: "http://fedibird.com/ns#quoteUri";
41
+ };
42
+ /**
43
+ * Writes a document as JSON text with its `@context`, ready to serve or deliver. It
44
+ * omits `null`, empty arrays and empty maps, never writes `bto` or `bcc`, and writes
45
+ * dates as ISO 8601; embedded objects carry no `@context` of their own.
46
+ *
47
+ * @param document - Any document, parsed or authored.
48
+ * @example
49
+ * stringify({ id: NOTE_ID, type: "Note", attributedTo: [ACTOR_ID], to: [PUBLIC], content: html });
50
+ */
51
+ export declare function stringify(document: ActivityPub.Draft<ActivityPub.Document>): string;
@@ -0,0 +1,122 @@
1
+ import { isObject } from "./compact.js";
2
+ import { AS2_CONTEXT, DATA_INTEGRITY_CONTEXT, MULTIKEY_CONTEXT, SECURITY_CONTEXT, } from "./constants.js";
3
+ /**
4
+ * The extension terms this package writes, defined the way Mastodon defines them, so a
5
+ * JSON-LD processor on the other side expands each to the IRI its own writer uses.
6
+ */
7
+ export const EXTENSION_CONTEXT = {
8
+ manuallyApprovesFollowers: "as:manuallyApprovesFollowers",
9
+ sensitive: "as:sensitive",
10
+ Hashtag: "as:Hashtag",
11
+ alsoKnownAs: { "@id": "as:alsoKnownAs", "@type": "@id" },
12
+ movedTo: { "@id": "as:movedTo", "@type": "@id" },
13
+ toot: "http://joinmastodon.org/ns#",
14
+ featured: { "@id": "toot:featured", "@type": "@id" },
15
+ discoverable: "toot:discoverable",
16
+ indexable: "toot:indexable",
17
+ Emoji: "toot:Emoji",
18
+ blurhash: "toot:blurhash",
19
+ focalPoint: { "@container": "@list", "@id": "toot:focalPoint" },
20
+ schema: "http://schema.org#",
21
+ PropertyValue: "schema:PropertyValue",
22
+ value: "schema:value",
23
+ misskey: "https://misskey-hub.net/ns#",
24
+ _misskey_quote: "misskey:_misskey_quote",
25
+ quote: { "@id": "https://w3id.org/fep/044f#quote", "@type": "@id" },
26
+ quoteUri: "http://fedibird.com/ns#quoteUri",
27
+ };
28
+ /** Members never written: ActivityPub §6 strips blind recipients before delivery. */
29
+ const STRIPPED_MEMBERS = new Set(["bto", "bcc"]);
30
+ /**
31
+ * Whether a written value carries nothing, which `stringify` omits rather than writing
32
+ * `null`, `[]` or `{}`.
33
+ *
34
+ * @param value - A value already written.
35
+ */
36
+ function isEmpty(value) {
37
+ if (value === undefined || value === null)
38
+ return true;
39
+ if (Array.isArray(value))
40
+ return value.length === 0;
41
+ return isObject(value) && Object.keys(value).length === 0;
42
+ }
43
+ /**
44
+ * A value as JSON: dates as ISO 8601, arrays and embedded objects written member by
45
+ * member with empty ones dropped.
46
+ *
47
+ * @param value - Any member of a document.
48
+ */
49
+ function writeValue(value) {
50
+ if (value instanceof Date)
51
+ return value.toISOString();
52
+ if (Array.isArray(value))
53
+ return value.map(writeValue).filter((entry) => !isEmpty(entry));
54
+ if (isObject(value))
55
+ return writeMembers(value);
56
+ return value;
57
+ }
58
+ /**
59
+ * An object's members as JSON, `id` and `type` first. `quote` is also written as
60
+ * `_misskey_quote` and `quoteUri`, which Misskey and older Fedibird-derived servers read.
61
+ *
62
+ * @param document - The object, as the API shapes it.
63
+ */
64
+ function writeMembers(document) {
65
+ let output = {};
66
+ if (document.id !== undefined)
67
+ output.id = document.id;
68
+ if (document.type !== undefined)
69
+ output.type = document.type;
70
+ for (let [member, value] of Object.entries(document)) {
71
+ if (member === "id" || member === "type" || STRIPPED_MEMBERS.has(member))
72
+ continue;
73
+ let written = writeValue(value);
74
+ if (isEmpty(written))
75
+ continue;
76
+ output[member] = written;
77
+ if (member === "quote") {
78
+ output._misskey_quote = written;
79
+ output.quoteUri = written;
80
+ }
81
+ }
82
+ return output;
83
+ }
84
+ /**
85
+ * Whether a member of a document holds at least one entry.
86
+ *
87
+ * @param document - The document.
88
+ * @param member - The member name.
89
+ */
90
+ function hasEntries(document, member) {
91
+ let value = document[member];
92
+ return Array.isArray(value) ? value.length > 0 : value !== undefined && value !== null;
93
+ }
94
+ /**
95
+ * The `@context` a document is written with: AS2, the security vocabulary, the
96
+ * data-integrity and Multikey contexts only when the document uses them, then the
97
+ * extension terms.
98
+ *
99
+ * @param document - The document being written.
100
+ */
101
+ function contextFor(document) {
102
+ let context = [AS2_CONTEXT, SECURITY_CONTEXT];
103
+ if (hasEntries(document, "proof"))
104
+ context.push(DATA_INTEGRITY_CONTEXT);
105
+ if (hasEntries(document, "assertionMethod"))
106
+ context.push(MULTIKEY_CONTEXT);
107
+ context.push(EXTENSION_CONTEXT);
108
+ return context;
109
+ }
110
+ /**
111
+ * Writes a document as JSON text with its `@context`, ready to serve or deliver. It
112
+ * omits `null`, empty arrays and empty maps, never writes `bto` or `bcc`, and writes
113
+ * dates as ISO 8601; embedded objects carry no `@context` of their own.
114
+ *
115
+ * @param document - Any document, parsed or authored.
116
+ * @example
117
+ * stringify({ id: NOTE_ID, type: "Note", attributedTo: [ACTOR_ID], to: [PUBLIC], content: html });
118
+ */
119
+ export function stringify(document) {
120
+ let members = document;
121
+ return JSON.stringify({ "@context": contextFor(members), ...writeMembers(members) });
122
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * The document a deleted object is served as, so a server that refetches it learns it
3
+ * is gone (`respond` answers it with `410`) and a `Delete` names what was removed.
4
+ *
5
+ * @author [Sergio Xalambrí](https://sergiodxa.com)
6
+ * @copyright Sergio Xalambrí 2026
7
+ */
8
+ import type { ActivityPub } from "./types.js";
9
+ /** What a deleted object is described by. */
10
+ export interface TombstoneInit {
11
+ /** The deleted object's own id, which the Tombstone keeps. */
12
+ id: string;
13
+ /** The type the object had, such as `Note` or `Article`. */
14
+ formerType?: string | null;
15
+ deleted?: Date | null;
16
+ }
17
+ /**
18
+ * A `Tombstone` for a deleted object, served in its place and embedded in its `Delete`.
19
+ *
20
+ * @param init - The object's id, former type and deletion time.
21
+ * @example
22
+ * return respond(tombstone({ id: post.url, formerType: "Article", deleted: post.deletedAt }));
23
+ */
24
+ export declare function tombstone(init: TombstoneInit): ActivityPub.Tombstone;
@@ -0,0 +1,15 @@
1
+ /**
2
+ * A `Tombstone` for a deleted object, served in its place and embedded in its `Delete`.
3
+ *
4
+ * @param init - The object's id, former type and deletion time.
5
+ * @example
6
+ * return respond(tombstone({ id: post.url, formerType: "Article", deleted: post.deletedAt }));
7
+ */
8
+ export function tombstone(init) {
9
+ return {
10
+ id: init.id,
11
+ type: "Tombstone",
12
+ formerType: init.formerType ?? null,
13
+ deleted: init.deleted ?? null,
14
+ };
15
+ }
@@ -0,0 +1,249 @@
1
+ /**
2
+ * The ActivityStreams 2.0 vocabulary as this package reads and writes it: every member
3
+ * normalized to one shape whatever the sender chose, so a handler never branches on
4
+ * whether a value arrived as a string, an array or an embedded object.
5
+ *
6
+ * @author [Sergio Xalambrí](https://sergiodxa.com)
7
+ * @copyright Sergio Xalambrí 2026
8
+ */
9
+ import type { ACTIVITY_TYPES, ACTOR_TYPES } from "./constants.js";
10
+ export declare namespace ActivityPub {
11
+ /** An IRI or the object it names; which one arrives is the sender's choice. */
12
+ type Ref<T> = string | T;
13
+ /**
14
+ * A document as an app authors it for `stringify` and `respond`: `id` and `type` are
15
+ * required and every other member may be left out, which writes as absent.
16
+ *
17
+ * @template T - The full shape, as `parse*` returns it.
18
+ */
19
+ type Draft<T> = T extends Date ? T : T extends string | number | boolean | null | undefined ? T : T extends readonly unknown[] ? {
20
+ [K in keyof T]: Draft<T[K]>;
21
+ } : T extends object ? {
22
+ [K in keyof T as K extends "id" | "type" ? K : never]: Draft<T[K]>;
23
+ } & {
24
+ [K in keyof T as K extends "id" | "type" ? never : K]?: Draft<T[K]>;
25
+ } : T;
26
+ /** The ActivityStreams activity types this package names. */
27
+ type ActivityType = (typeof ACTIVITY_TYPES)[number];
28
+ /** The ActivityStreams actor types. */
29
+ type ActorType = (typeof ACTOR_TYPES)[number];
30
+ /**
31
+ * Any ActivityStreams object. A type this package does not model (`Note`, `Article`,
32
+ * `Question`, `Event`, or an extension) keeps its `type`, so an app ignores it on purpose.
33
+ */
34
+ interface Object {
35
+ /** An absolute IRI; the origin every ownership check compares against. */
36
+ id: string;
37
+ /** The first type a sender listed that this package knows, else the first it listed. */
38
+ type: string;
39
+ /** Always IRIs; an embedded author is never trusted over the author's own document. */
40
+ attributedTo: string[];
41
+ /** Addressing, with every spelling of the public collection read as `PUBLIC`. */
42
+ to: string[];
43
+ cc: string[];
44
+ /** Read for fan-out, and never written: ActivityPub §6 strips them before delivery. */
45
+ bto: string[];
46
+ bcc: string[];
47
+ /** Filled from `nameMap` when a sender sent only the map. */
48
+ name: string | null;
49
+ /** Language tag to text; empty when the sender sent only the plain value. */
50
+ nameMap: Record<string, string>;
51
+ /** A content warning on a sensitive Note; the lede of an Article. */
52
+ summary: string | null;
53
+ summaryMap: Record<string, string>;
54
+ /** HTML, unsanitized; filled from `contentMap` when a sender sent only the map. */
55
+ content: string | null;
56
+ contentMap: Record<string, string>;
57
+ /** The page a person opens: the first `text/html` link a sender gave, else the first link. */
58
+ url: string | null;
59
+ inReplyTo: string | null;
60
+ /** The quoted object, from `quote`, `quoteUrl`, `quoteUri` or `_misskey_quote`. */
61
+ quote: string | null;
62
+ published: Date | null;
63
+ updated: Date | null;
64
+ /** Mastodon reads `summary` on a sensitive Note as a content warning. */
65
+ sensitive: boolean;
66
+ tag: Tag[];
67
+ attachment: Attachment[];
68
+ /** FEP-8b32 proofs, kept so a verifier can find them; an empty list writes no proof. */
69
+ proof: Proof[];
70
+ }
71
+ /** An activity: someone (`actor`) doing something (`type`) to something (`object`). */
72
+ interface Activity extends Object {
73
+ type: ActivityType | (string & {});
74
+ /** Always an IRI. An embedded actor is never trusted over the actor's own document. */
75
+ actor: string;
76
+ /**
77
+ * Embedded objects are kept, because Create carries its Note and Accept or Undo the
78
+ * Follow they answer. Trust one only when its `id` has the actor's origin.
79
+ */
80
+ object: Ref<Object | Activity> | null;
81
+ target: string | null;
82
+ }
83
+ /** An account that can follow, be followed, and send or receive activities. */
84
+ interface Actor extends Object {
85
+ type: ActorType;
86
+ /** The `user` of `acct:user@host`; Mastodon resolves it back through WebFinger. */
87
+ preferredUsername: string;
88
+ inbox: string;
89
+ outbox: string | null;
90
+ followers: string | null;
91
+ following: string | null;
92
+ /** Pinned posts, a Mastodon extension. */
93
+ featured: string | null;
94
+ endpoints: Endpoints;
95
+ /** The RSA key HTTP signatures from this actor verify against. */
96
+ publicKey: PublicKey | null;
97
+ /** FEP-521a Multikey entries; carries the Ed25519 key FEP-8b32 proofs name. */
98
+ assertionMethod: Multikey[];
99
+ alsoKnownAs: string[];
100
+ movedTo: string | null;
101
+ manuallyApprovesFollowers: boolean;
102
+ discoverable: boolean;
103
+ indexable: boolean;
104
+ icon: Image | null;
105
+ image: Image | null;
106
+ }
107
+ /** Server-wide endpoints of an actor. */
108
+ interface Endpoints {
109
+ /** Preferred for delivery, so one POST reaches every follower on that server. */
110
+ sharedInbox: string | null;
111
+ }
112
+ /** An actor's RSA public key, in the shape Mastodon publishes and reads. */
113
+ interface PublicKey {
114
+ /** `<actor>#main-key` on Mastodon; the `keyId` HTTP signatures name. */
115
+ id: string;
116
+ /** The actor the key belongs to; a key whose owner differs never verifies that actor. */
117
+ owner: string;
118
+ publicKeyPem: string;
119
+ }
120
+ /** A FEP-521a verification method. */
121
+ interface Multikey {
122
+ id: string;
123
+ type: "Multikey";
124
+ controller: string;
125
+ /** The key in multibase, `z` + base58btc for Ed25519. */
126
+ publicKeyMultibase: string;
127
+ }
128
+ /** An FEP-8b32 object integrity proof. */
129
+ interface Proof {
130
+ type: "DataIntegrityProof";
131
+ cryptosuite: string;
132
+ /** The `Multikey` id that signed, under the signer's actor. */
133
+ verificationMethod: string;
134
+ proofPurpose: string;
135
+ proofValue: string;
136
+ created: Date | null;
137
+ }
138
+ /** An entry of `tag`: a hashtag, a mention, or a custom emoji. */
139
+ type Tag = Hashtag | Mention | Emoji;
140
+ /** A topic; Mastodon lists the object on that tag's timeline. */
141
+ interface Hashtag {
142
+ type: "Hashtag";
143
+ /** With its `#`, as Mastodon writes it. */
144
+ name: string;
145
+ href: string | null;
146
+ }
147
+ /** An actor the object names, which Mastodon notifies and links in the rendered text. */
148
+ interface Mention {
149
+ type: "Mention";
150
+ /** The mentioned actor's id. */
151
+ href: string;
152
+ /** `@user@host` as the sender wrote it. */
153
+ name: string | null;
154
+ }
155
+ /** A custom emoji, which `content` names as `:shortcode:`. */
156
+ interface Emoji {
157
+ type: "Emoji";
158
+ id: string | null;
159
+ /** The shortcode with its colons, `:blobcat:`. */
160
+ name: string;
161
+ icon: Image;
162
+ updated: Date | null;
163
+ }
164
+ /** An entry of `attachment`: a media file, or a profile field. */
165
+ type Attachment = Media | PropertyValue;
166
+ /** An attached file. Mastodon sends `Document`; Pixelfed and Misskey also send `Image`. */
167
+ interface Media {
168
+ type: "Document" | "Image" | "Video" | "Audio";
169
+ url: string;
170
+ mediaType: string | null;
171
+ /** The alt text. */
172
+ name: string | null;
173
+ blurhash: string | null;
174
+ width: number | null;
175
+ height: number | null;
176
+ /** Mastodon's crop point, each coordinate from -1 to 1. */
177
+ focalPoint: [number, number] | null;
178
+ }
179
+ /** A profile metadata field, as Mastodon lists them in an actor's `attachment`. */
180
+ interface PropertyValue {
181
+ type: "PropertyValue";
182
+ name: string;
183
+ /** HTML; a verified link is an `<a rel="me">`. */
184
+ value: string;
185
+ }
186
+ /** An avatar, header or emoji image. */
187
+ interface Image {
188
+ type: "Image";
189
+ url: string;
190
+ mediaType: string | null;
191
+ }
192
+ /**
193
+ * What a deleted object is served as, answered with `410`. `parseObject` reads one as an
194
+ * `Object` whose `type` is `Tombstone`.
195
+ */
196
+ interface Tombstone {
197
+ id: string;
198
+ type: "Tombstone";
199
+ formerType: string | null;
200
+ deleted: Date | null;
201
+ }
202
+ /** An entry of a collection: an IRI, or the activity or object itself. */
203
+ type Item = Ref<Object | Activity>;
204
+ /** An unordered collection, such as Mastodon's `replies`. */
205
+ interface Collection {
206
+ id: string;
207
+ type: "Collection";
208
+ totalItems: number | null;
209
+ first: Ref<CollectionPage | OrderedCollectionPage> | null;
210
+ last: string | null;
211
+ items: Item[];
212
+ }
213
+ /** One page of an unordered collection. */
214
+ interface CollectionPage {
215
+ /** `null` for a page embedded without one, as Mastodon embeds the first page of `replies`. */
216
+ id: string | null;
217
+ type: "CollectionPage";
218
+ partOf: string | null;
219
+ next: string | null;
220
+ prev: string | null;
221
+ totalItems: number | null;
222
+ items: Item[];
223
+ }
224
+ /** A collection in reverse chronological order: outbox, followers, following, featured. */
225
+ interface OrderedCollection {
226
+ id: string;
227
+ type: "OrderedCollection";
228
+ /** Alone, with no `first`, it is Mastodon's "hide network" shape of a followers list. */
229
+ totalItems: number | null;
230
+ first: Ref<CollectionPage | OrderedCollectionPage> | null;
231
+ last: string | null;
232
+ orderedItems: Item[];
233
+ }
234
+ /** One page of an ordered collection. */
235
+ interface OrderedCollectionPage {
236
+ /** `null` for a page embedded without one. */
237
+ id: string | null;
238
+ type: "OrderedCollectionPage";
239
+ partOf: string | null;
240
+ next: string | null;
241
+ prev: string | null;
242
+ totalItems: number | null;
243
+ orderedItems: Item[];
244
+ }
245
+ /** Any of the four collection shapes `parseCollection` reads. */
246
+ type AnyCollection = Collection | CollectionPage | OrderedCollection | OrderedCollectionPage;
247
+ /** Any document `stringify` and `respond` write. */
248
+ type Document = Object | Activity | Actor | Tombstone | AnyCollection;
249
+ }
File without changes
@@ -0,0 +1,77 @@
1
+ /**
2
+ * In-memory implementations of every store the package calls, for tests and for a local
3
+ * server that needs no persistence. They pass the conformance suite, so a test that runs
4
+ * against them exercises the same contracts a production store is held to.
5
+ *
6
+ * @author [Sergio Xalambrí](https://sergiodxa.com)
7
+ * @copyright Sergio Xalambrí 2026
8
+ */
9
+ import type { DurationInput } from "@sdxc/duration";
10
+ import type { Result } from "@sdxc/result";
11
+ import type { ActorKeys } from "./keys.js";
12
+ import type { ActivityPub } from "./lib/types.js";
13
+ import type { Follower, FollowerStore, KeyProvider, LocalObjects, SeenActivities, StorePage } from "./store.js";
14
+ /**
15
+ * Followers held in insertion order, so every listing is stable across pages. A repeated `put`
16
+ * keeps the follower's position, and every read returns a copy the caller may change freely.
17
+ */
18
+ export declare class MemoryFollowerStore implements FollowerStore {
19
+ #private;
20
+ /** @param followers Followers the store starts with, as if each had been `put` in order. */
21
+ constructor(followers?: Follower[]);
22
+ /** Inserts or replaces by `(actor, id)`, refreshing inbox, shared inbox, `followId` and state. */
23
+ put(follower: Follower): Promise<Result<void, Error>>;
24
+ /** The follower in either state, or `null` when `id` does not follow `actor`. */
25
+ get(actor: string, id: string): Promise<Result<Follower | null, Error>>;
26
+ /** Succeeds whether or not the follower existed. */
27
+ remove(actor: string, id: string): Promise<Result<void, Error>>;
28
+ /** Drops followers of every local actor whose inbox or shared inbox is `inbox`. */
29
+ removeInbox(inbox: string): Promise<Result<void, Error>>;
30
+ /** Accepted followers of `actor` in insertion order, narrowed to `origin` when given. */
31
+ list(actor: string, options: {
32
+ cursor: string | null;
33
+ limit: number;
34
+ origin?: string;
35
+ }): Promise<Result<StorePage<Follower>, Error>>;
36
+ /** Accepted followers of `actor`, the number the followers collection publishes. */
37
+ count(actor: string): Promise<Result<number, Error>>;
38
+ /** Each distinct `sharedInbox ?? inbox` once, in the order its first follower was added. */
39
+ inboxes(actor: string, options: {
40
+ cursor: string | null;
41
+ limit: number;
42
+ }): Promise<Result<StorePage<string>, Error>>;
43
+ }
44
+ /** What a `MemorySeenActivities` reads the time from. */
45
+ export interface MemorySeenActivitiesOptions {
46
+ /**
47
+ * Milliseconds since the epoch, read on every claim, which is what lets a test pass a TTL
48
+ * without waiting for it.
49
+ *
50
+ * @default Date.now
51
+ */
52
+ now?: () => number;
53
+ }
54
+ /** Claimed activity ids, each held until its TTL passes and then claimable again. */
55
+ export declare class MemorySeenActivities implements SeenActivities {
56
+ #private;
57
+ /** @param options The clock claims are timed against. */
58
+ constructor(options?: MemorySeenActivitiesOptions);
59
+ /** Fails on a TTL `@sdxc/duration` cannot read, so a mistyped TTL surfaces as an error. */
60
+ claim(id: string, ttl: DurationInput): Promise<Result<boolean, Error>>;
61
+ }
62
+ /** A fixed set of objects the app serves, found by their `id`. */
63
+ export declare class MemoryLocalObjects implements LocalObjects {
64
+ #private;
65
+ /** @param objects The objects served; a later one with a repeated `id` wins. */
66
+ constructor(objects?: ActivityPub.Object[]);
67
+ /** The object served under `id`, or `null` for any other IRI. */
68
+ find(id: string): Promise<Result<ActivityPub.Object | null, Error>>;
69
+ }
70
+ /** A fixed set of hosted actors' keys, found by actor id. */
71
+ export declare class MemoryKeyProvider implements KeyProvider {
72
+ #private;
73
+ /** @param keys The keys of every hosted actor; a later entry for the same actor wins. */
74
+ constructor(keys?: ActorKeys[]);
75
+ /** The actor's keys, or `null` for an actor this provider does not host. */
76
+ keysOf(actor: string): Promise<Result<ActorKeys | null, Error>>;
77
+ }