UNPKG

wikitree-js

Version:

Javascript library for the WikiTree API

246 lines 9.56 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.login = exports.clientLogin = exports.navigateToLoginPage = exports.getLoggedInUserName = exports.getRelatives = exports.getDescendants = exports.getAncestors = exports.getPerson = exports.wikiTreeGet = exports.fetchWikiTree = exports.WikiTreeError = void 0; const js_cookie_1 = __importDefault(require("js-cookie")); const cross_fetch_1 = require("cross-fetch"); const form_data_1 = __importDefault(require("form-data")); /** Default API URL if not explicitly specified. */ const WIKITREE_API_URL = 'https://api.wikitree.com/api.php'; /** Default appId sent if not explicitly specified. */ const WIKITREE_JS_APPID = 'wikitree-js'; /** * Cookie where the logged in user name is stored. This cookie is shared * between apps hosted on apps.wikitree.com. It is not used to authenticate * requests but only to allow displaying what user is logged in. * Authentication cookies are stored when the user logs in via * https://api.wikitree.com/api.php */ const USER_NAME_COOKIE = 'wikidb_wtb_UserName'; /** Wraps an error returned from the WikiTree API. */ class WikiTreeError extends Error { constructor(message) { super(message); this.name = 'WikiTreeError'; } } exports.WikiTreeError = WikiTreeError; /** Sends a request to the WikiTree API. Returns the raw response. */ async function fetchWikiTree(request, options) { const requestData = new form_data_1.default(); requestData.append('format', 'json'); requestData.append('appId', options?.appId ?? WIKITREE_JS_APPID); for (const key in request) { if (request[key]) { requestData.append(key, request[key]); } } const apiUrl = options?.apiUrl || WIKITREE_API_URL; const postRequest = { method: 'POST', redirect: 'manual', body: requestData, credentials: isWikiTreeUrl(apiUrl) ? 'include' : undefined, }; if (options?.auth) { postRequest.headers = { Cookie: options.auth.cookies }; } return await (0, cross_fetch_1.fetch)(apiUrl, postRequest); } exports.fetchWikiTree = fetchWikiTree; /** Sends a request to the WikiTree API. Returns the parsed response JSON. */ async function wikiTreeGet(request, options) { const response = await fetchWikiTree(request, options); const result = await response.json(); if (result[0]?.status) { throw new WikiTreeError(result[0].status); } return result; } exports.wikiTreeGet = wikiTreeGet; /** * Retrieves a single person record from WikiTree. * * See also: https://github.com/wikitree/wikitree-api/blob/main/getPerson.md */ async function getPerson(key, args, options) { const request = { action: 'getPerson', key, bioFormat: args?.bioFormat, fields: args?.fields instanceof Array ? args.fields.join(',') : args?.fields, resolveRedirect: args?.resolveRedirect ? '1' : undefined, }; const response = await wikiTreeGet(request, options); return response[0].person; } exports.getPerson = getPerson; /** * Retrieves ancestors from WikiTree for the given person ID. * * See also: https://github.com/wikitree/wikitree-api/blob/main/getAncestors.md */ async function getAncestors(key, args, options) { const request = { action: 'getAncestors', key, depth: args?.depth, bioFormat: args?.bioFormat, fields: args?.fields instanceof Array ? args.fields.join(',') : args?.fields, resolveRedirect: args?.resolveRedirect ? '1' : undefined, }; const response = await wikiTreeGet(request, options); return response[0].ancestors; } exports.getAncestors = getAncestors; /** * Retrieves descendants from WikiTree for the given person ID. * * See also: https://github.com/wikitree/wikitree-api/blob/main/getDescendants.md */ async function getDescendants(key, args, options) { const request = { action: 'getDescendants', key, depth: args?.depth, bioFormat: args?.bioFormat, fields: args?.fields instanceof Array ? args.fields.join(',') : args?.fields, resolveRedirect: args?.resolveRedirect ? '1' : undefined, }; const response = await wikiTreeGet(request, options); return response[0].descendants; } exports.getDescendants = getDescendants; /** * Retrieves relatives from WikiTree for the given array of person IDs. * If a key does not exist or is inaccessible, it is omitted in the result. * * See also: https://github.com/wikitree/wikitree-api/blob/main/getRelatives.md */ async function getRelatives(keys, args, options) { if (args?.bioFormat && !args?.fields?.includes('Bio')) { console.warn('Setting bioFormat has no effect if the "Bio" field is not requested' + ' explicitly'); } const request = { action: 'getRelatives', keys: keys.join(','), getParents: args?.getParents ? 'true' : undefined, getChildren: args?.getChildren ? 'true' : undefined, getSpouses: args?.getSpouses ? 'true' : undefined, getSiblings: args?.getSiblings ? 'true' : undefined, bioFormat: args?.bioFormat, fields: args?.fields instanceof Array ? args.fields.join(',') : args?.fields, }; const response = await wikiTreeGet(request, options); if (response[0].items === null) { return []; } return response[0].items.map((item) => item.person); } exports.getRelatives = getRelatives; /** * Returns the logged in user name or undefined if not logged in. * * In the browser, call this function without arguments. * This is not an authoritative answer. The result of this function relies on * the cookies set on the apps.wikitree.com domain under which this application * is hosted. The authoritative source of login information is in cookies set on * the api.wikitree.com domain. * * In Node.js, call this function with the auth parameter. This is an * authoritative answer because the login flow is under control of the * wikitree-js library. */ function getLoggedInUserName(auth) { if (!auth) { // Return user name stored in browser cookies. return js_cookie_1.default.get(USER_NAME_COOKIE); } // Extract user name from cookies in WikiTreeAuthentication. const regex = new RegExp(`${USER_NAME_COOKIE}=(.*?);`); const match = auth.cookies.match(regex); return match ? match[1] : undefined; } exports.getLoggedInUserName = getLoggedInUserName; // === Browser-specific code === /** * Navigates to WikiTree login screen at https://api.wikitree.com/api.php with * the specified return URL. */ function navigateToLoginPage(returnUrl) { if (!isWikiTreeUrl(returnUrl)) { console.warn('Return URLs outside of the wikitree.com domain will not work with the' + " WikiTree login flow because of WikiTree API's CORS settings."); } const form = document.createElement('form'); form.setAttribute('action', WIKITREE_API_URL); form.setAttribute('method', 'POST'); form.setAttribute('hidden', 'true'); const actionInput = document.createElement('input'); actionInput.setAttribute('name', 'action'); actionInput.setAttribute('type', 'hidden'); actionInput.setAttribute('value', 'clientLogin'); const returnUrlInput = document.createElement('input'); returnUrlInput.setAttribute('name', 'returnURL'); returnUrlInput.setAttribute('type', 'hidden'); returnUrlInput.setAttribute('value', returnUrl); form.appendChild(actionInput); form.appendChild(returnUrlInput); document.body.appendChild(form); form.submit(); } exports.navigateToLoginPage = navigateToLoginPage; async function clientLogin(authcode, options) { const response = await wikiTreeGet({ action: 'clientLogin', authcode, }, options); const result = response.clientLogin; if (result.result === 'Success') { js_cookie_1.default.set(USER_NAME_COOKIE, result.username); } return result; } exports.clientLogin = clientLogin; // === Node.js-specific code === /** * Logs in to WikiTree returning authentication credentials. * This function will not work in the browser because it handles cookies directly. * Throws an exception if login fails. */ async function login(email, password) { const authcode = await getAuthcode(email, password); return { cookies: await getAuthCookies(authcode) }; } exports.login = login; async function getAuthcode(email, password, options) { const response = await fetchWikiTree({ action: 'clientLogin', doLogin: 1, returnURL: 'https://x/', wpEmail: email, wpPassword: password, }, options); if (response.status !== 302) { throw new WikiTreeError('Invalid login credentials'); } return response.headers.get('location').replace('https://x/?authcode=', ''); } async function getAuthCookies(authcode, options) { const response = await fetchWikiTree({ action: 'clientLogin', authcode, }, options); const result = await response.json(); if (result.clientLogin?.result !== 'Success') { throw new WikiTreeError('Could not authorize authcode'); } return response.headers.get('set-cookie'); } function isWikiTreeUrl(url) { return url.match(/^https:\/\/[^/]*wikitree.com\/.*/); } //# sourceMappingURL=wikitree_api.js.map