[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[taler-wallet-core] branch master updated: idb: custom structured clone,
From: |
gnunet |
Subject: |
[taler-wallet-core] branch master updated: idb: custom structured clone, don't rely on typeson anymore |
Date: |
Wed, 24 Feb 2021 01:28:08 +0100 |
This is an automated email from the git hooks/post-receive script.
dold pushed a commit to branch master
in repository wallet-core.
The following commit(s) were added to refs/heads/master by this push:
new 3a336799 idb: custom structured clone, don't rely on typeson anymore
3a336799 is described below
commit 3a336799a08564ddc91713a397707b01ae0425c5
Author: Florian Dold <florian@dold.me>
AuthorDate: Wed Feb 24 01:28:02 2021 +0100
idb: custom structured clone, don't rely on typeson anymore
---
packages/idb-bridge/package.json | 4 +-
packages/idb-bridge/src/index.ts | 2 +
packages/idb-bridge/src/util/structuredClone.ts | 227 +++++++++++++++++++++++-
3 files changed, 228 insertions(+), 5 deletions(-)
diff --git a/packages/idb-bridge/package.json b/packages/idb-bridge/package.json
index 9db293b5..52bc872d 100644
--- a/packages/idb-bridge/package.json
+++ b/packages/idb-bridge/package.json
@@ -28,9 +28,7 @@
"typescript": "^4.1.3"
},
"dependencies": {
- "tslib": "^2.1.0",
- "typeson": "^5.18.2",
- "typeson-registry": "^1.0.0-alpha.38"
+ "tslib": "^2.1.0"
},
"ava": {
"require": [
diff --git a/packages/idb-bridge/src/index.ts b/packages/idb-bridge/src/index.ts
index 2cb8bcf1..a0bd8b86 100644
--- a/packages/idb-bridge/src/index.ts
+++ b/packages/idb-bridge/src/index.ts
@@ -115,3 +115,5 @@ export function shimIndexedDB(factory: BridgeIDBFactory):
void {
}
export * from "./idbtypes";
+
+export * from "./util/structuredClone";
\ No newline at end of file
diff --git a/packages/idb-bridge/src/util/structuredClone.ts
b/packages/idb-bridge/src/util/structuredClone.ts
index 9bbeb715..1c96d743 100644
--- a/packages/idb-bridge/src/util/structuredClone.ts
+++ b/packages/idb-bridge/src/util/structuredClone.ts
@@ -18,15 +18,238 @@
import Typeson from "typeson";
// @ts-ignore
import structuredCloningThrowing from
"typeson-registry/dist/presets/structured-cloning-throwing";
+import { DataCloneError } from "./errors";
const TSON = new Typeson().register(structuredCloningThrowing);
+const { toString: toStr } = {};
+const hasOwn = {}.hasOwnProperty;
+const getProto = Object.getPrototypeOf;
+const fnToString = hasOwn.toString;
+
+function toStringTag(val: any) {
+ return toStr.call(val).slice(8, -1);
+}
+
+function hasConstructorOf(a: any, b: any) {
+ if (!a || typeof a !== "object") {
+ return false;
+ }
+ const proto = getProto(a);
+ if (!proto) {
+ return b === null;
+ }
+ const Ctor = hasOwn.call(proto, "constructor") && proto.constructor;
+ if (typeof Ctor !== "function") {
+ return b === null;
+ }
+ if (b === Ctor) {
+ return true;
+ }
+ if (b !== null && fnToString.call(Ctor) === fnToString.call(b)) {
+ return true;
+ }
+ return false;
+}
+
+/**
+ *
+ * @param {any} val
+ * @returns {boolean}
+ */
+function isPlainObject(val: any): boolean {
+ if (!val || toStringTag(val) !== "Object") {
+ return false;
+ }
+
+ const proto = getProto(val);
+ if (!proto) {
+ // `Object.create(null)`
+ return true;
+ }
+
+ return hasConstructorOf(val, Object);
+}
+
+function isUserObject(val: any): boolean {
+ if (!val || toStringTag(val) !== "Object") {
+ return false;
+ }
+
+ const proto = getProto(val);
+ if (!proto) {
+ // `Object.create(null)`
+ return true;
+ }
+ return hasConstructorOf(val, Object) || isUserObject(proto);
+}
+
+function isRegExp(val: any): boolean {
+ return toStringTag(val) === "RegExp";
+}
+
+function internalEncapsulate(
+ val: any,
+ outRoot: any,
+ path: string[],
+ memo: Map<any, string[]>,
+ types: Array<[string[], string]>,
+): any {
+ const memoPath = memo.get(val);
+ if (memoPath) {
+ types.push([path, "ref"]);
+ return memoPath;
+ }
+ if (val === null) {
+ return null;
+ }
+ if (val === undefined) {
+ types.push([path, "undef"]);
+ return 0;
+ }
+ if (Array.isArray(val)) {
+ memo.set(val, path);
+ const outArr: any[] = [];
+ let special = false;
+ for (const x in val) {
+ const n = Number(x);
+ if (n < 0 || n >= val.length || Number.isNaN(n)) {
+ special = true;
+ break;
+ }
+ }
+ if (special) {
+ types.push([path, "array"]);
+ }
+ for (const x in val) {
+ const p = [...path, x];
+ outArr[x] = internalEncapsulate(val[x], outRoot, p, memo, types);
+ }
+ return outArr;
+ }
+ if (val instanceof Date) {
+ types.push([path, "date"]);
+ return val.getTime();
+ }
+ if (isUserObject(val) || isPlainObject(val)) {
+ memo.set(val, path);
+ const outObj: any = {};
+ for (const x in val) {
+ const p = [...path, x];
+ outObj[x] = internalEncapsulate(val[x], outRoot, p, memo, types);
+ }
+ return outObj;
+ }
+ if (typeof val === "bigint") {
+ types.push([path, "bigint"]);
+ return val.toString();
+ }
+ if (typeof val === "boolean") {
+ return val;
+ }
+ if (typeof val === "number") {
+ return val;
+ }
+ if (typeof val === "string") {
+ return val;
+ }
+ throw Error();
+}
+
+/**
+ * Encapsulate a cloneable value into a plain JSON object.
+ */
export function structuredEncapsulate(val: any): any {
- return TSON.encapsulate(val);
+ const outRoot = {};
+ const types: Array<[string[], string]> = [];
+ let res;
+ try {
+ res = internalEncapsulate(val, outRoot, [], new Map(), types);
+ } catch (e) {
+ throw new DataCloneError();
+ }
+ if (res === null) {
+ return res;
+ }
+ // We need to further encapsulate the outer layer
+ if (
+ Array.isArray(res) ||
+ typeof res !== "object" ||
+ "$" in res ||
+ "$types" in res
+ ) {
+ res = { $: res };
+ }
+ if (types.length > 0) {
+ res["$types"] = types;
+ }
+ return res;
+}
+
+export function internalStructuredRevive(val: any): any {
+ val = JSON.parse(JSON.stringify(val));
+ if (val === null) {
+ return null;
+ }
+ if (typeof val === "number") {
+ return val;
+ }
+ if (typeof val === "string") {
+ return val;
+ }
+ if (!isPlainObject(val)) {
+ throw Error();
+ }
+ let types = val.$types ?? [];
+ delete val.$types;
+ let outRoot: any;
+ if ("$" in val) {
+ outRoot = val.$;
+ } else {
+ outRoot = val;
+ }
+ function mutatePath(path: string[], f: (x: any) => any): void {
+ if (path.length == 0) {
+ outRoot = f(outRoot);
+ return;
+ }
+ let obj = outRoot;
+ for (let i = 0; i < path.length - 1; i++) {
+ const n = path[i];
+ if (!(n in obj)) {
+ obj[n] = {};
+ }
+ obj = obj[n];
+ }
+ const last = path[path.length - 1];
+ obj[last] = f(obj[last]);
+ }
+ 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");
+ }
+ }
+ return outRoot;
}
export function structuredRevive(val: any): any {
- return TSON.revive(val);
+ try {
+ return internalStructuredRevive(val);
+ } catch (e) {
+ throw new DataCloneError();
+ }
}
/**
--
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [taler-wallet-core] branch master updated: idb: custom structured clone, don't rely on typeson anymore,
gnunet <=