@graffiti-garden/api 0.6.3 → 1.0.0

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.
@@ -1,237 +1,143 @@
1
- import type { GraffitiObjectUrl, GraffitiObject, GraffitiObjectBase, GraffitiPatch, GraffitiSession, GraffitiPutObject, GraffitiObjectStream, GraffitiChannelStatsStream, GraffitiObjectStreamContinue } from "./2-types";
1
+ import type { GraffitiObjectUrl, GraffitiObject, GraffitiSession, GraffitiPostObject, GraffitiObjectStream, GraffitiObjectStreamContinue } from "./2-types";
2
2
  import type { JSONSchema } from "json-schema-to-ts";
3
3
  /**
4
4
  * This API describes a small but powerful set of methods that
5
- * can be used to create many different kinds of social media applications,
6
- * all of which can interoperate.
7
- * These methods should satisfy all of an application's needs for
5
+ * can be used to create many different kinds of social applications,
6
+ * from applications like Twitter, to Messenger, to Wikipedia, to many more new designs.
7
+ * See the [Graffiti project website](https://graffiti.garden)
8
+ * for links to example applications. Additionally, apps built on top
9
+ * of the API interoperate with each other so you can seamlessly switch
10
+ * between apps without losing your friends or data.
11
+ *
12
+ * These API methods should satisfy all of an application's needs for
8
13
  * the communication, storage, and access management of social data.
9
14
  * The rest of the application can be built with standard client-side
10
- * user interface tools to present and interact with the data
11
- * no server code necessary.
12
- * The Typescript source for this API is available at
13
- * [graffiti-garden/api](https://github.com/graffiti-garden/api).
15
+ * user interface tools to present and interact with that data—no server code necessary!
16
+ *
17
+ * The Typescript code for this API is [open source on Github](https://github.com/graffiti-garden/api).
14
18
  *
15
19
  * There are several different implementations of this Graffiti API available,
16
- * including a [federated implementation](https://github.com/graffiti-garden/implementation-federated),
17
- * that lets users choose where their data is stored,
20
+ * including a [federated implementation](https://github.com/graffiti-garden/implementation-remote),
21
+ * that lets people choose where their data is stored (you do not need to host your own server)
18
22
  * and a [local implementation](https://github.com/graffiti-garden/implementation-local)
19
- * that can be used for testing and development. In our design of Graffiti, this API is our
20
- * primary focus as it is the layer that shapes the experience
21
- * of developing applications. While different implementations can provide tradeoffs between
22
- * other important properties (e.g. privacy, security, scalability), those properties
23
- * are useless if the system as a whole doesn't expose useful functionality to developers.
24
- *
25
- * On the other side of the stack, there is [Vue plugin](https://github.com/graffiti-garden/wrapper-vue/)
26
- * that wraps around this API to provide reactivity. Other high-level libraries
27
- * will be available in the future.
28
- *
29
- * ## Overview
30
- *
31
- * Graffiti provides applications with methods to create and store data
32
- * on behalf of their users using standard CRUD operations:
33
- * {@link put}, {@link get}, {@link patch}, and {@link delete}.
34
- * This data can represent both social artifacts (e.g. posts, profiles) and
35
- * activities (e.g. likes, follows) and is stored as JSON.
36
- *
37
- * The social aspect of Graffiti comes from the {@link discover} method
38
- * which allows applications to find objects that other users made.
39
- * It is a lot like a traditional query operation, but it only
40
- * returns objects that have been placed in particular
41
- * {@link GraffitiObjectBase.channels | `channels`}
42
- * specified by the discovering application.
43
- *
44
- * Graffiti builds on well known concepts and standards wherever possible.
45
- * JSON Objects can be typed with [JSON Schema](https://json-schema.org/) and patches
46
- * can be applied with [JSON Patch](https://jsonpatch.com).
47
- * For interoperability between Graffiti applications, we recommend that
48
- * objects use established properties from the
49
- * [Activity Vocabulary](https://www.w3.org/TR/activitystreams-vocabulary/) when available,
50
- * however it is always possible to create additional properties, contributing
51
- * to the broader [folksonomy](https://en.wikipedia.org/wiki/Folksonomy).
23
+ * that can be used for testing and development. Different implementations can
24
+ * be swapped-in in the future without changing the API or any of the apps built on
25
+ * top of it. In fact, we're working on an end-to-end encrypted version now!
26
+ * [Follow Theia on BlueSky for updates](https://bsky.app/profile/theias.place).
52
27
  *
53
- * {@link GraffitiObjectBase.channels | `channels`} are one of the major concepts
54
- * unique to Graffiti along with *interaction relativity*, defined below.
55
- * Channels create boundaries between public spaces and work to prevent
56
- * [context collapse](https://en.wikipedia.org/wiki/Context_collapse)
57
- * even in a highly interoperable environment.
58
- * Interaction relativity means that all interactions between users are
59
- * actually atomic single-user operations that can be interpreted in different ways,
60
- * which also supports interoperability and pluralism.
28
+ * On the other side of the stack, there is [Vue plugin](https://vue.graffiti.garden/variables/GraffitiPlugin.html)
29
+ * that wraps around this API to provide reactivity. Other plugin frameworks
30
+ * and high-level libraries will be available in the future.
61
31
  *
62
- * ### Channels
32
+ * ## API Overview
63
33
  *
64
- * {@link GraffitiObjectBase.channels | `channels`}
65
- * are a way for the creators of social data to express the intended audience of their
66
- * data. When a user creates data using the {@link put} method, they
67
- * can place their data in one or more channels.
68
- * Content consumers using the {@link discover} method will only see data
69
- * contained in one of the channels they specify.
34
+ * The Graffiti API provides applications with methods for {@link login} and {@link logout},
35
+ * methods to interact with data objects using standard database operations ({@link post}, {@link get}, and {@link delete}),
36
+ * and a method to {@link discover} data objects created by others.
37
+ * These data objects have a couple structured properties:
38
+ * - {@link GraffitiObjectBase.url | `url`} (string): A globally unique identifier and locator for the object.
39
+ * - {@link GraffitiObjectBase.actor | `actor`} (string): An unforgeable identifier for the creator of the object.
40
+ * - {@link GraffitiObjectBase.allowed | `allowed`} (string[] | undefined): An array of the actors who are allowed to access the object (undefined for public objects).
41
+ * - {@link GraffitiObjectBase.channels | `channels`} (string[]): An array of the *contexts* in which the object should appear.
70
42
  *
71
- * While many channels may be public, they partition
72
- * the public into different "contexts", mitigating the
73
- * phenomenon of [context collapse](https://en.wikipedia.org/wiki/Context_collapse) or the "flattening of multiple audiences."
74
- * Any [URI](https://en.wikipedia.org/wiki/Uniform_Resource_Identifier) can be used as a channel, and so channels can represent people,
75
- * comment threads, topics, places (real or virtual), pieces of media, and more.
76
- *
77
- * For example, consider a comment on a post. If we place that comment in the channel
78
- * represented by the post's URL, then only people viewing the post will know to
79
- * look in that channel, giving it visibility akin to a comment on a blog post
80
- * or comment on Instagram ([since 2019](https://www.buzzfeednews.com/article/katienotopoulos/instagrams-following-activity-tab-is-going-away)).
81
- * If we also place the comment in the channel represented by the commenter's URI (their
82
- * {@link GraffitiObjectBase.actor | `actor` URI}), then people viewing the commenter's profile
83
- * will also see the comment, giving it more visibility, like a reply on Twitter.
84
- * If we *only* place the comment in the channel represented by the commenter's URI, then
85
- * it becomes like a quote tweet ([prior to 2020](https://x.com/Support/status/1300555325750292480)),
86
- * where the comment is only visible to the commenter's followers but not the audience
87
- * of the original post.
88
- *
89
- * The channel model differs from other models of communication such as the
90
- * [actor model](https://www.w3.org/TR/activitypub/#Overview) used by ActivityPub,
91
- * the protocol underlying Mastodon, or the [firehose model](https://bsky.social/about/blog/5-5-2023-federation-architecture)
92
- * used by the AT Protocol, the protocol underlying BlueSky.
93
- * The actor model is a fusion of direct messaging (like Email) and broadcasting
94
- * (like RSS) and works well for follow-based communication but struggles
95
- * to pass information via other rendez-vous.
96
- * In the actor model, even something as simple as comments can be
97
- * [very tricky and require server "side effects"](https://seb.jambor.dev/posts/understanding-activitypub-part-3-the-state-of-mastodon/).
98
- * The firehose model dumps all user data into one public database,
99
- * which doesn't allow for the carving out of different contexts that we did in our comment
100
- * example above. In the firehose model a comment will always be visible to *both* the original post's audience and
101
- * the commenter's followers.
102
- *
103
- * In some sense, channels provide a sort of "social access control" by forming
104
- * expectations about the audiences of different online spaces.
105
- * As a real world analogy, oftentimes support groups, such as alcoholics
106
- * anonymous, are open to the public but people in those spaces feel comfortable sharing intimate details
107
- * because they have expectations about the other people attending.
108
- * If someone malicious went to support groups just to spread people's secrets,
109
- * they would be shamed for violating these norms.
110
- * Similarly, in Graffiti, while you could spider public channels like a search engine
111
- * to find content about a person, revealing that you've done such a thing
112
- * would be shameful.
43
+ * All other data is stored in the object's unstructured {@link GraffitiObjectBase.value | `value`} property.
44
+ * This data can be used to represent social artifacts (e.g. posts, profiles) and activities (e.g. likes, follows).
45
+ * For example, a post might have the value:
46
+
47
+ * ```js
48
+ * {
49
+ * title: "My First Post",
50
+ * content: "Hello, world!",
51
+ * published: 1630483200000
52
+ * }
53
+ * ```
113
54
  *
114
- * Still, social access control is not perfect and so in situations where privacy is important,
115
- * objects can also be given
116
- * an {@link GraffitiObjectBase.allowed | `allowed`} list.
117
- * For example, to send someone a direct message you should put an object representing
118
- * that message in the channel that represents them (their {@link GraffitiObjectBase.actor | `actor` URI}),
119
- * so they can find it, *and* set the `allowed` field to only include the recipient,
120
- * so only they can read it.
55
+ * a profile might have the value:
121
56
  *
122
- * ### Interaction relativity
57
+ * ```js
58
+ * {
59
+ * name: "Theia Henderson",
60
+ * pronouns: "she/her",
61
+ * describes: "did:web:theias.place" // Theia's actor ID
62
+ * }
63
+ * ```
123
64
  *
124
- * Interaction relativity posits that "interaction between two individuals only
125
- * exists relative to an observer," or equivalently, all interaction is [reified](https://en.wikipedia.org/wiki/Reification_(computer_science)).
126
- * For example, if one user creates a post and another user wants to "like" that post,
127
- * their like is not modifying the original post, it is simply another data object that points
128
- * to the post being liked, via its {@link GraffitiObjectBase.url | URL}.
65
+ * and a "Like" might have the value:
129
66
  *
130
- * ```json
67
+ * ```js
131
68
  * {
132
- * activity: 'like',
133
- * target: 'url-of-the-post-i-like',
134
- * actor: 'my-user-id'
69
+ * activity: "Like",
70
+ * target: "graffiti:remote:pod.graffiti.garden/12345" // The URL of the graffiti object being liked
135
71
  * }
136
72
  * ```
137
73
  *
138
- * In Graffiti, all interactions including *moderation* and *collaboration* are relative.
139
- * This means that applications can freely choose which interactions
140
- * they want to express to their users and how.
141
- * For example, one application could have a single fixed moderator,
142
- * another could allow users to choose which moderators they would like filter their content
143
- * like [Bluesky's stackable moderation](https://bsky.social/about/blog/03-12-2024-stackable-moderation),
144
- * and another could implement a fully democratic system like [PolicyKit](https://policykit.org/).
145
- * Each of these applications is one interpretation of the underlying refieid user interactions and
146
- * users can freely switch between them.
147
- *
148
- * Interaction relativy also allows applications to introduce new sorts of interactions
149
- * without having to coordinate with all the other existing applications,
150
- * keeping the ecosystem flexible and interoperable.
151
- * For example, an application could [add a "Trust" button to posts](https://social.cs.washington.edu/pub_details.html?id=trustnet)
152
- * and use it assess the truthfulness of posts made on applications across Graffiti.
153
- * New sorts of interactions like these can be smoothly absorbed by the broader ecosystem
154
- * as a [folksonomy](https://en.wikipedia.org/wiki/Folksonomy).
74
+ * New social artifacts and activities can be easily created, simply
75
+ * by creating new objects with appropriate properties. Despite the lack of
76
+ * structure, we expect Graffiti object properties to adhere to a "[folksonomy](https://en.wikipedia.org/wiki/Folksonomy)",
77
+ * similar to hashtags. Any string can be used as a hashtag on Twitter,
78
+ * but there is social value in using the same hashtags at other people and
79
+ * so a structure naturally emerges. Similarly, Graffiti objects
80
+ * can have arbitrary properties but if people use the same properties as each other,
81
+ * their apps will interoperate, which has social value.
155
82
  *
156
- * Interactivy relativity is realized in Graffiti through two design decisions:
157
- * 1. The creators of objects can only modify their own objects. It is important for
158
- * users to be able to change and delete their own content to respect their
159
- * [right to be forgotten](https://en.wikipedia.org/wiki/Right_to_be_forgotten),
160
- * but beyond self-correction and self-censorship all other interaction is reified.
161
- * Many interactions can be reified via pointers, as in the "like" example above, and collaborative
162
- * edits can be refieid via [CRDTs](https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type).
163
- * 2. No one owns channels. Unlike IRC/Slack channels or [Matrix rooms](https://matrix.org/docs/matrix-concepts/rooms_and_events/),
164
- * anyone can post to any channel, so long as they know the URI of that channel.
165
- * It is up to applications to hide content from channels either according to manual
166
- * filters or in response to user action.
167
- * For example, a user may create a post with the flag `disableReplies`.
168
- * Applications could then filter out any content from the replies channel
169
- * that the original poster has not specifically approved.
83
+ * For a more complete and detailed overview of Graffiti's design, please
84
+ * refer to [this section of the Graffiti paper](https://dl.acm.org/doi/10.1145/3746059.3747627#sec-3),
85
+ * published in ACM UIST 2025. The paper also overviews {@link GraffitiObjectBase.channels | `channels`},
86
+ * which are Graffiti's means of organizing data contextually, and a concept called "total reification",
87
+ * which handles explains how moderation, collaboration, and other interactions are managed.
170
88
  *
171
- * @groupDescription CRUD Methods
172
- * Methods for {@link put | creating}, {@link get | reading}, {@link patch | updating},
89
+ * @groupDescription 1 - Single-Object Methods
90
+ * Methods for {@link post | creating}, {@link get | reading},
173
91
  * and {@link delete | deleting} {@link GraffitiObjectBase | Graffiti objects}.
174
- * @groupDescription Query Methods
92
+ * @groupDescription 2 - Multi-Object Methods
175
93
  * Methods that retrieve or accumulate information about multiple {@link GraffitiObjectBase | Graffiti objects} at a time.
176
- * @groupDescription Session Management
177
- * Methods and properties for logging in and out of a Graffiti implementation.
94
+ * @groupDescription 3 - Media Methods
95
+ * Methods for {@link postMedia | creating}, {@link getMedia | reading},
96
+ * and {@link deleteMedia | deleting} media data.
97
+ * @groupDescription 4 - Session Management
98
+ * Methods and properties for logging in and out.
178
99
  */
