Publish Vulpes Preview 15 sources for desktop, Android and Tundra

Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
This commit is contained in:
OverSu 2026-09-30 23:13:03 +02:00
commit b38dea0597
25901 changed files with 9982925 additions and 369 deletions

View file

@ -0,0 +1,24 @@
/**
* The way that the current caldav library works,
* we need to preconfigure some global state :/.
*/
define(function(require, exports, module) {
'use strict';
var Thread = require('./thread');
var debug = require('common/debug')('worker/initialize');
self.window = self;
module.exports = function() {
var thread = self.thread = new Thread(window);
thread.addRole('caldav');
window.console = new thread.console('worker');
require(['service/caldav'], (CaldavService) => {
// Lazily loaded so that we can prime environment first.
debug('Will create new caldav service...');
self.caldav = new CaldavService(thread.roles.caldav);
});
};
});

View file

@ -0,0 +1,267 @@
define(function(require, exports, module) {
'use strict';
var Responder = require('common/responder');
var debug = require('common/debug')('worker/manager');
const IDLE_CLEANUP_TIME = 5000;
/**
* Worker manager. Each worker/thread
* is assigned one or more roles.
*
* There may be one or more workers for
* each role and there is a contract
* that assumes all roles are stateless
* requests/streams are routed to workers
* and will be completed eventually
* but without order guarantees.
*/
function Manager() {
this._lastId = 0;
Responder.call(this);
this.roles = Object.create(null);
this.workers = [];
}
module.exports = Manager;
Manager.prototype = {
// So that we can mock out Worker API in unit tests...
Worker: Worker,
__proto__: Responder.prototype,
_onLog: debug,
_formatData: function(data) {
if (data[1] && data[1].stack && data[1].constructorName) {
var err = data[1];
var builtErr;
if (window[err.constructorName]) {
builtErr = Object.create(window[err.constructorName].prototype);
} else {
builtErr = Object.create(Error.prototype);
}
var key;
for (key in err) {
if (err.hasOwnProperty(key)) {
builtErr[key] = err[key];
}
}
data[1] = builtErr;
}
return data;
},
_onWorkerError: function(worker, err) {
if (/reference to undefined property/.test(err.message)) {
// This is a warning spewed out by javascript.options.strict,
// the worker actually didn't crash at all, so ignore it.
return;
}
if (worker.instance) {
worker.instance.terminate();
worker.instance = null;
}
var pending = worker.pending;
worker.pending = Object.create(null);
for (var id in pending) {
if (pending[id].stream) {
pending[id].stream.emit('error', err);
}
pending[id].callback(err);
}
},
_onWorkerMessage: function(worker, event) {
var data = this._formatData(event.data);
var type = data.shift();
var match = type.match(/^(\d+) (end|stream)$/);
if (type == 'log') {
this._onLog.apply(this, data);
} else if (match) {
var pending = worker.pending[match[1]];
if (pending) {
this._dispatchMessage(worker, pending, match[2], data);
} else {
throw new Error('Message arrived for unknown consumer: ' +
type + ' ' + JSON.stringify(data));
}
} else {
this.respond([type].concat(data));
}
},
_dispatchMessage: function(worker, pending, type, data) {
if (type == 'stream') {
pending.stream.respond(data);
} else { // 'end'
pending.callback.apply(null, data);
delete worker.pending[pending.id];
// Bail out if there are other pending requests.
if (Object.keys(worker.pending).length) {
return;
}
// If none are left, schedule cleanup
this._scheduleCleanup(worker);
}
},
_addPending: function(worker, pending) {
worker.pending[pending.id] = pending;
clearTimeout(worker.cleanup);
},
_scheduleCleanup: function(worker) {
clearTimeout(worker.cleanup);
worker.cleanup = setTimeout(function() {
// Ensure we don't have a race condition where someone just
// added a request but the timeout fired anyway.
if (Object.keys(worker.pending).length) {
return;
}
if (!worker.instance) {
return;
}
worker.instance.terminate();
worker.instance = null;
}, IDLE_CLEANUP_TIME);
},
/**
* Adds a worker to the manager.
* Worker is associated with one or
* more roles. Workers are assumed
* stateless.
*
*
* @param {String|Array} role one or more roles.
* @param {String} worker url.
*/
add: function(role, workerURL) {
debug('Will add', role, 'worker at', workerURL);
var worker = {
// Actual Worker instance, when active
instance: null,
// Handlers that are waiting for a response from this worker
pending: Object.create(null),
// Script URL
url: workerURL,
// Timeout set to disable the worker when it hasn't been used
// for a given period of time
cleanup: null
};
this.workers.push(worker);
[].concat(role).forEach(function(role) {
if (!(role in this.roles)) {
this.roles[role] = [worker];
} else {
this.roles[role].push(worker);
}
}, this);
},
_ensureActiveWorker: function(role) {
if (role in this.roles) {
var workers = this.roles[role];
var worker = workers[Math.floor(Math.random() * workers.length)];
if (worker.instance) {
return worker;
} else {
this._startWorker(worker);
return worker;
}
} else {
throw new Error('no worker with role "' + role + '" active');
}
},
_startWorker: function(worker) {
worker.instance = new this.Worker(
// ?time= is for cache busting in development...
// there have been cases where nightly would not
// clear the cache of the worker.
worker.url + '?time=' + Date.now()
);
worker.instance.onerror = this._onWorkerError.bind(this, worker);
worker.instance.onmessage = this._onWorkerMessage.bind(this, worker);
this._scheduleCleanup(worker);
},
request: function(role /*, args..., callback*/) {
var args = Array.prototype.slice.call(arguments, 1);
var callback = args.pop();
var worker = null;
try {
worker = this._ensureActiveWorker(role);
} catch (e) {
callback(e);
return;
}
var data = {
id: this._lastId++,
role: role,
payload: args
};
this._addPending(worker, {
id: data.id,
callback: callback
});
worker.instance.postMessage(['_dispatch', data]);
},
stream: function(role /*, args...*/) {
var args = Array.prototype.slice.call(arguments, 1);
var stream = new Responder();
var self = this;
var data = {
id: this._lastId++,
role: role,
payload: args,
type: 'stream'
};
stream.request = function(callback) {
var worker = null;
stream.request = function() {
throw new Error('stream request has been sent');
};
try {
worker = self._ensureActiveWorker(role);
} catch (e) {
callback(e);
return;
}
self._addPending(worker, {
id: data.id,
stream: stream,
callback: callback
});
worker.instance.postMessage(['_dispatch', data]);
};
return stream;
}
};
});

