@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.
Files changed (68) hide show
  1. package/README.md +860 -0
  2. package/package.json +121 -0
  3. package/src/cache/cache.ts +46 -0
  4. package/src/cache/contract.ts +9 -0
  5. package/src/cache/manager.ts +110 -0
  6. package/src/cache/provider.ts +11 -0
  7. package/src/config/contract.ts +63 -0
  8. package/src/config/discovery.ts +99 -0
  9. package/src/config/env.global.d.ts +6 -0
  10. package/src/config/env.ts +68 -0
  11. package/src/config/index.ts +5 -0
  12. package/src/config/provider.ts +17 -0
  13. package/src/config/repository.ts +164 -0
  14. package/src/container/adapters/builtin.ts +323 -0
  15. package/src/container/contract.ts +43 -0
  16. package/src/container/runtime.ts +29 -0
  17. package/src/events/bus.ts +174 -0
  18. package/src/events/concerns/dispatchable.ts +10 -0
  19. package/src/events/provider.ts +9 -0
  20. package/src/events/types.ts +9 -0
  21. package/src/foundation/application.ts +136 -0
  22. package/src/foundation/current-application.ts +20 -0
  23. package/src/index.ts +58 -0
  24. package/src/log/contract.ts +21 -0
  25. package/src/log/drivers/console.ts +54 -0
  26. package/src/log/drivers/null.ts +23 -0
  27. package/src/log/drivers/stack.ts +46 -0
  28. package/src/log/manager.ts +76 -0
  29. package/src/log/provider.ts +11 -0
  30. package/src/react.ts +2 -0
  31. package/src/renderers/adapters/react.tsx +25 -0
  32. package/src/renderers/adapters/solid.tsx +26 -0
  33. package/src/renderers/adapters/svelte.ts +73 -0
  34. package/src/renderers/adapters/vue.ts +22 -0
  35. package/src/renderers/contract.ts +12 -0
  36. package/src/runtimes/react.tsx +81 -0
  37. package/src/runtimes/solid.tsx +47 -0
  38. package/src/runtimes/svelte.ts +17 -0
  39. package/src/runtimes/vue.ts +34 -0
  40. package/src/solid.ts +2 -0
  41. package/src/store/adapters/contract.ts +11 -0
  42. package/src/store/adapters/indexed-db.ts +187 -0
  43. package/src/store/adapters/local-storage.ts +48 -0
  44. package/src/store/adapters/memory.ts +35 -0
  45. package/src/store/manager.ts +68 -0
  46. package/src/store/provider.ts +10 -0
  47. package/src/store/store.ts +1 -0
  48. package/src/support/arr.ts +372 -0
  49. package/src/support/collection.ts +889 -0
  50. package/src/support/facades/cache.ts +6 -0
  51. package/src/support/facades/config.ts +6 -0
  52. package/src/support/facades/event.ts +6 -0
  53. package/src/support/facades/facade.ts +146 -0
  54. package/src/support/facades/index.ts +5 -0
  55. package/src/support/facades/log.ts +6 -0
  56. package/src/support/facades/store.ts +6 -0
  57. package/src/support/fluent.ts +56 -0
  58. package/src/support/globals.ts +8 -0
  59. package/src/support/lazy-collection.ts +341 -0
  60. package/src/support/manager.ts +53 -0
  61. package/src/support/num.ts +50 -0
  62. package/src/support/pipeline.ts +29 -0
  63. package/src/support/service-provider.ts +19 -0
  64. package/src/support/str.ts +682 -0
  65. package/src/svelte.ts +2 -0
  66. package/src/types/peer-deps.d.ts +10 -0
  67. package/src/vue.ts +2 -0
  68. package/tsconfig.json +15 -0
