@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,174 @@
|
|
|
1
|
+
export type {
|
|
2
|
+
EventClass,
|
|
3
|
+
EventIdentifier,
|
|
4
|
+
Listener,
|
|
5
|
+
ListenerObject,
|
|
6
|
+
Unsubscribe,
|
|
7
|
+
WildcardListener,
|
|
8
|
+
} from "./types"
|
|
9
|
+
|
|
10
|
+
import type { EventClass, EventIdentifier, Listener, ListenerObject, Unsubscribe, WildcardListener } from "./types"
|
|
11
|
+
|
|
12
|
+
export interface EventSubscriber {
|
|
13
|
+
subscribe(bus: Bus): Record<string, Listener | string> | void
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export class Bus {
|
|
17
|
+
protected listeners = new Map<string, Listener[]>()
|
|
18
|
+
protected wildcardListeners: WildcardListener[] = []
|
|
19
|
+
protected pushedEvents = new Map<string, Record<string, unknown>[]>()
|
|
20
|
+
|
|
21
|
+
listen<T>(
|
|
22
|
+
event: EventIdentifier<T> | Array<EventIdentifier<T>>,
|
|
23
|
+
listener: Listener<T> | ListenerObject<T>,
|
|
24
|
+
): Unsubscribe {
|
|
25
|
+
const events = Array.isArray(event) ? event : [event]
|
|
26
|
+
const normalized = this.normalizeListener(listener) as Listener
|
|
27
|
+
const unsubscribers: Unsubscribe[] = []
|
|
28
|
+
|
|
29
|
+
for (const ev of events) {
|
|
30
|
+
const name = this.resolveEventName(ev)
|
|
31
|
+
|
|
32
|
+
if (name === "*") {
|
|
33
|
+
const wl: WildcardListener = (_name, data) => void normalized(data as T)
|
|
34
|
+
this.wildcardListeners.push(wl)
|
|
35
|
+
unsubscribers.push(() => {
|
|
36
|
+
const idx = this.wildcardListeners.indexOf(wl)
|
|
37
|
+
if (idx !== -1) this.wildcardListeners.splice(idx, 1)
|
|
38
|
+
})
|
|
39
|
+
continue
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const current = this.listeners.get(name) ?? []
|
|
43
|
+
current.push(normalized)
|
|
44
|
+
this.listeners.set(name, current)
|
|
45
|
+
|
|
46
|
+
unsubscribers.push(() => {
|
|
47
|
+
const all = this.listeners.get(name) ?? []
|
|
48
|
+
const updated = all.filter((l) => l !== normalized)
|
|
49
|
+
if (updated.length === 0) {
|
|
50
|
+
this.listeners.delete(name)
|
|
51
|
+
} else {
|
|
52
|
+
this.listeners.set(name, updated)
|
|
53
|
+
}
|
|
54
|
+
})
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return () => {
|
|
58
|
+
unsubscribers.forEach((fn) => {
|
|
59
|
+
fn()
|
|
60
|
+
})
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
once<T>(event: EventIdentifier<T>, listener: Listener<T> | ListenerObject<T>): Unsubscribe {
|
|
65
|
+
const normalized = this.normalizeListener(listener)
|
|
66
|
+
let unsubscribe!: Unsubscribe
|
|
67
|
+
|
|
68
|
+
const wrapped: Listener<T> = async (data: T) => {
|
|
69
|
+
unsubscribe()
|
|
70
|
+
return normalized(data)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
unsubscribe = this.listen(event, wrapped)
|
|
74
|
+
return unsubscribe
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
subscribe(subscriber: EventSubscriber | (new () => EventSubscriber)): void {
|
|
78
|
+
const instance = typeof subscriber === "function" ? new subscriber() : subscriber
|
|
79
|
+
const mappings = instance.subscribe(this)
|
|
80
|
+
|
|
81
|
+
if (!mappings) return
|
|
82
|
+
|
|
83
|
+
for (const [event, handler] of Object.entries(mappings)) {
|
|
84
|
+
const listener =
|
|
85
|
+
typeof handler === "string"
|
|
86
|
+
? (data: unknown) => (instance as unknown as Record<string, Listener>)[handler]?.(data)
|
|
87
|
+
: (handler as Listener)
|
|
88
|
+
this.listen(event, listener)
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async dispatch<T>(event: T | string, payload?: unknown): Promise<void> {
|
|
93
|
+
const [name, data] = this.parseEventAndPayload(event, payload)
|
|
94
|
+
|
|
95
|
+
for (const wl of [...this.wildcardListeners]) {
|
|
96
|
+
await wl(name, data)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const listeners = [...(this.listeners.get(name) ?? [])]
|
|
100
|
+
for (const listener of listeners) {
|
|
101
|
+
await listener(data)
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
fire<T>(event: T | string, payload?: unknown): Promise<void> {
|
|
106
|
+
return this.dispatch(event, payload)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async until<T>(event: T | string, payload?: unknown): Promise<unknown> {
|
|
110
|
+
const [name, data] = this.parseEventAndPayload(event, payload)
|
|
111
|
+
|
|
112
|
+
const listeners = [...(this.listeners.get(name) ?? [])]
|
|
113
|
+
for (const listener of listeners) {
|
|
114
|
+
const result = await listener(data)
|
|
115
|
+
if (result !== null && result !== false && result !== undefined) {
|
|
116
|
+
return result
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return null
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
push(event: string, payload: Record<string, unknown> = {}): void {
|
|
123
|
+
const queued = this.pushedEvents.get(event) ?? []
|
|
124
|
+
queued.push(payload)
|
|
125
|
+
this.pushedEvents.set(event, queued)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async flush(event: string): Promise<void> {
|
|
129
|
+
const queued = [...(this.pushedEvents.get(event) ?? [])]
|
|
130
|
+
this.pushedEvents.delete(event)
|
|
131
|
+
for (const payload of queued) {
|
|
132
|
+
await this.dispatch(event, payload)
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
forget(event: EventIdentifier): void {
|
|
137
|
+
this.listeners.delete(this.resolveEventName(event))
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
forgetPushed(): void {
|
|
141
|
+
this.pushedEvents.clear()
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
hasListeners(event: EventIdentifier): boolean {
|
|
145
|
+
const name = this.resolveEventName(event)
|
|
146
|
+
return (this.listeners.get(name)?.length ?? 0) > 0 || this.wildcardListeners.length > 0
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
protected resolveEventName(event: unknown): string {
|
|
150
|
+
if (typeof event === "string") return event
|
|
151
|
+
if (typeof event === "function") {
|
|
152
|
+
// EventClass passed directly — prefer static label (minification-safe)
|
|
153
|
+
if ("label" in event && typeof event.label === "string") return event.label as string
|
|
154
|
+
return (event as EventClass).name
|
|
155
|
+
}
|
|
156
|
+
// Event instance — read label/name from its constructor
|
|
157
|
+
const ctor = (event as object)?.constructor
|
|
158
|
+
if (ctor && "label" in ctor && typeof (ctor as { label: unknown }).label === "string") {
|
|
159
|
+
return (ctor as { label: string }).label
|
|
160
|
+
}
|
|
161
|
+
return ctor?.name ?? ""
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
protected parseEventAndPayload<T>(event: T | string, payload?: unknown): [string, unknown] {
|
|
165
|
+
const name = this.resolveEventName(event)
|
|
166
|
+
const data = typeof event === "string" ? (payload ?? {}) : event
|
|
167
|
+
return [name, data]
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
protected normalizeListener<T>(listener: Listener<T> | ListenerObject<T>): Listener<T> {
|
|
171
|
+
if (typeof listener === "function") return listener
|
|
172
|
+
return (event: T) => listener.handle(event)
|
|
173
|
+
}
|
|
174
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Event } from "../../support/facades/event"
|
|
2
|
+
|
|
3
|
+
export abstract class Dispatchable {
|
|
4
|
+
static dispatch<TThis extends new (...args: any[]) => Dispatchable>(
|
|
5
|
+
this: TThis,
|
|
6
|
+
...args: ConstructorParameters<TThis>
|
|
7
|
+
): Promise<void> {
|
|
8
|
+
return Event.dispatch(new this(...args)) as Promise<void>
|
|
9
|
+
}
|
|
10
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ServiceProviderContext } from "../support/service-provider"
|
|
2
|
+
import ServiceProvider from "../support/service-provider"
|
|
3
|
+
import { Bus } from "./bus"
|
|
4
|
+
|
|
5
|
+
export class EventsProvider extends ServiceProvider {
|
|
6
|
+
register({ container }: ServiceProviderContext) {
|
|
7
|
+
container.singleton("events", (_c) => new Bus())
|
|
8
|
+
}
|
|
9
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export type EventClass<T = unknown> = new (...args: any[]) => T
|
|
2
|
+
export type EventIdentifier<T = unknown> = string | EventClass<T>
|
|
3
|
+
export type Listener<T = unknown> = (event: T) => void | boolean | Promise<void | boolean>
|
|
4
|
+
export type WildcardListener = (eventName: string, data: unknown) => void | Promise<void>
|
|
5
|
+
export type Unsubscribe = () => void
|
|
6
|
+
|
|
7
|
+
export interface ListenerObject<T = unknown> {
|
|
8
|
+
handle(event: T): void | boolean | Promise<void | boolean>
|
|
9
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { createConfig } from "../config/discovery"
|
|
2
|
+
import { registerGlobalEnv } from "../config/env"
|
|
3
|
+
import { ConfigProvider } from "../config/provider"
|
|
4
|
+
import type ConfigRepository from "../config/repository"
|
|
5
|
+
import type { ConfigItems } from "../config/repository"
|
|
6
|
+
import type { ContainerContract, ContainerIdentifier } from "../container/contract"
|
|
7
|
+
import {
|
|
8
|
+
type ContainerRuntimeOptions,
|
|
9
|
+
createRuntimeContainer,
|
|
10
|
+
mergeContainerRuntimeOptions,
|
|
11
|
+
} from "../container/runtime"
|
|
12
|
+
import { clearFacadeCache } from "../support/facades/facade"
|
|
13
|
+
import type ServiceProvider from "../support/service-provider"
|
|
14
|
+
import type { Cleanup, ServiceProviderContext } from "../support/service-provider"
|
|
15
|
+
import { getCurrentApplicationContainer, setCurrentApplicationContainer } from "./current-application"
|
|
16
|
+
|
|
17
|
+
type ApplicationRunContext = ServiceProviderContext & {
|
|
18
|
+
cleanupTasks: Cleanup[]
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export type ApplicationConfigureOptions = {
|
|
22
|
+
basePath?: string
|
|
23
|
+
container?: ContainerRuntimeOptions
|
|
24
|
+
config?: ConfigItems
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
type ApplicationResolvedOptions = {
|
|
28
|
+
basePath: string
|
|
29
|
+
container: ReturnType<typeof mergeContainerRuntimeOptions>
|
|
30
|
+
config: ConfigItems
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
registerGlobalEnv()
|
|
34
|
+
|
|
35
|
+
function mergeConfigureOptions(options: ApplicationConfigureOptions = {}): ApplicationResolvedOptions {
|
|
36
|
+
return {
|
|
37
|
+
basePath: options.basePath ?? "./",
|
|
38
|
+
container: mergeContainerRuntimeOptions(options.container),
|
|
39
|
+
config: options.config ?? {},
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export class Application {
|
|
44
|
+
protected providers: ServiceProvider[]
|
|
45
|
+
protected options: ApplicationResolvedOptions
|
|
46
|
+
|
|
47
|
+
constructor(options: ApplicationResolvedOptions) {
|
|
48
|
+
this.options = options
|
|
49
|
+
this.providers = []
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
protected getConfigItems(): ConfigRepository {
|
|
53
|
+
return createConfig(this.options.basePath, this.options.config)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
static clearConfigCache(_basePath?: string): void {}
|
|
57
|
+
|
|
58
|
+
static configure(basePath?: string): Application
|
|
59
|
+
static configure(options?: ApplicationConfigureOptions): Application
|
|
60
|
+
static configure(basePathOrOptions: string | ApplicationConfigureOptions = "./") {
|
|
61
|
+
if (typeof basePathOrOptions === "string") {
|
|
62
|
+
return new Application(mergeConfigureOptions({ basePath: basePathOrOptions }))
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return new Application(mergeConfigureOptions(basePathOrOptions))
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
static make<T>(identifier: ContainerIdentifier<T>): T {
|
|
69
|
+
const container = getCurrentApplicationContainer()
|
|
70
|
+
if (!container) {
|
|
71
|
+
throw new Error("Application container is not available. Call run() first.")
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return container.make<T>(identifier)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
withProviders(providers: ServiceProvider[]) {
|
|
78
|
+
this.providers.push(...providers)
|
|
79
|
+
return this
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
protected createContainer(): ContainerContract {
|
|
83
|
+
return createRuntimeContainer(this.options.container)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
protected rememberCleanup(cleanupTasks: Cleanup[], cleanup: void | Cleanup): void {
|
|
87
|
+
if (typeof cleanup === "function") {
|
|
88
|
+
cleanupTasks.push(cleanup)
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
protected createStopHandler(container: ContainerContract, cleanupTasks: Cleanup[]): Cleanup {
|
|
93
|
+
const stop: Cleanup = () => {
|
|
94
|
+
for (const cleanup of cleanupTasks.reverse()) {
|
|
95
|
+
cleanup()
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
clearFacadeCache()
|
|
99
|
+
|
|
100
|
+
container.flush()
|
|
101
|
+
|
|
102
|
+
if (getCurrentApplicationContainer() === container) {
|
|
103
|
+
setCurrentApplicationContainer(null)
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return stop
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
run() {
|
|
111
|
+
const container = this.createContainer()
|
|
112
|
+
|
|
113
|
+
setCurrentApplicationContainer(container)
|
|
114
|
+
clearFacadeCache()
|
|
115
|
+
|
|
116
|
+
const providers = [new ConfigProvider(this.getConfigItems()), ...this.providers]
|
|
117
|
+
const context: ApplicationRunContext = { container, cleanupTasks: [] }
|
|
118
|
+
|
|
119
|
+
for (const provider of providers) {
|
|
120
|
+
this.rememberCleanup(context.cleanupTasks, provider.register(context))
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
for (const provider of providers) {
|
|
124
|
+
this.rememberCleanup(context.cleanupTasks, provider.boot(context))
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const stop = this.createStopHandler(container, context.cleanupTasks)
|
|
128
|
+
|
|
129
|
+
window.addEventListener("beforeunload", stop, { once: true })
|
|
130
|
+
|
|
131
|
+
return {
|
|
132
|
+
container,
|
|
133
|
+
stop,
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ContainerContract, ContainerIdentifier } from "../container/contract"
|
|
2
|
+
|
|
3
|
+
let currentContainer: ContainerContract | null = null
|
|
4
|
+
|
|
5
|
+
export function setCurrentApplicationContainer(container: ContainerContract | null): void {
|
|
6
|
+
currentContainer = container
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function getCurrentApplicationContainer(): ContainerContract | null {
|
|
10
|
+
return currentContainer
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function makeFromCurrentApplication<T>(identifier: ContainerIdentifier<T>): T {
|
|
14
|
+
const container = getCurrentApplicationContainer()
|
|
15
|
+
if (!container) {
|
|
16
|
+
throw new Error("Application container is not available. Call run() first.")
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return container.make<T>(identifier)
|
|
20
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/// <reference path="./config/env.global.d.ts" />
|
|
2
|
+
|
|
3
|
+
export type { Contract as CacheStore } from "./cache/contract"
|
|
4
|
+
export { default as CacheManager } from "./cache/manager"
|
|
5
|
+
export { CacheProvider } from "./cache/provider"
|
|
6
|
+
export { createConfig } from "./config/discovery"
|
|
7
|
+
export { env } from "./config/env"
|
|
8
|
+
export { ConfigProvider } from "./config/provider"
|
|
9
|
+
export { default as ConfigRepository } from "./config/repository"
|
|
10
|
+
export { default as BuiltinContainer, inject } from "./container/adapters/builtin"
|
|
11
|
+
export type {
|
|
12
|
+
ContainerClass,
|
|
13
|
+
ContainerConcrete,
|
|
14
|
+
ContainerContract,
|
|
15
|
+
ContainerFactory,
|
|
16
|
+
ContainerIdentifier,
|
|
17
|
+
} from "./container/contract"
|
|
18
|
+
export type {
|
|
19
|
+
EventIdentifier,
|
|
20
|
+
EventSubscriber,
|
|
21
|
+
Listener,
|
|
22
|
+
ListenerObject,
|
|
23
|
+
Unsubscribe,
|
|
24
|
+
WildcardListener,
|
|
25
|
+
} from "./events/bus"
|
|
26
|
+
export { Bus } from "./events/bus"
|
|
27
|
+
export { Dispatchable } from "./events/concerns/dispatchable"
|
|
28
|
+
export { EventsProvider } from "./events/provider"
|
|
29
|
+
export type { ApplicationConfigureOptions } from "./foundation/application"
|
|
30
|
+
export { Application } from "./foundation/application"
|
|
31
|
+
export type { Contract as LogContract } from "./log/contract"
|
|
32
|
+
export { default as LogManager } from "./log/manager"
|
|
33
|
+
export { LogProvider } from "./log/provider"
|
|
34
|
+
export type { default as Contract, RendererContext, RootComponent } from "./renderers/contract"
|
|
35
|
+
export type { Adapter as StoreAdapter } from "./store/adapters/contract"
|
|
36
|
+
export { default as IndexedDbAdapter } from "./store/adapters/indexed-db"
|
|
37
|
+
export { default as LocalStorageAdapter } from "./store/adapters/local-storage"
|
|
38
|
+
export { default as MemoryStoreAdapter } from "./store/adapters/memory"
|
|
39
|
+
export { default as StoreManager } from "./store/manager"
|
|
40
|
+
export { StoreProvider } from "./store/provider"
|
|
41
|
+
export { Arr } from "./support/arr"
|
|
42
|
+
export { Collection } from "./support/collection"
|
|
43
|
+
export { Fluent } from "./support/fluent"
|
|
44
|
+
export { registerGlobalHelpers } from "./support/globals"
|
|
45
|
+
export { LazyCollection } from "./support/lazy-collection"
|
|
46
|
+
export { default as Manager } from "./support/manager"
|
|
47
|
+
export { Num } from "./support/num"
|
|
48
|
+
export { send } from "./support/pipeline"
|
|
49
|
+
export type { Cleanup, ServiceProviderContext } from "./support/service-provider"
|
|
50
|
+
export { DeferrableServiceProvider, default as ServiceProvider } from "./support/service-provider"
|
|
51
|
+
export { Str } from "./support/str"
|
|
52
|
+
|
|
53
|
+
import { CacheProvider } from "./cache/provider"
|
|
54
|
+
import { LogProvider } from "./log/provider"
|
|
55
|
+
import { StoreProvider } from "./store/provider"
|
|
56
|
+
import type ServiceProvider from "./support/service-provider"
|
|
57
|
+
|
|
58
|
+
export const defaultProviders: ServiceProvider[] = [new StoreProvider(), new CacheProvider(), new LogProvider()]
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export interface Contract {
|
|
2
|
+
/**
|
|
3
|
+
* Log a debug-level message for detailed diagnostic information.
|
|
4
|
+
*/
|
|
5
|
+
debug(message: string, context?: Record<string, unknown>): void
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Log an informational message about normal application flow.
|
|
9
|
+
*/
|
|
10
|
+
info(message: string, context?: Record<string, unknown>): void
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Log a warning about a recoverable or unexpected condition.
|
|
14
|
+
*/
|
|
15
|
+
warn(message: string, context?: Record<string, unknown>): void
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Log an error indicating a failure that requires attention.
|
|
19
|
+
*/
|
|
20
|
+
error(message: string, context?: Record<string, unknown>): void
|
|
21
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { Contract } from "../contract"
|
|
2
|
+
|
|
3
|
+
const LEVELS = ["debug", "info", "warn", "error"] as const
|
|
4
|
+
type Level = (typeof LEVELS)[number]
|
|
5
|
+
|
|
6
|
+
export default class ConsoleLogger implements Contract {
|
|
7
|
+
protected threshold: number
|
|
8
|
+
|
|
9
|
+
constructor(level: Level = "debug") {
|
|
10
|
+
this.threshold = LEVELS.indexOf(level)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* {@inheritDoc}
|
|
15
|
+
*/
|
|
16
|
+
debug(message: string, context?: Record<string, unknown>): void {
|
|
17
|
+
if (this.passes(0)) console.debug(message, ...this.spread(context))
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* {@inheritDoc}
|
|
22
|
+
*/
|
|
23
|
+
info(message: string, context?: Record<string, unknown>): void {
|
|
24
|
+
if (this.passes(1)) console.info(message, ...this.spread(context))
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* {@inheritDoc}
|
|
29
|
+
*/
|
|
30
|
+
warn(message: string, context?: Record<string, unknown>): void {
|
|
31
|
+
if (this.passes(2)) console.warn(message, ...this.spread(context))
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* {@inheritDoc}
|
|
36
|
+
*/
|
|
37
|
+
error(message: string, context?: Record<string, unknown>): void {
|
|
38
|
+
if (this.passes(3)) console.error(message, ...this.spread(context))
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Returns true if the given numeric level meets or exceeds the configured threshold.
|
|
43
|
+
*/
|
|
44
|
+
protected passes(level: number): boolean {
|
|
45
|
+
return level >= this.threshold
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Returns the context as a single-element array, or an empty array when absent.
|
|
50
|
+
*/
|
|
51
|
+
protected spread(context?: Record<string, unknown>): unknown[] {
|
|
52
|
+
return context !== undefined ? [context] : []
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Contract } from "../contract"
|
|
2
|
+
|
|
3
|
+
export default class NullLogger implements Contract {
|
|
4
|
+
/**
|
|
5
|
+
* {@inheritDoc}
|
|
6
|
+
*/
|
|
7
|
+
debug(_message: string, _context?: Record<string, unknown>): void {}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* {@inheritDoc}
|
|
11
|
+
*/
|
|
12
|
+
info(_message: string, _context?: Record<string, unknown>): void {}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* {@inheritDoc}
|
|
16
|
+
*/
|
|
17
|
+
warn(_message: string, _context?: Record<string, unknown>): void {}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* {@inheritDoc}
|
|
21
|
+
*/
|
|
22
|
+
error(_message: string, _context?: Record<string, unknown>): void {}
|
|
23
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { Contract } from "../contract"
|
|
2
|
+
|
|
3
|
+
export default class StackLogger implements Contract {
|
|
4
|
+
constructor(protected drivers: Contract[]) {}
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* {@inheritDoc}
|
|
8
|
+
*/
|
|
9
|
+
debug(message: string, context?: Record<string, unknown>): void {
|
|
10
|
+
this.dispatch("debug", message, context)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* {@inheritDoc}
|
|
15
|
+
*/
|
|
16
|
+
info(message: string, context?: Record<string, unknown>): void {
|
|
17
|
+
this.dispatch("info", message, context)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* {@inheritDoc}
|
|
22
|
+
*/
|
|
23
|
+
warn(message: string, context?: Record<string, unknown>): void {
|
|
24
|
+
this.dispatch("warn", message, context)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* {@inheritDoc}
|
|
29
|
+
*/
|
|
30
|
+
error(message: string, context?: Record<string, unknown>): void {
|
|
31
|
+
this.dispatch("error", message, context)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Forwards the log call to each driver in order, swallowing any individual driver errors.
|
|
36
|
+
*/
|
|
37
|
+
protected dispatch(level: keyof Contract, message: string, context?: Record<string, unknown>): void {
|
|
38
|
+
for (const driver of this.drivers) {
|
|
39
|
+
try {
|
|
40
|
+
driver[level](message, context)
|
|
41
|
+
} catch {
|
|
42
|
+
// swallow — a logging failure must not crash the application
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import type ConfigRepository from "../config/repository"
|
|
2
|
+
import Manager from "../support/manager"
|
|
3
|
+
import type { Contract } from "./contract"
|
|
4
|
+
import ConsoleLogger from "./drivers/console"
|
|
5
|
+
import NullLogger from "./drivers/null"
|
|
6
|
+
import StackLogger from "./drivers/stack"
|
|
7
|
+
|
|
8
|
+
export default class LogManager extends Manager<Contract, Contract> implements Contract {
|
|
9
|
+
/**
|
|
10
|
+
* {@inheritDoc}
|
|
11
|
+
*/
|
|
12
|
+
debug(message: string, context?: Record<string, unknown>): void {
|
|
13
|
+
this.resolve(this.active).debug(message, context)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* {@inheritDoc}
|
|
18
|
+
*/
|
|
19
|
+
info(message: string, context?: Record<string, unknown>): void {
|
|
20
|
+
this.resolve(this.active).info(message, context)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* {@inheritDoc}
|
|
25
|
+
*/
|
|
26
|
+
warn(message: string, context?: Record<string, unknown>): void {
|
|
27
|
+
this.resolve(this.active).warn(message, context)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* {@inheritDoc}
|
|
32
|
+
*/
|
|
33
|
+
error(message: string, context?: Record<string, unknown>): void {
|
|
34
|
+
this.resolve(this.active).error(message, context)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Returns the driver as-is; no wrapping is needed for log drivers.
|
|
39
|
+
*/
|
|
40
|
+
protected createDriver(raw: Contract): Contract {
|
|
41
|
+
return raw
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Returns the human-readable driver type label used in error messages.
|
|
46
|
+
*/
|
|
47
|
+
protected driverType(): string {
|
|
48
|
+
return "Log driver"
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Creates a LogManager from the application config, registering the built-in console, null, and stack drivers.
|
|
53
|
+
*/
|
|
54
|
+
static fromConfig(config: ConfigRepository): LogManager {
|
|
55
|
+
const active = config.get<string>("logging.default", "console")
|
|
56
|
+
const manager = new LogManager({}, active, config)
|
|
57
|
+
|
|
58
|
+
manager.extend("console", (cfg) => {
|
|
59
|
+
const level = cfg.get<"debug" | "info" | "warn" | "error">("logging.drivers.console.level", "debug")
|
|
60
|
+
return new ConsoleLogger(level ?? "debug")
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
manager.extend("null", (_cfg) => new NullLogger())
|
|
64
|
+
|
|
65
|
+
manager.extend("stack", (cfg) => {
|
|
66
|
+
const names = cfg.get<string[]>("logging.drivers.stack.drivers", []) ?? []
|
|
67
|
+
return new StackLogger(names.map((name) => manager.resolve(name)))
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
// Manager constructor only accepts pre-built drivers for active validation;
|
|
71
|
+
// re-set after custom creators are registered so the configured name resolves correctly.
|
|
72
|
+
manager.active = active
|
|
73
|
+
|
|
74
|
+
return manager
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type ConfigRepository from "../config/repository"
|
|
2
|
+
import type { ServiceProviderContext } from "../support/service-provider"
|
|
3
|
+
import ServiceProvider from "../support/service-provider"
|
|
4
|
+
import LogManager from "./manager"
|
|
5
|
+
|
|
6
|
+
export class LogProvider extends ServiceProvider {
|
|
7
|
+
register({ container }: ServiceProviderContext): void {
|
|
8
|
+
container.singleton("log", (c) => LogManager.fromConfig(c.make<ConfigRepository>("config")))
|
|
9
|
+
container.singleton(LogManager, (c) => c.make<LogManager>("log"))
|
|
10
|
+
}
|
|
11
|
+
}
|
package/src/react.ts
ADDED