View file

@ -0,0 +1,121 @@
define(function(require, exports, module) {
'use strict';
var Responder = require('common/responder');
var debug = require('common/debug')('worker/thread');
function Thread(worker) {
Responder.call(this);
this.worker = worker;
this.roles = {};
this._initEvents();
}
module.exports = Thread;
Thread.prototype = {
__proto__: Responder.prototype,
send: function() {
this.worker.postMessage(Array.prototype.slice.call(arguments));
},
addRole: function(name) {
this.roles[name] = new Responder();
},
_remoteEmitter: function(id) {
var self = this;
return {
emit: function emitRemote() {
var args = Array.prototype.slice.call(arguments);
self.worker.postMessage([id + ' stream'].concat(args));
}
};
},
_initEvents: function() {
var self = this;
debug('Will listen for messages from the main thread...');
this.on('_dispatch', function(data) {
// data.id
// data.type
// data.role
// data.payload
var callback = self._requestCallback.bind(
self, data.id
);
if (data.role) {
if (data.role in self.roles) {
if (data.type && data.type === 'stream') {
self.roles[data.role].respond(
data.payload,
self._remoteEmitter(data.id),
callback
);
} else {
self.roles[data.role].respond(
data.payload, callback
);
}
} else {
// TODO: respond with error
debug('ERROR: ' + data.role + ' is not available.');
}
} else {
self.respond(data.payload, callback);
}
});
},
_wrapError: function(err) {
var errorObject = {};
errorObject.stack = err.stack || '';
errorObject.message = err.message || err.toString();
errorObject.type = err.type || 'Error';
errorObject.constructorName = err.constructor.name || 'Error';
if (err.name) {
errorObject.name = err.name;
}
if (err.code) {
errorObject.code = err.code;
}
return errorObject;
},
_requestCallback: function(id) {
var args = Array.prototype.slice.call(arguments, 1);
args.unshift(id + ' end');
if (args[1] instanceof Error) {
args[1] = this._wrapError(args[1]);
}
this.worker.postMessage(args);
},
console: function console(name) {
return {
log: function() {
return postMessage(['log', {
name: name,
message: Array.prototype.slice.call(arguments).join(', ')
}]);
},
error: function() {
return postMessage(['error', {
name: name,
message: Array.prototype.slice.call(arguments).join(', ')
}]);
}
};
}
};
});