Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
titaniumnetwork-dev
GitHub Repository: titaniumnetwork-dev/Ultraviolet
Path: blob/main/src/client/storage.js
304 views
1
import EventEmitter from "events";
2
import HookEvent from "./hook.js";
3
4
/**
5
* @typedef {import('./index').default} UVClient
6
*/
7
8
class StorageApi extends EventEmitter {
9
/**
10
*
11
* @param {UVClient} ctx
12
*/
13
constructor(ctx) {
14
super();
15
this.ctx = ctx;
16
this.window = ctx.window;
17
this.localStorage = this.window.localStorage || null;
18
this.sessionStorage = this.window.sessionStorage || null;
19
this.Storage = this.window.Storage || {};
20
this.storeProto = this.Storage.prototype || {};
21
this.getItem = this.storeProto.getItem || null;
22
this.setItem = this.storeProto.setItem || null;
23
this.removeItem = this.storeProto.removeItem || null;
24
this.clear = this.storeProto.clear || null;
25
this.key = this.storeProto.key || null;
26
this.methods = ["key", "getItem", "setItem", "removeItem", "clear"];
27
this.wrappers = new ctx.nativeMethods.Map();
28
}
29
overrideMethods() {
30
this.ctx.override(this.storeProto, "getItem", (target, that, args) => {
31
if (!args.length)
32
return target.apply(this.wrappers.get(that) || that, args);
33
let [name] = args;
34
35
const event = new HookEvent(
36
{ name },
37
target,
38
this.wrappers.get(that) || that
39
);
40
this.emit("getItem", event);
41
42
if (event.intercepted) return event.returnValue;
43
return event.target.call(event.that, event.data.name);
44
});
45
this.ctx.override(this.storeProto, "setItem", (target, that, args) => {
46
if (2 > args.length)
47
return target.apply(this.wrappers.get(that) || that, args);
48
let [name, value] = args;
49
50
const event = new HookEvent(
51
{ name, value },
52
target,
53
this.wrappers.get(that) || that
54
);
55
this.emit("setItem", event);
56
57
if (event.intercepted) return event.returnValue;
58
return event.target.call(event.that, event.data.name, event.data.value);
59
});
60
this.ctx.override(this.storeProto, "removeItem", (target, that, args) => {
61
if (!args.length)
62
return target.apply(this.wrappers.get(that) || that, args);
63
let [name] = args;
64
65
const event = new HookEvent(
66
{ name },
67
target,
68
this.wrappers.get(that) || that
69
);
70
this.emit("removeItem", event);
71
72
if (event.intercepted) return event.returnValue;
73
return event.target.call(event.that, event.data.name);
74
});
75
this.ctx.override(this.storeProto, "clear", (target, that) => {
76
const event = new HookEvent(
77
null,
78
target,
79
this.wrappers.get(that) || that
80
);
81
this.emit("clear", event);
82
83
if (event.intercepted) return event.returnValue;
84
return event.target.call(event.that);
85
});
86
this.ctx.override(this.storeProto, "key", (target, that, args) => {
87
if (!args.length)
88
return target.apply(this.wrappers.get(that) || that, args);
89
let [index] = args;
90
91
const event = new HookEvent(
92
{ index },
93
target,
94
this.wrappers.get(that) || that
95
);
96
this.emit("key", event);
97
98
if (event.intercepted) return event.returnValue;
99
return event.target.call(event.that, event.data.index);
100
});
101
}
102
overrideLength() {
103
this.ctx.overrideDescriptor(this.storeProto, "length", {
104
get: (target, that) => {
105
const event = new HookEvent(
106
{ length: target.call(this.wrappers.get(that) || that) },
107
target,
108
this.wrappers.get(that) || that
109
);
110
this.emit("length", event);
111
112
if (event.intercepted) return event.returnValue;
113
return event.data.length;
114
},
115
});
116
}
117
emulate(storage, obj = {}) {
118
this.ctx.nativeMethods.setPrototypeOf(obj, this.storeProto);
119
120
const proxy = new this.ctx.window.Proxy(obj, {
121
get: (target, prop) => {
122
if (prop in this.storeProto || typeof prop === "symbol")
123
return storage[prop];
124
125
const event = new HookEvent({ name: prop }, null, storage);
126
this.emit("get", event);
127
128
if (event.intercepted) return event.returnValue;
129
return storage[event.data.name];
130
},
131
set: (target, prop, value) => {
132
if (prop in this.storeProto || typeof prop === "symbol")
133
return (storage[prop] = value);
134
135
const event = new HookEvent({ name: prop, value }, null, storage);
136
this.emit("set", event);
137
138
if (event.intercepted) return event.returnValue;
139
140
return (storage[event.data.name] = event.data.value);
141
},
142
deleteProperty: (target, prop) => {
143
if (typeof prop === "symbol") return delete storage[prop];
144
145
const event = new HookEvent({ name: prop }, null, storage);
146
this.emit("delete", event);
147
148
if (event.intercepted) return event.returnValue;
149
150
return delete storage[event.data.name];
151
},
152
});
153
154
this.wrappers.set(proxy, storage);
155
this.ctx.nativeMethods.setPrototypeOf(proxy, this.storeProto);
156
157
return proxy;
158
}
159
}
160
161
export default StorageApi;
162
163