parent
3c571a6098
commit
f11fcbda49
@ -1,3 +1,74 @@ |
|||||||
import type { Resource } from "@ldo/connected"; |
import { |
||||||
|
Unfetched, |
||||||
|
type ConnectedResult, |
||||||
|
type Resource, |
||||||
|
type ResourceResult, |
||||||
|
type SubscriptionCallbacks, |
||||||
|
type ResourceEventEmitter, |
||||||
|
} from "@ldo/connected"; |
||||||
|
import type { NextGraphUri } from "../types"; |
||||||
|
import EventEmitter from "events"; |
||||||
|
|
||||||
export class NextGraphResource implements Resource {} |
export class NextGraphResource |
||||||
|
extends (EventEmitter as new () => ResourceEventEmitter) |
||||||
|
implements Resource<NextGraphUri> |
||||||
|
{ |
||||||
|
public readonly uri: NextGraphUri; |
||||||
|
public readonly type = "NextGraphResource" as const; |
||||||
|
public status: ConnectedResult; |
||||||
|
|
||||||
|
constructor(uri: NextGraphUri) { |
||||||
|
super(); |
||||||
|
this.uri = uri; |
||||||
|
this.status = new Unfetched(this); |
||||||
|
} |
||||||
|
|
||||||
|
isLoading(): boolean { |
||||||
|
throw new Error("Method not implemented."); |
||||||
|
} |
||||||
|
|
||||||
|
isFetched(): boolean { |
||||||
|
throw new Error("Method not implemented."); |
||||||
|
} |
||||||
|
|
||||||
|
isUnfetched(): boolean { |
||||||
|
throw new Error("Method not implemented."); |
||||||
|
} |
||||||
|
|
||||||
|
isDoingInitialFetch(): boolean { |
||||||
|
throw new Error("Method not implemented."); |
||||||
|
} |
||||||
|
|
||||||
|
isPresent(): boolean { |
||||||
|
throw new Error("Method not implemented."); |
||||||
|
} |
||||||
|
|
||||||
|
isAbsent(): boolean { |
||||||
|
throw new Error("Method not implemented."); |
||||||
|
} |
||||||
|
|
||||||
|
isSubscribedToNotifications(): boolean { |
||||||
|
throw new Error("Method not implemented."); |
||||||
|
} |
||||||
|
|
||||||
|
read(): Promise<ResourceResult<this>> { |
||||||
|
throw new Error("Method not implemented."); |
||||||
|
} |
||||||
|
|
||||||
|
readIfAbsent(): Promise<ResourceResult<this>> { |
||||||
|
throw new Error("Method not implemented."); |
||||||
|
} |
||||||
|
|
||||||
|
subscribeToNotifications(callbacks?: SubscriptionCallbacks): Promise<string> { |
||||||
|
throw new Error("Method not implemented."); |
||||||
|
} |
||||||
|
|
||||||
|
unsubscribeFromNotifications(subscriptionId: string): Promise<void> { |
||||||
|
throw new Error("Method not implemented."); |
||||||
|
} |
||||||
|
|
||||||
|
unsubscribeFromAllNotifications(): Promise<void> { |
||||||
|
throw new Error("Method not implemented."); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
@ -0,0 +1,64 @@ |
|||||||
|
import EventEmitter from "events"; |
||||||
|
import type { Resource, ResourceEventEmitter } from "./Resource"; |
||||||
|
import { InvalidUriError } from "./results/error/InvalidUriError"; |
||||||
|
import type { SubscriptionCallbacks } from "./notifications/NotificationSubscription"; |
||||||
|
|
||||||
|
export class InvalidIdentifierResource |
||||||
|
extends (EventEmitter as new () => ResourceEventEmitter) |
||||||
|
implements Resource |
||||||
|
{ |
||||||
|
public readonly uri: string; |
||||||
|
public readonly type = "InvalidIdentifierResouce" as const; |
||||||
|
public status: InvalidUriError<this>; |
||||||
|
|
||||||
|
constructor(uri: string) { |
||||||
|
super(); |
||||||
|
this.uri = uri; |
||||||
|
this.status = new InvalidUriError(this); |
||||||
|
} |
||||||
|
|
||||||
|
isLoading(): boolean { |
||||||
|
return false; |
||||||
|
} |
||||||
|
isFetched(): boolean { |
||||||
|
return false; |
||||||
|
} |
||||||
|
isUnfetched(): boolean { |
||||||
|
return true; |
||||||
|
} |
||||||
|
isDoingInitialFetch(): boolean { |
||||||
|
return false; |
||||||
|
} |
||||||
|
isPresent(): boolean { |
||||||
|
return false; |
||||||
|
} |
||||||
|
isAbsent(): boolean { |
||||||
|
return true; |
||||||
|
} |
||||||
|
isSubscribedToNotifications(): boolean { |
||||||
|
return false; |
||||||
|
} |
||||||
|
async read(): Promise<InvalidUriError<this>> { |
||||||
|
return this.status; |
||||||
|
} |
||||||
|
async readIfAbsent(): Promise<InvalidUriError<this>> { |
||||||
|
return this.status; |
||||||
|
} |
||||||
|
async createAndOverwrite(): Promise<InvalidUriError<this>> { |
||||||
|
return this.status; |
||||||
|
} |
||||||
|
async createIfAbsent(): Promise<InvalidUriError<this>> { |
||||||
|
return this.status; |
||||||
|
} |
||||||
|
async subscribeToNotifications( |
||||||
|
_callbacks?: SubscriptionCallbacks, |
||||||
|
): Promise<string> { |
||||||
|
throw new Error("Cannot subscribe to an invalid resource."); |
||||||
|
} |
||||||
|
async unsubscribeFromNotifications(_subscriptionId: string): Promise<void> { |
||||||
|
// Do Nothing
|
||||||
|
} |
||||||
|
async unsubscribeFromAllNotifications(): Promise<void> { |
||||||
|
// Do Nothing
|
||||||
|
} |
||||||
|
} |
@ -1 +1,28 @@ |
|||||||
export interface Resource {} |
import type TypedEmitter from "typed-emitter"; |
||||||
|
import type { ConnectedResult } from "./results/ConnectedResult"; |
||||||
|
import type { ResourceResult } from "./results/ResourceResult"; |
||||||
|
import type { SubscriptionCallbacks } from "./notifications/NotificationSubscription"; |
||||||
|
|
||||||
|
export type ResourceEventEmitter = TypedEmitter<{ |
||||||
|
update: () => void; |
||||||
|
notification: () => void; |
||||||
|
}>; |
||||||
|
|
||||||
|
export interface Resource<UriType extends string = string> |
||||||
|
extends ResourceEventEmitter { |
||||||
|
readonly uri: UriType; |
||||||
|
readonly type: string; |
||||||
|
status: ConnectedResult; |
||||||
|
isLoading(): boolean; |
||||||
|
isFetched(): boolean; |
||||||
|
isUnfetched(): boolean; |
||||||
|
isDoingInitialFetch(): boolean; |
||||||
|
isPresent(): boolean; |
||||||
|
isAbsent(): boolean; |
||||||
|
isSubscribedToNotifications(): boolean; |
||||||
|
read(): Promise<ResourceResult<this>>; |
||||||
|
readIfAbsent(): Promise<ResourceResult<this>>; |
||||||
|
subscribeToNotifications(callbacks?: SubscriptionCallbacks): Promise<string>; |
||||||
|
unsubscribeFromNotifications(subscriptionId: string): Promise<void>; |
||||||
|
unsubscribeFromAllNotifications(): Promise<void>; |
||||||
|
} |
||||||
|
@ -1,5 +1,13 @@ |
|||||||
import { ConnectedLdoDataset } from "./ConnectedLdoDataset"; |
|
||||||
|
|
||||||
export * from "./ConnectedLdoDataset"; |
export * from "./ConnectedLdoDataset"; |
||||||
export * from "./ConnectedPlugin"; |
export * from "./ConnectedPlugin"; |
||||||
export * from "./Resource"; |
export * from "./Resource"; |
||||||
|
export * from "./InvalidIdentifierResource"; |
||||||
|
|
||||||
|
export * from "./notifications/NotificationMessage"; |
||||||
|
export * from "./notifications/NotificationSubscription"; |
||||||
|
|
||||||
|
export * from "./results/ConnectedResult"; |
||||||
|
export * from "./results/ResourceResult"; |
||||||
|
export * from "./results/error/ErrorResult"; |
||||||
|
export * from "./results/success/SuccessResult"; |
||||||
|
export * from "./results/success/Unfetched"; |
||||||
|
@ -0,0 +1,10 @@ |
|||||||
|
/** |
||||||
|
* A message sent from the Pod as a notification |
||||||
|
*/ |
||||||
|
export interface NotificationMessage { |
||||||
|
"@context": string | string[]; |
||||||
|
id: string; |
||||||
|
type: "Update" | "Delete" | "Remove" | "Add"; |
||||||
|
object: string; |
||||||
|
published: string; |
||||||
|
} |
@ -0,0 +1,144 @@ |
|||||||
|
import type { SolidLdoDatasetContext } from "../../SolidLdoDatasetContext"; |
||||||
|
import type { Resource } from "../Resource"; |
||||||
|
import type { NotificationMessage } from "./NotificationMessage"; |
||||||
|
import type { NotificationCallbackError } from "./results/NotificationErrors"; |
||||||
|
import { v4 } from "uuid"; |
||||||
|
|
||||||
|
export interface SubscriptionCallbacks { |
||||||
|
onNotification?: (message: NotificationMessage) => void; |
||||||
|
// TODO: make notification errors more specific
|
||||||
|
onNotificationError?: (error: Error) => void; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @internal |
||||||
|
* Abstract class for notification subscription methods. |
||||||
|
*/ |
||||||
|
export abstract class NotificationSubscription { |
||||||
|
protected resource: Resource; |
||||||
|
protected parentSubscription: (message: NotificationMessage) => void; |
||||||
|
protected context: SolidLdoDatasetContext; |
||||||
|
protected subscriptions: Record<string, SubscriptionCallbacks> = {}; |
||||||
|
private isOpen: boolean = false; |
||||||
|
|
||||||
|
constructor( |
||||||
|
resource: Resource, |
||||||
|
parentSubscription: (message: NotificationMessage) => void, |
||||||
|
context: SolidLdoDatasetContext, |
||||||
|
) { |
||||||
|
this.resource = resource; |
||||||
|
this.parentSubscription = parentSubscription; |
||||||
|
this.context = context; |
||||||
|
} |
||||||
|
|
||||||
|
public isSubscribedToNotifications(): boolean { |
||||||
|
return this.isOpen; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* =========================================================================== |
||||||
|
* PUBLIC |
||||||
|
* =========================================================================== |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* @internal |
||||||
|
* subscribeToNotifications |
||||||
|
*/ |
||||||
|
async subscribeToNotifications( |
||||||
|
subscriptionCallbacks?: SubscriptionCallbacks, |
||||||
|
): Promise<string> { |
||||||
|
const subscriptionId = v4(); |
||||||
|
this.subscriptions[subscriptionId] = subscriptionCallbacks ?? {}; |
||||||
|
if (!this.isOpen) { |
||||||
|
await this.open(); |
||||||
|
this.setIsOpen(true); |
||||||
|
} |
||||||
|
return subscriptionId; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @internal |
||||||
|
* unsubscribeFromNotification |
||||||
|
*/ |
||||||
|
async unsubscribeFromNotification(subscriptionId: string): Promise<void> { |
||||||
|
if ( |
||||||
|
!!this.subscriptions[subscriptionId] && |
||||||
|
Object.keys(this.subscriptions).length === 1 |
||||||
|
) { |
||||||
|
await this.close(); |
||||||
|
this.setIsOpen(false); |
||||||
|
} |
||||||
|
delete this.subscriptions[subscriptionId]; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @internal |
||||||
|
* unsubscribeFromAllNotifications |
||||||
|
*/ |
||||||
|
async unsubscribeFromAllNotifications(): Promise<void> { |
||||||
|
await Promise.all( |
||||||
|
Object.keys(this.subscriptions).map((id) => |
||||||
|
this.unsubscribeFromNotification(id), |
||||||
|
), |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* =========================================================================== |
||||||
|
* HELPERS |
||||||
|
* =========================================================================== |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* @internal |
||||||
|
* Opens the subscription |
||||||
|
*/ |
||||||
|
protected abstract open(): Promise<void>; |
||||||
|
|
||||||
|
/** |
||||||
|
* @internal |
||||||
|
* Closes the subscription |
||||||
|
*/ |
||||||
|
protected abstract close(): Promise<void>; |
||||||
|
|
||||||
|
/** |
||||||
|
* =========================================================================== |
||||||
|
* CALLBACKS |
||||||
|
* =========================================================================== |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* @internal |
||||||
|
* onNotification |
||||||
|
*/ |
||||||
|
protected onNotification(message: NotificationMessage): void { |
||||||
|
this.parentSubscription(message); |
||||||
|
Object.values(this.subscriptions).forEach(({ onNotification }) => { |
||||||
|
onNotification?.(message); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @internal |
||||||
|
* onNotificationError |
||||||
|
*/ |
||||||
|
protected onNotificationError(message: NotificationCallbackError): void { |
||||||
|
Object.values(this.subscriptions).forEach(({ onNotificationError }) => { |
||||||
|
onNotificationError?.(message); |
||||||
|
}); |
||||||
|
if (message.type === "disconnectedNotAttemptingReconnectError") { |
||||||
|
this.setIsOpen(false); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @internal |
||||||
|
* setIsOpen |
||||||
|
*/ |
||||||
|
protected setIsOpen(status: boolean) { |
||||||
|
const shouldUpdate = status !== this.isOpen; |
||||||
|
this.isOpen = status; |
||||||
|
if (shouldUpdate) this.resource.emit("update"); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,30 @@ |
|||||||
|
import type { UnexpectedResourceError } from "../../../requester/results/error/ErrorResult"; |
||||||
|
import { ResourceError } from "../../../requester/results/error/ErrorResult"; |
||||||
|
|
||||||
|
export type NotificationCallbackError = |
||||||
|
| DisconnectedAttemptingReconnectError |
||||||
|
| DisconnectedNotAttemptingReconnectError |
||||||
|
| UnsupportedNotificationError |
||||||
|
| UnexpectedResourceError; |
||||||
|
|
||||||
|
/** |
||||||
|
* Indicates that the requested method for receiving notifications is not |
||||||
|
* supported by this Pod. |
||||||
|
*/ |
||||||
|
export class UnsupportedNotificationError extends ResourceError { |
||||||
|
readonly type = "unsupportedNotificationError" as const; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Indicates that the socket has disconnected and is attempting to reconnect. |
||||||
|
*/ |
||||||
|
export class DisconnectedAttemptingReconnectError extends ResourceError { |
||||||
|
readonly type = "disconnectedAttemptingReconnectError" as const; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Indicates that the socket has disconnected and is attempting to reconnect. |
||||||
|
*/ |
||||||
|
export class DisconnectedNotAttemptingReconnectError extends ResourceError { |
||||||
|
readonly type = "disconnectedNotAttemptingReconnectError" as const; |
||||||
|
} |
@ -0,0 +1,7 @@ |
|||||||
|
/** |
||||||
|
* A type returned by all request functions |
||||||
|
*/ |
||||||
|
export interface ConnectedResult { |
||||||
|
readonly type: string; |
||||||
|
readonly isError: boolean; |
||||||
|
} |
@ -0,0 +1,7 @@ |
|||||||
|
import type { Resource } from "../Resource"; |
||||||
|
import type { ResourceError } from "./error/ErrorResult"; |
||||||
|
import type { ResourceSuccess } from "./success/SuccessResult"; |
||||||
|
|
||||||
|
export type ResourceResult<ResourceType extends Resource> = |
||||||
|
| ResourceSuccess<ResourceType> |
||||||
|
| ResourceError<ResourceType>; |
@ -0,0 +1,139 @@ |
|||||||
|
import type { Resource } from "../../Resource"; |
||||||
|
import type { ConnectedResult } from "../ConnectedResult"; |
||||||
|
|
||||||
|
/** |
||||||
|
* A result indicating that the request failed in some kind of way |
||||||
|
*/ |
||||||
|
export abstract class ErrorResult extends Error implements ConnectedResult { |
||||||
|
/** |
||||||
|
* Indicates the specific type of error |
||||||
|
*/ |
||||||
|
abstract readonly type: string; |
||||||
|
|
||||||
|
/** |
||||||
|
* Always true |
||||||
|
*/ |
||||||
|
readonly isError = true as const; |
||||||
|
|
||||||
|
/** |
||||||
|
* @param message - a custom message for the error |
||||||
|
*/ |
||||||
|
constructor(message?: string) { |
||||||
|
super(message || "An unkown error was encountered."); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* An error for a specific resource |
||||||
|
*/ |
||||||
|
export abstract class ResourceError< |
||||||
|
ResourceType extends Resource, |
||||||
|
> extends ErrorResult { |
||||||
|
/** |
||||||
|
* The URI of the resource |
||||||
|
*/ |
||||||
|
readonly uri: ResourceType["uri"]; |
||||||
|
|
||||||
|
/** |
||||||
|
* The resource that failed |
||||||
|
*/ |
||||||
|
readonly resource: ResourceType; |
||||||
|
|
||||||
|
/** |
||||||
|
* @param uri - The URI of the resource |
||||||
|
* @param message - A custom message for the error |
||||||
|
*/ |
||||||
|
constructor(resource: ResourceType, message?: string) { |
||||||
|
super(message || `An unkown error for ${resource.uri}`); |
||||||
|
this.uri = resource.uri; |
||||||
|
this.resource = resource; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* An error that aggregates many errors |
||||||
|
*/ |
||||||
|
export class AggregateError<ErrorType extends ErrorResult> extends ErrorResult { |
||||||
|
readonly type = "aggregateError" as const; |
||||||
|
|
||||||
|
/** |
||||||
|
* A list of all errors returned |
||||||
|
*/ |
||||||
|
readonly errors: ErrorType[]; |
||||||
|
|
||||||
|
/** |
||||||
|
* @param errors - List of all errors returned |
||||||
|
* @param message - A custom message for the error |
||||||
|
*/ |
||||||
|
constructor( |
||||||
|
errors: (ErrorType | AggregateError<ErrorType>)[], |
||||||
|
message?: string, |
||||||
|
) { |
||||||
|
const allErrors: ErrorType[] = []; |
||||||
|
errors.forEach((error) => { |
||||||
|
if (error instanceof AggregateError) { |
||||||
|
error.errors.forEach((subError) => { |
||||||
|
allErrors.push(subError); |
||||||
|
}); |
||||||
|
} else { |
||||||
|
allErrors.push(error); |
||||||
|
} |
||||||
|
}); |
||||||
|
super( |
||||||
|
message || |
||||||
|
`Encountered multiple errors:${allErrors.reduce( |
||||||
|
(agg, cur) => `${agg}\n${cur}`, |
||||||
|
"", |
||||||
|
)}`,
|
||||||
|
); |
||||||
|
this.errors = allErrors; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Represents some error that isn't handled under other errors. This is usually |
||||||
|
* returned when something threw an error that LDO did not expect. |
||||||
|
*/ |
||||||
|
export class UnexpectedResourceError< |
||||||
|
ResourceType extends Resource, |
||||||
|
> extends ResourceError<ResourceType> { |
||||||
|
readonly type = "unexpectedResourceError" as const; |
||||||
|
|
||||||
|
/** |
||||||
|
* The error that was thrown |
||||||
|
*/ |
||||||
|
error: Error; |
||||||
|
|
||||||
|
/** |
||||||
|
* @param uri - URI of the resource |
||||||
|
* @param error - The error that was thrown |
||||||
|
*/ |
||||||
|
constructor(resource: ResourceType, error: Error) { |
||||||
|
super(resource, error.message); |
||||||
|
this.error = error; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @internal |
||||||
|
* |
||||||
|
* Creates an UnexpectedResourceError from a thrown error |
||||||
|
* @param uri - The URI of the resource |
||||||
|
* @param err - The thrown error |
||||||
|
* @returns an UnexpectedResourceError |
||||||
|
*/ |
||||||
|
static fromThrown<ResourceType extends Resource>( |
||||||
|
resource: ResourceType, |
||||||
|
err: unknown, |
||||||
|
): UnexpectedResourceError<ResourceType> { |
||||||
|
if (err instanceof Error) { |
||||||
|
return new UnexpectedResourceError(resource, err); |
||||||
|
} else if (typeof err === "string") { |
||||||
|
return new UnexpectedResourceError(resource, new Error(err)); |
||||||
|
} else { |
||||||
|
return new UnexpectedResourceError( |
||||||
|
resource, |
||||||
|
new Error(`Error of type ${typeof err} thrown: ${err}`), |
||||||
|
); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,16 @@ |
|||||||
|
import type { Resource } from "../../Resource"; |
||||||
|
import { ResourceError } from "./ErrorResult"; |
||||||
|
|
||||||
|
/** |
||||||
|
* An InvalidUriError is returned when a URI was provided that is not a valid |
||||||
|
* URI. |
||||||
|
*/ |
||||||
|
export class InvalidUriError< |
||||||
|
ResourceType extends Resource, |
||||||
|
> extends ResourceError<ResourceType> { |
||||||
|
readonly type = "invalidUriError" as const; |
||||||
|
|
||||||
|
constructor(resource: ResourceType, message?: string) { |
||||||
|
super(resource, message || `${resource.uri} is an invalid uri.`); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,19 @@ |
|||||||
|
import type { Container } from "../../../resource/Container"; |
||||||
|
import type { ResourceSuccess, SuccessResult } from "./SuccessResult"; |
||||||
|
|
||||||
|
/** |
||||||
|
* Indicates that the request to check if a resource is the root container was |
||||||
|
* a success. |
||||||
|
*/ |
||||||
|
export interface CheckRootContainerSuccess extends ResourceSuccess { |
||||||
|
type: "checkRootContainerSuccess"; |
||||||
|
/** |
||||||
|
* True if this resoure is the root container |
||||||
|
*/ |
||||||
|
isRootContainer: boolean; |
||||||
|
} |
||||||
|
|
||||||
|
export interface GetStorageContainerFromWebIdSuccess extends SuccessResult { |
||||||
|
type: "getStorageContainerFromWebIdSuccess"; |
||||||
|
storageContainers: Container[]; |
||||||
|
} |
@ -0,0 +1,13 @@ |
|||||||
|
import type { ResourceSuccess } from "./SuccessResult"; |
||||||
|
|
||||||
|
/** |
||||||
|
* Indicates that the request to create the resource was a success. |
||||||
|
*/ |
||||||
|
export interface CreateSuccess extends ResourceSuccess { |
||||||
|
type: "createSuccess"; |
||||||
|
/** |
||||||
|
* True if there was a resource that existed before at the given URI that was |
||||||
|
* overwritten |
||||||
|
*/ |
||||||
|
didOverwrite: boolean; |
||||||
|
} |
@ -0,0 +1,14 @@ |
|||||||
|
import type { ResourceSuccess } from "./SuccessResult"; |
||||||
|
|
||||||
|
/** |
||||||
|
* Indicates that the request to delete a resource was a success. |
||||||
|
*/ |
||||||
|
export interface DeleteSuccess extends ResourceSuccess { |
||||||
|
type: "deleteSuccess"; |
||||||
|
|
||||||
|
/** |
||||||
|
* True if there was a resource at the provided URI that was deleted. False if |
||||||
|
* a resource didn't exist. |
||||||
|
*/ |
||||||
|
resourceExisted: boolean; |
||||||
|
} |
@ -0,0 +1,71 @@ |
|||||||
|
import type { ResourceSuccess, SuccessResult } from "./SuccessResult"; |
||||||
|
|
||||||
|
/** |
||||||
|
* Indicates that the request to read a resource was a success |
||||||
|
*/ |
||||||
|
export interface ReadSuccess extends ResourceSuccess { |
||||||
|
/** |
||||||
|
* True if the resource was recalled from local memory rather than a recent |
||||||
|
* request |
||||||
|
*/ |
||||||
|
recalledFromMemory: boolean; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Indicates that the read request was successful and that the resource |
||||||
|
* retrieved was a binary resource. |
||||||
|
*/ |
||||||
|
export interface BinaryReadSuccess extends ReadSuccess { |
||||||
|
type: "binaryReadSuccess"; |
||||||
|
/** |
||||||
|
* The raw data for the binary resource |
||||||
|
*/ |
||||||
|
blob: Blob; |
||||||
|
/** |
||||||
|
* The mime type of the binary resource |
||||||
|
*/ |
||||||
|
mimeType: string; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Indicates that the read request was successful and that the resource |
||||||
|
* retrieved was a data (RDF) resource. |
||||||
|
*/ |
||||||
|
export interface DataReadSuccess extends ReadSuccess { |
||||||
|
type: "dataReadSuccess"; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Indicates that the read request was successful and that the resource |
||||||
|
* retrieved was a container resource. |
||||||
|
*/ |
||||||
|
export interface ContainerReadSuccess extends ReadSuccess { |
||||||
|
type: "containerReadSuccess"; |
||||||
|
/** |
||||||
|
* True if this container is a root container |
||||||
|
*/ |
||||||
|
isRootContainer: boolean; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Indicates that the read request was successful, but no resource exists at |
||||||
|
* the provided URI. |
||||||
|
*/ |
||||||
|
export interface AbsentReadSuccess extends ReadSuccess { |
||||||
|
type: "absentReadSuccess"; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* A helper function that checks to see if a result is a ReadSuccess result |
||||||
|
* |
||||||
|
* @param result - the result to check |
||||||
|
* @returns true if the result is a ReadSuccessResult result |
||||||
|
*/ |
||||||
|
export function isReadSuccess(result: SuccessResult): result is ReadSuccess { |
||||||
|
return ( |
||||||
|
result.type === "binaryReadSuccess" || |
||||||
|
result.type === "dataReadSuccess" || |
||||||
|
result.type === "absentReadSuccess" || |
||||||
|
result.type === "containerReadSuccess" |
||||||
|
); |
||||||
|
} |
@ -0,0 +1,51 @@ |
|||||||
|
import type { Resource } from "../../Resource"; |
||||||
|
import type { ConnectedResult } from "../ConnectedResult"; |
||||||
|
|
||||||
|
/** |
||||||
|
* Indicates that some action taken by LDO was a success |
||||||
|
*/ |
||||||
|
export abstract class SuccessResult implements ConnectedResult { |
||||||
|
abstract readonly type: string; |
||||||
|
readonly isError = false as const; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Indicates that a request to a resource was aa success |
||||||
|
*/ |
||||||
|
export abstract class ResourceSuccess< |
||||||
|
ResourceType extends Resource, |
||||||
|
> extends SuccessResult { |
||||||
|
/** |
||||||
|
* The URI of the resource |
||||||
|
*/ |
||||||
|
uri: ResourceType["uri"]; |
||||||
|
/** |
||||||
|
* The resource that was successful |
||||||
|
*/ |
||||||
|
resource: Resource; |
||||||
|
|
||||||
|
constructor(resource: ResourceType) { |
||||||
|
super(); |
||||||
|
this.uri = resource.uri; |
||||||
|
this.resource = resource; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* A grouping of multiple successes as a result of an action |
||||||
|
*/ |
||||||
|
export class AggregateSuccess< |
||||||
|
SuccessType extends SuccessResult, |
||||||
|
> extends SuccessResult { |
||||||
|
type = "aggregateSuccess" as const; |
||||||
|
|
||||||
|
/** |
||||||
|
* An array of all successesses |
||||||
|
*/ |
||||||
|
results: SuccessType[]; |
||||||
|
|
||||||
|
constructor(results: SuccessType[]) { |
||||||
|
super(); |
||||||
|
this.results = results; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,11 @@ |
|||||||
|
import type { Resource } from "../../Resource"; |
||||||
|
import { ResourceSuccess } from "./SuccessResult"; |
||||||
|
|
||||||
|
/** |
||||||
|
* Indicates that a specific resource is unfetched |
||||||
|
*/ |
||||||
|
export class Unfetched< |
||||||
|
ResourceType extends Resource, |
||||||
|
> extends ResourceSuccess<ResourceType> { |
||||||
|
readonly type = "unfetched" as const; |
||||||
|
} |
@ -0,0 +1,24 @@ |
|||||||
|
import type { ResourceSuccess } from "./SuccessResult"; |
||||||
|
|
||||||
|
/** |
||||||
|
* Indicates that an update request to a resource was successful |
||||||
|
*/ |
||||||
|
export interface UpdateSuccess extends ResourceSuccess { |
||||||
|
type: "updateSuccess"; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Indicates that an update request to the default graph was successful. This |
||||||
|
* data was not written to a Pod. It was only written locally. |
||||||
|
*/ |
||||||
|
export interface UpdateDefaultGraphSuccess extends ResourceSuccess { |
||||||
|
type: "updateDefaultGraphSuccess"; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Indicates that LDO ignored an invalid update (usually because a container |
||||||
|
* attempted an update) |
||||||
|
*/ |
||||||
|
export interface IgnoredInvalidUpdateSuccess extends ResourceSuccess { |
||||||
|
type: "ignoredInvalidUpdateSuccess"; |
||||||
|
} |
Loading…
Reference in new issue