179
100
  export declare abstract class Graffiti {
180
101
  /**
181
- * Creates a new {@link GraffitiObjectBase | object} or replaces an existing object.
182
- * An object can only be replaced by the same {@link GraffitiObjectBase.actor | `actor`}
183
- * that created it.
102
+ * Creates a new {@link GraffitiObjectBase | object}.
184
103
  *
185
- * Replacement occurs when the {@link GraffitiObjectBase.url | `url`} of
186
- * the replaced object exactly matches an existing object's URL.
187
- *
188
- * @throws {@link GraffitiErrorNotFound} if a {@link GraffitiObjectBase.url | `url`}
189
- * is provided that has not been created yet or the {@link GraffitiObjectBase.actor | `actor`}
190
- * is not {@link GraffitiObjectBase.allowed | `allowed`} to see it.
191
- *
192
- * @throws {@link GraffitiErrorForbidden} if the {@link GraffitiObjectBase.actor | `actor`}
193
- * is not the same `actor` as the one who created the object.
194
- *
195
- * @returns The object that was replaced if one one exists, otherwise an object with
196
- * with an empty {@link GraffitiObjectBase.value | `value`},
197
- * {@link GraffitiObjectBase.channels | `channels`}, and {@link GraffitiObjectBase.allowed | `allowed`}
198
- * list.
199
- * The {@link GraffitiObjectBase.lastModified | `lastModified`} property of the returned object
200
- * will be updated to the time of replacement/creation.
104
+ * @returns Returns the object that has been posted, complete with its
105
+ * assigned {@link GraffitiObjectBase.url | `url`} and
106
+ * {@link GraffitiObjectBase.actor | `actor`}.
201
107
  *
202
- * @group CRUD Methods
108
+ * @group 1 - Single-Object Methods
203
109
  */
204
- abstract put<Schema extends JSONSchema>(
110
+ abstract post<Schema extends JSONSchema>(
205
111
  /**
206
- * The object to be put. This object is statically type-checked against the [JSON schema](https://json-schema.org/) that can be optionally provided
207
- * as the generic type parameter. We highly recommend providing a schema to
208
- * ensure that the PUT object matches subsequent {@link get} or {@link discover}
112
+ * An object to post. This object is statically type-checked against the [JSON schema](https://json-schema.org/) that can be optionally provided
113
+ * as the generic type parameter. It is recommended to a schema to
114
+ * ensure that the posted object matches subsequent {@link get} or {@link discover}
209
115
  * methods.
210
116
  */
211
- object: GraffitiPutObject<Schema>,
117
+ object: GraffitiPostObject<Schema>,
212
118
  /**
213
119
  * An implementation-specific object with information to authenticate the
214
120
  * {@link GraffitiObjectBase.actor | `actor`}.
215
121
  */
216
- session: GraffitiSession): Promise<GraffitiObjectBase>;
122
+ session: GraffitiSession): Promise<GraffitiObject<Schema>>;
217
123
  /**
218
- * Retrieves an object from a given {@link GraffitiObjectBase.url | `url`}.
219
- *
220
- * The retrieved object is type-checked against the provided [JSON schema](https://json-schema.org/)
221
- * otherwise a {@link GraffitiErrorSchemaMismatch} is thrown.
124
+ * Retrieves an object from a given {@link GraffitiObjectBase.url | `url`} matching
125
+ * the provided `schema`.
222
126
  *
223
127
  * If the retreiving {@link GraffitiObjectBase.actor | `actor`} is not
224
128
  * the object's `actor`,
225
129
  * the object's {@link GraffitiObjectBase.allowed | `allowed`} and
226
130
  * {@link GraffitiObjectBase.channels | `channels`} properties are
227
- * not revealed, similar to a BCC.
131
+ * not revealed, similar to a BCC email.
132
+ *
133
+ * @returns Returns the retrieved object.
228
134
  *
229
- * @throws {@link GraffitiErrorNotFound} if the object does not exist, has been deleted, or the user is not
135
+ * @throws {@link GraffitiErrorNotFound} if the object does not exist, has been deleted, or the actor is not
230
136
  * {@link GraffitiObjectBase.allowed | `allowed`} to access it.
231
137
  *
232
138
  * @throws {@link GraffitiErrorSchemaMismatch} if the retrieved object does not match the provided schema.
233
139
  *
234
- * @group CRUD Methods
140
+ * @group 1 - Single-Object Methods
235
141
  */
236
142
  abstract get<Schema extends JSONSchema>(
237
143
  /**
@@ -250,59 +156,18 @@ export declare abstract class Graffiti {
250
156
  */
251
157
  session?: GraffitiSession | null): Promise<GraffitiObject<Schema>>;
252
158
  /**
253
- * Patches an existing object at a given {@link GraffitiObjectBase.url | `url`}.
254
- * The patching {@link GraffitiObjectBase.actor | `actor`} must be the same as the
255
- * `actor` that created the object.
256
- *
257
- * @returns The original object prior to the patch with its
258
- * {@link GraffitiObjectBase.lastModified | `lastModified`}
259
- * property updated to the time of deletion.
260
- *
261
- * @throws {@link GraffitiErrorNotFound} if the object does not exist, has already been deleted,
262
- * or the user is not {@link GraffitiObjectBase.allowed | `allowed`} to access it.
263
- *
264
- * @throws {@link GraffitiErrorForbidden} if the {@link GraffitiObjectBase.actor | `actor`}
265
- * is not the same `actor` as the one who created the object.
266
- *
267
- * @group CRUD Methods
268
- */
269
- abstract patch(
270
- /**
271
- * A collection of [JSON Patch](https://jsonpatch.com) operations
272
- * to apply to the object. See {@link GraffitiPatch} for more information.
273
- */
274
- patch: GraffitiPatch,
275
- /**
276
- * The location of the object to patch.
277
- */
278
- url: string | GraffitiObjectUrl,
279
- /**
280
- * An implementation-specific object with information to authenticate the
281
- * {@link GraffitiObjectBase.actor | `actor`}.
282
- */
283
- session: GraffitiSession): Promise<GraffitiObjectBase>;
284
- /**
285
- * Deletes an object from a given {@link GraffitiObjectBase.url | `url`}.
159
+ * Deletes an object from a given {@link GraffitiObjectBase.url | `url`}
160
+ * that had previously been {@link post | `post`ed}.
286
161
  * The deleting {@link GraffitiObjectBase.actor | `actor`} must be the same as the
287
162
  * `actor` that created the object.
288
163
  *
289
- * It is not possible to re-{@link put} an object that has been deleted
290
- * to ensure a user's [right to be forgotten](https://en.wikipedia.org/wiki/Right_to_be_forgotten).
291
- * In cases where deleting and restoring an object is useful, an object's
292
- * {@link GraffitiObjectBase.allowed | `allowed`} property can be set to
293
- * an empty list to hide it from all users except the creator.
294
- *
295
- * @returns The object that was deleted with its
296
- * {@link GraffitiObjectBase.lastModified | `lastModified`}
297
- * property updated to the time of deletion.
298
- *
299
- * @throws {@link GraffitiErrorNotFound} if the object does not exist, has already been deleted,
300
- * or the user is not {@link GraffitiObjectBase.allowed | `allowed`} to access it.
164
+ * @throws {@link GraffitiErrorNotFound} if the object does not exist, has already been deleted,
165
+ * or the actor is not {@link GraffitiObjectBase.allowed | `allowed`} to access it.
301
166
  *
302
167
  * @throws {@link GraffitiErrorForbidden} if the {@link GraffitiObjectBase.actor | `actor`}
303
168
  * is not the same `actor` as the one who created the object.
304
169
  *
305
- * @group CRUD Methods
170
+ * @group 1 - Single-Object Methods
306
171
  */
307
172
  abstract delete(
308
173
  /**
@@ -313,9 +178,9 @@ export declare abstract class Graffiti {
313
178
  * An implementation-specific object with information to authenticate the
314
179
  * {@link GraffitiObjectBase.actor | `actor`}.
315
180
  */
316
- session: GraffitiSession): Promise<GraffitiObjectBase>;
181
+ session: GraffitiSession): Promise<void>;
317
182
  /**
318
- * Discovers objects created by any user that are contained
183
+ * Discovers objects created by any actor that are contained
319
184
  * in at least one of the given {@link GraffitiObjectBase.channels | `channels`}
320
185
  * and match the given [JSON Schema](https://json-schema.org).
321
186
  *
@@ -329,7 +194,7 @@ export declare abstract class Graffiti {
329
194
  * string can be serialized to continue the stream after an application is closed
330
195
  * and reopened.
331
196
  *
332
- * `discover` will not return objects that the {@link GraffitiObjectBase.actor | `actor`}
197
+ * `discover` will not return objects that the querying {@link GraffitiObjectBase.actor | `actor`}
333
198
  * is not {@link GraffitiObjectBase.allowed | `allowed`} to access.
334
199
  * If the `actor` is not the creator of a discovered object,
335
200
  * the allowed list will be masked to only contain the querying actor if the
@@ -340,14 +205,11 @@ export declare abstract class Graffiti {
340
205
  *
341
206
  * Since different implementations may fetch data from multiple sources there is
342
207
  * no guarentee on the order that objects are returned in.
343
- * It is also possible that duplicate objects are returned and their
344
- * {@link GraffitiObjectBase.lastModified | `lastModified`} fields must be used
345
- * to determine which object is the most recent.
346
208
  *
347
- * @returns A stream of objects that match the given {@link GraffitiObjectBase.channels | `channels`}
209
+ * @returns Returns a stream of objects that match the given {@link GraffitiObjectBase.channels | `channels`}
348
210
  * and [JSON Schema](https://json-schema.org).
349
211
  *
350
- * @group Query Methods
212
+ * @group 2 - Multi-Object Methods
351
213
  */