@@ -0,0 +1,164 @@
1
+ import type { Contract } from "./contract"
2
+
3
+ export type ConfigItems = Record<string, unknown>
4
+ export type ConfigDefaults = Record<string | number, unknown>
5
+
6
+ function isPlainObject(value: unknown): value is Record<string, unknown> {
7
+ return typeof value === "object" && value !== null && !Array.isArray(value)
8
+ }
9
+
10
+ function resolveDefault<T>(defaultValue: T | (() => T)): T {
11
+ return typeof defaultValue === "function" ? (defaultValue as () => T)() : defaultValue
12
+ }
13
+
14
+ function dataGet(source: unknown, path: string, defaultValue: unknown = null): unknown {
15
+ if (!path) {
16
+ return source
17
+ }
18
+
19
+ const segments = path.split(".")
20
+ let cursor: unknown = source
21
+
22
+ for (const segment of segments) {
23
+ if (!hasDataKey(cursor, segment)) {
24
+ return resolveDefault(defaultValue)
25
+ }
26
+
27
+ cursor = cursor[segment]
28
+ }
29
+
30
+ return cursor
31
+ }
32
+
33
+ function hasDataKey(value: unknown, key: string): value is Record<string, unknown> {
34
+ return isPlainObject(value) && key in value
35
+ }
36
+
37
+ function dataTraverse(
38
+ target: Record<string, unknown>,
39
+ path: string,
40
+ onCreate: boolean,
41
+ ): [Record<string, unknown>, string] | null {
42
+ const segments = path.split(".")
43
+ let cursor: Record<string, unknown> = target
44
+
45
+ for (let i = 0; i < segments.length - 1; i += 1) {
46
+ const segment = segments[i]
47
+
48
+ if (!isPlainObject(cursor[segment])) {
49
+ if (!onCreate) return null
50
+ cursor[segment] = {}
51
+ }
52
+
53
+ cursor = cursor[segment] as Record<string, unknown>
54
+ }
55
+
56
+ return [cursor, segments[segments.length - 1]]
57
+ }
58
+
59
+ function dataSet(target: Record<string, unknown>, path: string, value: unknown): void {
60
+ const result = dataTraverse(target, path, true)
61
+ if (result) result[0][result[1]] = value
62
+ }
63
+
64
+ function dataForget(target: Record<string, unknown>, path: string): void {
65
+ const result = dataTraverse(target, path, false)
66
+ if (result) delete result[0][result[1]]
67
+ }
68
+
69
+ class ConfigRepository implements Contract {
70
+ protected items: ConfigItems
71
+
72
+ constructor(items: ConfigItems = {}) {
73
+ this.items = items
74
+ }
75
+
76
+ has(key: string | string[]): boolean {
77
+ const keys = Array.isArray(key) ? key : [key]
78
+ for (const configKey of keys) {
79
+ if (this.get(configKey) == null) {
80
+ return false
81
+ }
82
+ }
83
+
84
+ return true
85
+ }
86
+
87
+ get(key: string): unknown
88
+ get<T>(key: string): T | null
89
+ get<T>(key: string, defaultValue: T | (() => T)): T
90
+ get<T>(key: string, defaultValue: T | (() => T) | null): T | null
91
+ get(key: string[]): Record<string, unknown>
92
+ get<T = unknown>(
93
+ key: string | string[],
94
+ defaultValue: T | (() => T) | null = null,
95
+ ): T | Record<string, unknown> | null {
96
+ if (Array.isArray(key)) {
97
+ return this.getMany(key)
98
+ }
99
+
100
+ return dataGet(this.items, key, defaultValue) as T | null
101
+ }
102
+
103
+ getMany(keys: string[] | ConfigDefaults): Record<string, unknown> {
104
+ const results: Record<string, unknown> = {}
105
+
106
+ if (Array.isArray(keys)) {
107
+ for (const key of keys) {
108
+ results[key] = this.get(key)
109
+ }
110
+
111
+ return results
112
+ }
113
+
114
+ for (const [key, defaultValue] of Object.entries(keys)) {
115
+ results[key] = this.get(key, defaultValue)
116
+ }
117
+
118
+ return results
119
+ }
120
+
121
+ set(key: string | ConfigItems, value: unknown = null): void {
122
+ const payload = isPlainObject(key) ? key : { [key]: value }
123
+
124
+ for (const [configKey, configValue] of Object.entries(payload)) {
125
+ dataSet(this.items, configKey, configValue)
126
+ }
127
+ }
128
+
129
+ prepend(key: string, value: unknown): void {
130
+ const values = this.get<unknown[]>(key, [])
131
+ this.set(key, [value, ...values])
132
+ }
133
+
134
+ push(key: string, value: unknown): void {
135
+ const values = this.get<unknown[]>(key, [])
136
+ values.push(value)
137
+ this.set(key, values)
138
+ }
139
+
140
+ all(): ConfigItems {
141
+ return this.items
142
+ }
143
+
144
+ offsetExists(key: string): boolean {
145
+ return this.has(key)
146
+ }
147
+
148
+ offsetGet(key: string): unknown
149
+ offsetGet<T>(key: string): T | null
150
+ offsetGet<T = unknown>(key: string): T | null {
151
+ return this.get<T>(key) as T | null
152
+ }
153
+
154
+ offsetSet(key: string, value: unknown): void {
155
+ this.set(key, value)
156
+ }
157
+
158
+ offsetUnset(key: string): void {
159
+ dataForget(this.items, key)
160
+ }
161
+ }
162
+
163
+ export { ConfigRepository }
164
+ export default ConfigRepository
@@ -0,0 +1,323 @@
1
+ import type {
2
+ ContainerBindToSyntax,
3
+ ContainerClass,
4
+ ContainerConcrete,
5
+ ContainerFactory,
6
+ ContainerIdentifier,
7
+ } from "../contract"
8
+
9
+ const INJECT_TOKENS_METADATA_KEY = "ioc:inject.tokens"
10
+
11
+ type Scope = "singleton" | "transient"
12
+
13
+ type BindingRecord<T = unknown> =
14
+ | {
15
+ kind: "constant"
16
+ value: T
17
+ }
18
+ | {
19
+ kind: "class"
20
+ concrete: ContainerClass<T>
21
+ scope: Scope
22
+ cached?: T
23
+ }
24
+ | {
25
+ kind: "factory"
26
+ concrete: ContainerFactory<T>
27
+ scope: Scope
28
+ cached?: T
29
+ }
30
+
31
+ type MetadataReflect = typeof Reflect & {
32
+ defineMetadata?: (key: string, value: unknown, target: object) => void
33
+ getMetadata?: (key: string, target: object) => unknown
34
+ }
35
+
36
+ const metadataReflect = Reflect as MetadataReflect
37
+
38
+ function isClassConcrete<T>(value: ContainerConcrete<T>): value is ContainerClass<T> {
39
+ return typeof value === "function" && Object.hasOwn(value, "prototype")
40
+ }
41
+
42
+ function isFactoryConcrete<T>(value: ContainerConcrete<T>): value is ContainerFactory<T> {
43
+ return typeof value === "function" && !isClassConcrete(value)
44
+ }
45
+
46
+ function readInjectTokens(target: object): Map<number, ContainerIdentifier> {
47
+ const metadata = metadataReflect.getMetadata?.(INJECT_TOKENS_METADATA_KEY, target) as
48
+ | Record<number, ContainerIdentifier>
49
+ | undefined
50
+
51
+ const tokens = new Map<number, ContainerIdentifier>()
52
+ if (!metadata) {
53
+ return tokens
54
+ }
55
+
56
+ for (const [index, identifier] of Object.entries(metadata)) {
57
+ tokens.set(Number(index), identifier)
58
+ }
59
+
60
+ return tokens
61
+ }
62
+
63
+ function readConstructorParamTypes(target: object): Array<ContainerIdentifier | undefined> {
64
+ return (
65
+ (metadataReflect.getMetadata?.("design:paramtypes", target) as
66
+ | Array<ContainerIdentifier | undefined>
67
+ | undefined) ?? []
68
+ )
69
+ }
70
+
71
+ class BuiltinBindingFluent<T> {
72
+ protected record: BindingRecord<T> | null = null
73
+
74
+ constructor(
75
+ protected readonly container: BuiltinContainer,
76
+ protected readonly identifier: ContainerIdentifier<T>,
77
+ ) {}
78
+
79
+ to(concrete: ContainerConcrete<T>): {
80
+ inSingletonScope: () => void
81
+ inTransientScope: () => void
82
+ } {
83
+ if (isClassConcrete(concrete)) {
84
+ this.record = { kind: "class", concrete, scope: "singleton" }
85
+ } else if (isFactoryConcrete(concrete)) {
86
+ this.record = { kind: "factory", concrete, scope: "singleton" }
87
+ } else {
88
+ this.record = { kind: "constant", value: concrete as T }
89
+ }
90
+
91
+ this.container.setRecord(this.identifier, this.record)
92
+
93
+ return {
94
+ inSingletonScope: () => {
95
+ if (this.record && this.record.kind !== "constant") {
96
+ this.record.scope = "singleton"
97
+ }
98
+ },
99
+ inTransientScope: () => {
100
+ if (this.record && this.record.kind !== "constant") {
101
+ this.record.scope = "transient"
102
+ delete this.record.cached
103
+ }
104
+ },
105
+ }
106
+ }
107
+
108
+ toConstantValue(value: T): void {
109
+ this.record = { kind: "constant", value }
110
+ this.container.setRecord(this.identifier, this.record)
111
+ }
112
+ }
113
+
114
+ export function inject(identifier: ContainerIdentifier): ParameterDecorator {
115
+ return (target, _propertyKey, parameterIndex) => {
116
+ const existing =
117
+ (metadataReflect.getMetadata?.(INJECT_TOKENS_METADATA_KEY, target) as
118
+ | Record<number, ContainerIdentifier>
119
+ | undefined) ?? {}
120
+ existing[parameterIndex] = identifier
121
+ metadataReflect.defineMetadata?.(INJECT_TOKENS_METADATA_KEY, existing, target)
122
+ }
123
+ }
124
+
125
+ export default class BuiltinContainer implements ContainerContract {
126
+ protected bindings = new Map<ContainerIdentifier, BindingRecord>()
127
+ protected resolving = new Set<ContainerIdentifier>()
128
+
129
+ bind<T>(identifier: ContainerIdentifier<T>): ContainerBindToSyntax<T>
130
+ bind<T>(identifier: ContainerIdentifier<T>, concrete: ContainerConcrete<T>): this
131
+ bind<T>(identifier: ContainerIdentifier<T>, concrete?: ContainerConcrete<T>): ContainerBindToSyntax<T> | this {
132
+ if (typeof concrete !== "undefined") {
133
+ this.removeIfBound(identifier)
134
+
135
+ if (isClassConcrete(concrete)) {
136
+ this.bindings.set(identifier, {
137
+ kind: "class",
138
+ concrete,
139
+ scope: "transient",
140
+ })
141
+ return this
142
+ }
143
+
144
+ if (isFactoryConcrete(concrete)) {
145
+ this.bindings.set(identifier, {
146
+ kind: "factory",
147
+ concrete,
148
+ scope: "transient",
149
+ })
150
+ return this
151
+ }
152
+
153
+ this.bindings.set(identifier, {
154
+ kind: "factory",
155
+ concrete: () => concrete as T,
156
+ scope: "transient",
157
+ })
158
+ return this
159
+ }
160
+
161
+ if (this.bindings.has(identifier)) {
162
+ throw new Error(`Cannot bind [${String(identifier)}] because it is already bound.`)
163
+ }
164
+
165
+ return new BuiltinBindingFluent<T>(this, identifier)
166
+ }
167
+
168
+ singleton<T>(identifier: ContainerIdentifier<T>, concrete: ContainerConcrete<T>): this {
169
+ this.removeIfBound(identifier)
170
+
171
+ if (isClassConcrete(concrete)) {
172
+ this.bindings.set(identifier, {
173
+ kind: "class",
174
+ concrete,
175
+ scope: "singleton",
176
+ })
177
+ return this
178
+ }
179
+
180
+ if (isFactoryConcrete(concrete)) {
181
+ this.bindings.set(identifier, {
182
+ kind: "factory",
183
+ concrete,
184
+ scope: "singleton",
185
+ })
186
+ return this
187
+ }
188
+
189
+ this.bindings.set(identifier, {
190
+ kind: "constant",
191
+ value: concrete as T,
192
+ })
193
+ return this
194
+ }
195
+
196
+ instance<T>(identifier: ContainerIdentifier<T>, value: T): this {
197
+ this.removeIfBound(identifier)
198
+ this.bindings.set(identifier, { kind: "constant", value })
199
+ return this
200
+ }
201
+
202
+ make<T>(identifier: ContainerIdentifier<T>): T {
203
+ const record = this.bindings.get(identifier)
204
+ if (record) {
205
+ return this.resolveRecord(record, identifier) as T
206
+ }
207
+
208
+ if (typeof identifier === "function") {
209
+ return this.resolveClass(identifier as ContainerClass<T>)
210
+ }
211
+
212
+ throw new Error(`Container binding [${String(identifier)}] is not registered.`)
213
+ }
214
+
215
+ get<T>(identifier: ContainerIdentifier<T>): T {
216
+ return this.make(identifier)
217
+ }
218
+
219
+ bound(identifier: ContainerIdentifier): boolean {
220
+ return this.bindings.has(identifier)
221
+ }
222
+
223
+ has(identifier: ContainerIdentifier): boolean {
224
+ return this.bound(identifier)
225
+ }
226
+
227
+ unbind(identifier: ContainerIdentifier): void {
228
+ this.bindings.delete(identifier)
229
+ }
230
+
231
+ unbindAll(): void {
232
+ this.bindings.clear()
233
+ this.resolving.clear()
234
+ }
235
+
236
+ flush(): void {
237
+ this.unbindAll()
238
+ }
239
+
240
+ getRawContainer(): unknown {
241
+ return this.bindings
242
+ }
243
+
244
+ setRecord<T>(identifier: ContainerIdentifier<T>, record: BindingRecord<T>): void {
245
+ this.bindings.set(identifier, record as BindingRecord)
246
+ }
247
+
248
+ protected removeIfBound(identifier: ContainerIdentifier): void {
249
+ this.bindings.delete(identifier)
250
+ }
251
+
252
+ protected resolveRecord<T>(record: BindingRecord<T>, _identifier: ContainerIdentifier): T {
253
+ if (record.kind === "constant") {
254
+ return record.value
255
+ }
256
+
257
+ if (this.hasCachedSingleton(record)) {
258
+ return record.cached
259
+ }
260
+
261
+ const resolved = this.resolveConcrete(record)
262
+
263
+ if (record.scope === "singleton") {
264
+ record.cached = resolved
265
+ }
266
+
267
+ return resolved
268
+ }
269
+
270
+ protected resolveConcrete<T>(record: Exclude<BindingRecord<T>, { kind: "constant" }>): T {
271
+ if (record.kind === "class") {
272
+ return this.resolveClass(record.concrete)
273
+ }
274
+
275
+ return record.concrete(this)
276
+ }
277
+
278
+ protected hasCachedSingleton<T>(record: BindingRecord<T>): record is BindingRecord<T> & { cached: T } {
279
+ return (
280
+ record.kind !== "constant" &&
281
+ record.scope === "singleton" &&
282
+ "cached" in record &&
283
+ typeof record.cached !== "undefined"
284
+ )
285
+ }
286
+
287
+ protected resolveClass<T>(concrete: ContainerClass<T>): T {
288
+ this.assertNotResolving(concrete)
289
+ this.resolving.add(concrete)
290
+
291
+ try {
292
+ const paramTypes = readConstructorParamTypes(concrete)
293
+ const injectTokens = readInjectTokens(concrete)
294
+ const args = paramTypes.map((designType, index) =>
295
+ this.resolveParameter(concrete, injectTokens, designType, index),
296
+ )
297
+
298
+ return new concrete(...args)
299
+ } finally {
300
+ this.resolving.delete(concrete)
301
+ }
302
+ }
303
+
304
+ protected assertNotResolving(concrete: ContainerClass<unknown>): void {
305
+ if (this.resolving.has(concrete)) {
306
+ throw new Error(`Circular dependency detected while resolving [${concrete.name || "anonymous"}].`)
307
+ }
308
+ }
309
+
310
+ protected resolveParameter(
311
+ concrete: ContainerClass<unknown>,
312
+ injectTokens: Map<number, ContainerIdentifier>,
313
+ designType: ContainerIdentifier | undefined,
314
+ index: number,
315
+ ): unknown {
316
+ const token = injectTokens.get(index) ?? designType
317
+ if (!token) {
318
+ throw new Error(`Cannot resolve parameter #${index} for [${concrete.name || "anonymous"}].`)
319
+ }
320
+
321
+ return this.make(token)
322
+ }
323
+ }
@@ -0,0 +1,43 @@
1
+ export type ContainerFactory<T> = (container: ContainerContract) => T
2
+ export type ContainerClass<T> = new (...args: any[]) => T
3
+ export type ContainerIdentifier<T = unknown> = string | symbol | ContainerClass<T>
4
+ export type ContainerConcrete<T> = ContainerClass<T> | ContainerFactory<T> | T
5
+
6
+ export interface ContainerScopeSyntax {
7
+ /** Set singleton scope for this binding. */
8
+ inSingletonScope(): void
9
+ /** Set transient scope for this binding. */
10
+ inTransientScope(): void
11
+ }
12
+
13
+ export interface ContainerBindToSyntax<T> {
14
+ /** Bind an identifier to a class, factory, or value concrete. */
15
+ to(concrete: ContainerConcrete<T>): ContainerScopeSyntax
16
+ /** Bind an identifier to a constant shared value. */
17
+ toConstantValue(value: T): void
18
+ }
19
+
20
+ export interface ContainerContract {
21
+ /** Register a singleton binding using a class, factory, or value concrete. */
22
+ singleton<T>(identifier: ContainerIdentifier<T>, concrete: ContainerConcrete<T>): this
23
+ /** Register a binding using a class, factory, or value concrete. */
24
+ bind<T>(identifier: ContainerIdentifier<T>, concrete: ContainerConcrete<T>): this
25
+ /** Register an existing instance as a shared binding. */
26
+ instance<T>(identifier: ContainerIdentifier<T>, value: T): this
27
+ /** Resolve an instance from the container. */
28
+ make<T>(identifier: ContainerIdentifier<T>): T
29
+ /** Compatibility alias for make(). */
30
+ get<T>(identifier: ContainerIdentifier<T>): T
31
+ /** Determine if an identifier is currently bound. */
32
+ bound(identifier: ContainerIdentifier): boolean
33
+ /** Alias for bound(). */
34
+ has(identifier: ContainerIdentifier): boolean
35
+ /** Remove a specific binding by identifier. */
36
+ unbind(identifier: ContainerIdentifier): void
37
+ /** Remove all bindings from the container. */
38
+ unbindAll(): void
39
+ /** Clear all bindings. */
40
+ flush(): void
41
+ /** Expose the underlying container for advanced use. */
42
+ getRawContainer(): unknown
43
+ }
@@ -0,0 +1,29 @@
1
+ import BuiltinContainer from "./adapters/builtin"
2
+ import type { ContainerContract } from "./contract"
3
+
4
+ export type ContainerAdapter = "builtin"
5
+
6
+ export type ContainerRuntimeOptions = {
7
+ adapter?: ContainerAdapter
8
+ factory?: (() => ContainerContract) | null
9
+ }
10
+
11
+ export type ResolvedContainerRuntimeOptions = {
12
+ adapter: ContainerAdapter
13
+ factory: (() => ContainerContract) | null
14
+ }
15
+
16
+ export function mergeContainerRuntimeOptions(options: ContainerRuntimeOptions = {}): ResolvedContainerRuntimeOptions {
17
+ return {
18
+ adapter: options.adapter ?? "builtin",
19
+ factory: options.factory ?? null,
20
+ }
21
+ }
22
+
23
+ export function createRuntimeContainer(options: ResolvedContainerRuntimeOptions): ContainerContract {
24
+ if (typeof options.factory === "function") {
25
+ return options.factory()
26
+ }
27
+
28
+ return new BuiltinContainer()
29
+ }