/** * @typedef {object} Destination * @property {string} name Title of the destination * @property {string} url link to visit the destination * @property {string} [privacy] 'direct', 'friends', or 'public' determines who can view this Destination and url e.g. in recently visited places. Default: friends * @property {string} [description] text summary of destination * @property {string} [previewImage] thumbnail image url * @property {Activities.APPlace} [immer] reference to the parent/homepage place object for this experience, if ommitted will use the local immer */ /** * @typedef {object} Profile * @property {string} id - Globally unique identifier (ActivityPub IRI) * @property {string} handle - Shorthand globally unique identifier, format: username[home.immer] * @property {string} displayName - User's changeable preferred identifier, may contain spaces & symbols * @property {string} homeImmer - Domain of imme where user account is registered * @property {string} username - User's permanent uniqe identifier within their home immer * @property {string} bio - Text description of user, may contain sanitized HTML * @property {string} avatarImage - Profile icon url * @property {string} avatarModel - Profile avatar 3d model url * @property {Activities.APModel} avatarObject - Profile avatar full Model object * @property {string} url - Webpage to view full profile * @property {object} collections - Map of user collections - urls to fetch lists of related activities. May include user-generated collections in addition to those listed * @property {string} collections.avatars - Activities with model objects representing user's collection of avatars * @property {string} collections.blocked - User blocks * @property {string} collections.destinations - Unique immers visited by user, most recent first * @property {string} collections.friends - Most recent activity for each friend (see {@link friendsList}) * @property {string} collections.friendsDestinations - Unique immers visited by user's friends, most recent first * @property {string} collections.inbox - All incoming activities * @property {string} collections.outbox - All outgoing activities */ /** * @typedef {object} FriendStatus * @property {Profile} profile - Profile object for friend * @property {boolean} isOnline - Currently online anywhere in Immers Space * @property {string} [locationName] - Name of current or last immer visited * @property {string} [locationURL] - URL of current or last immer visited * @property {Destination} [destination] - Destination object for current or last immer visited * @property {('friend-online'|'friend-offline'|'request-receved'|'request-sent'|'none')} status - descriptor of the current relationship to this user * @property {string} statusString - Text description of current status, "Offline" / "Online at..." * @property {string} __unsafeStatusHTML - Unsanitized HTML description of current status with link. * You must sanitize this string before inserting into the DOM to avoid XSS attacks. * @property {string} statusHTML - Sanitize HTML description of current status with link. Safe to insert into DOM. */ /** * @typedef {object} Message * @property {string} id - URL of original message activity object, usable as unique id * @property {Profile} sender - Message sender's Profile * @property {Date} timestamp - Message sent time * @property {string} type - Describes the message content: 'chat', 'media', 'status', or 'other' * @property {string} __unsafeMessageHTML - Unsanitized HTML message content. * You must sanitize this string before inserting into the DOM to avoid XSS attacks. * @property {string} messageHTML - Sanitized HTML message content. Safe to insert into DOM. Media wrapped in IMG/VIDEO will have class immers-message-media * @property {string} [mediaType] - 'image' or 'video' if the message is a media object * @property {string} [url] - source url if the message is a media object * (messageHTML will contain appropriate tags to display the media, but url can be used if you need custom display) * @property {Destination} [destination] - location tied to the message, if available * @property {object} _originalActivity - the unmodified ActivityPub activity that is the source of the message */ /** * @typedef {object} ImmersClientNewMessageEvent * @property {object} detail * @property {Message} detail.message */ /** * High-level interface to Immers profile and social features * @fires immers-client-connected * @fires immers-client-disconnected * @fires immers-client-friends-update * @fires immers-client-new-message * @fires immers-client-profile-update */ export class ImmersClient extends EventTarget { /** * Array.sort compareFunction to sort a friends list putting online * friends at the top and the rest by most recent update * @param {FriendStatus} a * @param {FriendStatus} b */ static FriendsSorter(a: FriendStatus, b: FriendStatus): 1 | -1 | 0; /** * Extract a Destination from a place object * @param {Activities.APPlace} place * @returns {Destination} */ static DestinationFromPlace(place: Activities.APPlace): Destination; /** * Extract friend status information from their most recent location activity * @param {Activities.APActivity} activity * @returns {FriendStatus} */ static FriendStatusFromActivity(activity: Activities.APActivity): FriendStatus; /** * Extract a Message from an activity object * @param {Activities.APActivity} activity * @returns {Message | null} */ static MessageFromActivity(activity: Activities.APActivity): Message | null; /** * Convert ActivityPub Actor format to Immers profile * @param {Activities.APActor} actor - ActivityPub Actor object * @returns {Profile} */ static ProfileFromActor(actor: Activities.APActor): Profile; /** * Links in ActivityPub objects can take a variety of forms. * Find and return the URL string. * @param {Activities.APObject|object|string} prop * @returns {(string|undefined)} URL string, if present */ static URLFromProperty(prop: Activities.APObject | object | string): (string | undefined); /** * @param {(Destination|Activities.APPlace|string)} destinationDescription Metadata about this destination used when sharing or url for the related Place object. Either a Destination/APPlace object or a url where one can be fetched. * @param {object} [options] * @param {string} [options.localImmer] Domain (host) of the local Immers Server, if there is one * @param {boolean} [options.allowStorage] Enable localStorage of handle & token for reconnection (make sure you've provided complaince notices as needed) */ constructor(destinationDescription: (Destination | Activities.APPlace | string), options?: { localImmer?: string; allowStorage?: boolean; }); /** * Activities instance for access to low-level ActivityPub API * @type {Activities} * @public */ public activities: Activities; /** * ImmersSocket instance for access to low-level streaming API * @type {ImmersSocket} * @public */ public streaming: ImmersSocket; /** * User's Immers profile * @type {Profile} * @public */ public profile: Profile; /** * Is the client connected to the User's immer? * @type {boolean} * @public */ public connected: boolean; /** * Additional data returned from the user's immer on login * @type {Object.} * @property {boolean} [isNewUser] true when this is the first login of a new local user * @property {string} [email] only included on home immer domain when that immer has passEmailToHub config enabled and this is their first login * @property {string} [provider] identity provider domain if on home immer domain and logged in with OpenId Connect and this is their first login * @public */ public sessionInfo: { [x: string]: any; }; localImmer: any; allowStorage: boolean; enterBound: () => Promise; /** * Utility method to hide details for checking if user is logged in * or waiting util they have before performing actions that require * a logged-in user * @example * await client.waitUntilConnected() * client.sendChatMessage('Hey friends, I am connected!', 'friends') * @returns {Promise} */ waitUntilConnected(): Promise; /** * Connect to user's Immers Space profile, using pop-up window for OAuth * @param {string} tokenCatcherURL Page on your domain that runs {@link catchToken} on load to retrieve the granted access token. * Can be the same page as long as loading it again in a pop-up won't cause a the main session to disconnect. * @param {string} requestedRole Access level to request, see {@link roles} for details * @param {string} [handle] User's immers handle. Optional if you have a local Immers Server * @param {boolean} [registration] For use with local immer only, open the popup with the registration tab selected instead of login. handle will be used to prefill the registratoin form if provided * @returns {Promise} token OAuth2 acess token */ login(tokenCatcherURL: string, requestedRole: string, handle?: string, registration?: boolean): Promise; /** * Initialize client with an existing credential, * e.g. one obtained through a service account or one returned from {@link catchToken} * when performing a redirect based OAuth flow * @param {string} token - OAuth2 Access Token * @param {string} homeImmer - Domain (host) for user's home immer * @param {(string|string[])} authorizedScopes - Scopes authorized for the token * @param {object} [sessionInfo] - optional session data provided alongside token * @returns {Promise} true if the login was successful */ loginWithToken(token: string, homeImmer: string, authorizedScopes: (string | string[]), sessionInfo?: object): Promise; /** * Attempt to restore session from a previously granted token. Requires options.allowStorage * @returns {Promise} Was reconnection successful */ restoreSession(): Promise; /** * Mark user as "online" at this immer and share the location with their friends. * Must be called after successful {@link login} or {@link restoreSession} * @param {(Destination|Activities.APPlace|string)} [destinationDescription] */ enter(destinationDescription?: (Destination | Activities.APPlace | string)): Promise; /** * Update user's current online location and share with friends * @param {(Destination|Activities.APPlace|string)} destinationDescription */ move(destinationDescription: (Destination | Activities.APPlace | string)): Promise; /** * Mark user as no longer online at this immer. */ exit(): Promise; /** * Disconnect from User's immer, retaining credentials to reconnect */ disconnect(): void; /** * Disconnect from User's immer and delete any traces of user identity. * If the user is from the local immer on the same apex domain * ({@link https://github.com/immers-space/immers#api-access more info}), * alsoLogoutFromImmer can cause the * login session on the immer to be terminated as well for a complete logout. * @param {boolean} [alsoLogoutFromImmer] - terminate the login session on the local immer as well * @returns {Promise} */ logout(alsoLogoutFromImmer?: boolean): Promise; /** * Update user's profile description * @param {object} info * @param {string} [info.displayName] User's preferred shorthand identifier, may contain spaces & symbols * @param {string} [info.bio] Summary paragraph displayed on user profile */ updateProfileInfo({ displayName, bio }: { displayName?: string; bio?: string; }): Promise; /** * Fetch list of friends and their online status and location * @returns {Promise} */ friendsList(): Promise; /** * Fetch a page of recent activity Messages * @returns {Promise} */ feed(): Promise; /** * Fetch list of Profile.id of all users blocked by this user * @param {boolean} forceRefresh - skip local cache and fetch from server * @returns {Promise} */ blockList(forceRefresh: boolean): Promise; /** * Send a message with text content. * privacy level determines who receives and can acccess the message. * direct: Only those named in `to` receive the message. * friends: Direct plus friends list. * public: Direct plus Friends plus accessible via URL for sharing. * @param {string} content - The text/HTML content. Will be sanitized before sending * @param {string} privacy - 'direct', 'friends', or 'public' * @param {string[]} [to] - Addressees. Accepts Immers handles (username[domain.name]) and ActivityPub IRIs * @returns {Promise} Url of newly posted message */ sendChatMessage(content: string, privacy: string, to?: string[]): Promise; /** * Delete a message. * @param {(string|Activities.APActivity)} sourceActivity - IRI of activity or Activity in the Outbox * @returns {Promise} IRI of the remove activity */ deleteMessage(sourceActivity: (string | Activities.APActivity)): Promise; /** * Upload and/or share an image. * When image is a canvas element, its toBlob method is used to generate a * png image to upload. * When image is a File/Blob, it will be uploaded to the user's home immer * and shared. The `name` attribute is optional, but `type` must contain the * correct MIME. When image is a url, an existing image is shared without * re-uploading. It's better to upload a file so that the user's home * immer can ensure it remains available. * privacy level determines who receives and can acccess the message. * direct: Only those named in `to` receive the message. * friends: Direct plus friends list. * public: Direct plus Friends plus accessible via URL for sharing. * @param {(File|Blob|HTMLCanvasElement|string)} image - Image data to upload or url to share * @param {string} privacy - 'direct', 'friends', or 'public' * @param {string[]} [to] - Addressees. Accepts Immers handles (username[domain.name]) and ActivityPub IRIs * @returns {Promise} Url of newly posted message */ sendImage(image: (File | Blob | HTMLCanvasElement | string), privacy: string, to?: string[]): Promise; /** * Upload and/or share a video. * When video is a File/Blob, it will be uploaded to the user's home immer * and shared. The `name` attribute is optional, but `type` must contain the * correct MIME. When video is a url, an existing video is shared without * re-uploading. It's better to upload a file so that the user's home * immer can ensure it remains available. * * Privacy level determines who receives and can access the message. * direct: Only those named in `to` receive the message. * friends: Direct plus friends list. * public: Direct plus Friends plus accessible via URL for sharing. * @param {(File|Blob|string)} video - Video data to upload or url to share * @param {string} privacy - 'direct', 'friends', or 'public' * @param {string[]} [to] - Addressees. Accepts Immers handles (username[domain.name]) and ActivityPub IRIs * @returns {Promise} Url of newly posted message */ sendVideo(video: (File | Blob | string), privacy: string, to?: string[]): Promise; /** * Upload and optionally share a 3D model. * GLB is preferred. Other single-file model formats can be uploaded, * but may not be supported when shared to other immers. The blob's * type attribute must contain the correct MIME. * * The privacy and to arguments determine who receives and can access a post featuring the uploaded model. * For some uses, like collecting a new avatar, you may omit these to not share a post * (this user will still be able to see the model in their outbox). * direct: Only those named in `to` receive the message. * friends: Direct plus friends list. * public: Direct plus Friends plus accessible via URL for sharing. * @param {string} name - Label for the model * @param {(File|Blob)} glb - the 3D model file * @param {(File|Blob)} [icon] - optional 2d image preview for the model * @param {string} [privacy] - 'direct', 'friends', or 'public' * @param {string[]} [to] - Addressees. Accepts Immers handles (username[domain.name]) and ActivityPub IRIs */ sendModel(name: string, glb: (File | Blob), icon?: (File | Blob), privacy?: string, to?: string[]): Promise; /** * This method will either initiate a new friend request or, * if a request has already been received from the target user, * accept a pending friend request. To create a friend connection, * both users will need to call this method with the other user's handle. * @param {string} handle - the target user's immers handle or profile id */ addFriend(handle: string): Promise; /** * Remove a relationship to another immerser, * either by removing an existing friend, * rejecting a pending incoming friend request, * or canceling a pending outgoing friend request * @param {string} handle - the target user's immers handle or profile id */ removeFriend(handle: string): Promise; /** * Add an someone to this immerser's blocklist. While on a blocklist, * no messages/requests will sent or received between these two users, * and any past messages will be omitted from feeds and friends lists. * You should also prevent realtime interactions in your * application from users in Profile.collections.blocked (e.g. hide avatars, mute audio) * @param {string} handle - the target user's immers handle or profile id */ blockUser(handle: string): Promise; /** * Remove someone to this immerser's blocklist. Messages will once again * be sent & received between users and past messages from before the block * will be visible again in feeds and friends lists. * @param {string} handle - the target user's immers handle or profile id */ unblockUser(handle: string): Promise; /** * Upload a 3d model as an avatar and optionally share it * @param {string} name - Name/description * @param {Blob} glb - 3d model gltf binary file * @param {Blob} icon - Preview image for thumbnails * @param {string} privacy - 'direct', 'friends', or 'public' * @param {} [to] - Addressees. Accepts Immers handles (username[domain.name]) and ActivityPub IRIs * @returns {Promise} IRI of avatar creation post */ createAvatar(name: string, glb: Blob, icon: Blob, privacy: string, to?: any): Promise; /** * Add an existing avatar to a user's personal avatar collection * @param {(string|Activities.APActivity)} sourceActivity - Create activity for the avatar or IRI of activity (other activities with the avatar as their object, e.g. Offer, also allowed) * @returns {Promise} IRI of avatar add activity */ addAvatar(sourceActivity: (string | Activities.APActivity)): Promise; /** * Update user's avatar in their profile. * @param {(object|string)} avatar - Model type object or id for one (or activity containing the model as its object) * @returns {Promise} IRI of profile update activity */ useAvatar(avatar: (object | string)): Promise; /** * Remove user's avatar from their Avatar Collection. * @param {(string|Activities.APActivity)} sourceActivity - Activity IRI or Activity from the Avatars Collection to remove * @returns {Promise} IRI of the remove activity */ removeAvatar(sourceActivity: (string | Activities.APActivity)): Promise; /** * Attempt to fetch a cross-domain resource. * Prefers using the local immer's proxy service if available, * falling back to the user's home immer's proxy service if available or plain fetch. * @param {string} url - resource to GET * @param {object} headers - fetch headers */ corsProxyFetch(url: string, headers: object): Promise; /** * Get a user ID/URL from their handle using webfinger * @param {string} handle - immers handle * @returns {string | undefined} - The profile IRI or undefined if failed */ resolveProfileIRI(handle: string): string | undefined; /** * Get a user's profile object from their handle. * Uses logged-in users's home immer proxy service if available * @param {string} handle - Immers handle * @returns {Profile | undefined} - User profile or undefined if failed */ getProfile(handle: string): Profile | undefined; getNodeInfo(handle: any): Promise; /** * Make navigating between immers easier by providing the user's * handle to the next experience so they don't have to type it in. * Use as an onClick handler to inject the "me hash" into any cross-origin * anchor when navigating. Can be registered directly on the anchor * or on a parent element. * Will fallback to default click behavior if same-origin, not logged in, * or unable to process url. * @param {MouseEvent} clickEvent */ handleImmerLinkClick(clickEvent: MouseEvent): void; /** * Navigate to a given url while injecting a "me hash" to provide the * user's handle to the destination site so that they don't have to re-enter it. * Safe to use without checking if user is logged in, will just navigate normally * if not * @param {string} href */ navigateToImmerLink(href: string): void; /** * fetch the /o/immer object for the current immer from memory cache or network * @type {Promise} */ get localImmerPlaceObject(): Promise; /** * Users Immers handle, if known. May be available even when logged-out if passed via URL or stored from past login * @type {string} */ get handle(): string; /** * List of scopes authorized by the user during login. * This may differ from what you requested, as the user can override * during the authorization process. * @type {string[]} * @see {SCOPES} */ get authorizedScopes(): string[]; place: any; /** * Connect to user's Immers Space profile, using pop-up window for OAuth if needed * @param {string} tokenCatcherURL Page on your domain that runs {@link catchToken} on load to retrieve the granted access token. * Can be the same page as long as loading it again in a pop-up won't cause a the main session to disconnect. * @param {string} requestedRole Access level to request, see {@link roles} for details * @param {string} [handle] User's immers handle. Optional if you have a local Immers Server * @deprecated Split into to methods, {@link login} and {@link enter}, for better control over when a user goes online * @returns {string} token OAuth2 acess token */ connect(tokenCatcherURL: string, requestedRole: string, handle?: string): string; /** * Attempt to restore session from a previously granted token. Requires options.allowStorage * @returns {Promise} Was reconnection successful * @deprecated Split into to methods, {@link restoreSession} and {@link enter}, for better control over when a user goes online */ reconnect(): Promise; #private; } export type Destination = { /** * Title of the destination */ name: string; /** * link to visit the destination */ url: string; /** * 'direct', 'friends', or 'public' determines who can view this Destination and url e.g. in recently visited places. Default: friends */ privacy?: string; /** * text summary of destination */ description?: string; /** * thumbnail image url */ previewImage?: string; /** * reference to the parent/homepage place object for this experience, if ommitted will use the local immer */ immer?: Activities.APPlace; }; export type Profile = { /** * - Globally unique identifier (ActivityPub IRI) */ id: string; /** * - Shorthand globally unique identifier, format: username[home.immer] */ handle: string; /** * - User's changeable preferred identifier, may contain spaces & symbols */ displayName: string; /** * - Domain of imme where user account is registered */ homeImmer: string; /** * - User's permanent uniqe identifier within their home immer */ username: string; /** * - Text description of user, may contain sanitized HTML */ bio: string; /** * - Profile icon url */ avatarImage: string; /** * - Profile avatar 3d model url */ avatarModel: string; /** * - Profile avatar full Model object */ avatarObject: Activities.APModel; /** * - Webpage to view full profile */ url: string; /** * - Map of user collections - urls to fetch lists of related activities. May include user-generated collections in addition to those listed */ collections: { avatars: string; blocked: string; destinations: string; friends: string; friendsDestinations: string; inbox: string; outbox: string; }; }; export type FriendStatus = { /** * - Profile object for friend */ profile: Profile; /** * - Currently online anywhere in Immers Space */ isOnline: boolean; /** * - Name of current or last immer visited */ locationName?: string; /** * - URL of current or last immer visited */ locationURL?: string; /** * - Destination object for current or last immer visited */ destination?: Destination; /** * - descriptor of the current relationship to this user */ status: ('friend-online' | 'friend-offline' | 'request-receved' | 'request-sent' | 'none'); /** * - Text description of current status, "Offline" / "Online at..." */ statusString: string; /** * - Unsanitized HTML description of current status with link. * You must sanitize this string before inserting into the DOM to avoid XSS attacks. */ __unsafeStatusHTML: string; /** * - Sanitize HTML description of current status with link. Safe to insert into DOM. */ statusHTML: string; }; export type Message = { /** * - URL of original message activity object, usable as unique id */ id: string; /** * - Message sender's Profile */ sender: Profile; /** * - Message sent time */ timestamp: Date; /** * - Describes the message content: 'chat', 'media', 'status', or 'other' */ type: string; /** * - Unsanitized HTML message content. * You must sanitize this string before inserting into the DOM to avoid XSS attacks. */ __unsafeMessageHTML: string; /** * - Sanitized HTML message content. Safe to insert into DOM. Media wrapped in IMG/VIDEO will have class immers-message-media */ messageHTML: string; /** * - 'image' or 'video' if the message is a media object */ mediaType?: string; /** * - source url if the message is a media object * (messageHTML will contain appropriate tags to display the media, but url can be used if you need custom display) */ url?: string; /** * - location tied to the message, if available */ destination?: Destination; /** * - the unmodified ActivityPub activity that is the source of the message */ _originalActivity: object; }; export type ImmersClientNewMessageEvent = { detail: { message: Message; }; }; import { Activities } from "./activities.js"; import { ImmersSocket } from "./streaming.js";