352
214
  abstract discover<Schema extends JSONSchema>(
353
215
  /**
@@ -366,138 +228,144 @@ export declare abstract class Graffiti {
366
228
  */
367
229
  session?: GraffitiSession | null): GraffitiObjectStream<Schema>;
368
230
  /**
369
- * Discovers objects **not** contained in any
370
- * {@link GraffitiObjectBase.channels | `channels`}
371
- * that were created by the querying {@link GraffitiObjectBase.actor | `actor`}
372
- * and match the given [JSON Schema](https://json-schema.org).
373
- * Unlike {@link discover}, this method will not return objects created by other users.
231
+ * Continues a {@link GraffitiObjectStream} from a given
232
+ * {@link GraffitiObjectStreamReturn.cursor | `cursor`} string.
233
+ * The continuation will return new objects that have been {@link post | `post`ed}
234
+ * that match the original stream, and also returns the
235
+ * {@link GraffitiObjectBase.url | `url`}s of objects that
236
+ * have been {@link delete | `delete`d}, as marked by a `tombstone`.
374
237
  *
375
- * This method is not useful for most applications, but necessary for
376
- * getting a global view of all a user's Graffiti data or debugging
377
- * channel usage.
238
+ * The `cursor` allows the client to
239
+ * serialize the state of the stream and continue it later.
240
+ * However this method loses any typing information that was
241
+ * present in the original stream. For better type safety
242
+ * and when serializing is not necessary, use the
243
+ * {@link GraffitiObjectStreamReturn.continue | `continue`} method
244
+ * instead, which is returned along with the `cursor` at the
245
+ * end of the original stream.
378
246
  *
379
- * Like {@link discover}, objects are returned asynchronously as they are discovered,
380
- * the stream will end once all leads have been exhausted, and the stream
381
- * can be continued using the {@link GraffitiObjectStreamReturn.continue | `continue`}
382
- * method or {@link GraffitiObjectStreamReturn.cursor | `cursor`} string.
247
+ * @throws {@link GraffitiErrorForbidden} if the {@link GraffitiObjectBase.actor | `actor`}
248
+ * provided in the `session` is not the same as the `actor`
249
+ * that initiated the original stream.
383
250
  *
384
- * @returns A stream of objects created by the querying {@link GraffitiObjectBase.actor | `actor`}
385
- * that do not belong to any {@link GraffitiObjectBase.channels | `channels`}
386
- * and match the given [JSON Schema](https://json-schema.org).
251
+ * @group 2 - Multi-Object Methods
252
+ */
253
+ abstract continueDiscover(cursor: string, session?: GraffitiSession | null): GraffitiObjectStreamContinue<{}>;
254
+ /**
255
+ * Uploads media data, such as an image or video.
256
+ *
257
+ * Unlike structured {@link GraffitiObjectBase | objects},
258
+ * media is not indexed for {@link discover | `discover`y} and
259
+ * must be retrieved by its exact URL using {@link getMedia}
260
+ *
261
+ * @returns The URL that the media was posted to.
387
262
  *
388
- * @group Query Methods
263
+ * @group 3 - Media Methods
389
264
  */
