@raubjo/architect 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +860 -0
- package/package.json +121 -0
- package/src/cache/cache.ts +46 -0
- package/src/cache/contract.ts +9 -0
- package/src/cache/manager.ts +110 -0
- package/src/cache/provider.ts +11 -0
- package/src/config/contract.ts +63 -0
- package/src/config/discovery.ts +99 -0
- package/src/config/env.global.d.ts +6 -0
- package/src/config/env.ts +68 -0
- package/src/config/index.ts +5 -0
- package/src/config/provider.ts +17 -0
- package/src/config/repository.ts +164 -0
- package/src/container/adapters/builtin.ts +323 -0
- package/src/container/contract.ts +43 -0
- package/src/container/runtime.ts +29 -0
- package/src/events/bus.ts +174 -0
- package/src/events/concerns/dispatchable.ts +10 -0
- package/src/events/provider.ts +9 -0
- package/src/events/types.ts +9 -0
- package/src/foundation/application.ts +136 -0
- package/src/foundation/current-application.ts +20 -0
- package/src/index.ts +58 -0
- package/src/log/contract.ts +21 -0
- package/src/log/drivers/console.ts +54 -0
- package/src/log/drivers/null.ts +23 -0
- package/src/log/drivers/stack.ts +46 -0
- package/src/log/manager.ts +76 -0
- package/src/log/provider.ts +11 -0
- package/src/react.ts +2 -0
- package/src/renderers/adapters/react.tsx +25 -0
- package/src/renderers/adapters/solid.tsx +26 -0
- package/src/renderers/adapters/svelte.ts +73 -0
- package/src/renderers/adapters/vue.ts +22 -0
- package/src/renderers/contract.ts +12 -0
- package/src/runtimes/react.tsx +81 -0
- package/src/runtimes/solid.tsx +47 -0
- package/src/runtimes/svelte.ts +17 -0
- package/src/runtimes/vue.ts +34 -0
- package/src/solid.ts +2 -0
- package/src/store/adapters/contract.ts +11 -0
- package/src/store/adapters/indexed-db.ts +187 -0
- package/src/store/adapters/local-storage.ts +48 -0
- package/src/store/adapters/memory.ts +35 -0
- package/src/store/manager.ts +68 -0
- package/src/store/provider.ts +10 -0
- package/src/store/store.ts +1 -0
- package/src/support/arr.ts +372 -0
- package/src/support/collection.ts +889 -0
- package/src/support/facades/cache.ts +6 -0
- package/src/support/facades/config.ts +6 -0
- package/src/support/facades/event.ts +6 -0
- package/src/support/facades/facade.ts +146 -0
- package/src/support/facades/index.ts +5 -0
- package/src/support/facades/log.ts +6 -0
- package/src/support/facades/store.ts +6 -0
- package/src/support/fluent.ts +56 -0
- package/src/support/globals.ts +8 -0
- package/src/support/lazy-collection.ts +341 -0
- package/src/support/manager.ts +53 -0
- package/src/support/num.ts +50 -0
- package/src/support/pipeline.ts +29 -0
- package/src/support/service-provider.ts +19 -0
- package/src/support/str.ts +682 -0
- package/src/svelte.ts +2 -0
- package/src/types/peer-deps.d.ts +10 -0
- package/src/vue.ts +2 -0
- package/tsconfig.json +15 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { makeFromCurrentApplication } from "../../foundation/current-application"
|
|
2
|
+
|
|
3
|
+
// Module-level state shared by all facades.
|
|
4
|
+
const macroRegistry = new Map<string, Map<string, (...args: unknown[]) => unknown>>()
|
|
5
|
+
const resolvedInstances = new Map<string, unknown>()
|
|
6
|
+
|
|
7
|
+
export function clearFacadeCache(): void {
|
|
8
|
+
resolvedInstances.clear()
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function flushAllMacros(): void {
|
|
12
|
+
macroRegistry.clear()
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function getMacros(accessor: string): Map<string, (...args: unknown[]) => unknown> {
|
|
16
|
+
let macros = macroRegistry.get(accessor)
|
|
17
|
+
if (!macros) {
|
|
18
|
+
macros = new Map()
|
|
19
|
+
macroRegistry.set(accessor, macros)
|
|
20
|
+
}
|
|
21
|
+
return macros
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
25
|
+
export interface FacadeInstance<T = unknown> {
|
|
26
|
+
getFacadeAccessor(): string
|
|
27
|
+
macro(name: string, fn: (instance: T, ...args: unknown[]) => unknown): void
|
|
28
|
+
hasMacro(name: string): boolean
|
|
29
|
+
flushMacros(): void
|
|
30
|
+
clearResolvedInstance(name: string): void
|
|
31
|
+
clearResolvedInstances(): void
|
|
32
|
+
callFacadeMethod(method: string, ...args: unknown[]): unknown
|
|
33
|
+
callFacadeMethod<R = unknown>(method: string, ...args: unknown[]): R
|
|
34
|
+
use(...args: unknown[]): unknown
|
|
35
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
36
|
+
[key: string]: any
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
40
|
+
export function createFacade<T = any>(accessor: string): FacadeInstance<T> {
|
|
41
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
42
|
+
let proxy: any
|
|
43
|
+
|
|
44
|
+
const facade: Record<string, unknown> = {
|
|
45
|
+
getFacadeAccessor(): string {
|
|
46
|
+
return accessor
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
macro(name: string, fn: (instance: T, ...args: unknown[]) => unknown): void {
|
|
50
|
+
getMacros(accessor).set(name, fn as (...args: unknown[]) => unknown)
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
hasMacro(name: string): boolean {
|
|
54
|
+
return getMacros(accessor).has(name)
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
flushMacros(): void {
|
|
58
|
+
getMacros(accessor).clear()
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
clearResolvedInstance(name: string): void {
|
|
62
|
+
resolvedInstances.delete(name)
|
|
63
|
+
},
|
|
64
|
+
|
|
65
|
+
clearResolvedInstances(): void {
|
|
66
|
+
resolvedInstances.clear()
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
callFacadeMethod<R = unknown>(method: string, ...args: unknown[]): R {
|
|
70
|
+
const macro = getMacros(accessor).get(method)
|
|
71
|
+
if (macro) {
|
|
72
|
+
const instance = resolveInstance<T>(accessor)
|
|
73
|
+
return macro(instance, ...args) as R
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const instance = resolveInstance<Record<string, (...a: unknown[]) => unknown>>(accessor)
|
|
77
|
+
const callable = instance[method]
|
|
78
|
+
if (typeof callable !== "function") {
|
|
79
|
+
throw new Error(`Method [${method}] does not exist on resolved facade instance.`)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return callable.apply(instance, args) as R
|
|
83
|
+
},
|
|
84
|
+
|
|
85
|
+
// use() returns the proxy instead of the instance's return value so callers can chain.
|
|
86
|
+
use(...args: unknown[]): unknown {
|
|
87
|
+
const instance = resolveInstance<Record<string, (...a: unknown[]) => unknown>>(accessor)
|
|
88
|
+
if (typeof instance.use === "function") {
|
|
89
|
+
instance.use(...args)
|
|
90
|
+
}
|
|
91
|
+
return proxy
|
|
92
|
+
},
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
96
|
+
proxy = new Proxy(facade, {
|
|
97
|
+
get(target, prop) {
|
|
98
|
+
if (prop in target) {
|
|
99
|
+
return target[prop as string]
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const name = String(prop)
|
|
103
|
+
|
|
104
|
+
// Macro check (takes precedence over instance methods).
|
|
105
|
+
const macros = getMacros(accessor)
|
|
106
|
+
const macro = macros.get(name)
|
|
107
|
+
if (macro) {
|
|
108
|
+
return (...args: unknown[]) => {
|
|
109
|
+
const instance = resolveInstance<T>(accessor)
|
|
110
|
+
return macro(instance, ...args)
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const instance = resolveInstance<Record<string, unknown>>(accessor)
|
|
115
|
+
const value = instance[prop as keyof typeof instance]
|
|
116
|
+
|
|
117
|
+
if (typeof value === "function") {
|
|
118
|
+
return (...args: unknown[]) => {
|
|
119
|
+
return (value as (...a: unknown[]) => unknown).apply(instance, args)
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return value
|
|
124
|
+
},
|
|
125
|
+
|
|
126
|
+
has(target, prop) {
|
|
127
|
+
if (prop in target) return true
|
|
128
|
+
const name = String(prop)
|
|
129
|
+
if (getMacros(accessor).has(name)) return true
|
|
130
|
+
const instance = resolveInstance<Record<string, unknown>>(accessor)
|
|
131
|
+
return name in instance
|
|
132
|
+
},
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
return proxy as FacadeInstance<T>
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function resolveInstance<T>(accessor: string): T {
|
|
139
|
+
if (resolvedInstances.has(accessor)) {
|
|
140
|
+
return resolvedInstances.get(accessor) as T
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const instance = makeFromCurrentApplication<T>(accessor)
|
|
144
|
+
resolvedInstances.set(accessor, instance)
|
|
145
|
+
return instance
|
|
146
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// biome-ignore lint/suspicious/noUnsafeDeclarationMerging: Proxy in constructor handles runtime property access; interface merging is intentional for typed T["key"] access
|
|
2
|
+
export class Fluent<T extends Record<string, unknown> = Record<string, unknown>> {
|
|
3
|
+
protected attributes: Record<string, unknown>
|
|
4
|
+
|
|
5
|
+
constructor(attributes: T = {} as T) {
|
|
6
|
+
this.attributes = { ...attributes }
|
|
7
|
+
|
|
8
|
+
// biome-ignore lint/correctness/noConstructorReturn: Creating a proxy for magic methods: `get()`
|
|
9
|
+
return new Proxy(this, {
|
|
10
|
+
get(target, prop, receiver) {
|
|
11
|
+
if (typeof prop === "string" && !(prop in target)) {
|
|
12
|
+
return target.attributes[prop]
|
|
13
|
+
}
|
|
14
|
+
return Reflect.get(target, prop, receiver)
|
|
15
|
+
},
|
|
16
|
+
}) as Fluent<T>
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
get(key: string): unknown
|
|
20
|
+
get<V>(key: string): V | null
|
|
21
|
+
get<V>(key: string, defaultValue: V): V
|
|
22
|
+
get<V>(key: string, defaultValue: V | null): V | null
|
|
23
|
+
get<V = unknown>(key: string, defaultValue?: V | null): unknown {
|
|
24
|
+
const fallback = defaultValue !== undefined ? defaultValue : null
|
|
25
|
+
const parts = key.split(".")
|
|
26
|
+
let current: unknown = this.attributes
|
|
27
|
+
for (const part of parts) {
|
|
28
|
+
if (current === null || current === undefined || typeof current !== "object") return fallback
|
|
29
|
+
current = (current as Record<string, unknown>)[part]
|
|
30
|
+
}
|
|
31
|
+
return current === undefined ? fallback : current
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
set(key: string, value: unknown): this {
|
|
35
|
+
const parts = key.split(".")
|
|
36
|
+
let current = this.attributes
|
|
37
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
38
|
+
const part = parts[i]
|
|
39
|
+
if (typeof current[part] !== "object" || current[part] === null) current[part] = {}
|
|
40
|
+
current = current[part] as Record<string, unknown>
|
|
41
|
+
}
|
|
42
|
+
current[parts[parts.length - 1]] = value
|
|
43
|
+
return this
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
has(key: string): boolean {
|
|
47
|
+
return this.get(key) !== null
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
toArray(): T {
|
|
51
|
+
return { ...this.attributes } as T
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
56
|
+
export interface Fluent<T extends Record<string, unknown> = Record<string, unknown>> extends T {}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export function registerGlobalHelpers(helpers: Record<string, unknown>): void {
|
|
2
|
+
const globalScope = globalThis as Record<string, unknown>
|
|
3
|
+
for (const [key, value] of Object.entries(helpers)) {
|
|
4
|
+
if (typeof globalScope[key] === "undefined") {
|
|
5
|
+
globalScope[key] = value
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
}
|
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
import { Collection } from "./collection"
|
|
2
|
+
|
|
3
|
+
export class LazyCollection<T> implements Iterable<T> {
|
|
4
|
+
protected constructor(protected readonly source: () => Iterable<T>) {}
|
|
5
|
+
|
|
6
|
+
static make<T>(source: (() => Iterable<T>) | Iterable<T>): LazyCollection<T> {
|
|
7
|
+
if (typeof source === "function") {
|
|
8
|
+
return new LazyCollection(source as () => Iterable<T>)
|
|
9
|
+
}
|
|
10
|
+
const captured = source
|
|
11
|
+
return new LazyCollection(() => captured)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
static times<U>(count: number, callback: (n: number) => U): LazyCollection<U> {
|
|
15
|
+
return new LazyCollection(function* () {
|
|
16
|
+
for (let i = 1; i <= count; i++) {
|
|
17
|
+
yield callback(i)
|
|
18
|
+
}
|
|
19
|
+
})
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
[Symbol.iterator](): Iterator<T> {
|
|
23
|
+
return this.source()[Symbol.iterator]()
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// ── Lazy (return LazyCollection) ──────────────────────────────────────────
|
|
27
|
+
|
|
28
|
+
map<U>(callback: (item: T, index: number) => U): LazyCollection<U> {
|
|
29
|
+
const self = this
|
|
30
|
+
return new LazyCollection(function* () {
|
|
31
|
+
let i = 0
|
|
32
|
+
for (const item of self) {
|
|
33
|
+
yield callback(item, i++)
|
|
34
|
+
}
|
|
35
|
+
})
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
filter(callback?: (item: T, index: number) => boolean): LazyCollection<T> {
|
|
39
|
+
const self = this
|
|
40
|
+
return new LazyCollection(function* () {
|
|
41
|
+
let i = 0
|
|
42
|
+
for (const item of self) {
|
|
43
|
+
if (callback ? callback(item, i++) : Boolean(item)) yield item
|
|
44
|
+
else i++
|
|
45
|
+
}
|
|
46
|
+
})
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
reject(callback: (item: T, index: number) => boolean): LazyCollection<T> {
|
|
50
|
+
return this.filter((item, i) => !callback(item, i))
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
flatMap<U>(callback: (item: T, index: number) => U[]): LazyCollection<U> {
|
|
54
|
+
const self = this
|
|
55
|
+
return new LazyCollection(function* () {
|
|
56
|
+
let i = 0
|
|
57
|
+
for (const item of self) {
|
|
58
|
+
yield* callback(item, i++)
|
|
59
|
+
}
|
|
60
|
+
})
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
take(count: number): LazyCollection<T> {
|
|
64
|
+
const self = this
|
|
65
|
+
return new LazyCollection(function* () {
|
|
66
|
+
if (count <= 0) return
|
|
67
|
+
let n = 0
|
|
68
|
+
const iter = self[Symbol.iterator]()
|
|
69
|
+
while (n < count) {
|
|
70
|
+
const { done, value } = iter.next()
|
|
71
|
+
if (done) break
|
|
72
|
+
yield value
|
|
73
|
+
n++
|
|
74
|
+
}
|
|
75
|
+
})
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
skip(count: number): LazyCollection<T> {
|
|
79
|
+
const self = this
|
|
80
|
+
return new LazyCollection(function* () {
|
|
81
|
+
let n = 0
|
|
82
|
+
for (const item of self) {
|
|
83
|
+
if (n++ < count) continue
|
|
84
|
+
yield item
|
|
85
|
+
}
|
|
86
|
+
})
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
takeUntil(valueOrCallback: T | ((item: T, index: number) => boolean)): LazyCollection<T> {
|
|
90
|
+
const self = this
|
|
91
|
+
return new LazyCollection(function* () {
|
|
92
|
+
let i = 0
|
|
93
|
+
const iter = self[Symbol.iterator]()
|
|
94
|
+
while (true) {
|
|
95
|
+
const { done, value } = iter.next()
|
|
96
|
+
if (done) break
|
|
97
|
+
const match =
|
|
98
|
+
typeof valueOrCallback === "function"
|
|
99
|
+
? (valueOrCallback as (item: T, index: number) => boolean)(value, i++)
|
|
100
|
+
: value === valueOrCallback
|
|
101
|
+
if (match) break
|
|
102
|
+
yield value
|
|
103
|
+
}
|
|
104
|
+
})
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
takeWhile(callback: (item: T, index: number) => boolean): LazyCollection<T> {
|
|
108
|
+
return this.takeUntil((item, i) => !callback(item, i))
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
skipUntil(valueOrCallback: T | ((item: T, index: number) => boolean)): LazyCollection<T> {
|
|
112
|
+
if (typeof valueOrCallback === "function") {
|
|
113
|
+
return this.skipWhile((item, i) => !valueOrCallback(item, i))
|
|
114
|
+
}
|
|
115
|
+
return this.skipWhile((item) => item !== valueOrCallback)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
skipWhile(callback: (item: T, index: number) => boolean): LazyCollection<T> {
|
|
119
|
+
const self = this
|
|
120
|
+
return new LazyCollection(function* () {
|
|
121
|
+
let skipping = true
|
|
122
|
+
let i = 0
|
|
123
|
+
for (const item of self) {
|
|
124
|
+
if (skipping && callback(item, i++)) continue
|
|
125
|
+
skipping = false
|
|
126
|
+
yield item
|
|
127
|
+
}
|
|
128
|
+
})
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
tap(callback: (item: T) => void): LazyCollection<T> {
|
|
132
|
+
const self = this
|
|
133
|
+
return new LazyCollection(function* () {
|
|
134
|
+
for (const item of self) {
|
|
135
|
+
callback(item)
|
|
136
|
+
yield item
|
|
137
|
+
}
|
|
138
|
+
})
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
chunk(size: number): LazyCollection<T[]> {
|
|
142
|
+
const self = this
|
|
143
|
+
return new LazyCollection(function* () {
|
|
144
|
+
let buf: T[] = []
|
|
145
|
+
for (const item of self) {
|
|
146
|
+
buf.push(item)
|
|
147
|
+
if (buf.length === size) {
|
|
148
|
+
yield buf
|
|
149
|
+
buf = []
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
if (buf.length > 0) yield buf
|
|
153
|
+
})
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
concat(other: Iterable<T>): LazyCollection<T> {
|
|
157
|
+
const self = this
|
|
158
|
+
return new LazyCollection(function* () {
|
|
159
|
+
yield* self
|
|
160
|
+
yield* other
|
|
161
|
+
})
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
values(): LazyCollection<T> {
|
|
165
|
+
return this
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
unique(key?: keyof T | ((item: T) => unknown)): LazyCollection<T> {
|
|
169
|
+
const self = this
|
|
170
|
+
return new LazyCollection(function* () {
|
|
171
|
+
const seen = new Set<unknown>()
|
|
172
|
+
for (const item of self) {
|
|
173
|
+
const v = self.extractValue(item, key)
|
|
174
|
+
if (!seen.has(v)) {
|
|
175
|
+
seen.add(v)
|
|
176
|
+
yield item
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
})
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
pluck<K extends keyof T>(key: K): LazyCollection<T[K]> {
|
|
183
|
+
return this.map((item) => item[key])
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// ── Terminal (materialize) ─────────────────────────────────────────────────
|
|
187
|
+
|
|
188
|
+
collect(): Collection<T> {
|
|
189
|
+
return new Collection(this.all())
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
all(): T[] {
|
|
193
|
+
return [...this]
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
toArray(): T[] {
|
|
197
|
+
return this.all()
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
count(): number {
|
|
201
|
+
return this.all().length
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
first(callback?: (item: T, index: number) => boolean): T | null {
|
|
205
|
+
let i = 0
|
|
206
|
+
for (const item of this) {
|
|
207
|
+
if (!callback || callback(item, i++)) return item
|
|
208
|
+
}
|
|
209
|
+
return null
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
last(callback?: (item: T, index: number) => boolean): T | null {
|
|
213
|
+
let result: T | null = null
|
|
214
|
+
let i = 0
|
|
215
|
+
for (const item of this) {
|
|
216
|
+
if (!callback || callback(item, i++)) result = item
|
|
217
|
+
}
|
|
218
|
+
return result
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
each(callback: (item: T, index: number) => void): void {
|
|
222
|
+
let i = 0
|
|
223
|
+
for (const item of this) {
|
|
224
|
+
callback(item, i++)
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
reduce<U>(callback: (carry: U, item: T, index: number) => U, initial: U): U {
|
|
229
|
+
let acc = initial
|
|
230
|
+
let i = 0
|
|
231
|
+
for (const item of this) {
|
|
232
|
+
acc = callback(acc, item, i++)
|
|
233
|
+
}
|
|
234
|
+
return acc
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
sum(key?: keyof T | ((item: T) => number)): number {
|
|
238
|
+
let total = 0
|
|
239
|
+
for (const item of this) total += this.extractValue(item, key) as number
|
|
240
|
+
return total
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
min(key?: keyof T | ((item: T) => number)): T | null {
|
|
244
|
+
let result: T | null = null
|
|
245
|
+
let minVal: number | null = null
|
|
246
|
+
for (const item of this) {
|
|
247
|
+
const v = this.extractValue(item, key) as number
|
|
248
|
+
if (minVal === null || v < minVal) {
|
|
249
|
+
minVal = v
|
|
250
|
+
result = item
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
return result
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
max(key?: keyof T | ((item: T) => number)): T | null {
|
|
257
|
+
let result: T | null = null
|
|
258
|
+
let maxVal: number | null = null
|
|
259
|
+
for (const item of this) {
|
|
260
|
+
const v = this.extractValue(item, key) as number
|
|
261
|
+
if (maxVal === null || v > maxVal) {
|
|
262
|
+
maxVal = v
|
|
263
|
+
result = item
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
return result
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
avg(key?: keyof T | ((item: T) => number)): number {
|
|
270
|
+
let total = 0
|
|
271
|
+
let count = 0
|
|
272
|
+
for (const item of this) {
|
|
273
|
+
total += this.extractValue(item, key) as number
|
|
274
|
+
count++
|
|
275
|
+
}
|
|
276
|
+
return count === 0 ? 0 : total / count
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
average(key?: keyof T | ((item: T) => number)): number {
|
|
280
|
+
return this.avg(key)
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
contains(itemOrCallback: T | ((item: T) => boolean)): boolean {
|
|
284
|
+
const test =
|
|
285
|
+
typeof itemOrCallback === "function"
|
|
286
|
+
? (itemOrCallback as (item: T) => boolean)
|
|
287
|
+
: (i: T) => i === itemOrCallback
|
|
288
|
+
for (const item of this) {
|
|
289
|
+
if (test(item)) return true
|
|
290
|
+
}
|
|
291
|
+
return false
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
every(callback: (item: T, index: number) => boolean): boolean {
|
|
295
|
+
let i = 0
|
|
296
|
+
for (const item of this) {
|
|
297
|
+
if (!callback(item, i++)) return false
|
|
298
|
+
}
|
|
299
|
+
return true
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
some(callback: (item: T, index: number) => boolean): boolean {
|
|
303
|
+
let i = 0
|
|
304
|
+
for (const item of this) {
|
|
305
|
+
if (callback(item, i++)) return true
|
|
306
|
+
}
|
|
307
|
+
return false
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
groupBy<K extends string>(key: keyof T | ((item: T) => K)): Record<string, LazyCollection<T>> {
|
|
311
|
+
const groups: Record<string, T[]> = {}
|
|
312
|
+
for (const item of this) {
|
|
313
|
+
const k = String(this.extractValue(item, key))
|
|
314
|
+
if (!groups[k]) groups[k] = []
|
|
315
|
+
groups[k].push(item)
|
|
316
|
+
}
|
|
317
|
+
return Object.fromEntries(Object.entries(groups).map(([k, v]) => [k, LazyCollection.make(v)]))
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
toJson(): string {
|
|
321
|
+
return JSON.stringify(this.all())
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// ── protected helpers ───────────────────────────────────────────────────────
|
|
325
|
+
|
|
326
|
+
protected extractValue(item: T, key?: keyof T | ((item: T) => unknown)): unknown {
|
|
327
|
+
if (!key) return item
|
|
328
|
+
return typeof key === "function" ? key(item) : item[key]
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
332
|
+
|
|
333
|
+
isEmpty(): boolean {
|
|
334
|
+
for (const _ of this) return false
|
|
335
|
+
return true
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
isNotEmpty(): boolean {
|
|
339
|
+
return !this.isEmpty()
|
|
340
|
+
}
|
|
341
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import ConfigRepository from "../config/repository"
|
|
2
|
+
|
|
3
|
+
export default abstract class Manager<TDriver, TFactory = TDriver> {
|
|
4
|
+
protected drivers: Record<string, TDriver>
|
|
5
|
+
protected customCreators: Record<string, (config: ConfigRepository) => TDriver>
|
|
6
|
+
protected active: string
|
|
7
|
+
protected config: ConfigRepository
|
|
8
|
+
|
|
9
|
+
constructor(
|
|
10
|
+
drivers: Record<string, TDriver>,
|
|
11
|
+
active = "default",
|
|
12
|
+
config: ConfigRepository = new ConfigRepository({}),
|
|
13
|
+
) {
|
|
14
|
+
this.drivers = { ...drivers }
|
|
15
|
+
this.customCreators = {}
|
|
16
|
+
this.config = config
|
|
17
|
+
this.active = active in drivers ? active : this.defaultDriverName()
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
extend(name: string, factory: (config: ConfigRepository) => TFactory): this {
|
|
21
|
+
this.customCreators[name] = (cfg) => this.createDriver(factory(cfg), cfg)
|
|
22
|
+
return this
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
use(name: string): this {
|
|
26
|
+
this.active = this.resolve(name) ? name : this.active
|
|
27
|
+
return this
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
protected abstract createDriver(raw: TFactory, config: ConfigRepository): TDriver
|
|
31
|
+
|
|
32
|
+
protected defaultDriverName(): string {
|
|
33
|
+
return Object.keys(this.drivers)[0] ?? "default"
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
protected driverType(): string {
|
|
37
|
+
return "Driver"
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
protected resolve(name: string): TDriver {
|
|
41
|
+
if (name in this.drivers) {
|
|
42
|
+
return this.drivers[name]
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (name in this.customCreators) {
|
|
46
|
+
const driver = this.customCreators[name](this.config)
|
|
47
|
+
this.drivers[name] = driver
|
|
48
|
+
return driver
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
throw new Error(`${this.driverType()} [${name}] is not defined.`)
|
|
52
|
+
}
|
|
53
|
+
}
|