-
Notifications
You must be signed in to change notification settings - Fork 1
/
index.js
146 lines (121 loc) · 3.25 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
const fs = require('fs')
const util = require('util')
const fileExists = util.promisify(fs.exists)
const readFile = util.promisify(fs.readFile)
const writeFile = util.promisify(fs.writeFile)
const apiByPath = {}
const apiByProxy = new Map()
let cleanupsRegistered = false
function cleanupAll() {
apiByProxy.forEach(api => api.closeSync())
}
async function init(path, opts) {
const period = (opts && opts.savePeriod) || 1000
let api = {}
let proxy = {}
let modified = false
let timerId
if (!path) {
throw new Error("path required")
}
if (apiByPath[path]) {
api = apiByPath[path]
proxy = api.proxy
return proxy
}
async function save() {
if (modified) {
modified = false
await writeFile(path, JSON.stringify(proxy))
}
}
function saveSync() {
if (modified) {
modified = false
fs.writeFileSync(path, JSON.stringify(proxy))
}
}
function _closeCommon() {
clearTimeout(timerId)
delete apiByPath[path]
apiByProxy.delete(proxy)
}
async function close() {
_closeCommon()
return await save()
}
function closeSync() {
_closeCommon()
return saveSync()
}
async function periodicSave() {
await save()
timerId = setTimeout(periodicSave, period)
}
function createProxy(object) {
return new Proxy(object, {
get(target, prop) {
return target[prop]
},
set(target, prop, value) {
if (value && typeof value == "object") {
target[prop] = createProxy(value)
}
else {
target[prop] = value
}
modified = true
return true
},
deleteProperty(target, key) {
if (Array.isArray(target)) {
target.splice(key, 1)
modified = true
}
else {
target[key] = undefined
delete target[key]
modified = true
}
return true
},
has(target, key) {
return key in target
},
})
}
function proxyRecurse(object) {
for (let key in object) {
if (object[key] && typeof(object[key]) == "object") {
object[key] = proxyRecurse(object[key])
}
}
return createProxy(object)
}
if (!cleanupsRegistered) {
// Try to save data on all kinds of exits.
["SIGINT", "SIGTERM", "exit"].forEach(ev => {
process.on(ev, cleanupAll)
})
cleanupsRegistered = true
}
if (await fileExists(path)) {
proxy = proxyRecurse(JSON.parse(await readFile(path)))
}
else {
proxy = createProxy({})
}
periodicSave()
api = {proxy, path, save, close, closeSync}
apiByProxy.set(proxy, api)
apiByPath[path] = api
return proxy
}
async function close(proxy) {
const obj = apiByProxy.get(proxy)
if (obj) {
await obj.close()
}
}
init.close = close
module.exports = init