390
- abstract recoverOrphans<Schema extends JSONSchema>(
265
+ abstract postMedia(
391
266
  /**
392
- * A [JSON Schema](https://json-schema.org) that orphaned objects must satisfy.
267
+ * The binary data of the media to be uploaded,
268
+ * along with its [media type](https://www.iana.org/assignments/media-types/media-types.xhtml),
269
+ * formatted as a [Blob](https://developer.mozilla.org/en-US/docs/Web/API/Blob).
393
270
  */
394
- schema: Schema,
271
+ media: Blob,
395
272
  /**
396
273
  * An implementation-specific object with information to authenticate the
397
274
  * {@link GraffitiObjectBase.actor | `actor`}.
398
275
  */
399
- session: GraffitiSession): GraffitiObjectStream<Schema>;
276
+ session: GraffitiSession): Promise<string>;
400
277
  /**
401
- * Returns statistics about all the {@link GraffitiObjectBase.channels | `channels`}
402
- * that an {@link GraffitiObjectBase.actor | `actor`} has posted to.
403
- * This is not very useful for most applications, but
404
- * necessary for certain applications where a user wants a
405
- * global view of all their Graffiti data or to debug
406
- * channel usage.
278
+ * Deletes media previously {@link postMedia | `post`ed} to a given URL.
407
279
  *
408
- * Like {@link discover}, objects are returned asynchronously as they are discovered,
409
- * the stream will end once all leads have been exhausted.
280
+ * @throws {@link GraffitiErrorNotFound} if no media at that URL exists.
410
281
  *
411
- * @group Query Methods
282
+ * @throws {@link GraffitiErrorForbidden} if the {@link GraffitiObjectBase.actor | `actor`}
283
+ * provided in the `session` is not the same as the `actor` that {@link postMedia | `post`ed}
284
+ * the media.
412
285
  *
413
- * @returns A stream of statistics for each {@link GraffitiObjectBase.channels | `channel`}
414
- * that the {@link GraffitiObjectBase.actor | `actor`} has posted to.
286
+ * @group 3 - Media Methods
415
287
  */
