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