@theweave/api
Version:
This package contains the interfaces and contracts that a Holochain app UI needs to implement in order to run as a Tool in a Weave Frame like [Moss](theweave.social#tryit).
183 lines • 8.95 kB
JavaScript
import { decodeHashFromBase64, encodeHashToBase64, } from '@holochain/client';
import { postMessage } from './utils.js';
import { decode, encode } from '@msgpack/msgpack';
import { fromUint8Array, toUint8Array } from 'js-base64';
/**
* The null hash is used in case a WAL is to address a DNA only, not specific
* DHT content. It starts with the prefix of an EntryHash, followed by zeroes
*/
export const NULL_HASH = new Uint8Array([132, 33, 36, 219, 175, ...new Uint8Array(34)]);
/**
*
* @returns bool: Returns whether this function is being called in a Weave context.
*/
export const isWeaveContext = () => window.location.protocol === 'applet:' || !!window.__WEAVE_API__;
/**
*
* @param appletHash Hash of the applet to generate the link for
* @param webPrefix Whether to make the link work via web browsers. Default is true.
* @returns
*/
export const weaveUrlFromAppletHash = (appletHash, webPrefix = false) => {
let url = '';
if (webPrefix) {
url = 'https://theweave.social/wal?';
}
url =
url +
`weave-${window.__WEAVE_PROTOCOL_VERSION__ || '0.12'}://applet/${encodeHashToBase64(appletHash)}`;
return url;
};
export function weaveUrlFromWal(wal, webPrefix = false) {
let url = '';
if (webPrefix) {
url = 'https://theweave.social/wal?';
}
url =
url +
`weave-${window.__WEAVE_PROTOCOL_VERSION__ || '0.12'}://hrl/${encodeHashToBase64(wal.hrl[0])}/${encodeHashToBase64(wal.hrl[1])}${wal.context ? `?context=${encodeContext(wal.context)}` : ''}`;
return url;
}
export function weaveUrlToLocation(url) {
if (!url.startsWith(`weave-${window.__WEAVE_PROTOCOL_VERSION__ || '0.12'}://`)) {
throw new Error(`Got invalid Weave url: ${url}`);
}
const split = url.split('://');
// ['we', 'hrl/uhC0k-GO_J2D51Ibh2jKjVJHAHPadV7gndBwrqAmDxRW3b…kzMgM3yU2RkmaCoiY8IVcUQx_TLOjJe8SxJVy7iIhoVIvlZrD']
const split2 = split[1].split('/');
// ['hrl', 'uhC0k-GO_J2D51Ibh2jKjVJHAHPadV7gndBwrqAmDxRW3buMpVRa9', 'uhCkkzMgM3yU2RkmaCoiY8IVcUQx_TLOjJe8SxJVy7iIhoVIvlZrD']
if (split2[0] === 'hrl') {
const contextSplit = split2[2].split('?context=');
return {
type: 'asset',
wal: {
hrl: [decodeHashFromBase64(split2[1]), decodeHashFromBase64(contextSplit[0])],
context: contextSplit[1] ? decodeContext(contextSplit[1]) : undefined,
},
};
}
else if (split2[0] === 'group') {
throw new Error('Needs to be implemented in Moss version 0.13.x by changing group to invitation');
}
else if (split2[0] === 'applet') {
return {
type: 'applet',
appletHash: decodeHashFromBase64(split2[1]),
};
}
throw new Error(`Got We url of unknown format: ${url}`);
}
export function weaveUrlToWAL(url) {
const weaveLocation = weaveUrlToLocation(url);
if (weaveLocation.type !== 'asset') {
throw new Error('Passed URL is not a valid asset locator.');
}
return weaveLocation.wal;
}
export function stringifyHrl(hrl) {
return `hrl://${encodeHashToBase64(hrl[0])}/${encodeHashToBase64(hrl[1])}`;
}
export function stringifyWal(wal) {
// If the context field is missing, it will be encoded differently than if it's undefined
// or null so the field needs to be explicitly added here to make sure it leads to a
// consistent result in both cases
wal = {
hrl: wal.hrl,
context: 'context' in wal ? wal.context : null,
};
return fromUint8Array(encode(wal));
}
export function deStringifyWal(walStringified) {
return decode(toUint8Array(walStringified));
}
export function encodeContext(context) {
return fromUint8Array(encode(context), true);
}
export function decodeContext(contextStringified) {
return decode(toUint8Array(contextStringified));
}
export const initializeHotReload = async () => {
try {
const appletIframeScript = await postMessage({
type: 'get-applet-iframe-script',
});
eval(appletIframeScript);
}
catch (e) {
throw new Error(`Failed to initialize applet hot-reloading: ${e}.\n\nIf the applet is running in production mode (.webhapp) 'initializeHotReload()' needs to be removed.`);
}
};
export class AppletServices {
constructor() {
(this.creatables = {}),
(this.blockTypes = {}),
(this.search = async (_appletClient, _appletHash, _weaveServices, _searchFilter) => []),
(this.getAssetInfo = async (_appletClient, _wal, _recordInfo) => undefined);
}
}
export class WeaveClient {
get renderInfo() {
return window.__WEAVE_RENDER_INFO__;
}
constructor() {
this.mossVersion = () => window.__MOSS_VERSION__;
this.onPeerStatusUpdate = (callback) => window.__WEAVE_API__.onPeerStatusUpdate(callback);
this.onBeforeUnload = (callback) => window.__WEAVE_API__.onBeforeUnload(callback);
this.openAppletMain = async (appletHash) => window.__WEAVE_API__.openAppletMain(appletHash);
this.openAppletBlock = async (appletHash, block, context) => window.__WEAVE_API__.openAppletBlock(appletHash, block, context);
this.openCrossGroupMain = (appletBundleId) => window.__WEAVE_API__.openCrossGroupMain(appletBundleId);
this.openCrossGroupBlock = (appletBundleId, block, context) => window.__WEAVE_API__.openCrossGroupBlock(appletBundleId, block, context);
this.openAsset = (wal, mode) => window.__WEAVE_API__.openAsset(wal, mode);
this.assets = {
dragAsset: (wal) => window.__WEAVE_API__.assets.dragAsset(wal),
assetInfo: (wal) => window.__WEAVE_API__.assets.assetInfo(wal),
assetToPocket: (wal) => window.__WEAVE_API__.assets.assetToPocket(wal),
userSelectAsset: (from) => window.__WEAVE_API__.assets.userSelectAsset(from),
userSelectAssetRelationTag: () => window.__WEAVE_API__.assets.userSelectAssetRelationTag(),
addTagsToAsset: (wal, tags) => window.__WEAVE_API__.assets.addTagsToAsset(wal, tags),
removeTagsFromAsset: (wal, tags) => window.__WEAVE_API__.assets.removeTagsFromAsset(wal, tags),
addAssetRelation: (srcWal, dstWal, tags) => window.__WEAVE_API__.assets.addAssetRelation(srcWal, dstWal, tags),
removeAssetRelation: (relationHash) => window.__WEAVE_API__.assets.removeAssetRelation(relationHash),
addTagsToAssetRelation: (relationHash, tags) => window.__WEAVE_API__.assets.addTagsToAssetRelation(relationHash, tags),
removeTagsFromAssetRelation: (relationHash, tags) => window.__WEAVE_API__.assets.removeTagsFromAssetRelation(relationHash, tags),
getAllAssetRelationTags: (crossGroup) => window.__WEAVE_API__.assets.getAllAssetRelationTags(crossGroup),
assetStore: (wal) => window.__WEAVE_API__.assets.assetStore(wal),
};
this.groupProfile = (groupHash) => window.__WEAVE_API__.groupProfile(groupHash);
this.appletInfo = (appletHash) => window.__WEAVE_API__.appletInfo(appletHash);
this.notifyFrame = (notifications) => window.__WEAVE_API__.notifyFrame(notifications);
this.userSelectScreen = () => window.__WEAVE_API__.userSelectScreen();
this.requestClose = () => window.__WEAVE_API__.requestClose();
this.myGroupPermissionType = () => window.__WEAVE_API__.myGroupPermissionType();
this.appletParticipants = () => window.__WEAVE_API__.appletParticipants();
this.sendRemoteSignal = (payload) => window.__WEAVE_API__.sendRemoteSignal(payload);
this.onRemoteSignal = (callback) => window.__WEAVE_API__.onRemoteSignal(callback);
this.createCloneCell = (req, publicToGroupMembers) => window.__WEAVE_API__.createCloneCell(req, publicToGroupMembers);
this.enableCloneCell = (req) => window.__WEAVE_API__.enableCloneCell(req);
this.disableCloneCell = (req) => window.__WEAVE_API__.disableCloneCell(req);
}
static async connect(appletServices) {
if (window.__WEAVE_RENDER_INFO__) {
if (appletServices) {
window.__WEAVE_APPLET_SERVICES__ = appletServices;
}
window.dispatchEvent(new CustomEvent('weave-client-connected'));
return new WeaveClient();
}
else {
await new Promise((resolve, _reject) => {
const listener = () => {
window.removeEventListener('applet-iframe-ready', listener);
resolve(null);
};
window.addEventListener('applet-iframe-ready', listener);
});
if (appletServices) {
window.__WEAVE_APPLET_SERVICES__ = appletServices;
}
window.dispatchEvent(new CustomEvent('weave-client-connected'));
return new WeaveClient();
}
}
}
//# sourceMappingURL=api.js.map