416
- abstract channelStats(
288
+ abstract deleteMedia(
289
+ /**
290
+ * A globally unique identifier and locator for the media.
291
+ */
292
+ mediaUrl: string,
417
293
  /**
418
294
  * An implementation-specific object with information to authenticate the
419
295
  * {@link GraffitiObjectBase.actor | `actor`}.
420
296
  */
421
- session: GraffitiSession): GraffitiChannelStatsStream;
297
+ session: GraffitiSession): Promise<void>;
422
298
  /**
423
- * Continues a {@link GraffitiObjectStream} from a given
424
- * {@link GraffitiObjectStreamReturn.cursor | `cursor`} string.
425
- * The continuation will return new objects that have been created
426
- * that match the original stream, and also returns the
427
- * {@link GraffitiObjectBase.url | `url`}s of objects that
428
- * have been deleted, as marked by a `tombstone`.
299
+ * Retrieves media from the given media URL, adhering to the given requirements.
429
300
  *
430
- * The continuation may also include duplicates of objects that
431
- * were already returned by the original stream. This is dependent
432
- * on how much state the underlying implementation maintains.
301
+ * @throws {@link GraffitiErrorNotFound} if no media at that URL exists.
433
302
  *
434
- * The `cursor` allows the client to
435
- * serialize the state of the stream and continue it later.
436
- * However this method loses any typing information that was
437
- * present in the original stream. For better type safety
438
- * and when serializing is not necessary, use the
439
- * {@link GraffitiObjectStreamReturn.continue | `continue`} method
440
- * instead, which is returned along with the `cursor` at the
441
- * end of the original stream.
303
+ * @throws {@link GraffitiErrorTooLarge} if the media exceeds the given `maxBytes`.
442
304
  *
443
- * @throws {@link GraffitiErrorForbidden} if the {@link GraffitiObjectBase.actor | `actor`}
444
- * provided in the `session` is not the same as the `actor`
445
- * that initiated the original stream.
305
+ * @throws {@link GraffitiErrorNotAcceptable} if the media does not match the given
306
+ * `accept` specification.
307
+ *
308
+ * @returns The URL of the retrieved media, as a [Blob](https://developer.mozilla.org/en-US/docs/Web/API/Blob)
309
+ * and the {@link GraffitiObjectBase.actor | `actor`} that posted it.
446
310
  *
447
- * @group Query Methods
311
+ * @group 3 - Media Methods
448
312
  */
