edgeconnect-cryptpad-deploy.../www/common/store-interface.js
Richard Robert Reitz addc3725ea
Some checks failed
build / build (push) Has been cancelled
first commit
2025-08-28 15:13:14 +02:00

247 lines
8.2 KiB
JavaScript

// SPDX-FileCopyrightText: 2025 XWiki CryptPad Team <contact@cryptpad.org> and contributors
//
// SPDX-License-Identifier: AGPL-3.0-or-later
(() => {
const factory = function (Channel, NodeWS) {
let USE_MIN = true;
if (typeof(localStorage) !== "undefined" &&
localStorage.CryptPad_noMin === "1") { USE_MIN = false; }
let path = '/common/worker.bundle.js?';
if (USE_MIN) { path = '/common/worker.bundle.min.js?'; }
const commands = {
account: {
load: 'CONNECT'
},
drive: {
migrateAnon: 'MIGRATE_ANON_DRIVE'
},
pad: {
join: 'JOIN_PAD',
leave: 'LEAVE_PAD',
sendMsg: 'SEND_PAD_MSG',
destroy: 'REMOVE_OWNED_CHANNEL',
clear: 'CLEAR_OWNED_CHANNEL',
setMetadata: 'SET_PAD_METADATA',
getMetadata: 'GET_PAD_METADATA'
}
};
const makeApi = (postMsg, msgEv, cb) => {
Channel.create(msgEv, postMsg, chan => {
const postMessage = (cmd, data, cb, opts) => {
cb ||= () => {};
chan.query(cmd, data, (err, res) => {
if (err) { return void cb ({error: err}); }
cb(res);
}, opts);
};
const api = {};
const make = (base, cmd) => {
Object.keys(cmd).forEach(k => {
const v = cmd[k];
if (!v) { return; }
if (typeof(v) === "string") {
base[k] = (data, cb, opts) => {
postMessage(v, data, cb, opts);
};
return;
}
base[k] = {};
make(base[k], v);
});
};
make(api, commands);
cb(api);
});
};
let create = function (cfg = {}) {
let { noWorker, noSharedWorker, AppConfig,
ApiConfig, Messages, Broadcast } = cfg;
let urlArgs = ApiConfig?.requireConf?.urlArgs;
const mkEvent = function () {
var handlers = [];
return {
reg: function (cb) {
handlers.push(cb);
},
unreg: function (cb) {
if (handlers.indexOf(cb) === -1) { return; }
handlers.splice(handlers.indexOf(cb), 1);
},
fire: function () {
var args = Array.prototype.slice.call(arguments);
handlers.forEach(function (h) {
h.apply(null, args);
});
}
};
};
let called = false;
let msgEv = mkEvent();
let todo = (resolve/*, reject*/) => {
if (called) { return; }
called = true;
let worker, postMsg;
if (!noWorker && !noSharedWorker && typeof(SharedWorker) !== "undefined") {
worker = new SharedWorker(path + urlArgs);
worker.onerror = function (e) {
console.error(e.message);
};
worker.port.onmessage = function (ev) {
if (ev.data === "SW_READY") {
return;
}
msgEv.fire(ev);
};
postMsg = function (data) {
worker.port.postMessage(data);
};
postMsg(JSON.parse(JSON.stringify({
type: 'INIT',
cfg: {
AppConfig,
ApiConfig,
Messages,
Broadcast
}
})));
window.addEventListener('unload', function () {
postMsg('CLOSE');
});
return void resolve({postMsg, msgEv});
}
if (!noWorker && typeof(Worker) !== "undefined") {
worker = new Worker(path + urlArgs);
worker.onerror = function (e) {
console.error(e.message);
};
worker.onmessage = function (ev) {
msgEv.fire(ev);
};
postMsg = function (data) {
worker.postMessage(data);
};
postMsg(JSON.parse(JSON.stringify({
type: 'INIT',
cfg: {
AppConfig,
ApiConfig,
Messages,
Broadcast
}
})));
return void resolve({postMsg, msgEv});
}
// Use the async store in the main thread if workers
// aren't available
//if (typeof(require) === "undefined") { return; }
require([path], function (Store) {
let store = Store?.store;
if (!store) { return void console.error("No store"); }
store.onMessage(function (data) {
if (data === "STORE_READY") { return; }
msgEv.fire({data: data, origin: ''});
});
postMsg = function (d) {
setTimeout(function () {
store.query(d);
});
};
store.init({
AppConfig,
ApiConfig,
Messages,
Broadcast
});
resolve({postMsg, msgEv});
});
};
let todoNode = (resolve, reject) => {
const Store = require('./worker.bundle');
let store = Store?.store;
if (!store) {
reject('NOSTORE');
return void console.error("No store");
}
store.onMessage(function (data) {
if (data === "STORE_READY") { return; }
msgEv.fire({data: data, origin: ''});
});
let postMsg = function (d) {
setTimeout(function () {
store.query(d);
});
};
store.init({
AppConfig,
ApiConfig,
Messages,
Broadcast
});
globalThis.WebSocket = NodeWS.WebSocket;
makeApi(postMsg, msgEv, api => {
resolve({api});
});
};
return new Promise((resolve, reject) => {
if (typeof (module) !== "undefined" && typeof(module.exports) !== "undefined") {
return void todoNode(resolve, reject);
}
if (typeof(SharedWorker) !== "undefined") {
try {
new SharedWorker('');
} catch (e) {
noSharedWorker = true;
console.log('Disabling SharedWorker because of privacy settings.');
}
}
if (typeof(Worker) !== "undefined") {
try {
let worker = new Worker('/common/testworker.js?' + urlArgs);
worker.onerror = function (errEv) {
errEv.preventDefault();
errEv.stopPropagation();
noWorker = true;
worker.terminate();
todo(resolve, reject);
};
worker.onmessage = function (ev) {
if (ev.data === "OK") {
worker.terminate();
todo(resolve, reject);
}
};
} catch (e) {
noWorker = true;
todo(resolve, reject);
}
}
});
};
return create;
};
if (typeof(module) !== 'undefined' && module.exports) {
module.exports = factory(
require('../../src/common/events-channel'),
require('ws')
);
} else if ((typeof(define) !== 'undefined' && define !== null) && (define.amd !== null)) {
define(['/common/events-channel.js'], factory);
}
})();