diff --git a/.gitignore b/.gitignore
new file mode 100644
index 000000000..431134610
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+*.zip
+*.xpi
diff --git a/extension/.gitignore b/extension/.gitignore
index ee7d076c3..6a5428cbc 100644
--- a/extension/.gitignore
+++ b/extension/.gitignore
@@ -1,3 +1,7 @@
node_modules/
*.js.map
background/*.js
+lib/wallet/*.js
+lib/*.js
+popup/*.js
+test/tests/*.js
diff --git a/extension/background/db.js b/extension/background/db.js
deleted file mode 100644
index 8abf56b48..000000000
--- a/extension/background/db.js
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- This file is part of TALER
- (C) 2015 GNUnet e.V.
-
- TALER is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- TALER; see the file COPYING. If not, If not, see
- */
-"use strict";
-const DB_NAME = "taler";
-const DB_VERSION = 1;
-/**
- * Return a promise that resolves
- * to the taler wallet db.
- */
-function openTalerDb() {
- return new Promise((resolve, reject) => {
- let req = indexedDB.open(DB_NAME, DB_VERSION);
- req.onerror = (e) => {
- reject(e);
- };
- req.onsuccess = (e) => {
- resolve(req.result);
- };
- req.onupgradeneeded = (e) => {
- let db = req.result;
- console.log("DB: upgrade needed: oldVersion = " + e.oldVersion);
- switch (e.oldVersion) {
- case 0:
- let mints = db.createObjectStore("mints", { keyPath: "baseUrl" });
- mints.createIndex("pubKey", "keys.master_public_key");
- db.createObjectStore("reserves", { keyPath: "reserve_pub" });
- db.createObjectStore("denoms", { keyPath: "denomPub" });
- let coins = db.createObjectStore("coins", { keyPath: "coinPub" });
- coins.createIndex("mintBaseUrl", "mintBaseUrl");
- db.createObjectStore("transactions", { keyPath: "contractHash" });
- db.createObjectStore("precoins", { keyPath: "coinPub", autoIncrement: true });
- db.createObjectStore("history", { keyPath: "id", autoIncrement: true });
- break;
- }
- };
- });
-}
-function exportDb(db) {
- let dump = {
- name: db.name,
- version: db.version,
- stores: {}
- };
- return new Promise((resolve, reject) => {
- let tx = db.transaction(db.objectStoreNames);
- tx.addEventListener("complete", (e) => {
- resolve(dump);
- });
- for (let i = 0; i < db.objectStoreNames.length; i++) {
- let name = db.objectStoreNames[i];
- let storeDump = {};
- dump.stores[name] = storeDump;
- let store = tx.objectStore(name)
- .openCursor()
- .addEventListener("success", (e) => {
- let cursor = e.target.result;
- if (cursor) {
- storeDump[cursor.key] = cursor.value;
- cursor.continue();
- }
- });
- }
- });
-}
diff --git a/extension/background/emscriptif.js b/extension/background/emscriptif.js
deleted file mode 100644
index 58b14b41c..000000000
--- a/extension/background/emscriptif.js
+++ /dev/null
@@ -1,643 +0,0 @@
-/*
- This file is part of TALER
- (C) 2015 GNUnet e.V.
-
- TALER is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- TALER; see the file COPYING. If not, If not, see
- */
-/**
- * High-level interface to emscripten-compiled modules used
- * by the wallet.
- * @module EmscriptIf
- * @author Florian Dold
- */
-"use strict";
-// Size of a native pointer.
-const PTR_SIZE = 4;
-const GNUNET_OK = 1;
-const GNUNET_YES = 1;
-const GNUNET_NO = 0;
-const GNUNET_SYSERR = -1;
-let getEmsc = (...args) => Module.cwrap.apply(null, args);
-var emsc = {
- free: (ptr) => Module._free(ptr),
- get_value: getEmsc('TALER_WR_get_value', 'number', ['number']),
- get_fraction: getEmsc('TALER_WR_get_fraction', 'number', ['number']),
- get_currency: getEmsc('TALER_WR_get_currency', 'string', ['number']),
- amount_add: getEmsc('TALER_amount_add', 'number', ['number', 'number', 'number']),
- amount_subtract: getEmsc('TALER_amount_subtract', 'number', ['number', 'number', 'number']),
- amount_normalize: getEmsc('TALER_amount_normalize', 'void', ['number']),
- amount_get_zero: getEmsc('TALER_amount_get_zero', 'number', ['string', 'number']),
- amount_cmp: getEmsc('TALER_amount_cmp', 'number', ['number', 'number']),
- amount_hton: getEmsc('TALER_amount_hton', 'void', ['number', 'number']),
- amount_ntoh: getEmsc('TALER_amount_ntoh', 'void', ['number', 'number']),
- hash: getEmsc('GNUNET_CRYPTO_hash', 'void', ['number', 'number', 'number']),
- memmove: getEmsc('memmove', 'number', ['number', 'number', 'number']),
- rsa_public_key_free: getEmsc('GNUNET_CRYPTO_rsa_public_key_free', 'void', ['number']),
- rsa_signature_free: getEmsc('GNUNET_CRYPTO_rsa_signature_free', 'void', ['number']),
- string_to_data: getEmsc('GNUNET_STRINGS_string_to_data', 'number', ['number', 'number', 'number', 'number']),
- eddsa_sign: getEmsc('GNUNET_CRYPTO_eddsa_sign', 'number', ['number', 'number', 'number']),
- hash_create_random: getEmsc('GNUNET_CRYPTO_hash_create_random', 'void', ['number', 'number']),
- rsa_blinding_key_destroy: getEmsc('GNUNET_CRYPTO_rsa_blinding_key_free', 'void', ['number']),
-};
-var emscAlloc = {
- get_amount: getEmsc('TALER_WRALL_get_amount', 'number', ['number', 'number', 'number', 'string']),
- eddsa_key_create: getEmsc('GNUNET_CRYPTO_eddsa_key_create', 'number', []),
- eddsa_public_key_from_private: getEmsc('TALER_WRALL_eddsa_public_key_from_private', 'number', ['number']),
- data_to_string_alloc: getEmsc('GNUNET_STRINGS_data_to_string_alloc', 'number', ['number', 'number']),
- purpose_create: getEmsc('TALER_WRALL_purpose_create', 'number', ['number', 'number', 'number']),
- rsa_blind: getEmsc('GNUNET_CRYPTO_rsa_blind', 'number', ['number', 'number', 'number', 'number']),
- rsa_blinding_key_create: getEmsc('GNUNET_CRYPTO_rsa_blinding_key_create', 'number', ['number']),
- rsa_blinding_key_encode: getEmsc('GNUNET_CRYPTO_rsa_blinding_key_encode', 'number', ['number', 'number']),
- rsa_signature_encode: getEmsc('GNUNET_CRYPTO_rsa_signature_encode', 'number', ['number', 'number']),
- rsa_blinding_key_decode: getEmsc('GNUNET_CRYPTO_rsa_blinding_key_decode', 'number', ['number', 'number']),
- rsa_public_key_decode: getEmsc('GNUNET_CRYPTO_rsa_public_key_decode', 'number', ['number', 'number']),
- rsa_signature_decode: getEmsc('GNUNET_CRYPTO_rsa_signature_decode', 'number', ['number', 'number']),
- rsa_public_key_encode: getEmsc('GNUNET_CRYPTO_rsa_public_key_encode', 'number', ['number', 'number']),
- rsa_unblind: getEmsc('GNUNET_CRYPTO_rsa_unblind', 'number', ['number', 'number', 'number']),
- malloc: (size) => Module._malloc(size),
-};
-var SignaturePurpose;
-(function (SignaturePurpose) {
- SignaturePurpose[SignaturePurpose["RESERVE_WITHDRAW"] = 1200] = "RESERVE_WITHDRAW";
- SignaturePurpose[SignaturePurpose["WALLET_COIN_DEPOSIT"] = 1201] = "WALLET_COIN_DEPOSIT";
-})(SignaturePurpose || (SignaturePurpose = {}));
-var RandomQuality;
-(function (RandomQuality) {
- RandomQuality[RandomQuality["WEAK"] = 0] = "WEAK";
- RandomQuality[RandomQuality["STRONG"] = 1] = "STRONG";
- RandomQuality[RandomQuality["NONCE"] = 2] = "NONCE";
-})(RandomQuality || (RandomQuality = {}));
-class ArenaObject {
- constructor(arena) {
- this.nativePtr = null;
- if (!arena) {
- if (arenaStack.length == 0) {
- throw Error("No arena available");
- }
- arena = arenaStack[arenaStack.length - 1];
- }
- arena.put(this);
- this.arena = arena;
- }
- getNative() {
- // We want to allow latent allocation
- // of native wrappers, but we never want to
- // pass 'undefined' to emscripten.
- if (this._nativePtr === undefined) {
- throw Error("Native pointer not initialized");
- }
- return this._nativePtr;
- }
- free() {
- if (this.nativePtr !== undefined) {
- emsc.free(this.nativePtr);
- this.nativePtr = undefined;
- }
- }
- alloc(size) {
- if (this.nativePtr !== undefined) {
- throw Error("Double allocation");
- }
- this.nativePtr = emscAlloc.malloc(size);
- }
- setNative(n) {
- if (n === undefined) {
- throw Error("Native pointer must be a number or null");
- }
- this._nativePtr = n;
- }
- set nativePtr(v) {
- this.setNative(v);
- }
- get nativePtr() {
- return this.getNative();
- }
-}
-class DefaultArena {
- constructor() {
- this.heap = [];
- }
- put(obj) {
- this.heap.push(obj);
- }
- destroy() {
- for (let obj of this.heap) {
- obj.destroy();
- }
- this.heap = [];
- }
-}
-/**
- * Arena that destroys all its objects once control has returned to the message
- * loop and a small interval has passed.
- */
-class SyncArena extends DefaultArena {
- constructor() {
- super();
- let me = this;
- this.timer = new Worker('background/timerThread.js');
- this.timer.onmessage = () => {
- this.destroy();
- };
- //this.timer.postMessage({interval: 50});
- }
- destroy() {
- super.destroy();
- }
-}
-let arenaStack = [];
-arenaStack.push(new SyncArena());
-class Amount extends ArenaObject {
- constructor(args, arena) {
- super(arena);
- if (args) {
- this.nativePtr = emscAlloc.get_amount(args.value, 0, args.fraction, args.currency);
- }
- else {
- this.nativePtr = emscAlloc.get_amount(0, 0, 0, "");
- }
- }
- destroy() {
- if (this.nativePtr != 0) {
- emsc.free(this.nativePtr);
- }
- }
- static getZero(currency, a) {
- let am = new Amount(null, a);
- let r = emsc.amount_get_zero(currency, am.getNative());
- if (r != GNUNET_OK) {
- throw Error("invalid currency");
- }
- return am;
- }
- toNbo(a) {
- let x = new AmountNbo(a);
- x.alloc();
- emsc.amount_hton(x.nativePtr, this.nativePtr);
- return x;
- }
- fromNbo(nbo) {
- emsc.amount_ntoh(this.nativePtr, nbo.nativePtr);
- }
- get value() {
- return emsc.get_value(this.nativePtr);
- }
- get fraction() {
- return emsc.get_fraction(this.nativePtr);
- }
- get currency() {
- return emsc.get_currency(this.nativePtr);
- }
- toJson() {
- return {
- value: emsc.get_value(this.nativePtr),
- fraction: emsc.get_fraction(this.nativePtr),
- currency: emsc.get_currency(this.nativePtr)
- };
- }
- /**
- * Add an amount to this amount.
- */
- add(a) {
- let res = emsc.amount_add(this.nativePtr, a.nativePtr, this.nativePtr);
- if (res < 1) {
- // Overflow
- return false;
- }
- return true;
- }
- /**
- * Perform saturating subtraction on amounts.
- */
- sub(a) {
- // this = this - a
- let res = emsc.amount_subtract(this.nativePtr, this.nativePtr, a.nativePtr);
- if (res == 0) {
- // Underflow
- return false;
- }
- if (res > 0) {
- return true;
- }
- throw Error("Incompatible currencies");
- }
- cmp(a) {
- return emsc.amount_cmp(this.nativePtr, a.nativePtr);
- }
- normalize() {
- emsc.amount_normalize(this.nativePtr);
- }
-}
-class PackedArenaObject extends ArenaObject {
- constructor(a) {
- super(a);
- }
- toCrock() {
- var d = emscAlloc.data_to_string_alloc(this.nativePtr, this.size());
- var s = Module.Pointer_stringify(d);
- emsc.free(d);
- return s;
- }
- toJson() {
- // Per default, the json encoding of
- // packed arena objects is just the crockford encoding.
- // Subclasses typically want to override this.
- return this.toCrock();
- }
- loadCrock(s) {
- this.alloc();
- // We need to get the javascript string
- // to the emscripten heap first.
- let buf = ByteArray.fromString(s);
- let res = emsc.string_to_data(buf.nativePtr, s.length, this.nativePtr, this.size());
- buf.destroy();
- if (res < 1) {
- throw { error: "wrong encoding" };
- }
- }
- alloc() {
- if (this.nativePtr === null) {
- this.nativePtr = emscAlloc.malloc(this.size());
- }
- }
- destroy() {
- emsc.free(this.nativePtr);
- this.nativePtr = 0;
- }
- hash() {
- var x = new HashCode();
- x.alloc();
- emsc.hash(this.nativePtr, this.size(), x.nativePtr);
- return x;
- }
- hexdump() {
- let bytes = [];
- for (let i = 0; i < this.size(); i++) {
- let b = Module.getValue(this.getNative() + i, "i8");
- b = (b + 256) % 256;
- bytes.push("0".concat(b.toString(16)).slice(-2));
- }
- let lines = [];
- for (let i = 0; i < bytes.length; i += 8) {
- lines.push(bytes.slice(i, i + 8).join(","));
- }
- return lines.join("\n");
- }
-}
-class AmountNbo extends PackedArenaObject {
- size() {
- return 24;
- }
- toJson() {
- let a = new DefaultArena();
- let am = new Amount(null, a);
- am.fromNbo(this);
- let json = am.toJson();
- a.destroy();
- return json;
- }
-}
-class EddsaPrivateKey extends PackedArenaObject {
- static create(a) {
- let obj = new EddsaPrivateKey(a);
- obj.nativePtr = emscAlloc.eddsa_key_create();
- return obj;
- }
- size() {
- return 32;
- }
- getPublicKey(a) {
- let obj = new EddsaPublicKey(a);
- obj.nativePtr = emscAlloc.eddsa_public_key_from_private(this.nativePtr);
- return obj;
- }
-}
-mixinStatic(EddsaPrivateKey, fromCrock);
-function fromCrock(s) {
- let x = new this();
- x.alloc();
- x.loadCrock(s);
- return x;
-}
-function mixin(obj, method, name) {
- if (!name) {
- name = method.name;
- }
- if (!name) {
- throw Error("Mixin needs a name.");
- }
- obj.prototype[method.name] = method;
-}
-function mixinStatic(obj, method, name) {
- if (!name) {
- name = method.name;
- }
- if (!name) {
- throw Error("Mixin needs a name.");
- }
- obj[method.name] = method;
-}
-class EddsaPublicKey extends PackedArenaObject {
- size() {
- return 32;
- }
-}
-mixinStatic(EddsaPublicKey, fromCrock);
-function makeFromCrock(decodeFn) {
- function fromCrock(s, a) {
- let obj = new this(a);
- let buf = ByteArray.fromCrock(s);
- obj.setNative(decodeFn(buf.getNative(), buf.size()));
- buf.destroy();
- return obj;
- }
- return fromCrock;
-}
-function makeToCrock(encodeFn) {
- function toCrock() {
- let ptr = emscAlloc.malloc(PTR_SIZE);
- let size = emscAlloc.rsa_blinding_key_encode(this.nativePtr, ptr);
- let res = new ByteArray(size, Module.getValue(ptr, '*'));
- let s = res.toCrock();
- emsc.free(ptr);
- res.destroy();
- return s;
- }
- return toCrock;
-}
-class RsaBlindingKey extends ArenaObject {
- constructor(...args) {
- super(...args);
- this.toCrock = makeToCrock(emscAlloc.rsa_blinding_key_encode);
- }
- static create(len, a) {
- let o = new RsaBlindingKey(a);
- o.nativePtr = emscAlloc.rsa_blinding_key_create(len);
- return o;
- }
- destroy() {
- // TODO
- }
-}
-mixinStatic(RsaBlindingKey, makeFromCrock(emscAlloc.rsa_blinding_key_decode));
-class HashCode extends PackedArenaObject {
- size() {
- return 64;
- }
- random(qualStr) {
- let qual;
- switch (qualStr) {
- case "weak":
- qual = RandomQuality.WEAK;
- break;
- case "strong":
- case null:
- case undefined:
- qual = RandomQuality.STRONG;
- break;
- case "nonce":
- qual = RandomQuality.NONCE;
- break;
- default:
- throw Error(format("unknown crypto quality: {0}", qual));
- }
- this.alloc();
- emsc.hash_create_random(qual, this.nativePtr);
- }
-}
-mixinStatic(HashCode, fromCrock);
-class ByteArray extends PackedArenaObject {
- constructor(desiredSize, init, a) {
- super(a);
- if (init === undefined || init === null) {
- this.nativePtr = emscAlloc.malloc(desiredSize);
- }
- else {
- this.nativePtr = init;
- }
- this.allocatedSize = desiredSize;
- }
- size() {
- return this.allocatedSize;
- }
- static fromString(s, a) {
- let hstr = emscAlloc.malloc(s.length + 1);
- Module.writeStringToMemory(s, hstr);
- return new ByteArray(s.length, hstr, a);
- }
- static fromCrock(s, a) {
- let hstr = emscAlloc.malloc(s.length + 1);
- Module.writeStringToMemory(s, hstr);
- let decodedLen = Math.floor((s.length * 5) / 8);
- let ba = new ByteArray(decodedLen, null, a);
- let res = emsc.string_to_data(hstr, s.length, ba.nativePtr, decodedLen);
- emsc.free(hstr);
- if (res != GNUNET_OK) {
- throw Error("decoding failed");
- }
- return ba;
- }
-}
-class EccSignaturePurpose extends PackedArenaObject {
- constructor(purpose, payload, a) {
- super(a);
- this.nativePtr = emscAlloc.purpose_create(purpose, payload.nativePtr, payload.size());
- this.payloadSize = payload.size();
- }
- size() {
- return this.payloadSize + 8;
- }
-}
-class SignatureStruct {
- constructor(x) {
- this.members = {};
- for (let k in x) {
- this.set(k, x[k]);
- }
- }
- toPurpose(a) {
- let totalSize = 0;
- for (let f of this.fieldTypes()) {
- let name = f[0];
- let member = this.members[name];
- if (!member) {
- throw Error(format("Member {0} not set", name));
- }
- totalSize += member.size();
- }
- let buf = emscAlloc.malloc(totalSize);
- let ptr = buf;
- for (let f of this.fieldTypes()) {
- let name = f[0];
- let member = this.members[name];
- let size = member.size();
- emsc.memmove(ptr, member.nativePtr, size);
- ptr += size;
- }
- let ba = new ByteArray(totalSize, buf, a);
- return new EccSignaturePurpose(this.purpose(), ba);
- }
- toJson() {
- let res = {};
- for (let f of this.fieldTypes()) {
- let name = f[0];
- let member = this.members[name];
- if (!member) {
- throw Error(format("Member {0} not set", name));
- }
- res[name] = member.toJson();
- }
- res["purpose"] = this.purpose();
- return res;
- }
- set(name, value) {
- let typemap = {};
- for (let f of this.fieldTypes()) {
- typemap[f[0]] = f[1];
- }
- if (!(name in typemap)) {
- throw Error(format("Key {0} not found", name));
- }
- if (!(value instanceof typemap[name])) {
- throw Error(format("Wrong type for {0}", name));
- }
- this.members[name] = value;
- }
-}
-class WithdrawRequestPS extends SignatureStruct {
- constructor(w) {
- super(w);
- }
- purpose() {
- return SignaturePurpose.RESERVE_WITHDRAW;
- }
- fieldTypes() {
- return [
- ["reserve_pub", EddsaPublicKey],
- ["amount_with_fee", AmountNbo],
- ["withdraw_fee", AmountNbo],
- ["h_denomination_pub", HashCode],
- ["h_coin_envelope", HashCode]
- ];
- }
-}
-class AbsoluteTimeNbo extends PackedArenaObject {
- static fromTalerString(s) {
- let x = new AbsoluteTimeNbo();
- x.alloc();
- let r = /Date\(([0-9]+)\)/;
- let m = r.exec(s);
- if (m.length != 2) {
- throw Error();
- }
- let n = parseInt(m[1]) * 1000000;
- // XXX: This only works up to 54 bit numbers.
- set64(x.getNative(), n);
- return x;
- }
- size() {
- return 8;
- }
-}
-// XXX: This only works up to 54 bit numbers.
-function set64(p, n) {
- for (let i = 0; i < 8; ++i) {
- Module.setValue(p + (7 - i), n & 0xFF, "i8");
- n = Math.floor(n / 256);
- }
-}
-class UInt64 extends PackedArenaObject {
- static fromNumber(n) {
- let x = new UInt64();
- x.alloc();
- set64(x.getNative(), n);
- return x;
- }
- size() {
- return 8;
- }
-}
-class DepositRequestPS extends SignatureStruct {
- constructor(w) {
- super(w);
- }
- purpose() {
- return SignaturePurpose.WALLET_COIN_DEPOSIT;
- }
- fieldTypes() {
- return [
- ["h_contract", HashCode],
- ["h_wire", HashCode],
- ["timestamp", AbsoluteTimeNbo],
- ["refund_deadline", AbsoluteTimeNbo],
- ["transaction_id", UInt64],
- ["amount_with_fee", AmountNbo],
- ["deposit_fee", AmountNbo],
- ["merchant", EddsaPublicKey],
- ["coin_pub", EddsaPublicKey],
- ];
- }
-}
-function makeEncode(encodeFn) {
- function encode(arena) {
- let ptr = emscAlloc.malloc(PTR_SIZE);
- let len = encodeFn(this.getNative(), ptr);
- let res = new ByteArray(len, null, arena);
- res.setNative(Module.getValue(ptr, '*'));
- emsc.free(ptr);
- return res;
- }
- return encode;
-}
-class RsaPublicKey extends ArenaObject {
- toCrock() {
- return this.encode().toCrock();
- }
- destroy() {
- emsc.rsa_public_key_free(this.nativePtr);
- this.nativePtr = 0;
- }
-}
-mixinStatic(RsaPublicKey, makeFromCrock(emscAlloc.rsa_public_key_decode));
-mixin(RsaPublicKey, makeEncode(emscAlloc.rsa_public_key_encode));
-class EddsaSignature extends PackedArenaObject {
- size() {
- return 64;
- }
-}
-class RsaSignature extends ArenaObject {
- destroy() {
- emsc.rsa_signature_free(this.getNative());
- this.setNative(0);
- }
-}
-mixinStatic(RsaSignature, makeFromCrock(emscAlloc.rsa_signature_decode));
-mixin(RsaSignature, makeEncode(emscAlloc.rsa_signature_encode));
-function rsaBlind(hashCode, blindingKey, pkey, arena) {
- let ptr = emscAlloc.malloc(PTR_SIZE);
- let s = emscAlloc.rsa_blind(hashCode.nativePtr, blindingKey.nativePtr, pkey.nativePtr, ptr);
- return new ByteArray(s, Module.getValue(ptr, '*'), arena);
-}
-function eddsaSign(purpose, priv, a) {
- let sig = new EddsaSignature(a);
- sig.alloc();
- let res = emsc.eddsa_sign(priv.nativePtr, purpose.nativePtr, sig.nativePtr);
- if (res < 1) {
- throw Error("EdDSA signing failed");
- }
- return sig;
-}
-function rsaUnblind(sig, bk, pk, a) {
- let x = new RsaSignature(a);
- x.nativePtr = emscAlloc.rsa_unblind(sig.nativePtr, bk.nativePtr, pk.nativePtr);
- return x;
-}
diff --git a/extension/background/main.ts b/extension/background/main.ts
new file mode 100644
index 000000000..ea04284e6
--- /dev/null
+++ b/extension/background/main.ts
@@ -0,0 +1,42 @@
+/*
+ This file is part of TALER
+ (C) 2016 GNUnet e.V.
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, If not, see
+ */
+
+// Entry point for the background page.
+
+"use strict";
+
+System.config({
+ defaultJSExtensions: true,
+});
+
+var Module: any;
+
+if ("object" !== typeof Module) {
+ throw Error("emscripten not loaded, no 'Module' defined");
+}
+
+let mod = System.newModule({Module: Module});
+let modName = System.normalizeSync("../lib/emscripten/emsc");
+console.log("registering", modName);
+System.set(modName, mod);
+
+System.import("../lib/wallet/wxmessaging")
+ .then((wxmessaging) => {
+ wxmessaging.wxMain();
+ })
+ .catch((e) => {
+ console.error("import failed", e.stack);
+ });
\ No newline at end of file
diff --git a/extension/background/wallet.js b/extension/background/wallet.js
deleted file mode 100644
index 047eb9970..000000000
--- a/extension/background/wallet.js
+++ /dev/null
@@ -1,502 +0,0 @@
-/*
- This file is part of TALER
- (C) 2015 GNUnet e.V.
-
- TALER is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- TALER; see the file COPYING. If not, If not, see
- */
-/**
- * High-level wallet operations that should be indepentent from the underlying
- * browser extension interface.
- * @module Wallet
- * @author Florian Dold
- */
-///
-"use strict";
-var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
- else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
- return c > 3 && r && Object.defineProperty(target, key, r), r;
-};
-let AmountJson = class {
-};
-__decorate([
- Checkable.Number
-], AmountJson.prototype, "value", void 0);
-__decorate([
- Checkable.Number
-], AmountJson.prototype, "fraction", void 0);
-__decorate([
- Checkable.String
-], AmountJson.prototype, "currency", void 0);
-AmountJson = __decorate([
- Checkable.Class
-], AmountJson);
-let CoinPaySig = class {
-};
-__decorate([
- Checkable.String
-], CoinPaySig.prototype, "coin_sig", void 0);
-__decorate([
- Checkable.String
-], CoinPaySig.prototype, "coin_pub", void 0);
-__decorate([
- Checkable.String
-], CoinPaySig.prototype, "ub_sig", void 0);
-__decorate([
- Checkable.String
-], CoinPaySig.prototype, "denom_pub", void 0);
-__decorate([
- Checkable.Value(AmountJson)
-], CoinPaySig.prototype, "f", void 0);
-CoinPaySig = __decorate([
- Checkable.Class
-], CoinPaySig);
-/**
- * See http://api.taler.net/wallet.html#general
- */
-function canonicalizeBaseUrl(url) {
- let x = new URI(url);
- if (!x.protocol()) {
- x.protocol("https");
- }
- x.path(x.path() + "/").normalizePath();
- x.fragment();
- x.query();
- return x.href();
-}
-function copy(o) {
- return JSON.parse(JSON.stringify(o));
-}
-function rankDenom(denom1, denom2) {
- // Slow ... we should find a better way than to convert it evert time.
- let v1 = new Amount(denom1.value);
- let v2 = new Amount(denom2.value);
- return (-1) * v1.cmp(v2);
-}
-class Wallet {
- constructor(db, http, badge) {
- this.db = db;
- this.http = http;
- this.badge = badge;
- }
- static signDeposit(offer, cds) {
- let ret = [];
- let amountSpent = Amount.getZero(cds[0].coin.currentAmount.currency);
- let amountRemaining = new Amount(offer.contract.amount);
- cds = copy(cds);
- for (let cd of cds) {
- let coinSpend;
- if (amountRemaining.value == 0 && amountRemaining.fraction == 0) {
- break;
- }
- if (amountRemaining.cmp(new Amount(cd.coin.currentAmount)) < 0) {
- coinSpend = new Amount(amountRemaining.toJson());
- }
- else {
- coinSpend = new Amount(cd.coin.currentAmount);
- }
- amountSpent.add(coinSpend);
- amountRemaining.sub(coinSpend);
- let newAmount = new Amount(cd.coin.currentAmount);
- newAmount.sub(coinSpend);
- cd.coin.currentAmount = newAmount.toJson();
- let args = {
- h_contract: HashCode.fromCrock(offer.H_contract),
- h_wire: HashCode.fromCrock(offer.contract.H_wire),
- amount_with_fee: coinSpend.toNbo(),
- coin_pub: EddsaPublicKey.fromCrock(cd.coin.coinPub),
- deposit_fee: new Amount(cd.denom.fee_deposit).toNbo(),
- merchant: EddsaPublicKey.fromCrock(offer.contract.merchant_pub),
- refund_deadline: AbsoluteTimeNbo.fromTalerString(offer.contract.refund_deadline),
- timestamp: AbsoluteTimeNbo.fromTalerString(offer.contract.timestamp),
- transaction_id: UInt64.fromNumber(offer.contract.transaction_id),
- };
- let d = new DepositRequestPS(args);
- let coinSig = eddsaSign(d.toPurpose(), EddsaPrivateKey.fromCrock(cd.coin.coinPriv))
- .toCrock();
- let s = {
- coin_sig: coinSig,
- coin_pub: cd.coin.coinPub,
- ub_sig: cd.coin.denomSig,
- denom_pub: cd.coin.denomPub,
- f: coinSpend.toJson(),
- };
- ret.push({ sig: s, updatedCoin: cd.coin });
- }
- return ret;
- }
- /**
- * Get mints and associated coins that are still spendable,
- * but only if the sum the coins' remaining value exceeds the payment amount.
- * @param paymentAmount
- * @param depositFeeLimit
- * @param allowedMints
- */
- getPossibleMintCoins(paymentAmount, depositFeeLimit, allowedMints) {
- let m = {};
- function storeMintCoin(mc) {
- let mint = mc[0];
- let coin = mc[1];
- let cd = {
- coin: coin,
- denom: mint.keys.denoms.find((e) => e.denom_pub === coin.denomPub)
- };
- if (!cd.denom) {
- throw Error("denom not found (database inconsistent)");
- }
- let x = m[mint.baseUrl];
- if (!x) {
- m[mint.baseUrl] = [cd];
- }
- else {
- x.push(cd);
- }
- }
- let ps = allowedMints.map((info) => {
- return Query(this.db)
- .iterIndex("mints", "pubKey", info.master_pub)
- .indexJoin("coins", "mintBaseUrl", (mint) => mint.baseUrl)
- .reduce(storeMintCoin);
- });
- return Promise.all(ps).then(() => {
- let ret = {};
- nextMint: for (let key in m) {
- let coins = m[key].map((x) => ({
- a: new Amount(x.denom.fee_deposit),
- c: x
- }));
- // Sort by ascending deposit fee
- coins.sort((o1, o2) => o1.a.cmp(o2.a));
- let maxFee = new Amount(depositFeeLimit);
- let minAmount = new Amount(paymentAmount);
- let accFee = new Amount(coins[0].c.denom.fee_deposit);
- let accAmount = Amount.getZero(coins[0].c.coin.currentAmount.currency);
- let usableCoins = [];
- nextCoin: for (let i = 0; i < coins.length; i++) {
- let coinAmount = new Amount(coins[i].c.coin.currentAmount);
- let coinFee = coins[i].a;
- if (coinAmount.cmp(coinFee) <= 0) {
- continue nextCoin;
- }
- accFee.add(coinFee);
- accAmount.add(coinAmount);
- if (accFee.cmp(maxFee) >= 0) {
- console.log("too much fees");
- continue nextMint;
- }
- usableCoins.push(coins[i].c);
- if (accAmount.cmp(minAmount) >= 0) {
- ret[key] = usableCoins;
- continue nextMint;
- }
- }
- }
- return ret;
- });
- }
- executePay(offer, payCoinInfo, merchantBaseUrl, chosenMint) {
- let payReq = {};
- payReq["H_wire"] = offer.contract.H_wire;
- payReq["H_contract"] = offer.H_contract;
- payReq["transaction_id"] = offer.contract.transaction_id;
- payReq["refund_deadline"] = offer.contract.refund_deadline;
- payReq["mint"] = URI(chosenMint).href();
- payReq["coins"] = payCoinInfo.map((x) => x.sig);
- payReq["timestamp"] = offer.contract.timestamp;
- let payUrl = URI(offer.pay_url).absoluteTo(merchantBaseUrl);
- let t = {
- contractHash: offer.H_contract,
- contract: offer.contract,
- payUrl: payUrl.href(),
- payReq: payReq
- };
- return Query(this.db)
- .put("transactions", t)
- .putAll("coins", payCoinInfo.map((pci) => pci.updatedCoin))
- .finish();
- }
- confirmPay(offer, merchantPageUrl) {
- return Promise.resolve().then(() => {
- return this.getPossibleMintCoins(offer.contract.amount, offer.contract.max_fee, offer.contract.mints);
- }).then((mcs) => {
- if (Object.keys(mcs).length == 0) {
- throw Error("Not enough coins.");
- }
- let mintUrl = Object.keys(mcs)[0];
- let ds = Wallet.signDeposit(offer, mcs[mintUrl]);
- return this.executePay(offer, ds, merchantPageUrl, mintUrl);
- });
- }
- doPayment(H_contract) {
- return Promise.resolve().then(() => {
- return Query(this.db)
- .get("transactions", H_contract)
- .then((t) => {
- if (!t) {
- throw Error("contract not found");
- }
- let resp = {
- payUrl: t.payUrl,
- payReq: t.payReq
- };
- return resp;
- });
- });
- }
- confirmReserve(req) {
- let reservePriv = EddsaPrivateKey.create();
- let reservePub = reservePriv.getPublicKey();
- let form = new FormData();
- let now = (new Date()).toString();
- form.append(req.field_amount, req.amount_str);
- form.append(req.field_reserve_pub, reservePub.toCrock());
- form.append(req.field_mint, req.mint);
- // TODO: set bank-specified fields.
- let mintBaseUrl = canonicalizeBaseUrl(req.mint);
- return this.http.postForm(req.post_url, form)
- .then((hresp) => {
- let resp = {
- status: hresp.status,
- text: hresp.responseText,
- success: undefined,
- backlink: undefined
- };
- let reserveRecord = {
- reserve_pub: reservePub.toCrock(),
- reserve_priv: reservePriv.toCrock(),
- mint_base_url: mintBaseUrl,
- created: now,
- last_query: null,
- current_amount: null,
- // XXX: set to actual amount
- initial_amount: null
- };
- if (hresp.status != 200) {
- resp.success = false;
- return resp;
- }
- resp.success = true;
- // We can't show the page directly, so
- // we show some generic page from the wallet.
- resp.backlink = null;
- return Query(this.db)
- .put("reserves", reserveRecord)
- .finish()
- .then(() => {
- // Do this in the background
- this.updateMintFromUrl(reserveRecord.mint_base_url)
- .then((mint) => this.updateReserve(reservePub, mint)
- .then((reserve) => this.depleteReserve(reserve, mint)));
- return resp;
- });
- });
- }
- withdrawPrepare(denom, reserve) {
- let reservePriv = new EddsaPrivateKey();
- reservePriv.loadCrock(reserve.reserve_priv);
- let reservePub = new EddsaPublicKey();
- reservePub.loadCrock(reserve.reserve_pub);
- let denomPub = RsaPublicKey.fromCrock(denom.denom_pub);
- let coinPriv = EddsaPrivateKey.create();
- let coinPub = coinPriv.getPublicKey();
- let blindingFactor = RsaBlindingKey.create(1024);
- let pubHash = coinPub.hash();
- let ev = rsaBlind(pubHash, blindingFactor, denomPub);
- if (!denom.fee_withdraw) {
- throw Error("Field fee_withdraw missing");
- }
- let amountWithFee = new Amount(denom.value);
- amountWithFee.add(new Amount(denom.fee_withdraw));
- let withdrawFee = new Amount(denom.fee_withdraw);
- // Signature
- let withdrawRequest = new WithdrawRequestPS({
- reserve_pub: reservePub,
- amount_with_fee: amountWithFee.toNbo(),
- withdraw_fee: withdrawFee.toNbo(),
- h_denomination_pub: denomPub.encode().hash(),
- h_coin_envelope: ev.hash()
- });
- var sig = eddsaSign(withdrawRequest.toPurpose(), reservePriv);
- let preCoin = {
- reservePub: reservePub.toCrock(),
- blindingKey: blindingFactor.toCrock(),
- coinPub: coinPub.toCrock(),
- coinPriv: coinPriv.toCrock(),
- denomPub: denomPub.encode().toCrock(),
- mintBaseUrl: reserve.mint_base_url,
- withdrawSig: sig.toCrock(),
- coinEv: ev.toCrock(),
- coinValue: denom.value
- };
- return Query(this.db).put("precoins", preCoin).finish().then(() => preCoin);
- }
- withdrawExecute(pc) {
- return Query(this.db)
- .get("reserves", pc.reservePub)
- .then((r) => {
- let wd = {};
- wd.denom_pub = pc.denomPub;
- wd.reserve_pub = pc.reservePub;
- wd.reserve_sig = pc.withdrawSig;
- wd.coin_ev = pc.coinEv;
- let reqUrl = URI("reserve/withdraw").absoluteTo(r.mint_base_url);
- return this.http.postJson(reqUrl, wd);
- })
- .then(resp => {
- if (resp.status != 200) {
- throw new RequestException({
- hint: "Withdrawal failed",
- status: resp.status
- });
- }
- let r = JSON.parse(resp.responseText);
- let denomSig = rsaUnblind(RsaSignature.fromCrock(r.ev_sig), RsaBlindingKey.fromCrock(pc.blindingKey), RsaPublicKey.fromCrock(pc.denomPub));
- let coin = {
- coinPub: pc.coinPub,
- coinPriv: pc.coinPriv,
- denomPub: pc.denomPub,
- denomSig: denomSig.encode().toCrock(),
- currentAmount: pc.coinValue,
- mintBaseUrl: pc.mintBaseUrl,
- };
- return coin;
- });
- }
- updateBadge() {
- function countNonEmpty(c, n) {
- if (c.currentAmount.fraction != 0 || c.currentAmount.value != 0) {
- return n + 1;
- }
- return n;
- }
- function doBadge(n) {
- this.badge.setText(n.toString());
- this.badge.setColor("#0F0");
- }
- Query(this.db)
- .iter("coins")
- .reduce(countNonEmpty, 0)
- .then(doBadge.bind(this));
- }
- storeCoin(coin) {
- Query(this.db)
- .delete("precoins", coin.coinPub)
- .add("coins", coin)
- .finish()
- .then(() => {
- this.updateBadge();
- });
- }
- withdraw(denom, reserve) {
- return this.withdrawPrepare(denom, reserve)
- .then((pc) => this.withdrawExecute(pc))
- .then((c) => this.storeCoin(c));
- }
- /**
- * Withdraw coins from a reserve until it is empty.
- */
- depleteReserve(reserve, mint) {
- let denoms = copy(mint.keys.denoms);
- let remaining = new Amount(reserve.current_amount);
- denoms.sort(rankDenom);
- let workList = [];
- for (let i = 0; i < 1000; i++) {
- let found = false;
- for (let d of denoms) {
- let cost = new Amount(d.value);
- cost.add(new Amount(d.fee_withdraw));
- if (remaining.cmp(cost) < 0) {
- continue;
- }
- found = true;
- remaining.sub(cost);
- workList.push(d);
- }
- if (!found) {
- console.log("did not find coins for remaining ", remaining.toJson());
- break;
- }
- }
- // Do the request one by one.
- let next = () => {
- if (workList.length == 0) {
- return;
- }
- let d = workList.pop();
- this.withdraw(d, reserve)
- .then(() => next());
- };
- // Asynchronous recursion
- next();
- }
- updateReserve(reservePub, mint) {
- let reservePubStr = reservePub.toCrock();
- return Query(this.db)
- .get("reserves", reservePubStr)
- .then((reserve) => {
- let reqUrl = URI("reserve/status").absoluteTo(mint.baseUrl);
- reqUrl.query({ 'reserve_pub': reservePubStr });
- return this.http.get(reqUrl).then(resp => {
- if (resp.status != 200) {
- throw Error();
- }
- let reserveInfo = JSON.parse(resp.responseText);
- if (!reserveInfo) {
- throw Error();
- }
- reserve.current_amount = reserveInfo.balance;
- return Query(this.db)
- .put("reserves", reserve)
- .finish()
- .then(() => reserve);
- });
- });
- }
- /**
- * Update or add mint DB entry by fetching the /keys information.
- * Optionally link the reserve entry to the new or existing
- * mint entry in then DB.
- */
- updateMintFromUrl(baseUrl) {
- let reqUrl = URI("keys").absoluteTo(baseUrl);
- return this.http.get(reqUrl).then((resp) => {
- if (resp.status != 200) {
- throw Error("/keys request failed");
- }
- let mintKeysJson = JSON.parse(resp.responseText);
- if (!mintKeysJson) {
- throw new RequestException({ url: reqUrl, hint: "keys invalid" });
- }
- let mint = {
- baseUrl: baseUrl,
- keys: mintKeysJson
- };
- return Query(this.db).put("mints", mint).finish().then(() => mint);
- });
- }
- getBalances() {
- function collectBalances(c, byCurrency) {
- let acc = byCurrency[c.currentAmount.currency];
- if (!acc) {
- acc = Amount.getZero(c.currentAmount.currency).toJson();
- }
- let am = new Amount(c.currentAmount);
- am.add(new Amount(acc));
- byCurrency[c.currentAmount.currency] = am.toJson();
- return byCurrency;
- }
- return Query(this.db)
- .iter("coins")
- .reduce(collectBalances, {});
- }
-}
diff --git a/extension/content_scripts/notify.js b/extension/content_scripts/notify.js
index 47c839799..5c6c02535 100644
--- a/extension/content_scripts/notify.js
+++ b/extension/content_scripts/notify.js
@@ -15,29 +15,30 @@
*/
// Script that is injected into pages in order to allow merchants pages to
// query the availability of Taler.
+///
"use strict";
document.addEventListener("taler-checkout-probe", function (e) {
- let evt = new Event("taler-wallet-present");
+ var evt = new Event("taler-wallet-present");
document.dispatchEvent(evt);
console.log("merchant handshake done");
});
document.addEventListener("taler-wire-probe", function (e) {
- let evt = new Event("taler-wallet-present");
+ var evt = new Event("taler-wallet-present");
document.dispatchEvent(evt);
console.log("bank handshake done");
});
document.addEventListener("taler-checkout-probe", function (e) {
- let evt = new Event("taler-wallet-present");
+ var evt = new Event("taler-wallet-present");
document.dispatchEvent(evt);
console.log("merchant handshake done");
});
document.addEventListener("taler-create-reserve", function (e) {
- let $ = (x) => document.getElementById(x);
+ var $ = function (x) { return document.getElementById(x); };
console.log("taler-create-reserve with " + JSON.stringify(e.detail));
- let form_uri = $(e.detail.form_id).action;
+ var form_uri = $(e.detail.form_id).action;
// TODO: validate event fields
// TODO: also send extra bank-defined form fields
- let params = {
+ var params = {
post_url: URI(form_uri).absoluteTo(document.location.href).href(),
// TODO: This should change in the future, we should not deal with the
// amount as a bank-specific string here.
@@ -47,14 +48,14 @@ document.addEventListener("taler-create-reserve", function (e) {
field_reserve_pub: $(e.detail.input_pub).name,
field_mint: $(e.detail.mint_rcv).name,
};
- let uri = URI(chrome.extension.getURL("pages/confirm-create-reserve.html"));
+ var uri = URI(chrome.extension.getURL("pages/confirm-create-reserve.html"));
document.location.href = uri.query(params).href();
});
document.addEventListener("taler-contract", function (e) {
// XXX: the merchant should just give us the parsed data ...
- let offer = JSON.parse(e.detail);
- let uri = URI(chrome.extension.getURL("pages/confirm-contract.html"));
- let params = {
+ var offer = JSON.parse(e.detail);
+ var uri = URI(chrome.extension.getURL("pages/confirm-contract.html"));
+ var params = {
offer: JSON.stringify(offer),
merchantPageUrl: document.location.href,
cookie: document.cookie,
@@ -63,23 +64,23 @@ document.addEventListener("taler-contract", function (e) {
});
document.addEventListener('taler-execute-payment', function (e) {
console.log("got taler-execute-payment in content page");
- let msg = {
+ var msg = {
type: "execute-payment",
detail: {
H_contract: e.detail.H_contract
},
};
- chrome.runtime.sendMessage(msg, (resp) => {
+ chrome.runtime.sendMessage(msg, function (resp) {
if (!resp.success) {
console.log("failure!");
return;
}
console.log("Making request to ", resp.payUrl);
- let r = new XMLHttpRequest();
+ var r = new XMLHttpRequest();
r.open('post', resp.payUrl);
r.send(JSON.stringify(resp.payReq));
- let detail = {};
- r.onload = (e) => {
+ var detail = {};
+ r.onload = function (e) {
switch (r.status) {
case 200:
detail.success = true;
@@ -101,3 +102,4 @@ document.addEventListener('taler-execute-payment', function (e) {
};
});
});
+//# sourceMappingURL=notify.js.map
\ No newline at end of file
diff --git a/extension/content_scripts/notify.ts b/extension/content_scripts/notify.ts
index e2ffaefa9..72d5a789d 100644
--- a/extension/content_scripts/notify.ts
+++ b/extension/content_scripts/notify.ts
@@ -17,6 +17,8 @@
// Script that is injected into pages in order to allow merchants pages to
// query the availability of Taler.
+///
+
"use strict";
document.addEventListener("taler-checkout-probe", function(e) {
diff --git a/extension/lib/commonHelpers.js b/extension/lib/commonHelpers.ts
similarity index 80%
rename from extension/lib/commonHelpers.js
rename to extension/lib/commonHelpers.ts
index fbaadee92..5c32e47c1 100644
--- a/extension/lib/commonHelpers.js
+++ b/extension/lib/commonHelpers.ts
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- (C) 2015 GNUnet e.V.
+ (C) 2016 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
@@ -14,16 +14,14 @@
TALER; see the file COPYING. If not, If not, see
*/
-"use strict";
-
Handlebars.registerHelper('prettyAmount', function (amount) {
- let v = amount.value + amount.fraction / 1e6;
- return v.toFixed(2) + " " + amount.currency;
+ let v = amount.value + amount.fraction / 1e6;
+ return v.toFixed(2) + " " + amount.currency;
});
Handlebars.registerHelper('prettyAmountNoCurrency', function (amount) {
- let v = amount.value + amount.fraction / 1e6;
- return v.toFixed(2);
+ let v = amount.value + amount.fraction / 1e6;
+ return v.toFixed(2);
});
Handlebars.registerHelper('objectStringifier', function (o) {
diff --git a/extension/decl/chrome/chrome.d.ts b/extension/lib/decl/chrome/chrome.d.ts
similarity index 100%
rename from extension/decl/chrome/chrome.d.ts
rename to extension/lib/decl/chrome/chrome.d.ts
diff --git a/extension/decl/filesystem/filesystem.d.ts b/extension/lib/decl/filesystem/filesystem.d.ts
similarity index 100%
rename from extension/decl/filesystem/filesystem.d.ts
rename to extension/lib/decl/filesystem/filesystem.d.ts
diff --git a/extension/decl/filewriter/filewriter.d.ts b/extension/lib/decl/filewriter/filewriter.d.ts
similarity index 100%
rename from extension/decl/filewriter/filewriter.d.ts
rename to extension/lib/decl/filewriter/filewriter.d.ts
diff --git a/extension/decl/handlebars/handlebars-1.0.0.d.ts b/extension/lib/decl/handlebars/handlebars-1.0.0.d.ts
similarity index 100%
rename from extension/decl/handlebars/handlebars-1.0.0.d.ts
rename to extension/lib/decl/handlebars/handlebars-1.0.0.d.ts
diff --git a/extension/decl/handlebars/handlebars.d.ts b/extension/lib/decl/handlebars/handlebars.d.ts
similarity index 100%
rename from extension/decl/handlebars/handlebars.d.ts
rename to extension/lib/decl/handlebars/handlebars.d.ts
diff --git a/extension/decl/jquery/jquery.d.ts b/extension/lib/decl/jquery/jquery.d.ts
similarity index 100%
rename from extension/decl/jquery/jquery.d.ts
rename to extension/lib/decl/jquery/jquery.d.ts
diff --git a/extension/lib/decl/lib.es6.d.ts b/extension/lib/decl/lib.es6.d.ts
new file mode 100644
index 000000000..ef3399ba8
--- /dev/null
+++ b/extension/lib/decl/lib.es6.d.ts
@@ -0,0 +1,18634 @@
+/*! *****************************************************************************
+Copyright (c) Microsoft Corporation. All rights reserved.
+Licensed under the Apache License, Version 2.0 (the "License"); you may not use
+this file except in compliance with the License. You may obtain a copy of the
+License at http://www.apache.org/licenses/LICENSE-2.0
+
+THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
+WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
+MERCHANTABLITY OR NON-INFRINGEMENT.
+
+See the Apache Version 2.0 License for specific language governing permissions
+and limitations under the License.
+***************************************************************************** */
+
+declare type PropertyKey = string | number | symbol;
+
+interface Symbol {
+ /** Returns a string representation of an object. */
+ toString(): string;
+
+ /** Returns the primitive value of the specified object. */
+ valueOf(): Object;
+
+ [Symbol.toStringTag]: "Symbol";
+}
+
+interface SymbolConstructor {
+ /**
+ * A reference to the prototype.
+ */
+ prototype: Symbol;
+
+ /**
+ * Returns a new unique Symbol value.
+ * @param description Description of the new Symbol object.
+ */
+ (description?: string|number): symbol;
+
+ /**
+ * Returns a Symbol object from the global symbol registry matching the given key if found.
+ * Otherwise, returns a new symbol with this key.
+ * @param key key to search for.
+ */
+ for(key: string): symbol;
+
+ /**
+ * Returns a key from the global symbol registry matching the given Symbol if found.
+ * Otherwise, returns a undefined.
+ * @param sym Symbol to find the key for.
+ */
+ keyFor(sym: symbol): string;
+
+ // Well-known Symbols
+
+ /**
+ * A method that determines if a constructor object recognizes an object as one of the
+ * constructor’s instances. Called by the semantics of the instanceof operator.
+ */
+ hasInstance: symbol;
+
+ /**
+ * A Boolean value that if true indicates that an object should flatten to its array elements
+ * by Array.prototype.concat.
+ */
+ isConcatSpreadable: symbol;
+
+ /**
+ * A method that returns the default iterator for an object. Called by the semantics of the
+ * for-of statement.
+ */
+ iterator: symbol;
+
+ /**
+ * A regular expression method that matches the regular expression against a string. Called
+ * by the String.prototype.match method.
+ */
+ match: symbol;
+
+ /**
+ * A regular expression method that replaces matched substrings of a string. Called by the
+ * String.prototype.replace method.
+ */
+ replace: symbol;
+
+ /**
+ * A regular expression method that returns the index within a string that matches the
+ * regular expression. Called by the String.prototype.search method.
+ */
+ search: symbol;
+
+ /**
+ * A function valued property that is the constructor function that is used to create
+ * derived objects.
+ */
+ species: symbol;
+
+ /**
+ * A regular expression method that splits a string at the indices that match the regular
+ * expression. Called by the String.prototype.split method.
+ */
+ split: symbol;
+
+ /**
+ * A method that converts an object to a corresponding primitive value.
+ * Called by the ToPrimitive abstract operation.
+ */
+ toPrimitive: symbol;
+
+ /**
+ * A String value that is used in the creation of the default string description of an object.
+ * Called by the built-in method Object.prototype.toString.
+ */
+ toStringTag: symbol;
+
+ /**
+ * An Object whose own property names are property names that are excluded from the 'with'
+ * environment bindings of the associated objects.
+ */
+ unscopables: symbol;
+}
+declare var Symbol: SymbolConstructor;
+
+interface Object {
+ /**
+ * Determines whether an object has a property with the specified name.
+ * @param v A property name.
+ */
+ hasOwnProperty(v: PropertyKey): boolean;
+
+ /**
+ * Determines whether a specified property is enumerable.
+ * @param v A property name.
+ */
+ propertyIsEnumerable(v: PropertyKey): boolean;
+}
+
+interface ObjectConstructor {
+ /**
+ * Copy the values of all of the enumerable own properties from one or more source objects to a
+ * target object. Returns the target object.
+ * @param target The target object to copy to.
+ * @param source The source object from which to copy properties.
+ */
+ assign(target: T, source: U): T & U;
+
+ /**
+ * Copy the values of all of the enumerable own properties from one or more source objects to a
+ * target object. Returns the target object.
+ * @param target The target object to copy to.
+ * @param source1 The first source object from which to copy properties.
+ * @param source2 The second source object from which to copy properties.
+ */
+ assign(target: T, source1: U, source2: V): T & U & V;
+
+ /**
+ * Copy the values of all of the enumerable own properties from one or more source objects to a
+ * target object. Returns the target object.
+ * @param target The target object to copy to.
+ * @param source1 The first source object from which to copy properties.
+ * @param source2 The second source object from which to copy properties.
+ * @param source3 The third source object from which to copy properties.
+ */
+ assign(target: T, source1: U, source2: V, source3: W): T & U & V & W;
+
+ /**
+ * Copy the values of all of the enumerable own properties from one or more source objects to a
+ * target object. Returns the target object.
+ * @param target The target object to copy to.
+ * @param sources One or more source objects from which to copy properties
+ */
+ assign(target: any, ...sources: any[]): any;
+
+ /**
+ * Returns an array of all symbol properties found directly on object o.
+ * @param o Object to retrieve the symbols from.
+ */
+ getOwnPropertySymbols(o: any): symbol[];
+
+ /**
+ * Returns true if the values are the same value, false otherwise.
+ * @param value1 The first value.
+ * @param value2 The second value.
+ */
+ is(value1: any, value2: any): boolean;
+
+ /**
+ * Sets the prototype of a specified object o to object proto or null. Returns the object o.
+ * @param o The object to change its prototype.
+ * @param proto The value of the new prototype or null.
+ */
+ setPrototypeOf(o: any, proto: any): any;
+
+ /**
+ * Gets the own property descriptor of the specified object.
+ * An own property descriptor is one that is defined directly on the object and is not
+ * inherited from the object's prototype.
+ * @param o Object that contains the property.
+ * @param p Name of the property.
+ */
+ getOwnPropertyDescriptor(o: any, propertyKey: PropertyKey): PropertyDescriptor;
+
+ /**
+ * Adds a property to an object, or modifies attributes of an existing property.
+ * @param o Object on which to add or modify the property. This can be a native JavaScript
+ * object (that is, a user-defined object or a built in object) or a DOM object.
+ * @param p The property name.
+ * @param attributes Descriptor for the property. It can be for a data property or an accessor
+ * property.
+ */
+ defineProperty(o: any, propertyKey: PropertyKey, attributes: PropertyDescriptor): any;
+}
+
+interface Function {
+ /**
+ * Returns the name of the function. Function names are read-only and can not be changed.
+ */
+ name: string;
+
+ /**
+ * Determines whether the given value inherits from this function if this function was used
+ * as a constructor function.
+ *
+ * A constructor function can control which objects are recognized as its instances by
+ * 'instanceof' by overriding this method.
+ */
+ [Symbol.hasInstance](value: any): boolean;
+}
+
+interface NumberConstructor {
+ /**
+ * The value of Number.EPSILON is the difference between 1 and the smallest value greater than 1
+ * that is representable as a Number value, which is approximately:
+ * 2.2204460492503130808472633361816 x 10−16.
+ */
+ EPSILON: number;
+
+ /**
+ * Returns true if passed value is finite.
+ * Unlike the global isFininte, Number.isFinite doesn't forcibly convert the parameter to a
+ * number. Only finite values of the type number, result in true.
+ * @param number A numeric value.
+ */
+ isFinite(number: number): boolean;
+
+ /**
+ * Returns true if the value passed is an integer, false otherwise.
+ * @param number A numeric value.
+ */
+ isInteger(number: number): boolean;
+
+ /**
+ * Returns a Boolean value that indicates whether a value is the reserved value NaN (not a
+ * number). Unlike the global isNaN(), Number.isNaN() doesn't forcefully convert the parameter
+ * to a number. Only values of the type number, that are also NaN, result in true.
+ * @param number A numeric value.
+ */
+ isNaN(number: number): boolean;
+
+ /**
+ * Returns true if the value passed is a safe integer.
+ * @param number A numeric value.
+ */
+ isSafeInteger(number: number): boolean;
+
+ /**
+ * The value of the largest integer n such that n and n + 1 are both exactly representable as
+ * a Number value.
+ * The value of Number.MIN_SAFE_INTEGER is 9007199254740991 2^53 − 1.
+ */
+ MAX_SAFE_INTEGER: number;
+
+ /**
+ * The value of the smallest integer n such that n and n − 1 are both exactly representable as
+ * a Number value.
+ * The value of Number.MIN_SAFE_INTEGER is −9007199254740991 (−(2^53 − 1)).
+ */
+ MIN_SAFE_INTEGER: number;
+
+ /**
+ * Converts a string to a floating-point number.
+ * @param string A string that contains a floating-point number.
+ */
+ parseFloat(string: string): number;
+
+ /**
+ * Converts A string to an integer.
+ * @param s A string to convert into a number.
+ * @param radix A value between 2 and 36 that specifies the base of the number in numString.
+ * If this argument is not supplied, strings with a prefix of '0x' are considered hexadecimal.
+ * All other strings are considered decimal.
+ */
+ parseInt(string: string, radix?: number): number;
+}
+
+interface Array {
+ /** Iterator */
+ [Symbol.iterator](): IterableIterator;
+
+ /**
+ * Returns an object whose properties have the value 'true'
+ * when they will be absent when used in a 'with' statement.
+ */
+ [Symbol.unscopables](): {
+ copyWithin: boolean;
+ entries: boolean;
+ fill: boolean;
+ find: boolean;
+ findIndex: boolean;
+ keys: boolean;
+ values: boolean;
+ };
+
+ /**
+ * Returns an array of key, value pairs for every entry in the array
+ */
+ entries(): IterableIterator<[number, T]>;
+
+ /**
+ * Returns an list of keys in the array
+ */
+ keys(): IterableIterator;
+
+ /**
+ * Returns an list of values in the array
+ */
+ values(): IterableIterator;
+
+ /**
+ * Returns the value of the first element in the array where predicate is true, and undefined
+ * otherwise.
+ * @param predicate find calls predicate once for each element of the array, in ascending
+ * order, until it finds one where predicate returns true. If such an element is found, find
+ * immediately returns that element value. Otherwise, find returns undefined.
+ * @param thisArg If provided, it will be used as the this value for each invocation of
+ * predicate. If it is not provided, undefined is used instead.
+ */
+ find(predicate: (value: T, index: number, obj: Array) => boolean, thisArg?: any): T;
+
+ /**
+ * Returns the index of the first element in the array where predicate is true, and undefined
+ * otherwise.
+ * @param predicate find calls predicate once for each element of the array, in ascending
+ * order, until it finds one where predicate returns true. If such an element is found, find
+ * immediately returns that element value. Otherwise, find returns undefined.
+ * @param thisArg If provided, it will be used as the this value for each invocation of
+ * predicate. If it is not provided, undefined is used instead.
+ */
+ findIndex(predicate: (value: T) => boolean, thisArg?: any): number;
+
+ /**
+ * Returns the this object after filling the section identified by start and end with value
+ * @param value value to fill array section with
+ * @param start index to start filling the array at. If start is negative, it is treated as
+ * length+start where length is the length of the array.
+ * @param end index to stop filling the array at. If end is negative, it is treated as
+ * length+end.
+ */
+ fill(value: T, start?: number, end?: number): T[];
+
+ /**
+ * Returns the this object after copying a section of the array identified by start and end
+ * to the same array starting at position target
+ * @param target If target is negative, it is treated as length+target where length is the
+ * length of the array.
+ * @param start If start is negative, it is treated as length+start. If end is negative, it
+ * is treated as length+end.
+ * @param end If not specified, length of the this object is used as its default value.
+ */
+ copyWithin(target: number, start: number, end?: number): T[];
+}
+
+interface IArguments {
+ /** Iterator */
+ [Symbol.iterator](): IterableIterator;
+}
+
+interface ArrayConstructor {
+ /**
+ * Creates an array from an array-like object.
+ * @param arrayLike An array-like object to convert to an array.
+ * @param mapfn A mapping function to call on every element of the array.
+ * @param thisArg Value of 'this' used to invoke the mapfn.
+ */
+ from(arrayLike: ArrayLike, mapfn: (v: T, k: number) => U, thisArg?: any): Array;
+
+ /**
+ * Creates an array from an iterable object.
+ * @param iterable An iterable object to convert to an array.
+ * @param mapfn A mapping function to call on every element of the array.
+ * @param thisArg Value of 'this' used to invoke the mapfn.
+ */
+ from(iterable: Iterable, mapfn: (v: T, k: number) => U, thisArg?: any): Array;
+
+ /**
+ * Creates an array from an array-like object.
+ * @param arrayLike An array-like object to convert to an array.
+ */
+ from(arrayLike: ArrayLike): Array;
+
+ /**
+ * Creates an array from an iterable object.
+ * @param iterable An iterable object to convert to an array.
+ */
+ from(iterable: Iterable): Array;
+
+ /**
+ * Returns a new array from a set of elements.
+ * @param items A set of elements to include in the new array object.
+ */
+ of(...items: T[]): Array;
+}
+
+interface String {
+ /** Iterator */
+ [Symbol.iterator](): IterableIterator;
+
+ /**
+ * Returns a nonnegative integer Number less than 1114112 (0x110000) that is the code point
+ * value of the UTF-16 encoded code point starting at the string element at position pos in
+ * the String resulting from converting this object to a String.
+ * If there is no element at that position, the result is undefined.
+ * If a valid UTF-16 surrogate pair does not begin at pos, the result is the code unit at pos.
+ */
+ codePointAt(pos: number): number;
+
+ /**
+ * Returns true if searchString appears as a substring of the result of converting this
+ * object to a String, at one or more positions that are
+ * greater than or equal to position; otherwise, returns false.
+ * @param searchString search string
+ * @param position If position is undefined, 0 is assumed, so as to search all of the String.
+ */
+ includes(searchString: string, position?: number): boolean;
+
+ /**
+ * Returns true if the sequence of elements of searchString converted to a String is the
+ * same as the corresponding elements of this object (converted to a String) starting at
+ * endPosition – length(this). Otherwise returns false.
+ */
+ endsWith(searchString: string, endPosition?: number): boolean;
+
+ /**
+ * Returns the String value result of normalizing the string into the normalization form
+ * named by form as specified in Unicode Standard Annex #15, Unicode Normalization Forms.
+ * @param form Applicable values: "NFC", "NFD", "NFKC", or "NFKD", If not specified default
+ * is "NFC"
+ */
+ normalize(form?: string): string;
+
+ /**
+ * Returns a String value that is made from count copies appended together. If count is 0,
+ * T is the empty String is returned.
+ * @param count number of copies to append
+ */
+ repeat(count: number): string;
+
+ /**
+ * Returns true if the sequence of elements of searchString converted to a String is the
+ * same as the corresponding elements of this object (converted to a String) starting at
+ * position. Otherwise returns false.
+ */
+ startsWith(searchString: string, position?: number): boolean;
+
+ // Overloads for objects with methods of well-known symbols.
+
+ /**
+ * Matches a string an object that supports being matched against, and returns an array containing the results of that search.
+ * @param matcher An object that supports being matched against.
+ */
+ match(matcher: { [Symbol.match](string: string): RegExpMatchArray; }): RegExpMatchArray;
+
+ /**
+ * Replaces text in a string, using an object that supports replacement within a string.
+ * @param searchValue A object can search for and replace matches within a string.
+ * @param replaceValue A string containing the text to replace for every successful match of searchValue in this string.
+ */
+ replace(searchValue: { [Symbol.replace](string: string, replaceValue: string): string; }, replaceValue: string): string;
+
+ /**
+ * Replaces text in a string, using an object that supports replacement within a string.
+ * @param searchValue A object can search for and replace matches within a string.
+ * @param replacer A function that returns the replacement text.
+ */
+ replace(searchValue: { [Symbol.replace](string: string, replacer: (substring: string, ...args: any[]) => string): string; }, replacer: (substring: string, ...args: any[]) => string): string;
+
+ /**
+ * Finds the first substring match in a regular expression search.
+ * @param searcher An object which supports searching within a string.
+ */
+ search(searcher: { [Symbol.search](string: string): number; }): number;
+
+ /**
+ * Split a string into substrings using the specified separator and return them as an array.
+ * @param splitter An object that can split a string.
+ * @param limit A value used to limit the number of elements returned in the array.
+ */
+ split(splitter: { [Symbol.split](string: string, limit?: number): string[]; }, limit?: number): string[];
+
+ /**
+ * Returns an HTML anchor element and sets the name attribute to the text value
+ * @param name
+ */
+ anchor(name: string): string;
+
+ /** Returns a HTML element */
+ big(): string;
+
+ /** Returns a