@graffiti-garden/api 1.0.0 → 1.0.2
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/dist/index.cjs +1 -1
- package/dist/index.cjs.map +4 -4
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +4 -4
- package/dist/src/1-api.d.ts +66 -19
- package/dist/src/1-api.d.ts.map +1 -1
- package/dist/src/2-types.d.ts +3 -3
- package/dist/src/2-types.d.ts.map +1 -1
- package/dist/src/3-errors.d.ts +0 -9
- package/dist/src/3-errors.d.ts.map +1 -1
- package/dist/src/4-utilities.d.ts +8 -0
- package/dist/src/4-utilities.d.ts.map +1 -0
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/tests/crud.d.ts.map +1 -1
- package/dist/tests/index.d.ts +3 -0
- package/dist/tests/index.d.ts.map +1 -1
- package/dist/tests/media.d.ts +3 -0
- package/dist/tests/media.d.ts.map +1 -0
- package/dist/tests.mjs +683 -0
- package/dist/tests.mjs.map +4 -4
- package/package.json +3 -2
- package/src/1-api.ts +67 -17
- package/src/2-types.ts +4 -3
- package/src/3-errors.ts +0 -24
- package/src/4-utilities.ts +65 -0
- package/src/index.ts +1 -0
- package/tests/crud.ts +0 -15
- package/tests/index.ts +3 -2
- package/tests/media.ts +158 -0
package/dist/index.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var o=Object.defineProperty;var O=Object.getOwnPropertyDescriptor;var h=Object.getOwnPropertyNames;var b=Object.prototype.hasOwnProperty;var u=(t,e)=>{for(var r in e)o(t,r,{get:e[r],enumerable:!0})},d=(t,e,r,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of h(e))!b.call(t,i)&&i!==r&&o(t,i,{get:()=>e[i],enumerable:!(s=O(e,i))||s.enumerable});return t};var G=t=>d(o({},"__esModule",{value:!0}),t);var P={};u(P,{Graffiti:()=>n,GraffitiErrorForbidden:()=>c,GraffitiErrorInvalidSchema:()=>a,GraffitiErrorNotAcceptable:()=>p,GraffitiErrorNotFound:()=>f,GraffitiErrorSchemaMismatch:()=>m,GraffitiErrorTooLarge:()=>S,GraffitiObjectJSONSchema:()=>l,GraffitiPostObjectJSONSchema:()=>j,compileGraffitiObjectSchema:()=>x,isActorAllowedGraffitiObject:()=>g,maskGraffitiObject:()=>E,unpackObjectUrl:()=>y});module.exports=G(P);var n=class{};var l={type:"object",properties:{value:{type:"object"},channels:{type:"array",items:{type:"string"}},allowed:{type:"array",items:{type:"string"},nullable:!0},url:{type:"string"},actor:{type:"string"}},additionalProperties:!1,required:["value","channels","actor","url"]},j={...l,required:["value","channels"]};var c=class t extends Error{constructor(e){super(e),this.name="GraffitiErrorForbidden",Object.setPrototypeOf(this,t.prototype)}},f=class t extends Error{constructor(e){super(e),this.name="GraffitiErrorNotFound",Object.setPrototypeOf(this,t.prototype)}},a=class t extends Error{constructor(e){super(e),this.name="GraffitiErrorInvalidSchema",Object.setPrototypeOf(this,t.prototype)}},m=class t extends Error{constructor(e){super(e),this.name="GraffitiErrorSchemaMismatch",Object.setPrototypeOf(this,t.prototype)}},S=class t extends Error{constructor(e){super(e),this.name="GraffitiErrorTooLarge",Object.setPrototypeOf(this,t.prototype)}},p=class t extends Error{constructor(e){super(e),this.name="GraffitiErrorNotAcceptable",Object.setPrototypeOf(this,t.prototype)}};function y(t){return typeof t=="string"?t:t.url}function x(t,e){try{return t.compile(e)}catch(r){throw new a(r instanceof Error?r.message:void 0)}}function g(t,e){return!Array.isArray(t.allowed)||typeof e?.actor=="string"&&(t.actor===e.actor||t.allowed.includes(e.actor))}function E(t,e,r){t.actor!==r?.actor&&(t.allowed=t.allowed&&r?[r.actor]:void 0,t.channels=t.channels.filter(s=>e.includes(s)))}
|
|
2
2
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../src/index.ts", "../src/1-api.ts", "../src/2-types.ts", "../src/3-errors.ts"],
|
|
4
|
-
"sourcesContent": ["export * from \"./1-api\";\nexport * from \"./2-types\";\nexport * from \"./3-errors\";\nexport type { JSONSchema } from \"json-schema-to-ts\";\n", "import type {\n GraffitiObjectUrl,\n GraffitiObject,\n GraffitiObjectBase,\n GraffitiSession,\n GraffitiPostObject,\n GraffitiObjectStream,\n GraffitiObjectStreamContinue,\n} from \"./2-types\";\nimport type { JSONSchema } from \"json-schema-to-ts\";\n\n/**\n * This API describes a small but powerful set of methods that\n * can be used to create many different kinds of social applications,\n * from applications like Twitter, to Messenger, to Wikipedia, to many more new designs.\n * See the [Graffiti project website](https://graffiti.garden)\n * for links to example applications. Additionally, apps built on top\n * of the API interoperate with each other so you can seamlessly switch\n * between apps without losing your friends or data.\n *\n * These API methods should satisfy all of an application's needs for\n * the communication, storage, and access management of social data.\n * The rest of the application can be built with standard client-side\n * user interface tools to present and interact with that data\u2014no server code necessary!\n *\n * The Typescript code for this API is [open source on Github](https://github.com/graffiti-garden/api).\n *\n * There are several different implementations of this Graffiti API available,\n * including a [federated implementation](https://github.com/graffiti-garden/implementation-remote),\n * that lets people choose where their data is stored (you do not need to host your own server)\n * and a [local implementation](https://github.com/graffiti-garden/implementation-local)\n * that can be used for testing and development. Different implementations can\n * be swapped-in in the future without changing the API or any of the apps built on\n * top of it. In fact, we're working on an end-to-end encrypted version now!\n * [Follow Theia on BlueSky for updates](https://bsky.app/profile/theias.place).\n *\n * On the other side of the stack, there is [Vue plugin](https://vue.graffiti.garden/variables/GraffitiPlugin.html)\n * that wraps around this API to provide reactivity. Other plugin frameworks\n * and high-level libraries will be available in the future.\n *\n * ## API Overview\n *\n * The Graffiti API provides applications with methods for {@link login} and {@link logout},\n * methods to interact with data objects using standard database operations ({@link post}, {@link get}, and {@link delete}),\n * and a method to {@link discover} data objects created by others.\n * These data objects have a couple structured properties:\n * - {@link GraffitiObjectBase.url | `url`} (string): A globally unique identifier and locator for the object.\n * - {@link GraffitiObjectBase.actor | `actor`} (string): An unforgeable identifier for the creator of the object.\n * - {@link GraffitiObjectBase.allowed | `allowed`} (string[] | undefined): An array of the actors who are allowed to access the object (undefined for public objects).\n * - {@link GraffitiObjectBase.channels | `channels`} (string[]): An array of the *contexts* in which the object should appear.\n *\n * All other data is stored in the object's unstructured {@link GraffitiObjectBase.value | `value`} property.\n * This data can be used to represent social artifacts (e.g. posts, profiles) and activities (e.g. likes, follows).\n * For example, a post might have the value:\n\n * ```js\n * {\n * title: \"My First Post\",\n * content: \"Hello, world!\",\n * published: 1630483200000\n * }\n * ```\n *\n * a profile might have the value:\n *\n * ```js\n * {\n * name: \"Theia Henderson\",\n * pronouns: \"she/her\",\n * describes: \"did:web:theias.place\" // Theia's actor ID\n * }\n * ```\n *\n * and a \"Like\" might have the value:\n *\n * ```js\n * {\n * activity: \"Like\",\n * target: \"graffiti:remote:pod.graffiti.garden/12345\" // The URL of the graffiti object being liked\n * }\n * ```\n *\n * New social artifacts and activities can be easily created, simply\n * by creating new objects with appropriate properties. Despite the lack of\n * structure, we expect Graffiti object properties to adhere to a \"[folksonomy](https://en.wikipedia.org/wiki/Folksonomy)\",\n * similar to hashtags. Any string can be used as a hashtag on Twitter,\n * but there is social value in using the same hashtags at other people and\n * so a structure naturally emerges. Similarly, Graffiti objects\n * can have arbitrary properties but if people use the same properties as each other,\n * their apps will interoperate, which has social value.\n *\n * For a more complete and detailed overview of Graffiti's design, please\n * refer to [this section of the Graffiti paper](https://dl.acm.org/doi/10.1145/3746059.3747627#sec-3),\n * published in ACM UIST 2025. The paper also overviews {@link GraffitiObjectBase.channels | `channels`},\n * which are Graffiti's means of organizing data contextually, and a concept called \"total reification\",\n * which handles explains how moderation, collaboration, and other interactions are managed.\n *\n * @groupDescription 1 - Single-Object Methods\n * Methods for {@link post | creating}, {@link get | reading},\n * and {@link delete | deleting} {@link GraffitiObjectBase | Graffiti objects}.\n * @groupDescription 2 - Multi-Object Methods\n * Methods that retrieve or accumulate information about multiple {@link GraffitiObjectBase | Graffiti objects} at a time.\n * @groupDescription 3 - Media Methods\n * Methods for {@link postMedia | creating}, {@link getMedia | reading},\n * and {@link deleteMedia | deleting} media data.\n * @groupDescription 4 - Session Management\n * Methods and properties for logging in and out.\n */\nexport abstract class Graffiti {\n /**\n * Creates a new {@link GraffitiObjectBase | object}.\n *\n * @returns Returns the object that has been posted, complete with its\n * assigned {@link GraffitiObjectBase.url | `url`} and\n * {@link GraffitiObjectBase.actor | `actor`}.\n *\n * @group 1 - Single-Object Methods\n */\n abstract post<Schema extends JSONSchema>(\n /**\n * An object to post. This object is statically type-checked against the [JSON schema](https://json-schema.org/) that can be optionally provided\n * as the generic type parameter. It is recommended to a schema to\n * ensure that the posted object matches subsequent {@link get} or {@link discover}\n * methods.\n */\n object: GraffitiPostObject<Schema>,\n /**\n * An implementation-specific object with information to authenticate the\n * {@link GraffitiObjectBase.actor | `actor`}.\n */\n session: GraffitiSession,\n ): Promise<GraffitiObject<Schema>>;\n\n /**\n * Retrieves an object from a given {@link GraffitiObjectBase.url | `url`} matching\n * the provided `schema`.\n *\n * If the retreiving {@link GraffitiObjectBase.actor | `actor`} is not\n * the object's `actor`,\n * the object's {@link GraffitiObjectBase.allowed | `allowed`} and\n * {@link GraffitiObjectBase.channels | `channels`} properties are\n * not revealed, similar to a BCC email.\n *\n * @returns Returns the retrieved object.\n *\n * @throws {@link GraffitiErrorNotFound} if the object does not exist, has been deleted, or the actor is not\n * {@link GraffitiObjectBase.allowed | `allowed`} to access it.\n *\n * @throws {@link GraffitiErrorSchemaMismatch} if the retrieved object does not match the provided schema.\n *\n * @group 1 - Single-Object Methods\n */\n abstract get<Schema extends JSONSchema>(\n /**\n * The location of the object to get.\n */\n url: string | GraffitiObjectUrl,\n /**\n * The JSON schema to validate the retrieved object against.\n */\n schema: Schema,\n /**\n * An implementation-specific object with information to authenticate the\n * {@link GraffitiObjectBase.actor | `actor`}. If no `session` is provided,\n * the retrieved object's {@link GraffitiObjectBase.allowed | `allowed`}\n * property must be `undefined`.\n */\n session?: GraffitiSession | null,\n ): Promise<GraffitiObject<Schema>>;\n\n /**\n * Deletes an object from a given {@link GraffitiObjectBase.url | `url`}\n * that had previously been {@link post | `post`ed}.\n * The deleting {@link GraffitiObjectBase.actor | `actor`} must be the same as the\n * `actor` that created the object.\n *\n * @throws {@link GraffitiErrorNotFound} if the object does not exist, has already been deleted,\n * or the actor is not {@link GraffitiObjectBase.allowed | `allowed`} to access it.\n *\n * @throws {@link GraffitiErrorForbidden} if the {@link GraffitiObjectBase.actor | `actor`}\n * is not the same `actor` as the one who created the object.\n *\n * @group 1 - Single-Object Methods\n */\n abstract delete(\n /**\n * The location of the object to delete.\n */\n url: string | GraffitiObjectUrl,\n /**\n * An implementation-specific object with information to authenticate the\n * {@link GraffitiObjectBase.actor | `actor`}.\n */\n session: GraffitiSession,\n ): Promise<void>;\n\n /**\n * Discovers objects created by any actor that are contained\n * in at least one of the given {@link GraffitiObjectBase.channels | `channels`}\n * and match the given [JSON Schema](https://json-schema.org).\n *\n * Objects are returned asynchronously as they are discovered but the stream\n * will end once all leads have been exhausted.\n * The {@link GraffitiObjectStream} ends by returning a\n * {@link GraffitiObjectStreamReturn.continue | `continue`} method and a\n * {@link GraffitiObjectStreamReturn.cursor | `cursor`} string,\n * each of which can be be used to poll for new objects.\n * The `continue` method preserves the type safety of the stream and the `cursor`\n * string can be serialized to continue the stream after an application is closed\n * and reopened.\n *\n * `discover` will not return objects that the querying {@link GraffitiObjectBase.actor | `actor`}\n * is not {@link GraffitiObjectBase.allowed | `allowed`} to access.\n * If the `actor` is not the creator of a discovered object,\n * the allowed list will be masked to only contain the querying actor if the\n * allowed list is not `undefined` (public). Additionally, if the actor is not the\n * creator of a discovered object, any {@link GraffitiObjectBase.channels | `channels`}\n * not specified by the `discover` method will not be revealed. This masking happens\n * before the object is validated against the supplied `schema`.\n *\n * Since different implementations may fetch data from multiple sources there is\n * no guarentee on the order that objects are returned in.\n *\n * @returns Returns a stream of objects that match the given {@link GraffitiObjectBase.channels | `channels`}\n * and [JSON Schema](https://json-schema.org).\n *\n * @group 2 - Multi-Object Methods\n */\n abstract discover<Schema extends JSONSchema>(\n /**\n * The {@link GraffitiObjectBase.channels | `channels`} that objects must be associated with.\n */\n channels: string[],\n /**\n * A [JSON Schema](https://json-schema.org) that objects must satisfy.\n */\n schema: Schema,\n /**\n * An implementation-specific object with information to authenticate the\n * {@link GraffitiObjectBase.actor | `actor`}. If no `session` is provided,\n * only objects that have no {@link GraffitiObjectBase.allowed | `allowed`}\n * property will be returned.\n */\n session?: GraffitiSession | null,\n ): GraffitiObjectStream<Schema>;\n\n /**\n * Continues a {@link GraffitiObjectStream} from a given\n * {@link GraffitiObjectStreamReturn.cursor | `cursor`} string.\n * The continuation will return new objects that have been {@link post | `post`ed}\n * that match the original stream, and also returns the\n * {@link GraffitiObjectBase.url | `url`}s of objects that\n * have been {@link delete | `delete`d}, as marked by a `tombstone`.\n *\n * The `cursor` allows the client to\n * serialize the state of the stream and continue it later.\n * However this method loses any typing information that was\n * present in the original stream. For better type safety\n * and when serializing is not necessary, use the\n * {@link GraffitiObjectStreamReturn.continue | `continue`} method\n * instead, which is returned along with the `cursor` at the\n * end of the original stream.\n *\n * @throws {@link GraffitiErrorForbidden} if the {@link GraffitiObjectBase.actor | `actor`}\n * provided in the `session` is not the same as the `actor`\n * that initiated the original stream.\n *\n * @group 2 - Multi-Object Methods\n */\n abstract continueDiscover(\n cursor: string,\n session?: GraffitiSession | null,\n ): GraffitiObjectStreamContinue<{}>;\n\n /**\n * Uploads media data, such as an image or video.\n *\n * Unlike structured {@link GraffitiObjectBase | objects},\n * media is not indexed for {@link discover | `discover`y} and\n * must be retrieved by its exact URL using {@link getMedia}\n *\n * @returns The URL that the media was posted to.\n *\n * @group 3 - Media Methods\n */\n abstract postMedia(\n /**\n * The binary data of the media to be uploaded,\n * along with its [media type](https://www.iana.org/assignments/media-types/media-types.xhtml),\n * formatted as a [Blob](https://developer.mozilla.org/en-US/docs/Web/API/Blob).\n */\n media: Blob,\n /**\n * An implementation-specific object with information to authenticate the\n * {@link GraffitiObjectBase.actor | `actor`}.\n */\n session: GraffitiSession,\n ): Promise<string>;\n\n /**\n * Deletes media previously {@link postMedia | `post`ed} to a given URL.\n *\n * @throws {@link GraffitiErrorNotFound} if no media at that URL exists.\n *\n * @throws {@link GraffitiErrorForbidden} if the {@link GraffitiObjectBase.actor | `actor`}\n * provided in the `session` is not the same as the `actor` that {@link postMedia | `post`ed}\n * the media.\n *\n * @group 3 - Media Methods\n */\n abstract deleteMedia(\n /**\n * A globally unique identifier and locator for the media.\n */\n mediaUrl: string,\n /**\n * An implementation-specific object with information to authenticate the\n * {@link GraffitiObjectBase.actor | `actor`}.\n */\n session: GraffitiSession,\n ): Promise<void>;\n\n /**\n * Retrieves media from the given media URL, adhering to the given requirements.\n *\n * @throws {@link GraffitiErrorNotFound} if no media at that URL exists.\n *\n * @throws {@link GraffitiErrorTooLarge} if the media exceeds the given `maxBytes`.\n *\n * @throws {@link GraffitiErrorNotAcceptable} if the media does not match the given\n * `accept` specification.\n *\n * @returns The URL of the retrieved media, as a [Blob](https://developer.mozilla.org/en-US/docs/Web/API/Blob)\n * and the {@link GraffitiObjectBase.actor | `actor`} that posted it.\n *\n * @group 3 - Media Methods\n */\n abstract getMedia(\n /**\n * A globally unique identifier and locator for the media.\n */\n mediaUrl: string,\n /**\n * An optional set of requirements the retrieved media must meet.\n */\n requirements?: {\n /**\n * A list of acceptable media types for the retrieved media,\n * formatted as like an [HTTP Accept header](https://httpwg.org/specs/rfc9110.html#field.accept)\n */\n accept?: string;\n /**\n * The maximum size, in bytes, of the media.\n */\n maxBytes?: number;\n },\n ): Promise<{\n media: Blob;\n actor: string;\n }>;\n\n /**\n * Begins the login process. Depending on the implementation, this may\n * involve redirecting to a login page or opening a popup,\n * so it should always be called in response to a gesture, such as clicking\n * a button, due to the [feature-gating browser security feature](https://developer.mozilla.org/en-US/docs/Web/Security/User_activation).\n *\n * The {@link GraffitiSession | session} object is returned\n * asynchronously via {@link Graffiti.sessionEvents | sessionEvents}\n * as a {@link GraffitiLoginEvent} with event type `login`.\n *\n * @group 4 - Session Management\n */\n abstract login(\n /**\n * A suggested actor to login as. For example, if a user tries to\n * edit a post but are not logged in, the interface can infer that\n * they might want to log in as the actor who created the post\n * they are attempting to edit.\n *\n * Even if provided, the implementation should allow the user\n * to log in as a different actor if they choose.\n */\n actor?: string,\n ): Promise<void>;\n\n /**\n * Begins the logout process for a particular {@link GraffitiSession | session}. Depending on the implementation, this may\n * involve redirecting the user to a logout page or opening a popup,\n * so it should always be called in response to a gesture, such as clicking\n * a button, due to the [feature-gating browser security feature](https://developer.mozilla.org/en-US/docs/Web/Security/User_activation).\n *\n * A confirmation will be returned asynchronously via\n * {@link Graffiti.sessionEvents | sessionEvents}\n * as a {@link GraffitiLogoutEvent} as event type `logout`.\n *\n * @group 4 - Session Management\n */\n abstract logout(\n /**\n * The {@link GraffitiSession | session} object to logout.\n */\n session: GraffitiSession,\n ): Promise<void>;\n\n /**\n * An event target that can be used to listen for the following\n * events and their corresponding event types:\n * - `login` - {@link GraffitiLoginEvent}\n * - `logout` - {@link GraffitiLogoutEvent}\n * - `initialized` - {@link GraffitiSessionInitializedEvent}\n *\n * @group 4 - Session Management\n */\n abstract readonly sessionEvents: EventTarget;\n}\n", "import type { JSONSchema, FromSchema } from \"json-schema-to-ts\";\n\n/**\n * Objects are the atomic unit in Graffiti that can represent both data (*e.g.* a social media post or profile)\n * and activities (*e.g.* a like or follow).\n *\n * Each object embeds the {@link actor | `actor`} that created it.\n * Object content and metadata are static but an object may be deleted by\n * its creating {@link actor | `actor`}.\n *\n * An object's content is stored in its {@link value | `value`} property, which can be any JSON\n * object. However, it is recommended to use properties from the\n * [Activity Vocabulary](https://www.w3.org/TR/activitystreams-vocabulary/)\n * or properties that emerge in the Graffiti [folksonomy](https://en.wikipedia.org/wiki/Folksonomy)\n * to promote interoperability.\n *\n * Each object is globally addressable via its {@link url | `url`}.\n *\n * An object's {@link channels | `channels`} and {@link allowed | `allowed`} properties\n * are set by an objects creator to shape the visibility of and access to their object.\n */\nexport interface GraffitiObjectBase {\n /**\n * The object's content as freeform JSON. We recommend using properties from the\n * [Activity Vocabulary](https://www.w3.org/TR/activitystreams-vocabulary/)\n * or properties that emerge in the Graffiti [folksonomy](https://en.wikipedia.org/wiki/Folksonomy)\n * to promote interoperability.\n */\n value: {};\n\n /**\n * An array of URIs the creator associates with the object. Objects can only be found by querying\n * one of the object's channels using the\n * {@link Graffiti.discover} method. This allows creators to express the intended audience of their object\n * which helps to prevent [context collapse](https://en.wikipedia.org/wiki/Context_collapse) even\n * in the highly interoperable ecosystem that Graffiti envisions. For example, channel URIs may be:\n * - A actor's own {@link actor | `actor`} URI. Posting an object to this channel is a way to broadcast\n * the object to the actor's followers, like posting a tweet.\n * - The URL of a Graffiti post. Posting an object to this channel is a way to broadcast to anyone viewing\n * the post, like commenting on a tweet.\n * - A URI representing a topic. Posting an object to this channel is a way to broadcast to anyone interested\n * in that topic, like posting in a subreddit.\n */\n channels: string[];\n\n /**\n * An optional array of {@link actor | `actor`} URIs that the creator allows to access the object.\n * If no `allowed` array is provided, the object can be accessed by anyone (so long as they\n * also know the right {@link channels | `channel` } to look in). An object can always be accessed by its creator, even if\n * the `allowed` array is empty.\n *\n * The `allowed` array is not revealed to actors other than the creator, like\n * a BCC email. An actor may choose to add a `to` property to the object's {@link value | `value`} to indicate\n * other recipients, however this is not enforced by Graffiti and may not accurately reflect the actual `allowed` array.\n *\n * `allowed` can be combined with {@link channels | `channels`}. For example, to send someone a direct message\n * the sender should post their object to the channel of the recipient's {@link actor | `actor`} URI to notify them of the message and also add\n * the recipient's {@link actor | `actor`} URI to the `allowed` array to prevent others from seeing the message.\n */\n allowed?: string[] | null;\n\n /**\n * The URI of the `actor` that {@link Graffiti.post | created } the object.\n * This `actor` has the unique permission to\n * {@link Graffiti.delete | delete} the object.\n *\n * We borrow the term actor from the ActivityPub because\n * [like in ActivityPub](https://www.w3.org/TR/activitypub/#h-note-0)\n * there is not necessarily a one-to-one mapping between actors and people/users.\n * Multiple people can share the same actor or one person can have multiple actors.\n * Actors can also be bots.\n *\n * In Graffiti, actors are always globally unique URIs which\n * allows them to also function as {@link channels | `channels`}.\n */\n actor: string;\n\n /**\n * A globally unique identifier and locator for the object. It can be used to point to\n * an object or to retrieve the object directly with {@link Graffiti.get}.\n *\n * An object's URL is generated when the object is first created and\n * should include sufficient randomness to prevent collisions\n * and guessing. The URL starts with a \"scheme,\" just like web URLs start with `http` or `https`, to indicate\n * to indicate the particular Graffiti implementation. This allows for applications\n * to pull from multiple coexisting Graffiti implementations without collision.\n * Existing schemes include `graffiti:local:` for objects stored locally\n * (see the [local implementation](https://github.com/graffiti-garden/implementation-local))\n * and `graffiti:remote:` for objects stored on Graffiti-specific web servers (see the [remote implementation](https://github.com/graffiti-garden/implementation-remote))\n * Options available in the future might include `graffiti:solid:` for objects stored on Solid servers\n * or `graffiti:p2p:` for objects stored on a peer-to-peer network.\n */\n url: string;\n}\n\n/**\n * This type constrains the {@link GraffitiObjectBase} type to adhere to a\n * particular [JSON schema](https://json-schema.org/).\n * This allows for static type-checking of an object's {@link GraffitiObjectBase.value | `value`}\n * which is otherwise a freeform JSON object.\n *\n * Schema-aware objects are returned by {@link Graffiti.get} and {@link Graffiti.discover}.\n */\nexport type GraffitiObject<Schema extends JSONSchema> = GraffitiObjectBase &\n FromSchema<Schema & typeof GraffitiObjectJSONSchema>;\n\n/**\n * A JSON Schema equivalent to the {@link GraffitiObjectBase} type.\n * Needed internally for type inference of JSON Schemas, but can\n * be used by implementations to validate objects.\n */\nexport const GraffitiObjectJSONSchema = {\n type: \"object\",\n properties: {\n value: { type: \"object\" },\n channels: { type: \"array\", items: { type: \"string\" } },\n allowed: { type: \"array\", items: { type: \"string\" }, nullable: true },\n url: { type: \"string\" },\n actor: { type: \"string\" },\n },\n additionalProperties: false,\n required: [\"value\", \"channels\", \"actor\", \"url\"],\n} as const satisfies JSONSchema;\n\n/**\n * This is an object containing only the {@link GraffitiObjectBase.url | `url`}\n * property of a {@link GraffitiObjectBase | GraffitiObject}.\n * It is used as a utility type so that applications can call\n * {@link Graffiti.delete} directly on an object\n * rather than on `object.url`.\n */\nexport type GraffitiObjectUrl = Pick<GraffitiObjectBase, \"url\">;\n\n/**\n * This object is a subset of {@link GraffitiObjectBase} that must be constructed locally before calling {@link Graffiti.post}.\n * This local copy ignores system-generated properties\n * ({@link GraffitiObjectBase.url | `url`} and {@link GraffitiObjectBase.actor | `actor`}),\n * and may be statically typed with\n * a [JSON schema](https://json-schema.org/) to prevent the accidental creation of erroneous objects.\n *\n * This local object must have a {@link GraffitiObjectBase.value | `value`} and {@link GraffitiObjectBase.channels | `channels`}\n * and may optionally have an {@link GraffitiObjectBase.allowed | `allowed`} property.\n */\nexport type GraffitiPostObject<Schema extends JSONSchema> = Pick<\n GraffitiObjectBase,\n \"value\" | \"channels\" | \"allowed\"\n> &\n Partial<GraffitiObjectBase> &\n FromSchema<Schema & typeof GraffitiPostObjectJSONSchema>;\n\n/**\n * A JSON Schema equivalent to the {@link GraffitiPostObject} type.\n * Needed internally for type inference of JSON Schemas, but can\n * be used by implementations to validate objects.\n */\nexport const GraffitiPostObjectJSONSchema = {\n ...GraffitiObjectJSONSchema,\n required: [\"value\", \"channels\"],\n} as const satisfies JSONSchema;\n\n/**\n * This object contains information that the underlying implementation can\n * use to authenticate a particular {@link GraffitiObjectBase.actor | `actor`}.\n * This object is required of all {@link Graffiti} methods\n * that modify objects or media and is optional for methods that read objects.\n *\n * At a minimum the `session` object must contain the\n * {@link GraffitiSession.actor | `actor`} URI to authenticate with.\n * However it is likely that the `session` object must contain other\n * implementation-specific properties.\n * For example, a Solid implementation might include a\n * [`fetch`](https://docs.inrupt.com/developer-tools/api/javascript/solid-client-authn-browser/functions.html#fetch)\n * function. A distributed implementation may include\n * a cryptographic signature.\n *\n * As to why the `session` object is passed as an argument to every method\n * rather than being an internal property of the {@link Graffiti} instance,\n * this is primarily for type-checking to catch bugs related to login state.\n * Graffiti applications can expose some functionality to people who are not logged in\n * with {@link Graffiti.get} and {@link Graffiti.discover} but without type-checking\n * the `session` it can be easy to forget to hide buttons that trigger\n * other methods that require login.\n *\n * Passing the `session` object per-method also allows for multiple sessions\n * to be used within the same application, like an Email client fetching from\n * multiple accounts.\n */\nexport interface GraffitiSession {\n /**\n * The {@link GraffitiObjectBase.actor | `actor`} to authenticate with.\n */\n actor: string;\n}\n\n/**\n * A stream of data that are returned by {@link Graffiti.discover}.\n *\n * Errors are returned within the stream rather than as\n * exceptions that would halt the entire stream. This is because\n * some implementations may pull data from multiple sources\n * including some that may be unreliable. In many cases,\n * these errors can be safely ignored.\n * See {@link GraffitiObjectStreamError}.\n *\n * The stream is an [`AsyncGenerator`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function)\n * that can be iterated over using `for await` loops or calling `next` on the generator.\n * The stream can be terminated by breaking out of a loop calling `return` on the generator.\n *\n * The stream ends by returning a {@link GraffitiObjectStreamReturn.continue | `continue`}\n * function and a {@link GraffitiObjectStreamReturn.cursor | `cursor`} string,\n * each of which can be used to resume the stream from where it left off.\n */\nexport type GraffitiObjectStream<Schema extends JSONSchema> = AsyncGenerator<\n GraffitiObjectStreamError | GraffitiObjectStreamEntry<Schema>,\n GraffitiObjectStreamReturn<Schema>\n>;\n\n/**\n * An error that can occur in a {@link GraffitiObjectStream}.\n *\n * @internal\n */\nexport interface GraffitiObjectStreamError {\n /**\n * The error that occurred while streaming data.\n */\n error: Error;\n /**\n * The origin that the error occurred. It will include\n * the scheme of the Graffiti implementation used and other\n * implementation-specific information like a hostname.\n */\n origin: string;\n}\n\n/**\n * A successful result from a {@link GraffitiObjectStream} or\n * {@link GraffitiObjectStreamContinue} that includes an object.\n *\n * @internal\n */\nexport interface GraffitiObjectStreamEntry<Schema extends JSONSchema> {\n /**\n * Empty property for compatibility with {@link GraffitiObjectStreamError}\n */\n error?: undefined;\n /**\n * Empty property for compatibility with {@link GraffitiObjectStreamContinueTombstone}\n */\n tombstone?: undefined;\n /**\n * The object returned by the stream.\n */\n object: GraffitiObject<Schema>;\n}\n\n/**\n * A result from a {@link GraffitiObjectStreamContinue} that indicated\n * an object has been deleted since the original stream was run.\n * Only sparse metadata about the deleted object is returned to respect\n * the deleting actor's privacy.\n *\n * @internal\n */\nexport interface GraffitiObjectStreamContinueTombstone {\n /**\n * Empty property for compatibility with {@link GraffitiObjectStreamError}\n */\n error?: undefined;\n /**\n * Use this property to differentiate a tombstone from a\n * {@link GraffitiObjectStreamEntry}.\n */\n tombstone: true;\n /**\n * Sparse metadata about the deleted object. The full object is not returned\n * to respect an actor's privacy.\n */\n object: {\n /**\n * The {@link GraffitiObjectBase.url | `url`} of the deleted object.\n */\n url: string;\n };\n}\n\n/**\n * A continuation of the {@link GraffitiObjectStream} type can include\n * both objects and tombstones of deleted objects.\n *\n * @internal\n */\nexport type GraffitiObjectStreamContinueEntry<Schema extends JSONSchema> =\n | GraffitiObjectStreamEntry<Schema>\n | GraffitiObjectStreamContinueTombstone;\n\n/**\n * The output of a {@link GraffitiObjectStream} or a {@link GraffitiObjectStreamContinue}\n * that allows the stream to be continued from where it left off.\n *\n * The {@link continue} function preserves the typing of the original stream,\n * where as the {@link cursor} string can be serialized for use after a person\n * has closed and reopened an application.\n *\n * The continued stream may include `tombstone`s of objects that have been\n * deleted since the original stream was run. See {@link GraffitiObjectStreamContinueTombstone}.\n * The continued stream may also return some objects that were already\n * returned by the original stream, depending on how much state the\n * underlying implementation is able to preserve.\n *\n * @internal\n */\nexport interface GraffitiObjectStreamReturn<Schema extends JSONSchema> {\n /**\n * @returns A function that creates new stream that continues from where the original stream left off.\n * It preserves the typing of the original stream.\n */\n continue: () => GraffitiObjectStreamContinue<Schema>;\n /**\n * A string that can be serialized and stored to resume the stream later.\n * It must be passed to the {@link Graffiti.continueDiscover} method\n * to resume the stream.\n */\n cursor: string;\n}\n\n/**\n * A continutation of the {@link GraffitiObjectStream} type, as returned by\n * the {@link GraffitiObjectStreamReturn.continue} or by using\n * {@link GraffitiObjectStreamReturn.cursor} with {@link Graffiti.continueDiscover}.\n *\n * The continued stream may include `tombstone`s of objects that have been\n * deleted since the original stream was run. See {@link GraffitiObjectStreamContinueTombstone}.\n *\n * @internal\n */\nexport type GraffitiObjectStreamContinue<Schema extends JSONSchema> =\n AsyncGenerator<\n GraffitiObjectStreamError | GraffitiObjectStreamContinueEntry<Schema>,\n GraffitiObjectStreamReturn<Schema>\n >;\n\n/**\n * The event type produced in {@link Graffiti.sessionEvents}\n * when a actor logs in manually from {@link Graffiti.login}\n * or when their session is restored from a previous login.\n * The event name to listen for is `login`.\n */\nexport type GraffitiLoginEvent = CustomEvent<\n | {\n error: Error;\n session?: undefined;\n }\n | {\n error?: undefined;\n session: GraffitiSession;\n }\n>;\n\n/**\n * The event type produced in {@link Graffiti.sessionEvents}\n * when a actor logs out either manually with {@link Graffiti.logout}\n * or when their session times out or otherwise becomes invalid.\n * The event name to listen for is `logout`.\n */\nexport type GraffitiLogoutEvent = CustomEvent<\n | {\n error: Error;\n actor?: string;\n }\n | {\n error?: undefined;\n actor: string;\n }\n>;\n\n/**\n * The event type produced in {@link Graffiti.sessionEvents}\n * after an application has attempted to complete any login redirects\n * and restore any previously active sessions.\n * Successful session restores will be returned in parallel as\n * their own {@link GraffitiLoginEvent} events.\n *\n * This event optionally returns an `href` property\n * representing the URL that originated a login request,\n * which may be useful for redirecting the user back to\n * the page they were on after login.\n * The event name to listen for is `initialized`.\n */\nexport type GraffitiSessionInitializedEvent = CustomEvent<\n | {\n error?: Error;\n href?: string;\n }\n | null\n | undefined\n>;\n", "export class GraffitiErrorUnauthorized extends Error {\n constructor(message?: string) {\n super(message);\n this.name = \"GraffitiErrorUnauthorized\";\n Object.setPrototypeOf(this, GraffitiErrorUnauthorized.prototype);\n }\n}\n\nexport class GraffitiErrorForbidden extends Error {\n constructor(message?: string) {\n super(message);\n this.name = \"GraffitiErrorForbidden\";\n Object.setPrototypeOf(this, GraffitiErrorForbidden.prototype);\n }\n}\n\nexport class GraffitiErrorNotFound extends Error {\n constructor(message?: string) {\n super(message);\n this.name = \"GraffitiErrorNotFound\";\n Object.setPrototypeOf(this, GraffitiErrorNotFound.prototype);\n }\n}\n\nexport class GraffitiErrorInvalidSchema extends Error {\n constructor(message?: string) {\n super(message);\n this.name = \"GraffitiErrorInvalidSchema\";\n Object.setPrototypeOf(this, GraffitiErrorInvalidSchema.prototype);\n }\n}\n\nexport class GraffitiErrorSchemaMismatch extends Error {\n constructor(message?: string) {\n super(message);\n this.name = \"GraffitiErrorSchemaMismatch\";\n Object.setPrototypeOf(this, GraffitiErrorSchemaMismatch.prototype);\n }\n}\n\nexport class GraffitiErrorTooLarge extends Error {\n constructor(message?: string) {\n super(message);\n this.name = \"GraffitiErrorTooLarge\";\n Object.setPrototypeOf(this, GraffitiErrorTooLarge.prototype);\n }\n}\n\nexport class GraffitiErrorNotAcceptable extends Error {\n constructor(message?: string) {\n super(message);\n this.name = \"GraffitiErrorNotAcceptable\";\n Object.setPrototypeOf(this, GraffitiErrorNotAcceptable.prototype);\n }\n}\n\nexport class GraffitiErrorInvalidUrl extends Error {\n constructor(message?: string) {\n super(message);\n this.name = \"GraffitiErrorInvalidUrl\";\n Object.setPrototypeOf(this, GraffitiErrorInvalidUrl.prototype);\n }\n}\n\nexport class GraffitiErrorUnrecognizedUrlScheme extends Error {\n constructor(message?: string) {\n super(message);\n this.name = \"GraffitiErrorUnrecognizedUriScheme\";\n Object.setPrototypeOf(this, GraffitiErrorUnrecognizedUrlScheme.prototype);\n }\n}\n"],
|
|
5
|
-
"mappings": "yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,cAAAE,EAAA,2BAAAC,EAAA,+BAAAC,EAAA
|
|
6
|
-
"names": ["index_exports", "__export", "Graffiti", "GraffitiErrorForbidden", "GraffitiErrorInvalidSchema", "
|
|
3
|
+
"sources": ["../src/index.ts", "../src/1-api.ts", "../src/2-types.ts", "../src/3-errors.ts", "../src/4-utilities.ts"],
|
|
4
|
+
"sourcesContent": ["export * from \"./1-api\";\nexport * from \"./2-types\";\nexport * from \"./3-errors\";\nexport * from \"./4-utilities\";\nexport type { JSONSchema } from \"json-schema-to-ts\";\n", "import type {\n GraffitiObjectUrl,\n GraffitiObject,\n GraffitiObjectBase,\n GraffitiSession,\n GraffitiPostObject,\n GraffitiObjectStream,\n GraffitiObjectStreamContinue,\n} from \"./2-types\";\nimport type { JSONSchema } from \"json-schema-to-ts\";\n\n/**\n * This API describes a small but powerful set of methods that\n * can be used to create many different kinds of social applications,\n * from applications like Twitter, to Messenger, to Wikipedia, to many more new designs.\n * See the [Graffiti project website](https://graffiti.garden)\n * for links to example applications. Additionally, apps built on top\n * of the API interoperate with each other so you can seamlessly switch\n * between apps without losing your friends or data.\n *\n * These API methods should satisfy all of an application's needs for\n * the communication, storage, and access management of social data.\n * The rest of the application can be built with standard client-side\n * user interface tools to present and interact with that data\u2014no server code necessary!\n *\n * The Typescript code for this API is [open source on Github](https://github.com/graffiti-garden/api).\n *\n * There are several different implementations of this Graffiti API available,\n * including a [federated implementation](https://github.com/graffiti-garden/implementation-remote),\n * that lets people choose where their data is stored (you do not need to host your own server)\n * and a [local implementation](https://github.com/graffiti-garden/implementation-local)\n * that can be used for testing and development. Different implementations can\n * be swapped-in in the future without changing the API or any of the apps built on\n * top of it. In fact, we're working on an end-to-end encrypted version now!\n * [Follow Theia on BlueSky for updates](https://bsky.app/profile/theias.place).\n *\n * On the other side of the stack, there is [Vue plugin](https://vue.graffiti.garden/variables/GraffitiPlugin.html)\n * that wraps around this API to provide reactivity. Other plugin frameworks\n * and high-level libraries will be available in the future.\n *\n * ## API Overview\n *\n * The Graffiti API provides applications with methods for {@link login} and {@link logout},\n * methods to interact with data objects using standard database operations ({@link post}, {@link get}, and {@link delete}),\n * and a method to {@link discover} data objects created by others.\n * These data objects have a couple structured properties:\n * - {@link GraffitiObjectBase.url | `url`} (string): A globally unique identifier and locator for the object.\n * - {@link GraffitiObjectBase.actor | `actor`} (string): An unforgeable identifier for the creator of the object.\n * - {@link GraffitiObjectBase.allowed | `allowed`} (string[] | undefined): An array of the actors who are allowed to access the object (undefined for public objects).\n * - {@link GraffitiObjectBase.channels | `channels`} (string[]): An array of the *contexts* in which the object should appear.\n *\n * All other data is stored in the object's unstructured {@link GraffitiObjectBase.value | `value`} property.\n * This data can be used to represent social artifacts (e.g. posts, profiles) and activities (e.g. likes, follows).\n * For example, a post might have the value:\n\n * ```js\n * {\n * title: \"My First Post\",\n * content: \"Hello, world!\",\n * published: 1630483200000\n * }\n * ```\n *\n * a profile might have the value:\n *\n * ```js\n * {\n * name: \"Theia Henderson\",\n * pronouns: \"she/her\",\n * describes: \"did:web:theias.place\" // Theia's actor ID\n * }\n * ```\n *\n * and a \"Like\" might have the value:\n *\n * ```js\n * {\n * activity: \"Like\",\n * target: \"graffiti:remote:pod.graffiti.garden/12345\" // The URL of the graffiti object being liked\n * }\n * ```\n *\n * New social artifacts and activities can be easily created, simply\n * by creating new objects with appropriate properties. Despite the lack of\n * structure, we expect Graffiti object properties to adhere to a \"[folksonomy](https://en.wikipedia.org/wiki/Folksonomy)\",\n * similar to hashtags. Any string can be used as a hashtag on Twitter,\n * but there is social value in using the same hashtags at other people and\n * so a structure naturally emerges. Similarly, Graffiti objects\n * can have arbitrary properties but if people use the same properties as each other,\n * their apps will interoperate, which has social value.\n *\n * For a more complete and detailed overview of Graffiti's design, please\n * refer to [this section of the Graffiti paper](https://dl.acm.org/doi/10.1145/3746059.3747627#sec-3),\n * published in ACM UIST 2025. The paper also overviews {@link GraffitiObjectBase.channels | `channels`},\n * which are Graffiti's means of organizing data contextually, and a concept called \"total reification\",\n * which handles explains how moderation, collaboration, and other interactions are managed.\n *\n * @groupDescription 1 - Single-Object Methods\n * Methods for {@link post | creating}, {@link get | reading},\n * and {@link delete | deleting} {@link GraffitiObjectBase | Graffiti objects}.\n * @groupDescription 2 - Multi-Object Methods\n * Methods that retrieve or accumulate information about multiple {@link GraffitiObjectBase | Graffiti objects} at a time.\n * @groupDescription 3 - Media Methods\n * Methods for {@link postMedia | creating}, {@link getMedia | reading},\n * and {@link deleteMedia | deleting} media data.\n * @groupDescription 4 - Identity Methods\n * Methods and properties for logging in and out.\n */\nexport abstract class Graffiti {\n /**\n * Creates a new {@link GraffitiObjectBase | object}.\n *\n * @returns Returns the object that has been posted, complete with its\n * assigned {@link GraffitiObjectBase.url | `url`} and\n * {@link GraffitiObjectBase.actor | `actor`}.\n *\n * @group 1 - Single-Object Methods\n */\n abstract post<Schema extends JSONSchema>(\n /**\n * An object to post, minus its {@link GraffitiObjectBase.url | `url`} and\n * {@link GraffitiObjectBase.actor | `actor`}, which will be assigned once posted.\n * This object is statically type-checked against the [JSON schema](https://json-schema.org/) that can be optionally provided\n * as the generic type parameter. It is recommended to use a schema to\n * ensure that the posted object matches subsequent {@link get} or {@link discover}\n * methods.\n */\n partialObject: GraffitiPostObject<Schema>,\n /**\n * An implementation-specific object with information to authenticate the\n * {@link GraffitiObjectBase.actor | `actor`}.\n */\n session: GraffitiSession,\n ): Promise<GraffitiObject<Schema>>;\n\n /**\n * Retrieves an object from a given {@link GraffitiObjectBase.url | `url`} matching\n * the provided `schema`.\n *\n * If the retreiving {@link GraffitiObjectBase.actor | `actor`} is not\n * the object's `actor`,\n * the object's {@link GraffitiObjectBase.allowed | `allowed`} and\n * {@link GraffitiObjectBase.channels | `channels`} properties are\n * not revealed, similar to a BCC email.\n *\n * @returns Returns the retrieved object.\n *\n * @throws {@link GraffitiErrorNotFound} if the object does not exist, has been deleted, or the actor is not\n * {@link GraffitiObjectBase.allowed | `allowed`} to access it.\n *\n * @throws {@link GraffitiErrorSchemaMismatch} if the retrieved object does not match the provided schema.\n *\n * @group 1 - Single-Object Methods\n */\n abstract get<Schema extends JSONSchema>(\n /**\n * The location of the object to get.\n */\n url: string | GraffitiObjectUrl,\n /**\n * The JSON schema to validate the retrieved object against.\n */\n schema: Schema,\n /**\n * An implementation-specific object with information to authenticate the\n * {@link GraffitiObjectBase.actor | `actor`}. If no `session` is provided,\n * the retrieved object's {@link GraffitiObjectBase.allowed | `allowed`}\n * property must be `undefined`.\n */\n session?: GraffitiSession | null,\n ): Promise<GraffitiObject<Schema>>;\n\n /**\n * Deletes an object from a given {@link GraffitiObjectBase.url | `url`}\n * that had previously been {@link post | `post`ed}.\n * The deleting {@link GraffitiObjectBase.actor | `actor`} must be the same as the\n * `actor` that created the object.\n *\n * @throws {@link GraffitiErrorNotFound} if the object does not exist, has already been deleted,\n * or the actor is not {@link GraffitiObjectBase.allowed | `allowed`} to access it.\n *\n * @throws {@link GraffitiErrorForbidden} if the {@link GraffitiObjectBase.actor | `actor`}\n * is not the same `actor` as the one who created the object.\n *\n * @group 1 - Single-Object Methods\n */\n abstract delete(\n /**\n * The location of the object to delete.\n */\n url: string | GraffitiObjectUrl,\n /**\n * An implementation-specific object with information to authenticate the\n * {@link GraffitiObjectBase.actor | `actor`}.\n */\n session: GraffitiSession,\n ): Promise<void>;\n\n /**\n * Discovers objects created by any actor that are contained\n * in at least one of the given {@link GraffitiObjectBase.channels | `channels`}\n * and match the given [JSON Schema](https://json-schema.org).\n *\n * Objects are returned asynchronously as they are discovered but the stream\n * will end once all leads have been exhausted.\n * The {@link GraffitiObjectStream} ends by returning a\n * {@link GraffitiObjectStreamReturn.continue | `continue`} method and a\n * {@link GraffitiObjectStreamReturn.cursor | `cursor`} string,\n * each of which can be be used to poll for new objects.\n * The `continue` method preserves the type safety of the stream and the `cursor`\n * string can be serialized to continue the stream after an application is closed\n * and reopened.\n *\n * `discover` will not return objects that the querying {@link GraffitiObjectBase.actor | `actor`}\n * is not {@link GraffitiObjectBase.allowed | `allowed`} to access.\n * If the `actor` is not the creator of a discovered object,\n * the allowed list will be masked to only contain the querying actor if the\n * allowed list is not `undefined` (public). Additionally, if the actor is not the\n * creator of a discovered object, any {@link GraffitiObjectBase.channels | `channels`}\n * not specified by the `discover` method will not be revealed. This masking happens\n * before the object is validated against the supplied `schema`.\n *\n * Since different implementations may fetch data from multiple sources there is\n * no guarentee on the order that objects are returned in.\n *\n * @returns Returns a stream of objects that match the given {@link GraffitiObjectBase.channels | `channels`}\n * and [JSON Schema](https://json-schema.org).\n *\n * @group 2 - Multi-Object Methods\n */\n abstract discover<Schema extends JSONSchema>(\n /**\n * The {@link GraffitiObjectBase.channels | `channels`} that objects must be associated with.\n */\n channels: string[],\n /**\n * A [JSON Schema](https://json-schema.org) that objects must satisfy.\n */\n schema: Schema,\n /**\n * An implementation-specific object with information to authenticate the\n * {@link GraffitiObjectBase.actor | `actor`}. If no `session` is provided,\n * only objects that have no {@link GraffitiObjectBase.allowed | `allowed`}\n * property will be returned.\n */\n session?: GraffitiSession | null,\n ): GraffitiObjectStream<Schema>;\n\n /**\n * Continues a {@link GraffitiObjectStream} from a given\n * {@link GraffitiObjectStreamReturn.cursor | `cursor`} string.\n * The continuation will return new objects that have been {@link post | `post`ed}\n * that match the original stream, and also returns the\n * {@link GraffitiObjectBase.url | `url`}s of objects that\n * have been {@link delete | `delete`d}, as marked by a `tombstone`.\n *\n * The `cursor` allows the client to\n * serialize the state of the stream and continue it later.\n * However this method loses any typing information that was\n * present in the original stream. For better type safety\n * and when serializing is not necessary, use the\n * {@link GraffitiObjectStreamReturn.continue | `continue`} method\n * instead, which is returned along with the `cursor` at the\n * end of the original stream.\n *\n * @throws {@link GraffitiErrorForbidden} if the {@link GraffitiObjectBase.actor | `actor`}\n * provided in the `session` is not the same as the `actor`\n * that initiated the original stream.\n *\n * @group 2 - Multi-Object Methods\n */\n abstract continueDiscover(\n cursor: string,\n session?: GraffitiSession | null,\n ): GraffitiObjectStreamContinue<{}>;\n\n /**\n * Uploads media data, such as an image or video.\n *\n * Unlike structured {@link GraffitiObjectBase | objects},\n * media is not indexed for {@link discover | `discover`y} and\n * must be retrieved by its exact URL using {@link getMedia}\n *\n * @returns The URL that the media was posted to.\n *\n * @group 3 - Media Methods\n */\n abstract postMedia(\n media: {\n /**\n * The binary data of the media to be uploaded,\n * along with its [media type](https://www.iana.org/assignments/media-types/media-types.xhtml),\n * formatted as a [Blob](https://developer.mozilla.org/en-US/docs/Web/API/Blob).\n */\n data: Blob;\n /**\n * An optional list, identical in function to an object's\n * {@link GraffitiObjectBase.allowed | `allowed`} property,\n * that specifies the {@link GraffitiObjectBase.actor | `actor`}s\n * who are allowed to access the media. If the list is `undefined`\n * or `null`, anyone with the URL can access the media. If the list\n * is empty, only the {@link GraffitiObjectBase.actor | `actor`}\n * who {@link postMedia | `post`ed} the media can access it.\n */\n allowed?: string[] | null;\n },\n /**\n * An implementation-specific object with information to authenticate the\n * {@link GraffitiObjectBase.actor | `actor`}.\n */\n session: GraffitiSession,\n ): Promise<string>;\n\n /**\n * Deletes media previously {@link postMedia | `post`ed} to a given URL.\n *\n * @throws {@link GraffitiErrorNotFound} if no media at that URL exists.\n *\n * @throws {@link GraffitiErrorForbidden} if the {@link GraffitiObjectBase.actor | `actor`}\n * provided in the `session` is not the same as the `actor` that {@link postMedia | `post`ed}\n * the media.\n *\n * @group 3 - Media Methods\n */\n abstract deleteMedia(\n /**\n * A globally unique identifier and locator for the media.\n */\n mediaUrl: string,\n /**\n * An implementation-specific object with information to authenticate the\n * {@link GraffitiObjectBase.actor | `actor`}.\n */\n session: GraffitiSession,\n ): Promise<void>;\n\n /**\n * Retrieves media from the given media URL, adhering to the given requirements.\n *\n * @throws {@link GraffitiErrorNotFound} if no media at that URL exists.\n *\n * @throws {@link GraffitiErrorTooLarge} if the media exceeds the given `maxBytes`.\n *\n * @throws {@link GraffitiErrorNotAcceptable} if the media does not match the given\n * `accept` specification.\n *\n * @returns The URL of the retrieved media, as a [Blob](https://developer.mozilla.org/en-US/docs/Web/API/Blob)\n * and the {@link GraffitiObjectBase.actor | `actor`} that posted it.\n *\n * @group 3 - Media Methods\n */\n abstract getMedia(\n /**\n * A globally unique identifier and locator for the media.\n */\n mediaUrl: string,\n /**\n * A set of requirements the retrieved media must meet.\n */\n requirements: {\n /**\n * A list of acceptable media types for the retrieved media,\n * formatted as like an [HTTP Accept header](https://httpwg.org/specs/rfc9110.html#field.accept)\n */\n accept?: string;\n /**\n * The maximum acceptable size, in bytes, of the media.\n */\n maxBytes?: number;\n },\n /**\n * An implementation-specific object with information to authenticate the\n * {@link GraffitiObjectBase.actor | `actor`}.\n */\n session?: GraffitiSession | null,\n ): Promise<{\n data: Blob;\n actor: string;\n allowed?: string[] | null;\n }>;\n\n /**\n * Begins the login process. Depending on the implementation, this may\n * involve redirecting to a login page or opening a popup,\n * so it should always be called in response to a gesture, such as clicking\n * a button, due to the [feature-gating browser security feature](https://developer.mozilla.org/en-US/docs/Web/Security/User_activation).\n *\n * The {@link GraffitiSession | session} object is returned\n * asynchronously via {@link Graffiti.sessionEvents | sessionEvents}\n * as a {@link GraffitiLoginEvent} with event type `login`.\n *\n * @group 4 - Identity Methods\n */\n abstract login(\n /**\n * A suggested actor to login as. For example, if a user tries to\n * edit a post but are not logged in, the interface can infer that\n * they might want to log in as the actor who created the post\n * they are attempting to edit.\n *\n * Even if provided, the implementation should allow the user\n * to log in as a different actor if they choose.\n */\n actor?: string,\n ): Promise<void>;\n\n /**\n * Begins the logout process for a particular {@link GraffitiSession | session}. Depending on the implementation, this may\n * involve redirecting the user to a logout page or opening a popup,\n * so it should always be called in response to a gesture, such as clicking\n * a button, due to the [feature-gating browser security feature](https://developer.mozilla.org/en-US/docs/Web/Security/User_activation).\n *\n * A confirmation will be returned asynchronously via\n * {@link Graffiti.sessionEvents | sessionEvents}\n * as a {@link GraffitiLogoutEvent} as event type `logout`.\n *\n * @group 4 - Identity Methods\n */\n abstract logout(\n /**\n * The {@link GraffitiSession | session} object to logout.\n */\n session: GraffitiSession,\n ): Promise<void>;\n\n /**\n * An event target that can be used to listen for the following\n * events and their corresponding event types:\n * - `login` - {@link GraffitiLoginEvent}\n * - `logout` - {@link GraffitiLogoutEvent}\n * - `initialized` - {@link GraffitiSessionInitializedEvent}\n *\n * @group 4 - Identity Methods\n */\n abstract readonly sessionEvents: EventTarget;\n\n /**\n * Retrieves the human-readable handle associated\n * with the given actor. The handle may change over time\n * and so it should be used for display purposes only.\n *\n * The inverse of {@link handleToActor}.\n *\n * @throws {@link GraffitiErrorNotFound} if a handle cannot be\n * found for the given actor.\n *\n * @returns A human-readable handle for the given actor.\n *\n * @group 4 - Identity Methods\n */\n abstract actorToHandle(actor: string): Promise<string>;\n\n /**\n * Retrieves the actor ID associated with the given handle.\n *\n * The inverse of {@link actorToHandle}.\n *\n * @throws {@link GraffitiErrorNotFound} if there is no actor\n * with the given handle.\n *\n * @returns The actor ID for the given handle.\n *\n * @group 4 - Identity Methods\n */\n abstract handleToActor(handle: string): Promise<string>;\n}\n", "import type { JSONSchema, FromSchema } from \"json-schema-to-ts\";\n\n/**\n * Objects are the atomic unit in Graffiti that can represent both data (*e.g.* a social media post or profile)\n * and activities (*e.g.* a like or follow).\n *\n * Each object embeds the {@link actor | `actor`} that created it.\n * Object content and metadata are static but an object may be deleted by\n * its creating {@link actor | `actor`}.\n *\n * An object's content is stored in its {@link value | `value`} property, which can be any JSON\n * object. However, it is recommended to use properties from the\n * [Activity Vocabulary](https://www.w3.org/TR/activitystreams-vocabulary/)\n * or properties that emerge in the Graffiti [folksonomy](https://en.wikipedia.org/wiki/Folksonomy)\n * to promote interoperability.\n *\n * Each object is globally addressable via its {@link url | `url`}.\n *\n * An object's {@link channels | `channels`} and {@link allowed | `allowed`} properties\n * are set by an objects creator to shape the visibility of and access to their object.\n */\nexport interface GraffitiObjectBase {\n /**\n * The object's content as freeform JSON. We recommend using properties from the\n * [Activity Vocabulary](https://www.w3.org/TR/activitystreams-vocabulary/)\n * or properties that emerge in the Graffiti [folksonomy](https://en.wikipedia.org/wiki/Folksonomy)\n * to promote interoperability.\n */\n value: {};\n\n /**\n * An array of URIs the creator associates with the object. Objects can only be found by querying\n * one of the object's channels using the\n * {@link Graffiti.discover} method. This allows creators to express the intended audience of their object\n * which helps to prevent [context collapse](https://en.wikipedia.org/wiki/Context_collapse) even\n * in the highly interoperable ecosystem that Graffiti envisions. For example, channel URIs may be:\n * - A actor's own {@link actor | `actor`} URI. Posting an object to this channel is a way to broadcast\n * the object to the actor's followers, like posting a tweet.\n * - The URL of a Graffiti post. Posting an object to this channel is a way to broadcast to anyone viewing\n * the post, like commenting on a tweet.\n * - A URI representing a topic. Posting an object to this channel is a way to broadcast to anyone interested\n * in that topic, like posting in a subreddit.\n */\n channels: string[];\n\n /**\n * An optional array of {@link actor | `actor`} URIs that the creator allows to access the object.\n * If no `allowed` array is provided, the object can be accessed by anyone (so long as they\n * also know the right {@link channels | `channel` } to look in). An object can always be accessed by its creator, even if\n * the `allowed` array is empty.\n *\n * The `allowed` array is not revealed to actors other than the creator, like\n * a BCC email. An actor may choose to add a `to` property to the object's {@link value | `value`} to indicate\n * other recipients, however this is not enforced by Graffiti and may not accurately reflect the actual `allowed` array.\n *\n * `allowed` can be combined with {@link channels | `channels`}. For example, to send someone a direct message\n * the sender should post their object to the channel of the recipient's {@link actor | `actor`} URI to notify them of the message and also add\n * the recipient's {@link actor | `actor`} URI to the `allowed` array to prevent others from seeing the message.\n */\n allowed?: string[] | null;\n\n /**\n * The URI of the `actor` that {@link Graffiti.post | created } the object.\n * This `actor` has the unique permission to\n * {@link Graffiti.delete | delete} the object.\n *\n * We borrow the term actor from the ActivityPub because\n * [like in ActivityPub](https://www.w3.org/TR/activitypub/#h-note-0)\n * there is not necessarily a one-to-one mapping between actors and people/users.\n * Multiple people can share the same actor or one person can have multiple actors.\n * Actors can also be bots.\n *\n * In Graffiti, actors are always globally unique URIs which\n * allows them to also function as {@link channels | `channels`}.\n */\n actor: string;\n\n /**\n * A globally unique identifier and locator for the object. It can be used to point to\n * an object or to retrieve the object directly with {@link Graffiti.get}.\n *\n * An object's URL is generated when the object is first created and\n * should include sufficient randomness to prevent collisions\n * and guessing. The URL starts with a \"scheme,\" just like web URLs start with `http` or `https`, to indicate\n * to indicate the particular Graffiti implementation. This allows for applications\n * to pull from multiple coexisting Graffiti implementations without collision.\n * Existing schemes include `graffiti:local:` for objects stored locally\n * (see the [local implementation](https://github.com/graffiti-garden/implementation-local))\n * and `graffiti:remote:` for objects stored on Graffiti-specific web servers (see the [remote implementation](https://github.com/graffiti-garden/implementation-remote))\n * Options available in the future might include `graffiti:solid:` for objects stored on Solid servers\n * or `graffiti:p2p:` for objects stored on a peer-to-peer network.\n */\n url: string;\n}\n\n/**\n * This type constrains the {@link GraffitiObjectBase} type to adhere to a\n * particular [JSON schema](https://json-schema.org/).\n * This allows for static type-checking of an object's {@link GraffitiObjectBase.value | `value`}\n * which is otherwise a freeform JSON object.\n *\n * Schema-aware objects are returned by {@link Graffiti.get} and {@link Graffiti.discover}.\n */\nexport type GraffitiObject<Schema extends JSONSchema> = GraffitiObjectBase &\n FromSchema<Schema & typeof GraffitiPostObjectJSONSchema>;\n\n/**\n * A JSON Schema equivalent to the {@link GraffitiObjectBase} type.\n * Needed internally for type inference of JSON Schemas, but can\n * be used by implementations to validate objects.\n */\nexport const GraffitiObjectJSONSchema = {\n type: \"object\",\n properties: {\n value: { type: \"object\" },\n channels: { type: \"array\", items: { type: \"string\" } },\n allowed: { type: \"array\", items: { type: \"string\" }, nullable: true },\n url: { type: \"string\" },\n actor: { type: \"string\" },\n },\n additionalProperties: false,\n required: [\"value\", \"channels\", \"actor\", \"url\"],\n} as const satisfies JSONSchema;\n\n/**\n * This is an object containing only the {@link GraffitiObjectBase.url | `url`}\n * property of a {@link GraffitiObjectBase | GraffitiObject}.\n * It is used as a utility type so that applications can call\n * {@link Graffiti.delete} directly on an object\n * rather than on `object.url`.\n */\nexport type GraffitiObjectUrl = Pick<GraffitiObjectBase, \"url\">;\n\n/**\n * This object is a subset of {@link GraffitiObjectBase} that must be constructed locally before calling {@link Graffiti.post}.\n * This local copy ignores system-generated properties\n * ({@link GraffitiObjectBase.url | `url`} and {@link GraffitiObjectBase.actor | `actor`}),\n * and may be statically typed with\n * a [JSON schema](https://json-schema.org/) to prevent the accidental creation of erroneous objects.\n *\n * This local object must have a {@link GraffitiObjectBase.value | `value`} and {@link GraffitiObjectBase.channels | `channels`}\n * and may optionally have an {@link GraffitiObjectBase.allowed | `allowed`} property.\n */\nexport type GraffitiPostObject<Schema extends JSONSchema> = Pick<\n GraffitiObjectBase,\n \"value\" | \"channels\" | \"allowed\"\n> &\n FromSchema<Schema & typeof GraffitiPostObjectJSONSchema>;\n\n/**\n * A JSON Schema equivalent to the {@link GraffitiPostObject} type.\n * Needed internally for type inference of JSON Schemas, but can\n * be used by implementations to validate objects.\n */\nexport const GraffitiPostObjectJSONSchema = {\n ...GraffitiObjectJSONSchema,\n required: [\"value\", \"channels\"],\n} as const satisfies JSONSchema;\n\n/**\n * This object contains information that the underlying implementation can\n * use to authenticate a particular {@link GraffitiObjectBase.actor | `actor`}.\n * This object is required of all {@link Graffiti} methods\n * that modify objects or media and is optional for methods that read objects.\n *\n * At a minimum the `session` object must contain the\n * {@link GraffitiSession.actor | `actor`} URI to authenticate with.\n * However it is likely that the `session` object must contain other\n * implementation-specific properties.\n * For example, a Solid implementation might include a\n * [`fetch`](https://docs.inrupt.com/developer-tools/api/javascript/solid-client-authn-browser/functions.html#fetch)\n * function. A distributed implementation may include\n * a cryptographic signature.\n *\n * As to why the `session` object is passed as an argument to every method\n * rather than being an internal property of the {@link Graffiti} instance,\n * this is primarily for type-checking to catch bugs related to login state.\n * Graffiti applications can expose some functionality to people who are not logged in\n * with {@link Graffiti.get} and {@link Graffiti.discover} but without type-checking\n * the `session` it can be easy to forget to hide buttons that trigger\n * other methods that require login.\n *\n * Passing the `session` object per-method also allows for multiple sessions\n * to be used within the same application, like an Email client fetching from\n * multiple accounts.\n */\nexport interface GraffitiSession {\n /**\n * The {@link GraffitiObjectBase.actor | `actor`} to authenticate with.\n */\n actor: string;\n}\n\n/**\n * A stream of data that are returned by {@link Graffiti.discover}.\n *\n * Errors are returned within the stream rather than as\n * exceptions that would halt the entire stream. This is because\n * some implementations may pull data from multiple sources\n * including some that may be unreliable. In many cases,\n * these errors can be safely ignored.\n * See {@link GraffitiObjectStreamError}.\n *\n * The stream is an [`AsyncGenerator`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function)\n * that can be iterated over using `for await` loops or calling `next` on the generator.\n * The stream can be terminated by breaking out of a loop calling `return` on the generator.\n *\n * The stream ends by returning a {@link GraffitiObjectStreamReturn.continue | `continue`}\n * function and a {@link GraffitiObjectStreamReturn.cursor | `cursor`} string,\n * each of which can be used to resume the stream from where it left off.\n */\nexport type GraffitiObjectStream<Schema extends JSONSchema> = AsyncGenerator<\n GraffitiObjectStreamError | GraffitiObjectStreamEntry<Schema>,\n GraffitiObjectStreamReturn<Schema>\n>;\n\n/**\n * An error that can occur in a {@link GraffitiObjectStream}.\n *\n * @internal\n */\nexport interface GraffitiObjectStreamError {\n /**\n * The error that occurred while streaming data.\n */\n error: Error;\n /**\n * The origin that the error occurred. It will include\n * the scheme of the Graffiti implementation used and other\n * implementation-specific information like a hostname.\n */\n origin: string;\n}\n\n/**\n * A successful result from a {@link GraffitiObjectStream} or\n * {@link GraffitiObjectStreamContinue} that includes an object.\n *\n * @internal\n */\nexport interface GraffitiObjectStreamEntry<Schema extends JSONSchema> {\n /**\n * Empty property for compatibility with {@link GraffitiObjectStreamError}\n */\n error?: undefined;\n /**\n * Empty property for compatibility with {@link GraffitiObjectStreamContinueTombstone}\n */\n tombstone?: undefined;\n /**\n * The object returned by the stream.\n */\n object: GraffitiObject<Schema>;\n}\n\n/**\n * A result from a {@link GraffitiObjectStreamContinue} that indicated\n * an object has been deleted since the original stream was run.\n * Only sparse metadata about the deleted object is returned to respect\n * the deleting actor's privacy.\n *\n * @internal\n */\nexport interface GraffitiObjectStreamContinueTombstone {\n /**\n * Empty property for compatibility with {@link GraffitiObjectStreamError}\n */\n error?: undefined;\n /**\n * Use this property to differentiate a tombstone from a\n * {@link GraffitiObjectStreamEntry}.\n */\n tombstone: true;\n /**\n * Sparse metadata about the deleted object. The full object is not returned\n * to respect an actor's privacy.\n */\n object: {\n /**\n * The {@link GraffitiObjectBase.url | `url`} of the deleted object.\n */\n url: string;\n };\n}\n\n/**\n * A continuation of the {@link GraffitiObjectStream} type can include\n * both objects and tombstones of deleted objects.\n *\n * @internal\n */\nexport type GraffitiObjectStreamContinueEntry<Schema extends JSONSchema> =\n | GraffitiObjectStreamEntry<Schema>\n | GraffitiObjectStreamContinueTombstone;\n\n/**\n * The output of a {@link GraffitiObjectStream} or a {@link GraffitiObjectStreamContinue}\n * that allows the stream to be continued from where it left off.\n *\n * The {@link continue} function preserves the typing of the original stream,\n * where as the {@link cursor} string can be serialized for use after a person\n * has closed and reopened an application.\n *\n * The continued stream may include `tombstone`s of objects that have been\n * deleted since the original stream was run. See {@link GraffitiObjectStreamContinueTombstone}.\n * The continued stream may also return some objects that were already\n * returned by the original stream, depending on how much state the\n * underlying implementation is able to preserve.\n *\n * @internal\n */\nexport interface GraffitiObjectStreamReturn<Schema extends JSONSchema> {\n /**\n * @returns A function that creates new stream that continues from where the original stream left off.\n * It preserves the typing of the original stream.\n */\n continue: (\n session?: GraffitiSession | null,\n ) => GraffitiObjectStreamContinue<Schema>;\n /**\n * A string that can be serialized and stored to resume the stream later.\n * It must be passed to the {@link Graffiti.continueDiscover} method\n * to resume the stream.\n */\n cursor: string;\n}\n\n/**\n * A continutation of the {@link GraffitiObjectStream} type, as returned by\n * the {@link GraffitiObjectStreamReturn.continue} or by using\n * {@link GraffitiObjectStreamReturn.cursor} with {@link Graffiti.continueDiscover}.\n *\n * The continued stream may include `tombstone`s of objects that have been\n * deleted since the original stream was run. See {@link GraffitiObjectStreamContinueTombstone}.\n *\n * @internal\n */\nexport type GraffitiObjectStreamContinue<Schema extends JSONSchema> =\n AsyncGenerator<\n GraffitiObjectStreamError | GraffitiObjectStreamContinueEntry<Schema>,\n GraffitiObjectStreamReturn<Schema>\n >;\n\n/**\n * The event type produced in {@link Graffiti.sessionEvents}\n * when a actor logs in manually from {@link Graffiti.login}\n * or when their session is restored from a previous login.\n * The event name to listen for is `login`.\n */\nexport type GraffitiLoginEvent = CustomEvent<\n | {\n error: Error;\n session?: undefined;\n }\n | {\n error?: undefined;\n session: GraffitiSession;\n }\n>;\n\n/**\n * The event type produced in {@link Graffiti.sessionEvents}\n * when a actor logs out either manually with {@link Graffiti.logout}\n * or when their session times out or otherwise becomes invalid.\n * The event name to listen for is `logout`.\n */\nexport type GraffitiLogoutEvent = CustomEvent<\n | {\n error: Error;\n actor?: string;\n }\n | {\n error?: undefined;\n actor: string;\n }\n>;\n\n/**\n * The event type produced in {@link Graffiti.sessionEvents}\n * after an application has attempted to complete any login redirects\n * and restore any previously active sessions.\n * Successful session restores will be returned in parallel as\n * their own {@link GraffitiLoginEvent} events.\n *\n * This event optionally returns an `href` property\n * representing the URL that originated a login request,\n * which may be useful for redirecting the user back to\n * the page they were on after login.\n * The event name to listen for is `initialized`.\n */\nexport type GraffitiSessionInitializedEvent = CustomEvent<\n | {\n error?: Error;\n href?: string;\n }\n | null\n | undefined\n>;\n", "export class GraffitiErrorForbidden extends Error {\n constructor(message?: string) {\n super(message);\n this.name = \"GraffitiErrorForbidden\";\n Object.setPrototypeOf(this, GraffitiErrorForbidden.prototype);\n }\n}\n\nexport class GraffitiErrorNotFound extends Error {\n constructor(message?: string) {\n super(message);\n this.name = \"GraffitiErrorNotFound\";\n Object.setPrototypeOf(this, GraffitiErrorNotFound.prototype);\n }\n}\n\nexport class GraffitiErrorInvalidSchema extends Error {\n constructor(message?: string) {\n super(message);\n this.name = \"GraffitiErrorInvalidSchema\";\n Object.setPrototypeOf(this, GraffitiErrorInvalidSchema.prototype);\n }\n}\n\nexport class GraffitiErrorSchemaMismatch extends Error {\n constructor(message?: string) {\n super(message);\n this.name = \"GraffitiErrorSchemaMismatch\";\n Object.setPrototypeOf(this, GraffitiErrorSchemaMismatch.prototype);\n }\n}\n\nexport class GraffitiErrorTooLarge extends Error {\n constructor(message?: string) {\n super(message);\n this.name = \"GraffitiErrorTooLarge\";\n Object.setPrototypeOf(this, GraffitiErrorTooLarge.prototype);\n }\n}\n\nexport class GraffitiErrorNotAcceptable extends Error {\n constructor(message?: string) {\n super(message);\n this.name = \"GraffitiErrorNotAcceptable\";\n Object.setPrototypeOf(this, GraffitiErrorNotAcceptable.prototype);\n }\n}\n", "import type { JSONSchema } from \"json-schema-to-ts\";\nimport type { Ajv } from \"ajv\";\nimport { GraffitiErrorInvalidSchema } from \"./3-errors\";\nimport type {\n GraffitiObjectBase,\n GraffitiObject,\n GraffitiObjectUrl,\n GraffitiSession,\n} from \"./2-types\";\n\nexport function unpackObjectUrl(url: string | GraffitiObjectUrl) {\n return typeof url === \"string\" ? url : url.url;\n}\n\nexport function compileGraffitiObjectSchema<Schema extends JSONSchema>(\n ajv: Ajv,\n schema: Schema,\n) {\n try {\n // Force the validation guard because\n // it is too big for the type checker.\n // Fortunately json-schema-to-ts is\n // well tested against ajv.\n return ajv.compile(schema) as (\n data: GraffitiObjectBase,\n ) => data is GraffitiObject<Schema>;\n } catch (error) {\n throw new GraffitiErrorInvalidSchema(\n error instanceof Error ? error.message : undefined,\n );\n }\n}\n\nexport function isActorAllowedGraffitiObject(\n object: GraffitiObjectBase,\n session?: GraffitiSession | null,\n) {\n return (\n // If there is no allowed list, the actor is allowed.\n !Array.isArray(object.allowed) ||\n // Otherwise...\n (typeof session?.actor === \"string\" &&\n // The actor must be the creator of the object\n (object.actor === session.actor ||\n // Or be on the allowed list\n object.allowed.includes(session.actor)))\n );\n}\n\nexport function maskGraffitiObject(\n object: GraffitiObjectBase,\n channels: string[],\n session?: GraffitiSession | null,\n): void {\n // If the actor is not the creator, mask the object.\n if (object.actor !== session?.actor) {\n // If there is an allowed list, mask it to only include the actor\n // (This assumes the actor is already allowed to access the object)\n object.allowed = object.allowed && session ? [session.actor] : undefined;\n // Mask the channels to only include the channels that are being queried\n object.channels = object.channels.filter((channel) =>\n channels.includes(channel),\n );\n }\n}\n"],
|
|
5
|
+
"mappings": "yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,cAAAE,EAAA,2BAAAC,EAAA,+BAAAC,EAAA,+BAAAC,EAAA,0BAAAC,EAAA,gCAAAC,EAAA,0BAAAC,EAAA,6BAAAC,EAAA,iCAAAC,EAAA,gCAAAC,EAAA,iCAAAC,EAAA,uBAAAC,EAAA,oBAAAC,IAAA,eAAAC,EAAAf,GC4GO,IAAegB,EAAf,KAAwB,CAqW/B,EClWO,IAAMC,EAA2B,CACtC,KAAM,SACN,WAAY,CACV,MAAO,CAAE,KAAM,QAAS,EACxB,SAAU,CAAE,KAAM,QAAS,MAAO,CAAE,KAAM,QAAS,CAAE,EACrD,QAAS,CAAE,KAAM,QAAS,MAAO,CAAE,KAAM,QAAS,EAAG,SAAU,EAAK,EACpE,IAAK,CAAE,KAAM,QAAS,EACtB,MAAO,CAAE,KAAM,QAAS,CAC1B,EACA,qBAAsB,GACtB,SAAU,CAAC,QAAS,WAAY,QAAS,KAAK,CAChD,EAgCaC,EAA+B,CAC1C,GAAGD,EACH,SAAU,CAAC,QAAS,UAAU,CAChC,EC7JO,IAAME,EAAN,MAAMC,UAA+B,KAAM,CAChD,YAAYC,EAAkB,CAC5B,MAAMA,CAAO,EACb,KAAK,KAAO,yBACZ,OAAO,eAAe,KAAMD,EAAuB,SAAS,CAC9D,CACF,EAEaE,EAAN,MAAMC,UAA8B,KAAM,CAC/C,YAAYF,EAAkB,CAC5B,MAAMA,CAAO,EACb,KAAK,KAAO,wBACZ,OAAO,eAAe,KAAME,EAAsB,SAAS,CAC7D,CACF,EAEaC,EAAN,MAAMC,UAAmC,KAAM,CACpD,YAAYJ,EAAkB,CAC5B,MAAMA,CAAO,EACb,KAAK,KAAO,6BACZ,OAAO,eAAe,KAAMI,EAA2B,SAAS,CAClE,CACF,EAEaC,EAAN,MAAMC,UAAoC,KAAM,CACrD,YAAYN,EAAkB,CAC5B,MAAMA,CAAO,EACb,KAAK,KAAO,8BACZ,OAAO,eAAe,KAAMM,EAA4B,SAAS,CACnE,CACF,EAEaC,EAAN,MAAMC,UAA8B,KAAM,CAC/C,YAAYR,EAAkB,CAC5B,MAAMA,CAAO,EACb,KAAK,KAAO,wBACZ,OAAO,eAAe,KAAMQ,EAAsB,SAAS,CAC7D,CACF,EAEaC,EAAN,MAAMC,UAAmC,KAAM,CACpD,YAAYV,EAAkB,CAC5B,MAAMA,CAAO,EACb,KAAK,KAAO,6BACZ,OAAO,eAAe,KAAMU,EAA2B,SAAS,CAClE,CACF,ECpCO,SAASC,EAAgBC,EAAiC,CAC/D,OAAO,OAAOA,GAAQ,SAAWA,EAAMA,EAAI,GAC7C,CAEO,SAASC,EACdC,EACAC,EACA,CACA,GAAI,CAKF,OAAOD,EAAI,QAAQC,CAAM,CAG3B,OAASC,EAAO,CACd,MAAM,IAAIC,EACRD,aAAiB,MAAQA,EAAM,QAAU,MAC3C,CACF,CACF,CAEO,SAASE,EACdC,EACAC,EACA,CACA,MAEE,CAAC,MAAM,QAAQD,EAAO,OAAO,GAE5B,OAAOC,GAAS,OAAU,WAExBD,EAAO,QAAUC,EAAQ,OAExBD,EAAO,QAAQ,SAASC,EAAQ,KAAK,EAE7C,CAEO,SAASC,EACdF,EACAG,EACAF,EACM,CAEFD,EAAO,QAAUC,GAAS,QAG5BD,EAAO,QAAUA,EAAO,SAAWC,EAAU,CAACA,EAAQ,KAAK,EAAI,OAE/DD,EAAO,SAAWA,EAAO,SAAS,OAAQI,GACxCD,EAAS,SAASC,CAAO,CAC3B,EAEJ",
|
|
6
|
+
"names": ["index_exports", "__export", "Graffiti", "GraffitiErrorForbidden", "GraffitiErrorInvalidSchema", "GraffitiErrorNotAcceptable", "GraffitiErrorNotFound", "GraffitiErrorSchemaMismatch", "GraffitiErrorTooLarge", "GraffitiObjectJSONSchema", "GraffitiPostObjectJSONSchema", "compileGraffitiObjectSchema", "isActorAllowedGraffitiObject", "maskGraffitiObject", "unpackObjectUrl", "__toCommonJS", "Graffiti", "GraffitiObjectJSONSchema", "GraffitiPostObjectJSONSchema", "GraffitiErrorForbidden", "_GraffitiErrorForbidden", "message", "GraffitiErrorNotFound", "_GraffitiErrorNotFound", "GraffitiErrorInvalidSchema", "_GraffitiErrorInvalidSchema", "GraffitiErrorSchemaMismatch", "_GraffitiErrorSchemaMismatch", "GraffitiErrorTooLarge", "_GraffitiErrorTooLarge", "GraffitiErrorNotAcceptable", "_GraffitiErrorNotAcceptable", "unpackObjectUrl", "url", "compileGraffitiObjectSchema", "ajv", "schema", "error", "GraffitiErrorInvalidSchema", "isActorAllowedGraffitiObject", "object", "session", "maskGraffitiObject", "channels", "channel"]
|
|
7
7
|
}
|
package/dist/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var
|
|
1
|
+
var a=class{};var S={type:"object",properties:{value:{type:"object"},channels:{type:"array",items:{type:"string"}},allowed:{type:"array",items:{type:"string"},nullable:!0},url:{type:"string"},actor:{type:"string"}},additionalProperties:!1,required:["value","channels","actor","url"]},l={...S,required:["value","channels"]};var s=class t extends Error{constructor(e){super(e),this.name="GraffitiErrorForbidden",Object.setPrototypeOf(this,t.prototype)}},o=class t extends Error{constructor(e){super(e),this.name="GraffitiErrorNotFound",Object.setPrototypeOf(this,t.prototype)}},i=class t extends Error{constructor(e){super(e),this.name="GraffitiErrorInvalidSchema",Object.setPrototypeOf(this,t.prototype)}},n=class t extends Error{constructor(e){super(e),this.name="GraffitiErrorSchemaMismatch",Object.setPrototypeOf(this,t.prototype)}},c=class t extends Error{constructor(e){super(e),this.name="GraffitiErrorTooLarge",Object.setPrototypeOf(this,t.prototype)}},f=class t extends Error{constructor(e){super(e),this.name="GraffitiErrorNotAcceptable",Object.setPrototypeOf(this,t.prototype)}};function u(t){return typeof t=="string"?t:t.url}function d(t,e){try{return t.compile(e)}catch(r){throw new i(r instanceof Error?r.message:void 0)}}function G(t,e){return!Array.isArray(t.allowed)||typeof e?.actor=="string"&&(t.actor===e.actor||t.allowed.includes(e.actor))}function j(t,e,r){t.actor!==r?.actor&&(t.allowed=t.allowed&&r?[r.actor]:void 0,t.channels=t.channels.filter(m=>e.includes(m)))}export{a as Graffiti,s as GraffitiErrorForbidden,i as GraffitiErrorInvalidSchema,f as GraffitiErrorNotAcceptable,o as GraffitiErrorNotFound,n as GraffitiErrorSchemaMismatch,c as GraffitiErrorTooLarge,S as GraffitiObjectJSONSchema,l as GraffitiPostObjectJSONSchema,d as compileGraffitiObjectSchema,G as isActorAllowedGraffitiObject,j as maskGraffitiObject,u as unpackObjectUrl};
|
|
2
2
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../src/1-api.ts", "../src/2-types.ts", "../src/3-errors.ts"],
|
|
4
|
-
"sourcesContent": ["import type {\n GraffitiObjectUrl,\n GraffitiObject,\n GraffitiObjectBase,\n GraffitiSession,\n GraffitiPostObject,\n GraffitiObjectStream,\n GraffitiObjectStreamContinue,\n} from \"./2-types\";\nimport type { JSONSchema } from \"json-schema-to-ts\";\n\n/**\n * This API describes a small but powerful set of methods that\n * can be used to create many different kinds of social applications,\n * from applications like Twitter, to Messenger, to Wikipedia, to many more new designs.\n * See the [Graffiti project website](https://graffiti.garden)\n * for links to example applications. Additionally, apps built on top\n * of the API interoperate with each other so you can seamlessly switch\n * between apps without losing your friends or data.\n *\n * These API methods should satisfy all of an application's needs for\n * the communication, storage, and access management of social data.\n * The rest of the application can be built with standard client-side\n * user interface tools to present and interact with that data\u2014no server code necessary!\n *\n * The Typescript code for this API is [open source on Github](https://github.com/graffiti-garden/api).\n *\n * There are several different implementations of this Graffiti API available,\n * including a [federated implementation](https://github.com/graffiti-garden/implementation-remote),\n * that lets people choose where their data is stored (you do not need to host your own server)\n * and a [local implementation](https://github.com/graffiti-garden/implementation-local)\n * that can be used for testing and development. Different implementations can\n * be swapped-in in the future without changing the API or any of the apps built on\n * top of it. In fact, we're working on an end-to-end encrypted version now!\n * [Follow Theia on BlueSky for updates](https://bsky.app/profile/theias.place).\n *\n * On the other side of the stack, there is [Vue plugin](https://vue.graffiti.garden/variables/GraffitiPlugin.html)\n * that wraps around this API to provide reactivity. Other plugin frameworks\n * and high-level libraries will be available in the future.\n *\n * ## API Overview\n *\n * The Graffiti API provides applications with methods for {@link login} and {@link logout},\n * methods to interact with data objects using standard database operations ({@link post}, {@link get}, and {@link delete}),\n * and a method to {@link discover} data objects created by others.\n * These data objects have a couple structured properties:\n * - {@link GraffitiObjectBase.url | `url`} (string): A globally unique identifier and locator for the object.\n * - {@link GraffitiObjectBase.actor | `actor`} (string): An unforgeable identifier for the creator of the object.\n * - {@link GraffitiObjectBase.allowed | `allowed`} (string[] | undefined): An array of the actors who are allowed to access the object (undefined for public objects).\n * - {@link GraffitiObjectBase.channels | `channels`} (string[]): An array of the *contexts* in which the object should appear.\n *\n * All other data is stored in the object's unstructured {@link GraffitiObjectBase.value | `value`} property.\n * This data can be used to represent social artifacts (e.g. posts, profiles) and activities (e.g. likes, follows).\n * For example, a post might have the value:\n\n * ```js\n * {\n * title: \"My First Post\",\n * content: \"Hello, world!\",\n * published: 1630483200000\n * }\n * ```\n *\n * a profile might have the value:\n *\n * ```js\n * {\n * name: \"Theia Henderson\",\n * pronouns: \"she/her\",\n * describes: \"did:web:theias.place\" // Theia's actor ID\n * }\n * ```\n *\n * and a \"Like\" might have the value:\n *\n * ```js\n * {\n * activity: \"Like\",\n * target: \"graffiti:remote:pod.graffiti.garden/12345\" // The URL of the graffiti object being liked\n * }\n * ```\n *\n * New social artifacts and activities can be easily created, simply\n * by creating new objects with appropriate properties. Despite the lack of\n * structure, we expect Graffiti object properties to adhere to a \"[folksonomy](https://en.wikipedia.org/wiki/Folksonomy)\",\n * similar to hashtags. Any string can be used as a hashtag on Twitter,\n * but there is social value in using the same hashtags at other people and\n * so a structure naturally emerges. Similarly, Graffiti objects\n * can have arbitrary properties but if people use the same properties as each other,\n * their apps will interoperate, which has social value.\n *\n * For a more complete and detailed overview of Graffiti's design, please\n * refer to [this section of the Graffiti paper](https://dl.acm.org/doi/10.1145/3746059.3747627#sec-3),\n * published in ACM UIST 2025. The paper also overviews {@link GraffitiObjectBase.channels | `channels`},\n * which are Graffiti's means of organizing data contextually, and a concept called \"total reification\",\n * which handles explains how moderation, collaboration, and other interactions are managed.\n *\n * @groupDescription 1 - Single-Object Methods\n * Methods for {@link post | creating}, {@link get | reading},\n * and {@link delete | deleting} {@link GraffitiObjectBase | Graffiti objects}.\n * @groupDescription 2 - Multi-Object Methods\n * Methods that retrieve or accumulate information about multiple {@link GraffitiObjectBase | Graffiti objects} at a time.\n * @groupDescription 3 - Media Methods\n * Methods for {@link postMedia | creating}, {@link getMedia | reading},\n * and {@link deleteMedia | deleting} media data.\n * @groupDescription 4 - Session Management\n * Methods and properties for logging in and out.\n */\nexport abstract class Graffiti {\n /**\n * Creates a new {@link GraffitiObjectBase | object}.\n *\n * @returns Returns the object that has been posted, complete with its\n * assigned {@link GraffitiObjectBase.url | `url`} and\n * {@link GraffitiObjectBase.actor | `actor`}.\n *\n * @group 1 - Single-Object Methods\n */\n abstract post<Schema extends JSONSchema>(\n /**\n * An object to post. This object is statically type-checked against the [JSON schema](https://json-schema.org/) that can be optionally provided\n * as the generic type parameter. It is recommended to a schema to\n * ensure that the posted object matches subsequent {@link get} or {@link discover}\n * methods.\n */\n object: GraffitiPostObject<Schema>,\n /**\n * An implementation-specific object with information to authenticate the\n * {@link GraffitiObjectBase.actor | `actor`}.\n */\n session: GraffitiSession,\n ): Promise<GraffitiObject<Schema>>;\n\n /**\n * Retrieves an object from a given {@link GraffitiObjectBase.url | `url`} matching\n * the provided `schema`.\n *\n * If the retreiving {@link GraffitiObjectBase.actor | `actor`} is not\n * the object's `actor`,\n * the object's {@link GraffitiObjectBase.allowed | `allowed`} and\n * {@link GraffitiObjectBase.channels | `channels`} properties are\n * not revealed, similar to a BCC email.\n *\n * @returns Returns the retrieved object.\n *\n * @throws {@link GraffitiErrorNotFound} if the object does not exist, has been deleted, or the actor is not\n * {@link GraffitiObjectBase.allowed | `allowed`} to access it.\n *\n * @throws {@link GraffitiErrorSchemaMismatch} if the retrieved object does not match the provided schema.\n *\n * @group 1 - Single-Object Methods\n */\n abstract get<Schema extends JSONSchema>(\n /**\n * The location of the object to get.\n */\n url: string | GraffitiObjectUrl,\n /**\n * The JSON schema to validate the retrieved object against.\n */\n schema: Schema,\n /**\n * An implementation-specific object with information to authenticate the\n * {@link GraffitiObjectBase.actor | `actor`}. If no `session` is provided,\n * the retrieved object's {@link GraffitiObjectBase.allowed | `allowed`}\n * property must be `undefined`.\n */\n session?: GraffitiSession | null,\n ): Promise<GraffitiObject<Schema>>;\n\n /**\n * Deletes an object from a given {@link GraffitiObjectBase.url | `url`}\n * that had previously been {@link post | `post`ed}.\n * The deleting {@link GraffitiObjectBase.actor | `actor`} must be the same as the\n * `actor` that created the object.\n *\n * @throws {@link GraffitiErrorNotFound} if the object does not exist, has already been deleted,\n * or the actor is not {@link GraffitiObjectBase.allowed | `allowed`} to access it.\n *\n * @throws {@link GraffitiErrorForbidden} if the {@link GraffitiObjectBase.actor | `actor`}\n * is not the same `actor` as the one who created the object.\n *\n * @group 1 - Single-Object Methods\n */\n abstract delete(\n /**\n * The location of the object to delete.\n */\n url: string | GraffitiObjectUrl,\n /**\n * An implementation-specific object with information to authenticate the\n * {@link GraffitiObjectBase.actor | `actor`}.\n */\n session: GraffitiSession,\n ): Promise<void>;\n\n /**\n * Discovers objects created by any actor that are contained\n * in at least one of the given {@link GraffitiObjectBase.channels | `channels`}\n * and match the given [JSON Schema](https://json-schema.org).\n *\n * Objects are returned asynchronously as they are discovered but the stream\n * will end once all leads have been exhausted.\n * The {@link GraffitiObjectStream} ends by returning a\n * {@link GraffitiObjectStreamReturn.continue | `continue`} method and a\n * {@link GraffitiObjectStreamReturn.cursor | `cursor`} string,\n * each of which can be be used to poll for new objects.\n * The `continue` method preserves the type safety of the stream and the `cursor`\n * string can be serialized to continue the stream after an application is closed\n * and reopened.\n *\n * `discover` will not return objects that the querying {@link GraffitiObjectBase.actor | `actor`}\n * is not {@link GraffitiObjectBase.allowed | `allowed`} to access.\n * If the `actor` is not the creator of a discovered object,\n * the allowed list will be masked to only contain the querying actor if the\n * allowed list is not `undefined` (public). Additionally, if the actor is not the\n * creator of a discovered object, any {@link GraffitiObjectBase.channels | `channels`}\n * not specified by the `discover` method will not be revealed. This masking happens\n * before the object is validated against the supplied `schema`.\n *\n * Since different implementations may fetch data from multiple sources there is\n * no guarentee on the order that objects are returned in.\n *\n * @returns Returns a stream of objects that match the given {@link GraffitiObjectBase.channels | `channels`}\n * and [JSON Schema](https://json-schema.org).\n *\n * @group 2 - Multi-Object Methods\n */\n abstract discover<Schema extends JSONSchema>(\n /**\n * The {@link GraffitiObjectBase.channels | `channels`} that objects must be associated with.\n */\n channels: string[],\n /**\n * A [JSON Schema](https://json-schema.org) that objects must satisfy.\n */\n schema: Schema,\n /**\n * An implementation-specific object with information to authenticate the\n * {@link GraffitiObjectBase.actor | `actor`}. If no `session` is provided,\n * only objects that have no {@link GraffitiObjectBase.allowed | `allowed`}\n * property will be returned.\n */\n session?: GraffitiSession | null,\n ): GraffitiObjectStream<Schema>;\n\n /**\n * Continues a {@link GraffitiObjectStream} from a given\n * {@link GraffitiObjectStreamReturn.cursor | `cursor`} string.\n * The continuation will return new objects that have been {@link post | `post`ed}\n * that match the original stream, and also returns the\n * {@link GraffitiObjectBase.url | `url`}s of objects that\n * have been {@link delete | `delete`d}, as marked by a `tombstone`.\n *\n * The `cursor` allows the client to\n * serialize the state of the stream and continue it later.\n * However this method loses any typing information that was\n * present in the original stream. For better type safety\n * and when serializing is not necessary, use the\n * {@link GraffitiObjectStreamReturn.continue | `continue`} method\n * instead, which is returned along with the `cursor` at the\n * end of the original stream.\n *\n * @throws {@link GraffitiErrorForbidden} if the {@link GraffitiObjectBase.actor | `actor`}\n * provided in the `session` is not the same as the `actor`\n * that initiated the original stream.\n *\n * @group 2 - Multi-Object Methods\n */\n abstract continueDiscover(\n cursor: string,\n session?: GraffitiSession | null,\n ): GraffitiObjectStreamContinue<{}>;\n\n /**\n * Uploads media data, such as an image or video.\n *\n * Unlike structured {@link GraffitiObjectBase | objects},\n * media is not indexed for {@link discover | `discover`y} and\n * must be retrieved by its exact URL using {@link getMedia}\n *\n * @returns The URL that the media was posted to.\n *\n * @group 3 - Media Methods\n */\n abstract postMedia(\n /**\n * The binary data of the media to be uploaded,\n * along with its [media type](https://www.iana.org/assignments/media-types/media-types.xhtml),\n * formatted as a [Blob](https://developer.mozilla.org/en-US/docs/Web/API/Blob).\n */\n media: Blob,\n /**\n * An implementation-specific object with information to authenticate the\n * {@link GraffitiObjectBase.actor | `actor`}.\n */\n session: GraffitiSession,\n ): Promise<string>;\n\n /**\n * Deletes media previously {@link postMedia | `post`ed} to a given URL.\n *\n * @throws {@link GraffitiErrorNotFound} if no media at that URL exists.\n *\n * @throws {@link GraffitiErrorForbidden} if the {@link GraffitiObjectBase.actor | `actor`}\n * provided in the `session` is not the same as the `actor` that {@link postMedia | `post`ed}\n * the media.\n *\n * @group 3 - Media Methods\n */\n abstract deleteMedia(\n /**\n * A globally unique identifier and locator for the media.\n */\n mediaUrl: string,\n /**\n * An implementation-specific object with information to authenticate the\n * {@link GraffitiObjectBase.actor | `actor`}.\n */\n session: GraffitiSession,\n ): Promise<void>;\n\n /**\n * Retrieves media from the given media URL, adhering to the given requirements.\n *\n * @throws {@link GraffitiErrorNotFound} if no media at that URL exists.\n *\n * @throws {@link GraffitiErrorTooLarge} if the media exceeds the given `maxBytes`.\n *\n * @throws {@link GraffitiErrorNotAcceptable} if the media does not match the given\n * `accept` specification.\n *\n * @returns The URL of the retrieved media, as a [Blob](https://developer.mozilla.org/en-US/docs/Web/API/Blob)\n * and the {@link GraffitiObjectBase.actor | `actor`} that posted it.\n *\n * @group 3 - Media Methods\n */\n abstract getMedia(\n /**\n * A globally unique identifier and locator for the media.\n */\n mediaUrl: string,\n /**\n * An optional set of requirements the retrieved media must meet.\n */\n requirements?: {\n /**\n * A list of acceptable media types for the retrieved media,\n * formatted as like an [HTTP Accept header](https://httpwg.org/specs/rfc9110.html#field.accept)\n */\n accept?: string;\n /**\n * The maximum size, in bytes, of the media.\n */\n maxBytes?: number;\n },\n ): Promise<{\n media: Blob;\n actor: string;\n }>;\n\n /**\n * Begins the login process. Depending on the implementation, this may\n * involve redirecting to a login page or opening a popup,\n * so it should always be called in response to a gesture, such as clicking\n * a button, due to the [feature-gating browser security feature](https://developer.mozilla.org/en-US/docs/Web/Security/User_activation).\n *\n * The {@link GraffitiSession | session} object is returned\n * asynchronously via {@link Graffiti.sessionEvents | sessionEvents}\n * as a {@link GraffitiLoginEvent} with event type `login`.\n *\n * @group 4 - Session Management\n */\n abstract login(\n /**\n * A suggested actor to login as. For example, if a user tries to\n * edit a post but are not logged in, the interface can infer that\n * they might want to log in as the actor who created the post\n * they are attempting to edit.\n *\n * Even if provided, the implementation should allow the user\n * to log in as a different actor if they choose.\n */\n actor?: string,\n ): Promise<void>;\n\n /**\n * Begins the logout process for a particular {@link GraffitiSession | session}. Depending on the implementation, this may\n * involve redirecting the user to a logout page or opening a popup,\n * so it should always be called in response to a gesture, such as clicking\n * a button, due to the [feature-gating browser security feature](https://developer.mozilla.org/en-US/docs/Web/Security/User_activation).\n *\n * A confirmation will be returned asynchronously via\n * {@link Graffiti.sessionEvents | sessionEvents}\n * as a {@link GraffitiLogoutEvent} as event type `logout`.\n *\n * @group 4 - Session Management\n */\n abstract logout(\n /**\n * The {@link GraffitiSession | session} object to logout.\n */\n session: GraffitiSession,\n ): Promise<void>;\n\n /**\n * An event target that can be used to listen for the following\n * events and their corresponding event types:\n * - `login` - {@link GraffitiLoginEvent}\n * - `logout` - {@link GraffitiLogoutEvent}\n * - `initialized` - {@link GraffitiSessionInitializedEvent}\n *\n * @group 4 - Session Management\n */\n abstract readonly sessionEvents: EventTarget;\n}\n", "import type { JSONSchema, FromSchema } from \"json-schema-to-ts\";\n\n/**\n * Objects are the atomic unit in Graffiti that can represent both data (*e.g.* a social media post or profile)\n * and activities (*e.g.* a like or follow).\n *\n * Each object embeds the {@link actor | `actor`} that created it.\n * Object content and metadata are static but an object may be deleted by\n * its creating {@link actor | `actor`}.\n *\n * An object's content is stored in its {@link value | `value`} property, which can be any JSON\n * object. However, it is recommended to use properties from the\n * [Activity Vocabulary](https://www.w3.org/TR/activitystreams-vocabulary/)\n * or properties that emerge in the Graffiti [folksonomy](https://en.wikipedia.org/wiki/Folksonomy)\n * to promote interoperability.\n *\n * Each object is globally addressable via its {@link url | `url`}.\n *\n * An object's {@link channels | `channels`} and {@link allowed | `allowed`} properties\n * are set by an objects creator to shape the visibility of and access to their object.\n */\nexport interface GraffitiObjectBase {\n /**\n * The object's content as freeform JSON. We recommend using properties from the\n * [Activity Vocabulary](https://www.w3.org/TR/activitystreams-vocabulary/)\n * or properties that emerge in the Graffiti [folksonomy](https://en.wikipedia.org/wiki/Folksonomy)\n * to promote interoperability.\n */\n value: {};\n\n /**\n * An array of URIs the creator associates with the object. Objects can only be found by querying\n * one of the object's channels using the\n * {@link Graffiti.discover} method. This allows creators to express the intended audience of their object\n * which helps to prevent [context collapse](https://en.wikipedia.org/wiki/Context_collapse) even\n * in the highly interoperable ecosystem that Graffiti envisions. For example, channel URIs may be:\n * - A actor's own {@link actor | `actor`} URI. Posting an object to this channel is a way to broadcast\n * the object to the actor's followers, like posting a tweet.\n * - The URL of a Graffiti post. Posting an object to this channel is a way to broadcast to anyone viewing\n * the post, like commenting on a tweet.\n * - A URI representing a topic. Posting an object to this channel is a way to broadcast to anyone interested\n * in that topic, like posting in a subreddit.\n */\n channels: string[];\n\n /**\n * An optional array of {@link actor | `actor`} URIs that the creator allows to access the object.\n * If no `allowed` array is provided, the object can be accessed by anyone (so long as they\n * also know the right {@link channels | `channel` } to look in). An object can always be accessed by its creator, even if\n * the `allowed` array is empty.\n *\n * The `allowed` array is not revealed to actors other than the creator, like\n * a BCC email. An actor may choose to add a `to` property to the object's {@link value | `value`} to indicate\n * other recipients, however this is not enforced by Graffiti and may not accurately reflect the actual `allowed` array.\n *\n * `allowed` can be combined with {@link channels | `channels`}. For example, to send someone a direct message\n * the sender should post their object to the channel of the recipient's {@link actor | `actor`} URI to notify them of the message and also add\n * the recipient's {@link actor | `actor`} URI to the `allowed` array to prevent others from seeing the message.\n */\n allowed?: string[] | null;\n\n /**\n * The URI of the `actor` that {@link Graffiti.post | created } the object.\n * This `actor` has the unique permission to\n * {@link Graffiti.delete | delete} the object.\n *\n * We borrow the term actor from the ActivityPub because\n * [like in ActivityPub](https://www.w3.org/TR/activitypub/#h-note-0)\n * there is not necessarily a one-to-one mapping between actors and people/users.\n * Multiple people can share the same actor or one person can have multiple actors.\n * Actors can also be bots.\n *\n * In Graffiti, actors are always globally unique URIs which\n * allows them to also function as {@link channels | `channels`}.\n */\n actor: string;\n\n /**\n * A globally unique identifier and locator for the object. It can be used to point to\n * an object or to retrieve the object directly with {@link Graffiti.get}.\n *\n * An object's URL is generated when the object is first created and\n * should include sufficient randomness to prevent collisions\n * and guessing. The URL starts with a \"scheme,\" just like web URLs start with `http` or `https`, to indicate\n * to indicate the particular Graffiti implementation. This allows for applications\n * to pull from multiple coexisting Graffiti implementations without collision.\n * Existing schemes include `graffiti:local:` for objects stored locally\n * (see the [local implementation](https://github.com/graffiti-garden/implementation-local))\n * and `graffiti:remote:` for objects stored on Graffiti-specific web servers (see the [remote implementation](https://github.com/graffiti-garden/implementation-remote))\n * Options available in the future might include `graffiti:solid:` for objects stored on Solid servers\n * or `graffiti:p2p:` for objects stored on a peer-to-peer network.\n */\n url: string;\n}\n\n/**\n * This type constrains the {@link GraffitiObjectBase} type to adhere to a\n * particular [JSON schema](https://json-schema.org/).\n * This allows for static type-checking of an object's {@link GraffitiObjectBase.value | `value`}\n * which is otherwise a freeform JSON object.\n *\n * Schema-aware objects are returned by {@link Graffiti.get} and {@link Graffiti.discover}.\n */\nexport type GraffitiObject<Schema extends JSONSchema> = GraffitiObjectBase &\n FromSchema<Schema & typeof GraffitiObjectJSONSchema>;\n\n/**\n * A JSON Schema equivalent to the {@link GraffitiObjectBase} type.\n * Needed internally for type inference of JSON Schemas, but can\n * be used by implementations to validate objects.\n */\nexport const GraffitiObjectJSONSchema = {\n type: \"object\",\n properties: {\n value: { type: \"object\" },\n channels: { type: \"array\", items: { type: \"string\" } },\n allowed: { type: \"array\", items: { type: \"string\" }, nullable: true },\n url: { type: \"string\" },\n actor: { type: \"string\" },\n },\n additionalProperties: false,\n required: [\"value\", \"channels\", \"actor\", \"url\"],\n} as const satisfies JSONSchema;\n\n/**\n * This is an object containing only the {@link GraffitiObjectBase.url | `url`}\n * property of a {@link GraffitiObjectBase | GraffitiObject}.\n * It is used as a utility type so that applications can call\n * {@link Graffiti.delete} directly on an object\n * rather than on `object.url`.\n */\nexport type GraffitiObjectUrl = Pick<GraffitiObjectBase, \"url\">;\n\n/**\n * This object is a subset of {@link GraffitiObjectBase} that must be constructed locally before calling {@link Graffiti.post}.\n * This local copy ignores system-generated properties\n * ({@link GraffitiObjectBase.url | `url`} and {@link GraffitiObjectBase.actor | `actor`}),\n * and may be statically typed with\n * a [JSON schema](https://json-schema.org/) to prevent the accidental creation of erroneous objects.\n *\n * This local object must have a {@link GraffitiObjectBase.value | `value`} and {@link GraffitiObjectBase.channels | `channels`}\n * and may optionally have an {@link GraffitiObjectBase.allowed | `allowed`} property.\n */\nexport type GraffitiPostObject<Schema extends JSONSchema> = Pick<\n GraffitiObjectBase,\n \"value\" | \"channels\" | \"allowed\"\n> &\n Partial<GraffitiObjectBase> &\n FromSchema<Schema & typeof GraffitiPostObjectJSONSchema>;\n\n/**\n * A JSON Schema equivalent to the {@link GraffitiPostObject} type.\n * Needed internally for type inference of JSON Schemas, but can\n * be used by implementations to validate objects.\n */\nexport const GraffitiPostObjectJSONSchema = {\n ...GraffitiObjectJSONSchema,\n required: [\"value\", \"channels\"],\n} as const satisfies JSONSchema;\n\n/**\n * This object contains information that the underlying implementation can\n * use to authenticate a particular {@link GraffitiObjectBase.actor | `actor`}.\n * This object is required of all {@link Graffiti} methods\n * that modify objects or media and is optional for methods that read objects.\n *\n * At a minimum the `session` object must contain the\n * {@link GraffitiSession.actor | `actor`} URI to authenticate with.\n * However it is likely that the `session` object must contain other\n * implementation-specific properties.\n * For example, a Solid implementation might include a\n * [`fetch`](https://docs.inrupt.com/developer-tools/api/javascript/solid-client-authn-browser/functions.html#fetch)\n * function. A distributed implementation may include\n * a cryptographic signature.\n *\n * As to why the `session` object is passed as an argument to every method\n * rather than being an internal property of the {@link Graffiti} instance,\n * this is primarily for type-checking to catch bugs related to login state.\n * Graffiti applications can expose some functionality to people who are not logged in\n * with {@link Graffiti.get} and {@link Graffiti.discover} but without type-checking\n * the `session` it can be easy to forget to hide buttons that trigger\n * other methods that require login.\n *\n * Passing the `session` object per-method also allows for multiple sessions\n * to be used within the same application, like an Email client fetching from\n * multiple accounts.\n */\nexport interface GraffitiSession {\n /**\n * The {@link GraffitiObjectBase.actor | `actor`} to authenticate with.\n */\n actor: string;\n}\n\n/**\n * A stream of data that are returned by {@link Graffiti.discover}.\n *\n * Errors are returned within the stream rather than as\n * exceptions that would halt the entire stream. This is because\n * some implementations may pull data from multiple sources\n * including some that may be unreliable. In many cases,\n * these errors can be safely ignored.\n * See {@link GraffitiObjectStreamError}.\n *\n * The stream is an [`AsyncGenerator`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function)\n * that can be iterated over using `for await` loops or calling `next` on the generator.\n * The stream can be terminated by breaking out of a loop calling `return` on the generator.\n *\n * The stream ends by returning a {@link GraffitiObjectStreamReturn.continue | `continue`}\n * function and a {@link GraffitiObjectStreamReturn.cursor | `cursor`} string,\n * each of which can be used to resume the stream from where it left off.\n */\nexport type GraffitiObjectStream<Schema extends JSONSchema> = AsyncGenerator<\n GraffitiObjectStreamError | GraffitiObjectStreamEntry<Schema>,\n GraffitiObjectStreamReturn<Schema>\n>;\n\n/**\n * An error that can occur in a {@link GraffitiObjectStream}.\n *\n * @internal\n */\nexport interface GraffitiObjectStreamError {\n /**\n * The error that occurred while streaming data.\n */\n error: Error;\n /**\n * The origin that the error occurred. It will include\n * the scheme of the Graffiti implementation used and other\n * implementation-specific information like a hostname.\n */\n origin: string;\n}\n\n/**\n * A successful result from a {@link GraffitiObjectStream} or\n * {@link GraffitiObjectStreamContinue} that includes an object.\n *\n * @internal\n */\nexport interface GraffitiObjectStreamEntry<Schema extends JSONSchema> {\n /**\n * Empty property for compatibility with {@link GraffitiObjectStreamError}\n */\n error?: undefined;\n /**\n * Empty property for compatibility with {@link GraffitiObjectStreamContinueTombstone}\n */\n tombstone?: undefined;\n /**\n * The object returned by the stream.\n */\n object: GraffitiObject<Schema>;\n}\n\n/**\n * A result from a {@link GraffitiObjectStreamContinue} that indicated\n * an object has been deleted since the original stream was run.\n * Only sparse metadata about the deleted object is returned to respect\n * the deleting actor's privacy.\n *\n * @internal\n */\nexport interface GraffitiObjectStreamContinueTombstone {\n /**\n * Empty property for compatibility with {@link GraffitiObjectStreamError}\n */\n error?: undefined;\n /**\n * Use this property to differentiate a tombstone from a\n * {@link GraffitiObjectStreamEntry}.\n */\n tombstone: true;\n /**\n * Sparse metadata about the deleted object. The full object is not returned\n * to respect an actor's privacy.\n */\n object: {\n /**\n * The {@link GraffitiObjectBase.url | `url`} of the deleted object.\n */\n url: string;\n };\n}\n\n/**\n * A continuation of the {@link GraffitiObjectStream} type can include\n * both objects and tombstones of deleted objects.\n *\n * @internal\n */\nexport type GraffitiObjectStreamContinueEntry<Schema extends JSONSchema> =\n | GraffitiObjectStreamEntry<Schema>\n | GraffitiObjectStreamContinueTombstone;\n\n/**\n * The output of a {@link GraffitiObjectStream} or a {@link GraffitiObjectStreamContinue}\n * that allows the stream to be continued from where it left off.\n *\n * The {@link continue} function preserves the typing of the original stream,\n * where as the {@link cursor} string can be serialized for use after a person\n * has closed and reopened an application.\n *\n * The continued stream may include `tombstone`s of objects that have been\n * deleted since the original stream was run. See {@link GraffitiObjectStreamContinueTombstone}.\n * The continued stream may also return some objects that were already\n * returned by the original stream, depending on how much state the\n * underlying implementation is able to preserve.\n *\n * @internal\n */\nexport interface GraffitiObjectStreamReturn<Schema extends JSONSchema> {\n /**\n * @returns A function that creates new stream that continues from where the original stream left off.\n * It preserves the typing of the original stream.\n */\n continue: () => GraffitiObjectStreamContinue<Schema>;\n /**\n * A string that can be serialized and stored to resume the stream later.\n * It must be passed to the {@link Graffiti.continueDiscover} method\n * to resume the stream.\n */\n cursor: string;\n}\n\n/**\n * A continutation of the {@link GraffitiObjectStream} type, as returned by\n * the {@link GraffitiObjectStreamReturn.continue} or by using\n * {@link GraffitiObjectStreamReturn.cursor} with {@link Graffiti.continueDiscover}.\n *\n * The continued stream may include `tombstone`s of objects that have been\n * deleted since the original stream was run. See {@link GraffitiObjectStreamContinueTombstone}.\n *\n * @internal\n */\nexport type GraffitiObjectStreamContinue<Schema extends JSONSchema> =\n AsyncGenerator<\n GraffitiObjectStreamError | GraffitiObjectStreamContinueEntry<Schema>,\n GraffitiObjectStreamReturn<Schema>\n >;\n\n/**\n * The event type produced in {@link Graffiti.sessionEvents}\n * when a actor logs in manually from {@link Graffiti.login}\n * or when their session is restored from a previous login.\n * The event name to listen for is `login`.\n */\nexport type GraffitiLoginEvent = CustomEvent<\n | {\n error: Error;\n session?: undefined;\n }\n | {\n error?: undefined;\n session: GraffitiSession;\n }\n>;\n\n/**\n * The event type produced in {@link Graffiti.sessionEvents}\n * when a actor logs out either manually with {@link Graffiti.logout}\n * or when their session times out or otherwise becomes invalid.\n * The event name to listen for is `logout`.\n */\nexport type GraffitiLogoutEvent = CustomEvent<\n | {\n error: Error;\n actor?: string;\n }\n | {\n error?: undefined;\n actor: string;\n }\n>;\n\n/**\n * The event type produced in {@link Graffiti.sessionEvents}\n * after an application has attempted to complete any login redirects\n * and restore any previously active sessions.\n * Successful session restores will be returned in parallel as\n * their own {@link GraffitiLoginEvent} events.\n *\n * This event optionally returns an `href` property\n * representing the URL that originated a login request,\n * which may be useful for redirecting the user back to\n * the page they were on after login.\n * The event name to listen for is `initialized`.\n */\nexport type GraffitiSessionInitializedEvent = CustomEvent<\n | {\n error?: Error;\n href?: string;\n }\n | null\n | undefined\n>;\n", "export class GraffitiErrorUnauthorized extends Error {\n constructor(message?: string) {\n super(message);\n this.name = \"GraffitiErrorUnauthorized\";\n Object.setPrototypeOf(this, GraffitiErrorUnauthorized.prototype);\n }\n}\n\nexport class GraffitiErrorForbidden extends Error {\n constructor(message?: string) {\n super(message);\n this.name = \"GraffitiErrorForbidden\";\n Object.setPrototypeOf(this, GraffitiErrorForbidden.prototype);\n }\n}\n\nexport class GraffitiErrorNotFound extends Error {\n constructor(message?: string) {\n super(message);\n this.name = \"GraffitiErrorNotFound\";\n Object.setPrototypeOf(this, GraffitiErrorNotFound.prototype);\n }\n}\n\nexport class GraffitiErrorInvalidSchema extends Error {\n constructor(message?: string) {\n super(message);\n this.name = \"GraffitiErrorInvalidSchema\";\n Object.setPrototypeOf(this, GraffitiErrorInvalidSchema.prototype);\n }\n}\n\nexport class GraffitiErrorSchemaMismatch extends Error {\n constructor(message?: string) {\n super(message);\n this.name = \"GraffitiErrorSchemaMismatch\";\n Object.setPrototypeOf(this, GraffitiErrorSchemaMismatch.prototype);\n }\n}\n\nexport class GraffitiErrorTooLarge extends Error {\n constructor(message?: string) {\n super(message);\n this.name = \"GraffitiErrorTooLarge\";\n Object.setPrototypeOf(this, GraffitiErrorTooLarge.prototype);\n }\n}\n\nexport class GraffitiErrorNotAcceptable extends Error {\n constructor(message?: string) {\n super(message);\n this.name = \"GraffitiErrorNotAcceptable\";\n Object.setPrototypeOf(this, GraffitiErrorNotAcceptable.prototype);\n }\n}\n\nexport class GraffitiErrorInvalidUrl extends Error {\n constructor(message?: string) {\n super(message);\n this.name = \"GraffitiErrorInvalidUrl\";\n Object.setPrototypeOf(this, GraffitiErrorInvalidUrl.prototype);\n }\n}\n\nexport class GraffitiErrorUnrecognizedUrlScheme extends Error {\n constructor(message?: string) {\n super(message);\n this.name = \"GraffitiErrorUnrecognizedUriScheme\";\n Object.setPrototypeOf(this, GraffitiErrorUnrecognizedUrlScheme.prototype);\n }\n}\n"],
|
|
5
|
-
"mappings": "AA4GO,IAAeA,EAAf,KAAwB,
|
|
6
|
-
"names": ["Graffiti", "GraffitiObjectJSONSchema", "GraffitiPostObjectJSONSchema", "
|
|
3
|
+
"sources": ["../src/1-api.ts", "../src/2-types.ts", "../src/3-errors.ts", "../src/4-utilities.ts"],
|
|
4
|
+
"sourcesContent": ["import type {\n GraffitiObjectUrl,\n GraffitiObject,\n GraffitiObjectBase,\n GraffitiSession,\n GraffitiPostObject,\n GraffitiObjectStream,\n GraffitiObjectStreamContinue,\n} from \"./2-types\";\nimport type { JSONSchema } from \"json-schema-to-ts\";\n\n/**\n * This API describes a small but powerful set of methods that\n * can be used to create many different kinds of social applications,\n * from applications like Twitter, to Messenger, to Wikipedia, to many more new designs.\n * See the [Graffiti project website](https://graffiti.garden)\n * for links to example applications. Additionally, apps built on top\n * of the API interoperate with each other so you can seamlessly switch\n * between apps without losing your friends or data.\n *\n * These API methods should satisfy all of an application's needs for\n * the communication, storage, and access management of social data.\n * The rest of the application can be built with standard client-side\n * user interface tools to present and interact with that data\u2014no server code necessary!\n *\n * The Typescript code for this API is [open source on Github](https://github.com/graffiti-garden/api).\n *\n * There are several different implementations of this Graffiti API available,\n * including a [federated implementation](https://github.com/graffiti-garden/implementation-remote),\n * that lets people choose where their data is stored (you do not need to host your own server)\n * and a [local implementation](https://github.com/graffiti-garden/implementation-local)\n * that can be used for testing and development. Different implementations can\n * be swapped-in in the future without changing the API or any of the apps built on\n * top of it. In fact, we're working on an end-to-end encrypted version now!\n * [Follow Theia on BlueSky for updates](https://bsky.app/profile/theias.place).\n *\n * On the other side of the stack, there is [Vue plugin](https://vue.graffiti.garden/variables/GraffitiPlugin.html)\n * that wraps around this API to provide reactivity. Other plugin frameworks\n * and high-level libraries will be available in the future.\n *\n * ## API Overview\n *\n * The Graffiti API provides applications with methods for {@link login} and {@link logout},\n * methods to interact with data objects using standard database operations ({@link post}, {@link get}, and {@link delete}),\n * and a method to {@link discover} data objects created by others.\n * These data objects have a couple structured properties:\n * - {@link GraffitiObjectBase.url | `url`} (string): A globally unique identifier and locator for the object.\n * - {@link GraffitiObjectBase.actor | `actor`} (string): An unforgeable identifier for the creator of the object.\n * - {@link GraffitiObjectBase.allowed | `allowed`} (string[] | undefined): An array of the actors who are allowed to access the object (undefined for public objects).\n * - {@link GraffitiObjectBase.channels | `channels`} (string[]): An array of the *contexts* in which the object should appear.\n *\n * All other data is stored in the object's unstructured {@link GraffitiObjectBase.value | `value`} property.\n * This data can be used to represent social artifacts (e.g. posts, profiles) and activities (e.g. likes, follows).\n * For example, a post might have the value:\n\n * ```js\n * {\n * title: \"My First Post\",\n * content: \"Hello, world!\",\n * published: 1630483200000\n * }\n * ```\n *\n * a profile might have the value:\n *\n * ```js\n * {\n * name: \"Theia Henderson\",\n * pronouns: \"she/her\",\n * describes: \"did:web:theias.place\" // Theia's actor ID\n * }\n * ```\n *\n * and a \"Like\" might have the value:\n *\n * ```js\n * {\n * activity: \"Like\",\n * target: \"graffiti:remote:pod.graffiti.garden/12345\" // The URL of the graffiti object being liked\n * }\n * ```\n *\n * New social artifacts and activities can be easily created, simply\n * by creating new objects with appropriate properties. Despite the lack of\n * structure, we expect Graffiti object properties to adhere to a \"[folksonomy](https://en.wikipedia.org/wiki/Folksonomy)\",\n * similar to hashtags. Any string can be used as a hashtag on Twitter,\n * but there is social value in using the same hashtags at other people and\n * so a structure naturally emerges. Similarly, Graffiti objects\n * can have arbitrary properties but if people use the same properties as each other,\n * their apps will interoperate, which has social value.\n *\n * For a more complete and detailed overview of Graffiti's design, please\n * refer to [this section of the Graffiti paper](https://dl.acm.org/doi/10.1145/3746059.3747627#sec-3),\n * published in ACM UIST 2025. The paper also overviews {@link GraffitiObjectBase.channels | `channels`},\n * which are Graffiti's means of organizing data contextually, and a concept called \"total reification\",\n * which handles explains how moderation, collaboration, and other interactions are managed.\n *\n * @groupDescription 1 - Single-Object Methods\n * Methods for {@link post | creating}, {@link get | reading},\n * and {@link delete | deleting} {@link GraffitiObjectBase | Graffiti objects}.\n * @groupDescription 2 - Multi-Object Methods\n * Methods that retrieve or accumulate information about multiple {@link GraffitiObjectBase | Graffiti objects} at a time.\n * @groupDescription 3 - Media Methods\n * Methods for {@link postMedia | creating}, {@link getMedia | reading},\n * and {@link deleteMedia | deleting} media data.\n * @groupDescription 4 - Identity Methods\n * Methods and properties for logging in and out.\n */\nexport abstract class Graffiti {\n /**\n * Creates a new {@link GraffitiObjectBase | object}.\n *\n * @returns Returns the object that has been posted, complete with its\n * assigned {@link GraffitiObjectBase.url | `url`} and\n * {@link GraffitiObjectBase.actor | `actor`}.\n *\n * @group 1 - Single-Object Methods\n */\n abstract post<Schema extends JSONSchema>(\n /**\n * An object to post, minus its {@link GraffitiObjectBase.url | `url`} and\n * {@link GraffitiObjectBase.actor | `actor`}, which will be assigned once posted.\n * This object is statically type-checked against the [JSON schema](https://json-schema.org/) that can be optionally provided\n * as the generic type parameter. It is recommended to use a schema to\n * ensure that the posted object matches subsequent {@link get} or {@link discover}\n * methods.\n */\n partialObject: GraffitiPostObject<Schema>,\n /**\n * An implementation-specific object with information to authenticate the\n * {@link GraffitiObjectBase.actor | `actor`}.\n */\n session: GraffitiSession,\n ): Promise<GraffitiObject<Schema>>;\n\n /**\n * Retrieves an object from a given {@link GraffitiObjectBase.url | `url`} matching\n * the provided `schema`.\n *\n * If the retreiving {@link GraffitiObjectBase.actor | `actor`} is not\n * the object's `actor`,\n * the object's {@link GraffitiObjectBase.allowed | `allowed`} and\n * {@link GraffitiObjectBase.channels | `channels`} properties are\n * not revealed, similar to a BCC email.\n *\n * @returns Returns the retrieved object.\n *\n * @throws {@link GraffitiErrorNotFound} if the object does not exist, has been deleted, or the actor is not\n * {@link GraffitiObjectBase.allowed | `allowed`} to access it.\n *\n * @throws {@link GraffitiErrorSchemaMismatch} if the retrieved object does not match the provided schema.\n *\n * @group 1 - Single-Object Methods\n */\n abstract get<Schema extends JSONSchema>(\n /**\n * The location of the object to get.\n */\n url: string | GraffitiObjectUrl,\n /**\n * The JSON schema to validate the retrieved object against.\n */\n schema: Schema,\n /**\n * An implementation-specific object with information to authenticate the\n * {@link GraffitiObjectBase.actor | `actor`}. If no `session` is provided,\n * the retrieved object's {@link GraffitiObjectBase.allowed | `allowed`}\n * property must be `undefined`.\n */\n session?: GraffitiSession | null,\n ): Promise<GraffitiObject<Schema>>;\n\n /**\n * Deletes an object from a given {@link GraffitiObjectBase.url | `url`}\n * that had previously been {@link post | `post`ed}.\n * The deleting {@link GraffitiObjectBase.actor | `actor`} must be the same as the\n * `actor` that created the object.\n *\n * @throws {@link GraffitiErrorNotFound} if the object does not exist, has already been deleted,\n * or the actor is not {@link GraffitiObjectBase.allowed | `allowed`} to access it.\n *\n * @throws {@link GraffitiErrorForbidden} if the {@link GraffitiObjectBase.actor | `actor`}\n * is not the same `actor` as the one who created the object.\n *\n * @group 1 - Single-Object Methods\n */\n abstract delete(\n /**\n * The location of the object to delete.\n */\n url: string | GraffitiObjectUrl,\n /**\n * An implementation-specific object with information to authenticate the\n * {@link GraffitiObjectBase.actor | `actor`}.\n */\n session: GraffitiSession,\n ): Promise<void>;\n\n /**\n * Discovers objects created by any actor that are contained\n * in at least one of the given {@link GraffitiObjectBase.channels | `channels`}\n * and match the given [JSON Schema](https://json-schema.org).\n *\n * Objects are returned asynchronously as they are discovered but the stream\n * will end once all leads have been exhausted.\n * The {@link GraffitiObjectStream} ends by returning a\n * {@link GraffitiObjectStreamReturn.continue | `continue`} method and a\n * {@link GraffitiObjectStreamReturn.cursor | `cursor`} string,\n * each of which can be be used to poll for new objects.\n * The `continue` method preserves the type safety of the stream and the `cursor`\n * string can be serialized to continue the stream after an application is closed\n * and reopened.\n *\n * `discover` will not return objects that the querying {@link GraffitiObjectBase.actor | `actor`}\n * is not {@link GraffitiObjectBase.allowed | `allowed`} to access.\n * If the `actor` is not the creator of a discovered object,\n * the allowed list will be masked to only contain the querying actor if the\n * allowed list is not `undefined` (public). Additionally, if the actor is not the\n * creator of a discovered object, any {@link GraffitiObjectBase.channels | `channels`}\n * not specified by the `discover` method will not be revealed. This masking happens\n * before the object is validated against the supplied `schema`.\n *\n * Since different implementations may fetch data from multiple sources there is\n * no guarentee on the order that objects are returned in.\n *\n * @returns Returns a stream of objects that match the given {@link GraffitiObjectBase.channels | `channels`}\n * and [JSON Schema](https://json-schema.org).\n *\n * @group 2 - Multi-Object Methods\n */\n abstract discover<Schema extends JSONSchema>(\n /**\n * The {@link GraffitiObjectBase.channels | `channels`} that objects must be associated with.\n */\n channels: string[],\n /**\n * A [JSON Schema](https://json-schema.org) that objects must satisfy.\n */\n schema: Schema,\n /**\n * An implementation-specific object with information to authenticate the\n * {@link GraffitiObjectBase.actor | `actor`}. If no `session` is provided,\n * only objects that have no {@link GraffitiObjectBase.allowed | `allowed`}\n * property will be returned.\n */\n session?: GraffitiSession | null,\n ): GraffitiObjectStream<Schema>;\n\n /**\n * Continues a {@link GraffitiObjectStream} from a given\n * {@link GraffitiObjectStreamReturn.cursor | `cursor`} string.\n * The continuation will return new objects that have been {@link post | `post`ed}\n * that match the original stream, and also returns the\n * {@link GraffitiObjectBase.url | `url`}s of objects that\n * have been {@link delete | `delete`d}, as marked by a `tombstone`.\n *\n * The `cursor` allows the client to\n * serialize the state of the stream and continue it later.\n * However this method loses any typing information that was\n * present in the original stream. For better type safety\n * and when serializing is not necessary, use the\n * {@link GraffitiObjectStreamReturn.continue | `continue`} method\n * instead, which is returned along with the `cursor` at the\n * end of the original stream.\n *\n * @throws {@link GraffitiErrorForbidden} if the {@link GraffitiObjectBase.actor | `actor`}\n * provided in the `session` is not the same as the `actor`\n * that initiated the original stream.\n *\n * @group 2 - Multi-Object Methods\n */\n abstract continueDiscover(\n cursor: string,\n session?: GraffitiSession | null,\n ): GraffitiObjectStreamContinue<{}>;\n\n /**\n * Uploads media data, such as an image or video.\n *\n * Unlike structured {@link GraffitiObjectBase | objects},\n * media is not indexed for {@link discover | `discover`y} and\n * must be retrieved by its exact URL using {@link getMedia}\n *\n * @returns The URL that the media was posted to.\n *\n * @group 3 - Media Methods\n */\n abstract postMedia(\n media: {\n /**\n * The binary data of the media to be uploaded,\n * along with its [media type](https://www.iana.org/assignments/media-types/media-types.xhtml),\n * formatted as a [Blob](https://developer.mozilla.org/en-US/docs/Web/API/Blob).\n */\n data: Blob;\n /**\n * An optional list, identical in function to an object's\n * {@link GraffitiObjectBase.allowed | `allowed`} property,\n * that specifies the {@link GraffitiObjectBase.actor | `actor`}s\n * who are allowed to access the media. If the list is `undefined`\n * or `null`, anyone with the URL can access the media. If the list\n * is empty, only the {@link GraffitiObjectBase.actor | `actor`}\n * who {@link postMedia | `post`ed} the media can access it.\n */\n allowed?: string[] | null;\n },\n /**\n * An implementation-specific object with information to authenticate the\n * {@link GraffitiObjectBase.actor | `actor`}.\n */\n session: GraffitiSession,\n ): Promise<string>;\n\n /**\n * Deletes media previously {@link postMedia | `post`ed} to a given URL.\n *\n * @throws {@link GraffitiErrorNotFound} if no media at that URL exists.\n *\n * @throws {@link GraffitiErrorForbidden} if the {@link GraffitiObjectBase.actor | `actor`}\n * provided in the `session` is not the same as the `actor` that {@link postMedia | `post`ed}\n * the media.\n *\n * @group 3 - Media Methods\n */\n abstract deleteMedia(\n /**\n * A globally unique identifier and locator for the media.\n */\n mediaUrl: string,\n /**\n * An implementation-specific object with information to authenticate the\n * {@link GraffitiObjectBase.actor | `actor`}.\n */\n session: GraffitiSession,\n ): Promise<void>;\n\n /**\n * Retrieves media from the given media URL, adhering to the given requirements.\n *\n * @throws {@link GraffitiErrorNotFound} if no media at that URL exists.\n *\n * @throws {@link GraffitiErrorTooLarge} if the media exceeds the given `maxBytes`.\n *\n * @throws {@link GraffitiErrorNotAcceptable} if the media does not match the given\n * `accept` specification.\n *\n * @returns The URL of the retrieved media, as a [Blob](https://developer.mozilla.org/en-US/docs/Web/API/Blob)\n * and the {@link GraffitiObjectBase.actor | `actor`} that posted it.\n *\n * @group 3 - Media Methods\n */\n abstract getMedia(\n /**\n * A globally unique identifier and locator for the media.\n */\n mediaUrl: string,\n /**\n * A set of requirements the retrieved media must meet.\n */\n requirements: {\n /**\n * A list of acceptable media types for the retrieved media,\n * formatted as like an [HTTP Accept header](https://httpwg.org/specs/rfc9110.html#field.accept)\n */\n accept?: string;\n /**\n * The maximum acceptable size, in bytes, of the media.\n */\n maxBytes?: number;\n },\n /**\n * An implementation-specific object with information to authenticate the\n * {@link GraffitiObjectBase.actor | `actor`}.\n */\n session?: GraffitiSession | null,\n ): Promise<{\n data: Blob;\n actor: string;\n allowed?: string[] | null;\n }>;\n\n /**\n * Begins the login process. Depending on the implementation, this may\n * involve redirecting to a login page or opening a popup,\n * so it should always be called in response to a gesture, such as clicking\n * a button, due to the [feature-gating browser security feature](https://developer.mozilla.org/en-US/docs/Web/Security/User_activation).\n *\n * The {@link GraffitiSession | session} object is returned\n * asynchronously via {@link Graffiti.sessionEvents | sessionEvents}\n * as a {@link GraffitiLoginEvent} with event type `login`.\n *\n * @group 4 - Identity Methods\n */\n abstract login(\n /**\n * A suggested actor to login as. For example, if a user tries to\n * edit a post but are not logged in, the interface can infer that\n * they might want to log in as the actor who created the post\n * they are attempting to edit.\n *\n * Even if provided, the implementation should allow the user\n * to log in as a different actor if they choose.\n */\n actor?: string,\n ): Promise<void>;\n\n /**\n * Begins the logout process for a particular {@link GraffitiSession | session}. Depending on the implementation, this may\n * involve redirecting the user to a logout page or opening a popup,\n * so it should always be called in response to a gesture, such as clicking\n * a button, due to the [feature-gating browser security feature](https://developer.mozilla.org/en-US/docs/Web/Security/User_activation).\n *\n * A confirmation will be returned asynchronously via\n * {@link Graffiti.sessionEvents | sessionEvents}\n * as a {@link GraffitiLogoutEvent} as event type `logout`.\n *\n * @group 4 - Identity Methods\n */\n abstract logout(\n /**\n * The {@link GraffitiSession | session} object to logout.\n */\n session: GraffitiSession,\n ): Promise<void>;\n\n /**\n * An event target that can be used to listen for the following\n * events and their corresponding event types:\n * - `login` - {@link GraffitiLoginEvent}\n * - `logout` - {@link GraffitiLogoutEvent}\n * - `initialized` - {@link GraffitiSessionInitializedEvent}\n *\n * @group 4 - Identity Methods\n */\n abstract readonly sessionEvents: EventTarget;\n\n /**\n * Retrieves the human-readable handle associated\n * with the given actor. The handle may change over time\n * and so it should be used for display purposes only.\n *\n * The inverse of {@link handleToActor}.\n *\n * @throws {@link GraffitiErrorNotFound} if a handle cannot be\n * found for the given actor.\n *\n * @returns A human-readable handle for the given actor.\n *\n * @group 4 - Identity Methods\n */\n abstract actorToHandle(actor: string): Promise<string>;\n\n /**\n * Retrieves the actor ID associated with the given handle.\n *\n * The inverse of {@link actorToHandle}.\n *\n * @throws {@link GraffitiErrorNotFound} if there is no actor\n * with the given handle.\n *\n * @returns The actor ID for the given handle.\n *\n * @group 4 - Identity Methods\n */\n abstract handleToActor(handle: string): Promise<string>;\n}\n", "import type { JSONSchema, FromSchema } from \"json-schema-to-ts\";\n\n/**\n * Objects are the atomic unit in Graffiti that can represent both data (*e.g.* a social media post or profile)\n * and activities (*e.g.* a like or follow).\n *\n * Each object embeds the {@link actor | `actor`} that created it.\n * Object content and metadata are static but an object may be deleted by\n * its creating {@link actor | `actor`}.\n *\n * An object's content is stored in its {@link value | `value`} property, which can be any JSON\n * object. However, it is recommended to use properties from the\n * [Activity Vocabulary](https://www.w3.org/TR/activitystreams-vocabulary/)\n * or properties that emerge in the Graffiti [folksonomy](https://en.wikipedia.org/wiki/Folksonomy)\n * to promote interoperability.\n *\n * Each object is globally addressable via its {@link url | `url`}.\n *\n * An object's {@link channels | `channels`} and {@link allowed | `allowed`} properties\n * are set by an objects creator to shape the visibility of and access to their object.\n */\nexport interface GraffitiObjectBase {\n /**\n * The object's content as freeform JSON. We recommend using properties from the\n * [Activity Vocabulary](https://www.w3.org/TR/activitystreams-vocabulary/)\n * or properties that emerge in the Graffiti [folksonomy](https://en.wikipedia.org/wiki/Folksonomy)\n * to promote interoperability.\n */\n value: {};\n\n /**\n * An array of URIs the creator associates with the object. Objects can only be found by querying\n * one of the object's channels using the\n * {@link Graffiti.discover} method. This allows creators to express the intended audience of their object\n * which helps to prevent [context collapse](https://en.wikipedia.org/wiki/Context_collapse) even\n * in the highly interoperable ecosystem that Graffiti envisions. For example, channel URIs may be:\n * - A actor's own {@link actor | `actor`} URI. Posting an object to this channel is a way to broadcast\n * the object to the actor's followers, like posting a tweet.\n * - The URL of a Graffiti post. Posting an object to this channel is a way to broadcast to anyone viewing\n * the post, like commenting on a tweet.\n * - A URI representing a topic. Posting an object to this channel is a way to broadcast to anyone interested\n * in that topic, like posting in a subreddit.\n */\n channels: string[];\n\n /**\n * An optional array of {@link actor | `actor`} URIs that the creator allows to access the object.\n * If no `allowed` array is provided, the object can be accessed by anyone (so long as they\n * also know the right {@link channels | `channel` } to look in). An object can always be accessed by its creator, even if\n * the `allowed` array is empty.\n *\n * The `allowed` array is not revealed to actors other than the creator, like\n * a BCC email. An actor may choose to add a `to` property to the object's {@link value | `value`} to indicate\n * other recipients, however this is not enforced by Graffiti and may not accurately reflect the actual `allowed` array.\n *\n * `allowed` can be combined with {@link channels | `channels`}. For example, to send someone a direct message\n * the sender should post their object to the channel of the recipient's {@link actor | `actor`} URI to notify them of the message and also add\n * the recipient's {@link actor | `actor`} URI to the `allowed` array to prevent others from seeing the message.\n */\n allowed?: string[] | null;\n\n /**\n * The URI of the `actor` that {@link Graffiti.post | created } the object.\n * This `actor` has the unique permission to\n * {@link Graffiti.delete | delete} the object.\n *\n * We borrow the term actor from the ActivityPub because\n * [like in ActivityPub](https://www.w3.org/TR/activitypub/#h-note-0)\n * there is not necessarily a one-to-one mapping between actors and people/users.\n * Multiple people can share the same actor or one person can have multiple actors.\n * Actors can also be bots.\n *\n * In Graffiti, actors are always globally unique URIs which\n * allows them to also function as {@link channels | `channels`}.\n */\n actor: string;\n\n /**\n * A globally unique identifier and locator for the object. It can be used to point to\n * an object or to retrieve the object directly with {@link Graffiti.get}.\n *\n * An object's URL is generated when the object is first created and\n * should include sufficient randomness to prevent collisions\n * and guessing. The URL starts with a \"scheme,\" just like web URLs start with `http` or `https`, to indicate\n * to indicate the particular Graffiti implementation. This allows for applications\n * to pull from multiple coexisting Graffiti implementations without collision.\n * Existing schemes include `graffiti:local:` for objects stored locally\n * (see the [local implementation](https://github.com/graffiti-garden/implementation-local))\n * and `graffiti:remote:` for objects stored on Graffiti-specific web servers (see the [remote implementation](https://github.com/graffiti-garden/implementation-remote))\n * Options available in the future might include `graffiti:solid:` for objects stored on Solid servers\n * or `graffiti:p2p:` for objects stored on a peer-to-peer network.\n */\n url: string;\n}\n\n/**\n * This type constrains the {@link GraffitiObjectBase} type to adhere to a\n * particular [JSON schema](https://json-schema.org/).\n * This allows for static type-checking of an object's {@link GraffitiObjectBase.value | `value`}\n * which is otherwise a freeform JSON object.\n *\n * Schema-aware objects are returned by {@link Graffiti.get} and {@link Graffiti.discover}.\n */\nexport type GraffitiObject<Schema extends JSONSchema> = GraffitiObjectBase &\n FromSchema<Schema & typeof GraffitiPostObjectJSONSchema>;\n\n/**\n * A JSON Schema equivalent to the {@link GraffitiObjectBase} type.\n * Needed internally for type inference of JSON Schemas, but can\n * be used by implementations to validate objects.\n */\nexport const GraffitiObjectJSONSchema = {\n type: \"object\",\n properties: {\n value: { type: \"object\" },\n channels: { type: \"array\", items: { type: \"string\" } },\n allowed: { type: \"array\", items: { type: \"string\" }, nullable: true },\n url: { type: \"string\" },\n actor: { type: \"string\" },\n },\n additionalProperties: false,\n required: [\"value\", \"channels\", \"actor\", \"url\"],\n} as const satisfies JSONSchema;\n\n/**\n * This is an object containing only the {@link GraffitiObjectBase.url | `url`}\n * property of a {@link GraffitiObjectBase | GraffitiObject}.\n * It is used as a utility type so that applications can call\n * {@link Graffiti.delete} directly on an object\n * rather than on `object.url`.\n */\nexport type GraffitiObjectUrl = Pick<GraffitiObjectBase, \"url\">;\n\n/**\n * This object is a subset of {@link GraffitiObjectBase} that must be constructed locally before calling {@link Graffiti.post}.\n * This local copy ignores system-generated properties\n * ({@link GraffitiObjectBase.url | `url`} and {@link GraffitiObjectBase.actor | `actor`}),\n * and may be statically typed with\n * a [JSON schema](https://json-schema.org/) to prevent the accidental creation of erroneous objects.\n *\n * This local object must have a {@link GraffitiObjectBase.value | `value`} and {@link GraffitiObjectBase.channels | `channels`}\n * and may optionally have an {@link GraffitiObjectBase.allowed | `allowed`} property.\n */\nexport type GraffitiPostObject<Schema extends JSONSchema> = Pick<\n GraffitiObjectBase,\n \"value\" | \"channels\" | \"allowed\"\n> &\n FromSchema<Schema & typeof GraffitiPostObjectJSONSchema>;\n\n/**\n * A JSON Schema equivalent to the {@link GraffitiPostObject} type.\n * Needed internally for type inference of JSON Schemas, but can\n * be used by implementations to validate objects.\n */\nexport const GraffitiPostObjectJSONSchema = {\n ...GraffitiObjectJSONSchema,\n required: [\"value\", \"channels\"],\n} as const satisfies JSONSchema;\n\n/**\n * This object contains information that the underlying implementation can\n * use to authenticate a particular {@link GraffitiObjectBase.actor | `actor`}.\n * This object is required of all {@link Graffiti} methods\n * that modify objects or media and is optional for methods that read objects.\n *\n * At a minimum the `session` object must contain the\n * {@link GraffitiSession.actor | `actor`} URI to authenticate with.\n * However it is likely that the `session` object must contain other\n * implementation-specific properties.\n * For example, a Solid implementation might include a\n * [`fetch`](https://docs.inrupt.com/developer-tools/api/javascript/solid-client-authn-browser/functions.html#fetch)\n * function. A distributed implementation may include\n * a cryptographic signature.\n *\n * As to why the `session` object is passed as an argument to every method\n * rather than being an internal property of the {@link Graffiti} instance,\n * this is primarily for type-checking to catch bugs related to login state.\n * Graffiti applications can expose some functionality to people who are not logged in\n * with {@link Graffiti.get} and {@link Graffiti.discover} but without type-checking\n * the `session` it can be easy to forget to hide buttons that trigger\n * other methods that require login.\n *\n * Passing the `session` object per-method also allows for multiple sessions\n * to be used within the same application, like an Email client fetching from\n * multiple accounts.\n */\nexport interface GraffitiSession {\n /**\n * The {@link GraffitiObjectBase.actor | `actor`} to authenticate with.\n */\n actor: string;\n}\n\n/**\n * A stream of data that are returned by {@link Graffiti.discover}.\n *\n * Errors are returned within the stream rather than as\n * exceptions that would halt the entire stream. This is because\n * some implementations may pull data from multiple sources\n * including some that may be unreliable. In many cases,\n * these errors can be safely ignored.\n * See {@link GraffitiObjectStreamError}.\n *\n * The stream is an [`AsyncGenerator`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function)\n * that can be iterated over using `for await` loops or calling `next` on the generator.\n * The stream can be terminated by breaking out of a loop calling `return` on the generator.\n *\n * The stream ends by returning a {@link GraffitiObjectStreamReturn.continue | `continue`}\n * function and a {@link GraffitiObjectStreamReturn.cursor | `cursor`} string,\n * each of which can be used to resume the stream from where it left off.\n */\nexport type GraffitiObjectStream<Schema extends JSONSchema> = AsyncGenerator<\n GraffitiObjectStreamError | GraffitiObjectStreamEntry<Schema>,\n GraffitiObjectStreamReturn<Schema>\n>;\n\n/**\n * An error that can occur in a {@link GraffitiObjectStream}.\n *\n * @internal\n */\nexport interface GraffitiObjectStreamError {\n /**\n * The error that occurred while streaming data.\n */\n error: Error;\n /**\n * The origin that the error occurred. It will include\n * the scheme of the Graffiti implementation used and other\n * implementation-specific information like a hostname.\n */\n origin: string;\n}\n\n/**\n * A successful result from a {@link GraffitiObjectStream} or\n * {@link GraffitiObjectStreamContinue} that includes an object.\n *\n * @internal\n */\nexport interface GraffitiObjectStreamEntry<Schema extends JSONSchema> {\n /**\n * Empty property for compatibility with {@link GraffitiObjectStreamError}\n */\n error?: undefined;\n /**\n * Empty property for compatibility with {@link GraffitiObjectStreamContinueTombstone}\n */\n tombstone?: undefined;\n /**\n * The object returned by the stream.\n */\n object: GraffitiObject<Schema>;\n}\n\n/**\n * A result from a {@link GraffitiObjectStreamContinue} that indicated\n * an object has been deleted since the original stream was run.\n * Only sparse metadata about the deleted object is returned to respect\n * the deleting actor's privacy.\n *\n * @internal\n */\nexport interface GraffitiObjectStreamContinueTombstone {\n /**\n * Empty property for compatibility with {@link GraffitiObjectStreamError}\n */\n error?: undefined;\n /**\n * Use this property to differentiate a tombstone from a\n * {@link GraffitiObjectStreamEntry}.\n */\n tombstone: true;\n /**\n * Sparse metadata about the deleted object. The full object is not returned\n * to respect an actor's privacy.\n */\n object: {\n /**\n * The {@link GraffitiObjectBase.url | `url`} of the deleted object.\n */\n url: string;\n };\n}\n\n/**\n * A continuation of the {@link GraffitiObjectStream} type can include\n * both objects and tombstones of deleted objects.\n *\n * @internal\n */\nexport type GraffitiObjectStreamContinueEntry<Schema extends JSONSchema> =\n | GraffitiObjectStreamEntry<Schema>\n | GraffitiObjectStreamContinueTombstone;\n\n/**\n * The output of a {@link GraffitiObjectStream} or a {@link GraffitiObjectStreamContinue}\n * that allows the stream to be continued from where it left off.\n *\n * The {@link continue} function preserves the typing of the original stream,\n * where as the {@link cursor} string can be serialized for use after a person\n * has closed and reopened an application.\n *\n * The continued stream may include `tombstone`s of objects that have been\n * deleted since the original stream was run. See {@link GraffitiObjectStreamContinueTombstone}.\n * The continued stream may also return some objects that were already\n * returned by the original stream, depending on how much state the\n * underlying implementation is able to preserve.\n *\n * @internal\n */\nexport interface GraffitiObjectStreamReturn<Schema extends JSONSchema> {\n /**\n * @returns A function that creates new stream that continues from where the original stream left off.\n * It preserves the typing of the original stream.\n */\n continue: (\n session?: GraffitiSession | null,\n ) => GraffitiObjectStreamContinue<Schema>;\n /**\n * A string that can be serialized and stored to resume the stream later.\n * It must be passed to the {@link Graffiti.continueDiscover} method\n * to resume the stream.\n */\n cursor: string;\n}\n\n/**\n * A continutation of the {@link GraffitiObjectStream} type, as returned by\n * the {@link GraffitiObjectStreamReturn.continue} or by using\n * {@link GraffitiObjectStreamReturn.cursor} with {@link Graffiti.continueDiscover}.\n *\n * The continued stream may include `tombstone`s of objects that have been\n * deleted since the original stream was run. See {@link GraffitiObjectStreamContinueTombstone}.\n *\n * @internal\n */\nexport type GraffitiObjectStreamContinue<Schema extends JSONSchema> =\n AsyncGenerator<\n GraffitiObjectStreamError | GraffitiObjectStreamContinueEntry<Schema>,\n GraffitiObjectStreamReturn<Schema>\n >;\n\n/**\n * The event type produced in {@link Graffiti.sessionEvents}\n * when a actor logs in manually from {@link Graffiti.login}\n * or when their session is restored from a previous login.\n * The event name to listen for is `login`.\n */\nexport type GraffitiLoginEvent = CustomEvent<\n | {\n error: Error;\n session?: undefined;\n }\n | {\n error?: undefined;\n session: GraffitiSession;\n }\n>;\n\n/**\n * The event type produced in {@link Graffiti.sessionEvents}\n * when a actor logs out either manually with {@link Graffiti.logout}\n * or when their session times out or otherwise becomes invalid.\n * The event name to listen for is `logout`.\n */\nexport type GraffitiLogoutEvent = CustomEvent<\n | {\n error: Error;\n actor?: string;\n }\n | {\n error?: undefined;\n actor: string;\n }\n>;\n\n/**\n * The event type produced in {@link Graffiti.sessionEvents}\n * after an application has attempted to complete any login redirects\n * and restore any previously active sessions.\n * Successful session restores will be returned in parallel as\n * their own {@link GraffitiLoginEvent} events.\n *\n * This event optionally returns an `href` property\n * representing the URL that originated a login request,\n * which may be useful for redirecting the user back to\n * the page they were on after login.\n * The event name to listen for is `initialized`.\n */\nexport type GraffitiSessionInitializedEvent = CustomEvent<\n | {\n error?: Error;\n href?: string;\n }\n | null\n | undefined\n>;\n", "export class GraffitiErrorForbidden extends Error {\n constructor(message?: string) {\n super(message);\n this.name = \"GraffitiErrorForbidden\";\n Object.setPrototypeOf(this, GraffitiErrorForbidden.prototype);\n }\n}\n\nexport class GraffitiErrorNotFound extends Error {\n constructor(message?: string) {\n super(message);\n this.name = \"GraffitiErrorNotFound\";\n Object.setPrototypeOf(this, GraffitiErrorNotFound.prototype);\n }\n}\n\nexport class GraffitiErrorInvalidSchema extends Error {\n constructor(message?: string) {\n super(message);\n this.name = \"GraffitiErrorInvalidSchema\";\n Object.setPrototypeOf(this, GraffitiErrorInvalidSchema.prototype);\n }\n}\n\nexport class GraffitiErrorSchemaMismatch extends Error {\n constructor(message?: string) {\n super(message);\n this.name = \"GraffitiErrorSchemaMismatch\";\n Object.setPrototypeOf(this, GraffitiErrorSchemaMismatch.prototype);\n }\n}\n\nexport class GraffitiErrorTooLarge extends Error {\n constructor(message?: string) {\n super(message);\n this.name = \"GraffitiErrorTooLarge\";\n Object.setPrototypeOf(this, GraffitiErrorTooLarge.prototype);\n }\n}\n\nexport class GraffitiErrorNotAcceptable extends Error {\n constructor(message?: string) {\n super(message);\n this.name = \"GraffitiErrorNotAcceptable\";\n Object.setPrototypeOf(this, GraffitiErrorNotAcceptable.prototype);\n }\n}\n", "import type { JSONSchema } from \"json-schema-to-ts\";\nimport type { Ajv } from \"ajv\";\nimport { GraffitiErrorInvalidSchema } from \"./3-errors\";\nimport type {\n GraffitiObjectBase,\n GraffitiObject,\n GraffitiObjectUrl,\n GraffitiSession,\n} from \"./2-types\";\n\nexport function unpackObjectUrl(url: string | GraffitiObjectUrl) {\n return typeof url === \"string\" ? url : url.url;\n}\n\nexport function compileGraffitiObjectSchema<Schema extends JSONSchema>(\n ajv: Ajv,\n schema: Schema,\n) {\n try {\n // Force the validation guard because\n // it is too big for the type checker.\n // Fortunately json-schema-to-ts is\n // well tested against ajv.\n return ajv.compile(schema) as (\n data: GraffitiObjectBase,\n ) => data is GraffitiObject<Schema>;\n } catch (error) {\n throw new GraffitiErrorInvalidSchema(\n error instanceof Error ? error.message : undefined,\n );\n }\n}\n\nexport function isActorAllowedGraffitiObject(\n object: GraffitiObjectBase,\n session?: GraffitiSession | null,\n) {\n return (\n // If there is no allowed list, the actor is allowed.\n !Array.isArray(object.allowed) ||\n // Otherwise...\n (typeof session?.actor === \"string\" &&\n // The actor must be the creator of the object\n (object.actor === session.actor ||\n // Or be on the allowed list\n object.allowed.includes(session.actor)))\n );\n}\n\nexport function maskGraffitiObject(\n object: GraffitiObjectBase,\n channels: string[],\n session?: GraffitiSession | null,\n): void {\n // If the actor is not the creator, mask the object.\n if (object.actor !== session?.actor) {\n // If there is an allowed list, mask it to only include the actor\n // (This assumes the actor is already allowed to access the object)\n object.allowed = object.allowed && session ? [session.actor] : undefined;\n // Mask the channels to only include the channels that are being queried\n object.channels = object.channels.filter((channel) =>\n channels.includes(channel),\n );\n }\n}\n"],
|
|
5
|
+
"mappings": "AA4GO,IAAeA,EAAf,KAAwB,CAqW/B,EClWO,IAAMC,EAA2B,CACtC,KAAM,SACN,WAAY,CACV,MAAO,CAAE,KAAM,QAAS,EACxB,SAAU,CAAE,KAAM,QAAS,MAAO,CAAE,KAAM,QAAS,CAAE,EACrD,QAAS,CAAE,KAAM,QAAS,MAAO,CAAE,KAAM,QAAS,EAAG,SAAU,EAAK,EACpE,IAAK,CAAE,KAAM,QAAS,EACtB,MAAO,CAAE,KAAM,QAAS,CAC1B,EACA,qBAAsB,GACtB,SAAU,CAAC,QAAS,WAAY,QAAS,KAAK,CAChD,EAgCaC,EAA+B,CAC1C,GAAGD,EACH,SAAU,CAAC,QAAS,UAAU,CAChC,EC7JO,IAAME,EAAN,MAAMC,UAA+B,KAAM,CAChD,YAAYC,EAAkB,CAC5B,MAAMA,CAAO,EACb,KAAK,KAAO,yBACZ,OAAO,eAAe,KAAMD,EAAuB,SAAS,CAC9D,CACF,EAEaE,EAAN,MAAMC,UAA8B,KAAM,CAC/C,YAAYF,EAAkB,CAC5B,MAAMA,CAAO,EACb,KAAK,KAAO,wBACZ,OAAO,eAAe,KAAME,EAAsB,SAAS,CAC7D,CACF,EAEaC,EAAN,MAAMC,UAAmC,KAAM,CACpD,YAAYJ,EAAkB,CAC5B,MAAMA,CAAO,EACb,KAAK,KAAO,6BACZ,OAAO,eAAe,KAAMI,EAA2B,SAAS,CAClE,CACF,EAEaC,EAAN,MAAMC,UAAoC,KAAM,CACrD,YAAYN,EAAkB,CAC5B,MAAMA,CAAO,EACb,KAAK,KAAO,8BACZ,OAAO,eAAe,KAAMM,EAA4B,SAAS,CACnE,CACF,EAEaC,EAAN,MAAMC,UAA8B,KAAM,CAC/C,YAAYR,EAAkB,CAC5B,MAAMA,CAAO,EACb,KAAK,KAAO,wBACZ,OAAO,eAAe,KAAMQ,EAAsB,SAAS,CAC7D,CACF,EAEaC,EAAN,MAAMC,UAAmC,KAAM,CACpD,YAAYV,EAAkB,CAC5B,MAAMA,CAAO,EACb,KAAK,KAAO,6BACZ,OAAO,eAAe,KAAMU,EAA2B,SAAS,CAClE,CACF,ECpCO,SAASC,EAAgBC,EAAiC,CAC/D,OAAO,OAAOA,GAAQ,SAAWA,EAAMA,EAAI,GAC7C,CAEO,SAASC,EACdC,EACAC,EACA,CACA,GAAI,CAKF,OAAOD,EAAI,QAAQC,CAAM,CAG3B,OAASC,EAAO,CACd,MAAM,IAAIC,EACRD,aAAiB,MAAQA,EAAM,QAAU,MAC3C,CACF,CACF,CAEO,SAASE,EACdC,EACAC,EACA,CACA,MAEE,CAAC,MAAM,QAAQD,EAAO,OAAO,GAE5B,OAAOC,GAAS,OAAU,WAExBD,EAAO,QAAUC,EAAQ,OAExBD,EAAO,QAAQ,SAASC,EAAQ,KAAK,EAE7C,CAEO,SAASC,EACdF,EACAG,EACAF,EACM,CAEFD,EAAO,QAAUC,GAAS,QAG5BD,EAAO,QAAUA,EAAO,SAAWC,EAAU,CAACA,EAAQ,KAAK,EAAI,OAE/DD,EAAO,SAAWA,EAAO,SAAS,OAAQI,GACxCD,EAAS,SAASC,CAAO,CAC3B,EAEJ",
|
|
6
|
+
"names": ["Graffiti", "GraffitiObjectJSONSchema", "GraffitiPostObjectJSONSchema", "GraffitiErrorForbidden", "_GraffitiErrorForbidden", "message", "GraffitiErrorNotFound", "_GraffitiErrorNotFound", "GraffitiErrorInvalidSchema", "_GraffitiErrorInvalidSchema", "GraffitiErrorSchemaMismatch", "_GraffitiErrorSchemaMismatch", "GraffitiErrorTooLarge", "_GraffitiErrorTooLarge", "GraffitiErrorNotAcceptable", "_GraffitiErrorNotAcceptable", "unpackObjectUrl", "url", "compileGraffitiObjectSchema", "ajv", "schema", "error", "GraffitiErrorInvalidSchema", "isActorAllowedGraffitiObject", "object", "session", "maskGraffitiObject", "channels", "channel"]
|
|
7
7
|
}
|