449
- abstract continueObjectStream(cursor: string, session?: GraffitiSession | null): GraffitiObjectStreamContinue<{}>;
313
+ abstract getMedia(
314
+ /**
315
+ * A globally unique identifier and locator for the media.
316
+ */
317
+ mediaUrl: string,
318
+ /**
319
+ * An optional set of requirements the retrieved media must meet.
320
+ */
321
+ requirements?: {
322
+ /**
323
+ * A list of acceptable media types for the retrieved media,
324
+ * formatted as like an [HTTP Accept header](https://httpwg.org/specs/rfc9110.html#field.accept)
325
+ */
326
+ accept?: string;
327
+ /**
328
+ * The maximum size, in bytes, of the media.
329
+ */
330
+ maxBytes?: number;
331
+ }): Promise<{
332
+ media: Blob;
333
+ actor: string;
334
+ }>;
450
335
  /**
451
336
  * Begins the login process. Depending on the implementation, this may
452
- * involve redirecting the user to a login page or opening a popup,
453
- * so it should always be called in response to a user action.
337
+ * involve redirecting to a login page or opening a popup,
338
+ * so it should always be called in response to a gesture, such as clicking
339
+ * a button, due to the [feature-gating browser security feature](https://developer.mozilla.org/en-US/docs/Web/Security/User_activation).
454
340
  *
455
341
  * The {@link GraffitiSession | session} object is returned
456
342
  * asynchronously via {@link Graffiti.sessionEvents | sessionEvents}
457
343
  * as a {@link GraffitiLoginEvent} with event type `login`.
458
344
  *
459
- * @group Session Management
345
+ * @group 4 - Session Management
460
346
  */
