"use strict";
(function(){
const wMsg =
(type,data,transferables)=>{
postMessage({type, data}, transferables || []);
};
const stdout = (...args)=>wMsg('stdout', args);
const stderr = (...args)=>wMsg('stderr', args);
const toss = (...args)=>{
throw new Error(args.join(' '));
};
const fixmeOPFS = "(FIXME: won't work with OPFS-over-sqlite3_vfs.)";
let sqlite3 ;
self.onerror = function() {
const err = arguments[4];
if(err && 'ExitStatus'==err.name){
fiddleModule.isDead = true;
stderr("FATAL ERROR:", err.message);
stderr("Restarting the app requires reloading the page.");
wMsg('error', err);
}
console.error(err);
fiddleModule.setStatus('Exception thrown, see JavaScript console: '+err);
};
const Sqlite3Shell = {
dbFilename: function f(){
if(!f._) f._ = sqlite3.wasm.xWrap('fiddle_db_filename', "string", ['string']);
return f._(0);
},
dbHandle: function f(){
if(!f._) f._ = sqlite3.wasm.xWrap("fiddle_db_handle", "sqlite3*");
return f._();
},
dbIsOpfs: function f(){
return sqlite3.opfs && sqlite3.capi.sqlite3_js_db_uses_vfs(
this.dbHandle(), "opfs"
);
},
runMain: function f(){
if(f.argv) return 0===f.argv.rc;
const dbName = "/fiddle.sqlite3";
f.argv = [
'sqlite3-fiddle.wasm',
'-bail', '-safe',
dbName
];
const capi = sqlite3.capi, wasm = sqlite3.wasm;
capi.sqlite3_shutdown();
f.argv.pArgv = wasm.allocMainArgv(f.argv);
f.argv.rc = wasm.exports.fiddle_main(
f.argv.length, f.argv.pArgv
);
if(f.argv.rc){
stderr("Fatal error initializing sqlite3 shell.");
fiddleModule.isDead = true;
return false;
}
stdout("SQLite version", capi.sqlite3_libversion(),
capi.sqlite3_sourceid().substr(0,19));
stdout('Welcome to the "fiddle" shell.');
if(sqlite3.opfs){
stdout("\nOPFS is available. To open a persistent db, use:\n\n",
" .open file:name?vfs=opfs\n\nbut note that some",
"features (e.g. upload) do not yet work with OPFS.");
sqlite3.opfs.registerVfs();
}
stdout('\nEnter ".help" for usage hints.');
this.exec([ '.nullvalue NULL',
'.headers on'
].join('\n'));
return true;
},
exec: function f(sql){
if(!f._){
if(!this.runMain()) return;
f._ = sqlite3.wasm.xWrap('fiddle_exec', null, ['string']);
}
if(fiddleModule.isDead){
stderr("shell module has exit()ed. Cannot run SQL.");
return;
}
wMsg('working','start');
try {
if(f._running){
stderr('Cannot run multiple commands concurrently.');
}else if(sql){
if(Array.isArray(sql)) sql = sql.join('');
f._running = true;
f._(sql);
}
}finally{
delete f._running;
wMsg('working','end');
}
},
resetDb: function f(){
if(!f._) f._ = sqlite3.wasm.xWrap('fiddle_reset_db', null);
stdout("Resetting database.");
f._();
stdout("Reset",this.dbFilename());
},
interrupt: function f(){
if(!f._) f._ = sqlite3.wasm.xWrap('fiddle_interrupt', null);
stdout("Requesting interrupt.");
f._();
}
};
self.onmessage = function f(ev){
ev = ev.data;
if(!f.cache){
f.cache = {
prevFilename: null
};
}
switch(ev.type){
case 'shellExec': Sqlite3Shell.exec(ev.data); return;
case 'db-reset': Sqlite3Shell.resetDb(); return;
case 'interrupt': Sqlite3Shell.interrupt(); return;
case 'db-export': {
const fn = Sqlite3Shell.dbFilename();
stdout("Exporting",fn+".");
const fn2 = fn ? fn.split(/[/\\]/).pop() : null;
try{
if(!fn2) toss("DB appears to be closed.");
const buffer = sqlite3.capi.sqlite3_js_db_export(
Sqlite3Shell.dbHandle()
);
wMsg('db-export',{filename: fn2, buffer: buffer.buffer}, [buffer.buffer]);
}catch(e){
console.error("Export failed:",e);
wMsg('db-export',{
filename: fn,
error: e.message
});
}
return;
}
case 'open': {
const opt = ev.data;
let buffer = opt.buffer;
stderr('open():',fixmeOPFS);
if(buffer instanceof ArrayBuffer){
buffer = new Uint8Array(buffer);
}else if(!(buffer instanceof Uint8Array)){
stderr("'open' expects {buffer:Uint8Array} containing an uploaded db.");
return;
}
const fn = (
opt.filename
? opt.filename.split(/[/\\]/).pop().replace('"','_')
: ("db-"+((Math.random() * 10000000) | 0)+
"-"+((Math.random() * 10000000) | 0)+".sqlite3")
);
try {
const fnAbs = '/'+fn;
const oldName = Sqlite3Shell.dbFilename();
if(oldName && oldName===fnAbs){
Sqlite3Shell.exec('.open :memory:');
fiddleModule.FS.unlink(fnAbs);
}
fiddleModule.FS.createDataFile("/", fn, buffer, true, true);
Sqlite3Shell.exec('.open "'+fnAbs+'"');
if(oldName && oldName!==fnAbs){
try{fiddleModule.fsUnlink(oldName)}
catch(e){}
}
stdout("Replaced DB with",fn+".");
}catch(e){
stderr("Error installing db",fn+":",e.message);
}
return;
}
};
console.warn("Unknown fiddle-worker message type:",ev);
};
const fiddleModule = {
print: stdout,
printErr: stderr,
setStatus: function f(text){
if(!f.last) f.last = { step: 0, text: '' };
else if(text === f.last.text) return;
f.last.text = text;
wMsg('module',{
type:'status',
data:{step: ++f.last.step, text: text||null}
});
}
};
importScripts('fiddle-module.js'+self.location.search);
sqlite3InitModule(fiddleModule).then((_sqlite3)=>{
sqlite3 = _sqlite3;
console.warn("Installing sqlite3 module globally (in Worker)",
"for use in the dev console.");
self.sqlite3 = sqlite3;
const dbVfs = sqlite3.wasm.xWrap('fiddle_db_vfs', "*", ['string']);
fiddleModule.fsUnlink = (fn)=>{
return sqlite3.wasm.sqlite3_wasm_vfs_unlink(dbVfs(0), fn);
};
wMsg('fiddle-ready');
});
})();