From b9b15c9c094af9c7867098d6ae4a472c7bee5475 Mon Sep 17 00:00:00 2001 From: Jackson Morgan Date: Tue, 1 Apr 2025 22:48:50 -0400 Subject: [PATCH] connected documentation --- packages/connected/src/ConnectedLdoDataset.ts | 75 ++++++++++++++++++- .../src/ConnectedLdoTransactionDataset.ts | 65 ++++++++++------ packages/connected/src/ConnectedPlugin.ts | 40 +++++++++- .../connected/src/IConnectedLdoDataset.ts | 74 +++++++++++++++++- .../src/InvalidIdentifierResource.ts | 4 + packages/connected/src/Resource.ts | 62 +++++++++++++++ .../connected/src/SubscriptionCallbacks.ts | 10 +++ .../src/createConntectedLdoDataset.ts | 22 ++++++ packages/connected/src/index.ts | 1 - packages/connected/src/methods.ts | 14 ++-- .../connected/src/results/ResourceResult.ts | 7 -- 11 files changed, 331 insertions(+), 43 deletions(-) delete mode 100644 packages/connected/src/results/ResourceResult.ts diff --git a/packages/connected/src/ConnectedLdoDataset.ts b/packages/connected/src/ConnectedLdoDataset.ts index 44fab4e..7200b81 100644 --- a/packages/connected/src/ConnectedLdoDataset.ts +++ b/packages/connected/src/ConnectedLdoDataset.ts @@ -25,9 +25,17 @@ import type { SubjectNode } from "@ldo/rdf-utils"; * import { createConnectedLdoDataset } from "@ldo/connected"; * import { ProfileShapeType } from "./.ldo/profile.shapeTypes.ts" * + * // At least one plugin needs to be provided to a ConnectedLdoDataset. In this + * // example we'll use both the Solid and NextGraph plugins. + * import { solidConnectedPlugin } from "@ldo/connected-solid"; + * import { nextGraphConnectedPlugin } from "@ldo/connected-nextgraph"; + * * // ... * - * const connectedLdoDataset = createConnectedLdoDataset(); + * const connectedLdoDataset = createConnectedLdoDataset([ + * solidConnectedPlugin, + * nextGraphConnectedPlugin + * ]); * * const profileDocument = connectedLdoDataset * .getResource("https://example.com/profile"); @@ -64,6 +72,15 @@ export class ConnectedLdoDataset< */ protected context: ConnectedContext; + /** + * It is recommended to use the `createConnectedLdoDataset` function to + * instantiate a ConnectedLdoDataset. + * + * @param plugins An array of plugins for each platform to connect to + * @param datasetFactory Creates Datasets + * @param transactionDatasetFactory Creates Transaction Datasets + * @param initialDataset Initial quads + */ constructor( plugins: Plugins, datasetFactory: DatasetFactory, @@ -143,11 +160,26 @@ export class ConnectedLdoDataset< return resource as any; } + /** + * Generates a random uri and creates a resource. + * + * @param pluginName - A string name for the platform you'd like to create + * the resource on. + * @param createResourceOptions - Some set of options specific to the plugin + * you've selected. + * @returns A created resource or an error + * + * @example + * ```typescript + * const profileDocument = await connectedLdoDataset + * .createResource("solid"); + * ``` + */ async createResource< Name extends Plugins[number]["name"], Plugin extends Extract, >( - name: Name, + pluginName: Name, createResourceOptions?: Plugin["types"]["createResourceOptions"], ): Promise> { const validPlugin = this.plugins.find((plugin) => name === plugin.name)!; @@ -164,12 +196,30 @@ export class ConnectedLdoDataset< return newResourceResult as any; } + /** + * Removes a resource from local memory + * @param uri - the URI of the resource to remove + * @returns true if the resource was present before removal + * + * @example + * ```typescript + * connectedLdoDataset.forgetResource("https://example.com/resource.ttl"); + * ``` + */ forgetResource(uri: string): boolean { const plugin = this.getValidPlugin(uri); const normalizedUri = plugin?.normalizeUri?.(uri) ?? uri; return this.resourceMap.delete(normalizedUri); } + /** + * Removes all resources from memory + * + * @example + * ```typescript + * connectedLdoDataset.forgetAllResources(); + * ``` + */ forgetAllResources(): void { this.resourceMap.clear(); } @@ -182,6 +232,19 @@ export class ConnectedLdoDataset< * @param shapeType - The shapetype to represent the data * @param subject - A subject URI * @param resources - The resources changes to should written to + * + * @example + * ```typescript + * import { ProfielShapeType } from "./.ldo/foafProfile.shapeType.ts" + * + * const resource = connectedLdoDataset + * .getResource("https://example.com/profile"); + * const profile = connectedLdoDataset.createData( + * ProfileShapeType, + * "https://example.com/profile#me", + * resource + * ); + * ``` */ createData( shapeType: ShapeType, @@ -197,10 +260,16 @@ export class ConnectedLdoDataset< return linkedDataObject; } + /** + * Sets conetext for a specific plugin + * + * @param pluginName - the name of the plugin + * @param context - the context for this specific plugin + */ setContext< Name extends Plugins[number]["name"], Plugin extends Extract, - >(name: Name, context: Plugin["types"]["context"]) { + >(pluginName: Name, context: Plugin["types"]["context"]) { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore this.context[name] = context; diff --git a/packages/connected/src/ConnectedLdoTransactionDataset.ts b/packages/connected/src/ConnectedLdoTransactionDataset.ts index 98b420f..f3e8155 100644 --- a/packages/connected/src/ConnectedLdoTransactionDataset.ts +++ b/packages/connected/src/ConnectedLdoTransactionDataset.ts @@ -32,24 +32,30 @@ import type { * * @example * ```typescript - * import { createSolidLdoDataset } from "@ldo/solid"; + * import { createConnectedLdoDataset } from "@ldo/connected"; * import { ProfileShapeType } from "./.ldo/profile.shapeTypes.ts" + * import { solidConnectedPlugin } from "connected-solid"; * * // ... * - * const solidLdoDataset = createSolidLdoDataset(); + * const connectedLdoDataset = createConnectedLdoDataset([ + * solidConnectedPlugin + * ]); * - * const profileDocument = solidLdoDataset + * const profileDocument = connectedLdoDataset * .getResource("https://example.com/profile"); * await profileDocument.read(); * - * const transaction = solidLdoDataset.startTransaction(); + * const transaction = connectedLdoDataset.startTransaction(); * * const profile = transaction * .using(ProfileShapeType) * .fromSubject("https://example.com/profile#me"); * profile.name = "Some Name"; - * await transaction.commitToPod(); + * const result = await transaction.commitToRemote(); + * if (result.isError) { + * // handle error + * } * ``` */ export class ConnectedLdoTransactionDataset @@ -113,23 +119,6 @@ export class ConnectedLdoTransactionDataset this.context.dataset.forgetAllResources(); } - /** - * Retireves a representation (either a LeafResource or a ContainerResource) - * of a Solid Resource at the given URI. This resource represents the - * current state of the resource: whether it is currently fetched or in the - * process of fetching as well as some information about it. - * - * @param uri - the URI of the resource - * @param options - Special options for getting the resource - * - * @returns a Leaf or Container Resource - * - * @example - * ```typescript - * const profileDocument = solidLdoDataset - * .getResource("https://example.com/profile"); - * ``` - */ public startTransaction(): ConnectedLdoTransactionDataset { return new ConnectedLdoTransactionDataset( this, @@ -139,6 +128,38 @@ export class ConnectedLdoTransactionDataset ); } + /** + * Commits all changes made in this transaction to the remote connected + * platforms as well as the parent dataset. + * + * @returns A success or failure + * + * @example + * ```typescript + * import { createConnectedLdoDataset } from "@ldo/connected"; + * import { ProfileShapeType } from "./.ldo/profile.shapeTypes.ts" + * import { solidConnectedPlugin } from "connected-solid"; + * + * // ... + * + * const connectedLdoDataset = createConnectedLdoDataset([solidConnectedPlugin]); + * + * const profileDocument = connectedLdoDataset + * .getResource("https://example.com/profile"); + * await profileDocument.read(); + * + * const transaction = connectedLdoDataset.startTransaction(); + * + * const profile = transaction + * .using(ProfileShapeType) + * .fromSubject("https://example.com/profile#me"); + * profile.name = "Some Name"; + * const result = await transaction.commitToRemote(); + * if (result.isError) { + * // handle error + * } + * ``` + */ async commitToRemote(): Promise< | AggregateSuccess< | Extract< diff --git a/packages/connected/src/ConnectedPlugin.ts b/packages/connected/src/ConnectedPlugin.ts index f5f6a19..4ed7306 100644 --- a/packages/connected/src/ConnectedPlugin.ts +++ b/packages/connected/src/ConnectedPlugin.ts @@ -3,6 +3,10 @@ import type { ConnectedContext } from "./ConnectedContext"; import type { Resource } from "./Resource"; import type { ErrorResult } from "./results/error/ErrorResult"; +/** + * A ConnectedPlugin can be passed to a ConnectedDataset to allow it to connect + * to a remote platform. + */ export interface ConnectedPlugin< Name extends string = any, UriType extends string = any, @@ -10,17 +14,49 @@ export interface ConnectedPlugin< ContextType = any, CreateResourceOptions = any, > { + /** + * The name of the plugin ("solid" for example). + */ name: Name; + /** + * A function that returns a newly minted resource on this platform. This + * function does not fetch the resource or interface with a cache. + * @param uri - The uri of the resource + * @param context - The context for the plugin + */ getResource(uri: UriType, context: ConnectedContext): ResourceType; + /** + * A function that will create a resource on the remote at a random URI. + * @param context - the context for the plugin + * @param createResourceOptions - special options for creating a resource that + * varies based on the plugin + */ createResource( context: ConnectedContext, createResourceOptions?: CreateResourceOptions, ): Promise; + /** + * Checks if a specific uri is valid for this plugin + * @param uri - the URI to check + * @returns true if this is a valid URI + */ isUriValid(uri: string): uri is UriType; + /** + * Optional function that takes in a URI and returns a normalized uri. For + * example `https://example.com/profile#me` becomes + * `https://example.com/profile`. + * @param uri - The uri to normalize + * @returns - The normalized uri + */ normalizeUri?: (uri: UriType) => UriType; + /** + * A starting context + */ initialContext: ContextType; - // This object exists to transfer typescript types. It does not need to be - // filled out in an actual instance. + /** + * This object exists to transfer typescript types. It does not need to be + * filled out in an actual instance. + */ types: { uri: UriType; context: ContextType; diff --git a/packages/connected/src/IConnectedLdoDataset.ts b/packages/connected/src/IConnectedLdoDataset.ts index a683a41..73c99a9 100644 --- a/packages/connected/src/IConnectedLdoDataset.ts +++ b/packages/connected/src/IConnectedLdoDataset.ts @@ -17,6 +17,39 @@ export type GetResourceReturnType< ? ReturnTypeFromArgs : ReturnType | InvalidIdentifierResource; +/** + * A ConnectedLdoDataset has all the functionality of a LdoDataset with the + * added functionality of keeping track of fetched remote Resources. + * + * It is recommended to use the { @link createConnectedLdoDataset } to + * initialize this class. + * + * @example + * ```typescript + * import { createConnectedLdoDataset } from "@ldo/connected"; + * import { ProfileShapeType } from "./.ldo/profile.shapeTypes.ts" + * + * // At least one plugin needs to be provided to a ConnectedLdoDataset. In this + * // example we'll use both the Solid and NextGraph plugins. + * import { solidConnectedPlugin } from "@ldo/connected-solid"; + * import { nextGraphConnectedPlugin } from "@ldo/connected-nextgraph"; + * + * // ... + * + * const connectedLdoDataset = createConnectedLdoDataset([ + * solidConnectedPlugin, + * nextGraphConnectedPlugin + * ]); + * + * const profileDocument = connectedLdoDataset + * .getResource("https://example.com/profile"); + * await profileDocument.read(); + * + * const profile = connectedLdoDataset + * .using(ProfileShapeType) + * .fromSubject("https://example.com/profile#me"); + * ``` + */ export interface IConnectedLdoDataset extends LdoDataset { /** @@ -32,7 +65,7 @@ export interface IConnectedLdoDataset * * @example * ```typescript - * const profileDocument = solidLdoDataset + * const profileDocument = connectedLdoDataset * .getResource("https://example.com/profile"); * ``` */ @@ -45,6 +78,21 @@ export interface IConnectedLdoDataset pluginName?: Name, ): GetResourceReturnType; + /** + * Generates a random uri and creates a resource. + * + * @param pluginName - A string name for the platform you'd like to create + * the resource on. + * @param createResourceOptions - Some set of options specific to the plugin + * you've selected. + * @returns A created resource or an error + * + * @example + * ```typescript + * const profileDocument = await connectedLdoDataset + * .createResource("solid"); + * ``` + */ createResource< Name extends Plugins[number]["name"], Plugin extends Extract, @@ -53,10 +101,34 @@ export interface IConnectedLdoDataset createResourceOptions?: Plugin["types"]["createResourceOptions"], ): Promise>; + /** + * Removes a resource from local memory + * @param uri - the URI of the resource to remove + * @returns true if the resource was present before removal + * + * @example + * ```typescript + * connectedLdoDataset.forgetResource("https://example.com/resource.ttl"); + * ``` + */ forgetResource(uri: string): boolean; + /** + * Removes all resources from memory + * + * @example + * ```typescript + * connectedLdoDataset.forgetAllResources(); + * ``` + */ forgetAllResources(): void; + /** + * Sets conetext for a specific plugin + * + * @param pluginName - the name of the plugin + * @param context - the context for this specific plugin + */ setContext< Name extends Plugins[number]["name"], Plugin extends Extract, diff --git a/packages/connected/src/InvalidIdentifierResource.ts b/packages/connected/src/InvalidIdentifierResource.ts index dd63bc5..da1d28c 100644 --- a/packages/connected/src/InvalidIdentifierResource.ts +++ b/packages/connected/src/InvalidIdentifierResource.ts @@ -2,6 +2,10 @@ import EventEmitter from "events"; import type { Resource, ResourceEventEmitter } from "./Resource"; import { InvalidUriError } from "./results/error/InvalidUriError"; +/** + * A resource that represents a URI that does not have a valid URI given the + * plugins available to the ConnectedLdoDataset. + */ export class InvalidIdentifierResource extends (EventEmitter as new () => ResourceEventEmitter) implements Resource diff --git a/packages/connected/src/Resource.ts b/packages/connected/src/Resource.ts index d300388..e890829 100644 --- a/packages/connected/src/Resource.ts +++ b/packages/connected/src/Resource.ts @@ -14,30 +14,92 @@ export type ResourceEventEmitter = TypedEmitter<{ notification: () => void; }>; +/** + * A resource is an abstract representation for a group of data on a remote + * platform. For example, "Solid" has resources that could be containers or + * leafs. + */ export interface Resource extends ResourceEventEmitter { + /** + * Indicates that this is not an error + */ readonly isError: false; + /** + * The uri of the resource + */ readonly uri: UriType; + /** + * The name of the resource. For example "NextGraphResource" + */ readonly type: string; + /** + * The most recent result from one of the resource methods. + */ status: ConnectedResult; + /** + * Returns true if this resource is currently loading. + */ isLoading(): boolean; + /** + * Returns true if this resource has performed a fetch at least once + */ isFetched(): boolean; + /** + * Returns true if this reosource has not performed a fetch at least once + */ isUnfetched(): boolean; + /** + * Returns true if this resource is currently performing its first fetch + */ isDoingInitialFetch(): boolean; + /** + * Returns true if this resource exists. Returns undefined if that is + * currently unknown. + */ isPresent(): boolean | undefined; + /** + * Returns true if its confirmed that this resource doesn't exist. Returns + * undefined if that is currently unknown. + */ isAbsent(): boolean | undefined; + /** + * Returns true if this resource is currently subscribed to notifications. + */ isSubscribedToNotifications(): boolean; + /** + * Fetches the resource. + */ read(): Promise | ResourceError>; + /** + * Fetches the resource if it hasn't been fetched yet. + */ readIfUnfetched(): Promise | ResourceError>; + /** + * Applies updates to this resource. + * @param datasetChanges - A list of changes to data + */ update( datasetChanges: DatasetChanges, ): Promise< UpdateSuccess | IgnoredInvalidUpdateSuccess | ResourceError >; + /** + * Begins a subscription to this resource + * @param callbacks - optional set of callbacks to be called when this + * resource is updated + */ subscribeToNotifications(callbacks?: { onNotification: (message: any) => void; onNotificationError: (err: Error) => void; }): Promise; + /** + * Unsubscribes from notifications on this resource + * @param subscriptionId the Id of the subscription to unsubscribe + */ unsubscribeFromNotifications(subscriptionId: string): Promise; + /** + * Unsubscribes from all notifications. + */ unsubscribeFromAllNotifications(): Promise; } diff --git a/packages/connected/src/SubscriptionCallbacks.ts b/packages/connected/src/SubscriptionCallbacks.ts index 9b2dc08..72a6027 100644 --- a/packages/connected/src/SubscriptionCallbacks.ts +++ b/packages/connected/src/SubscriptionCallbacks.ts @@ -1,5 +1,15 @@ +/** + * A set of callback functions that are called when a resource recieves a + * notification + */ export interface SubscriptionCallbacks { + /** + * Triggers when a notification was received. + */ onNotification?: (message: NotificationMessage) => void; + /** + * Triggers when a notification error was received. + */ // TODO: make notification errors more specific onNotificationError?: (error: Error) => void; } diff --git a/packages/connected/src/createConntectedLdoDataset.ts b/packages/connected/src/createConntectedLdoDataset.ts index c7440d3..d42df33 100644 --- a/packages/connected/src/createConntectedLdoDataset.ts +++ b/packages/connected/src/createConntectedLdoDataset.ts @@ -3,6 +3,28 @@ import { ConnectedLdoDataset } from "./ConnectedLdoDataset"; import type { ConnectedPlugin } from "./ConnectedPlugin"; import { createTransactionDatasetFactory } from "@ldo/subscribable-dataset"; +/** + * Creates a ConnectedLdoDataset + * @param plugins - An array of plugins for platforms to connect to + * @returns - A ConnectedLdoDataset + * + * @example + * ```typescript + * import { createConnectedLdoDataset } from "@ldo/connected"; + * + * // At least one plugin needs to be provided to a ConnectedLdoDataset. In this + * // example we'll use both the Solid and NextGraph plugins. + * import { solidConnectedPlugin } from "@ldo/connected-solid"; + * import { nextGraphConnectedPlugin } from "@ldo/connected-nextgraph"; + * + * // ... + * + * const connectedLdoDataset = createConnectedLdoDataset([ + * solidConnectedPlugin, + * nextGraphConnectedPlugin + * ]); + * ``` + */ export function createConnectedLdoDataset( plugins: Plugins, ): ConnectedLdoDataset { diff --git a/packages/connected/src/index.ts b/packages/connected/src/index.ts index 880cade..ee412a7 100644 --- a/packages/connected/src/index.ts +++ b/packages/connected/src/index.ts @@ -12,7 +12,6 @@ export * from "./SubscriptionCallbacks"; export * from "./util/splitChangesByGraph"; export * from "./results/ConnectedResult"; -export * from "./results/ResourceResult"; export * from "./results/error/ErrorResult"; export * from "./results/error/InvalidUriError"; export * from "./results/error/NotificationErrors"; diff --git a/packages/connected/src/methods.ts b/packages/connected/src/methods.ts index f09cf3f..8c8622a 100644 --- a/packages/connected/src/methods.ts +++ b/packages/connected/src/methods.ts @@ -21,14 +21,14 @@ import type { AggregateError, ErrorResult } from "./results/error/ErrorResult"; * * @example * ```typescript - * import { changeData } from "@ldo/solid"; + * import { changeData } from "@ldo/connected"; * * // ... * - * const profile = solidLdoDataset + * const profile = connectedLdoDataset * .using(ProfileShapeType) * .fromSubject("https://example.com/profile#me"); - * const resource = solidLdoDataset.getResource("https://example.com/profile"); + * const resource = connectedLdoDataset.getResource("https://example.com/profile"); * * const cProfile = changeData(profile, resource); * cProfile.name = "My New Name"; @@ -53,20 +53,20 @@ export function changeData( /** * Commits the transaction to the global dataset, syncing all subscribing - * components and Solid Pods + * components and connected Pods * * @param input - A transactable linked data object * * @example * ```typescript - * import { changeData } from "@ldo/solid"; + * import { changeData } from "@ldo/connected"; * * // ... * - * const profile = solidLdoDataset + * const profile = connectedLdoDataset * .using(ProfileShapeType) * .fromSubject("https://example.com/profile#me"); - * const resource = solidLdoDataset.getResource("https://example.com/profile"); + * const resource = connectedLdoDataset.getResource("https://example.com/profile"); * * const cProfile = changeData(profile, resource); * cProfile.name = "My New Name"; diff --git a/packages/connected/src/results/ResourceResult.ts b/packages/connected/src/results/ResourceResult.ts deleted file mode 100644 index 89b36df..0000000 --- a/packages/connected/src/results/ResourceResult.ts +++ /dev/null @@ -1,7 +0,0 @@ -import type { Resource } from "../Resource"; -import type { ResourceError } from "./error/ErrorResult"; -import type { ResourceSuccess } from "./success/SuccessResult"; - -export type ResourceResult = - | ResourceSuccess - | ResourceError;