-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.ts
92 lines (73 loc) · 2.19 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
type DoubleRecord<K extends string | number | symbol> = { [P in K]: P }
type Detectors = {
[key: string]: () => boolean | void
}
type Config = {
revaluateContext: boolean
}
export const ERRORS = {
UNDETECTABLE_CONTEXT: `No compatible context has been detected`,
VALUE_NOT_FOUND: `No respective value found for active context`,
}
export const demand = (value: any, name?: string) => {
if (value === undefined) {
throw new Error(`required variable ${name ? `'${name}' ` : ''}has not been defined`)
}
return value
}
const runIfFunction = <T>(value: T): T extends (...args: any) => any ? ReturnType<T> : T =>
value instanceof Function && typeof value === 'function' ? value() : value
export default class DynamicEnvironment<T extends Detectors> {
private setup: Detectors
private config: Config
private pristine = true
private activeContext: keyof T | null = null
constructor(setup: T, config = { revaluateContext: false }) {
this.setup = setup
this.config = config
}
get contexts(): DoubleRecord<keyof T> {
return <DoubleRecord<keyof T>>(
Object.fromEntries(Object.keys(this.setup).map((key) => [key, key]))
)
}
revealContext() {
this.pristine = false
for (const [env, detector] of Object.entries(this.setup)) {
if (detector()) {
this.activeContext = env
return env
}
}
this.activeContext = null
return null
}
pick(data: Partial<Record<keyof T, unknown>>): unknown {
if (this.pristine || this.config.revaluateContext) {
this.revealContext()
}
if (!this.activeContext) {
throw new Error(ERRORS.UNDETECTABLE_CONTEXT)
}
if (!data.hasOwnProperty(this.activeContext)) {
console.warn(`The active context is '${String(this.activeContext)}'`)
throw new Error(ERRORS.VALUE_NOT_FOUND)
}
return data[this.activeContext]
}
tree(obj: Record<PropertyKey, any>): unknown {
const self = this
try {
return new Proxy(obj, {
get(target, name) {
if (!target.hasOwnProperty(name)) {
return target[name]
}
return self.tree(runIfFunction(target[name]))
},
})
} catch (e) {
return obj
}
}
}