461
347
  abstract login(
462
348
  /**
463
- * Suggestions for the permissions that the
464
- * login process should grant. The login process may not
465
- * provide the exact proposed permissions.
349
+ * A suggested actor to login as. For example, if a user tries to
350
+ * edit a post but are not logged in, the interface can infer that
351
+ * they might want to log in as the actor who created the post
352
+ * they are attempting to edit.
353
+ *
354
+ * Even if provided, the implementation should allow the user
355
+ * to log in as a different actor if they choose.
466
356
  */
467
- proposal?: {
468
- /**
469
- * A suggested actor to login as. For example, if a user tries to
470
- * edit a post but are not logged in, the interface can infer that
471
- * they might want to log in as the actor who created the post
472
- * they are attempting to edit.
473
- *
474
- * Even if provided, the implementation should allow the user
475
- * to log in as a different actor if they choose.
476
- */
477
- actor?: string;
478
- /**
479
- * A yet to be defined permissions scope. An application may use
480
- * this to indicate the minimum necessary scope needed to
481
- * operate. For example, it may need to be able read private
482
- * messages from a certain set of channels, or write messages that
483
- * follow a particular schema.
484
- *
485
- * The login process should make it clear what scope an application
486
- * is requesting and allow the user to enhance or reduce that
487
- * scope as necessary.
488
- */
489
- scope?: {};
490
- }): Promise<void>;
357
+ actor?: string): Promise<void>;
491
358
  /**
492
- * Begins the logout process. Depending on the implementation, this may
359
+ * Begins the logout process for a particular {@link GraffitiSession | session}. Depending on the implementation, this may
493
360
  * involve redirecting the user to a logout page or opening a popup,
494
- * so it should always be called in response to a user action.
361
+ * so it should always be called in response to a gesture, such as clicking
362
+ * a button, due to the [feature-gating browser security feature](https://developer.mozilla.org/en-US/docs/Web/Security/User_activation).
495
363
  *
496
364
  * A confirmation will be returned asynchronously via
497
365
  * {@link Graffiti.sessionEvents | sessionEvents}
498
366
  * as a {@link GraffitiLogoutEvent} as event type `logout`.
499
367
  *
500
- * @group Session Management
368
+ * @group 4 - Session Management
501
369
  */
502
370
  abstract logout(
503
371
  /**
@@ -506,12 +374,12 @@ export declare abstract class Graffiti {
506
374
  session: GraffitiSession): Promise<void>;
507
375
  /**
508
376
  * An event target that can be used to listen for the following
509
- * events and they're corresponding event types:
377
+ * events and their corresponding event types:
510
378
  * - `login` - {@link GraffitiLoginEvent}
511
379
  * - `logout` - {@link GraffitiLogoutEvent}
512
380
  * - `initialized` - {@link GraffitiSessionInitializedEvent}
513
381
  *
514
- * @group Session Management
382
+ * @group 4 - Session Management
515
383
  */
516
384
  abstract readonly sessionEvents: EventTarget;
517
385
  }