diff options
author | Florian Dold <florian@dold.me> | 2021-02-24 17:33:07 +0100 |
---|---|---|
committer | Florian Dold <florian@dold.me> | 2021-02-24 17:33:07 +0100 |
commit | 564e4f8710388ab2ae40c959c497f2e0260199ed (patch) | |
tree | 1d9f1b835bf6580977158f5eee133f8fa39f67d4 /packages/idb-bridge/src/util | |
parent | bc7956c2ba685e459c94204ca30f85eef881d0ac (diff) | |
download | wallet-core-564e4f8710388ab2ae40c959c497f2e0260199ed.tar.xz |
idb: encapsulate non-JSON data correctly
Diffstat (limited to 'packages/idb-bridge/src/util')
-rw-r--r-- | packages/idb-bridge/src/util/canInjectKey.ts | 50 | ||||
-rw-r--r-- | packages/idb-bridge/src/util/deepEquals.ts | 72 | ||||
-rw-r--r-- | packages/idb-bridge/src/util/injectKey.ts | 69 | ||||
-rw-r--r-- | packages/idb-bridge/src/util/makeStoreKeyValue.ts | 50 | ||||
-rw-r--r-- | packages/idb-bridge/src/util/structuredClone.test.ts | 39 | ||||
-rw-r--r-- | packages/idb-bridge/src/util/structuredClone.ts | 69 |
6 files changed, 126 insertions, 223 deletions
diff --git a/packages/idb-bridge/src/util/canInjectKey.ts b/packages/idb-bridge/src/util/canInjectKey.ts deleted file mode 100644 index 09ecbd3ad..000000000 --- a/packages/idb-bridge/src/util/canInjectKey.ts +++ /dev/null @@ -1,50 +0,0 @@ -/* - Copyright 2017 Jeremy Scheff - - 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 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - or implied. See the License for the specific language governing - permissions and limitations under the License. -*/ - -import { IDBKeyPath } from "../idbtypes"; - -// http://w3c.github.io/IndexedDB/#check-that-a-key-could-be-injected-into-a-value -const canInjectKey = (keyPath: IDBKeyPath, value: any) => { - if (Array.isArray(keyPath)) { - // tslint:disable-next-line max-line-length - throw new Error( - "The key paths used in this section are always strings and never sequences, since it is not possible to create a object store which has a key generator and also has a key path that is a sequence.", - ); - } - - const identifiers = keyPath.split("."); - if (identifiers.length === 0) { - throw new Error("Assert: identifiers is not empty"); - } - identifiers.pop(); - - for (const identifier of identifiers) { - if (typeof value !== "object" && !Array.isArray(value)) { - return false; - } - - const hop = value.hasOwnProperty(identifier); - if (!hop) { - return true; - } - - value = value[identifier]; - } - - return typeof value === "object" || Array.isArray(value); -}; - -export default canInjectKey; diff --git a/packages/idb-bridge/src/util/deepEquals.ts b/packages/idb-bridge/src/util/deepEquals.ts deleted file mode 100644 index bb7c0269c..000000000 --- a/packages/idb-bridge/src/util/deepEquals.ts +++ /dev/null @@ -1,72 +0,0 @@ -/* -Copyright (c) 2017 Evgeny Poberezkin - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -const isArray = Array.isArray; -const keyList = Object.keys; -const hasProp = Object.prototype.hasOwnProperty; - -export function deepEquals(a: any, b: any): boolean { - if (a === b) return true; - - if (a && b && typeof a == "object" && typeof b == "object") { - const arrA = isArray(a); - const arrB = isArray(b); - let i; - let length; - let key; - - if (arrA && arrB) { - length = a.length; - if (length != b.length) return false; - for (i = length; i-- !== 0; ) if (!deepEquals(a[i], b[i])) return false; - return true; - } - - if (arrA != arrB) return false; - - const dateA = a instanceof Date; - const dateB = b instanceof Date; - if (dateA != dateB) return false; - if (dateA && dateB) return a.getTime() == b.getTime(); - - const regexpA = a instanceof RegExp; - const regexpB = b instanceof RegExp; - if (regexpA != regexpB) return false; - if (regexpA && regexpB) return a.toString() == b.toString(); - - const keys = keyList(a); - length = keys.length; - - if (length !== keyList(b).length) return false; - - for (i = length; i-- !== 0; ) if (!hasProp.call(b, keys[i])) return false; - - for (i = length; i-- !== 0; ) { - key = keys[i]; - if (!deepEquals(a[key], b[key])) return false; - } - - return true; - } - - return a !== a && b !== b; -} diff --git a/packages/idb-bridge/src/util/injectKey.ts b/packages/idb-bridge/src/util/injectKey.ts deleted file mode 100644 index 02acfaa4c..000000000 --- a/packages/idb-bridge/src/util/injectKey.ts +++ /dev/null @@ -1,69 +0,0 @@ -/* - Copyright 2017 Jeremy Scheff - Copyright 2019 Florian Dold - - 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 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - or implied. See the License for the specific language governing - permissions and limitations under the License. -*/ - -import { IDBKeyPath, IDBValidKey } from "../idbtypes"; -import { structuredClone } from "./structuredClone"; - -export function injectKey( - keyPath: IDBKeyPath | IDBKeyPath[], - value: any, - key: IDBValidKey, -): any { - if (Array.isArray(keyPath)) { - // tslint:disable-next-line max-line-length - throw new Error( - "The key paths used in this section are always strings and never sequences, since it is not possible to create a object store which has a key generator and also has a key path that is a sequence.", - ); - } - - const newValue = structuredClone(value); - - // Position inside the new value where we'll place the key eventually. - let ptr = newValue; - - const identifiers = keyPath.split("."); - if (identifiers.length === 0) { - throw new Error("Assert: identifiers is not empty"); - } - - const lastIdentifier = identifiers.pop(); - - if (lastIdentifier === null || lastIdentifier === undefined) { - throw Error(); - } - - for (const identifier of identifiers) { - if (typeof ptr !== "object" && !Array.isArray(ptr)) { - throw new Error("can't inject key"); - } - - const hop = value.hasOwnProperty(identifier); - if (!hop) { - ptr[identifier] = {}; - } - - ptr = ptr[identifier]; - } - - if (!(typeof ptr === "object" || Array.isArray(ptr))) { - throw new Error("can't inject key"); - } - - ptr[lastIdentifier] = structuredClone(key); - - return newValue; -} diff --git a/packages/idb-bridge/src/util/makeStoreKeyValue.ts b/packages/idb-bridge/src/util/makeStoreKeyValue.ts index 442a69ff5..243e46e04 100644 --- a/packages/idb-bridge/src/util/makeStoreKeyValue.ts +++ b/packages/idb-bridge/src/util/makeStoreKeyValue.ts @@ -18,7 +18,6 @@ import { extractKey } from "./extractKey"; import { DataError } from "./errors"; import { valueToKey } from "./valueToKey"; import { structuredClone } from "./structuredClone"; -import { injectKey } from "./injectKey"; import { IDBKeyPath, IDBValidKey } from "../idbtypes"; export interface StoreKeyResult { @@ -27,6 +26,55 @@ export interface StoreKeyResult { value: any; } +export function injectKey( + keyPath: IDBKeyPath | IDBKeyPath[], + value: any, + key: IDBValidKey, +): any { + if (Array.isArray(keyPath)) { + throw new Error( + "The key paths used in this section are always strings and never sequences, since it is not possible to create a object store which has a key generator and also has a key path that is a sequence.", + ); + } + + const newValue = structuredClone(value); + + // Position inside the new value where we'll place the key eventually. + let ptr = newValue; + + const identifiers = keyPath.split("."); + if (identifiers.length === 0) { + throw new Error("Assert: identifiers is not empty"); + } + + const lastIdentifier = identifiers.pop(); + + if (lastIdentifier === null || lastIdentifier === undefined) { + throw Error(); + } + + for (const identifier of identifiers) { + if (typeof ptr !== "object" && !Array.isArray(ptr)) { + throw new Error("can't inject key"); + } + + const hop = value.hasOwnProperty(identifier); + if (!hop) { + ptr[identifier] = {}; + } + + ptr = ptr[identifier]; + } + + if (!(typeof ptr === "object" || Array.isArray(ptr))) { + throw new Error("can't inject key"); + } + + ptr[lastIdentifier] = structuredClone(key); + + return newValue; +} + export function makeStoreKeyValue( value: any, key: IDBValidKey | undefined, diff --git a/packages/idb-bridge/src/util/structuredClone.test.ts b/packages/idb-bridge/src/util/structuredClone.test.ts new file mode 100644 index 000000000..58a7f32c1 --- /dev/null +++ b/packages/idb-bridge/src/util/structuredClone.test.ts @@ -0,0 +1,39 @@ +/* + Copyright 2019 Florian Dold + + 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 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + or implied. See the License for the specific language governing + permissions and limitations under the License. +*/ + +import test, { ExecutionContext } from "ava"; +import { structuredClone } from "./structuredClone"; + +function checkClone(t: ExecutionContext, x: any): void { + t.deepEqual(structuredClone(x), x); +} + +test("structured clone", (t) => { + checkClone(t, "foo"); + checkClone(t, [1, 2]); + checkClone(t, { x1: "foo" }); + checkClone(t, new Date()); + checkClone(t, [new Date()]); + checkClone(t, undefined); + checkClone(t, [undefined]); +}); + +test("structured clone (cycles)", (t) => { + const obj1: any[] = [1, 2]; + obj1.push(obj1); + const obj1Clone = structuredClone(obj1); + t.is(obj1Clone, obj1Clone[2]); +}); diff --git a/packages/idb-bridge/src/util/structuredClone.ts b/packages/idb-bridge/src/util/structuredClone.ts index 215681a2d..4ba97dd7a 100644 --- a/packages/idb-bridge/src/util/structuredClone.ts +++ b/packages/idb-bridge/src/util/structuredClone.ts @@ -14,8 +14,6 @@ permissions and limitations under the License. */ -import { DataCloneError } from "./errors"; - const { toString: toStr } = {}; const hasOwn = {}.hasOwnProperty; const getProto = Object.getPrototypeOf; @@ -46,11 +44,6 @@ function hasConstructorOf(a: any, b: any) { return false; } -/** - * - * @param {any} val - * @returns {boolean} - */ function isPlainObject(val: any): boolean { if (!val || toStringTag(val) !== "Object") { return false; @@ -157,11 +150,7 @@ export function structuredEncapsulate(val: any): any { const outRoot = {}; const types: Array<[string[], string]> = []; let res; - try { - res = internalEncapsulate(val, outRoot, [], new Map(), types); - } catch (e) { - throw new DataCloneError(); - } + res = internalEncapsulate(val, outRoot, [], new Map(), types); if (res === null) { return res; } @@ -218,32 +207,50 @@ export function internalStructuredRevive(val: any): any { const last = path[path.length - 1]; obj[last] = f(obj[last]); } + function lookupPath(path: string[]): any { + let obj = outRoot; + for (const n of path) { + obj = obj[n]; + } + return obj; + } for (const [path, type] of types) { - if (type === "bigint") { - mutatePath(path, (x) => BigInt(x)); - } else if (type === "array") { - mutatePath(path, (x) => { - const newArr: any = []; - for (const k in x) { - newArr[k] = x[k]; - } - return newArr; - }); - } else if (type === "date") { - mutatePath(path, (x) => new Date(x)); - } else { - throw Error("type not implemented"); + switch (type) { + case "bigint": { + mutatePath(path, (x) => BigInt(x)); + break; + } + case "array": { + mutatePath(path, (x) => { + const newArr: any = []; + for (const k in x) { + newArr[k] = x[k]; + } + return newArr; + }); + break; + } + case "date": { + mutatePath(path, (x) => new Date(x)); + break; + } + case "undef": { + mutatePath(path, (x) => undefined); + break; + } + case "ref": { + mutatePath(path, (x) => lookupPath(x)); + break; + } + default: + throw Error(`type '${type}' not implemented`); } } return outRoot; } export function structuredRevive(val: any): any { - try { - return internalStructuredRevive(val); - } catch (e) { - throw new DataCloneError(); - } + return internalStructuredRevive(val); } /** |