@bzr/bazaar
Version:
The Bazaar SDK. Conveniently use Bazaar with your app in the browser.
1,319 lines (1,304 loc) • 73.4 kB
JavaScript
import io from 'socket.io-client';
import { OAuth2Client, generateCodeVerifier } from '@badgateway/oauth2-client';
/******************************************************************************
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
/* global Reflect, Promise, SuppressedError, Symbol */
function __awaiter(thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
}
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
var e = new Error(message);
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
};
var GranteeType;
(function (GranteeType) {
GranteeType["USER"] = "user";
GranteeType["ANY"] = "any";
GranteeType["GROUP"] = "group";
GranteeType["ORG"] = "org";
})(GranteeType || (GranteeType = {}));
/**
*
*/
var PermissionType;
(function (PermissionType) {
PermissionType["READ"] = "read";
PermissionType["INSERT"] = "insert";
PermissionType["UPDATE"] = "update";
PermissionType["DELETE"] = "delete";
})(PermissionType || (PermissionType = {}));
/**
* Represents the options for sending notifications.
*/
var SendNotification;
(function (SendNotification) {
/**
* Send an notification if the target user does not use the app.
* This is akin to inviting the target user to use the app.
*/
SendNotification["INVITE_ONLY"] = "invite-only";
/**
* Always send an notification to the target user
*/
SendNotification["ALWAYS"] = "always";
/**
* Never send an notification to the target user
*/
SendNotification["NEVER"] = "never";
})(SendNotification || (SendNotification = {}));
/**
* The possible login types.
*/
var LoginType;
(function (LoginType) {
/**
* Default login type.
* Do pop-up login, if the pop-up fails to open, fallback to redirect.
*/
LoginType["POPUP_FALLBACK"] = "popup_fallback";
/**
* Do pop-up login only. Do not fallback to redirect.
*/
LoginType["POPUP"] = "popup";
/**
* Do redirect login. Do not open a pop-up.
*/
LoginType["REDIRECT"] = "redirect";
})(LoginType || (LoginType = {}));
/**
*
*/
var OrderByType;
(function (OrderByType) {
OrderByType["ASC"] = "asc";
OrderByType["DESC"] = "desc";
})(OrderByType || (OrderByType = {}));
/**
* Constant of all the error types
*
*/
var ErrorTypes;
(function (ErrorTypes) {
ErrorTypes[ErrorTypes["Unspecified"] = 1] = "Unspecified";
ErrorTypes[ErrorTypes["NoPermission"] = 2] = "NoPermission";
ErrorTypes[ErrorTypes["ReservedCollectionName"] = 3] = "ReservedCollectionName";
ErrorTypes[ErrorTypes["CollectionDoesNotExist"] = 4] = "CollectionDoesNotExist";
ErrorTypes[ErrorTypes["DatabaseDoesNotExist"] = 5] = "DatabaseDoesNotExist";
})(ErrorTypes || (ErrorTypes = {}));
/**
*
*/
class BazaarError extends Error {
constructor(type, ...params) {
super(...params);
// Maintains proper stack trace for where our error was thrown (only available on V8)
if (Error.captureStackTrace) {
Error.captureStackTrace(this, BazaarError);
}
this.name = "BazaarError";
this.type = type;
}
}
function isNoAppUserError(e) {
return e instanceof BazaarError && e.type === ErrorTypes.DatabaseDoesNotExist;
}
function isNoPermissionError(e) {
return e instanceof BazaarError && e.type === ErrorTypes.NoPermission;
}
/**
* Generates a secure random string using the browser crypto functions
*/
function generateRandomString() {
const array = new Uint32Array(28);
window.crypto.getRandomValues(array);
return Array.from(array, (dec) => ("0" + dec.toString(16)).slice(-2)).join("");
}
/**
* Opens and centers pop-up on specific window to account for multiple monitors
* @param url url to open
* @param windowName name to identify pop-up window
* @param win the parent/opener window
* @returns `Window` if successful, `null` if blocked by a built-in browser pop-up blocker. Otherwise fails silently I think...
*/
function popupWindow(url, windowName, win) {
const w = 500;
const h = 608;
const y = win.top.outerHeight / 2 + win.top.screenY - h / 2;
const x = win.top.outerWidth / 2 + win.top.screenX - w / 2;
return win.open(url, windowName, `popup=yes, width=${w}, height=${h}, top=${y}, left=${x}`);
}
function arrayMirrorSubscribeListener(data, listener) {
// Clear the array before returning the listener
data.length = 0;
return {
onInitial: (doc) => {
data.push(doc);
if (listener && listener.onInitial) {
listener.onInitial(doc);
}
},
onAdd: (doc) => {
data.push(doc);
if (listener && listener.onAdd) {
listener.onAdd(doc);
}
},
onChange: (oldDoc, newDoc) => {
const idx = data.findIndex((d) => d.id === oldDoc.id);
if (idx > -1) {
data[idx] = newDoc;
}
else {
// It is missing for some reason, add it.
data.push(newDoc);
}
if (listener && listener.onChange) {
listener.onChange(oldDoc, newDoc);
}
},
onDelete: (doc) => {
const idx = data.findIndex((d) => d.id === doc.id);
if (idx > -1) {
data.splice(idx, 1);
}
if (listener && listener.onDelete) {
listener.onDelete(doc);
}
},
};
}
function objectMirrorSubscribeListener(data, listener) {
return {
onInitial: (doc) => {
if (listener && listener.onInitial) {
listener.onInitial(doc);
}
},
onAdd: (doc) => {
if (listener && listener.onAdd) {
listener.onAdd(doc);
}
},
onChange: (oldDoc, newDoc) => {
if (listener && listener.onChange) {
listener.onChange(oldDoc, newDoc);
}
},
onDelete: (doc) => {
if (listener && listener.onDelete) {
listener.onDelete(doc);
}
},
};
}
const noSharingNotification = { createNotification: false, sendMessage: SendNotification.NEVER };
/**
* The URI of the current Bazaar deployment
*/
const bazaarUri = process.env.NODE_ENV === "development" ? "http://localhost:3377" : "https://cloud.bzr.dev";
/**
* The URL path for sharing links
*/
const linkPath = "/l/";
/**
* The prefix when namespacing local storage variables
*/
const namespacePrefix = "bazaar_";
/**
* The class that encapsulates the low level data API
* @internal
*/
class API {
constructor(options, onConnect, onConnectError) {
/**
* Version of the API
*/
this.version = "v1";
/**
* URI for the Data API, Bazaar's realtime data storage service.
*/
this.bazaarUri = bazaarUri;
/**
* ID attribute of the dialog HTML element
*/
this.modalId = "bazaar-modal";
/**
* ID attribute of the style HTML element for the modal styles
*/
this.modalStylesId = "bazaar-modal-styles";
/**
* Makes sure a connection to the Data API has been made.
*/
this.waitForConnection = () => {
if (!this.dataApi) {
return Promise.reject(new Error("Uninitialized data API. Possible unauthenticated request"));
}
return new Promise((resolve, reject) => {
if (this.dataApi.connected) {
resolve(true);
}
else {
this.dataApi.on("connect", () => {
resolve(true);
});
// Don't wait for connection indefinitely
setTimeout(() => {
reject(new Error("Timeout waiting for on connect"));
}, 3000);
}
});
};
/**
* Promisifies a Data API emit event
*
* @param event - A Data API event name, like `collections:create`
* @param payload -
*/
this.asyncEmit = (event, payload) => __awaiter(this, void 0, void 0, function* () {
yield this.waitForConnection();
return new Promise((resolve, reject) => {
this.dataApi.emit(event, payload, (response) => {
if (response.error) {
reject(new BazaarError(response.error.type, `${response.error.message} (event: ${event}, payload: ${JSON.stringify(payload)})`));
}
else {
resolve(response);
}
});
});
});
if (options.bazaarUri) {
this.bazaarUri = options.bazaarUri;
}
this.onConnect = onConnect;
this.onConnectError = onConnectError;
/**
* Namespace local storage key names
*/
const namespace = namespacePrefix + options.appId;
this.tokenKeyName = `${namespace}_token`;
// Make a connection to the Data API if logged in
this.connect();
}
/**
* Creates a Data API connection with an auth token
*/
connect() {
const token = localStorage.getItem(this.tokenKeyName);
if (!token) {
return;
}
this.dataApi = io(this.bazaarUri, {
auth: { token },
});
this.dataApi.on("connect", () => {
console.log("sdk: connected. dataApi.id:", this.dataApi.id);
this.onConnect();
});
this.dataApi.on("connect_error", (error) => {
let errorMessage = error.message;
if (error.message.includes("Unauthorized")) {
errorMessage = "Unauthorized";
}
else if (error.message.includes("TokenExpiredError")) {
errorMessage = "Token expired";
}
this.onConnectError(errorMessage);
});
}
/**
* Create context
*
* @param options - An optional object for specifying query options.
* @returns BazaarMessage
*/
createContext(options = {}) {
return __awaiter(this, void 0, void 0, function* () {
if (!options.ownerId) {
return { message: "Own user context exists" };
}
return this.asyncEmit(this.version + ":createContext", options);
});
}
//
// Collection API
//
/**
* Gets a collection doc
*
* @param collectionName - The name of the collection to read
* @param docId - The doc ID
* @param options - An optional object for specifying query options.
* @returns Specify a doc ID to get a specific doc, otherwise all docs are returned. Specify a user ID to operate on a collection owned by that user ID. Otherwise operates on a collection owned by the authenticated user.
*/
collectionGetOne(collectionName, docId, options = {}) {
return __awaiter(this, void 0, void 0, function* () {
const payload = { collectionName, docId };
Object.assign(payload, options);
return this.asyncEmit(this.version + ":collection:getOne", payload);
});
}
/**
* Gets all collection documents for a given filter.
*
* @param collectionName - The name of the collection to read.
* @param options - An optional object for specifying query options.
* @returns Returns a specific document if a `docId` is specified; otherwise, returns all documents. If a `userId` is specified, it operates on a collection owned by that user ID. Otherwise, it operates on a collection owned by the authenticated user.
*/
collectionGetAll(collectionName, options = {}) {
return __awaiter(this, void 0, void 0, function* () {
const payload = { collectionName };
Object.assign(payload, options);
return this.asyncEmit(this.version + ":collection:getAll", payload);
});
}
/**
* Subscribes to doc changes.
*
* @param collectionName - The name of the collection to subscribe to
* @param docId - The document ID
* @param options - An optional object for specifying query options.
* @param listener - The callback function that receives document change events.
* @returns An unsubscribe function
*/
collectionSubscribeOne(collectionName, docId, options = {}, listener) {
return __awaiter(this, void 0, void 0, function* () {
const payload = { collectionName, docId };
Object.assign(payload, options);
const response = (yield this.asyncEmit(this.version + ":collection:subscribeOne", payload)); // where data is the subscription handle
const subscriptionHandle = response.data;
const rawListener = toRawSubscribeListener(listener);
this.dataApi.on(subscriptionHandle, rawListener);
return () => __awaiter(this, void 0, void 0, function* () {
this.dataApi.off(subscriptionHandle, rawListener);
const resp = (yield this.asyncEmit(this.version + ":collection:unsubscribe", subscriptionHandle));
return resp.message;
});
});
}
/**
* Subscribes to collection changes. Private by default, or public with read permission.
*
* @param collectionName - The name of the collection to subscribe to.
* @param options - An optional object for specifying query options.
* @param listener - The callback function that receives document change events.
* @returns An unsubscribe function
*/
collectionSubscribeAll(collectionName, options = {}, listener) {
return __awaiter(this, void 0, void 0, function* () {
const payload = { collectionName };
Object.assign(payload, options);
const response = (yield this.asyncEmit(this.version + ":collection:subscribeAll", payload)); // where data is the subscription handle
const subscriptionHandle = response.data;
const rawListener = toRawSubscribeListener(listener);
this.dataApi.on(subscriptionHandle, rawListener);
return () => __awaiter(this, void 0, void 0, function* () {
this.dataApi.off(subscriptionHandle, rawListener);
const resp = (yield this.asyncEmit(this.version + ":collection:unsubscribe", subscriptionHandle));
return resp.message;
});
});
}
/**
* Inserts a collection doc.
*
* @param collectionName - The name of the collection to operate on.
* @param doc - The doc to insert.
* @param options - An optional object for specifying query options.
* @returns Where `data` is the array of new doc IDs (only generated IDs)
*/
collectionInsertOne(collectionName, doc, options = {}) {
return __awaiter(this, void 0, void 0, function* () {
const payload = { collectionName, doc };
Object.assign(payload, options);
return this.asyncEmit(this.version + ":collection:insertOne", payload);
});
}
/**
* Updates all collection docs, or a single doc if doc ID exists.
*
* @param collectionName - The name of the collection to operate on.
* @param docId - ID of document to update
* @param doc - Document changes
* @param options - An optional object for specifying query options.
*/
collectionUpdateOne(collectionName, docId, doc, options = {}) {
return __awaiter(this, void 0, void 0, function* () {
const payload = { collectionName, docId, doc };
Object.assign(payload, options);
return this.asyncEmit(this.version + ":collection:updateOne", payload);
});
}
/**
* Replaces a collection doc. Private by default, or public with insert, update, delete permissions.
*
* @param collectionName - The name of the collection to operate on.
* @param docId - ID of document to replace.
* @param doc - The new doc.
* @param options - An optional object for specifying query options.
*/
collectionReplaceOne(collectionName, docId, doc, options = {}) {
return __awaiter(this, void 0, void 0, function* () {
const payload = { collectionName, docId, doc };
Object.assign(payload, options);
return this.asyncEmit(this.version + ":collection:replaceOne", payload);
});
}
/**
* Deletes a doc
*
* @param collectionName - The name of the collection to operate on.
* @param docId - ID of document to delete
* @param options - An optional object for specifying query options.
*/
collectionDeleteOne(collectionName, docId, options = {}) {
return __awaiter(this, void 0, void 0, function* () {
const payload = { collectionName, docId };
Object.assign(payload, options);
return this.asyncEmit(this.version + ":collection:deleteOne", payload);
});
}
/**
* Deletes all collection docs matching an optional filter.
*
* @param collectionName - The name of the collection to operate on.
* @param options - An optional object for specifying query options.
*/
collectionDeleteAll(collectionName, options = {}) {
return __awaiter(this, void 0, void 0, function* () {
const payload = { collectionName };
Object.assign(payload, options);
return this.asyncEmit(this.version + ":collection:deleteAll", payload);
});
}
//
// Collections API
//
/**
* Creates a collection.
*/
collectionsCreate(collectionName, options = {}) {
return __awaiter(this, void 0, void 0, function* () {
const payload = { collectionName };
Object.assign(payload, options);
return this.asyncEmit(this.version + ":collections:create", payload);
});
}
/**
* Drops a collection.
*/
collectionsDrop(collectionName, options = {}) {
return __awaiter(this, void 0, void 0, function* () {
const payload = { collectionName };
Object.assign(payload, options);
return this.asyncEmit(this.version + ":collections:drop", payload);
});
}
/**
* Lists all collection names.
* @returns Where `data` is an array of collection names
*/
collectionsList(options = {}) {
return __awaiter(this, void 0, void 0, function* () {
return this.asyncEmit(this.version + ":collections:list", options);
});
}
//
// Permissions API (permissions, links, granted_permissions, groups)
//
/**
* Lists permissions.
*
* @param query - If no options are set, all permissions are returned.
* @param options - database ID options.
* @returns All permissions matching query options.
*/
permissionsList(query = {}, options = {}) {
return __awaiter(this, void 0, void 0, function* () {
const payload = { query };
Object.assign(payload, options);
return this.asyncEmit(this.version + ":permissions:list", payload);
});
}
/**
* Creates a permission.
*/
permissionsCreate(permission, notification, options = {}) {
return __awaiter(this, void 0, void 0, function* () {
const payload = { permission, notification };
Object.assign(payload, options);
return this.asyncEmit(this.version + ":permissions:create", payload);
});
}
/**
* Deletes a permission.
*
* @param permissionId - The ID of the permission to delete.
*/
permissionsDelete(permissionId, options = {}) {
return __awaiter(this, void 0, void 0, function* () {
const payload = { permissionId };
Object.assign(payload, options);
return this.asyncEmit(this.version + ":permissions:delete", payload);
});
}
/**
* Creates a permission link.
*/
linksCreate(permission, description = "", limit = 1, options = {}) {
return __awaiter(this, void 0, void 0, function* () {
const payload = { permission, description, limit };
Object.assign(payload, options);
return this.asyncEmit(this.version + ":links:create", { permission, description, limit });
});
}
/**
* Lists permission links.
*
* @param query - If no options are set, all links are returned.
* @param options - database ID options.
* @returns Where `data` is an array of links
*/
linksList(query = {}, options = {}) {
return __awaiter(this, void 0, void 0, function* () {
const payload = { query };
Object.assign(payload, options);
return this.asyncEmit(this.version + ":links:list", payload);
});
}
/**
* Subscribes to link changes
*
* @param query -
* @param options -
* @param listener - The callback function that receives link change events.
* @returns An unsubscribe function
*/
linksSubscribe(query = {}, options = {}, listener) {
return __awaiter(this, void 0, void 0, function* () {
const payload = { query };
Object.assign(payload, options);
const response = (yield this.asyncEmit(this.version + ":links:subscribe", payload)); // where data is the subscription handle
const subscriptionHandle = response.data;
const rawListener = toRawSubscribeListener(listener);
this.dataApi.on(subscriptionHandle, rawListener);
return () => __awaiter(this, void 0, void 0, function* () {
this.dataApi.off(subscriptionHandle, rawListener);
return this.asyncEmit(this.version + ":links:unsubscribe", subscriptionHandle);
});
});
}
/**
* Deletes permission links.
*/
linksDelete(linkId) {
return __awaiter(this, void 0, void 0, function* () {
return this.asyncEmit(this.version + ":links:delete", { linkId });
});
}
/**
* Lists granted permissions
*
* @returns a list of granted permissions
*/
grantedPermissionsList(query = {}, options) {
return __awaiter(this, void 0, void 0, function* () {
const payload = { query };
Object.assign(payload, options);
return this.asyncEmit(this.version + ":granted_permissions:list", payload);
});
}
/**
* Subscribes to granted permissions changes
*
* @param listener - The callback function that receives granted permissions change events.
* @returns An unsubscribe function
*/
grantedPermissionsSubscribe(query = {}, options, listener) {
return __awaiter(this, void 0, void 0, function* () {
const payload = { query };
Object.assign(payload, options);
const response = (yield this.asyncEmit(this.version + ":granted_permissions:subscribe", payload)); // where data is the subscription handle
const subscriptionHandle = response.data;
const rawListener = toRawSubscribeListener(listener);
this.dataApi.on(subscriptionHandle, rawListener);
return () => __awaiter(this, void 0, void 0, function* () {
this.dataApi.off(subscriptionHandle, rawListener);
return this.asyncEmit(this.version + ":granted_permissions:unsubscribe", subscriptionHandle);
});
});
}
/**
* Deletes a granted permission
*
* @param grantedPermissionId - The ID of the granted permission to delete
*/
grantedPermissionsDelete(grantedPermissionId) {
return __awaiter(this, void 0, void 0, function* () {
const payload = { grantedPermissionId };
return this.asyncEmit(this.version + ":granted_permissions:delete", payload);
});
}
/**
* Get a group
*
* @param groupId - The ID of the group to get.
*/
groupsGet(groupId, options = {}) {
return __awaiter(this, void 0, void 0, function* () {
const payload = { groupId };
Object.assign(payload, options);
return this.asyncEmit(this.version + ":groups:get", payload);
});
}
/**
* Lists groups.
*
* @param options - database ID options.
* @returns All groups matching query.
*/
groupsList(options = {}) {
return __awaiter(this, void 0, void 0, function* () {
return this.asyncEmit(this.version + ":groups:list", options);
});
}
/**
* Creates a group.
*/
groupsCreate(group, options = {}) {
return __awaiter(this, void 0, void 0, function* () {
const payload = { group };
Object.assign(payload, options);
return this.asyncEmit(this.version + ":groups:create", payload);
});
}
/**
* Add member to a group.
*
* @param groupId - The ID of the group
* @param userId - The ID of the user
* @param options - database ID options.
*/
groupsAddMember(groupId, userId, options = {}) {
return __awaiter(this, void 0, void 0, function* () {
const payload = { groupId, userId };
Object.assign(payload, options);
return this.asyncEmit(this.version + ":groups:addMember", payload);
});
}
/**
* Remove member from a group.
*
* @param groupId - The ID of the group
* @param userId - The ID of the user
* @param options - database ID options.
*
*/
groupsRemoveMember(groupId, userId, options = {}) {
return __awaiter(this, void 0, void 0, function* () {
const payload = { groupId, userId };
Object.assign(payload, options);
return this.asyncEmit(this.version + ":groups:removeMember", payload);
});
}
/**
* Deletes a group.
*
* @param groupId - The ID of the group to delete.
*/
groupsDelete(groupId, options = {}) {
return __awaiter(this, void 0, void 0, function* () {
const payload = { groupId };
Object.assign(payload, options);
return this.asyncEmit(this.version + ":groups:delete", payload);
});
}
//
// Notifications API
//
/**
* Creates a notification.
*/
notificationsCreate(notification) {
return __awaiter(this, void 0, void 0, function* () {
return this.asyncEmit(this.version + ":notifications:create", { notification });
});
}
/**
* Lists notifications.
*
* @param options - If no options are set, all non-hidden notifications are returned.
* @returns Where `data` is an array of notifications
*/
notificationsList(options = {}) {
return __awaiter(this, void 0, void 0, function* () {
return this.asyncEmit(this.version + ":notifications:list", options);
});
}
/**
* Subscribes to notification changes
*
* @param options -
* @param listener - The callback function that receives notification change events.
* @returns An unsubscribe function
*/
notificationsSubscribe(options = {}, listener) {
return __awaiter(this, void 0, void 0, function* () {
const response = (yield this.asyncEmit(this.version + ":notifications:subscribe", options)); // where data is the subscription handle
const subscriptionHandle = response.data;
const rawListener = toRawSubscribeListener(listener);
this.dataApi.on(subscriptionHandle, rawListener);
return () => __awaiter(this, void 0, void 0, function* () {
this.dataApi.off(subscriptionHandle, rawListener);
return this.asyncEmit(this.version + ":notifications:unsubscribe", subscriptionHandle);
});
});
}
/**
* Hides notifications.
*/
notificationsHide(notificationId) {
return __awaiter(this, void 0, void 0, function* () {
return this.asyncEmit(this.version + ":notifications:hide", { notificationId });
});
}
/**
* Deletes notifications.
*/
notificationsDelete(notificationId) {
return __awaiter(this, void 0, void 0, function* () {
return this.asyncEmit(this.version + ":notifications:delete", { notificationId });
});
}
//
// Social API
//
/**
* Gets the user info for a given ID or handle.
* @param payload - The ID or handle of the user, defaults to logged in user's ID.
*/
usersGet(payload = {}) {
return __awaiter(this, void 0, void 0, function* () {
return this.asyncEmit(this.version + ":users:get", payload);
});
}
/**
* Lists contacts
* @returns a list of contacts
*/
contactsList() {
return __awaiter(this, void 0, void 0, function* () {
const payload = {};
return this.asyncEmit(this.version + ":contacts:list", payload);
});
}
/**
* Subscribes to contacts changes
*
* @param listener - The callback function that receives contact change events.
* @returns An unsubscribe function
*/
contactsSubscribe(listener) {
return __awaiter(this, void 0, void 0, function* () {
const response = (yield this.asyncEmit(this.version + ":contacts:subscribe", {})); // where data is the subscription handle
const subscriptionHandle = response.data;
const rawListener = toRawSubscribeListener(listener);
this.dataApi.on(subscriptionHandle, rawListener);
return () => __awaiter(this, void 0, void 0, function* () {
this.dataApi.off(subscriptionHandle, rawListener);
return this.asyncEmit(this.version + ":contacts:unsubscribe", subscriptionHandle);
});
});
}
//
// Organizations API
//
/**
* Gets all orgs user is member/admin.
*/
organizationsList() {
return __awaiter(this, void 0, void 0, function* () {
const payload = {};
return this.asyncEmit(this.version + ":organizations:list", payload);
});
}
/**
* Gets the org info for a given ID or handle.
* @param payload - The ID or handle of the org, requires one of the two.
*/
organizationsGet(payload) {
return __awaiter(this, void 0, void 0, function* () {
return this.asyncEmit(this.version + ":organizations:get", payload);
});
}
/**
* Lists teams
* @returns a list of teams
*/
teamsList(payload) {
return __awaiter(this, void 0, void 0, function* () {
return this.asyncEmit(this.version + ":teams:list", payload);
});
}
/**
* Send email message
* @returns
*/
emailSendMessage(emailMessage) {
return __awaiter(this, void 0, void 0, function* () {
return this.asyncEmit(this.version + ":email:sendMessage", emailMessage);
});
}
/**
* Send email event
* @returns
*/
emailSendCalendarInvite(calendarInvite) {
return __awaiter(this, void 0, void 0, function* () {
return this.asyncEmit(this.version + ":email:sendCalendarInvite", calendarInvite);
});
}
closeModal() {
if (this.onModalMessage) {
window.removeEventListener("message", this.onModalMessage);
this.onModalMessage = undefined;
}
if (this.onModalClose) {
this.onModalClose();
this.onModalClose = undefined;
}
this.modal.close();
}
/**
* Opens a modal
*/
openModal(path, onMessage = undefined, onClose = undefined) {
this.initializeModal();
this.iframe.src = this.bazaarUri + path;
this.modal.showModal();
this.onModalClose = onClose;
this.onModalMessage = (event) => {
// Only handle messages from our iframe
if (this.iframe && event.source !== this.iframe.contentWindow)
return;
if (event.data && event.data.command) {
// On the login screen there are a lot of events with
// event.data.command==="calculateSubFramePositioning"
return;
}
if (onMessage && typeof onMessage === "function") {
onMessage(event.data);
}
this.closeModal();
};
window.addEventListener("message", this.onModalMessage);
return;
}
/**
* Creates and appends a modal to the DOM
*/
initializeModal() {
const modal = document.getElementById(this.modalId);
if (modal)
return;
// Initialize modal
this.modal = document.createElement("dialog");
this.modal.setAttribute("id", this.modalId);
// Needed to hide a white bar at the bottom of the dialog
this.modal.style.backgroundColor = "oklch(.178606 .034249 265.754874)"; // --b3
this.modal.style.width = "min(700px, 100vw)";
this.modal.style.borderWidth = "0";
this.modal.style.padding = "0";
this.modal.style.margin = "auto";
this.modal.style.borderRadius = "1rem";
const style = document.createElement("style");
style.id = this.modalStylesId;
document.head.appendChild(style);
// The ::backdrop pseudo property cannot be added with the `.style` syntax
style.sheet.insertRule(`#${this.modalId}::backdrop {
background: rgba(0,0,0,.8);
} `, 0);
const button = document.createElement("button");
button.style.borderWidth = "0";
button.style.fontWeight = "bold";
button.style.fontSize = "32px";
button.style.right = "8px";
button.style.top = "5px";
button.style.background = "transparent";
button.style.cursor = "pointer";
button.style.position = "fixed";
button.style.zIndex = "10";
button.style.color = "oklch(.841536 .007965 265.754874)"; // --bc
button.innerHTML = "×";
button.autofocus = true;
button.ariaLabel = "Close Bazaar modal";
button.onclick = () => this.closeModal();
this.modal.appendChild(button);
this.iframe = document.createElement("iframe");
this.iframe.style.width = "100%";
this.iframe.style.minHeight = "80vh";
this.iframe.style.borderWidth = "0";
this.modal.appendChild(this.iframe);
document.body.appendChild(this.modal);
}
}
function toRawSubscribeListener(listener) {
return (changes) => {
if (!changes.oldDoc && changes.newDoc && listener.onAdd) {
return listener.onAdd(changes.newDoc);
}
if (changes.oldDoc && changes.newDoc && listener.onChange) {
return listener.onChange(changes.oldDoc, changes.newDoc);
}
if (changes.oldDoc && !changes.newDoc && listener.onDelete) {
return listener.onDelete(changes.oldDoc);
}
};
}
/**
* @internal
*/
class CollectionAPI {
constructor(api, collectionName, collectionOptions = {}, contextOptions = {}) {
this.api = api;
this.collectionName = collectionName;
this.collectionOptions = collectionOptions;
this.contextOptions = contextOptions;
}
getOne(docId) {
return __awaiter(this, void 0, void 0, function* () {
return this.withCollection(() => __awaiter(this, void 0, void 0, function* () {
const res = yield this.api.collectionGetOne(this.collectionName, docId, this.contextOptions);
return res.data;
}));
});
}
getAll(filter = {}, options = {}) {
return __awaiter(this, void 0, void 0, function* () {
return this.withCollection(() => __awaiter(this, void 0, void 0, function* () {
const res = yield this.api.collectionGetAll(this.collectionName, Object.assign(Object.assign(Object.assign({}, this.contextOptions), options), { filter }));
return res.data;
}));
});
}
getPage(pageNumber, pageSize, filter = {}, options = {}) {
return __awaiter(this, void 0, void 0, function* () {
const startOffset = pageNumber * pageSize;
const endOffset = (pageNumber + 1) * pageSize;
return this.getAll(filter, Object.assign({ startOffset, endOffset }, options));
});
}
/**
* @returns An unsubscribe function
*/
subscribeOne(docId, listener) {
return __awaiter(this, void 0, void 0, function* () {
if (listener.onInitial) {
const doc = yield this.getOne(docId);
if (doc) {
listener.onInitial(doc);
}
}
return this.withCollection(() => this.api.collectionSubscribeOne(this.collectionName, docId, this.contextOptions, listener));
});
}
/**
* @returns An unsubscribe function
*/
subscribeAll(filter, listener) {
return __awaiter(this, void 0, void 0, function* () {
if (listener.onInitial) {
const docs = yield this.getAll(filter);
for (const doc of docs) {
listener.onInitial(doc);
}
}
return this.withCollection(() => this.api.collectionSubscribeAll(this.collectionName, Object.assign({ filter }, this.contextOptions), listener));
});
}
/**
* @returns The doc ID
*/
insertOne(doc) {
return __awaiter(this, void 0, void 0, function* () {
return this.withCollection(() => __awaiter(this, void 0, void 0, function* () {
const res = yield this.api.collectionInsertOne(this.collectionName, doc, this.contextOptions);
return res.data;
}));
});
}
updateOne(docId, doc) {
return __awaiter(this, void 0, void 0, function* () {
return this.withCollection(() => this.api.collectionUpdateOne(this.collectionName, docId, doc, this.contextOptions));
});
}
replaceOne(docId, doc) {
return __awaiter(this, void 0, void 0, function* () {
return this.withCollection(() => this.api.collectionReplaceOne(this.collectionName, docId, doc, this.contextOptions));
});
}
deleteOne(docId) {
return __awaiter(this, void 0, void 0, function* () {
return this.withCollection(() => this.api.collectionDeleteOne(this.collectionName, docId, this.contextOptions));
});
}
deleteAll(filter = {}) {
return __awaiter(this, void 0, void 0, function* () {
return this.withCollection(() => this.api.collectionDeleteAll(this.collectionName, Object.assign({ filter }, this.contextOptions)));
});
}
//
withCollection(collectionQuery) {
return __awaiter(this, void 0, void 0, function* () {
try {
return yield collectionQuery();
}
catch (error) {
if (error instanceof BazaarError && error.type == ErrorTypes.CollectionDoesNotExist) {
yield this.api.collectionsCreate(this.collectionName, this.contextOptions);
if (this.collectionOptions.onCreate) {
yield this.collectionOptions.onCreate();
}
return collectionQuery();
}
throw error;
}
});
}
}
/**
* The class that encapsulates the collections API
* @internal
*/
class CollectionsAPI {
constructor(api, contextOptions = {}) {
this.api = api;
this.contextOptions = contextOptions;
}
/**
* Creates a collection.
*/
create(collectionName) {
return __awaiter(this, void 0, void 0, function* () {
return this.api.collectionsCreate(collectionName, this.contextOptions);
});
}
/**
* Drops a collection.
*/
drop(collectionName) {
return __awaiter(this, void 0, void 0, function* () {
return this.api.collectionsDrop(collectionName, this.contextOptions);
});
}
/**
* Lists all collection names.
* @returns Where `data` is an array of collection names
*/
list() {
return __awaiter(this, void 0, void 0, function* () {
const res = yield this.api.collectionsList(this.contextOptions);
return res.data;
});
}
}
/**
* The class that encapsulates the permissions API
* @internal
*/
class PermissionsAPI {
constructor(api, uri, contextOptions = {}) {
/**
* Links
*/
this.links = {
/**
* Creates a link
*/
create: (permission, description = "", limit = 1) => __awaiter(this, void 0, void 0, function* () {
const { data: basicLink } = yield this.api.linksCreate(permission, description, limit, this.contextOptions);
return Object.assign({ url: this.linkUri + basicLink.id }, basicLink);
}),
/**
* Lists links
*/
list: (query = {}) => __awaiter(this, void 0, void 0, function* () {
const { data: basicLinks } = yield this.api.linksList(query, this.contextOptions);
let links = [];
for (let l of basicLinks) {
links.push(Object.assign({ url: this.linkUri + l.id }, l));
}
return links;
}),
/**
* Subscribes to links changes
* @returns an unsubscribe function
*/
subscribe: (query = {}, listener) => __awaiter(this, void 0, void 0, function* () {
if (listener.onInitial) {
const links = yield this.links.list(query);
for (const link of links) {
listener.onInitial(Object.assign({ url: this.linkUri + link.id }, link));
}
}
const newListener = {};
if (listener.onAdd) {
newListener.onAdd = (doc) => {
return listener.onAdd(Object.assign({ url: this.linkUri + doc.id }, doc));
};
}
if (listener.onDelete) {
newListener.onDelete = (doc) => {
return listener.onDelete(Object.assign({ url: this.linkUri + doc.id }, doc));
};
}
if (listener.onChange) {
newListener.onChange = (oldDoc, newDoc) => {
return listener.onChange(Object.assign({ url: this.linkUri + oldDoc.id }, oldDoc), Object.assign({ url: this.linkUri + newDoc.id }, newDoc));
};
}
return this.api.linksSubscribe(query, this.contextOptions, newListener);
}),
/**
* Deletes a link
*/
delete: (linkId) => __awaiter(this, void 0, void 0, function* () {
return this.api.linksDelete(linkId);
}),
};
/**
* Granted Permissions
*/
this.granted = {
list: (query = {}) => __awaiter(this, void 0, void 0, function* () {
const res = yield this.api.grantedPermissionsList(query, this.contextOptions);
return res.data;
}),
subscribe: (query = {}, listener) => __awaiter(this, void 0, void 0, function* () {
if (listener.onInitial) {
const permissions = yield this.granted.list(query);
for (const permission of permissions) {
listener.onInitial(permission);
}
}
return this.api.grantedPermissionsSubscribe(query, this.contextOptions, listener);
}),
delete: (grantedPermissionId) => __awaiter(this, void 0, void 0, function* () {
return this.api.grantedPermissionsDelete(grantedPermissionId);
}),
};
/**
* Groups
* @alpha
*/
this.groups = {
/**
* Get group by Id
* @returns group for a given ID
*/
get: (groupId) => __awaiter(this, void 0, void 0, function* () {
const res = yield this.api.groupsGet(groupId, this.contextOptions);
return res.data;
}),
/**
* Lists groups
* @returns a list of groups user is part of (in current app)
*/
list: () => __awaiter(this, void 0, void 0, function* () {
const res = yield this.api.groupsList(this.contextOptions);
return res.data;
}),
/**
* Creates a new group
* Groups require at least one member.
*/
create: (group) => __awaiter(this, void 0, void 0, function* () {
return this.api.groupsCreate(group, this.contextOptions);
}),
addMember: (groupId, userId) => __awaiter(this, void 0, void 0, function* () {
return this.api.groupsAddMember(groupId, userId, this.contextOptions);
}),
removeMember: (groupId, userId) => __awaiter(this, void 0, void 0, function* () {
return this.api.groupsRemoveMember(groupId, userId, this.contextOptions);
}),
delete: (groupId) => __awaiter(this, void 0, void 0, function* () {
return this.api.groupsDelete(groupId, this.contextOptions);
}),
};
this.api = api;
this.linkUri = (uri || bazaarUri) + linkPath;
this.contextOptions = contextOptions;
}
/**
* Creates permission for a collection query and a user.
* @param {NewPermission} permission "Specifies the permission to be created"
* @param {SharingNotification} notification "Specifies if/how the user is notified. Defaults to {createNotification: false, sendMessage: SendNotification.Never}"
*/
create(permission, notification = noSharingNotification) {
return __awaiter(this, void 0, void 0, function* () {
return this.api.permissionsCreate(permission, notification, this.contextOptions);
});
}
/**
* Lists permissions.
* @param options - If no optional params are set, all permissions for the user are returned.
* @returns All permissions are returned if no options are passed.
*/
list(query = {}) {
return __awaiter(this, void 0, void 0, function* () {
const res = yield this.api.permissionsList(query, this.contextOptions);
return res.data;
});
}
/**
* Deletes permission with a given ID
* @param permissionId - ID of the permission to delete
*/
delete(permissionId) {
return __awaiter(this, void 0, void 0, function* () {
return this.api.permissionsDelete(permissionId, this.contextOptions);
});
}
}
/**
* The class that encapsulates the social API
* @internal
*/
class SocialAPI {
constructor(api) {
/**
* Contacts
*/
this.contacts = {
/**
* Lists contacts
* @returns a list of contacts
*/
list: () => __awaiter(this, void 0, void 0, function* () {
const res = yield this.api.contactsList();
return res.data;
}),
/**
* Subscribes to contacts
* @returns an unsubscribe function
*/
subscribe: (listener) => __awaiter(this, void 0, void 0, function* () {
if (listener.onInitial) {
const contacts = yield this.contacts.list();
for (const contact of contacts) {