@graffiti-garden/implementation-local 0.6.4 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -1
- package/dist/browser/ajv-IY2ZY7VT.js +9 -0
- package/dist/browser/ajv-IY2ZY7VT.js.map +7 -0
- package/dist/browser/{chunk-KNUPPOQC.js → chunk-GE6AZATH.js} +2 -2
- package/dist/browser/{chunk-KNUPPOQC.js.map → chunk-GE6AZATH.js.map} +1 -1
- package/dist/browser/{index-browser.es-G37SKL53.js → index-browser.es-UXYPGJ2M.js} +2 -2
- package/dist/browser/{index-browser.es-G37SKL53.js.map → index-browser.es-UXYPGJ2M.js.map} +1 -1
- package/dist/browser/index.js +11 -2
- package/dist/browser/index.js.map +4 -4
- package/dist/cjs/identity.js +112 -0
- package/dist/cjs/identity.js.map +7 -0
- package/dist/cjs/index.js +43 -22
- package/dist/cjs/index.js.map +2 -2
- package/dist/cjs/media.js +111 -0
- package/dist/cjs/media.js.map +7 -0
- package/dist/cjs/objects.js +307 -0
- package/dist/cjs/objects.js.map +7 -0
- package/dist/cjs/tests.spec.js +1 -2
- package/dist/cjs/tests.spec.js.map +2 -2
- package/dist/cjs/utilities.js +68 -43
- package/dist/cjs/utilities.js.map +2 -2
- package/dist/esm/identity.js +92 -0
- package/dist/esm/identity.js.map +7 -0
- package/dist/esm/index.js +43 -24
- package/dist/esm/index.js.map +2 -2
- package/dist/esm/media.js +91 -0
- package/dist/esm/media.js.map +7 -0
- package/dist/esm/objects.js +285 -0
- package/dist/esm/objects.js.map +7 -0
- package/dist/esm/tests.spec.js +2 -4
- package/dist/esm/tests.spec.js.map +2 -2
- package/dist/esm/utilities.js +69 -48
- package/dist/esm/utilities.js.map +2 -2
- package/dist/{session-manager.d.ts → identity.d.ts} +7 -5
- package/dist/identity.d.ts.map +1 -0
- package/dist/index.d.ts +15 -13
- package/dist/index.d.ts.map +1 -1
- package/dist/media.d.ts +9 -0
- package/dist/media.d.ts.map +1 -0
- package/dist/objects.d.ts +63 -0
- package/dist/objects.d.ts.map +1 -0
- package/dist/utilities.d.ts +19 -8
- package/dist/utilities.d.ts.map +1 -1
- package/package.json +31 -19
- package/src/identity.ts +131 -0
- package/src/index.ts +44 -29
- package/src/media.ts +106 -0
- package/src/objects.ts +432 -0
- package/src/tests.spec.ts +2 -4
- package/src/utilities.ts +67 -87
- package/dist/browser/ajv-6AI3HK2A.js +0 -9
- package/dist/browser/ajv-6AI3HK2A.js.map +0 -7
- package/dist/browser/fast-json-patch-ZE7SZEYK.js +0 -19
- package/dist/browser/fast-json-patch-ZE7SZEYK.js.map +0 -7
- package/dist/cjs/database.js +0 -626
- package/dist/cjs/database.js.map +0 -7
- package/dist/cjs/session-manager.js +0 -107
- package/dist/cjs/session-manager.js.map +0 -7
- package/dist/database.d.ts +0 -106
- package/dist/database.d.ts.map +0 -1
- package/dist/esm/database.js +0 -608
- package/dist/esm/database.js.map +0 -7
- package/dist/esm/session-manager.js +0 -87
- package/dist/esm/session-manager.js.map +0 -7
- package/dist/session-manager.d.ts.map +0 -1
- package/src/database.ts +0 -921
- package/src/session-manager.ts +0 -123
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../../src/index.ts", "../../node_modules/@graffiti-garden/api/src/1-api.ts", "../../node_modules/@graffiti-garden/api/src/2-types.ts", "../../node_modules/@graffiti-garden/api/src/3-errors.ts", "../../src/
|
|
4
|
-
"sourcesContent": ["import { Graffiti, type GraffitiSession } from \"@graffiti-garden/api\";\nimport { GraffitiLocalSessionManager } from \"./session-manager.js\";\nimport {\n GraffitiLocalDatabase,\n type GraffitiLocalOptions,\n} from \"./database.js\";\n\nexport type { GraffitiLocalOptions };\n\n/**\n * A local implementation of the [Graffiti API](https://api.graffiti.garden/classes/Graffiti.html)\n * based on [PouchDB](https://pouchdb.com/). PouchDb will automatically persist data in a local\n * database, either in the browser or in Node.js.\n * It can also be configured to work with an external [CouchDB](https://couchdb.apache.org/) server,\n * although using it with a remote server will not be secure.\n */\nexport class GraffitiLocal extends Graffiti {\n protected sessionManagerLocal = new GraffitiLocalSessionManager();\n login = this.sessionManagerLocal.login.bind(this.sessionManagerLocal);\n logout = this.sessionManagerLocal.logout.bind(this.sessionManagerLocal);\n sessionEvents = this.sessionManagerLocal.sessionEvents;\n\n put: Graffiti[\"put\"];\n get: Graffiti[\"get\"];\n patch: Graffiti[\"patch\"];\n delete: Graffiti[\"delete\"];\n discover: Graffiti[\"discover\"];\n recoverOrphans: Graffiti[\"recoverOrphans\"];\n channelStats: Graffiti[\"channelStats\"];\n continueObjectStream: Graffiti[\"continueObjectStream\"];\n\n constructor(options?: GraffitiLocalOptions) {\n super();\n\n const graffitiPouchDbBase = new GraffitiLocalDatabase(options);\n\n this.put = graffitiPouchDbBase.put.bind(graffitiPouchDbBase);\n this.get = graffitiPouchDbBase.get.bind(graffitiPouchDbBase);\n this.patch = graffitiPouchDbBase.patch.bind(graffitiPouchDbBase);\n this.delete = graffitiPouchDbBase.delete.bind(graffitiPouchDbBase);\n this.discover = graffitiPouchDbBase.discover.bind(graffitiPouchDbBase);\n this.recoverOrphans =\n graffitiPouchDbBase.recoverOrphans.bind(graffitiPouchDbBase);\n this.channelStats =\n graffitiPouchDbBase.channelStats.bind(graffitiPouchDbBase);\n this.continueObjectStream =\n graffitiPouchDbBase.continueObjectStream.bind(graffitiPouchDbBase);\n }\n}\n", "import type {\n GraffitiObjectUrl,\n GraffitiObject,\n GraffitiObjectBase,\n GraffitiPatch,\n GraffitiSession,\n GraffitiPutObject,\n GraffitiObjectStream,\n ChannelStats,\n GraffitiChannelStatsStream,\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 media applications,\n * all of which can interoperate.\n * These 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 the data \u2014\n * no server code necessary.\n * The Typescript source for this API is available at\n * [graffiti-garden/api](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-federated),\n * that lets users choose where their data is stored,\n * and a [local implementation](https://github.com/graffiti-garden/implementation-local)\n * that can be used for testing and development. In our design of Graffiti, this API is our\n * primary focus as it is the layer that shapes the experience\n * of developing applications. While different implementations can provide tradeoffs between\n * other important properties (e.g. privacy, security, scalability), those properties\n * are useless if the system as a whole doesn't expose useful functionality to developers.\n *\n * On the other side of the stack, there is [Vue plugin](https://github.com/graffiti-garden/wrapper-vue/)\n * that wraps around this API to provide reactivity. Other high-level libraries\n * will be available in the future.\n *\n * ## Overview\n *\n * Graffiti provides applications with methods to create and store data\n * on behalf of their users using standard CRUD operations:\n * {@link put}, {@link get}, {@link patch}, and {@link delete}.\n * This data can represent both social artifacts (e.g. posts, profiles) and\n * activities (e.g. likes, follows) and is stored as JSON.\n *\n * The social aspect of Graffiti comes from the {@link discover} method\n * which allows applications to find objects that other users made.\n * It is a lot like a traditional query operation, but it only\n * returns objects that have been placed in particular\n * {@link GraffitiObjectBase.channels | `channels`}\n * specified by the discovering application.\n *\n * Graffiti builds on well known concepts and standards wherever possible.\n * JSON Objects can be typed with [JSON Schema](https://json-schema.org/) and patches\n * can be applied with [JSON Patch](https://jsonpatch.com).\n * For interoperability between Graffiti applications, we recommend that\n * objects use established properties from the\n * [Activity Vocabulary](https://www.w3.org/TR/activitystreams-vocabulary/) when available,\n * however it is always possible to create additional properties, contributing\n * to the broader [folksonomy](https://en.wikipedia.org/wiki/Folksonomy).\n *\n * {@link GraffitiObjectBase.channels | `channels`} are one of the major concepts\n * unique to Graffiti along with *interaction relativity*, defined below.\n * Channels create boundaries between public spaces and work to prevent\n * [context collapse](https://en.wikipedia.org/wiki/Context_collapse)\n * even in a highly interoperable environment.\n * Interaction relativity means that all interactions between users are\n * actually atomic single-user operations that can be interpreted in different ways,\n * which also supports interoperability and pluralism.\n *\n * ### Channels\n *\n * {@link GraffitiObjectBase.channels | `channels`}\n * are a way for the creators of social data to express the intended audience of their\n * data. When a user creates data using the {@link put} method, they\n * can place their data in one or more channels.\n * Content consumers using the {@link discover} method will only see data\n * contained in one of the channels they specify.\n *\n * While many channels may be public, they partition\n * the public into different \"contexts\", mitigating the\n * phenomenon of [context collapse](https://en.wikipedia.org/wiki/Context_collapse) or the \"flattening of multiple audiences.\"\n * Any [URI](https://en.wikipedia.org/wiki/Uniform_Resource_Identifier) can be used as a channel, and so channels can represent people,\n * comment threads, topics, places (real or virtual), pieces of media, and more.\n *\n * For example, consider a comment on a post. If we place that comment in the channel\n * represented by the post's URL, then only people viewing the post will know to\n * look in that channel, giving it visibility akin to a comment on a blog post\n * or comment on Instagram ([since 2019](https://www.buzzfeednews.com/article/katienotopoulos/instagrams-following-activity-tab-is-going-away)).\n * If we also place the comment in the channel represented by the commenter's URI (their\n * {@link GraffitiObjectBase.actor | `actor` URI}), then people viewing the commenter's profile\n * will also see the comment, giving it more visibility, like a reply on Twitter.\n * If we *only* place the comment in the channel represented by the commenter's URI, then\n * it becomes like a quote tweet ([prior to 2020](https://x.com/Support/status/1300555325750292480)),\n * where the comment is only visible to the commenter's followers but not the audience\n * of the original post.\n *\n * The channel model differs from other models of communication such as the\n * [actor model](https://www.w3.org/TR/activitypub/#Overview) used by ActivityPub,\n * the protocol underlying Mastodon, or the [firehose model](https://bsky.social/about/blog/5-5-2023-federation-architecture)\n * used by the AT Protocol, the protocol underlying BlueSky.\n * The actor model is a fusion of direct messaging (like Email) and broadcasting\n * (like RSS) and works well for follow-based communication but struggles\n * to pass information via other rendez-vous.\n * In the actor model, even something as simple as comments can be\n * [very tricky and require server \"side effects\"](https://seb.jambor.dev/posts/understanding-activitypub-part-3-the-state-of-mastodon/).\n * The firehose model dumps all user data into one public database,\n * which doesn't allow for the carving out of different contexts that we did in our comment\n * example above. In the firehose model a comment will always be visible to *both* the original post's audience and\n * the commenter's followers.\n *\n * In some sense, channels provide a sort of \"social access control\" by forming\n * expectations about the audiences of different online spaces.\n * As a real world analogy, oftentimes support groups, such as alcoholics\n * anonymous, are open to the public but people in those spaces feel comfortable sharing intimate details\n * because they have expectations about the other people attending.\n * If someone malicious went to support groups just to spread people's secrets,\n * they would be shamed for violating these norms.\n * Similarly, in Graffiti, while you could spider public channels like a search engine\n * to find content about a person, revealing that you've done such a thing\n * would be shameful.\n *\n * Still, social access control is not perfect and so in situations where privacy is important,\n * objects can also be given\n * an {@link GraffitiObjectBase.allowed | `allowed`} list.\n * For example, to send someone a direct message you should put an object representing\n * that message in the channel that represents them (their {@link GraffitiObjectBase.actor | `actor` URI}),\n * so they can find it, *and* set the `allowed` field to only include the recipient,\n * so only they can read it.\n *\n * ### Interaction relativity\n *\n * Interaction relativity posits that \"interaction between two individuals only\n * exists relative to an observer,\" or equivalently, all interaction is [reified](https://en.wikipedia.org/wiki/Reification_(computer_science)).\n * For example, if one user creates a post and another user wants to \"like\" that post,\n * their like is not modifying the original post, it is simply another data object that points\n * to the post being liked, via its {@link GraffitiObjectBase.url | URL}.\n *\n * ```json\n * {\n * activity: 'like',\n * target: 'url-of-the-post-i-like',\n * actor: 'my-user-id'\n * }\n * ```\n *\n * In Graffiti, all interactions including *moderation* and *collaboration* are relative.\n * This means that applications can freely choose which interactions\n * they want to express to their users and how.\n * For example, one application could have a single fixed moderator,\n * another could allow users to choose which moderators they would like filter their content\n * like [Bluesky's stackable moderation](https://bsky.social/about/blog/03-12-2024-stackable-moderation),\n * and another could implement a fully democratic system like [PolicyKit](https://policykit.org/).\n * Each of these applications is one interpretation of the underlying refieid user interactions and\n * users can freely switch between them.\n *\n * Interaction relativy also allows applications to introduce new sorts of interactions\n * without having to coordinate with all the other existing applications,\n * keeping the ecosystem flexible and interoperable.\n * For example, an application could [add a \"Trust\" button to posts](https://social.cs.washington.edu/pub_details.html?id=trustnet)\n * and use it assess the truthfulness of posts made on applications across Graffiti.\n * New sorts of interactions like these can be smoothly absorbed by the broader ecosystem\n * as a [folksonomy](https://en.wikipedia.org/wiki/Folksonomy).\n *\n * Interactivy relativity is realized in Graffiti through two design decisions:\n * 1. The creators of objects can only modify their own objects. It is important for\n * users to be able to change and delete their own content to respect their\n * [right to be forgotten](https://en.wikipedia.org/wiki/Right_to_be_forgotten),\n * but beyond self-correction and self-censorship all other interaction is reified.\n * Many interactions can be reified via pointers, as in the \"like\" example above, and collaborative\n * edits can be refieid via [CRDTs](https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type).\n * 2. No one owns channels. Unlike IRC/Slack channels or [Matrix rooms](https://matrix.org/docs/matrix-concepts/rooms_and_events/),\n * anyone can post to any channel, so long as they know the URI of that channel.\n * It is up to applications to hide content from channels either according to manual\n * filters or in response to user action.\n * For example, a user may create a post with the flag `disableReplies`.\n * Applications could then filter out any content from the replies channel\n * that the original poster has not specifically approved.\n *\n * @groupDescription CRUD Methods\n * Methods for {@link put | creating}, {@link get | reading}, {@link patch | updating},\n * and {@link delete | deleting} {@link GraffitiObjectBase | Graffiti objects}.\n * @groupDescription Query Methods\n * Methods that retrieve or accumulate information about multiple {@link GraffitiObjectBase | Graffiti objects} at a time.\n * @groupDescription Session Management\n * Methods and properties for logging in and out of a Graffiti implementation.\n */\nexport abstract class Graffiti {\n /**\n * Creates a new {@link GraffitiObjectBase | object} or replaces an existing object.\n * An object can only be replaced by the same {@link GraffitiObjectBase.actor | `actor`}\n * that created it.\n *\n * Replacement occurs when the {@link GraffitiObjectBase.url | `url`} of\n * the replaced object exactly matches an existing object's URL.\n *\n * @throws {@link GraffitiErrorNotFound} if a {@link GraffitiObjectBase.url | `url`}\n * is provided that has not been created yet or the {@link GraffitiObjectBase.actor | `actor`}\n * is not {@link GraffitiObjectBase.allowed | `allowed`} to see 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 * @returns The object that was replaced if one one exists, otherwise an object with\n * with an empty {@link GraffitiObjectBase.value | `value`},\n * {@link GraffitiObjectBase.channels | `channels`}, and {@link GraffitiObjectBase.allowed | `allowed`}\n * list.\n * The {@link GraffitiObjectBase.lastModified | `lastModified`} property of the returned object\n * will be updated to the time of replacement/creation.\n *\n * @group CRUD Methods\n */\n abstract put<Schema extends JSONSchema>(\n /**\n * The object to be put. 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. We highly recommend providing a schema to\n * ensure that the PUT object matches subsequent {@link get} or {@link discover}\n * methods.\n */\n object: GraffitiPutObject<Schema>,\n /**\n * An implementation-specific object with information to authenticate the\n * {@link GraffitiObjectBase.actor | `actor`}.\n */\n session: GraffitiSession,\n ): Promise<GraffitiObjectBase>;\n\n /**\n * Retrieves an object from a given {@link GraffitiObjectBase.url | `url`}.\n *\n * The retrieved object is type-checked against the provided [JSON schema](https://json-schema.org/)\n * otherwise a {@link GraffitiErrorSchemaMismatch} is thrown.\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.\n *\n * @throws {@link GraffitiErrorNotFound} if the object does not exist, has been deleted, or the user 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 CRUD 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 * Patches an existing object at a given {@link GraffitiObjectBase.url | `url`}.\n * The patching {@link GraffitiObjectBase.actor | `actor`} must be the same as the\n * `actor` that created the object.\n *\n * @returns The original object prior to the patch with its\n * {@link GraffitiObjectBase.lastModified | `lastModified`}\n * property updated to the time of deletion.\n *\n * @throws {@link GraffitiErrorNotFound} if the object does not exist, has already been deleted,\n * or the user 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 CRUD Methods\n */\n abstract patch(\n /**\n * A collection of [JSON Patch](https://jsonpatch.com) operations\n * to apply to the object. See {@link GraffitiPatch} for more information.\n */\n patch: GraffitiPatch,\n /**\n * The location of the object to patch.\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<GraffitiObjectBase>;\n\n /**\n * Deletes an object from a given {@link GraffitiObjectBase.url | `url`}.\n * The deleting {@link GraffitiObjectBase.actor | `actor`} must be the same as the\n * `actor` that created the object.\n *\n * It is not possible to re-{@link put} an object that has been deleted\n * to ensure a user's [right to be forgotten](https://en.wikipedia.org/wiki/Right_to_be_forgotten).\n * In cases where deleting and restoring an object is useful, an object's\n * {@link GraffitiObjectBase.allowed | `allowed`} property can be set to\n * an empty list to hide it from all users except the creator.\n *\n * @returns The object that was deleted with its\n * {@link GraffitiObjectBase.lastModified | `lastModified`}\n * property updated to the time of deletion.\n *\n * @throws {@link GraffitiErrorNotFound} if the object does not exist, has already been deleted,\n * or the user 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 CRUD 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<GraffitiObjectBase>;\n\n /**\n * Discovers objects created by any user 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 {@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 * It is also possible that duplicate objects are returned and their\n * {@link GraffitiObjectBase.lastModified | `lastModified`} fields must be used\n * to determine which object is the most recent.\n *\n * @returns A stream of objects that match the given {@link GraffitiObjectBase.channels | `channels`}\n * and [JSON Schema](https://json-schema.org).\n *\n * @group Query 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 * Discovers objects **not** contained in any\n * {@link GraffitiObjectBase.channels | `channels`}\n * that were created by the querying {@link GraffitiObjectBase.actor | `actor`}\n * and match the given [JSON Schema](https://json-schema.org).\n * Unlike {@link discover}, this method will not return objects created by other users.\n *\n * This method is not useful for most applications, but necessary for\n * getting a global view of all a user's Graffiti data or debugging\n * channel usage.\n *\n * Like {@link discover}, objects are returned asynchronously as they are discovered,\n * the stream will end once all leads have been exhausted, and the stream\n * can be continued using the {@link GraffitiObjectStreamReturn.continue | `continue`}\n * method or {@link GraffitiObjectStreamReturn.cursor | `cursor`} string.\n *\n * @returns A stream of objects created by the querying {@link GraffitiObjectBase.actor | `actor`}\n * that do not belong to any {@link GraffitiObjectBase.channels | `channels`}\n * and match the given [JSON Schema](https://json-schema.org).\n *\n * @group Query Methods\n */\n abstract recoverOrphans<Schema extends JSONSchema>(\n /**\n * A [JSON Schema](https://json-schema.org) that orphaned objects must satisfy.\n */\n schema: Schema,\n /**\n * An implementation-specific object with information to authenticate the\n * {@link GraffitiObjectBase.actor | `actor`}.\n */\n session: GraffitiSession,\n ): GraffitiObjectStream<Schema>;\n\n /**\n * Returns statistics about all the {@link GraffitiObjectBase.channels | `channels`}\n * that an {@link GraffitiObjectBase.actor | `actor`} has posted to.\n * This is not very useful for most applications, but\n * necessary for certain applications where a user wants a\n * global view of all their Graffiti data or to debug\n * channel usage.\n *\n * Like {@link discover}, objects are returned asynchronously as they are discovered,\n * the stream will end once all leads have been exhausted.\n *\n * @group Query Methods\n *\n * @returns A stream of statistics for each {@link GraffitiObjectBase.channels | `channel`}\n * that the {@link GraffitiObjectBase.actor | `actor`} has posted to.\n */\n abstract channelStats(\n /**\n * An implementation-specific object with information to authenticate the\n * {@link GraffitiObjectBase.actor | `actor`}.\n */\n session: GraffitiSession,\n ): GraffitiChannelStatsStream;\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 created\n * that match the original stream, and also returns the\n * {@link GraffitiObjectBase.url | `url`}s of objects that\n * have been deleted, as marked by a `tombstone`.\n *\n * The continuation may also include duplicates of objects that\n * were already returned by the original stream. This is dependent\n * on how much state the underlying implementation maintains.\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 Query Methods\n */\n abstract continueObjectStream(\n cursor: string,\n session?: GraffitiSession | null,\n ): GraffitiObjectStreamContinue<{}>;\n\n /**\n * Begins the login process. Depending on the implementation, this may\n * involve redirecting the user to a login page or opening a popup,\n * so it should always be called in response to a user action.\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 Session Management\n */\n abstract login(\n /**\n * Suggestions for the permissions that the\n * login process should grant. The login process may not\n * provide the exact proposed permissions.\n */\n proposal?: {\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 /**\n * A yet to be defined permissions scope. An application may use\n * this to indicate the minimum necessary scope needed to\n * operate. For example, it may need to be able read private\n * messages from a certain set of channels, or write messages that\n * follow a particular schema.\n *\n * The login process should make it clear what scope an application\n * is requesting and allow the user to enhance or reduce that\n * scope as necessary.\n */\n scope?: {};\n },\n ): Promise<void>;\n\n /**\n * Begins the logout process. 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 user action.\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 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 they're corresponding event types:\n * - `login` - {@link GraffitiLoginEvent}\n * - `logout` - {@link GraffitiLogoutEvent}\n * - `initialized` - {@link GraffitiSessionInitializedEvent}\n *\n * @group Session Management\n */\n abstract readonly sessionEvents: EventTarget;\n}\n", "import type { JSONSchema, FromSchema } from \"json-schema-to-ts\";\nimport type { Operation as JSONPatchOperation } from \"fast-json-patch\";\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 * Objects are created and modified by a single {@link actor | `actor`}.\n *\n * Most of an object's content is stored in its {@link value | `value`} property, which can be any JSON\n * object. However, 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 * The object is globally addressable via its {@link url | `url`}.\n *\n * The {@link channels | `channels`} and {@link allowed | `allowed`} properties\n * enable the object's creator to shape the visibility of and access to their object.\n *\n * The {@link lastModified | `lastModified`} property can be used to compare object versions.\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 user's own {@link actor | `actor`} URI. Putting an object in this channel is a way to broadcast\n * the object to the user's followers, like posting a tweet.\n * - The URL of a Graffiti post. Putting an object in this channel is a way to broadcast to anyone viewing\n * the post, like commenting on a tweet.\n * - A URI representing a topic. Putting an object in 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 users other than the creator, like\n * a BCC email. A user 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 put their object in 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.put | created } the object. This `actor` also has the unique permission to\n * {@link Graffiti.patch | modify} or {@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 * If an object is {@link Graffiti.put | put} with the same URL\n * as an existing object, the existing object will be replaced with the new object.\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\n * [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 * The time the object was last modified, measured in milliseconds since January 1, 1970.\n * It can be used to compare object versions.\n * A number, rather than an ISO string or Date object, is used for easy comparison, sorting,\n * and JSON Schema [range queries](https://json-schema.org/understanding-json-schema/reference/numeric#range).\n *\n * It is possible to use this value to sort objects in a user's interface but in many cases it would be better to\n * use a [`published`](https://www.w3.org/TR/activitystreams-vocabulary/#dfn-published)\n * property in the object's {@link value | `value`} to indicate when the object was created\n * rather than when it was modified.\n */\n lastModified: number;\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 lastModified: { type: \"number\" },\n },\n additionalProperties: false,\n required: [\"value\", \"channels\", \"actor\", \"url\", \"lastModified\"],\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 users can call {@link Graffiti.get},\n * {@link Graffiti.patch}, or {@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 a user must construct locally before calling {@link Graffiti.put}.\n * This local copy does not require system-generated properties 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 *\n * It may also include a {@link GraffitiObjectBase.url | `url`} property to specify the\n * URL of an existing object to replace. If no `url` is provided, one will be generated during object creation.\n *\n * This object does not need a {@link GraffitiObjectBase.lastModified | `lastModified`}\n * property since it will be automatically generated by the Graffiti system.\n */\nexport type GraffitiPutObject<Schema extends JSONSchema> = Pick<\n GraffitiObjectBase,\n \"value\" | \"channels\" | \"allowed\"\n> &\n Partial<GraffitiObjectBase> &\n FromSchema<Schema & typeof GraffitiPutObjectJSONSchema>;\n\n/**\n * A JSON Schema equivalent to the {@link GraffitiPutObject} type.\n * Needed internally for type inference of JSON Schemas, but can\n * be used by implementations to validate objects.\n */\nexport const GraffitiPutObjectJSONSchema = {\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 verify that a user has permission to operate a\n * particular {@link GraffitiObjectBase.actor | `actor`}.\n * This object is required of all {@link Graffiti} methods\n * that modify objects 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 the user wants 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 users 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 * In the future, `session` object may be updated to include scope information\n * and passing the `session` to each method can type-check whether the session provides the\n * necessary permissions.\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`} a user wants to authenticate with.\n */\n actor: string;\n /**\n * A yet undefined property detailing what operations the session\n * grants the user to perform. For example, to allow a user to\n * read private messages from a particular set of channels or\n * to allow the user to write object matching a particular schema.\n */\n scope?: {};\n}\n\n/**\n * This is the format for patches that modify {@link GraffitiObjectBase} objects\n * using the {@link Graffiti.patch} method. The patches must\n * be an array of [JSON Patch](https://jsonpatch.com) operations.\n * Patches can only be applied to the\n * {@link GraffitiObjectBase.value | `value`}, {@link GraffitiObjectBase.channels | `channels`},\n * and {@link GraffitiObjectBase.allowed | `allowed`} properties since the other\n * properties either describe the object's location or are automatically generated.\n * (See also {@link GraffitiPutObject}).\n */\nexport interface GraffitiPatch {\n /**\n * An array of [JSON Patch](https://jsonpatch.com) operations to\n * modify the object's {@link GraffitiObjectBase.value | `value`}. The resulting\n * `value` must still be a JSON object.\n */\n value?: JSONPatchOperation[];\n\n /**\n * An array of [JSON Patch](https://jsonpatch.com) operations to\n * modify the object's {@link GraffitiObjectBase.channels | `channels`}. The resulting\n * `channels` must still be an array of strings.\n */\n channels?: JSONPatchOperation[];\n\n /**\n * An array of [JSON Patch](https://jsonpatch.com) operations to\n * modify the object's {@link GraffitiObjectBase.allowed | `allowed`} property. The resulting\n * `allowed` property must still be an array of strings or `undefined`.\n */\n allowed?: JSONPatchOperation[];\n}\n\n/**\n * A stream of data that are returned by Graffiti's query-like operations\n * {@link Graffiti.discover} and {@link Graffiti.recoverOrphans}.\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 GraffitiStreamError}.\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 GraffitiStreamError | GraffitiObjectStreamEntry<Schema>,\n GraffitiObjectStreamReturn<Schema>\n>;\n\n/**\n * An error that can occur in either the\n * {@link GraffitiObjectStream} or {@link GraffitiChannelStatsStream}.\n *\n * @internal\n */\nexport interface GraffitiStreamError {\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 GraffitiStreamError}\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 user's privacy.\n *\n * @internal\n */\nexport interface GraffitiObjectStreamContinueTombstone {\n /**\n * Empty property for compatibility with {@link GraffitiStreamError}\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 a user's privacy.\n */\n object: {\n /**\n * The {@link GraffitiObjectBase.url | `url`} of the deleted object.\n */\n url: string;\n /**\n * The time at which the object was deleted, comparable to\n * {@link GraffitiObjectBase.lastModified | `lastModified`}.\n *\n * While it is not possible to re-{@link Graffiti.put | put} objects that have been\n * {@link Graffiti.delete | deleted}, objects may appear deleted if\n * an {@link GraffitiObjectBase.actor | `actor`} is no longer\n * {@link GraffitiObjectBase.allowed | `allowed`} to access them.\n * Therefore the {@link GraffitiObjectBase.lastModified | `lastModified`} property\n * is necessary to compare object versions.\n */\n lastModified: number;\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 user\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.continueObjectStream} 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.continueObjectStream}.\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 GraffitiStreamError | GraffitiObjectStreamContinueEntry<Schema>,\n GraffitiObjectStreamReturn<Schema>\n >;\n\n/**\n * Statistic about single channel returned by {@link Graffiti.channelStats}.\n * These statistics only account for contributions made by the\n * querying actor.\n */\nexport type ChannelStats = {\n /**\n * The URI of the channel.\n */\n channel: string;\n /**\n * The number of objects that the actor has {@link Graffiti.put | put}\n * and not {@link Graffiti.delete | deleted} in the channel.\n */\n count: number;\n /**\n * The time that the actor {@link GraffitiObjectBase.lastModified | last modified} an object in the channel,\n * measured in milliseconds since January 1, 1970.\n * {@link Graffiti.delete | Deleted} objects do not effect this modification time.\n */\n lastModified: number;\n};\n\n/**\n * A stream of data that are returned by Graffiti's {@link Graffiti.channelStats} method.\n * See {@link GraffitiObjectStream} for more information on streams.\n */\nexport type GraffitiChannelStatsStream = AsyncGenerator<\n | GraffitiStreamError\n | {\n error?: undefined;\n value: ChannelStats;\n }\n>;\n\n/**\n * The event type produced in {@link Graffiti.sessionEvents}\n * when a user 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 user 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 the user originated a login request\n * from, 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 GraffitiErrorPatchTestFailed extends Error {\n constructor(message?: string) {\n super(message);\n this.name = \"GraffitiErrorPatchTestFailed\";\n Object.setPrototypeOf(this, GraffitiErrorPatchTestFailed.prototype);\n }\n}\n\nexport class GraffitiErrorPatchError extends Error {\n constructor(message?: string) {\n super(message);\n this.name = \"GraffitiErrorPatchError\";\n Object.setPrototypeOf(this, GraffitiErrorPatchError.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", "import type {\n Graffiti,\n GraffitiLoginEvent,\n GraffitiLogoutEvent,\n GraffitiSessionInitializedEvent,\n} from \"@graffiti-garden/api\";\n\n/**\n * A class that implements the login methods\n * of the [Graffiti API]() for use in the browser.\n * It is completely insecure and should only be used\n * for testing and demonstrations.\n *\n * It uses `localStorage` to store login state and\n * window prompts rather than an oauth flow for log in.\n * It can be used in node.js but will not persist\n * login state and a proposed username must be provided.\n */\nexport class GraffitiLocalSessionManager {\n sessionEvents: Graffiti[\"sessionEvents\"] = new EventTarget();\n\n constructor() {\n // Look for any existing sessions\n const sessionRestorer = async () => {\n // Allow listeners to be added first\n await Promise.resolve();\n\n // Restore previous sessions\n for (const actor of this.getLoggedInActors()) {\n const event: GraffitiLoginEvent = new CustomEvent(\"login\", {\n detail: { session: { actor } },\n });\n this.sessionEvents.dispatchEvent(event);\n }\n\n const event: GraffitiSessionInitializedEvent = new CustomEvent(\n \"initialized\",\n { detail: {} },\n );\n this.sessionEvents.dispatchEvent(event);\n };\n sessionRestorer();\n }\n\n loggedInActors: string[] = [];\n\n protected getLoggedInActors(): string[] {\n if (typeof window !== \"undefined\") {\n const actorsString = window.localStorage.getItem(\"graffiti-actor\");\n return actorsString\n ? actorsString.split(\",\").map(decodeURIComponent)\n : [];\n } else {\n return this.loggedInActors;\n }\n }\n\n protected setLoggedInActors(actors: string[]) {\n if (typeof window !== \"undefined\") {\n window.localStorage.setItem(\n \"graffiti-actor\",\n actors.map(encodeURIComponent).join(\",\"),\n );\n } else {\n this.loggedInActors = actors;\n }\n }\n\n login: Graffiti[\"login\"] = async (proposal) => {\n let actor = proposal?.actor;\n if (!actor && typeof window !== \"undefined\") {\n const response = window.prompt(\n `This is an insecure implementation of the Graffiti API \\\n for *demo purposes only*. Do not store any sensitive information \\\n here.\\\n \\n\\n\\\n Simply choose a username to log in.`,\n );\n if (response) actor = response;\n }\n\n let detail: GraffitiLoginEvent[\"detail\"];\n if (!actor) {\n detail = {\n error: new Error(\"No actor ID provided to login\"),\n };\n } else {\n const existingActors = this.getLoggedInActors();\n if (!existingActors.includes(actor)) {\n this.setLoggedInActors([...existingActors, actor]);\n }\n\n detail = {\n session: { actor },\n };\n }\n\n const event: GraffitiLoginEvent = new CustomEvent(\"login\", { detail });\n this.sessionEvents.dispatchEvent(event);\n };\n\n logout: Graffiti[\"logout\"] = async (session) => {\n const existingActors = this.getLoggedInActors();\n const exists = existingActors.includes(session.actor);\n if (exists) {\n this.setLoggedInActors(\n existingActors.filter((actor) => actor !== session.actor),\n );\n }\n\n const detail: GraffitiLogoutEvent[\"detail\"] = exists\n ? {\n actor: session.actor,\n }\n : {\n actor: session.actor,\n error: new Error(\"Not logged in with that actor\"),\n };\n\n const event: GraffitiLogoutEvent = new CustomEvent(\"logout\", { detail });\n this.sessionEvents.dispatchEvent(event);\n };\n}\n", "import type {\n Graffiti,\n GraffitiObjectBase,\n GraffitiObjectUrl,\n JSONSchema,\n GraffitiSession,\n GraffitiObjectStreamContinue,\n GraffitiObjectStreamContinueEntry,\n} from \"@graffiti-garden/api\";\nimport {\n GraffitiErrorNotFound,\n GraffitiErrorSchemaMismatch,\n GraffitiErrorForbidden,\n GraffitiErrorPatchError,\n} from \"@graffiti-garden/api\";\nimport {\n randomBase64,\n applyGraffitiPatch,\n maskGraffitiObject,\n isActorAllowedGraffitiObject,\n compileGraffitiObjectSchema,\n unpackObjectUrl,\n} from \"./utilities.js\";\nimport type Ajv from \"ajv\";\nimport type { applyPatch } from \"fast-json-patch\";\n\n/**\n * Constructor options for the GraffitiPoubchDB class.\n */\nexport interface GraffitiLocalOptions {\n /**\n * Options to pass to the PouchDB constructor.\n * Defaults to `{ name: \"graffitiDb\" }`.\n *\n * See the [PouchDB documentation](https://pouchdb.com/api.html#create_database)\n * for available options.\n */\n pouchDBOptions?: PouchDB.Configuration.DatabaseConfiguration;\n /**\n * Includes the scheme and other information (possibly domain name)\n * to prefix prefixes all URLs put in the system. Defaults to `graffiti:local`.\n */\n origin?: string;\n /**\n * Whether to allow putting objects at arbtirary URLs, i.e.\n * URLs that are *not* prefixed with the origin or not generated\n * by the system. Defaults to `false`.\n *\n * Allows this implementation to be used as a client-side cache\n * for remote sources.\n */\n allowSettingArbitraryUrls?: boolean;\n /**\n * Whether to allow the user to set the lastModified field\n * when putting objects. Defaults to `false`.\n *\n * Allows this implementation to be used as a client-side cache\n * for remote sources.\n */\n allowSettinngLastModified?: boolean;\n /**\n * An optional Ajv instance to use for schema validation.\n * If not provided, an internal instance will be created.\n */\n ajv?: Ajv;\n /**\n * Wait at least this long (in milliseconds) before continuing a stream.\n * A basic form of rate limiting. Defaults to 1 seconds.\n */\n continueBuffer?: number;\n}\n\nconst DEFAULT_ORIGIN = \"graffiti:local:\";\n\n// During stream continuations, return objects\n// that have possibly already been seen over this window\nconst LAST_MODIFIED_BUFFER = 60000;\n\ntype GraffitiObjectWithTombstone = GraffitiObjectBase & { tombstone: boolean };\n\n/**\n * An implementation of only the database operations of the\n * GraffitiAPI without synchronization or session management.\n */\nexport class GraffitiLocalDatabase\n implements Omit<Graffiti, \"login\" | \"logout\" | \"sessionEvents\">\n{\n protected db_:\n | Promise<PouchDB.Database<GraffitiObjectWithTombstone>>\n | undefined;\n protected applyPatch_: Promise<typeof applyPatch> | undefined;\n protected ajv_: Promise<Ajv> | undefined;\n protected readonly options: GraffitiLocalOptions;\n protected readonly origin: string;\n\n get db() {\n if (!this.db_) {\n this.db_ = (async () => {\n const { default: PouchDB } = await import(\"pouchdb\");\n const pouchDbOptions = {\n name: \"graffitiDb\",\n ...this.options.pouchDBOptions,\n };\n const db = new PouchDB<GraffitiObjectWithTombstone>(\n pouchDbOptions.name,\n pouchDbOptions,\n );\n await db\n //@ts-ignore\n .put({\n _id: \"_design/indexes\",\n views: {\n objectsPerChannelAndLastModified: {\n map: function (object: GraffitiObjectWithTombstone) {\n const paddedLastModified = object.lastModified\n .toString()\n .padStart(15, \"0\");\n object.channels.forEach(function (channel) {\n const id =\n encodeURIComponent(channel) + \"/\" + paddedLastModified;\n //@ts-ignore\n emit(id);\n });\n }.toString(),\n },\n orphansPerActorAndLastModified: {\n map: function (object: GraffitiObjectWithTombstone) {\n if (object.channels.length === 0) {\n const paddedLastModified = object.lastModified\n .toString()\n .padStart(15, \"0\");\n const id =\n encodeURIComponent(object.actor) +\n \"/\" +\n paddedLastModified;\n //@ts-ignore\n emit(id);\n }\n }.toString(),\n },\n channelStatsPerActor: {\n map: function (object: GraffitiObjectWithTombstone) {\n if (object.tombstone) return;\n object.channels.forEach(function (channel) {\n const id =\n encodeURIComponent(object.actor) +\n \"/\" +\n encodeURIComponent(channel);\n //@ts-ignore\n emit(id, object.lastModified);\n });\n }.toString(),\n reduce: \"_stats\",\n },\n },\n })\n //@ts-ignore\n .catch((error) => {\n if (\n error &&\n typeof error === \"object\" &&\n \"name\" in error &&\n error.name === \"conflict\"\n ) {\n // Design document already exists\n return;\n } else {\n throw error;\n }\n });\n return db;\n })();\n }\n return this.db_;\n }\n\n protected get applyPatch() {\n if (!this.applyPatch_) {\n this.applyPatch_ = (async () => {\n const imported = await import(\"fast-json-patch\");\n return imported.applyPatch || imported.default.applyPatch;\n })();\n }\n return this.applyPatch_;\n }\n\n protected get ajv() {\n if (!this.ajv_) {\n this.ajv_ = this.options.ajv\n ? Promise.resolve(this.options.ajv)\n : (async () => {\n const { default: Ajv } = await import(\"ajv\");\n return new Ajv({ strict: false });\n })();\n }\n return this.ajv_;\n }\n\n protected extractGraffitiObject(\n object: GraffitiObjectWithTombstone,\n ): GraffitiObjectBase {\n const { value, channels, allowed, url, actor, lastModified } = object;\n return {\n value,\n channels,\n allowed,\n url,\n actor,\n lastModified,\n };\n }\n\n constructor(options?: GraffitiLocalOptions) {\n this.options = options ?? {};\n this.origin = this.options.origin ?? DEFAULT_ORIGIN;\n if (!this.origin.endsWith(\":\") && !this.origin.endsWith(\"/\")) {\n this.origin += \"/\";\n }\n }\n\n protected async allDocsAtLocation(objectUrl: string | GraffitiObjectUrl) {\n const url = unpackObjectUrl(objectUrl) + \"/\";\n const results = await (\n await this.db\n ).allDocs({\n startkey: url,\n endkey: url + \"\\uffff\", // \\uffff is the last unicode character\n include_docs: true,\n });\n const docs = results.rows\n .map((row) => row.doc)\n // Remove undefined docs\n .reduce<\n PouchDB.Core.ExistingDocument<\n GraffitiObjectWithTombstone & PouchDB.Core.AllDocsMeta\n >[]\n >((acc, doc) => {\n if (doc) acc.push(doc);\n return acc;\n }, []);\n return docs;\n }\n\n protected docId(objectUrl: GraffitiObjectUrl) {\n return objectUrl.url + \"/\" + randomBase64();\n }\n\n get: Graffiti[\"get\"] = async (...args) => {\n const [urlObject, schema, session] = args;\n\n // TODO: Rate limit getting the same object\n // over and over\n\n const docsAll = await this.allDocsAtLocation(urlObject);\n\n // Filter out ones not allowed\n const docs = docsAll.filter((doc) =>\n isActorAllowedGraffitiObject(doc, session),\n );\n if (!docs.length)\n throw new GraffitiErrorNotFound(\n \"The object you are trying to get either does not exist or you are not allowed to see it\",\n );\n\n // Get the most recent document\n const doc = docs.reduce((a, b) =>\n a.lastModified > b.lastModified ||\n (a.lastModified === b.lastModified && !a.tombstone && b.tombstone)\n ? a\n : b,\n );\n\n if (doc.tombstone) {\n throw new GraffitiErrorNotFound(\n \"The object you are trying to get either does not exist or you are not allowed to see it\",\n );\n }\n\n const object = this.extractGraffitiObject(doc);\n\n // Mask out the allowed list and channels\n // if the user is not the owner\n maskGraffitiObject(object, [], session);\n\n const validate = compileGraffitiObjectSchema(await this.ajv, schema);\n if (!validate(object)) {\n throw new GraffitiErrorSchemaMismatch();\n }\n return object;\n };\n\n /**\n * Deletes all docs at a particular location.\n * If the `keepLatest` flag is set to true,\n * the doc with the most recent timestamp will be\n * spared. If there are multiple docs with the same\n * timestamp, the one with the highest `_id` will be\n * spared.\n */\n protected async deleteAtLocation(\n url: GraffitiObjectUrl | string,\n options: {\n keepLatest?: boolean;\n session?: GraffitiSession;\n } = {\n keepLatest: false,\n },\n ) {\n const docsAtLocationAll = await this.allDocsAtLocation(url);\n const docsAtLocationAllowed = options.session\n ? docsAtLocationAll.filter((doc) =>\n isActorAllowedGraffitiObject(doc, options.session),\n )\n : docsAtLocationAll;\n if (!docsAtLocationAllowed.length) {\n throw new GraffitiErrorNotFound(\n \"The object you are trying to delete either does not exist or you are not allowed to see it\",\n );\n } else if (\n options.session &&\n docsAtLocationAllowed.some((doc) => doc.actor !== options.session?.actor)\n ) {\n throw new GraffitiErrorForbidden(\n \"You cannot delete an object owned by another actor\",\n );\n }\n const docsAtLocation = docsAtLocationAllowed.filter(\n (doc) => !doc.tombstone,\n );\n if (!docsAtLocation.length) return undefined;\n\n // Get the most recent lastModified timestamp.\n const latestModified = docsAtLocation\n .map((doc) => doc.lastModified)\n .reduce((a, b) => (a > b ? a : b));\n\n // Delete all old docs\n const docsToDelete = docsAtLocation.filter(\n (doc) => !options.keepLatest || doc.lastModified < latestModified,\n );\n\n // For docs with the same timestamp,\n // keep the one with the highest _id\n // to break concurrency ties\n const concurrentDocsAll = docsAtLocation.filter(\n (doc) => options.keepLatest && doc.lastModified === latestModified,\n );\n if (concurrentDocsAll.length) {\n const keepDocId = concurrentDocsAll\n .map((doc) => doc._id)\n .reduce((a, b) => (a > b ? a : b));\n const concurrentDocsToDelete = concurrentDocsAll.filter(\n (doc) => doc._id !== keepDocId,\n );\n docsToDelete.push(...concurrentDocsToDelete);\n }\n\n const lastModified = options.keepLatest\n ? latestModified\n : new Date().getTime();\n\n const deleteResults = await (\n await this.db\n ).bulkDocs<GraffitiObjectBase>(\n docsToDelete.map((doc) => ({\n ...doc,\n tombstone: true,\n lastModified,\n })),\n );\n\n // Get one of the docs that was deleted\n let deletedObject: GraffitiObjectBase | undefined = undefined;\n for (const resultOrError of deleteResults) {\n if (\"ok\" in resultOrError) {\n const { id } = resultOrError;\n const deletedDoc = docsToDelete.find((doc) => doc._id === id);\n if (deletedDoc) {\n deletedObject = {\n ...this.extractGraffitiObject(deletedDoc),\n lastModified,\n };\n break;\n }\n }\n }\n\n return deletedObject;\n }\n\n delete: Graffiti[\"delete\"] = async (...args) => {\n const [url, session] = args;\n const deletedObject = await this.deleteAtLocation(url, {\n session,\n });\n if (!deletedObject) {\n throw new GraffitiErrorNotFound(\"The object has already been deleted\");\n }\n return deletedObject;\n };\n\n put: Graffiti[\"put\"] = async (...args) => {\n const [objectPartial, session] = args;\n if (objectPartial.actor && objectPartial.actor !== session.actor) {\n throw new GraffitiErrorForbidden(\n \"Cannot put an object with a different actor than the session actor\",\n );\n }\n\n if (objectPartial.url) {\n let oldObject: GraffitiObjectBase | undefined;\n try {\n oldObject = await this.get(objectPartial.url, {}, session);\n } catch (e) {\n if (e instanceof GraffitiErrorNotFound) {\n if (!this.options.allowSettingArbitraryUrls) {\n throw new GraffitiErrorNotFound(\n \"The object you are trying to replace does not exist or you are not allowed to see it\",\n );\n }\n } else {\n throw e;\n }\n }\n if (oldObject?.actor !== session.actor) {\n throw new GraffitiErrorForbidden(\n \"The object you are trying to replace is owned by another actor\",\n );\n }\n }\n\n const lastModified =\n ((this.options.allowSettinngLastModified ?? false) &&\n objectPartial.lastModified) ||\n new Date().getTime();\n\n const object: GraffitiObjectWithTombstone = {\n value: objectPartial.value,\n channels: objectPartial.channels,\n allowed: objectPartial.allowed,\n url: objectPartial.url ?? this.origin + randomBase64(),\n actor: session.actor,\n tombstone: false,\n lastModified,\n };\n\n await (\n await this.db\n ).put({\n _id: this.docId(object),\n ...object,\n });\n\n // Delete the old object\n const previousObject = await this.deleteAtLocation(object, {\n keepLatest: true,\n });\n if (previousObject) {\n return previousObject;\n } else {\n return {\n ...object,\n value: {},\n channels: [],\n allowed: [],\n tombstone: true,\n };\n }\n };\n\n patch: Graffiti[\"patch\"] = async (...args) => {\n const [patch, url, session] = args;\n let originalObject: GraffitiObjectBase;\n try {\n originalObject = await this.get(url, {}, session);\n } catch (e) {\n if (e instanceof GraffitiErrorNotFound) {\n throw new GraffitiErrorNotFound(\n \"The object you are trying to patch does not exist or you are not allowed to see it\",\n );\n } else {\n throw e;\n }\n }\n if (originalObject.actor !== session.actor) {\n throw new GraffitiErrorForbidden(\n \"The object you are trying to patch is owned by another actor\",\n );\n }\n\n // Patch it outside of the database\n const patchObject: GraffitiObjectBase = { ...originalObject };\n for (const prop of [\"value\", \"channels\", \"allowed\"] as const) {\n applyGraffitiPatch(await this.applyPatch, prop, patch, patchObject);\n }\n\n // Make sure the value is an object\n if (\n typeof patchObject.value !== \"object\" ||\n Array.isArray(patchObject.value) ||\n !patchObject.value\n ) {\n throw new GraffitiErrorPatchError(\"value is no longer an object\");\n }\n\n // Make sure the channels are an array of strings\n if (\n !Array.isArray(patchObject.channels) ||\n !patchObject.channels.every((channel) => typeof channel === \"string\")\n ) {\n throw new GraffitiErrorPatchError(\n \"channels are no longer an array of strings\",\n );\n }\n\n // Make sure the allowed list is an array of strings or undefined\n if (\n patchObject.allowed &&\n (!Array.isArray(patchObject.allowed) ||\n !patchObject.allowed.every((allowed) => typeof allowed === \"string\"))\n ) {\n throw new GraffitiErrorPatchError(\n \"allowed list is not an array of strings\",\n );\n }\n\n patchObject.lastModified = new Date().getTime();\n await (\n await this.db\n ).put({\n ...patchObject,\n tombstone: false,\n _id: this.docId(patchObject),\n });\n\n // Delete the old object\n await this.deleteAtLocation(patchObject, {\n keepLatest: true,\n });\n\n return {\n ...originalObject,\n lastModified: patchObject.lastModified,\n };\n };\n\n protected queryLastModifiedSuffixes(\n schema: JSONSchema,\n lastModified?: number,\n ) {\n // Use the index for queries over ranges of lastModified\n let startKeySuffix = \"\";\n let endKeySuffix = \"\\uffff\";\n if (\n typeof schema === \"object\" &&\n schema.properties?.lastModified &&\n typeof schema.properties.lastModified === \"object\"\n ) {\n const lastModifiedSchema = schema.properties.lastModified;\n\n const minimum =\n lastModified && lastModifiedSchema.minimum\n ? Math.max(lastModified, lastModifiedSchema.minimum)\n : (lastModified ?? lastModifiedSchema.minimum);\n const exclusiveMinimum = lastModifiedSchema.exclusiveMinimum;\n\n let intMinimum: number | undefined;\n if (exclusiveMinimum !== undefined) {\n intMinimum = Math.ceil(exclusiveMinimum);\n intMinimum === exclusiveMinimum && intMinimum++;\n } else if (minimum !== undefined) {\n intMinimum = Math.ceil(minimum);\n }\n\n if (intMinimum !== undefined) {\n startKeySuffix = intMinimum.toString().padStart(15, \"0\");\n }\n\n const maximum = lastModifiedSchema.maximum;\n const exclusiveMaximum = lastModifiedSchema.exclusiveMaximum;\n\n let intMaximum: number | undefined;\n if (exclusiveMaximum !== undefined) {\n intMaximum = Math.floor(exclusiveMaximum);\n intMaximum === exclusiveMaximum && intMaximum--;\n } else if (maximum !== undefined) {\n intMaximum = Math.floor(maximum);\n }\n\n if (intMaximum !== undefined) {\n endKeySuffix = intMaximum.toString().padStart(15, \"0\");\n }\n }\n return {\n startKeySuffix,\n endKeySuffix,\n };\n }\n\n protected async *streamObjects<Schema extends JSONSchema>(\n index: string,\n startkey: string,\n endkey: string,\n validate: ReturnType<typeof compileGraffitiObjectSchema<Schema>>,\n session: GraffitiSession | undefined | null,\n ifModifiedSince: number | undefined,\n channels?: string[],\n processedIds?: Set<string>,\n ): AsyncGenerator<GraffitiObjectStreamContinueEntry<Schema>> {\n if (ifModifiedSince !== undefined) {\n // Subtract a minute to make sure we don't miss any objects\n ifModifiedSince -= LAST_MODIFIED_BUFFER;\n }\n\n const result = await (\n await this.db\n ).query<GraffitiObjectWithTombstone>(index, {\n startkey,\n endkey,\n include_docs: true,\n });\n\n for (const row of result.rows) {\n const doc = row.doc;\n if (!doc) continue;\n\n if (processedIds?.has(doc._id)) continue;\n processedIds?.add(doc._id);\n\n // If this is not a continuation, skip tombstones\n if (ifModifiedSince === undefined && doc.tombstone) continue;\n\n const object = this.extractGraffitiObject(doc);\n\n if (channels) {\n if (!isActorAllowedGraffitiObject(object, session)) continue;\n maskGraffitiObject(object, channels, session);\n }\n\n if (!validate(object)) continue;\n\n yield doc.tombstone\n ? {\n tombstone: true,\n object: {\n url: object.url,\n lastModified: object.lastModified,\n },\n }\n : { object };\n }\n }\n\n protected async waitToContinue(ifModifiedSince: number | undefined) {\n if (ifModifiedSince === undefined) return;\n const continueBuffer = this.options.continueBuffer ?? 1000;\n const timeElapsedSinceContinue = Date.now() - ifModifiedSince;\n if (timeElapsedSinceContinue < continueBuffer) {\n // Continue was called too soon,\n // wait a bit before continuing\n await new Promise((resolve) =>\n setTimeout(resolve, continueBuffer - timeElapsedSinceContinue),\n );\n }\n }\n\n protected async *discoverMeta<Schema extends JSONSchema>(\n args: Parameters<typeof Graffiti.prototype.discover<Schema>>,\n ifModifiedSince?: number,\n ): AsyncGenerator<\n GraffitiObjectStreamContinueEntry<Schema>,\n number | undefined\n > {\n await this.waitToContinue(ifModifiedSince);\n\n const [channels, schema, session] = args;\n const validate = compileGraffitiObjectSchema(await this.ajv, schema);\n const { startKeySuffix, endKeySuffix } = this.queryLastModifiedSuffixes(\n schema,\n ifModifiedSince,\n );\n\n const processedIds = new Set<string>();\n\n const startTime = new Date().getTime();\n\n for (const channel of channels) {\n const keyPrefix = encodeURIComponent(channel) + \"/\";\n const startkey = keyPrefix + startKeySuffix;\n const endkey = keyPrefix + endKeySuffix;\n\n const iterator = this.streamObjects<Schema>(\n \"indexes/objectsPerChannelAndLastModified\",\n startkey,\n endkey,\n validate,\n session,\n ifModifiedSince,\n channels,\n processedIds,\n );\n\n for await (const result of iterator) yield result;\n }\n\n return startTime;\n }\n\n protected async *recoverOrphansMeta<Schema extends JSONSchema>(\n args: Parameters<typeof Graffiti.prototype.recoverOrphans<Schema>>,\n ifModifiedSince?: number,\n ): AsyncGenerator<\n GraffitiObjectStreamContinueEntry<Schema>,\n number | undefined\n > {\n await this.waitToContinue(ifModifiedSince);\n\n const [schema, session] = args;\n const { startKeySuffix, endKeySuffix } = this.queryLastModifiedSuffixes(\n schema,\n ifModifiedSince,\n );\n const keyPrefix = encodeURIComponent(session.actor) + \"/\";\n const startkey = keyPrefix + startKeySuffix;\n const endkey = keyPrefix + endKeySuffix;\n\n const validate = compileGraffitiObjectSchema(await this.ajv, schema);\n\n const startTime = new Date().getTime();\n\n const iterator = this.streamObjects<Schema>(\n \"indexes/orphansPerActorAndLastModified\",\n startkey,\n endkey,\n validate,\n session,\n ifModifiedSince,\n );\n\n for await (const result of iterator) yield result;\n\n return startTime;\n }\n\n protected discoverCursor(\n args: Parameters<typeof Graffiti.prototype.discover<{}>>,\n ifModifiedSince?: number,\n ): string {\n return (\n \"discover:\" +\n JSON.stringify({\n channels: args[0],\n schema: args[1],\n actor: args[2]?.actor,\n ifModifiedSince: ifModifiedSince,\n })\n );\n }\n\n protected async *discoverContinue<Schema extends JSONSchema>(\n args: Parameters<typeof Graffiti.prototype.discover<Schema>>,\n ifModifiedSince?: number,\n ): GraffitiObjectStreamContinue<Schema> {\n const iterator = this.discoverMeta(args, ifModifiedSince);\n\n while (true) {\n const result = await iterator.next();\n if (result.done) {\n const ifModifiedSince = result.value;\n return {\n continue: () => this.discoverContinue<Schema>(args, ifModifiedSince),\n cursor: this.discoverCursor(args, ifModifiedSince),\n };\n }\n yield result.value;\n }\n }\n\n discover: Graffiti[\"discover\"] = (...args) => {\n const iterator = this.discoverMeta(args);\n\n const this_ = this;\n return (async function* () {\n while (true) {\n const result = await iterator.next();\n if (result.done) {\n return {\n continue: () =>\n this_.discoverContinue<(typeof args)[1]>(args, result.value),\n cursor: this_.discoverCursor(args, result.value),\n };\n }\n // Make sure to filter out tombstones\n if (result.value.tombstone) continue;\n yield result.value;\n }\n })();\n };\n\n protected recoverOrphansCursor(\n args: Parameters<typeof Graffiti.prototype.recoverOrphans<{}>>,\n ifModifiedSince?: number,\n ): string {\n return (\n \"orphans:\" +\n JSON.stringify({\n schema: args[0],\n actor: args[1]?.actor,\n ifModifiedSince,\n })\n );\n }\n\n protected async *recoverOrphansContinue<Schema extends JSONSchema>(\n args: Parameters<typeof Graffiti.prototype.recoverOrphans<Schema>>,\n ifModifiedSince?: number,\n ): GraffitiObjectStreamContinue<Schema> {\n const iterator = this.recoverOrphansMeta(args, ifModifiedSince);\n\n while (true) {\n const result = await iterator.next();\n if (result.done) {\n const ifModifiedSince = result.value;\n return {\n continue: () =>\n this.recoverOrphansContinue<Schema>(args, ifModifiedSince),\n cursor: this.recoverOrphansCursor(args, ifModifiedSince),\n };\n }\n yield result.value;\n }\n }\n\n recoverOrphans: Graffiti[\"recoverOrphans\"] = (...args) => {\n const iterator = this.recoverOrphansMeta(args);\n\n const this_ = this;\n return (async function* () {\n while (true) {\n const result = await iterator.next();\n if (result.done) {\n return {\n continue: () =>\n this_.recoverOrphansContinue<(typeof args)[0]>(\n args,\n result.value,\n ),\n cursor: this_.recoverOrphansCursor(args, result.value),\n };\n }\n // Make sure to filter out tombstones\n if (result.value.tombstone) continue;\n yield result.value;\n }\n })();\n };\n\n channelStats: Graffiti[\"channelStats\"] = (session) => {\n const this_ = this;\n return (async function* () {\n const keyPrefix = encodeURIComponent(session.actor) + \"/\";\n const result = await (\n await this_.db\n ).query(\"indexes/channelStatsPerActor\", {\n startkey: keyPrefix,\n endkey: keyPrefix + \"\\uffff\",\n reduce: true,\n group: true,\n });\n for (const row of result.rows) {\n const channelEncoded = row.key.split(\"/\")[1];\n if (typeof channelEncoded !== \"string\") continue;\n const { count, max: lastModified } = row.value;\n if (typeof count !== \"number\" || typeof lastModified !== \"number\")\n continue;\n yield {\n value: {\n channel: decodeURIComponent(channelEncoded),\n count,\n lastModified,\n },\n };\n }\n })();\n };\n\n continueObjectStream: Graffiti[\"continueObjectStream\"] = (\n cursor,\n session,\n ) => {\n if (cursor.startsWith(\"discover:\")) {\n const { channels, schema, actor, ifModifiedSince } = JSON.parse(\n cursor.slice(\"discover:\".length),\n );\n if (actor && actor !== session?.actor) {\n throw new GraffitiErrorForbidden(\n \"Cannot continue a cursor for another actor\",\n );\n }\n return this.discoverContinue<{}>(\n [channels, schema, session],\n ifModifiedSince,\n );\n } else if (cursor.startsWith(\"orphans:\")) {\n const { schema, actor, ifModifiedSince } = JSON.parse(\n cursor.slice(\"orphans:\".length),\n );\n if (!session || actor !== session?.actor) {\n throw new GraffitiErrorForbidden(\n \"Cannot continue a cursor for another actor\",\n );\n }\n return this.recoverOrphansContinue<{}>(\n [schema, session],\n ifModifiedSince,\n );\n } else {\n throw new GraffitiErrorNotFound(\"Cursor not found\");\n }\n };\n}\n", "import {\n GraffitiErrorInvalidSchema,\n GraffitiErrorPatchError,\n GraffitiErrorPatchTestFailed,\n} from \"@graffiti-garden/api\";\nimport type {\n GraffitiObject,\n GraffitiObjectBase,\n GraffitiPatch,\n JSONSchema,\n GraffitiSession,\n GraffitiObjectUrl,\n} from \"@graffiti-garden/api\";\nimport type { Ajv } from \"ajv\";\nimport type { applyPatch } from \"fast-json-patch\";\n\nexport function unpackObjectUrl(url: string | GraffitiObjectUrl) {\n return typeof url === \"string\" ? url : url.url;\n}\n\nexport function randomBase64(numBytes: number = 24) {\n const bytes = new Uint8Array(numBytes);\n crypto.getRandomValues(bytes);\n // Convert it to base64\n const base64 = btoa(String.fromCodePoint(...bytes));\n // Make sure it is url safe\n return base64.replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/\\=+$/, \"\");\n}\n\nexport function applyGraffitiPatch<Prop extends keyof GraffitiPatch>(\n apply: typeof applyPatch,\n prop: Prop,\n patch: GraffitiPatch,\n object: GraffitiObjectBase,\n): void {\n const ops = patch[prop];\n if (!ops || !ops.length) return;\n try {\n object[prop] = apply(object[prop], ops, true, false).newDocument;\n } catch (e) {\n if (\n typeof e === \"object\" &&\n e &&\n \"name\" in e &&\n typeof e.name === \"string\" &&\n \"message\" in e &&\n typeof e.message === \"string\"\n ) {\n if (e.name === \"TEST_OPERATION_FAILED\") {\n throw new GraffitiErrorPatchTestFailed(e.message);\n } else {\n throw new GraffitiErrorPatchError(e.name + \": \" + e.message);\n }\n } else {\n throw e;\n }\n }\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 maskGraffitiObject(\n object: GraffitiObjectBase,\n channels: string[],\n session?: GraffitiSession | null,\n): void {\n if (object.actor !== session?.actor) {\n object.allowed = object.allowed && session ? [session.actor] : undefined;\n object.channels = object.channels.filter((channel) =>\n channels.includes(channel),\n );\n }\n}\nexport function isActorAllowedGraffitiObject(\n object: GraffitiObjectBase,\n session?: GraffitiSession | null,\n) {\n return (\n object.allowed === undefined ||\n object.allowed === null ||\n (!!session?.actor &&\n (object.actor === session.actor ||\n object.allowed.includes(session.actor)))\n );\n}\n"],
|
|
5
|
-
"mappings": "sDAAAA,IAAAC,IAAAC,IC8LOC,IAAAC,IAAAC,IAAA,IAAeC,EAAf,KAAwB,CAwW/B,ECxaaC,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,EACxB,aAAc,CAAE,KAAM,QAAS,CACjC,EACA,qBAAsB,GACtB,SAAU,CAAC,QAAS,WAAY,QAAS,MAAO,cAAc,CAChE,EAqCaC,EAA8B,CACzC,GAAGD,EACH,SAAU,CAAC,QAAS,UAAU,CAChC,EClLO,IAQME,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,UAAqC,KAAM,CACtD,YAAYR,EAAkB,CAC5B,MAAMA,CAAO,EACb,KAAK,KAAO,+BACZ,OAAO,eAAe,KAAMQ,EAA6B,SAAS,CACpE,CACF,EAEaC,EAAN,MAAMC,UAAgC,KAAM,CACjD,YAAYV,EAAkB,CAC5B,MAAMA,CAAO,EACb,KAAK,KAAO,0BACZ,OAAO,eAAe,KAAMU,EAAwB,SAAS,CAC/D,CACF,ECtDAC,IAAAC,IAAAC,IAkBO,IAAMC,EAAN,KAAkC,CACvC,cAA2C,IAAI,YAE/C,aAAc,EAEY,SAAY,CAElC,MAAM,QAAQ,QAAQ,EAGtB,QAAWC,KAAS,KAAK,kBAAkB,EAAG,CAC5C,IAAMC,EAA4B,IAAI,YAAY,QAAS,CACzD,OAAQ,CAAE,QAAS,CAAE,MAAAD,CAAM,CAAE,CAC/B,CAAC,EACD,KAAK,cAAc,cAAcC,CAAK,CACxC,CAEA,IAAMA,EAAyC,IAAI,YACjD,cACA,CAAE,OAAQ,CAAC,CAAE,CACf,EACA,KAAK,cAAc,cAAcA,CAAK,CACxC,GACgB,CAClB,CAEA,eAA2B,CAAC,EAElB,mBAA8B,CACtC,GAAI,OAAO,OAAW,IAAa,CACjC,IAAMC,EAAe,OAAO,aAAa,QAAQ,gBAAgB,EACjE,OAAOA,EACHA,EAAa,MAAM,GAAG,EAAE,IAAI,kBAAkB,EAC9C,CAAC,CACP,KACE,QAAO,KAAK,cAEhB,CAEU,kBAAkBC,EAAkB,CACxC,OAAO,OAAW,IACpB,OAAO,aAAa,QAClB,iBACAA,EAAO,IAAI,kBAAkB,EAAE,KAAK,GAAG,CACzC,EAEA,KAAK,eAAiBA,CAE1B,CAEA,MAA2B,MAAOC,GAAa,CAC7C,IAAIJ,EAAQI,GAAU,MACtB,GAAI,CAACJ,GAAS,OAAO,OAAW,IAAa,CAC3C,IAAMK,EAAW,OAAO,OACtB;AAAA;AAAA,sCAKF,EACIA,IAAUL,EAAQK,EACxB,CAEA,IAAIC,EACJ,GAAI,CAACN,EACHM,EAAS,CACP,MAAO,IAAI,MAAM,+BAA+B,CAClD,MACK,CACL,IAAMC,EAAiB,KAAK,kBAAkB,EACzCA,EAAe,SAASP,CAAK,GAChC,KAAK,kBAAkB,CAAC,GAAGO,EAAgBP,CAAK,CAAC,EAGnDM,EAAS,CACP,QAAS,CAAE,MAAAN,CAAM,CACnB,CACF,CAEA,IAAMC,EAA4B,IAAI,YAAY,QAAS,CAAE,OAAAK,CAAO,CAAC,EACrE,KAAK,cAAc,cAAcL,CAAK,CACxC,EAEA,OAA6B,MAAOO,GAAY,CAC9C,IAAMD,EAAiB,KAAK,kBAAkB,EACxCE,EAASF,EAAe,SAASC,EAAQ,KAAK,EAChDC,GACF,KAAK,kBACHF,EAAe,OAAQP,GAAUA,IAAUQ,EAAQ,KAAK,CAC1D,EAGF,IAAMF,EAAwCG,EAC1C,CACE,MAAOD,EAAQ,KACjB,EACA,CACE,MAAOA,EAAQ,MACf,MAAO,IAAI,MAAM,+BAA+B,CAClD,EAEEP,EAA6B,IAAI,YAAY,SAAU,CAAE,OAAAK,CAAO,CAAC,EACvE,KAAK,cAAc,cAAcL,CAAK,CACxC,CACF,EC1HAS,IAAAC,IAAAC,ICAAC,IAAAC,IAAAC,IAgBO,SAASC,EAAgBC,EAAiC,CAC/D,OAAO,OAAOA,GAAQ,SAAWA,EAAMA,EAAI,GAC7C,CAEO,SAASC,EAAaC,EAAmB,GAAI,CAClD,IAAMC,EAAQ,IAAI,WAAWD,CAAQ,EACrC,cAAO,gBAAgBC,CAAK,EAEb,KAAK,OAAO,cAAc,GAAGA,CAAK,CAAC,EAEpC,QAAQ,MAAO,GAAG,EAAE,QAAQ,MAAO,GAAG,EAAE,QAAQ,OAAQ,EAAE,CAC1E,CAEO,SAASC,EACdC,EACAC,EACAC,EACAC,EACM,CACN,IAAMC,EAAMF,EAAMD,CAAI,EACtB,GAAI,GAACG,GAAO,CAACA,EAAI,QACjB,GAAI,CACFD,EAAOF,CAAI,EAAID,EAAMG,EAAOF,CAAI,EAAGG,EAAK,GAAM,EAAK,EAAE,WACvD,OAASC,EAAG,CACV,MACE,OAAOA,GAAM,UACbA,GACA,SAAUA,GACV,OAAOA,EAAE,MAAS,UAClB,YAAaA,GACb,OAAOA,EAAE,SAAY,SAEjBA,EAAE,OAAS,wBACP,IAAIC,EAA6BD,EAAE,OAAO,EAE1C,IAAIE,EAAwBF,EAAE,KAAO,KAAOA,EAAE,OAAO,EAGvDA,CAEV,CACF,CAEO,SAASG,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,EACdV,EACAW,EACAC,EACM,CACFZ,EAAO,QAAUY,GAAS,QAC5BZ,EAAO,QAAUA,EAAO,SAAWY,EAAU,CAACA,EAAQ,KAAK,EAAI,OAC/DZ,EAAO,SAAWA,EAAO,SAAS,OAAQa,GACxCF,EAAS,SAASE,CAAO,CAC3B,EAEJ,CACO,SAASC,EACdd,EACAY,EACA,CACA,OACEZ,EAAO,UAAY,QACnBA,EAAO,UAAY,MAClB,CAAC,CAACY,GAAS,QACTZ,EAAO,QAAUY,EAAQ,OACxBZ,EAAO,QAAQ,SAASY,EAAQ,KAAK,EAE7C,CD7BA,IAAMG,EAAiB,kBAIjBC,EAAuB,IAQhBC,EAAN,KAEP,CACY,IAGA,YACA,KACS,QACA,OAEnB,IAAI,IAAK,CACP,OAAK,KAAK,MACR,KAAK,KAAO,SAAY,CACtB,GAAM,CAAE,QAASC,CAAQ,EAAI,KAAM,QAAO,gCAAS,EAC7CC,EAAiB,CACrB,KAAM,aACN,GAAG,KAAK,QAAQ,cAClB,EACMC,EAAK,IAAIF,EACbC,EAAe,KACfA,CACF,EACA,aAAMC,EAEH,IAAI,CACH,IAAK,kBACL,MAAO,CACL,iCAAkC,CAChC,IAAK,SAAUC,EAAqC,CAClD,IAAMC,EAAqBD,EAAO,aAC/B,SAAS,EACT,SAAS,GAAI,GAAG,EACnBA,EAAO,SAAS,QAAQ,SAAUE,EAAS,CACzC,IAAMC,EACJ,mBAAmBD,CAAO,EAAI,IAAMD,EAEtC,KAAKE,CAAE,CACT,CAAC,CACH,EAAE,SAAS,CACb,EACA,+BAAgC,CAC9B,IAAK,SAAUH,EAAqC,CAClD,GAAIA,EAAO,SAAS,SAAW,EAAG,CAChC,IAAMC,EAAqBD,EAAO,aAC/B,SAAS,EACT,SAAS,GAAI,GAAG,EACbG,EACJ,mBAAmBH,EAAO,KAAK,EAC/B,IACAC,EAEF,KAAKE,CAAE,CACT,CACF,EAAE,SAAS,CACb,EACA,qBAAsB,CACpB,IAAK,SAAUH,EAAqC,CAC9CA,EAAO,WACXA,EAAO,SAAS,QAAQ,SAAUE,EAAS,CACzC,IAAMC,EACJ,mBAAmBH,EAAO,KAAK,EAC/B,IACA,mBAAmBE,CAAO,EAE5B,KAAKC,EAAIH,EAAO,YAAY,CAC9B,CAAC,CACH,EAAE,SAAS,EACX,OAAQ,QACV,CACF,CACF,CAAC,EAEA,MAAOI,GAAU,CAChB,GACE,EAAAA,GACA,OAAOA,GAAU,UACjB,SAAUA,GACVA,EAAM,OAAS,YAKf,MAAMA,CAEV,CAAC,EACIL,CACT,GAAG,GAEE,KAAK,GACd,CAEA,IAAc,YAAa,CACzB,OAAK,KAAK,cACR,KAAK,aAAe,SAAY,CAC9B,IAAMM,EAAW,KAAM,QAAO,+BAAiB,EAC/C,OAAOA,EAAS,YAAcA,EAAS,QAAQ,UACjD,GAAG,GAEE,KAAK,WACd,CAEA,IAAc,KAAM,CAClB,OAAK,KAAK,OACR,KAAK,KAAO,KAAK,QAAQ,IACrB,QAAQ,QAAQ,KAAK,QAAQ,GAAG,GAC/B,SAAY,CACX,GAAM,CAAE,QAASC,CAAI,EAAI,KAAM,QAAO,mBAAK,EAC3C,OAAO,IAAIA,EAAI,CAAE,OAAQ,EAAM,CAAC,CAClC,GAAG,GAEF,KAAK,IACd,CAEU,sBACRN,EACoB,CACpB,GAAM,CAAE,MAAAO,EAAO,SAAAC,EAAU,QAAAC,EAAS,IAAAC,EAAK,MAAAC,EAAO,aAAAC,CAAa,EAAIZ,EAC/D,MAAO,CACL,MAAAO,EACA,SAAAC,EACA,QAAAC,EACA,IAAAC,EACA,MAAAC,EACA,aAAAC,CACF,CACF,CAEA,YAAYC,EAAgC,CAC1C,KAAK,QAAUA,GAAW,CAAC,EAC3B,KAAK,OAAS,KAAK,QAAQ,QAAUnB,EACjC,CAAC,KAAK,OAAO,SAAS,GAAG,GAAK,CAAC,KAAK,OAAO,SAAS,GAAG,IACzD,KAAK,QAAU,IAEnB,CAEA,MAAgB,kBAAkBoB,EAAuC,CACvE,IAAMJ,EAAMK,EAAgBD,CAAS,EAAI,IAmBzC,OAlBgB,MACd,MAAM,KAAK,IACX,QAAQ,CACR,SAAUJ,EACV,OAAQA,EAAM,SACd,aAAc,EAChB,CAAC,GACoB,KAClB,IAAKM,GAAQA,EAAI,GAAG,EAEpB,OAIC,CAACC,EAAKC,KACFA,GAAKD,EAAI,KAAKC,CAAG,EACdD,GACN,CAAC,CAAC,CAET,CAEU,MAAMH,EAA8B,CAC5C,OAAOA,EAAU,IAAM,IAAMK,EAAa,CAC5C,CAEA,IAAuB,SAAUC,IAAS,CACxC,GAAM,CAACC,EAAWC,EAAQC,CAAO,EAAIH,EAQ/BI,GAHU,MAAM,KAAK,kBAAkBH,CAAS,GAGjC,OAAQH,GAC3BO,EAA6BP,EAAKK,CAAO,CAC3C,EACA,GAAI,CAACC,EAAK,OACR,MAAM,IAAIE,EACR,yFACF,EAGF,IAAMR,EAAMM,EAAK,OAAO,CAACG,EAAGC,IAC1BD,EAAE,aAAeC,EAAE,cAClBD,EAAE,eAAiBC,EAAE,cAAgB,CAACD,EAAE,WAAaC,EAAE,UACpDD,EACAC,CACN,EAEA,GAAIV,EAAI,UACN,MAAM,IAAIQ,EACR,yFACF,EAGF,IAAM1B,EAAS,KAAK,sBAAsBkB,CAAG,EAO7C,GAHAW,EAAmB7B,EAAQ,CAAC,EAAGuB,CAAO,EAGlC,CADaO,EAA4B,MAAM,KAAK,IAAKR,CAAM,EACrDtB,CAAM,EAClB,MAAM,IAAI+B,EAEZ,OAAO/B,CACT,EAUA,MAAgB,iBACdU,EACAG,EAGI,CACF,WAAY,EACd,EACA,CACA,IAAMmB,EAAoB,MAAM,KAAK,kBAAkBtB,CAAG,EACpDuB,EAAwBpB,EAAQ,QAClCmB,EAAkB,OAAQd,GACxBO,EAA6BP,EAAKL,EAAQ,OAAO,CACnD,EACAmB,EACJ,GAAKC,EAAsB,QAIpB,GACLpB,EAAQ,SACRoB,EAAsB,KAAMf,GAAQA,EAAI,QAAUL,EAAQ,SAAS,KAAK,EAExE,MAAM,IAAIqB,EACR,oDACF,MATA,OAAM,IAAIR,EACR,4FACF,EASF,IAAMS,EAAiBF,EAAsB,OAC1Cf,GAAQ,CAACA,EAAI,SAChB,EACA,GAAI,CAACiB,EAAe,OAAQ,OAG5B,IAAMC,EAAiBD,EACpB,IAAKjB,GAAQA,EAAI,YAAY,EAC7B,OAAO,CAACS,EAAGC,IAAOD,EAAIC,EAAID,EAAIC,CAAE,EAG7BS,EAAeF,EAAe,OACjCjB,GAAQ,CAACL,EAAQ,YAAcK,EAAI,aAAekB,CACrD,EAKME,EAAoBH,EAAe,OACtCjB,GAAQL,EAAQ,YAAcK,EAAI,eAAiBkB,CACtD,EACA,GAAIE,EAAkB,OAAQ,CAC5B,IAAMC,EAAYD,EACf,IAAKpB,GAAQA,EAAI,GAAG,EACpB,OAAO,CAACS,EAAGC,IAAOD,EAAIC,EAAID,EAAIC,CAAE,EAC7BY,EAAyBF,EAAkB,OAC9CpB,GAAQA,EAAI,MAAQqB,CACvB,EACAF,EAAa,KAAK,GAAGG,CAAsB,CAC7C,CAEA,IAAM5B,EAAeC,EAAQ,WACzBuB,EACA,IAAI,KAAK,EAAE,QAAQ,EAEjBK,EAAgB,MACpB,MAAM,KAAK,IACX,SACAJ,EAAa,IAAKnB,IAAS,CACzB,GAAGA,EACH,UAAW,GACX,aAAAN,CACF,EAAE,CACJ,EAGI8B,EACJ,QAAWC,KAAiBF,EAC1B,GAAI,OAAQE,EAAe,CACzB,GAAM,CAAE,GAAAxC,CAAG,EAAIwC,EACTC,EAAaP,EAAa,KAAMnB,GAAQA,EAAI,MAAQf,CAAE,EAC5D,GAAIyC,EAAY,CACdF,EAAgB,CACd,GAAG,KAAK,sBAAsBE,CAAU,EACxC,aAAAhC,CACF,EACA,KACF,CACF,CAGF,OAAO8B,CACT,CAEA,OAA6B,SAAUtB,IAAS,CAC9C,GAAM,CAACV,EAAKa,CAAO,EAAIH,EACjBsB,EAAgB,MAAM,KAAK,iBAAiBhC,EAAK,CACrD,QAAAa,CACF,CAAC,EACD,GAAI,CAACmB,EACH,MAAM,IAAIhB,EAAsB,qCAAqC,EAEvE,OAAOgB,CACT,EAEA,IAAuB,SAAUtB,IAAS,CACxC,GAAM,CAACyB,EAAetB,CAAO,EAAIH,EACjC,GAAIyB,EAAc,OAASA,EAAc,QAAUtB,EAAQ,MACzD,MAAM,IAAIW,EACR,oEACF,EAGF,GAAIW,EAAc,IAAK,CACrB,IAAIC,EACJ,GAAI,CACFA,EAAY,MAAM,KAAK,IAAID,EAAc,IAAK,CAAC,EAAGtB,CAAO,CAC3D,OAASwB,EAAG,CACV,GAAIA,aAAarB,GACf,GAAI,CAAC,KAAK,QAAQ,0BAChB,MAAM,IAAIA,EACR,sFACF,MAGF,OAAMqB,CAEV,CACA,GAAID,GAAW,QAAUvB,EAAQ,MAC/B,MAAM,IAAIW,EACR,gEACF,CAEJ,CAEA,IAAMtB,GACF,KAAK,QAAQ,2BAA6B,KAC1CiC,EAAc,cAChB,IAAI,KAAK,EAAE,QAAQ,EAEf7C,EAAsC,CAC1C,MAAO6C,EAAc,MACrB,SAAUA,EAAc,SACxB,QAASA,EAAc,QACvB,IAAKA,EAAc,KAAO,KAAK,OAAS1B,EAAa,EACrD,MAAOI,EAAQ,MACf,UAAW,GACX,aAAAX,CACF,EAEA,MACE,MAAM,KAAK,IACX,IAAI,CACJ,IAAK,KAAK,MAAMZ,CAAM,EACtB,GAAGA,CACL,CAAC,EAGD,IAAMgD,EAAiB,MAAM,KAAK,iBAAiBhD,EAAQ,CACzD,WAAY,EACd,CAAC,EACD,OAAIgD,GAGK,CACL,GAAGhD,EACH,MAAO,CAAC,EACR,SAAU,CAAC,EACX,QAAS,CAAC,EACV,UAAW,EACb,CAEJ,EAEA,MAA2B,SAAUoB,IAAS,CAC5C,GAAM,CAAC6B,EAAOvC,EAAKa,CAAO,EAAIH,EAC1B8B,EACJ,GAAI,CACFA,EAAiB,MAAM,KAAK,IAAIxC,EAAK,CAAC,EAAGa,CAAO,CAClD,OAASwB,EAAG,CACV,MAAIA,aAAarB,EACT,IAAIA,EACR,oFACF,EAEMqB,CAEV,CACA,GAAIG,EAAe,QAAU3B,EAAQ,MACnC,MAAM,IAAIW,EACR,8DACF,EAIF,IAAMiB,EAAkC,CAAE,GAAGD,CAAe,EAC5D,QAAWE,IAAQ,CAAC,QAAS,WAAY,SAAS,EAChDC,EAAmB,MAAM,KAAK,WAAYD,EAAMH,EAAOE,CAAW,EAIpE,GACE,OAAOA,EAAY,OAAU,UAC7B,MAAM,QAAQA,EAAY,KAAK,GAC/B,CAACA,EAAY,MAEb,MAAM,IAAIG,EAAwB,8BAA8B,EAIlE,GACE,CAAC,MAAM,QAAQH,EAAY,QAAQ,GACnC,CAACA,EAAY,SAAS,MAAOjD,GAAY,OAAOA,GAAY,QAAQ,EAEpE,MAAM,IAAIoD,EACR,4CACF,EAIF,GACEH,EAAY,UACX,CAAC,MAAM,QAAQA,EAAY,OAAO,GACjC,CAACA,EAAY,QAAQ,MAAO1C,GAAY,OAAOA,GAAY,QAAQ,GAErE,MAAM,IAAI6C,EACR,yCACF,EAGF,OAAAH,EAAY,aAAe,IAAI,KAAK,EAAE,QAAQ,EAC9C,MACE,MAAM,KAAK,IACX,IAAI,CACJ,GAAGA,EACH,UAAW,GACX,IAAK,KAAK,MAAMA,CAAW,CAC7B,CAAC,EAGD,MAAM,KAAK,iBAAiBA,EAAa,CACvC,WAAY,EACd,CAAC,EAEM,CACL,GAAGD,EACH,aAAcC,EAAY,YAC5B,CACF,EAEU,0BACR7B,EACAV,EACA,CAEA,IAAI2C,EAAiB,GACjBC,EAAe,SACnB,GACE,OAAOlC,GAAW,UAClBA,EAAO,YAAY,cACnB,OAAOA,EAAO,WAAW,cAAiB,SAC1C,CACA,IAAMmC,EAAqBnC,EAAO,WAAW,aAEvCoC,EACJ9C,GAAgB6C,EAAmB,QAC/B,KAAK,IAAI7C,EAAc6C,EAAmB,OAAO,EAChD7C,GAAgB6C,EAAmB,QACpCE,EAAmBF,EAAmB,iBAExCG,EACAD,IAAqB,QACvBC,EAAa,KAAK,KAAKD,CAAgB,EACvCC,IAAeD,GAAoBC,KAC1BF,IAAY,SACrBE,EAAa,KAAK,KAAKF,CAAO,GAG5BE,IAAe,SACjBL,EAAiBK,EAAW,SAAS,EAAE,SAAS,GAAI,GAAG,GAGzD,IAAMC,EAAUJ,EAAmB,QAC7BK,EAAmBL,EAAmB,iBAExCM,EACAD,IAAqB,QACvBC,EAAa,KAAK,MAAMD,CAAgB,EACxCC,IAAeD,GAAoBC,KAC1BF,IAAY,SACrBE,EAAa,KAAK,MAAMF,CAAO,GAG7BE,IAAe,SACjBP,EAAeO,EAAW,SAAS,EAAE,SAAS,GAAI,GAAG,EAEzD,CACA,MAAO,CACL,eAAAR,EACA,aAAAC,CACF,CACF,CAEA,MAAiB,cACfQ,EACAC,EACAC,EACAC,EACA5C,EACA6C,EACA5D,EACA6D,EAC2D,CACvDD,IAAoB,SAEtBA,GAAmBzE,GAGrB,IAAM2E,EAAS,MACb,MAAM,KAAK,IACX,MAAmCN,EAAO,CAC1C,SAAAC,EACA,OAAAC,EACA,aAAc,EAChB,CAAC,EAED,QAAWlD,KAAOsD,EAAO,KAAM,CAC7B,IAAMpD,EAAMF,EAAI,IAOhB,GANI,CAACE,GAEDmD,GAAc,IAAInD,EAAI,GAAG,IAC7BmD,GAAc,IAAInD,EAAI,GAAG,EAGrBkD,IAAoB,QAAalD,EAAI,WAAW,SAEpD,IAAMlB,EAAS,KAAK,sBAAsBkB,CAAG,EAE7C,GAAIV,EAAU,CACZ,GAAI,CAACiB,EAA6BzB,EAAQuB,CAAO,EAAG,SACpDM,EAAmB7B,EAAQQ,EAAUe,CAAO,CAC9C,CAEK4C,EAASnE,CAAM,IAEpB,MAAMkB,EAAI,UACN,CACE,UAAW,GACX,OAAQ,CACN,IAAKlB,EAAO,IACZ,aAAcA,EAAO,YACvB,CACF,EACA,CAAE,OAAAA,CAAO,EACf,CACF,CAEA,MAAgB,eAAeoE,EAAqC,CAClE,GAAIA,IAAoB,OAAW,OACnC,IAAMG,EAAiB,KAAK,QAAQ,gBAAkB,IAChDC,EAA2B,KAAK,IAAI,EAAIJ,EAC1CI,EAA2BD,GAG7B,MAAM,IAAI,QAASE,GACjB,WAAWA,EAASF,EAAiBC,CAAwB,CAC/D,CAEJ,CAEA,MAAiB,aACfpD,EACAgD,EAIA,CACA,MAAM,KAAK,eAAeA,CAAe,EAEzC,GAAM,CAAC5D,EAAUc,EAAQC,CAAO,EAAIH,EAC9B+C,EAAWrC,EAA4B,MAAM,KAAK,IAAKR,CAAM,EAC7D,CAAE,eAAAiC,EAAgB,aAAAC,CAAa,EAAI,KAAK,0BAC5ClC,EACA8C,CACF,EAEMC,EAAe,IAAI,IAEnBK,EAAY,IAAI,KAAK,EAAE,QAAQ,EAErC,QAAWxE,KAAWM,EAAU,CAC9B,IAAMmE,EAAY,mBAAmBzE,CAAO,EAAI,IAC1C+D,EAAWU,EAAYpB,EACvBW,EAASS,EAAYnB,EAErBoB,EAAW,KAAK,cACpB,2CACAX,EACAC,EACAC,EACA5C,EACA6C,EACA5D,EACA6D,CACF,EAEA,cAAiBC,KAAUM,EAAU,MAAMN,CAC7C,CAEA,OAAOI,CACT,CAEA,MAAiB,mBACftD,EACAgD,EAIA,CACA,MAAM,KAAK,eAAeA,CAAe,EAEzC,GAAM,CAAC9C,EAAQC,CAAO,EAAIH,EACpB,CAAE,eAAAmC,EAAgB,aAAAC,CAAa,EAAI,KAAK,0BAC5ClC,EACA8C,CACF,EACMO,EAAY,mBAAmBpD,EAAQ,KAAK,EAAI,IAChD0C,EAAWU,EAAYpB,EACvBW,EAASS,EAAYnB,EAErBW,EAAWrC,EAA4B,MAAM,KAAK,IAAKR,CAAM,EAE7DoD,EAAY,IAAI,KAAK,EAAE,QAAQ,EAE/BE,EAAW,KAAK,cACpB,yCACAX,EACAC,EACAC,EACA5C,EACA6C,CACF,EAEA,cAAiBE,KAAUM,EAAU,MAAMN,EAE3C,OAAOI,CACT,CAEU,eACRtD,EACAgD,EACQ,CACR,MACE,YACA,KAAK,UAAU,CACb,SAAUhD,EAAK,CAAC,EAChB,OAAQA,EAAK,CAAC,EACd,MAAOA,EAAK,CAAC,GAAG,MAChB,gBAAiBgD,CACnB,CAAC,CAEL,CAEA,MAAiB,iBACfhD,EACAgD,EACsC,CACtC,IAAMQ,EAAW,KAAK,aAAaxD,EAAMgD,CAAe,EAExD,OAAa,CACX,IAAME,EAAS,MAAMM,EAAS,KAAK,EACnC,GAAIN,EAAO,KAAM,CACf,IAAMF,EAAkBE,EAAO,MAC/B,MAAO,CACL,SAAU,IAAM,KAAK,iBAAyBlD,EAAMgD,CAAe,EACnE,OAAQ,KAAK,eAAehD,EAAMgD,CAAe,CACnD,CACF,CACA,MAAME,EAAO,KACf,CACF,CAEA,SAAiC,IAAIlD,IAAS,CAC5C,IAAMwD,EAAW,KAAK,aAAaxD,CAAI,EAEjCyD,EAAQ,KACd,OAAQ,iBAAmB,CACzB,OAAa,CACX,IAAMP,EAAS,MAAMM,EAAS,KAAK,EACnC,GAAIN,EAAO,KACT,MAAO,CACL,SAAU,IACRO,EAAM,iBAAmCzD,EAAMkD,EAAO,KAAK,EAC7D,OAAQO,EAAM,eAAezD,EAAMkD,EAAO,KAAK,CACjD,EAGEA,EAAO,MAAM,YACjB,MAAMA,EAAO,MACf,CACF,EAAG,CACL,EAEU,qBACRlD,EACAgD,EACQ,CACR,MACE,WACA,KAAK,UAAU,CACb,OAAQhD,EAAK,CAAC,EACd,MAAOA,EAAK,CAAC,GAAG,MAChB,gBAAAgD,CACF,CAAC,CAEL,CAEA,MAAiB,uBACfhD,EACAgD,EACsC,CACtC,IAAMQ,EAAW,KAAK,mBAAmBxD,EAAMgD,CAAe,EAE9D,OAAa,CACX,IAAME,EAAS,MAAMM,EAAS,KAAK,EACnC,GAAIN,EAAO,KAAM,CACf,IAAMF,EAAkBE,EAAO,MAC/B,MAAO,CACL,SAAU,IACR,KAAK,uBAA+BlD,EAAMgD,CAAe,EAC3D,OAAQ,KAAK,qBAAqBhD,EAAMgD,CAAe,CACzD,CACF,CACA,MAAME,EAAO,KACf,CACF,CAEA,eAA6C,IAAIlD,IAAS,CACxD,IAAMwD,EAAW,KAAK,mBAAmBxD,CAAI,EAEvCyD,EAAQ,KACd,OAAQ,iBAAmB,CACzB,OAAa,CACX,IAAMP,EAAS,MAAMM,EAAS,KAAK,EACnC,GAAIN,EAAO,KACT,MAAO,CACL,SAAU,IACRO,EAAM,uBACJzD,EACAkD,EAAO,KACT,EACF,OAAQO,EAAM,qBAAqBzD,EAAMkD,EAAO,KAAK,CACvD,EAGEA,EAAO,MAAM,YACjB,MAAMA,EAAO,MACf,CACF,EAAG,CACL,EAEA,aAA0C/C,GAAY,CACpD,IAAMsD,EAAQ,KACd,OAAQ,iBAAmB,CACzB,IAAMF,EAAY,mBAAmBpD,EAAQ,KAAK,EAAI,IAChD+C,EAAS,MACb,MAAMO,EAAM,IACZ,MAAM,+BAAgC,CACtC,SAAUF,EACV,OAAQA,EAAY,SACpB,OAAQ,GACR,MAAO,EACT,CAAC,EACD,QAAW3D,KAAOsD,EAAO,KAAM,CAC7B,IAAMQ,EAAiB9D,EAAI,IAAI,MAAM,GAAG,EAAE,CAAC,EAC3C,GAAI,OAAO8D,GAAmB,SAAU,SACxC,GAAM,CAAE,MAAAC,EAAO,IAAKnE,CAAa,EAAII,EAAI,MACrC,OAAO+D,GAAU,UAAY,OAAOnE,GAAiB,WAEzD,KAAM,CACJ,MAAO,CACL,QAAS,mBAAmBkE,CAAc,EAC1C,MAAAC,EACA,aAAAnE,CACF,CACF,EACF,CACF,EAAG,CACL,EAEA,qBAAyD,CACvDoE,EACAzD,IACG,CACH,GAAIyD,EAAO,WAAW,WAAW,EAAG,CAClC,GAAM,CAAE,SAAAxE,EAAU,OAAAc,EAAQ,MAAAX,EAAO,gBAAAyD,CAAgB,EAAI,KAAK,MACxDY,EAAO,MAAM,CAAkB,CACjC,EACA,GAAIrE,GAASA,IAAUY,GAAS,MAC9B,MAAM,IAAIW,EACR,4CACF,EAEF,OAAO,KAAK,iBACV,CAAC1B,EAAUc,EAAQC,CAAO,EAC1B6C,CACF,CACF,SAAWY,EAAO,WAAW,UAAU,EAAG,CACxC,GAAM,CAAE,OAAA1D,EAAQ,MAAAX,EAAO,gBAAAyD,CAAgB,EAAI,KAAK,MAC9CY,EAAO,MAAM,CAAiB,CAChC,EACA,GAAI,CAACzD,GAAWZ,IAAUY,GAAS,MACjC,MAAM,IAAIW,EACR,4CACF,EAEF,OAAO,KAAK,uBACV,CAACZ,EAAQC,CAAO,EAChB6C,CACF,CACF,KACE,OAAM,IAAI1C,EAAsB,kBAAkB,CAEtD,CACF,ELx4BO,IAAMuD,EAAN,cAA4BC,CAAS,CAChC,oBAAsB,IAAIC,EACpC,MAAQ,KAAK,oBAAoB,MAAM,KAAK,KAAK,mBAAmB,EACpE,OAAS,KAAK,oBAAoB,OAAO,KAAK,KAAK,mBAAmB,EACtE,cAAgB,KAAK,oBAAoB,cAEzC,IACA,IACA,MACA,OACA,SACA,eACA,aACA,qBAEA,YAAYC,EAAgC,CAC1C,MAAM,EAEN,IAAMC,EAAsB,IAAIC,EAAsBF,CAAO,EAE7D,KAAK,IAAMC,EAAoB,IAAI,KAAKA,CAAmB,EAC3D,KAAK,IAAMA,EAAoB,IAAI,KAAKA,CAAmB,EAC3D,KAAK,MAAQA,EAAoB,MAAM,KAAKA,CAAmB,EAC/D,KAAK,OAASA,EAAoB,OAAO,KAAKA,CAAmB,EACjE,KAAK,SAAWA,EAAoB,SAAS,KAAKA,CAAmB,EACrE,KAAK,eACHA,EAAoB,eAAe,KAAKA,CAAmB,EAC7D,KAAK,aACHA,EAAoB,aAAa,KAAKA,CAAmB,EAC3D,KAAK,qBACHA,EAAoB,qBAAqB,KAAKA,CAAmB,CACrE,CACF",
|
|
6
|
-
"names": ["init_dirname", "init_buffer", "init_process", "init_dirname", "init_buffer", "init_process", "
|
|
3
|
+
"sources": ["../../node_modules/negotiator/lib/charset.js", "../../node_modules/negotiator/lib/encoding.js", "../../node_modules/negotiator/lib/language.js", "../../node_modules/negotiator/lib/mediaType.js", "../../node_modules/negotiator/index.js", "../../src/index.ts", "../../node_modules/@graffiti-garden/api/src/1-api.ts", "../../node_modules/@graffiti-garden/api/src/2-types.ts", "../../node_modules/@graffiti-garden/api/src/3-errors.ts", "../../node_modules/@graffiti-garden/api/src/4-utilities.ts", "../../src/identity.ts", "../../src/utilities.ts", "../../src/objects.ts", "../../src/media.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * negotiator\n * Copyright(c) 2012 Isaac Z. Schlueter\n * Copyright(c) 2014 Federico Romero\n * Copyright(c) 2014-2015 Douglas Christopher Wilson\n * MIT Licensed\n */\n\n'use strict';\n\n/**\n * Module exports.\n * @public\n */\n\nmodule.exports = preferredCharsets;\nmodule.exports.preferredCharsets = preferredCharsets;\n\n/**\n * Module variables.\n * @private\n */\n\nvar simpleCharsetRegExp = /^\\s*([^\\s;]+)\\s*(?:;(.*))?$/;\n\n/**\n * Parse the Accept-Charset header.\n * @private\n */\n\nfunction parseAcceptCharset(accept) {\n var accepts = accept.split(',');\n\n for (var i = 0, j = 0; i < accepts.length; i++) {\n var charset = parseCharset(accepts[i].trim(), i);\n\n if (charset) {\n accepts[j++] = charset;\n }\n }\n\n // trim accepts\n accepts.length = j;\n\n return accepts;\n}\n\n/**\n * Parse a charset from the Accept-Charset header.\n * @private\n */\n\nfunction parseCharset(str, i) {\n var match = simpleCharsetRegExp.exec(str);\n if (!match) return null;\n\n var charset = match[1];\n var q = 1;\n if (match[2]) {\n var params = match[2].split(';')\n for (var j = 0; j < params.length; j++) {\n var p = params[j].trim().split('=');\n if (p[0] === 'q') {\n q = parseFloat(p[1]);\n break;\n }\n }\n }\n\n return {\n charset: charset,\n q: q,\n i: i\n };\n}\n\n/**\n * Get the priority of a charset.\n * @private\n */\n\nfunction getCharsetPriority(charset, accepted, index) {\n var priority = {o: -1, q: 0, s: 0};\n\n for (var i = 0; i < accepted.length; i++) {\n var spec = specify(charset, accepted[i], index);\n\n if (spec && (priority.s - spec.s || priority.q - spec.q || priority.o - spec.o) < 0) {\n priority = spec;\n }\n }\n\n return priority;\n}\n\n/**\n * Get the specificity of the charset.\n * @private\n */\n\nfunction specify(charset, spec, index) {\n var s = 0;\n if(spec.charset.toLowerCase() === charset.toLowerCase()){\n s |= 1;\n } else if (spec.charset !== '*' ) {\n return null\n }\n\n return {\n i: index,\n o: spec.i,\n q: spec.q,\n s: s\n }\n}\n\n/**\n * Get the preferred charsets from an Accept-Charset header.\n * @public\n */\n\nfunction preferredCharsets(accept, provided) {\n // RFC 2616 sec 14.2: no header = *\n var accepts = parseAcceptCharset(accept === undefined ? '*' : accept || '');\n\n if (!provided) {\n // sorted list of all charsets\n return accepts\n .filter(isQuality)\n .sort(compareSpecs)\n .map(getFullCharset);\n }\n\n var priorities = provided.map(function getPriority(type, index) {\n return getCharsetPriority(type, accepts, index);\n });\n\n // sorted list of accepted charsets\n return priorities.filter(isQuality).sort(compareSpecs).map(function getCharset(priority) {\n return provided[priorities.indexOf(priority)];\n });\n}\n\n/**\n * Compare two specs.\n * @private\n */\n\nfunction compareSpecs(a, b) {\n return (b.q - a.q) || (b.s - a.s) || (a.o - b.o) || (a.i - b.i) || 0;\n}\n\n/**\n * Get full charset string.\n * @private\n */\n\nfunction getFullCharset(spec) {\n return spec.charset;\n}\n\n/**\n * Check if a spec has any quality.\n * @private\n */\n\nfunction isQuality(spec) {\n return spec.q > 0;\n}\n", "/**\n * negotiator\n * Copyright(c) 2012 Isaac Z. Schlueter\n * Copyright(c) 2014 Federico Romero\n * Copyright(c) 2014-2015 Douglas Christopher Wilson\n * MIT Licensed\n */\n\n'use strict';\n\n/**\n * Module exports.\n * @public\n */\n\nmodule.exports = preferredEncodings;\nmodule.exports.preferredEncodings = preferredEncodings;\n\n/**\n * Module variables.\n * @private\n */\n\nvar simpleEncodingRegExp = /^\\s*([^\\s;]+)\\s*(?:;(.*))?$/;\n\n/**\n * Parse the Accept-Encoding header.\n * @private\n */\n\nfunction parseAcceptEncoding(accept) {\n var accepts = accept.split(',');\n var hasIdentity = false;\n var minQuality = 1;\n\n for (var i = 0, j = 0; i < accepts.length; i++) {\n var encoding = parseEncoding(accepts[i].trim(), i);\n\n if (encoding) {\n accepts[j++] = encoding;\n hasIdentity = hasIdentity || specify('identity', encoding);\n minQuality = Math.min(minQuality, encoding.q || 1);\n }\n }\n\n if (!hasIdentity) {\n /*\n * If identity doesn't explicitly appear in the accept-encoding header,\n * it's added to the list of acceptable encoding with the lowest q\n */\n accepts[j++] = {\n encoding: 'identity',\n q: minQuality,\n i: i\n };\n }\n\n // trim accepts\n accepts.length = j;\n\n return accepts;\n}\n\n/**\n * Parse an encoding from the Accept-Encoding header.\n * @private\n */\n\nfunction parseEncoding(str, i) {\n var match = simpleEncodingRegExp.exec(str);\n if (!match) return null;\n\n var encoding = match[1];\n var q = 1;\n if (match[2]) {\n var params = match[2].split(';');\n for (var j = 0; j < params.length; j++) {\n var p = params[j].trim().split('=');\n if (p[0] === 'q') {\n q = parseFloat(p[1]);\n break;\n }\n }\n }\n\n return {\n encoding: encoding,\n q: q,\n i: i\n };\n}\n\n/**\n * Get the priority of an encoding.\n * @private\n */\n\nfunction getEncodingPriority(encoding, accepted, index) {\n var priority = {encoding: encoding, o: -1, q: 0, s: 0};\n\n for (var i = 0; i < accepted.length; i++) {\n var spec = specify(encoding, accepted[i], index);\n\n if (spec && (priority.s - spec.s || priority.q - spec.q || priority.o - spec.o) < 0) {\n priority = spec;\n }\n }\n\n return priority;\n}\n\n/**\n * Get the specificity of the encoding.\n * @private\n */\n\nfunction specify(encoding, spec, index) {\n var s = 0;\n if(spec.encoding.toLowerCase() === encoding.toLowerCase()){\n s |= 1;\n } else if (spec.encoding !== '*' ) {\n return null\n }\n\n return {\n encoding: encoding,\n i: index,\n o: spec.i,\n q: spec.q,\n s: s\n }\n};\n\n/**\n * Get the preferred encodings from an Accept-Encoding header.\n * @public\n */\n\nfunction preferredEncodings(accept, provided, preferred) {\n var accepts = parseAcceptEncoding(accept || '');\n\n var comparator = preferred ? function comparator (a, b) {\n if (a.q !== b.q) {\n return b.q - a.q // higher quality first\n }\n\n var aPreferred = preferred.indexOf(a.encoding)\n var bPreferred = preferred.indexOf(b.encoding)\n\n if (aPreferred === -1 && bPreferred === -1) {\n // consider the original specifity/order\n return (b.s - a.s) || (a.o - b.o) || (a.i - b.i)\n }\n\n if (aPreferred !== -1 && bPreferred !== -1) {\n return aPreferred - bPreferred // consider the preferred order\n }\n\n return aPreferred === -1 ? 1 : -1 // preferred first\n } : compareSpecs;\n\n if (!provided) {\n // sorted list of all encodings\n return accepts\n .filter(isQuality)\n .sort(comparator)\n .map(getFullEncoding);\n }\n\n var priorities = provided.map(function getPriority(type, index) {\n return getEncodingPriority(type, accepts, index);\n });\n\n // sorted list of accepted encodings\n return priorities.filter(isQuality).sort(comparator).map(function getEncoding(priority) {\n return provided[priorities.indexOf(priority)];\n });\n}\n\n/**\n * Compare two specs.\n * @private\n */\n\nfunction compareSpecs(a, b) {\n return (b.q - a.q) || (b.s - a.s) || (a.o - b.o) || (a.i - b.i);\n}\n\n/**\n * Get full encoding string.\n * @private\n */\n\nfunction getFullEncoding(spec) {\n return spec.encoding;\n}\n\n/**\n * Check if a spec has any quality.\n * @private\n */\n\nfunction isQuality(spec) {\n return spec.q > 0;\n}\n", "/**\n * negotiator\n * Copyright(c) 2012 Isaac Z. Schlueter\n * Copyright(c) 2014 Federico Romero\n * Copyright(c) 2014-2015 Douglas Christopher Wilson\n * MIT Licensed\n */\n\n'use strict';\n\n/**\n * Module exports.\n * @public\n */\n\nmodule.exports = preferredLanguages;\nmodule.exports.preferredLanguages = preferredLanguages;\n\n/**\n * Module variables.\n * @private\n */\n\nvar simpleLanguageRegExp = /^\\s*([^\\s\\-;]+)(?:-([^\\s;]+))?\\s*(?:;(.*))?$/;\n\n/**\n * Parse the Accept-Language header.\n * @private\n */\n\nfunction parseAcceptLanguage(accept) {\n var accepts = accept.split(',');\n\n for (var i = 0, j = 0; i < accepts.length; i++) {\n var language = parseLanguage(accepts[i].trim(), i);\n\n if (language) {\n accepts[j++] = language;\n }\n }\n\n // trim accepts\n accepts.length = j;\n\n return accepts;\n}\n\n/**\n * Parse a language from the Accept-Language header.\n * @private\n */\n\nfunction parseLanguage(str, i) {\n var match = simpleLanguageRegExp.exec(str);\n if (!match) return null;\n\n var prefix = match[1]\n var suffix = match[2]\n var full = prefix\n\n if (suffix) full += \"-\" + suffix;\n\n var q = 1;\n if (match[3]) {\n var params = match[3].split(';')\n for (var j = 0; j < params.length; j++) {\n var p = params[j].split('=');\n if (p[0] === 'q') q = parseFloat(p[1]);\n }\n }\n\n return {\n prefix: prefix,\n suffix: suffix,\n q: q,\n i: i,\n full: full\n };\n}\n\n/**\n * Get the priority of a language.\n * @private\n */\n\nfunction getLanguagePriority(language, accepted, index) {\n var priority = {o: -1, q: 0, s: 0};\n\n for (var i = 0; i < accepted.length; i++) {\n var spec = specify(language, accepted[i], index);\n\n if (spec && (priority.s - spec.s || priority.q - spec.q || priority.o - spec.o) < 0) {\n priority = spec;\n }\n }\n\n return priority;\n}\n\n/**\n * Get the specificity of the language.\n * @private\n */\n\nfunction specify(language, spec, index) {\n var p = parseLanguage(language)\n if (!p) return null;\n var s = 0;\n if(spec.full.toLowerCase() === p.full.toLowerCase()){\n s |= 4;\n } else if (spec.prefix.toLowerCase() === p.full.toLowerCase()) {\n s |= 2;\n } else if (spec.full.toLowerCase() === p.prefix.toLowerCase()) {\n s |= 1;\n } else if (spec.full !== '*' ) {\n return null\n }\n\n return {\n i: index,\n o: spec.i,\n q: spec.q,\n s: s\n }\n};\n\n/**\n * Get the preferred languages from an Accept-Language header.\n * @public\n */\n\nfunction preferredLanguages(accept, provided) {\n // RFC 2616 sec 14.4: no header = *\n var accepts = parseAcceptLanguage(accept === undefined ? '*' : accept || '');\n\n if (!provided) {\n // sorted list of all languages\n return accepts\n .filter(isQuality)\n .sort(compareSpecs)\n .map(getFullLanguage);\n }\n\n var priorities = provided.map(function getPriority(type, index) {\n return getLanguagePriority(type, accepts, index);\n });\n\n // sorted list of accepted languages\n return priorities.filter(isQuality).sort(compareSpecs).map(function getLanguage(priority) {\n return provided[priorities.indexOf(priority)];\n });\n}\n\n/**\n * Compare two specs.\n * @private\n */\n\nfunction compareSpecs(a, b) {\n return (b.q - a.q) || (b.s - a.s) || (a.o - b.o) || (a.i - b.i) || 0;\n}\n\n/**\n * Get full language string.\n * @private\n */\n\nfunction getFullLanguage(spec) {\n return spec.full;\n}\n\n/**\n * Check if a spec has any quality.\n * @private\n */\n\nfunction isQuality(spec) {\n return spec.q > 0;\n}\n", "/**\n * negotiator\n * Copyright(c) 2012 Isaac Z. Schlueter\n * Copyright(c) 2014 Federico Romero\n * Copyright(c) 2014-2015 Douglas Christopher Wilson\n * MIT Licensed\n */\n\n'use strict';\n\n/**\n * Module exports.\n * @public\n */\n\nmodule.exports = preferredMediaTypes;\nmodule.exports.preferredMediaTypes = preferredMediaTypes;\n\n/**\n * Module variables.\n * @private\n */\n\nvar simpleMediaTypeRegExp = /^\\s*([^\\s\\/;]+)\\/([^;\\s]+)\\s*(?:;(.*))?$/;\n\n/**\n * Parse the Accept header.\n * @private\n */\n\nfunction parseAccept(accept) {\n var accepts = splitMediaTypes(accept);\n\n for (var i = 0, j = 0; i < accepts.length; i++) {\n var mediaType = parseMediaType(accepts[i].trim(), i);\n\n if (mediaType) {\n accepts[j++] = mediaType;\n }\n }\n\n // trim accepts\n accepts.length = j;\n\n return accepts;\n}\n\n/**\n * Parse a media type from the Accept header.\n * @private\n */\n\nfunction parseMediaType(str, i) {\n var match = simpleMediaTypeRegExp.exec(str);\n if (!match) return null;\n\n var params = Object.create(null);\n var q = 1;\n var subtype = match[2];\n var type = match[1];\n\n if (match[3]) {\n var kvps = splitParameters(match[3]).map(splitKeyValuePair);\n\n for (var j = 0; j < kvps.length; j++) {\n var pair = kvps[j];\n var key = pair[0].toLowerCase();\n var val = pair[1];\n\n // get the value, unwrapping quotes\n var value = val && val[0] === '\"' && val[val.length - 1] === '\"'\n ? val.slice(1, -1)\n : val;\n\n if (key === 'q') {\n q = parseFloat(value);\n break;\n }\n\n // store parameter\n params[key] = value;\n }\n }\n\n return {\n type: type,\n subtype: subtype,\n params: params,\n q: q,\n i: i\n };\n}\n\n/**\n * Get the priority of a media type.\n * @private\n */\n\nfunction getMediaTypePriority(type, accepted, index) {\n var priority = {o: -1, q: 0, s: 0};\n\n for (var i = 0; i < accepted.length; i++) {\n var spec = specify(type, accepted[i], index);\n\n if (spec && (priority.s - spec.s || priority.q - spec.q || priority.o - spec.o) < 0) {\n priority = spec;\n }\n }\n\n return priority;\n}\n\n/**\n * Get the specificity of the media type.\n * @private\n */\n\nfunction specify(type, spec, index) {\n var p = parseMediaType(type);\n var s = 0;\n\n if (!p) {\n return null;\n }\n\n if(spec.type.toLowerCase() == p.type.toLowerCase()) {\n s |= 4\n } else if(spec.type != '*') {\n return null;\n }\n\n if(spec.subtype.toLowerCase() == p.subtype.toLowerCase()) {\n s |= 2\n } else if(spec.subtype != '*') {\n return null;\n }\n\n var keys = Object.keys(spec.params);\n if (keys.length > 0) {\n if (keys.every(function (k) {\n return spec.params[k] == '*' || (spec.params[k] || '').toLowerCase() == (p.params[k] || '').toLowerCase();\n })) {\n s |= 1\n } else {\n return null\n }\n }\n\n return {\n i: index,\n o: spec.i,\n q: spec.q,\n s: s,\n }\n}\n\n/**\n * Get the preferred media types from an Accept header.\n * @public\n */\n\nfunction preferredMediaTypes(accept, provided) {\n // RFC 2616 sec 14.2: no header = */*\n var accepts = parseAccept(accept === undefined ? '*/*' : accept || '');\n\n if (!provided) {\n // sorted list of all types\n return accepts\n .filter(isQuality)\n .sort(compareSpecs)\n .map(getFullType);\n }\n\n var priorities = provided.map(function getPriority(type, index) {\n return getMediaTypePriority(type, accepts, index);\n });\n\n // sorted list of accepted types\n return priorities.filter(isQuality).sort(compareSpecs).map(function getType(priority) {\n return provided[priorities.indexOf(priority)];\n });\n}\n\n/**\n * Compare two specs.\n * @private\n */\n\nfunction compareSpecs(a, b) {\n return (b.q - a.q) || (b.s - a.s) || (a.o - b.o) || (a.i - b.i) || 0;\n}\n\n/**\n * Get full type string.\n * @private\n */\n\nfunction getFullType(spec) {\n return spec.type + '/' + spec.subtype;\n}\n\n/**\n * Check if a spec has any quality.\n * @private\n */\n\nfunction isQuality(spec) {\n return spec.q > 0;\n}\n\n/**\n * Count the number of quotes in a string.\n * @private\n */\n\nfunction quoteCount(string) {\n var count = 0;\n var index = 0;\n\n while ((index = string.indexOf('\"', index)) !== -1) {\n count++;\n index++;\n }\n\n return count;\n}\n\n/**\n * Split a key value pair.\n * @private\n */\n\nfunction splitKeyValuePair(str) {\n var index = str.indexOf('=');\n var key;\n var val;\n\n if (index === -1) {\n key = str;\n } else {\n key = str.slice(0, index);\n val = str.slice(index + 1);\n }\n\n return [key, val];\n}\n\n/**\n * Split an Accept header into media types.\n * @private\n */\n\nfunction splitMediaTypes(accept) {\n var accepts = accept.split(',');\n\n for (var i = 1, j = 0; i < accepts.length; i++) {\n if (quoteCount(accepts[j]) % 2 == 0) {\n accepts[++j] = accepts[i];\n } else {\n accepts[j] += ',' + accepts[i];\n }\n }\n\n // trim accepts\n accepts.length = j + 1;\n\n return accepts;\n}\n\n/**\n * Split a string of parameters.\n * @private\n */\n\nfunction splitParameters(str) {\n var parameters = str.split(';');\n\n for (var i = 1, j = 0; i < parameters.length; i++) {\n if (quoteCount(parameters[j]) % 2 == 0) {\n parameters[++j] = parameters[i];\n } else {\n parameters[j] += ';' + parameters[i];\n }\n }\n\n // trim parameters\n parameters.length = j + 1;\n\n for (var i = 0; i < parameters.length; i++) {\n parameters[i] = parameters[i].trim();\n }\n\n return parameters;\n}\n", "/*!\n * negotiator\n * Copyright(c) 2012 Federico Romero\n * Copyright(c) 2012-2014 Isaac Z. Schlueter\n * Copyright(c) 2015 Douglas Christopher Wilson\n * MIT Licensed\n */\n\n'use strict';\n\nvar preferredCharsets = require('./lib/charset')\nvar preferredEncodings = require('./lib/encoding')\nvar preferredLanguages = require('./lib/language')\nvar preferredMediaTypes = require('./lib/mediaType')\n\n/**\n * Module exports.\n * @public\n */\n\nmodule.exports = Negotiator;\nmodule.exports.Negotiator = Negotiator;\n\n/**\n * Create a Negotiator instance from a request.\n * @param {object} request\n * @public\n */\n\nfunction Negotiator(request) {\n if (!(this instanceof Negotiator)) {\n return new Negotiator(request);\n }\n\n this.request = request;\n}\n\nNegotiator.prototype.charset = function charset(available) {\n var set = this.charsets(available);\n return set && set[0];\n};\n\nNegotiator.prototype.charsets = function charsets(available) {\n return preferredCharsets(this.request.headers['accept-charset'], available);\n};\n\nNegotiator.prototype.encoding = function encoding(available, opts) {\n var set = this.encodings(available, opts);\n return set && set[0];\n};\n\nNegotiator.prototype.encodings = function encodings(available, options) {\n var opts = options || {};\n return preferredEncodings(this.request.headers['accept-encoding'], available, opts.preferred);\n};\n\nNegotiator.prototype.language = function language(available) {\n var set = this.languages(available);\n return set && set[0];\n};\n\nNegotiator.prototype.languages = function languages(available) {\n return preferredLanguages(this.request.headers['accept-language'], available);\n};\n\nNegotiator.prototype.mediaType = function mediaType(available) {\n var set = this.mediaTypes(available);\n return set && set[0];\n};\n\nNegotiator.prototype.mediaTypes = function mediaTypes(available) {\n return preferredMediaTypes(this.request.headers.accept, available);\n};\n\n// Backwards compatibility\nNegotiator.prototype.preferredCharset = Negotiator.prototype.charset;\nNegotiator.prototype.preferredCharsets = Negotiator.prototype.charsets;\nNegotiator.prototype.preferredEncoding = Negotiator.prototype.encoding;\nNegotiator.prototype.preferredEncodings = Negotiator.prototype.encodings;\nNegotiator.prototype.preferredLanguage = Negotiator.prototype.language;\nNegotiator.prototype.preferredLanguages = Negotiator.prototype.languages;\nNegotiator.prototype.preferredMediaType = Negotiator.prototype.mediaType;\nNegotiator.prototype.preferredMediaTypes = Negotiator.prototype.mediaTypes;\n", "import { Graffiti, type GraffitiSession } from \"@graffiti-garden/api\";\nimport { GraffitiLocalIdentity } from \"./identity\";\nimport { GraffitiLocalObjects, type GraffitiLocalOptions } from \"./objects\";\nimport { GraffitiLocalMedia } from \"./media\";\n\nexport type { GraffitiLocalOptions };\n\n/**\n * A local implementation of the [Graffiti API](https://api.graffiti.garden/classes/Graffiti.html)\n * based on [PouchDB](https://pouchdb.com/). PouchDb will automatically persist data in a local\n * database, either in the browser or in Node.js.\n * It can also be configured to work with an external [CouchDB](https://couchdb.apache.org/) server,\n * although using it with a remote server will not be secure.\n */\nexport class GraffitiLocal implements Graffiti {\n protected graffitiLocalIdentity = new GraffitiLocalIdentity();\n login = this.graffitiLocalIdentity.login.bind(this.graffitiLocalIdentity);\n logout = this.graffitiLocalIdentity.logout.bind(this.graffitiLocalIdentity);\n handleToActor = this.graffitiLocalIdentity.handleToActor.bind(\n this.graffitiLocalIdentity,\n );\n actorToHandle = this.graffitiLocalIdentity.actorToHandle.bind(\n this.graffitiLocalIdentity,\n );\n sessionEvents = this.graffitiLocalIdentity.sessionEvents;\n\n protected graffitiLocalObjects: GraffitiLocalObjects;\n post: Graffiti[\"post\"];\n get: Graffiti[\"get\"];\n delete: Graffiti[\"delete\"];\n discover: Graffiti[\"discover\"];\n continueDiscover: Graffiti[\"continueDiscover\"];\n\n protected graffitiLocalMedia: GraffitiLocalMedia;\n postMedia: Graffiti[\"postMedia\"];\n getMedia: Graffiti[\"getMedia\"];\n deleteMedia: Graffiti[\"deleteMedia\"];\n\n constructor(options?: GraffitiLocalOptions) {\n this.graffitiLocalObjects = new GraffitiLocalObjects(options);\n this.post = this.graffitiLocalObjects.post.bind(this.graffitiLocalObjects);\n this.get = this.graffitiLocalObjects.get.bind(this.graffitiLocalObjects);\n this.delete = this.graffitiLocalObjects.delete.bind(\n this.graffitiLocalObjects,\n );\n this.discover = this.graffitiLocalObjects.discover.bind(\n this.graffitiLocalObjects,\n );\n this.continueDiscover = this.graffitiLocalObjects.continueDiscover.bind(\n this.graffitiLocalObjects,\n );\n\n this.graffitiLocalMedia = new GraffitiLocalMedia(this.graffitiLocalObjects);\n this.postMedia = this.graffitiLocalMedia.postMedia.bind(\n this.graffitiLocalMedia,\n );\n this.getMedia = this.graffitiLocalMedia.getMedia.bind(\n this.graffitiLocalMedia,\n );\n this.deleteMedia = this.graffitiLocalMedia.deleteMedia.bind(\n this.graffitiLocalMedia,\n );\n }\n}\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 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<\n GraffitiPostObject<Schema> & {\n url: string;\n actor: string;\n }\n >;\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 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: (\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 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", "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", "import type {\n Graffiti,\n GraffitiLoginEvent,\n GraffitiLogoutEvent,\n GraffitiSessionInitializedEvent,\n} from \"@graffiti-garden/api\";\nimport { decodeBase64, encodeBase64 } from \"./utilities\";\n\nconst DID_LOCAL_PREFIX = \"did:local:\";\n\n/**\n * A class that implements the login methods\n * of the [Graffiti API]() for use in the browser.\n * It is completely insecure and should only be used\n * for testing and demonstrations.\n *\n * It uses `localStorage` to store login state and\n * window prompts rather than an oauth flow for log in.\n * It can be used in node.js but will not persist\n * login state and a proposed username must be provided.\n */\nexport class GraffitiLocalIdentity {\n sessionEvents: Graffiti[\"sessionEvents\"] = new EventTarget();\n\n handleToActor: Graffiti[\"handleToActor\"] = async (handle: string) => {\n const bytes = new TextEncoder().encode(handle);\n const base64 = encodeBase64(bytes);\n return `${DID_LOCAL_PREFIX}${base64}`;\n };\n\n actorToHandle: Graffiti[\"actorToHandle\"] = async (actor: string) => {\n if (!actor.startsWith(DID_LOCAL_PREFIX)) {\n throw new Error(`actor must start with ${DID_LOCAL_PREFIX}`);\n }\n const base64 = actor.slice(DID_LOCAL_PREFIX.length);\n const bytes = decodeBase64(base64);\n return new TextDecoder().decode(bytes);\n };\n\n constructor() {\n // Look for any existing sessions\n const sessionRestorer = async () => {\n // Allow listeners to be added first\n await Promise.resolve();\n\n // Restore previous sessions\n for (const handle of this.getLoggedInHandles()) {\n const event: GraffitiLoginEvent = new CustomEvent(\"login\", {\n detail: { session: { actor: await this.handleToActor(handle) } },\n });\n this.sessionEvents.dispatchEvent(event);\n }\n\n const event: GraffitiSessionInitializedEvent = new CustomEvent(\n \"initialized\",\n { detail: {} },\n );\n this.sessionEvents.dispatchEvent(event);\n };\n sessionRestorer();\n }\n\n loggedInHandles: string[] = [];\n\n protected getLoggedInHandles(): string[] {\n if (typeof window !== \"undefined\") {\n const handlesString = window.localStorage.getItem(\"graffiti-handles\");\n return handlesString\n ? handlesString.split(\",\").map(decodeURIComponent)\n : [];\n } else {\n return this.loggedInHandles;\n }\n }\n\n protected setLoggedInHandles(handles: string[]) {\n if (typeof window !== \"undefined\") {\n window.localStorage.setItem(\"graffiti-handles\", handles.join(\",\"));\n } else {\n this.loggedInHandles = handles;\n }\n }\n\n login: Graffiti[\"login\"] = async (actor) => {\n // Wait a tick for the browser to update the UI\n await new Promise((resolve) => setTimeout(resolve, 0));\n\n let handle = actor ? await this.actorToHandle(actor) : undefined;\n\n if (typeof window !== \"undefined\") {\n const response = window.prompt(\"Choose a username to log in.\", handle);\n handle = response ?? undefined;\n }\n\n if (!handle) {\n const detail: GraffitiLoginEvent[\"detail\"] = {\n error: new Error(\"No handle provided to login\"),\n };\n const event: GraffitiLoginEvent = new CustomEvent(\"login\", { detail });\n this.sessionEvents.dispatchEvent(event);\n } else {\n const existingHandles = this.getLoggedInHandles();\n if (!existingHandles.includes(handle)) {\n this.setLoggedInHandles([...existingHandles, handle]);\n }\n // Refresh the page to simulate oauth\n window.location.reload();\n }\n };\n\n logout: Graffiti[\"logout\"] = async (session) => {\n const handle = await this.actorToHandle(session.actor);\n const existingHandles = this.getLoggedInHandles();\n const exists = existingHandles.includes(handle);\n if (exists) {\n this.setLoggedInHandles(existingHandles.filter((h) => h !== handle));\n }\n\n const detail: GraffitiLogoutEvent[\"detail\"] = exists\n ? {\n actor: session.actor,\n }\n : {\n actor: session.actor,\n error: new Error(\"Not logged in with that actor\"),\n };\n\n const event: GraffitiLogoutEvent = new CustomEvent(\"logout\", { detail });\n this.sessionEvents.dispatchEvent(event);\n };\n}\n", "export function encodeBase64(bytes: Uint8Array): string {\n // Convert it to base64\n const base64 = btoa(String.fromCodePoint(...bytes));\n // Make sure it is url safe\n return base64.replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/\\=+$/, \"\");\n}\n\nexport function decodeBase64(base64Url: string): Uint8Array {\n // Undo url-safe base64\n let base64 = base64Url.replace(/-/g, \"+\").replace(/_/g, \"/\");\n // Add padding if necessary\n while (base64.length % 4 !== 0) base64 += \"=\";\n // Decode\n return Uint8Array.from(atob(base64), (c) => c.charCodeAt(0));\n}\n\nexport function randomBase64(numBytes: number = 32): string {\n // Generate random bytes\n const bytes = new Uint8Array(numBytes);\n crypto.getRandomValues(bytes);\n return encodeBase64(bytes);\n}\n\nconst OBJECT_URL_PREFIX = \"graffiti:object:\";\nconst MEDIA_URL_PREFIX = \"graffiti:media:\";\n\nexport function encodeGraffitiUrl(actor: string, id: string, prefix: string) {\n return `${prefix}${encodeURIComponent(actor)}:${encodeURIComponent(id)}`;\n}\nexport function encodeObjectUrl(actor: string, id: string) {\n return encodeGraffitiUrl(actor, id, OBJECT_URL_PREFIX);\n}\nexport function encodeMediaUrl(actor: string, id: string) {\n return encodeGraffitiUrl(actor, id, MEDIA_URL_PREFIX);\n}\n\nexport function decodeGraffitiUrl(url: string, prefix: string) {\n if (!url.startsWith(prefix)) {\n throw new Error(`URL does not start with ${prefix}`);\n }\n const slices = url.slice(prefix.length).split(\":\");\n if (slices.length !== 2) {\n throw new Error(\"URL has too many colon-seperated parts\");\n }\n const [actor, id] = slices.map(decodeURIComponent);\n return { actor, id };\n}\nexport function decodeObjectUrl(url: string) {\n return decodeGraffitiUrl(url, OBJECT_URL_PREFIX);\n}\nexport function decodeMediaUrl(url: string) {\n return decodeGraffitiUrl(url, MEDIA_URL_PREFIX);\n}\n\nexport async function blobToBase64(blob: Blob): Promise<string> {\n if (typeof FileReader !== \"undefined\") {\n return new Promise((resolve, reject) => {\n const r = new FileReader();\n r.onload = () => {\n if (typeof r.result === \"string\") {\n resolve(r.result);\n } else {\n reject(new Error(\"Unexpected result type\"));\n }\n };\n r.onerror = reject;\n r.readAsDataURL(blob);\n });\n }\n\n if (typeof Buffer !== \"undefined\") {\n const ab = await blob.arrayBuffer();\n return `data:${blob.type};base64,${Buffer.from(ab).toString(\"base64\")}`;\n }\n\n throw new Error(\"Unsupported environment\");\n}\n\nexport async function base64ToBlob(dataUrl: string) {\n const response = await fetch(dataUrl);\n return await response.blob();\n}\n", "import type {\n Graffiti,\n GraffitiObjectBase,\n JSONSchema,\n GraffitiSession,\n GraffitiObjectStreamContinue,\n GraffitiObjectStreamContinueEntry,\n} from \"@graffiti-garden/api\";\nimport {\n GraffitiErrorNotFound,\n GraffitiErrorSchemaMismatch,\n GraffitiErrorForbidden,\n unpackObjectUrl,\n maskGraffitiObject,\n isActorAllowedGraffitiObject,\n compileGraffitiObjectSchema,\n} from \"@graffiti-garden/api\";\nimport { randomBase64, decodeObjectUrl, encodeObjectUrl } from \"./utilities.js\";\nimport type Ajv from \"ajv\";\n\n/**\n * Constructor options for the GraffitiPoubchDB class.\n */\nexport interface GraffitiLocalOptions {\n /**\n * Options to pass to the PouchDB constructor.\n * Defaults to `{ name: \"graffitiDb\" }`.\n *\n * See the [PouchDB documentation](https://pouchdb.com/api.html#create_database)\n * for available options.\n */\n pouchDBOptions?: PouchDB.Configuration.DatabaseConfiguration;\n /**\n * Wait at least this long (in milliseconds) before continuing a stream.\n * A basic form of rate limiting. Defaults to 2 seconds.\n */\n continueBuffer?: number;\n}\n\ntype GraffitiObjectData = {\n tombstone: boolean;\n value: {};\n channels: string[];\n allowed?: string[] | null;\n lastModified: number;\n};\n\ntype ContinueDiscoverParams = {\n lastDiscovered: number;\n ifModifiedSince: number;\n};\n\n/**\n * An implementation of only the database operations of the\n * GraffitiAPI without synchronization or session management.\n */\nexport class GraffitiLocalObjects {\n protected db_: Promise<PouchDB.Database<GraffitiObjectData>> | undefined;\n protected ajv_: Promise<Ajv> | undefined;\n protected readonly options: GraffitiLocalOptions;\n\n get db() {\n if (!this.db_) {\n this.db_ = (async () => {\n const { default: PouchDB } = await import(\"pouchdb\");\n const pouchDbOptions = {\n name: \"graffitiDb\",\n ...this.options.pouchDBOptions,\n };\n const db = new PouchDB<GraffitiObjectData>(\n pouchDbOptions.name,\n pouchDbOptions,\n );\n await db\n //@ts-ignore\n .put({\n _id: \"_design/indexes\",\n views: {\n objectsPerChannelAndLastModified: {\n map: function (object: GraffitiObjectData) {\n const paddedLastModified = object.lastModified\n .toString()\n .padStart(15, \"0\");\n object.channels.forEach(function (channel) {\n const id =\n encodeURIComponent(channel) + \"/\" + paddedLastModified;\n //@ts-ignore\n emit(id);\n });\n }.toString(),\n },\n },\n })\n //@ts-ignore\n .catch((error) => {\n if (\n error &&\n typeof error === \"object\" &&\n \"name\" in error &&\n error.name === \"conflict\"\n ) {\n // Design document already exists\n return;\n } else {\n throw error;\n }\n });\n return db;\n })();\n }\n return this.db_;\n }\n\n protected get ajv() {\n if (!this.ajv_) {\n this.ajv_ = (async () => {\n const { default: Ajv } = await import(\"ajv\");\n return new Ajv({ strict: false });\n })();\n }\n return this.ajv_;\n }\n\n protected async getOperationClock() {\n return Number((await (await this.db).info()).update_seq);\n }\n\n constructor(options?: GraffitiLocalOptions) {\n this.options = options ?? {};\n }\n\n get: Graffiti[\"get\"] = async (...args) => {\n const [urlObject, schema, session] = args;\n const url = unpackObjectUrl(urlObject);\n\n let doc: GraffitiObjectData;\n try {\n doc = await (await this.db).get(url);\n } catch (error) {\n throw new GraffitiErrorNotFound(\n \"The object you are trying to get either does not exist or you are not allowed to see it\",\n );\n }\n\n if (doc.tombstone) {\n throw new GraffitiErrorNotFound(\n \"The object you are trying to get either does not exist or you are not allowed to see it\",\n );\n }\n\n const { actor } = decodeObjectUrl(url);\n const { value, channels, allowed } = doc;\n const object: GraffitiObjectBase = {\n value,\n channels,\n allowed,\n url,\n actor,\n };\n\n if (!isActorAllowedGraffitiObject(object, session)) {\n throw new GraffitiErrorNotFound(\n \"The object you are trying to get either does not exist or you are not allowed to see it\",\n );\n }\n\n // Mask out the allowed list and channels\n // if the user is not the owner\n maskGraffitiObject(object, [], session);\n\n const validate = compileGraffitiObjectSchema(await this.ajv, schema);\n if (!validate(object)) {\n throw new GraffitiErrorSchemaMismatch();\n }\n return object;\n };\n\n delete: Graffiti[\"delete\"] = async (...args) => {\n const [urlObject, session] = args;\n\n const url = unpackObjectUrl(urlObject);\n const { actor } = decodeObjectUrl(url);\n if (actor !== session.actor) {\n throw new GraffitiErrorForbidden(\n \"You cannot delete an object that you did not create.\",\n );\n }\n\n let doc: GraffitiObjectData & PouchDB.Core.IdMeta & PouchDB.Core.GetMeta;\n try {\n doc = await (await this.db).get(url);\n } catch {\n throw new GraffitiErrorNotFound(\"Object not found.\");\n }\n\n if (doc.tombstone) {\n throw new GraffitiErrorNotFound(\"Object not found.\");\n }\n\n // Set the tombstone and update lastModified\n doc.tombstone = true;\n doc.lastModified = await this.getOperationClock();\n try {\n await (await this.db).put(doc);\n } catch {\n throw new GraffitiErrorNotFound(\"Object not found.\");\n }\n\n return;\n };\n\n post: Graffiti[\"post\"] = async (...args) => {\n const [objectPartial, session] = args;\n\n const actor = session.actor;\n const id = randomBase64();\n const url = encodeObjectUrl(actor, id);\n\n const { value, channels, allowed } = objectPartial;\n const object: GraffitiObjectData = {\n value,\n channels,\n allowed,\n lastModified: await this.getOperationClock(),\n tombstone: false,\n };\n\n await (\n await this.db\n ).put({\n _id: url,\n ...object,\n });\n\n return {\n ...objectPartial,\n actor,\n url,\n };\n };\n\n protected async *discoverMeta<Schema extends JSONSchema>(\n args: Parameters<typeof Graffiti.prototype.discover<Schema>>,\n continueParams?: {\n lastDiscovered: number;\n ifModifiedSince: number;\n },\n ): AsyncGenerator<\n GraffitiObjectStreamContinueEntry<Schema>,\n ContinueDiscoverParams\n > {\n // If we are continuing a discover, make sure to wait at\n // least 2 seconds since the last poll to start a new one.\n if (continueParams) {\n const continueBuffer = this.options.continueBuffer ?? 2000;\n const timeElapsedSinceLastDiscover =\n Date.now() - continueParams.lastDiscovered;\n if (timeElapsedSinceLastDiscover < continueBuffer) {\n // Continue was called too soon,\n // wait a bit before continuing\n await new Promise((resolve) =>\n setTimeout(resolve, continueBuffer - timeElapsedSinceLastDiscover),\n );\n }\n }\n\n const [discoverChannels, schema, session] = args;\n const validate = compileGraffitiObjectSchema(await this.ajv, schema);\n const startKeySuffix = continueParams\n ? continueParams.ifModifiedSince.toString().padStart(15, \"0\")\n : \"\";\n const endKeySuffix = \"\\uffff\";\n\n const processedUrls = new Set<string>();\n\n const startTime = await this.getOperationClock();\n\n for (const channel of discoverChannels) {\n const keyPrefix = encodeURIComponent(channel) + \"/\";\n const startkey = keyPrefix + startKeySuffix;\n const endkey = keyPrefix + endKeySuffix;\n\n const result = await (\n await this.db\n ).query<GraffitiObjectData>(\"indexes/objectsPerChannelAndLastModified\", {\n startkey,\n endkey,\n include_docs: true,\n });\n\n for (const row of result.rows) {\n const doc = row.doc;\n if (!doc) continue;\n\n const url = doc._id;\n\n if (processedUrls.has(url)) continue;\n processedUrls.add(url);\n\n // If this is not a continuation, skip tombstones\n if (!continueParams && doc.tombstone) continue;\n\n const { tombstone, value, channels, allowed } = doc;\n const { actor } = decodeObjectUrl(url);\n\n const object: GraffitiObjectBase = {\n url,\n value,\n allowed,\n channels,\n actor,\n };\n\n if (!isActorAllowedGraffitiObject(object, session)) continue;\n\n maskGraffitiObject(object, discoverChannels, session);\n\n if (!validate(object)) continue;\n\n yield tombstone\n ? {\n tombstone: true,\n object: { url },\n }\n : { object };\n }\n }\n\n return {\n lastDiscovered: Date.now(),\n ifModifiedSince: startTime,\n };\n }\n\n protected discoverCursor(\n args: Parameters<typeof Graffiti.prototype.discover<{}>>,\n continueParams: {\n lastDiscovered: number;\n ifModifiedSince: number;\n },\n ): string {\n const [channels, schema, session] = args;\n return (\n \"discover:\" +\n JSON.stringify({\n channels,\n schema,\n continueParams,\n actor: session?.actor,\n })\n );\n }\n\n protected async *discoverContinue<Schema extends JSONSchema>(\n args: Parameters<typeof Graffiti.prototype.discover<Schema>>,\n continueParams: {\n lastDiscovered: number;\n ifModifiedSince: number;\n },\n session?: GraffitiSession | null,\n ): GraffitiObjectStreamContinue<Schema> {\n if (session?.actor !== args[2]?.actor) {\n throw new GraffitiErrorForbidden(\n \"Cannot continue a cursor started by another actor\",\n );\n }\n const iterator = this.discoverMeta<Schema>(args, continueParams);\n\n while (true) {\n const result = await iterator.next();\n if (result.done) {\n return {\n continue: (session) =>\n this.discoverContinue<Schema>(args, result.value, session),\n cursor: this.discoverCursor(args, result.value),\n };\n }\n yield result.value;\n }\n }\n\n discover: Graffiti[\"discover\"] = (...args) => {\n const [channels, schema, session] = args;\n const iterator = this.discoverMeta<(typeof args)[1]>([\n channels,\n schema,\n session,\n ]);\n\n const this_ = this;\n return (async function* () {\n while (true) {\n const result = await iterator.next();\n if (result.done) {\n return {\n continue: (session) =>\n this_.discoverContinue<(typeof args)[1]>(\n args,\n result.value,\n session,\n ),\n cursor: this_.discoverCursor(args, result.value),\n };\n }\n // Make sure to filter out tombstones\n if (result.value.tombstone) continue;\n yield result.value;\n }\n })();\n };\n\n continueDiscover: Graffiti[\"continueDiscover\"] = (...args) => {\n const [cursor, session] = args;\n if (cursor.startsWith(\"discover:\")) {\n // TODO: use AJV here\n const { channels, schema, actor, continueParams } = JSON.parse(\n cursor.slice(\"discover:\".length),\n );\n if (actor && actor !== session?.actor) {\n throw new GraffitiErrorForbidden(\n \"Cannot continue a cursor started by another actor\",\n );\n }\n return this.discoverContinue<{}>(\n [channels, schema, session],\n continueParams,\n );\n } else {\n throw new GraffitiErrorNotFound(\"Cursor not found\");\n }\n };\n}\n", "import {\n GraffitiErrorNotAcceptable,\n GraffitiErrorTooLarge,\n type Graffiti,\n type JSONSchema,\n} from \"@graffiti-garden/api\";\nimport {\n decodeObjectUrl,\n encodeObjectUrl,\n decodeMediaUrl,\n encodeMediaUrl,\n blobToBase64,\n base64ToBlob,\n} from \"./utilities\";\nimport Negotiator from \"negotiator\";\n\nconst MEDIA_OBJECT_SCHEMA = {\n properties: {\n value: {\n properties: {\n dataBase64: { type: \"string\" },\n type: { type: \"string\" },\n size: { type: \"number\" },\n },\n required: [\"dataBase64\", \"type\", \"size\"],\n },\n },\n} as const satisfies JSONSchema;\n\nexport class GraffitiLocalMedia {\n protected db: Pick<Graffiti, \"post\" | \"get\" | \"delete\">;\n\n constructor(db: Pick<Graffiti, \"post\" | \"get\" | \"delete\">) {\n this.db = db;\n }\n\n postMedia: Graffiti[\"postMedia\"] = async (...args) => {\n const [media, session] = args;\n\n const dataBase64 = await blobToBase64(media.data);\n const type = media.data.type;\n\n const { url } = await this.db.post<typeof MEDIA_OBJECT_SCHEMA>(\n {\n value: {\n dataBase64,\n type,\n size: media.data.size,\n },\n channels: [],\n allowed: media.allowed,\n },\n session,\n );\n\n const { actor, id } = decodeObjectUrl(url);\n return encodeMediaUrl(actor, id);\n };\n\n getMedia: Graffiti[\"getMedia\"] = async (...args) => {\n const [mediaUrl, requirements, session] = args;\n const { actor, id } = decodeMediaUrl(mediaUrl);\n const objectUrl = encodeObjectUrl(actor, id);\n\n const object = await this.db.get<typeof MEDIA_OBJECT_SCHEMA>(\n objectUrl,\n MEDIA_OBJECT_SCHEMA,\n session,\n );\n\n const { dataBase64, type, size } = object.value;\n\n if (requirements?.maxBytes && size > requirements.maxBytes) {\n throw new GraffitiErrorTooLarge(\"File size exceeds limit\");\n }\n\n // Make sure it adheres to requirements.accept\n if (requirements?.accept) {\n const negotiator = new Negotiator({\n headers: { accept: requirements.accept },\n });\n if (negotiator.mediaType([type]) !== type) {\n throw new GraffitiErrorNotAcceptable(`Unsupported media type, ${type}`);\n }\n }\n\n const data = await base64ToBlob(dataBase64);\n if (data.size !== size || data.type !== type) {\n throw new Error(\"Invalid data\");\n }\n\n return {\n data,\n actor: object.actor,\n allowed: object.allowed,\n };\n };\n\n deleteMedia: Graffiti[\"deleteMedia\"] = async (...args) => {\n const [mediaUrl, session] = args;\n const { actor, id } = decodeMediaUrl(mediaUrl);\n const objectUrl = encodeObjectUrl(actor, id);\n\n await this.db.delete(objectUrl, session);\n };\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["require_charset", "__commonJSMin", "exports", "module", "init_dirname", "init_buffer", "init_process", "preferredCharsets", "simpleCharsetRegExp", "parseAcceptCharset", "accept", "accepts", "i", "j", "charset", "parseCharset", "str", "match", "q", "params", "p", "getCharsetPriority", "accepted", "index", "priority", "spec", "specify", "s", "provided", "isQuality", "compareSpecs", "getFullCharset", "priorities", "type", "a", "b", "require_encoding", "__commonJSMin", "exports", "module", "init_dirname", "init_buffer", "init_process", "preferredEncodings", "simpleEncodingRegExp", "parseAcceptEncoding", "accept", "accepts", "hasIdentity", "minQuality", "i", "j", "encoding", "parseEncoding", "specify", "str", "match", "q", "params", "p", "getEncodingPriority", "accepted", "index", "priority", "spec", "s", "provided", "preferred", "comparator", "a", "b", "aPreferred", "bPreferred", "compareSpecs", "isQuality", "getFullEncoding", "priorities", "type", "require_language", "__commonJSMin", "exports", "module", "init_dirname", "init_buffer", "init_process", "preferredLanguages", "simpleLanguageRegExp", "parseAcceptLanguage", "accept", "accepts", "i", "j", "language", "parseLanguage", "str", "match", "prefix", "suffix", "full", "q", "params", "getLanguagePriority", "accepted", "index", "priority", "spec", "specify", "p", "s", "provided", "isQuality", "compareSpecs", "getFullLanguage", "priorities", "type", "a", "b", "require_mediaType", "__commonJSMin", "exports", "module", "init_dirname", "init_buffer", "init_process", "preferredMediaTypes", "simpleMediaTypeRegExp", "parseAccept", "accept", "accepts", "splitMediaTypes", "i", "j", "mediaType", "parseMediaType", "str", "match", "params", "q", "subtype", "type", "kvps", "splitParameters", "splitKeyValuePair", "pair", "key", "val", "value", "getMediaTypePriority", "accepted", "index", "priority", "spec", "specify", "p", "s", "keys", "k", "provided", "isQuality", "compareSpecs", "getFullType", "priorities", "a", "b", "quoteCount", "string", "count", "parameters", "require_negotiator", "__commonJSMin", "exports", "module", "init_dirname", "init_buffer", "init_process", "preferredCharsets", "preferredEncodings", "preferredLanguages", "preferredMediaTypes", "Negotiator", "request", "available", "set", "opts", "options", "init_dirname", "init_buffer", "init_process", "init_dirname", "init_buffer", "init_process", "GraffitiObjectJSONSchema", "GraffitiPostObjectJSONSchema", "GraffitiErrorForbidden", "_GraffitiErrorForbidden", "message", "GraffitiErrorNotFound", "_GraffitiErrorNotFound", "GraffitiErrorInvalidSchema", "_GraffitiErrorInvalidSchema", "GraffitiErrorSchemaMismatch", "_GraffitiErrorSchemaMismatch", "GraffitiErrorTooLarge", "_GraffitiErrorTooLarge", "GraffitiErrorNotAcceptable", "_GraffitiErrorNotAcceptable", "unpackObjectUrl", "url", "compileGraffitiObjectSchema", "ajv", "schema", "error", "isActorAllowedGraffitiObject", "object", "session", "maskGraffitiObject", "channels", "channel", "init_dirname", "init_buffer", "init_process", "init_dirname", "init_buffer", "init_process", "encodeBase64", "bytes", "decodeBase64", "base64Url", "base64", "c", "randomBase64", "numBytes", "OBJECT_URL_PREFIX", "MEDIA_URL_PREFIX", "encodeGraffitiUrl", "actor", "id", "prefix", "encodeObjectUrl", "encodeMediaUrl", "decodeGraffitiUrl", "url", "slices", "decodeObjectUrl", "decodeMediaUrl", "blobToBase64", "blob", "resolve", "reject", "Buffer", "ab", "base64ToBlob", "dataUrl", "DID_LOCAL_PREFIX", "GraffitiLocalIdentity", "handle", "bytes", "base64", "encodeBase64", "actor", "decodeBase64", "event", "handlesString", "handles", "resolve", "existingHandles", "detail", "session", "exists", "h", "init_dirname", "init_buffer", "init_process", "GraffitiLocalObjects", "PouchDB", "pouchDbOptions", "db", "object", "paddedLastModified", "channel", "id", "error", "Ajv", "options", "args", "urlObject", "schema", "session", "url", "d", "doc", "n", "actor", "decodeObjectUrl", "value", "channels", "allowed", "j", "y", "G", "c", "o", "objectPartial", "randomBase64", "encodeObjectUrl", "continueParams", "continueBuffer", "timeElapsedSinceLastDiscover", "resolve", "discoverChannels", "validate", "startKeySuffix", "endKeySuffix", "processedUrls", "startTime", "keyPrefix", "startkey", "endkey", "result", "row", "tombstone", "iterator", "this_", "cursor", "init_dirname", "init_buffer", "init_process", "import_negotiator", "MEDIA_OBJECT_SCHEMA", "GraffitiLocalMedia", "db", "args", "media", "session", "dataBase64", "blobToBase64", "type", "url", "actor", "id", "decodeObjectUrl", "encodeMediaUrl", "mediaUrl", "requirements", "decodeMediaUrl", "objectUrl", "encodeObjectUrl", "object", "size", "f", "Negotiator", "m", "data", "base64ToBlob", "GraffitiLocal", "GraffitiLocalIdentity", "options", "GraffitiLocalObjects", "GraffitiLocalMedia"]
|
|
7
7
|
}
|