-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.ts
192 lines (166 loc) · 6.69 KB
/
index.ts
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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
/**
* © 2022 WavePlay <[email protected]>
*/
import { StashyBackend, StashyOptions } from './backend/_base'
import CookieBackend from './backend/cookie'
import LocalStorageBackend from './backend/local-storage'
import { Platform } from 'react-native'
import AsyncStorageBackend from './backend/async-storage'
export interface Logger {
debug: (...args: any[]) => void
error: (...args: any[]) => void
info: (...args: any[]) => void
warn: (...args: any[]) => void
trace: (...args: any[]) => void
}
interface StashyConstructor {
backend?:
| StashyBackend
| {
native?: StashyBackend
ssr?: StashyBackend
web?: StashyBackend
}
id?: string
logger?: Logger
}
export class Stashy {
// Instance ID
private _id?: string
// Possible backend variants to use for actual storage
// Ideally, only one of these should be active per instance, assuming that a given process doesn't need all
private _backendNative: StashyBackend
private _backendSsr: StashyBackend
private _backendWeb: StashyBackend
// Our handy little logger helps us track down storage and backend compatibility bugs
private _logger: Logger
constructor(options?: StashyConstructor) {
const { backend, id, logger } = options ?? {}
// IDs are often used to separate out data storage into different entry groups
// It's also useful for distinguishing between multiple instances of Stashy in logs
this._id = id
// Set logger to preference level
this._logger = logger
// Prepare our backing storage backend(s)
// Defaults only get assigned if currently running in that platform
this._backendNative = backend?.['native'] ?? (backend?.['_init'] ? backend : getDefaultBackend('native'))
this._backendSsr = backend?.['ssr'] ?? (backend?.['_init'] ? backend : getDefaultBackend('ssr'))
this._backendWeb = backend?.['web'] ?? (backend?.['_init'] ? backend : getDefaultBackend('web'))
this._backend()._init({ id })
}
private _backend(options?: StashyOptions): StashyBackend {
if (options?.backend === 'native') {
this._log('info', `forced native backend!`)
return this._backendNative
} else if (options?.backend === 'ssr') {
this._log('info', `forced server side backend!`)
return this._backendSsr
} else if (options?.backend === 'web') {
this._log('info', `forced web client backend!`)
return this._backendWeb
}
if (isNative()) {
this._log('info', `using native backend...`)
return this._backendNative
} else if (isSSR()) {
this._log('info', `using server side backend...`)
return this._backendSsr
} else {
this._log('info', `using web client backend...`)
return this._backendWeb
}
}
private _log(level: 'trace' | 'debug' | 'info' | 'warn' | 'error', message?: string, ...args: any[]) {
const id = `stashy${this._id ? '-' + this._id : ''}`
this._logger?.[level]?.(`[${id}] ` + message, ...args)
}
public clearAll(options?: StashyOptions) {
this._log('debug', `clearAll() with options:`, options)
this._backend(options).clearAll(options)
}
public delete(key: string, options?: StashyOptions) {
this._log('debug', `delete(${key}) with options:`, options)
this._backend(options).delete(key, options)
}
public get<T>(key: string, options?: StashyOptions): T {
this._log('debug', `get(${key}) with options:`, options)
const encodedValue = this._backend(options).getString(key, options)
let value = null
if (typeof encodedValue === 'string') {
value = encodedValue ? JSON.parse(encodedValue) : null
} else if (encodedValue) {
value = encodedValue
}
this._log('info', `get(${key}) value is:`, value)
return value ?? options?.default
}
public async getAsync<T>(key: string, options?: StashyOptions): Promise<T> {
this._log('debug', `getAsync(${key}) with options:`, options)
const encodedValue = await this._backend(options).getStringAsync(key, options)
const value = encodedValue ? JSON.parse(encodedValue) : null
this._log('info', `getAsync(${key}) value is:`, value)
return value ?? options?.default
}
public getBoolean(key: string, options?: StashyOptions): boolean {
this._log('debug', `getBoolean(${key}) with options:`, options)
const value = this._backend(options).getBoolean(key, options)
this._log('info', `getBoolean(${key}) value is:`, value)
return value ?? options?.default
}
public async getBooleanAsync(key: string, options?: StashyOptions): Promise<boolean> {
this._log('debug', `getBooleanAsync(${key}) with options:`, options)
const value = await this._backend(options).getBooleanAsync(key, options)
this._log('info', `getBooleanAsync(${key}) value is:`, value)
return value ?? options?.default
}
public getNumber(key: string, options?: StashyOptions): number {
this._log('debug', `getNumber(${key}) with options:`, options)
const value = this._backend(options).getNumber(key, options)
this._log('info', `getNumber(${key}) value is:`, value)
return value ?? options?.default
}
public async getNumberAsync(key: string, options?: StashyOptions): Promise<number> {
this._log('debug', `getNumberAsync(${key}) with options:`, options)
const value = await this._backend(options).getNumberAsync(key, options)
this._log('info', `getNumberAsync(${key}) value is:`, value)
return value ?? options?.default
}
public getString(key: string, options?: StashyOptions): string {
this._log('debug', `getString(${key}) with options:`, options)
const value = this._backend(options).getString(key, options)
this._log('info', `getString(${key}) value is:`, value)
return value ?? options?.default
}
public async getStringAsync(key: string, options?: StashyOptions): Promise<string> {
this._log('debug', `getStringAsync(${key}) with options:`, options)
const value = await this._backend(options).getStringAsync(key, options)
this._log('info', `getStringAsync(${key}) value is:`, value)
return value ?? options?.default
}
public set(key: string, value: boolean | number | string | any, options?: StashyOptions) {
this._log('debug', `set(${key}) with options:`, options)
const isObject = typeof value === 'object'
const encodedValue = isObject ? JSON.stringify(value) : value
this._backend(options).set(key, encodedValue, options)
this._log('info', `set(${key}) <<`, value)
}
}
const stashy = new Stashy()
export default stashy
function getDefaultBackend(platform: 'native' | 'ssr' | 'web'): StashyBackend {
if (isNative() && platform === 'native') {
return new AsyncStorageBackend()
} else if (isSSR() && platform === 'ssr') {
return new CookieBackend()
} else if (!isNative() && !isSSR() && platform === 'web') {
return new LocalStorageBackend()
} else {
return null
}
}
function isNative(): boolean {
return Platform.OS === 'android' || Platform.OS === 'ios'
}
function isSSR(): boolean {
return !isNative() && typeof window !== 'object'
}