moeralib
Version:
Library to interact with Moera decentralized social network
331 lines (330 loc) • 14.2 kB
JavaScript
"use strict";
var __awaiter = (this && this.__awaiter) || function (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());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.resolve = exports.expand = exports.shorten = exports.parseNodeName = exports.MoeraNaming = exports.MoeraNamingConnectionError = exports.MoeraNamingApiError = exports.MoeraNamingError = exports.DEV_NAMING_SERVER = exports.MAIN_NAMING_SERVER = void 0;
const json_rpc_2_0_1 = require("json-rpc-2.0");
const schema_1 = require("../schema");
const validate_1 = require("./validate");
/**
* Main Moera naming server.
*/
exports.MAIN_NAMING_SERVER = "https://naming.moera.org/moera-naming";
/**
* Moera developers' naming server.
*/
exports.DEV_NAMING_SERVER = "https://naming-dev.moera.org/moera-naming";
/**
* Generic naming server error.
*/
class MoeraNamingError extends Error {
/**
* @param {string} method - API method name
* @param {string} message - error message
*/
constructor(method, message) {
super(method + ": Naming server error: " + message);
}
}
exports.MoeraNamingError = MoeraNamingError;
/**
* Naming server returned an error response.
*/
class MoeraNamingApiError extends MoeraNamingError {
/**
* @param {string} method - API method name
* @param {ErrorResult} result - server response
*/
constructor(method, result) {
super(method, result.error.message);
this.errorCode = result.error.code;
}
}
exports.MoeraNamingApiError = MoeraNamingApiError;
/**
* Naming server connection error.
*/
class MoeraNamingConnectionError extends Error {
/**
* @param {string} message - error message
*/
constructor(message) {
super("Naming server connection error: " + message);
}
}
exports.MoeraNamingConnectionError = MoeraNamingConnectionError;
/**
* Naming API interface.
*/
class MoeraNaming {
/**
* @param {string} server - the naming server URL
*/
constructor(server = exports.MAIN_NAMING_SERVER) {
this.rpcClient = new json_rpc_2_0_1.JSONRPCClient((request_1, _a) => __awaiter(this, [request_1, _a], void 0, function* (request, [method, schema]) {
let response;
try {
response = yield fetch(server, {
method: "POST",
headers: {
"accept": "application/json",
"content-type": "application/json"
},
body: JSON.stringify(request),
});
}
catch (e) {
return Promise.reject(new MoeraNamingConnectionError(String(e)));
}
let data;
try {
data = yield response.json();
}
catch (e) {
if (!response.ok) {
return Promise.reject(new MoeraNamingError(method, "Server returned error status"));
}
else {
return Promise.reject(new MoeraNamingError(method, "Server returned empty result"));
}
}
if ("error" in data) {
const { valid, errors } = (0, validate_1.validateSchema)("ErrorResult", data);
if (!valid) {
return Promise.reject(new MoeraNamingError(method, "Server returned invalid error response: " + (0, schema_1.formatSchemaErrors)(errors)));
}
return Promise.reject(new MoeraNamingApiError(method, data));
}
else if (!response.ok && request.id !== undefined) {
return Promise.reject(new MoeraNamingError(method, "Server returned invalid error response: " + JSON.stringify(data)));
}
if (schema == "boolean") {
const { valid, errors } = (0, validate_1.validateSchema)("BooleanResult", data);
if (!valid) {
return Promise.reject(new MoeraNamingError(method, "Server returned invalid boolean response: " + (0, schema_1.formatSchemaErrors)(errors)));
}
this.rpcClient.receive(data);
return;
}
if (schema == "string") {
const { valid, errors } = (0, validate_1.validateSchema)("StringResult", data);
if (!valid) {
return Promise.reject(new MoeraNamingError(method, "Server returned invalid string response: " + (0, schema_1.formatSchemaErrors)(errors)));
}
this.rpcClient.receive(data);
return;
}
{
const { valid, errors } = (0, validate_1.validateSchema)("ObjectResult", data);
if (!valid) {
return Promise.reject(new MoeraNamingError(method, "Server returned invalid object response: " + (0, schema_1.formatSchemaErrors)(errors)));
}
}
const result = data.result;
if (result == null) {
this.rpcClient.receive(data);
return;
}
const { valid, errors } = (0, validate_1.validateSchema)(schema, result);
if (!valid) {
return Promise.reject(new MoeraNamingError(method, "Server returned invalid object: " + (0, schema_1.formatSchemaErrors)(errors)));
}
this.rpcClient.receive(data);
}));
}
/**
* Register or update the name. See Architecture Overview for the `detailed description
* {@link https://moera.org/overview/naming.html} of the algorithm.
*
* @param {string} name - the name to be registered/updated
* @param {number} generation - the name generation to be registered/updated
* @param {string | null} updatingKey - the public key for verifying signatures of further updates of the name. May
* be ``null`` if the current generation of the name is updated – the current key is preserved in this case.
* @param {string | null} nodeUri - URI of the REST API endpoint of the node to which the name is assigned. May be
* ``null`` - the current URI is preserved in this case.
* @param {string | null} signingKey - the public key of the name owner. May be ``null`` – the current key is
* preserved in this case.
* @param {number | null} validFrom - the moment in time the owner's key is valid from. May be ``null`` if
* ``signingKey`` is also ``null``.
* @param {string | null} previousDigest - the unique identifier as reported by a naming server of the current state
* of the name. Used to detect the situations when the name was changed by someone else between sending
* the request and processing it. May be ``null`` if the name was never registered before.
* @param {string | null} signature - the signature, if required, ``null`` otherwise
* @return {Promise<string>} identifier of the operation that was created
*/
put(name_1, generation_1) {
return __awaiter(this, arguments, void 0, function* (name, generation, updatingKey = null, nodeUri = null, signingKey = null, validFrom = null, previousDigest = null, signature = null) {
return yield this.rpcClient.request("put", { name, generation, updatingKey, nodeUri, signingKey, validFrom, previousDigest, signature }, ["put", "string"]);
});
}
/**
* Get the current status of the operation.
*
* @param {string} operationId
* @return {Promise<OperationStatusInfo |null>} the operation status or ``null``, if the operation ID is unknown
*/
getStatus(operationId) {
return __awaiter(this, void 0, void 0, function* () {
return yield this.rpcClient.request("getStatus", { operationId }, ["getStatus", "OperationStatusInfo"]);
});
}
/**
* Get current information about the given generation of the name.
*
* @param {string} name
* @param {number} generation
* @return {Promise<RegisteredNameInfo>} the information or ``null``, if the name/generation is not found
*/
getCurrent(name, generation) {
return __awaiter(this, void 0, void 0, function* () {
return yield this.rpcClient.request("getCurrent", { name, generation }, ["getCurrent", "RegisteredNameInfo"]);
});
}
/**
* Get past information about the given generation of the name.
*
* @param {string} name
* @param {number} generation
* @param {number} at - the moment in time the information is related to
* @return {Promise<RegisteredNameInfo | null>} the information or ``null``, if the name/generation did not exist at
* the given moment
*/
getPast(name, generation, at) {
return __awaiter(this, void 0, void 0, function* () {
return yield this.rpcClient.request("getPast", { name, generation, at }, ["getPast", "RegisteredNameInfo"]);
});
}
/**
* Check if the given name is available for registration.
*
* @param {string} name
* @param {number} generation
* @return {Promise<boolean>} ``true``, if the name is free, ``false`` otherwise
*/
isFree(name, generation) {
return __awaiter(this, void 0, void 0, function* () {
return yield this.rpcClient.request("isFree", { name, generation }, ["isFree", "boolean"]);
});
}
/**
* Find a name that is close to the given name.
*
* @param {string} name
* @return {Promise<RegisteredNameInfo | null>} information about the name or ``null``, if no name found that is
* close enough
*/
getSimilar(name) {
return __awaiter(this, void 0, void 0, function* () {
return yield this.rpcClient.request("getSimilar", { name }, ["getSimilar", "RegisteredNameInfo"]);
});
}
/**
* Get the whole history of signing keys for the given name.
*
* @param {string} name
* @param {number} generation
* @return {Promise<SigningKeyInfo[]>} the keys
*/
getAllKeys(name, generation) {
return __awaiter(this, void 0, void 0, function* () {
return yield this.rpcClient.request("getAllKeys", { name, generation }, ["getAllKeys", "SigningKeyInfoArray"]);
});
}
/**
* Get the list of all registered names at the given moment. The list is returned in pages, one per call.
*
* @param {number} at - the moment in time the information is related to
* @param {number} page - number of the page to be returned (starting from 0)
* @param {number} size - size of the page
* @return {Promise<RegisteredNameInfo[]>}
*/
getAll(at, page, size) {
return __awaiter(this, void 0, void 0, function* () {
return yield this.rpcClient.request("getAll", { at, page, size }, ["getAll", "RegisteredNameInfoArray"]);
});
}
/**
* Get the list of all names registered after the given moment. The list is returned in pages, one per call.
*
* @param {number} at - the moment in time the information is related to
* @param {number} page - number of the page to be returned (starting from 0)
* @param {number} size - size of the page
* @return {Promise<RegisteredNameInfo[]>}
*/
getAllNewer(at, page, size) {
return __awaiter(this, void 0, void 0, function* () {
return yield this.rpcClient.request("getAllNewer", { at, page, size }, ["getAllNewer", "RegisteredNameInfoArray"]);
});
}
}
exports.MoeraNaming = MoeraNaming;
/**
* Parse a node name and return its name and generation parts.
*
* If the node name does not include a generation, generation 0 is returned. If name syntax is invalid, ``Error``
* is thrown.
*
* @param {string} nodeName - the node name to be parsed
* @return {[string, number]} [name, generation]
*/
function parseNodeName(nodeName) {
let name = nodeName;
let generation = 0;
const pos = nodeName.lastIndexOf('_');
if (pos >= 0) {
const gen = nodeName.substring(pos + 1);
name = nodeName.substring(0, pos);
try {
generation = parseInt(gen, 10);
}
catch (error) {
throw new Error(`invalid generation: "${gen}"`);
}
}
return [name, generation];
}
exports.parseNodeName = parseNodeName;
function shorten(nodeName) {
if (nodeName === null) {
return null;
}
const [name, gen] = parseNodeName(nodeName);
if (gen === 0) {
return name;
}
else {
return nodeName;
}
}
exports.shorten = shorten;
function expand(nodeName) {
if (nodeName === null) {
return null;
}
const [name, gen] = parseNodeName(nodeName);
return `${name}_${gen}`;
}
exports.expand = expand;
/**
* Shortcut function to resolve a node name and get the node URI.
*
* @param name {string} - the node name
* @param namingServer {string} - a naming server to be used
* @return {Promise<string | null>} the node URI, or ``null`` if the name does not exist
*/
function resolve(name_1) {
return __awaiter(this, arguments, void 0, function* (name, namingServer = exports.MAIN_NAMING_SERVER) {
var _a, _b;
const [parsedName, gen] = parseNodeName(name);
const naming = new MoeraNaming(namingServer);
return (_b = (_a = (yield naming.getCurrent(parsedName, gen))) === null || _a === void 0 ? void 0 : _a.nodeUri) !== null && _b !== void 0 ? _b : null;
});
}
exports.resolve = resolve;