const axios = require("axios"); /** * @typedef {Object} JLINCConfig * @property {boolean} [debug] * @property {string} [systemPrefix] * @property {string} [defaultAgreementId] * @property {string|null} [domain] * @property {string|null} [dataStoreApiUrl] * @property {string|null} [dataStoreApiKey] * @property {string|null} [archiveApiUrl] * @property {string|null} [archiveApiKey] */ /** * @typedef {Object} JLINCProduceEventData * @property {string} [to] * @property {string} [from] * @property {string|null} [agreementId] * @property {any} [payload] * @property {any} [auth] */ /** * @typedef {Object} JLINCProcessEventData * @property {string} [to] * @property {string} [eventId] * @property {any} [auth] */ /** * @typedef {Object} JLINCProduceAgreementData * @property {Array} [requiredSigners] * @property {string} [signer] * @property {string} [uri] * @property {Array} [purposes] * @property {Array} [caveats] * @property {Array} [validRoles] * @property {string} [role] * @property {any} [auth] */ /** * @typedef {Object} JLINCProcessAgreementData * @property {string} [agreementId] * @property {string} [signer] * @property {string} [role] * @property {any} [auth] */ /** * @param {JLINCConfig} config - The configuration object for the library */ let config = { debug: false, systemPrefix: 'my-system', defaultAgreementId: '00000000-0000-0000-0000-000000000000', domain: null, dataStoreApiUrl: null, dataStoreApiKey: null, archiveApiUrl: null, archiveApiKey: null, } const entities = new Set(); /** * @param {string} input * @returns {string} */ function sanitize(input) { return input.replace(/[^a-zA-Z0-9._-]/g, '_'); } /** * @param {string} dir * @param {any} payload * @returns {Promise} */ async function postToApi(dir, payload) { const response = await axios.post( `${config.dataStoreApiUrl}/api/v1/data/${dir}`, payload, { headers: { 'Authorization': `Bearer ${config.dataStoreApiKey}`, } } ); return response.data; } /** * @param {string} entityShortName * @returns {Promise} */ async function setEntity(name) { const entityShortName = `${config.systemPrefix}-${sanitize(name)}@${config.domain}`; if (!entities.has(entityShortName)) { try { await postToApi(`entity/get`, { shortName: entityShortName }); } catch (e) { await postToApi(`entity/create`, { shortName: entityShortName }); } entities.add(entityShortName); } return entityShortName } /** * @param {JLINCConfig} data - The configuration object for the application. */ function jlincInit(data) { config = { ...config, ...data, } } async function getDomain() { if (!config.domain) { const domains = await postToApi(`entity/domains/get`, {}); config.domain = domains[0]; } } /** * @param {JLINCProduceEventData} data - The data required for producing an event. */ async function jlincProduceEvent(data) { let res = null; try { await getDomain(); const senderShortName = await setEntity(data.from) const recipientShortName = await setEntity(data.to) const agreementId = data.agreementId ?? config.defaultAgreementId; const postData = { type: 'data', senderShortName, recipientShortName, agreementId, data: data.payload, archive: { url: config.archiveApiUrl, key: config.archiveApiKey, } }; if (data.auth) postData.auth = data.auth; res = await postToApi(`event/produce`, postData); if (config.debug) { console.log(JSON.stringify({ res }, null, 2)); } } catch (error) { console.error("[JLINC] Failed to produce event:", error.message); if (config.debug) console.error(error) } return res; } /** * @param {JLINCProcessEventData} data - The data required for processing an event. */ async function jlincProcessEvent(data) { let res = null; try { await getDomain(); const shortName = await setEntity(data.to) const postData = { type: 'data', shortName, eventId: data.eventId, archive: { url: config.archiveApiUrl, key: config.archiveApiKey, } }; if (data.auth) postData.auth = data.auth; res = await postToApi(`event/process`, postData); if (config.debug) { console.log(JSON.stringify({ res }, null, 2)); } } catch (error) { console.error("[JLINC] Failed to process event:", error.message); if (config.debug) console.error(error) } return res; } /** * @param {JLINCProduceAgreementData} data - The data required for producing an agreement. */ async function jlincProduceAgreement(data) { let res = null; try { await getDomain(); const shortNames = []; for (const id of data.requiredSigners) { shortNames.push(await setEntity(id)) } const shortName = await setEntity(data.signer) const postData = { data: { uri: data.uri, purposes: data.purposes, caveats: data.caveats, shortNames, validRoles: data.validRoles, }, shortName, role: data.role, archive: { url: config.archiveApiUrl, key: config.archiveApiKey, } }; if (data.auth) postData.auth = data.auth; res = await postToApi(`agreement/produce`, postData); if (config.debug) { console.log(JSON.stringify({ res }, null, 2)); } } catch (error) { console.error("[JLINC] Failed to produce agreement:", error.message); if (config.debug) console.error(error) } return res; } /** * @param {JLINCProcessAgreementData} data - The data required for processing an agreement. */ async function jlincProcessAgreement(data) { let res = null; try { await getDomain(); const shortName = await setEntity(data.signer) const postData = { agreementId: data.agreementId, shortName, role: data.role, archive: { url: config.archiveApiUrl, key: config.archiveApiKey, } }; if (data.auth) postData.auth = data.auth; res = await postToApi(`agreement/process`, postData); if (config.debug) { console.log(JSON.stringify({ res }, null, 2)); } } catch (error) { console.error("[JLINC] Failed to process agreement:", error.message); if (config.debug) console.error(error) } return res; } module.exports = { jlincInit, jlincProduceEvent, jlincProcessEvent, jlincProduceAgreement, jlincProcessAgreement, }