remove incomplete memory IDB implementation for now
Currently lives in its own branch, will be re-added to master once complete to avoid linting issues.
This commit is contained in:
parent
8b2f53e3ed
commit
1eec95e840
@ -61,7 +61,7 @@ msgstr ""
|
|||||||
msgid "The merchant%1$s offers you to purchase:\n"
|
msgid "The merchant%1$s offers you to purchase:\n"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/webex/pages/confirm-contract.tsx:232
|
#: src/webex/pages/confirm-contract.tsx:235
|
||||||
#, fuzzy, c-format
|
#, fuzzy, c-format
|
||||||
msgid "Confirm payment"
|
msgid "Confirm payment"
|
||||||
msgstr "Bezahlung bestätigen"
|
msgstr "Bezahlung bestätigen"
|
||||||
@ -270,38 +270,41 @@ msgid ""
|
|||||||
msgstr "Reserve (%1$s) mit %2$s bei %3$s erzeugt"
|
msgstr "Reserve (%1$s) mit %2$s bei %3$s erzeugt"
|
||||||
|
|
||||||
#: src/webex/pages/popup.tsx:368
|
#: src/webex/pages/popup.tsx:368
|
||||||
#, c-format
|
#, fuzzy, c-format
|
||||||
msgid "Merchant%1$soffered contract%2$s;\n"
|
msgid "Merchant%1$soffered contract%2$s.\n"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
"%1$s\n"
|
||||||
|
" möchte einen Vertrag über %2$s\n"
|
||||||
|
" mit Ihnen abschließen."
|
||||||
|
|
||||||
#: src/webex/pages/popup.tsx:378
|
#: src/webex/pages/popup.tsx:379
|
||||||
#, fuzzy, c-format
|
#, fuzzy, c-format
|
||||||
msgid "Withdrew%1$sfrom%2$s(%3$s).\n"
|
msgid "Withdrew%1$sfrom%2$s(%3$s).\n"
|
||||||
msgstr "Reserve (%1$s) mit %2$s bei %3$s erzeugt"
|
msgstr "Reserve (%1$s) mit %2$s bei %3$s erzeugt"
|
||||||
|
|
||||||
#: src/webex/pages/popup.tsx:388
|
#: src/webex/pages/popup.tsx:389
|
||||||
#, fuzzy, c-format
|
#, fuzzy, c-format
|
||||||
msgid ""
|
msgid ""
|
||||||
"Paid%1$sto merchant%2$s.\n"
|
"Paid%1$sto merchant%2$s.\n"
|
||||||
" (%3$s)\n"
|
" (%3$s)\n"
|
||||||
msgstr "Reserve (%1$s) mit %2$s bei %3$s erzeugt"
|
msgstr "Reserve (%1$s) mit %2$s bei %3$s erzeugt"
|
||||||
|
|
||||||
#: src/webex/pages/popup.tsx:398
|
#: src/webex/pages/popup.tsx:399
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "Merchant%1$sgave a refund over%2$s.\n"
|
msgid "Merchant%1$sgave a refund over%2$s.\n"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/webex/pages/popup.tsx:405
|
#: src/webex/pages/popup.tsx:406
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "Unknown event (%1$s)"
|
msgid "Unknown event (%1$s)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/webex/pages/popup.tsx:448
|
#: src/webex/pages/popup.tsx:449
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "Error: could not retrieve event history"
|
msgid "Error: could not retrieve event history"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/webex/pages/popup.tsx:473
|
#: src/webex/pages/popup.tsx:474
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "Your wallet has no events recorded."
|
msgid "Your wallet has no events recorded."
|
||||||
msgstr "Ihre Geldbörse verzeichnet keine Vorkommnisse."
|
msgstr "Ihre Geldbörse verzeichnet keine Vorkommnisse."
|
||||||
@ -341,13 +344,6 @@ msgstr ""
|
|||||||
msgid "Unknown Wire Detail"
|
msgid "Unknown Wire Detail"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, fuzzy
|
|
||||||
#~ msgid "The merchant%1$swants to enter a contract over%2$s with you.\n"
|
|
||||||
#~ msgstr ""
|
|
||||||
#~ "%1$s\n"
|
|
||||||
#~ " möchte einen Vertrag über %2$s\n"
|
|
||||||
#~ " mit Ihnen abschließen."
|
|
||||||
|
|
||||||
#, fuzzy
|
#, fuzzy
|
||||||
#~ msgid "You are about to purchase:"
|
#~ msgid "You are about to purchase:"
|
||||||
#~ msgstr "Sie sind dabei, Folgendes zu kaufen:"
|
#~ msgstr "Sie sind dabei, Folgendes zu kaufen:"
|
||||||
|
@ -61,7 +61,7 @@ msgstr ""
|
|||||||
msgid "The merchant%1$s offers you to purchase:\n"
|
msgid "The merchant%1$s offers you to purchase:\n"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/webex/pages/confirm-contract.tsx:232
|
#: src/webex/pages/confirm-contract.tsx:235
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "Confirm payment"
|
msgid "Confirm payment"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -271,37 +271,37 @@ msgstr ""
|
|||||||
|
|
||||||
#: src/webex/pages/popup.tsx:368
|
#: src/webex/pages/popup.tsx:368
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "Merchant%1$soffered contract%2$s;\n"
|
msgid "Merchant%1$soffered contract%2$s.\n"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/webex/pages/popup.tsx:378
|
#: src/webex/pages/popup.tsx:379
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "Withdrew%1$sfrom%2$s(%3$s).\n"
|
msgid "Withdrew%1$sfrom%2$s(%3$s).\n"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/webex/pages/popup.tsx:388
|
#: src/webex/pages/popup.tsx:389
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid ""
|
msgid ""
|
||||||
"Paid%1$sto merchant%2$s.\n"
|
"Paid%1$sto merchant%2$s.\n"
|
||||||
" (%3$s)\n"
|
" (%3$s)\n"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/webex/pages/popup.tsx:398
|
#: src/webex/pages/popup.tsx:399
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "Merchant%1$sgave a refund over%2$s.\n"
|
msgid "Merchant%1$sgave a refund over%2$s.\n"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/webex/pages/popup.tsx:405
|
#: src/webex/pages/popup.tsx:406
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "Unknown event (%1$s)"
|
msgid "Unknown event (%1$s)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/webex/pages/popup.tsx:448
|
#: src/webex/pages/popup.tsx:449
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "Error: could not retrieve event history"
|
msgid "Error: could not retrieve event history"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/webex/pages/popup.tsx:473
|
#: src/webex/pages/popup.tsx:474
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "Your wallet has no events recorded."
|
msgid "Your wallet has no events recorded."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
@ -61,7 +61,7 @@ msgstr ""
|
|||||||
msgid "The merchant%1$s offers you to purchase:\n"
|
msgid "The merchant%1$s offers you to purchase:\n"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/webex/pages/confirm-contract.tsx:232
|
#: src/webex/pages/confirm-contract.tsx:235
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "Confirm payment"
|
msgid "Confirm payment"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -271,37 +271,37 @@ msgstr ""
|
|||||||
|
|
||||||
#: src/webex/pages/popup.tsx:368
|
#: src/webex/pages/popup.tsx:368
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "Merchant%1$soffered contract%2$s;\n"
|
msgid "Merchant%1$soffered contract%2$s.\n"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/webex/pages/popup.tsx:378
|
#: src/webex/pages/popup.tsx:379
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "Withdrew%1$sfrom%2$s(%3$s).\n"
|
msgid "Withdrew%1$sfrom%2$s(%3$s).\n"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/webex/pages/popup.tsx:388
|
#: src/webex/pages/popup.tsx:389
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid ""
|
msgid ""
|
||||||
"Paid%1$sto merchant%2$s.\n"
|
"Paid%1$sto merchant%2$s.\n"
|
||||||
" (%3$s)\n"
|
" (%3$s)\n"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/webex/pages/popup.tsx:398
|
#: src/webex/pages/popup.tsx:399
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "Merchant%1$sgave a refund over%2$s.\n"
|
msgid "Merchant%1$sgave a refund over%2$s.\n"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/webex/pages/popup.tsx:405
|
#: src/webex/pages/popup.tsx:406
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "Unknown event (%1$s)"
|
msgid "Unknown event (%1$s)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/webex/pages/popup.tsx:448
|
#: src/webex/pages/popup.tsx:449
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "Error: could not retrieve event history"
|
msgid "Error: could not retrieve event history"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/webex/pages/popup.tsx:473
|
#: src/webex/pages/popup.tsx:474
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "Your wallet has no events recorded."
|
msgid "Your wallet has no events recorded."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
@ -61,7 +61,7 @@ msgstr ""
|
|||||||
msgid "The merchant%1$s offers you to purchase:\n"
|
msgid "The merchant%1$s offers you to purchase:\n"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/webex/pages/confirm-contract.tsx:232
|
#: src/webex/pages/confirm-contract.tsx:235
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "Confirm payment"
|
msgid "Confirm payment"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -271,37 +271,37 @@ msgstr ""
|
|||||||
|
|
||||||
#: src/webex/pages/popup.tsx:368
|
#: src/webex/pages/popup.tsx:368
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "Merchant%1$soffered contract%2$s;\n"
|
msgid "Merchant%1$soffered contract%2$s.\n"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/webex/pages/popup.tsx:378
|
#: src/webex/pages/popup.tsx:379
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "Withdrew%1$sfrom%2$s(%3$s).\n"
|
msgid "Withdrew%1$sfrom%2$s(%3$s).\n"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/webex/pages/popup.tsx:388
|
#: src/webex/pages/popup.tsx:389
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid ""
|
msgid ""
|
||||||
"Paid%1$sto merchant%2$s.\n"
|
"Paid%1$sto merchant%2$s.\n"
|
||||||
" (%3$s)\n"
|
" (%3$s)\n"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/webex/pages/popup.tsx:398
|
#: src/webex/pages/popup.tsx:399
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "Merchant%1$sgave a refund over%2$s.\n"
|
msgid "Merchant%1$sgave a refund over%2$s.\n"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/webex/pages/popup.tsx:405
|
#: src/webex/pages/popup.tsx:406
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "Unknown event (%1$s)"
|
msgid "Unknown event (%1$s)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/webex/pages/popup.tsx:448
|
#: src/webex/pages/popup.tsx:449
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "Error: could not retrieve event history"
|
msgid "Error: could not retrieve event history"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/webex/pages/popup.tsx:473
|
#: src/webex/pages/popup.tsx:474
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "Your wallet has no events recorded."
|
msgid "Your wallet has no events recorded."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
@ -156,8 +156,8 @@ strings['de'] = {
|
|||||||
"Started to withdraw\n %1$s from%2$s(%3$s).\n": [
|
"Started to withdraw\n %1$s from%2$s(%3$s).\n": [
|
||||||
"Reserve (%1$s) mit %2$s bei %3$s erzeugt"
|
"Reserve (%1$s) mit %2$s bei %3$s erzeugt"
|
||||||
],
|
],
|
||||||
"Merchant%1$soffered contract%2$s;\n": [
|
"Merchant%1$soffered contract%2$s.\n": [
|
||||||
""
|
"%1$s\n möchte einen Vertrag über %2$s\n mit Ihnen abschließen."
|
||||||
],
|
],
|
||||||
"Withdrew%1$sfrom%2$s(%3$s).\n": [
|
"Withdrew%1$sfrom%2$s(%3$s).\n": [
|
||||||
"Reserve (%1$s) mit %2$s bei %3$s erzeugt"
|
"Reserve (%1$s) mit %2$s bei %3$s erzeugt"
|
||||||
@ -342,7 +342,7 @@ strings['en-US'] = {
|
|||||||
"Started to withdraw\n %1$s from%2$s(%3$s).\n": [
|
"Started to withdraw\n %1$s from%2$s(%3$s).\n": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
"Merchant%1$soffered contract%2$s;\n": [
|
"Merchant%1$soffered contract%2$s.\n": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
"Withdrew%1$sfrom%2$s(%3$s).\n": [
|
"Withdrew%1$sfrom%2$s(%3$s).\n": [
|
||||||
@ -528,7 +528,7 @@ strings['fr'] = {
|
|||||||
"Started to withdraw\n %1$s from%2$s(%3$s).\n": [
|
"Started to withdraw\n %1$s from%2$s(%3$s).\n": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
"Merchant%1$soffered contract%2$s;\n": [
|
"Merchant%1$soffered contract%2$s.\n": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
"Withdrew%1$sfrom%2$s(%3$s).\n": [
|
"Withdrew%1$sfrom%2$s(%3$s).\n": [
|
||||||
@ -714,7 +714,7 @@ strings['it'] = {
|
|||||||
"Started to withdraw\n %1$s from%2$s(%3$s).\n": [
|
"Started to withdraw\n %1$s from%2$s(%3$s).\n": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
"Merchant%1$soffered contract%2$s;\n": [
|
"Merchant%1$soffered contract%2$s.\n": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
"Withdrew%1$sfrom%2$s(%3$s).\n": [
|
"Withdrew%1$sfrom%2$s(%3$s).\n": [
|
||||||
|
@ -61,7 +61,7 @@ msgstr ""
|
|||||||
msgid "The merchant%1$s offers you to purchase:\n"
|
msgid "The merchant%1$s offers you to purchase:\n"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/webex/pages/confirm-contract.tsx:232
|
#: src/webex/pages/confirm-contract.tsx:235
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "Confirm payment"
|
msgid "Confirm payment"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -271,37 +271,37 @@ msgstr ""
|
|||||||
|
|
||||||
#: src/webex/pages/popup.tsx:368
|
#: src/webex/pages/popup.tsx:368
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "Merchant%1$soffered contract%2$s;\n"
|
msgid "Merchant%1$soffered contract%2$s.\n"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/webex/pages/popup.tsx:378
|
#: src/webex/pages/popup.tsx:379
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "Withdrew%1$sfrom%2$s(%3$s).\n"
|
msgid "Withdrew%1$sfrom%2$s(%3$s).\n"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/webex/pages/popup.tsx:388
|
#: src/webex/pages/popup.tsx:389
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid ""
|
msgid ""
|
||||||
"Paid%1$sto merchant%2$s.\n"
|
"Paid%1$sto merchant%2$s.\n"
|
||||||
" (%3$s)\n"
|
" (%3$s)\n"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/webex/pages/popup.tsx:398
|
#: src/webex/pages/popup.tsx:399
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "Merchant%1$sgave a refund over%2$s.\n"
|
msgid "Merchant%1$sgave a refund over%2$s.\n"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/webex/pages/popup.tsx:405
|
#: src/webex/pages/popup.tsx:406
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "Unknown event (%1$s)"
|
msgid "Unknown event (%1$s)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/webex/pages/popup.tsx:448
|
#: src/webex/pages/popup.tsx:449
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "Error: could not retrieve event history"
|
msgid "Error: could not retrieve event history"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/webex/pages/popup.tsx:473
|
#: src/webex/pages/popup.tsx:474
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "Your wallet has no events recorded."
|
msgid "Your wallet has no events recorded."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
@ -1,114 +0,0 @@
|
|||||||
/*
|
|
||||||
This file is part of TALER
|
|
||||||
(C) 2017 Inria and 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, see <http://www.gnu.org/licenses/>
|
|
||||||
*/
|
|
||||||
|
|
||||||
import {test} from "ava";
|
|
||||||
import * as memidb from "./memidb";
|
|
||||||
|
|
||||||
test.cb("db open", (t) => {
|
|
||||||
let ncb = 0;
|
|
||||||
const idb = new memidb.MemoryIDBFactory();
|
|
||||||
const req = idb.open("testdb");
|
|
||||||
let called = false;
|
|
||||||
req.onupgradeneeded = (evt) => {
|
|
||||||
ncb += 1;
|
|
||||||
called = true;
|
|
||||||
t.is(req.result, evt.target);
|
|
||||||
t.is(evt.oldVersion, 0);
|
|
||||||
t.is(evt.newVersion, 1);
|
|
||||||
t.truthy(req.result);
|
|
||||||
t.pass();
|
|
||||||
};
|
|
||||||
req.onsuccess = (evt) => {
|
|
||||||
t.is(ncb, 1);
|
|
||||||
t.is(req.result, evt.target);
|
|
||||||
t.truthy(req.result);
|
|
||||||
t.end();
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
test.cb("store creation", (t) => {
|
|
||||||
const idb = new memidb.MemoryIDBFactory();
|
|
||||||
const req = idb.open("testdb");
|
|
||||||
req.onupgradeneeded = (evt) => {
|
|
||||||
const db: IDBDatabase = req.result;
|
|
||||||
|
|
||||||
const store1 = db.createObjectStore("b-store");
|
|
||||||
t.is(store1.name, "b-store");
|
|
||||||
t.deepEqual(Array.from(db.objectStoreNames), ["b-store"]);
|
|
||||||
|
|
||||||
const store2 = db.createObjectStore("a-store");
|
|
||||||
t.is(store2.name, "a-store");
|
|
||||||
t.deepEqual(Array.from(db.objectStoreNames), ["a-store", "b-store"]);
|
|
||||||
|
|
||||||
const store3 = db.createObjectStore("c-store");
|
|
||||||
t.is(store3.name, "c-store");
|
|
||||||
t.deepEqual(Array.from(db.objectStoreNames), ["a-store", "b-store", "c-store"]);
|
|
||||||
t.pass();
|
|
||||||
};
|
|
||||||
req.onsuccess = (evt) => {
|
|
||||||
t.end();
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
test.cb("put and get", (t) => {
|
|
||||||
const idb = new memidb.MemoryIDBFactory();
|
|
||||||
const req = idb.open("testdb");
|
|
||||||
req.onupgradeneeded = (evt) => {
|
|
||||||
const db: IDBDatabase = req.result;
|
|
||||||
const store1 = db.createObjectStore("mystore");
|
|
||||||
store1.put({answer: 42}, "a");
|
|
||||||
};
|
|
||||||
req.onsuccess = (evt) => {
|
|
||||||
t.end();
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
test("key path evaluation", (t) => {
|
|
||||||
const obj = {
|
|
||||||
a: {
|
|
||||||
b: {
|
|
||||||
c: 42,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
b: "hello",
|
|
||||||
"": "spam",
|
|
||||||
arr: ["foo", "bar"],
|
|
||||||
};
|
|
||||||
t.deepEqual(memidb.evaluateKeyPath(obj, ""), obj);
|
|
||||||
t.deepEqual(memidb.evaluateKeyPath(obj, "a.b.c"), 42);
|
|
||||||
t.deepEqual(memidb.evaluateKeyPath(obj, "a.b"), {c: 42});
|
|
||||||
t.deepEqual(memidb.evaluateKeyPath(obj, "foo"), undefined);
|
|
||||||
t.deepEqual(memidb.evaluateKeyPath(obj, ["a.b.c", "foo"]), undefined);
|
|
||||||
t.deepEqual(memidb.evaluateKeyPath(obj, ["a.b.c", "b"]), [42, "hello"]);
|
|
||||||
t.deepEqual(memidb.evaluateKeyPath(obj, "arr.0"), "foo");
|
|
||||||
t.deepEqual(memidb.evaluateKeyPath(obj, "."), "spam");
|
|
||||||
});
|
|
||||||
|
|
||||||
test("key path evaluation with replacement", (t) => {
|
|
||||||
const obj: any = {
|
|
||||||
a: {
|
|
||||||
b: {
|
|
||||||
c: 42,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
memidb.evaluateKeyPath(obj, "a.b.c", 24);
|
|
||||||
t.is(obj.a.b.c, 24);
|
|
||||||
memidb.evaluateKeyPath(obj, "a.b", 24);
|
|
||||||
t.is(obj.a.b, 24);
|
|
||||||
});
|
|
922
src/memidb.ts
922
src/memidb.ts
@ -1,922 +0,0 @@
|
|||||||
/*
|
|
||||||
This file is part of TALER
|
|
||||||
(C) 2017 Inria and 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, see <http://www.gnu.org/licenses/>
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* In-memory implementation of the IndexedDB interface.
|
|
||||||
*
|
|
||||||
* Transactions support rollback, but they are all run sequentially within the
|
|
||||||
* same MemoryIDBFactory.
|
|
||||||
*
|
|
||||||
* Every operation involves copying the whole database state, making it only
|
|
||||||
* feasible for small databases.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* work in progres ... */
|
|
||||||
/* tslint:disable */
|
|
||||||
|
|
||||||
|
|
||||||
const structuredClone = require("structured-clone");
|
|
||||||
|
|
||||||
|
|
||||||
interface Store {
|
|
||||||
name: string;
|
|
||||||
keyPath?: string | string[];
|
|
||||||
keyGenerator: number;
|
|
||||||
autoIncrement: boolean;
|
|
||||||
objects: { [primaryKey: string]: any };
|
|
||||||
indices: { [indexName: string]: Index };
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Index {
|
|
||||||
multiEntry: boolean;
|
|
||||||
unique: boolean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Map the index's key to the primary key.
|
|
||||||
*/
|
|
||||||
map: { [indexKey: string]: string[] };
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
interface Database {
|
|
||||||
name: string;
|
|
||||||
version: number;
|
|
||||||
stores: { [name: string]: Store };
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
interface Databases {
|
|
||||||
[name: string]: Database;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resolved promise, used to schedule various things
|
|
||||||
* by calling .next on it.
|
|
||||||
*/
|
|
||||||
const alreadyResolved = Promise.resolve();
|
|
||||||
|
|
||||||
|
|
||||||
class MyDomStringList extends Array<string> implements DOMStringList {
|
|
||||||
contains(s: string) {
|
|
||||||
for (let i = 0; i < this.length; i++) {
|
|
||||||
if (s === this[i]) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
item(i: number) {
|
|
||||||
return this[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//class MyKeyRange implements IDBKeyRange {
|
|
||||||
// static only(value: any): IDBKeyRange {
|
|
||||||
// return new MyKeyRange(value, value, false, false);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// static bound(lower: any, upper: any, lowerOpen: boolean = false, upperOpen: boolean = false) {
|
|
||||||
// return new MyKeyRange(lower, upper, lowerOpen, upperOpen);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// static lowerBound(lower: any, lowerOpen: boolean = false) {
|
|
||||||
// return new MyKeyRange(lower, undefined, lowerOpen, true);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// static upperBound(upper: any, upperOpen: boolean = false) {
|
|
||||||
// return new MyKeyRange(undefined, upper, true, upperOpen);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// constructor(public lower: any, public upper: any, public lowerOpen: boolean, public upperOpen: boolean) {
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Type guard for an IDBKeyRange.
|
|
||||||
*/
|
|
||||||
export function isKeyRange(obj: any): obj is IDBKeyRange {
|
|
||||||
return (typeof obj === "object" &&
|
|
||||||
"lower" in obj && "upper" in obj &&
|
|
||||||
"lowerOpen" in obj && "upperOpen" in obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class IndexHandle implements IDBIndex {
|
|
||||||
|
|
||||||
_unique: boolean;
|
|
||||||
_multiEntry: boolean;
|
|
||||||
|
|
||||||
get keyPath(): string | string[] {
|
|
||||||
throw Error("not implemented");
|
|
||||||
}
|
|
||||||
|
|
||||||
get name () {
|
|
||||||
return this.indexName;
|
|
||||||
}
|
|
||||||
|
|
||||||
get unique() {
|
|
||||||
return this._unique;
|
|
||||||
}
|
|
||||||
|
|
||||||
get multiEntry() {
|
|
||||||
return this._multiEntry;
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(public objectStore: MyObjectStore, public indexName: string) {
|
|
||||||
}
|
|
||||||
|
|
||||||
count(key?: IDBKeyRange | IDBValidKey): IDBRequest {
|
|
||||||
throw Error("not implemented");
|
|
||||||
}
|
|
||||||
|
|
||||||
get(key: IDBKeyRange | IDBValidKey): IDBRequest {
|
|
||||||
throw Error("not implemented");
|
|
||||||
}
|
|
||||||
|
|
||||||
getKey(key: IDBKeyRange | IDBValidKey): IDBRequest {
|
|
||||||
throw Error("not implemented");
|
|
||||||
}
|
|
||||||
|
|
||||||
openCursor(range?: IDBKeyRange | IDBValidKey, direction?: IDBCursorDirection): IDBRequest {
|
|
||||||
throw Error("not implemented");
|
|
||||||
}
|
|
||||||
|
|
||||||
openKeyCursor(range?: IDBKeyRange | IDBValidKey, direction?: IDBCursorDirection): IDBRequest {
|
|
||||||
throw Error("not implemented");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class MyRequest implements IDBRequest {
|
|
||||||
onerror: (this: IDBRequest, ev: Event) => any;
|
|
||||||
|
|
||||||
onsuccess: (this: IDBRequest, ev: Event) => any;
|
|
||||||
successHandlers: Array<(this: IDBRequest, ev: Event) => any> = [];
|
|
||||||
|
|
||||||
done: boolean = false;
|
|
||||||
_result: any;
|
|
||||||
|
|
||||||
constructor(public _transaction: Transaction, public runner: () => void) {
|
|
||||||
}
|
|
||||||
|
|
||||||
callSuccess(ev: Event) {
|
|
||||||
if (this.onsuccess) {
|
|
||||||
this.onsuccess(ev);
|
|
||||||
}
|
|
||||||
for (let h of this.successHandlers) {
|
|
||||||
h.call(this, ev);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
get error(): DOMException {
|
|
||||||
return (null as any) as DOMException;
|
|
||||||
}
|
|
||||||
|
|
||||||
get result(): any {
|
|
||||||
return this._result;
|
|
||||||
}
|
|
||||||
|
|
||||||
get source() {
|
|
||||||
// buggy type definitions don't allow null even though it's in
|
|
||||||
// the spec.
|
|
||||||
return (null as any) as (IDBObjectStore | IDBIndex | IDBCursor);
|
|
||||||
}
|
|
||||||
|
|
||||||
get transaction() {
|
|
||||||
return this._transaction;
|
|
||||||
}
|
|
||||||
|
|
||||||
dispatchEvent(evt: Event): boolean {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
get readyState() {
|
|
||||||
if (this.done) {
|
|
||||||
return "done";
|
|
||||||
}
|
|
||||||
return "pending";
|
|
||||||
}
|
|
||||||
|
|
||||||
removeEventListener(type: string,
|
|
||||||
listener?: EventListenerOrEventListenerObject,
|
|
||||||
options?: boolean | EventListenerOptions): void {
|
|
||||||
throw Error("not implemented");
|
|
||||||
}
|
|
||||||
|
|
||||||
addEventListener(type: string,
|
|
||||||
listener: EventListenerOrEventListenerObject,
|
|
||||||
useCapture?: boolean): void {
|
|
||||||
switch (type) {
|
|
||||||
case "success":
|
|
||||||
this.successHandlers.push(listener as any);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class OpenDBRequest extends MyRequest implements IDBOpenDBRequest {
|
|
||||||
onblocked: (this: IDBOpenDBRequest, ev: Event) => any;
|
|
||||||
|
|
||||||
onupgradeneeded: (this: IDBOpenDBRequest, ev: IDBVersionChangeEvent) => any;
|
|
||||||
upgradeneededHandlers: Array<(this: IDBOpenDBRequest, ev: IDBVersionChangeEvent) => any> = [];
|
|
||||||
|
|
||||||
callOnupgradeneeded(ev: IDBVersionChangeEvent) {
|
|
||||||
if (this.onupgradeneeded) {
|
|
||||||
this.onupgradeneeded(ev);
|
|
||||||
}
|
|
||||||
for (let h of this.upgradeneededHandlers) {
|
|
||||||
h.call(this, ev);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
removeEventListener(type: string,
|
|
||||||
listener?: EventListenerOrEventListenerObject,
|
|
||||||
options?: boolean | EventListenerOptions): void {
|
|
||||||
throw Error("not implemented");
|
|
||||||
}
|
|
||||||
|
|
||||||
addEventListener(type: string,
|
|
||||||
listener: EventListenerOrEventListenerObject,
|
|
||||||
useCapture?: boolean): void {
|
|
||||||
switch (type) {
|
|
||||||
case "upgradeneeded":
|
|
||||||
this.upgradeneededHandlers.push(listener as any);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
super.addEventListener(type, listener, useCapture);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function follow(x: any, s: string, replacement?: any): any {
|
|
||||||
if (s === "") {
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
const ptIdx = s.indexOf(".");
|
|
||||||
if (ptIdx < 0) {
|
|
||||||
const v = x[s];
|
|
||||||
if (replacement !== undefined) {
|
|
||||||
x[s] = replacement;
|
|
||||||
}
|
|
||||||
return v;
|
|
||||||
} else {
|
|
||||||
const identifier = s.substring(0, ptIdx);
|
|
||||||
const rest = s.substring(ptIdx + 1);
|
|
||||||
return follow(x[identifier], rest, replacement);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function evaluateKeyPath(x: any, path: string | string[], replacement?: any): any {
|
|
||||||
if (typeof path === "string") {
|
|
||||||
return follow(x, path, replacement);
|
|
||||||
} else if (Array.isArray(path)) {
|
|
||||||
const res: any[] = [];
|
|
||||||
for (let s of path) {
|
|
||||||
let c = follow(x, s, replacement);
|
|
||||||
if (c === undefined) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
res.push(c);
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
} else {
|
|
||||||
throw Error("invalid key path, must be string or array of strings");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function stringifyKey(key: any) {
|
|
||||||
return JSON.stringify(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isValidKey(key: any, memo: any[] = []) {
|
|
||||||
if (typeof key === "string" || typeof key === "number" || key instanceof Date) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (Array.isArray(key)) {
|
|
||||||
for (const element of key) {
|
|
||||||
if (!isValidKey(element, memo.concat([key]))) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
class MyObjectStore implements IDBObjectStore {
|
|
||||||
|
|
||||||
_keyPath: string | string[] | undefined;
|
|
||||||
_autoIncrement: boolean;
|
|
||||||
|
|
||||||
get indexNames() {
|
|
||||||
return new DOMStringList();
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(public transaction: Transaction, public storeName: string) {
|
|
||||||
this._keyPath = this.transaction.transactionDbData.stores[this.storeName].keyPath as (string | string[]);
|
|
||||||
this._autoIncrement = this.transaction.transactionDbData.stores[this.storeName].autoIncrement;
|
|
||||||
}
|
|
||||||
|
|
||||||
get keyPath(): string | string[] {
|
|
||||||
// TypeScript definitions are wrong here and don't permit a null keyPath
|
|
||||||
return this._keyPath as (string | string[]);
|
|
||||||
}
|
|
||||||
|
|
||||||
get name() {
|
|
||||||
return this.storeName;
|
|
||||||
}
|
|
||||||
|
|
||||||
get autoIncrement() {
|
|
||||||
return this._autoIncrement;
|
|
||||||
}
|
|
||||||
|
|
||||||
storeImpl(originalValue: any, key: any|undefined, allowExisting: boolean) {
|
|
||||||
if (this.transaction.mode === "readonly") {
|
|
||||||
throw Error();
|
|
||||||
}
|
|
||||||
if (!this.transaction.active) {
|
|
||||||
throw Error();
|
|
||||||
}
|
|
||||||
if (!this.transaction.transactionDbData.stores.hasOwnProperty(this.storeName)) {
|
|
||||||
throw Error("object store was deleted");
|
|
||||||
}
|
|
||||||
|
|
||||||
const store = this.transaction.transactionDbData.stores[this.storeName];
|
|
||||||
|
|
||||||
const value = structuredClone(originalValue);
|
|
||||||
|
|
||||||
if (this.keyPath) {
|
|
||||||
// we're dealine with in-line keys
|
|
||||||
if (key) {
|
|
||||||
throw Error("keys not allowed with in-line keys");
|
|
||||||
}
|
|
||||||
key = evaluateKeyPath(value, this.keyPath);
|
|
||||||
if (!key && !this.autoIncrement) {
|
|
||||||
throw Error("key path must evaluate to key for in-line stores without autoIncrement");
|
|
||||||
}
|
|
||||||
if (this.autoIncrement) {
|
|
||||||
if (key && typeof key === "number") {
|
|
||||||
store.keyGenerator = key + 1;
|
|
||||||
} else {
|
|
||||||
key = store.keyGenerator;
|
|
||||||
store.keyGenerator += 1;
|
|
||||||
evaluateKeyPath(value, this.keyPath, key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// we're dealing with out-of-line keys
|
|
||||||
if (!key && !this.autoIncrement) {
|
|
||||||
throw Error("key must be provided for out-of-line stores without autoIncrement");
|
|
||||||
}
|
|
||||||
key = this.transaction.transactionDbData.stores
|
|
||||||
if (this.autoIncrement) {
|
|
||||||
if (key && typeof key === "number") {
|
|
||||||
store.keyGenerator = key + 1;
|
|
||||||
} else {
|
|
||||||
key = store.keyGenerator;
|
|
||||||
store.keyGenerator += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const stringKey = stringifyKey(key);
|
|
||||||
|
|
||||||
if (store.objects.hasOwnProperty(stringKey) && !allowExisting) {
|
|
||||||
throw Error("key already exists");
|
|
||||||
}
|
|
||||||
|
|
||||||
store.objects[stringKey] = value;
|
|
||||||
|
|
||||||
const req = new MyRequest(this.transaction, () => {
|
|
||||||
});
|
|
||||||
return req;
|
|
||||||
}
|
|
||||||
|
|
||||||
put(value: any, key?: any): IDBRequest {
|
|
||||||
return this.storeImpl(value, key, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
add(value: any, key?: any): IDBRequest {
|
|
||||||
return this.storeImpl(value, key, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
delete(key: any): IDBRequest {
|
|
||||||
throw Error("not implemented");
|
|
||||||
}
|
|
||||||
|
|
||||||
get(key: any): IDBRequest {
|
|
||||||
throw Error("not implemented");
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteIndex(indexName: string) {
|
|
||||||
throw Error("not implemented");
|
|
||||||
}
|
|
||||||
|
|
||||||
clear(): IDBRequest {
|
|
||||||
throw Error("not implemented");
|
|
||||||
}
|
|
||||||
|
|
||||||
count(key?: any): IDBRequest {
|
|
||||||
throw Error("not implemented");
|
|
||||||
}
|
|
||||||
|
|
||||||
createIndex(name: string, keyPath: string | string[], optionalParameters?: IDBIndexParameters): IDBIndex {
|
|
||||||
throw Error("not implemented");
|
|
||||||
}
|
|
||||||
|
|
||||||
index(indexName: string): IDBIndex {
|
|
||||||
return new IndexHandle(this, indexName);
|
|
||||||
}
|
|
||||||
|
|
||||||
openCursor(range?: IDBKeyRange | IDBValidKey, direction?: IDBCursorDirection): IDBRequest {
|
|
||||||
throw Error("not implemented");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class Db implements IDBDatabase {
|
|
||||||
|
|
||||||
onabort: (this: IDBDatabase, ev: Event) => any;
|
|
||||||
onerror: (this: IDBDatabase, ev: Event) => any;
|
|
||||||
onversionchange: (ev: IDBVersionChangeEvent) => any;
|
|
||||||
|
|
||||||
_storeNames: string[] = [];
|
|
||||||
|
|
||||||
constructor(private _name: string, private _version: number, private factory: MemoryIDBFactory) {
|
|
||||||
for (let storeName in this.dbData.stores) {
|
|
||||||
if (this.dbData.stores.hasOwnProperty(storeName)) {
|
|
||||||
this._storeNames.push(storeName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this._storeNames.sort();
|
|
||||||
}
|
|
||||||
|
|
||||||
get dbData(): Database {
|
|
||||||
return this.factory.data[this._name];
|
|
||||||
}
|
|
||||||
|
|
||||||
set dbData(data) {
|
|
||||||
this.factory.data[this._name] = data;
|
|
||||||
}
|
|
||||||
|
|
||||||
get name() {
|
|
||||||
return this._name;
|
|
||||||
}
|
|
||||||
|
|
||||||
get objectStoreNames() {
|
|
||||||
return new MyDomStringList(...this._storeNames);
|
|
||||||
}
|
|
||||||
|
|
||||||
get version() {
|
|
||||||
return this._version;
|
|
||||||
}
|
|
||||||
|
|
||||||
close() {
|
|
||||||
}
|
|
||||||
|
|
||||||
createObjectStore(name: string, optionalParameters?: IDBObjectStoreParameters): IDBObjectStore {
|
|
||||||
let tx = this.factory.getTransaction();
|
|
||||||
if (tx.mode !== "versionchange") {
|
|
||||||
throw Error("invalid mode");
|
|
||||||
}
|
|
||||||
|
|
||||||
const td = tx.transactionDbData;
|
|
||||||
if (td.stores[name]) {
|
|
||||||
throw Error("object store already exists");
|
|
||||||
}
|
|
||||||
|
|
||||||
td.stores[name] = {
|
|
||||||
autoIncrement: !!(optionalParameters && optionalParameters.autoIncrement),
|
|
||||||
indices: {},
|
|
||||||
keyGenerator: 1,
|
|
||||||
name,
|
|
||||||
objects: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
this._storeNames.push(name);
|
|
||||||
this._storeNames.sort();
|
|
||||||
|
|
||||||
return new MyObjectStore(tx, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteObjectStore(name: string): void {
|
|
||||||
let tx = this.factory.getTransaction();
|
|
||||||
if (tx.mode !== "versionchange") {
|
|
||||||
throw Error("invalid mode");
|
|
||||||
}
|
|
||||||
|
|
||||||
const td = tx.transactionDbData;
|
|
||||||
if (td.stores[name]) {
|
|
||||||
throw Error("object store does not exists");
|
|
||||||
}
|
|
||||||
|
|
||||||
const idx = this._storeNames.indexOf(name);
|
|
||||||
if (idx < 0) {
|
|
||||||
throw Error();
|
|
||||||
}
|
|
||||||
this._storeNames.splice(idx, 1);
|
|
||||||
|
|
||||||
delete td.stores[name];
|
|
||||||
}
|
|
||||||
|
|
||||||
transaction(storeNames: string | string[], mode: IDBTransactionMode = "readonly"): IDBTransaction {
|
|
||||||
const tx = new Transaction(this._name, this, mode);
|
|
||||||
return tx;
|
|
||||||
}
|
|
||||||
|
|
||||||
dispatchEvent(evt: Event): boolean {
|
|
||||||
throw Error("not implemented");
|
|
||||||
}
|
|
||||||
|
|
||||||
removeEventListener(type: string,
|
|
||||||
listener?: EventListenerOrEventListenerObject,
|
|
||||||
options?: boolean | EventListenerOptions): void {
|
|
||||||
throw Error("not implemented");
|
|
||||||
}
|
|
||||||
|
|
||||||
addEventListener(type: string,
|
|
||||||
listener: EventListenerOrEventListenerObject,
|
|
||||||
useCapture?: boolean): void {
|
|
||||||
throw Error("not implemented");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum TransactionState {
|
|
||||||
Created = 1,
|
|
||||||
Running = 2,
|
|
||||||
Commited = 3,
|
|
||||||
Aborted = 4,
|
|
||||||
}
|
|
||||||
|
|
||||||
class Transaction implements IDBTransaction {
|
|
||||||
readonly READ_ONLY: string = "readonly";
|
|
||||||
readonly READ_WRITE: string = "readwrite";
|
|
||||||
readonly VERSION_CHANGE: string = "versionchange";
|
|
||||||
|
|
||||||
onabort: (this: IDBTransaction, ev: Event) => any;
|
|
||||||
onerror: (this: IDBTransaction, ev: Event) => any;
|
|
||||||
oncomplete: (this: IDBTransaction, ev: Event) => any;
|
|
||||||
|
|
||||||
completeHandlers: Array<(this: IDBTransaction, ev: Event) => any> = [];
|
|
||||||
|
|
||||||
state: TransactionState = TransactionState.Created;
|
|
||||||
|
|
||||||
_transactionDbData: Database|undefined;
|
|
||||||
|
|
||||||
constructor(public dbName: string, public dbHandle: Db, public _mode: IDBTransactionMode) {
|
|
||||||
}
|
|
||||||
|
|
||||||
get mode() {
|
|
||||||
return this._mode;
|
|
||||||
}
|
|
||||||
|
|
||||||
get active(): boolean {
|
|
||||||
return this.state === TransactionState.Running || this.state === TransactionState.Created;
|
|
||||||
}
|
|
||||||
|
|
||||||
start() {
|
|
||||||
if (this.state != TransactionState.Created) {
|
|
||||||
throw Error();
|
|
||||||
}
|
|
||||||
this.state = TransactionState.Running;
|
|
||||||
this._transactionDbData = structuredClone(this.dbHandle.dbData);
|
|
||||||
if (!this._transactionDbData) {
|
|
||||||
throw Error();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
commit() {
|
|
||||||
if (this.state != TransactionState.Running) {
|
|
||||||
throw Error();
|
|
||||||
}
|
|
||||||
if (!this._transactionDbData) {
|
|
||||||
throw Error();
|
|
||||||
}
|
|
||||||
this.state = TransactionState.Commited;
|
|
||||||
this.dbHandle.dbData = this._transactionDbData;
|
|
||||||
}
|
|
||||||
|
|
||||||
get error(): DOMException {
|
|
||||||
throw Error("not implemented");
|
|
||||||
}
|
|
||||||
|
|
||||||
get db() {
|
|
||||||
return this.dbHandle;
|
|
||||||
}
|
|
||||||
|
|
||||||
get transactionDbData() {
|
|
||||||
if (this.state != TransactionState.Running) {
|
|
||||||
throw Error();
|
|
||||||
}
|
|
||||||
let d = this._transactionDbData;
|
|
||||||
if (!d) {
|
|
||||||
throw Error();
|
|
||||||
}
|
|
||||||
return d;
|
|
||||||
}
|
|
||||||
|
|
||||||
abort() {
|
|
||||||
throw Error("not implemented");
|
|
||||||
}
|
|
||||||
|
|
||||||
objectStore(storeName: string): IDBObjectStore {
|
|
||||||
return new MyObjectStore(this, storeName);
|
|
||||||
}
|
|
||||||
|
|
||||||
dispatchEvent(evt: Event): boolean {
|
|
||||||
throw Error("not implemented");
|
|
||||||
}
|
|
||||||
|
|
||||||
removeEventListener(type: string,
|
|
||||||
listener?: EventListenerOrEventListenerObject,
|
|
||||||
options?: boolean | EventListenerOptions): void {
|
|
||||||
throw Error("not implemented");
|
|
||||||
}
|
|
||||||
|
|
||||||
addEventListener(type: string,
|
|
||||||
listener: EventListenerOrEventListenerObject,
|
|
||||||
useCapture?: boolean): void {
|
|
||||||
switch (type) {
|
|
||||||
case "complete":
|
|
||||||
this.completeHandlers.push(listener as any);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
callComplete(ev: Event) {
|
|
||||||
if (this.oncomplete) {
|
|
||||||
this.oncomplete(ev);
|
|
||||||
}
|
|
||||||
for (let h of this.completeHandlers) {
|
|
||||||
h.call(this, ev);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Polyfill for CustomEvent.
|
|
||||||
*/
|
|
||||||
class MyEvent implements Event {
|
|
||||||
readonly NONE: number = 0;
|
|
||||||
readonly CAPTURING_PHASE: number = 1;
|
|
||||||
readonly AT_TARGET: number = 2;
|
|
||||||
readonly BUBBLING_PHASE: number = 3;
|
|
||||||
|
|
||||||
_bubbles = false;
|
|
||||||
_cancelable = false;
|
|
||||||
_target: any;
|
|
||||||
_currentTarget: any;
|
|
||||||
_defaultPrevented: boolean = false;
|
|
||||||
_eventPhase: number = 0;
|
|
||||||
_timeStamp: number = 0;
|
|
||||||
_type: string;
|
|
||||||
|
|
||||||
constructor(typeArg: string, target: any) {
|
|
||||||
this._type = typeArg;
|
|
||||||
this._target = target;
|
|
||||||
}
|
|
||||||
|
|
||||||
get eventPhase() {
|
|
||||||
return this._eventPhase;
|
|
||||||
}
|
|
||||||
|
|
||||||
get returnValue() {
|
|
||||||
return this.defaultPrevented;
|
|
||||||
}
|
|
||||||
|
|
||||||
set returnValue(v: boolean) {
|
|
||||||
if (v) {
|
|
||||||
this.preventDefault();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
get isTrusted() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
get bubbles() {
|
|
||||||
return this._bubbles;
|
|
||||||
}
|
|
||||||
|
|
||||||
get cancelable() {
|
|
||||||
return this._cancelable;
|
|
||||||
}
|
|
||||||
|
|
||||||
set cancelBubble(v: boolean) {
|
|
||||||
if (v) {
|
|
||||||
this.stopPropagation();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
get defaultPrevented() {
|
|
||||||
return this._defaultPrevented;
|
|
||||||
}
|
|
||||||
|
|
||||||
stopPropagation() {
|
|
||||||
throw Error("not implemented");
|
|
||||||
}
|
|
||||||
|
|
||||||
get currentTarget() {
|
|
||||||
return this._currentTarget;
|
|
||||||
}
|
|
||||||
|
|
||||||
get target() {
|
|
||||||
return this._target;
|
|
||||||
}
|
|
||||||
|
|
||||||
preventDefault() {
|
|
||||||
}
|
|
||||||
|
|
||||||
get srcElement() {
|
|
||||||
return this.target;
|
|
||||||
}
|
|
||||||
|
|
||||||
get timeStamp() {
|
|
||||||
return this._timeStamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
get type() {
|
|
||||||
return this._type;
|
|
||||||
}
|
|
||||||
|
|
||||||
get scoped() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
initEvent(eventTypeArg: string, canBubbleArg: boolean, cancelableArg: boolean) {
|
|
||||||
if (this._eventPhase != 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._type = eventTypeArg;
|
|
||||||
this._bubbles = canBubbleArg;
|
|
||||||
this._cancelable = cancelableArg;
|
|
||||||
}
|
|
||||||
|
|
||||||
stopImmediatePropagation() {
|
|
||||||
throw Error("not implemented");
|
|
||||||
}
|
|
||||||
|
|
||||||
deepPath(): EventTarget[] {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class VersionChangeEvent extends MyEvent {
|
|
||||||
_newVersion: number|null;
|
|
||||||
_oldVersion: number;
|
|
||||||
constructor(oldVersion: number, newVersion: number|null, target: any) {
|
|
||||||
super("VersionChange", target);
|
|
||||||
this._oldVersion = oldVersion;
|
|
||||||
this._newVersion = newVersion;
|
|
||||||
}
|
|
||||||
|
|
||||||
get newVersion() {
|
|
||||||
return this._newVersion;
|
|
||||||
}
|
|
||||||
|
|
||||||
get oldVersion() {
|
|
||||||
return this._oldVersion;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export class MemoryIDBFactory implements IDBFactory {
|
|
||||||
data: Databases = {};
|
|
||||||
|
|
||||||
currentRequest: MyRequest|undefined;
|
|
||||||
|
|
||||||
scheduledRequests: MyRequest[] = [];
|
|
||||||
|
|
||||||
private addRequest(r: MyRequest) {
|
|
||||||
this.scheduledRequests.push(r);
|
|
||||||
if (this.currentRequest) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const runNext = (prevRequest?: MyRequest) => {
|
|
||||||
const nextRequest = this.scheduledRequests.shift();
|
|
||||||
if (nextRequest) {
|
|
||||||
const tx = nextRequest.transaction;
|
|
||||||
|
|
||||||
if (tx.state === TransactionState.Running) {
|
|
||||||
// Okay, we're continuing with the same transaction
|
|
||||||
} else if (tx.state === TransactionState.Created) {
|
|
||||||
tx.start();
|
|
||||||
} else {
|
|
||||||
throw Error();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.currentRequest = nextRequest;
|
|
||||||
this.currentRequest.runner();
|
|
||||||
this.currentRequest.done = true;
|
|
||||||
this.currentRequest = undefined;
|
|
||||||
runNext(nextRequest);
|
|
||||||
} else if (prevRequest) {
|
|
||||||
// We have no other request scheduled, so
|
|
||||||
// auto-commit the transaction that the
|
|
||||||
// previous request worked on.
|
|
||||||
let lastTx = prevRequest._transaction;
|
|
||||||
lastTx.commit();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
alreadyResolved.then(() => {
|
|
||||||
runNext();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the only transaction that is active right now
|
|
||||||
* or throw if no transaction is active.
|
|
||||||
*/
|
|
||||||
getTransaction() {
|
|
||||||
const req = this.currentRequest;
|
|
||||||
if (!req) {
|
|
||||||
throw Error();
|
|
||||||
}
|
|
||||||
return req.transaction;
|
|
||||||
}
|
|
||||||
|
|
||||||
cmp(a: any, b: any): number {
|
|
||||||
throw Error("not implemented");
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteDatabase(name: string): IDBOpenDBRequest {
|
|
||||||
throw Error("not implemented");
|
|
||||||
}
|
|
||||||
|
|
||||||
open(dbName: string, version?: number): IDBOpenDBRequest {
|
|
||||||
if (version !== undefined && version <= 0) {
|
|
||||||
throw Error("invalid version");
|
|
||||||
}
|
|
||||||
|
|
||||||
let upgradeNeeded = false;
|
|
||||||
let oldVersion: number;
|
|
||||||
let mydb: Database;
|
|
||||||
if (dbName in this.data) {
|
|
||||||
mydb = this.data[dbName];
|
|
||||||
if (!mydb) {
|
|
||||||
throw Error();
|
|
||||||
}
|
|
||||||
oldVersion = mydb.version;
|
|
||||||
if (version === undefined || version == mydb.version) {
|
|
||||||
// we can open without upgrading
|
|
||||||
} else if (version > mydb.version) {
|
|
||||||
upgradeNeeded = true;
|
|
||||||
mydb.version = version;
|
|
||||||
} else {
|
|
||||||
throw Error("version error");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
mydb = {
|
|
||||||
name: dbName,
|
|
||||||
stores: {},
|
|
||||||
version: (version || 1),
|
|
||||||
};
|
|
||||||
upgradeNeeded = true;
|
|
||||||
oldVersion = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.data[dbName] = mydb;
|
|
||||||
|
|
||||||
const db = new Db(dbName, mydb.version, this);
|
|
||||||
const tx = new Transaction(dbName, db, "versionchange");
|
|
||||||
|
|
||||||
const req = new OpenDBRequest(tx, () => {
|
|
||||||
req._result = db;
|
|
||||||
if (upgradeNeeded) {
|
|
||||||
let versionChangeEvt = new VersionChangeEvent(oldVersion, mydb.version, db);
|
|
||||||
req.callOnupgradeneeded(versionChangeEvt);
|
|
||||||
}
|
|
||||||
let successEvent = new MyEvent("success", db);
|
|
||||||
req.callSuccess(successEvent);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.addRequest(req);
|
|
||||||
|
|
||||||
return req;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Inject our IndexedDb implementation in the global namespace,
|
|
||||||
* potentially replacing an existing implementation.
|
|
||||||
*/
|
|
||||||
export function injectGlobals() {
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user