@graffiti-garden/api 0.6.3 → 0.6.4
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 +4 -1
- package/dist/index.cjs.map +2 -2
- package/dist/index.mjs.map +2 -2
- package/dist/src/1-api.d.ts +101 -177
- package/dist/src/1-api.d.ts.map +1 -1
- package/dist/src/2-types.d.ts +34 -30
- package/dist/src/2-types.d.ts.map +1 -1
- package/package.json +6 -6
- package/src/1-api.ts +101 -177
- package/src/2-types.ts +34 -30
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@graffiti-garden/api",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.4",
|
|
4
4
|
"description": "The heart of Graffiti",
|
|
5
5
|
"types": "./dist/src/index.d.ts",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -48,11 +48,11 @@
|
|
|
48
48
|
},
|
|
49
49
|
"homepage": "https://api.graffiti.garden/classes/Graffiti.html",
|
|
50
50
|
"devDependencies": {
|
|
51
|
-
"@types/node": "^
|
|
52
|
-
"tsx": "^4.
|
|
53
|
-
"typedoc": "^0.
|
|
54
|
-
"typescript": "^5.
|
|
55
|
-
"vitest": "^3.
|
|
51
|
+
"@types/node": "^24.7.2",
|
|
52
|
+
"tsx": "^4.20.6",
|
|
53
|
+
"typedoc": "^0.28.14",
|
|
54
|
+
"typescript": "^5.9.3",
|
|
55
|
+
"vitest": "^3.2.4"
|
|
56
56
|
},
|
|
57
57
|
"dependencies": {
|
|
58
58
|
"fast-json-patch": "^3.1.1",
|
package/src/1-api.ts
CHANGED
|
@@ -14,171 +14,90 @@ import type { JSONSchema } from "json-schema-to-ts";
|
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* This API describes a small but powerful set of methods that
|
|
17
|
-
* can be used to create many different kinds of social
|
|
18
|
-
*
|
|
19
|
-
*
|
|
17
|
+
* can be used to create many different kinds of social applications,
|
|
18
|
+
* from applications like Twitter, to Messenger, to Wikipedia, to many more new designs.
|
|
19
|
+
* See the [Graffiti project website](https://graffiti.garden)
|
|
20
|
+
* for links to example applications. Additionally, apps built on top
|
|
21
|
+
* of the API interoperate with each other so you can seamlessly switch
|
|
22
|
+
* between apps without losing your friends or data.
|
|
23
|
+
*
|
|
24
|
+
* These API methods should satisfy all of an application's needs for
|
|
20
25
|
* the communication, storage, and access management of social data.
|
|
21
26
|
* 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).
|
|
27
|
+
* user interface tools to present and interact with that data—no server code necessary!
|
|
28
|
+
*
|
|
29
|
+
* The Typescript code for this API is [open source on Github](https://github.com/graffiti-garden/api).
|
|
26
30
|
*
|
|
27
31
|
* There are several different implementations of this Graffiti API available,
|
|
28
|
-
* including a [federated implementation](https://github.com/graffiti-garden/implementation-
|
|
29
|
-
* that lets
|
|
32
|
+
* including a [federated implementation](https://github.com/graffiti-garden/implementation-remote),
|
|
33
|
+
* that lets people choose where their data is stored (you do not need to host your own server)
|
|
30
34
|
* 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.
|
|
35
|
+
* that can be used for testing and development. Different implementations can
|
|
36
|
+
* be swapped-in in the future without changing the API or any of the apps built on
|
|
37
|
+
* top of it. In fact, we're working on an end-to-end encrypted version now!
|
|
38
|
+
* [Follow Theia on BlueSky for updates](https://bsky.app/profile/theias.place).
|
|
55
39
|
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
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).
|
|
40
|
+
* On the other side of the stack, there is [Vue plugin](https://vue.graffiti.garden/variables/GraffitiPlugin.html)
|
|
41
|
+
* that wraps around this API to provide reactivity. Other plugin frameworks
|
|
42
|
+
* and high-level libraries will be available in the future.
|
|
64
43
|
*
|
|
65
|
-
*
|
|
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.
|
|
44
|
+
* ## API Overview
|
|
73
45
|
*
|
|
74
|
-
*
|
|
46
|
+
* The Graffiti API provides applications with methods for {@link login} and {@link logout},
|
|
47
|
+
* methods to store data objects using standard database operations ({@link put}, {@link get}, {@link patch}, and {@link delete}),
|
|
48
|
+
* and a method to {@link discover} data objects from other people.
|
|
49
|
+
* These data objects have a couple structured properties:
|
|
50
|
+
* - {@link GraffitiObjectBase.url | `url`} (string): A globally unique identifier and locator for the object.
|
|
51
|
+
* - {@link GraffitiObjectBase.actor | `actor`} (string): An unforgeable identifier for the creator of the object.
|
|
52
|
+
* - {@link GraffitiObjectBase.allowed | `allowed`} (string[] | undefined): An array of the identities who are allowed to access the object (undefined for public objects).
|
|
53
|
+
* - {@link GraffitiObjectBase.channels | `channels`} (string[]): An array of the *contexts* in which the object should appear.
|
|
54
|
+
* - {@link GraffitiObjectBase.lastModified | `revision`} (number): A number to compare different versions of an object.
|
|
75
55
|
*
|
|
76
|
-
* {@link GraffitiObjectBase.
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
* comment threads, topics, places (real or virtual), pieces of media, and more.
|
|
88
|
-
*
|
|
89
|
-
* For example, consider a comment on a post. If we place that comment in the channel
|
|
90
|
-
* represented by the post's URL, then only people viewing the post will know to
|
|
91
|
-
* look in that channel, giving it visibility akin to a comment on a blog post
|
|
92
|
-
* or comment on Instagram ([since 2019](https://www.buzzfeednews.com/article/katienotopoulos/instagrams-following-activity-tab-is-going-away)).
|
|
93
|
-
* If we also place the comment in the channel represented by the commenter's URI (their
|
|
94
|
-
* {@link GraffitiObjectBase.actor | `actor` URI}), then people viewing the commenter's profile
|
|
95
|
-
* will also see the comment, giving it more visibility, like a reply on Twitter.
|
|
96
|
-
* If we *only* place the comment in the channel represented by the commenter's URI, then
|
|
97
|
-
* it becomes like a quote tweet ([prior to 2020](https://x.com/Support/status/1300555325750292480)),
|
|
98
|
-
* where the comment is only visible to the commenter's followers but not the audience
|
|
99
|
-
* of the original post.
|
|
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.
|
|
56
|
+
* All other data is stored in the object's unstructured {@link GraffitiObjectBase.value | `value`} property.
|
|
57
|
+
* This data can be used to represent social artifacts (e.g. posts, profiles) and activities (e.g. likes, follows).
|
|
58
|
+
* For example, a post might have the value:
|
|
59
|
+
|
|
60
|
+
* ```js
|
|
61
|
+
* {
|
|
62
|
+
* title: "My First Post",
|
|
63
|
+
* content: "Hello, world!",
|
|
64
|
+
* published: 1630483200000
|
|
65
|
+
* }
|
|
66
|
+
* ```
|
|
125
67
|
*
|
|
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.
|
|
68
|
+
* a profile might have the value:
|
|
133
69
|
*
|
|
134
|
-
*
|
|
70
|
+
* ```js
|
|
71
|
+
* {
|
|
72
|
+
* name: "Theia Henderson",
|
|
73
|
+
* pronouns: "she/her",
|
|
74
|
+
* describes: "did:web:theias.place" // Theia's actor ID
|
|
75
|
+
* }
|
|
76
|
+
* ```
|
|
135
77
|
*
|
|
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}.
|
|
78
|
+
* and a "Like" might have the value:
|
|
141
79
|
*
|
|
142
|
-
* ```
|
|
80
|
+
* ```js
|
|
143
81
|
* {
|
|
144
|
-
* activity:
|
|
145
|
-
* target:
|
|
146
|
-
* actor: 'my-user-id'
|
|
82
|
+
* activity: "Like",
|
|
83
|
+
* target: "graffiti:remote:pod.graffiti.garden/12345" // The URL of the graffiti object being liked
|
|
147
84
|
* }
|
|
148
85
|
* ```
|
|
149
86
|
*
|
|
150
|
-
*
|
|
151
|
-
*
|
|
152
|
-
*
|
|
153
|
-
*
|
|
154
|
-
*
|
|
155
|
-
*
|
|
156
|
-
*
|
|
157
|
-
*
|
|
158
|
-
* users can freely switch between them.
|
|
87
|
+
* New social artifacts and activities can be easily created, simply
|
|
88
|
+
* by creating new objects with appropriate properties. Despite the lack of
|
|
89
|
+
* structure, we expect Graffiti object properties to adhere to a "[folksonomy](https://en.wikipedia.org/wiki/Folksonomy)",
|
|
90
|
+
* similar to hashtags. Any string can be used as a hashtag on Twitter,
|
|
91
|
+
* but there is social value in using the same hashtags at other people and
|
|
92
|
+
* so a structure naturally emerges. Similarly, Graffiti objects
|
|
93
|
+
* can have arbitrary properties but if people use the same properties as each other,
|
|
94
|
+
* their apps will interoperate, which has social value.
|
|
159
95
|
*
|
|
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).
|
|
167
|
-
*
|
|
168
|
-
* Interactivy relativity is realized in Graffiti through two design decisions:
|
|
169
|
-
* 1. The creators of objects can only modify their own objects. It is important for
|
|
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.
|
|
96
|
+
* For a more complete and detailed overview of Graffiti's design, please
|
|
97
|
+
* refer to [this section of the Graffiti paper](https://dl.acm.org/doi/10.1145/3746059.3747627#sec-3),
|
|
98
|
+
* published in ACM UIST 2025. The paper also overviews {@link GraffitiObjectBase.channels | `channels`},
|
|
99
|
+
* which are Graffiti's means of organizing data contextually, and a concept called "total reification",
|
|
100
|
+
* which handles explains how moderation, collaboration, and other interactions are managed.
|
|
182
101
|
*
|
|
183
102
|
* @groupDescription CRUD Methods
|
|
184
103
|
* Methods for {@link put | creating}, {@link get | reading}, {@link patch | updating},
|
|
@@ -186,7 +105,7 @@ import type { JSONSchema } from "json-schema-to-ts";
|
|
|
186
105
|
* @groupDescription Query Methods
|
|
187
106
|
* Methods that retrieve or accumulate information about multiple {@link GraffitiObjectBase | Graffiti objects} at a time.
|
|
188
107
|
* @groupDescription Session Management
|
|
189
|
-
* Methods and properties for logging in and out
|
|
108
|
+
* Methods and properties for logging in and out.
|
|
190
109
|
*/
|
|
191
110
|
export abstract class Graffiti {
|
|
192
111
|
/**
|
|
@@ -204,7 +123,7 @@ export abstract class Graffiti {
|
|
|
204
123
|
* @throws {@link GraffitiErrorForbidden} if the {@link GraffitiObjectBase.actor | `actor`}
|
|
205
124
|
* is not the same `actor` as the one who created the object.
|
|
206
125
|
*
|
|
207
|
-
* @returns
|
|
126
|
+
* @returns Returns the object that was replaced if one one exists, otherwise returns an object with
|
|
208
127
|
* with an empty {@link GraffitiObjectBase.value | `value`},
|
|
209
128
|
* {@link GraffitiObjectBase.channels | `channels`}, and {@link GraffitiObjectBase.allowed | `allowed`}
|
|
210
129
|
* list.
|
|
@@ -217,7 +136,7 @@ export abstract class Graffiti {
|
|
|
217
136
|
/**
|
|
218
137
|
* The object to be put. This object is statically type-checked against the [JSON schema](https://json-schema.org/) that can be optionally provided
|
|
219
138
|
* as the generic type parameter. We highly recommend providing a schema to
|
|
220
|
-
* ensure that the
|
|
139
|
+
* ensure that the put object matches subsequent {@link get} or {@link discover}
|
|
221
140
|
* methods.
|
|
222
141
|
*/
|
|
223
142
|
object: GraffitiPutObject<Schema>,
|
|
@@ -238,9 +157,11 @@ export abstract class Graffiti {
|
|
|
238
157
|
* the object's `actor`,
|
|
239
158
|
* the object's {@link GraffitiObjectBase.allowed | `allowed`} and
|
|
240
159
|
* {@link GraffitiObjectBase.channels | `channels`} properties are
|
|
241
|
-
* not revealed, similar to a BCC.
|
|
160
|
+
* not revealed, similar to a BCC email.
|
|
161
|
+
*
|
|
162
|
+
* @returns Returns the retrieved object.
|
|
242
163
|
*
|
|
243
|
-
* @throws {@link GraffitiErrorNotFound} if the object does not exist, has been deleted, or the
|
|
164
|
+
* @throws {@link GraffitiErrorNotFound} if the object does not exist, has been deleted, or the actor is not
|
|
244
165
|
* {@link GraffitiObjectBase.allowed | `allowed`} to access it.
|
|
245
166
|
*
|
|
246
167
|
* @throws {@link GraffitiErrorSchemaMismatch} if the retrieved object does not match the provided schema.
|
|
@@ -270,12 +191,12 @@ export abstract class Graffiti {
|
|
|
270
191
|
* The patching {@link GraffitiObjectBase.actor | `actor`} must be the same as the
|
|
271
192
|
* `actor` that created the object.
|
|
272
193
|
*
|
|
273
|
-
* @returns
|
|
194
|
+
* @returns Returns the original object prior to the patch with its
|
|
274
195
|
* {@link GraffitiObjectBase.lastModified | `lastModified`}
|
|
275
|
-
* property updated to the time of
|
|
196
|
+
* property updated to the time of patching.
|
|
276
197
|
*
|
|
277
198
|
* @throws {@link GraffitiErrorNotFound} if the object does not exist, has already been deleted,
|
|
278
|
-
* or the
|
|
199
|
+
* or the actor is not {@link GraffitiObjectBase.allowed | `allowed`} to access it.
|
|
279
200
|
*
|
|
280
201
|
* @throws {@link GraffitiErrorForbidden} if the {@link GraffitiObjectBase.actor | `actor`}
|
|
281
202
|
* is not the same `actor` as the one who created the object.
|
|
@@ -305,17 +226,17 @@ export abstract class Graffiti {
|
|
|
305
226
|
* `actor` that created the object.
|
|
306
227
|
*
|
|
307
228
|
* It is not possible to re-{@link put} an object that has been deleted
|
|
308
|
-
* to ensure a
|
|
229
|
+
* to ensure a person's [right to be forgotten](https://en.wikipedia.org/wiki/Right_to_be_forgotten).
|
|
309
230
|
* In cases where deleting and restoring an object is useful, an object's
|
|
310
231
|
* {@link GraffitiObjectBase.allowed | `allowed`} property can be set to
|
|
311
|
-
* an empty list to hide it from all
|
|
232
|
+
* an empty list to hide it from all actors except the creator.
|
|
312
233
|
*
|
|
313
|
-
* @returns
|
|
234
|
+
* @returns Returns the object that was deleted with its
|
|
314
235
|
* {@link GraffitiObjectBase.lastModified | `lastModified`}
|
|
315
236
|
* property updated to the time of deletion.
|
|
316
237
|
*
|
|
317
238
|
* @throws {@link GraffitiErrorNotFound} if the object does not exist, has already been deleted,
|
|
318
|
-
* or the
|
|
239
|
+
* or the actor is not {@link GraffitiObjectBase.allowed | `allowed`} to access it.
|
|
319
240
|
*
|
|
320
241
|
* @throws {@link GraffitiErrorForbidden} if the {@link GraffitiObjectBase.actor | `actor`}
|
|
321
242
|
* is not the same `actor` as the one who created the object.
|
|
@@ -335,7 +256,7 @@ export abstract class Graffiti {
|
|
|
335
256
|
): Promise<GraffitiObjectBase>;
|
|
336
257
|
|
|
337
258
|
/**
|
|
338
|
-
* Discovers objects created by any
|
|
259
|
+
* Discovers objects created by any actor that are contained
|
|
339
260
|
* in at least one of the given {@link GraffitiObjectBase.channels | `channels`}
|
|
340
261
|
* and match the given [JSON Schema](https://json-schema.org).
|
|
341
262
|
*
|
|
@@ -364,7 +285,7 @@ export abstract class Graffiti {
|
|
|
364
285
|
* {@link GraffitiObjectBase.lastModified | `lastModified`} fields must be used
|
|
365
286
|
* to determine which object is the most recent.
|
|
366
287
|
*
|
|
367
|
-
* @returns
|
|
288
|
+
* @returns Returns a stream of objects that match the given {@link GraffitiObjectBase.channels | `channels`}
|
|
368
289
|
* and [JSON Schema](https://json-schema.org).
|
|
369
290
|
*
|
|
370
291
|
* @group Query Methods
|
|
@@ -392,18 +313,18 @@ export abstract class Graffiti {
|
|
|
392
313
|
* {@link GraffitiObjectBase.channels | `channels`}
|
|
393
314
|
* that were created by the querying {@link GraffitiObjectBase.actor | `actor`}
|
|
394
315
|
* and match the given [JSON Schema](https://json-schema.org).
|
|
395
|
-
* Unlike {@link discover}, this method will not return objects created by other
|
|
316
|
+
* Unlike {@link discover}, this method will not return objects created by other actors.
|
|
396
317
|
*
|
|
397
|
-
*
|
|
398
|
-
* getting a global view of all
|
|
399
|
-
*
|
|
318
|
+
* Like {@link channelStats}, this method is not useful for most applications,
|
|
319
|
+
* but necessary for getting a global view of all an actor's Graffiti data
|
|
320
|
+
* to implement something like Facebook's Activity Log or a debugging interface.
|
|
400
321
|
*
|
|
401
322
|
* Like {@link discover}, objects are returned asynchronously as they are discovered,
|
|
402
323
|
* the stream will end once all leads have been exhausted, and the stream
|
|
403
324
|
* can be continued using the {@link GraffitiObjectStreamReturn.continue | `continue`}
|
|
404
325
|
* method or {@link GraffitiObjectStreamReturn.cursor | `cursor`} string.
|
|
405
326
|
*
|
|
406
|
-
* @returns
|
|
327
|
+
* @returns Returns a stream of objects created by the querying {@link GraffitiObjectBase.actor | `actor`}
|
|
407
328
|
* that do not belong to any {@link GraffitiObjectBase.channels | `channels`}
|
|
408
329
|
* and match the given [JSON Schema](https://json-schema.org).
|
|
409
330
|
*
|
|
@@ -424,17 +345,18 @@ export abstract class Graffiti {
|
|
|
424
345
|
/**
|
|
425
346
|
* Returns statistics about all the {@link GraffitiObjectBase.channels | `channels`}
|
|
426
347
|
* that an {@link GraffitiObjectBase.actor | `actor`} has posted to.
|
|
427
|
-
* This
|
|
428
|
-
* necessary for certain applications where a user wants a
|
|
429
|
-
* global view of all their Graffiti data or to debug
|
|
430
|
-
* channel usage.
|
|
348
|
+
* This method will not return statistics related to any other actor's channel usage.
|
|
431
349
|
*
|
|
432
|
-
* Like {@link
|
|
350
|
+
* Like {@link recoverOrphans}, this method is not useful for most applications,
|
|
351
|
+
* but necessary for getting a global view of all an actor's Graffiti data
|
|
352
|
+
* to implement something like Facebook's Activity Log or a debugging interface.
|
|
353
|
+
*
|
|
354
|
+
* Like {@link discover}, objects are returned asynchronously as they are discovered and
|
|
433
355
|
* the stream will end once all leads have been exhausted.
|
|
434
356
|
*
|
|
435
357
|
* @group Query Methods
|
|
436
358
|
*
|
|
437
|
-
* @returns
|
|
359
|
+
* @returns Returns a stream of statistics for each {@link GraffitiObjectBase.channels | `channel`}
|
|
438
360
|
* that the {@link GraffitiObjectBase.actor | `actor`} has posted to.
|
|
439
361
|
*/
|
|
440
362
|
abstract channelStats(
|
|
@@ -479,8 +401,9 @@ export abstract class Graffiti {
|
|
|
479
401
|
|
|
480
402
|
/**
|
|
481
403
|
* Begins the login process. Depending on the implementation, this may
|
|
482
|
-
* involve redirecting
|
|
483
|
-
* so it should always be called in response to a
|
|
404
|
+
* involve redirecting to a login page or opening a popup,
|
|
405
|
+
* so it should always be called in response to a gesture, such as clicking
|
|
406
|
+
* a button, due to the [feature-gating browser security feature](https://developer.mozilla.org/en-US/docs/Web/Security/User_activation).
|
|
484
407
|
*
|
|
485
408
|
* The {@link GraffitiSession | session} object is returned
|
|
486
409
|
* asynchronously via {@link Graffiti.sessionEvents | sessionEvents}
|
|
@@ -521,9 +444,10 @@ export abstract class Graffiti {
|
|
|
521
444
|
): Promise<void>;
|
|
522
445
|
|
|
523
446
|
/**
|
|
524
|
-
* Begins the logout process. Depending on the implementation, this may
|
|
447
|
+
* Begins the logout process for a particular {@link GraffitiSession | session}. Depending on the implementation, this may
|
|
525
448
|
* involve redirecting the user to a logout page or opening a popup,
|
|
526
|
-
* so it should always be called in response to a
|
|
449
|
+
* so it should always be called in response to a gesture, such as clicking
|
|
450
|
+
* a button, due to the [feature-gating browser security feature](https://developer.mozilla.org/en-US/docs/Web/Security/User_activation).
|
|
527
451
|
*
|
|
528
452
|
* A confirmation will be returned asynchronously via
|
|
529
453
|
* {@link Graffiti.sessionEvents | sessionEvents}
|
|
@@ -540,7 +464,7 @@ export abstract class Graffiti {
|
|
|
540
464
|
|
|
541
465
|
/**
|
|
542
466
|
* An event target that can be used to listen for the following
|
|
543
|
-
* events and
|
|
467
|
+
* events and their corresponding event types:
|
|
544
468
|
* - `login` - {@link GraffitiLoginEvent}
|
|
545
469
|
* - `logout` - {@link GraffitiLogoutEvent}
|
|
546
470
|
* - `initialized` - {@link GraffitiSessionInitializedEvent}
|
package/src/2-types.ts
CHANGED
|
@@ -34,8 +34,8 @@ export interface GraffitiObjectBase {
|
|
|
34
34
|
* {@link Graffiti.discover} method. This allows creators to express the intended audience of their object
|
|
35
35
|
* which helps to prevent [context collapse](https://en.wikipedia.org/wiki/Context_collapse) even
|
|
36
36
|
* in the highly interoperable ecosystem that Graffiti envisions. For example, channel URIs may be:
|
|
37
|
-
* - A
|
|
38
|
-
* the object to the
|
|
37
|
+
* - A actor's own {@link actor | `actor`} URI. Putting an object in this channel is a way to broadcast
|
|
38
|
+
* the object to the actor's followers, like posting a tweet.
|
|
39
39
|
* - The URL of a Graffiti post. Putting an object in this channel is a way to broadcast to anyone viewing
|
|
40
40
|
* the post, like commenting on a tweet.
|
|
41
41
|
* - A URI representing a topic. Putting an object in this channel is a way to broadcast to anyone interested
|
|
@@ -49,8 +49,8 @@ export interface GraffitiObjectBase {
|
|
|
49
49
|
* also know the right {@link channels | `channel` } to look in). An object can always be accessed by its creator, even if
|
|
50
50
|
* the `allowed` array is empty.
|
|
51
51
|
*
|
|
52
|
-
* The `allowed` array is not revealed to
|
|
53
|
-
* a BCC email.
|
|
52
|
+
* The `allowed` array is not revealed to actors other than the creator, like
|
|
53
|
+
* a BCC email. An actor may choose to add a `to` property to the object's {@link value | `value`} to indicate
|
|
54
54
|
* other recipients, however this is not enforced by Graffiti and may not accurately reflect the actual `allowed` array.
|
|
55
55
|
*
|
|
56
56
|
* `allowed` can be combined with {@link channels | `channels`}. For example, to send someone a direct message
|
|
@@ -87,23 +87,28 @@ export interface GraffitiObjectBase {
|
|
|
87
87
|
* to pull from multiple coexisting Graffiti implementations without collision.
|
|
88
88
|
* Existing schemes include `graffiti:local:` for objects stored locally
|
|
89
89
|
* (see the [local implementation](https://github.com/graffiti-garden/implementation-local))
|
|
90
|
-
* and `graffiti:remote:` for objects stored on Graffiti-specific web servers (see the
|
|
91
|
-
* [remote implementation](https://github.com/graffiti-garden/implementation-remote)).
|
|
90
|
+
* and `graffiti:remote:` for objects stored on Graffiti-specific web servers (see the [remote implementation](https://github.com/graffiti-garden/implementation-remote))
|
|
92
91
|
* Options available in the future might include `graffiti:solid:` for objects stored on Solid servers
|
|
93
92
|
* or `graffiti:p2p:` for objects stored on a peer-to-peer network.
|
|
94
93
|
*/
|
|
95
94
|
url: string;
|
|
96
95
|
|
|
97
96
|
/**
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
*
|
|
97
|
+
* A number used to compare different versions of an object that has been
|
|
98
|
+
* updated via replacement (see {@link Graffiti.put}) or
|
|
99
|
+
* patch (see {@link Graffiti.patch}). Newer versions of
|
|
100
|
+
* an object have larger `revision` values than older versions.
|
|
101
|
+
* The `revision` can only
|
|
102
|
+
* be used to compare different versions of the same object,
|
|
103
|
+
* but cannot reliably be used to compare different objects.
|
|
104
|
+
* In cases where comparing different objects by time is useful,
|
|
105
|
+
* you could instead add `createdAt` or `lastModified` timestamp properties
|
|
106
|
+
* to an object's {@link value | `value`}.
|
|
102
107
|
*
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
*
|
|
108
|
+
* Depending on the implementation, the revision may be a timestamp,
|
|
109
|
+
* an incremental counter, may include randomness for obfuscation, and so on.
|
|
110
|
+
* Be careful not to rely on any of these specific `revision` instantiations
|
|
111
|
+
* as they may not be consistent across different implementations.
|
|
107
112
|
*/
|
|
108
113
|
lastModified: number;
|
|
109
114
|
}
|
|
@@ -141,14 +146,14 @@ export const GraffitiObjectJSONSchema = {
|
|
|
141
146
|
/**
|
|
142
147
|
* This is an object containing only the {@link GraffitiObjectBase.url | `url`}
|
|
143
148
|
* property of a {@link GraffitiObjectBase | GraffitiObject}.
|
|
144
|
-
* It is used as a utility type so that
|
|
149
|
+
* It is used as a utility type so that applications can call {@link Graffiti.get},
|
|
145
150
|
* {@link Graffiti.patch}, or {@link Graffiti.delete} directly on an object
|
|
146
151
|
* rather than on `object.url`.
|
|
147
152
|
*/
|
|
148
153
|
export type GraffitiObjectUrl = Pick<GraffitiObjectBase, "url">;
|
|
149
154
|
|
|
150
155
|
/**
|
|
151
|
-
* This object is a subset of {@link GraffitiObjectBase} that
|
|
156
|
+
* This object is a subset of {@link GraffitiObjectBase} that must be constructed locally before calling {@link Graffiti.put}.
|
|
152
157
|
* This local copy does not require system-generated properties and may be statically typed with
|
|
153
158
|
* a [JSON schema](https://json-schema.org/) to prevent the accidental creation of erroneous objects.
|
|
154
159
|
*
|
|
@@ -180,13 +185,12 @@ export const GraffitiPutObjectJSONSchema = {
|
|
|
180
185
|
|
|
181
186
|
/**
|
|
182
187
|
* This object contains information that the underlying implementation can
|
|
183
|
-
* use to
|
|
184
|
-
* particular {@link GraffitiObjectBase.actor | `actor`}.
|
|
188
|
+
* use to authenticate a particular {@link GraffitiObjectBase.actor | `actor`}.
|
|
185
189
|
* This object is required of all {@link Graffiti} methods
|
|
186
190
|
* that modify objects and is optional for methods that read objects.
|
|
187
191
|
*
|
|
188
192
|
* At a minimum the `session` object must contain the
|
|
189
|
-
* {@link GraffitiSession.actor | `actor`} URI
|
|
193
|
+
* {@link GraffitiSession.actor | `actor`} URI to authenticate with.
|
|
190
194
|
* However it is likely that the `session` object must contain other
|
|
191
195
|
* implementation-specific properties.
|
|
192
196
|
* For example, a Solid implementation might include a
|
|
@@ -197,7 +201,7 @@ export const GraffitiPutObjectJSONSchema = {
|
|
|
197
201
|
* As to why the `session` object is passed as an argument to every method
|
|
198
202
|
* rather than being an internal property of the {@link Graffiti} instance,
|
|
199
203
|
* this is primarily for type-checking to catch bugs related to login state.
|
|
200
|
-
* Graffiti applications can expose some functionality to
|
|
204
|
+
* Graffiti applications can expose some functionality to people who are not logged in
|
|
201
205
|
* with {@link Graffiti.get} and {@link Graffiti.discover} but without type-checking
|
|
202
206
|
* the `session` it can be easy to forget to hide buttons that trigger
|
|
203
207
|
* other methods that require login.
|
|
@@ -211,14 +215,14 @@ export const GraffitiPutObjectJSONSchema = {
|
|
|
211
215
|
*/
|
|
212
216
|
export interface GraffitiSession {
|
|
213
217
|
/**
|
|
214
|
-
* The {@link GraffitiObjectBase.actor | `actor`}
|
|
218
|
+
* The {@link GraffitiObjectBase.actor | `actor`} to authenticate with.
|
|
215
219
|
*/
|
|
216
220
|
actor: string;
|
|
217
221
|
/**
|
|
218
222
|
* A yet undefined property detailing what operations the session
|
|
219
|
-
* grants the
|
|
223
|
+
* grants the actor to perform. For example, to allow a actor to
|
|
220
224
|
* read private messages from a particular set of channels or
|
|
221
|
-
* to allow the
|
|
225
|
+
* to allow the actor to write object matching a particular schema.
|
|
222
226
|
*/
|
|
223
227
|
scope?: {};
|
|
224
228
|
}
|
|
@@ -324,7 +328,7 @@ export interface GraffitiObjectStreamEntry<Schema extends JSONSchema> {
|
|
|
324
328
|
* A result from a {@link GraffitiObjectStreamContinue} that indicated
|
|
325
329
|
* an object has been deleted since the original stream was run.
|
|
326
330
|
* Only sparse metadata about the deleted object is returned to respect
|
|
327
|
-
* the deleting
|
|
331
|
+
* the deleting actor's privacy.
|
|
328
332
|
*
|
|
329
333
|
* @internal
|
|
330
334
|
*/
|
|
@@ -340,7 +344,7 @@ export interface GraffitiObjectStreamContinueTombstone {
|
|
|
340
344
|
tombstone: true;
|
|
341
345
|
/**
|
|
342
346
|
* Sparse metadata about the deleted object. The full object is not returned
|
|
343
|
-
* to respect a
|
|
347
|
+
* to respect a actor's privacy.
|
|
344
348
|
*/
|
|
345
349
|
object: {
|
|
346
350
|
/**
|
|
@@ -377,7 +381,7 @@ export type GraffitiObjectStreamContinueEntry<Schema extends JSONSchema> =
|
|
|
377
381
|
* that allows the stream to be continued from where it left off.
|
|
378
382
|
*
|
|
379
383
|
* The {@link continue} function preserves the typing of the original stream,
|
|
380
|
-
* where as the {@link cursor} string can be serialized for use after a
|
|
384
|
+
* where as the {@link cursor} string can be serialized for use after a person
|
|
381
385
|
* has closed and reopened an application.
|
|
382
386
|
*
|
|
383
387
|
* The continued stream may include `tombstone`s of objects that have been
|
|
@@ -455,7 +459,7 @@ export type GraffitiChannelStatsStream = AsyncGenerator<
|
|
|
455
459
|
|
|
456
460
|
/**
|
|
457
461
|
* The event type produced in {@link Graffiti.sessionEvents}
|
|
458
|
-
* when a
|
|
462
|
+
* when a actor logs in manually from {@link Graffiti.login}
|
|
459
463
|
* or when their session is restored from a previous login.
|
|
460
464
|
* The event name to listen for is `login`.
|
|
461
465
|
*/
|
|
@@ -472,7 +476,7 @@ export type GraffitiLoginEvent = CustomEvent<
|
|
|
472
476
|
|
|
473
477
|
/**
|
|
474
478
|
* The event type produced in {@link Graffiti.sessionEvents}
|
|
475
|
-
* when a
|
|
479
|
+
* when a actor logs out either manually with {@link Graffiti.logout}
|
|
476
480
|
* or when their session times out or otherwise becomes invalid.
|
|
477
481
|
* The event name to listen for is `logout`.
|
|
478
482
|
*/
|
|
@@ -495,8 +499,8 @@ export type GraffitiLogoutEvent = CustomEvent<
|
|
|
495
499
|
* their own {@link GraffitiLoginEvent} events.
|
|
496
500
|
*
|
|
497
501
|
* This event optionally returns an `href` property
|
|
498
|
-
* representing the URL
|
|
499
|
-
*
|
|
502
|
+
* representing the URL that originated a login request,
|
|
503
|
+
* which may be useful for redirecting the user back to
|
|
500
504
|
* the page they were on after login.
|
|
501
505
|
* The event name to listen for is `initialized`.
|
|
502
506
|
*/
|