@raubjo/architect-core 0.1.0 → 0.1.2

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 (67) hide show
  1. package/README.md +216 -0
  2. package/bun.lock +82 -1
  3. package/package.json +55 -6
  4. package/src/cache/cache.ts +2 -2
  5. package/src/cache/manager.ts +93 -83
  6. package/src/config/adapters/esm.ts +26 -0
  7. package/src/config/clone.ts +5 -5
  8. package/src/config/env.global.d.ts +2 -1
  9. package/src/config/env.ts +53 -55
  10. package/src/config/env_test.helpers.ts +58 -0
  11. package/src/config/repository.ts +180 -142
  12. package/src/container/adapters/builtin.ts +347 -0
  13. package/src/container/adapters/inversify.ts +123 -0
  14. package/src/container/contract.ts +58 -0
  15. package/src/container/runtime.ts +149 -0
  16. package/src/filesystem/adapters/local.ts +92 -83
  17. package/src/filesystem/adapters/local_test.helpers.ts +50 -0
  18. package/src/filesystem/filesystem.ts +11 -11
  19. package/src/foundation/application.ts +205 -175
  20. package/src/foundation/application_test.helpers.ts +31 -0
  21. package/src/index.ts +15 -6
  22. package/src/react.ts +2 -0
  23. package/src/renderers/adapters/react.tsx +35 -0
  24. package/src/renderers/adapters/solid.tsx +32 -0
  25. package/src/renderers/adapters/svelte.ts +70 -0
  26. package/src/renderers/adapters/vue.ts +28 -0
  27. package/src/renderers/contract.ts +15 -0
  28. package/src/runtimes/react.tsx +24 -12
  29. package/src/runtimes/solid.tsx +30 -0
  30. package/src/runtimes/svelte.ts +23 -0
  31. package/src/runtimes/vue.ts +20 -0
  32. package/src/solid.ts +2 -0
  33. package/src/storage/adapters/contract.ts +10 -0
  34. package/src/storage/adapters/indexed-db.ts +170 -156
  35. package/src/storage/adapters/local-storage.ts +34 -34
  36. package/src/storage/adapters/memory.ts +25 -25
  37. package/src/storage/manager.ts +65 -61
  38. package/src/storage/storage.ts +1 -8
  39. package/src/support/facades/cache.ts +40 -40
  40. package/src/support/facades/config.ts +78 -48
  41. package/src/support/facades/facade.ts +43 -28
  42. package/src/support/facades/storage.ts +41 -41
  43. package/src/support/service-provider.ts +11 -11
  44. package/src/support/str.ts +94 -90
  45. package/src/support/str_test.helpers.ts +26 -0
  46. package/src/svelte.ts +2 -0
  47. package/src/vue.ts +2 -0
  48. package/tsconfig.json +16 -0
  49. package/coverage/lcov.info +0 -1078
  50. package/src/config/app.ts +0 -5
  51. package/src/rendering/adapters/react.tsx +0 -27
  52. package/src/rendering/renderer.ts +0 -13
  53. package/src/support/providers/config-service-provider.ts +0 -19
  54. package/tests/application.test.ts +0 -236
  55. package/tests/cache-facade.test.ts +0 -45
  56. package/tests/cache.test.ts +0 -68
  57. package/tests/config-clone.test.ts +0 -31
  58. package/tests/config-env.test.ts +0 -88
  59. package/tests/config-facade.test.ts +0 -96
  60. package/tests/config-repository.test.ts +0 -124
  61. package/tests/facade-base.test.ts +0 -80
  62. package/tests/filesystem.test.ts +0 -81
  63. package/tests/runtime-react.test.tsx +0 -37
  64. package/tests/service-provider.test.ts +0 -23
  65. package/tests/storage-facade.test.ts +0 -46
  66. package/tests/storage.test.ts +0 -264
  67. package/tests/str.test.ts +0 -73
@@ -0,0 +1,347 @@
1
+ import type {
2
+ ContainerClass,
3
+ ContainerBindToSyntax,
4
+ ContainerConcrete,
5
+ ContainerContract,
6
+ ContainerFactory,
7
+ ContainerIdentifier,
8
+ } from "@/container/contract";
9
+
10
+ const INJECT_TOKENS_METADATA_KEY = "ioc:inject.tokens";
11
+
12
+ type Scope = "singleton" | "transient";
13
+
14
+ type BindingRecord<T = unknown> =
15
+ | {
16
+ kind: "constant";
17
+ value: T;
18
+ }
19
+ | {
20
+ kind: "class";
21
+ concrete: ContainerClass<T>;
22
+ scope: Scope;
23
+ cached?: T;
24
+ }
25
+ | {
26
+ kind: "factory";
27
+ concrete: ContainerFactory<T>;
28
+ scope: Scope;
29
+ cached?: T;
30
+ };
31
+
32
+ type MetadataReflect = typeof Reflect & {
33
+ defineMetadata?: (key: string, value: unknown, target: object) => void;
34
+ getMetadata?: (key: string, target: object) => unknown;
35
+ };
36
+
37
+ const metadataReflect = Reflect as MetadataReflect;
38
+
39
+ function isClassConcrete<T>(
40
+ value: ContainerConcrete<T>,
41
+ ): value is ContainerClass<T> {
42
+ return (
43
+ typeof value === "function" &&
44
+ Object.prototype.hasOwnProperty.call(value, "prototype")
45
+ );
46
+ }
47
+
48
+ function isFactoryConcrete<T>(
49
+ value: ContainerConcrete<T>,
50
+ ): value is ContainerFactory<T> {
51
+ return typeof value === "function" && !isClassConcrete(value);
52
+ }
53
+
54
+ function readInjectTokens(target: object): Map<number, ContainerIdentifier> {
55
+ const metadata = metadataReflect.getMetadata?.(
56
+ INJECT_TOKENS_METADATA_KEY,
57
+ target,
58
+ ) as Record<number, ContainerIdentifier> | undefined;
59
+
60
+ const tokens = new Map<number, ContainerIdentifier>();
61
+ if (!metadata) {
62
+ return tokens;
63
+ }
64
+
65
+ for (const [index, identifier] of Object.entries(metadata)) {
66
+ tokens.set(Number(index), identifier);
67
+ }
68
+
69
+ return tokens;
70
+ }
71
+
72
+ class BuiltinBindingFluent<T> {
73
+ protected record: BindingRecord<T> | null = null;
74
+
75
+ constructor(
76
+ protected readonly container: BuiltinContainer,
77
+ protected readonly identifier: ContainerIdentifier<T>,
78
+ ) {}
79
+
80
+ to(concrete: ContainerClass<T>): {
81
+ inSingletonScope: () => void;
82
+ inTransientScope: () => void;
83
+ } {
84
+ this.record = { kind: "class", concrete, scope: "singleton" };
85
+ this.container.setRecord(this.identifier, this.record);
86
+
87
+ return {
88
+ inSingletonScope: () => {
89
+ if (this.record && this.record.kind !== "constant") {
90
+ this.record.scope = "singleton";
91
+ }
92
+ },
93
+ inTransientScope: () => {
94
+ if (this.record && this.record.kind !== "constant") {
95
+ this.record.scope = "transient";
96
+ delete this.record.cached;
97
+ }
98
+ },
99
+ };
100
+ }
101
+
102
+ toDynamicValue(
103
+ concrete: (context: { container: ContainerContract }) => T,
104
+ ): { inSingletonScope: () => void; inTransientScope: () => void } {
105
+ this.record = {
106
+ kind: "factory",
107
+ concrete: () => concrete({ container: this.container }),
108
+ scope: "singleton",
109
+ };
110
+ this.container.setRecord(this.identifier, this.record);
111
+
112
+ return {
113
+ inSingletonScope: () => {
114
+ if (this.record && this.record.kind !== "constant") {
115
+ this.record.scope = "singleton";
116
+ }
117
+ },
118
+ inTransientScope: () => {
119
+ if (this.record && this.record.kind !== "constant") {
120
+ this.record.scope = "transient";
121
+ delete this.record.cached;
122
+ }
123
+ },
124
+ };
125
+ }
126
+
127
+ toConstantValue(value: T): void {
128
+ this.record = { kind: "constant", value };
129
+ this.container.setRecord(this.identifier, this.record);
130
+ }
131
+ }
132
+
133
+ export function inject(identifier: ContainerIdentifier): ParameterDecorator {
134
+ return (target, _propertyKey, parameterIndex) => {
135
+ const existing =
136
+ (metadataReflect.getMetadata?.(
137
+ INJECT_TOKENS_METADATA_KEY,
138
+ target,
139
+ ) as Record<number, ContainerIdentifier> | undefined) ?? {};
140
+ existing[parameterIndex] = identifier;
141
+ metadataReflect.defineMetadata?.(
142
+ INJECT_TOKENS_METADATA_KEY,
143
+ existing,
144
+ target,
145
+ );
146
+ };
147
+ }
148
+
149
+ export default class BuiltinContainer implements ContainerContract {
150
+ protected bindings = new Map<ContainerIdentifier, BindingRecord>();
151
+ protected resolving = new Set<ContainerIdentifier>();
152
+
153
+ bind<T>(identifier: ContainerIdentifier<T>): ContainerBindToSyntax<T> {
154
+ if (this.bindings.has(identifier)) {
155
+ throw new Error(
156
+ `Cannot bind [${String(identifier)}] because it is already bound.`,
157
+ );
158
+ }
159
+
160
+ return new BuiltinBindingFluent<T>(this, identifier);
161
+ }
162
+
163
+ singleton<T>(
164
+ identifier: ContainerIdentifier<T>,
165
+ concrete: ContainerConcrete<T>,
166
+ ): this {
167
+ this.removeIfBound(identifier);
168
+
169
+ if (isClassConcrete(concrete)) {
170
+ this.bindings.set(identifier, {
171
+ kind: "class",
172
+ concrete,
173
+ scope: "singleton",
174
+ });
175
+ return this;
176
+ }
177
+
178
+ if (isFactoryConcrete(concrete)) {
179
+ this.bindings.set(identifier, {
180
+ kind: "factory",
181
+ concrete,
182
+ scope: "singleton",
183
+ });
184
+ return this;
185
+ }
186
+
187
+ this.bindings.set(identifier, {
188
+ kind: "constant",
189
+ value: concrete as T,
190
+ });
191
+ return this;
192
+ }
193
+
194
+ transient<T>(
195
+ identifier: ContainerIdentifier<T>,
196
+ concrete: ContainerConcrete<T>,
197
+ ): this {
198
+ this.removeIfBound(identifier);
199
+
200
+ if (isClassConcrete(concrete)) {
201
+ this.bindings.set(identifier, {
202
+ kind: "class",
203
+ concrete,
204
+ scope: "transient",
205
+ });
206
+ return this;
207
+ }
208
+
209
+ if (isFactoryConcrete(concrete)) {
210
+ this.bindings.set(identifier, {
211
+ kind: "factory",
212
+ concrete,
213
+ scope: "transient",
214
+ });
215
+ return this;
216
+ }
217
+
218
+ this.bindings.set(identifier, {
219
+ kind: "factory",
220
+ concrete: () => concrete as T,
221
+ scope: "transient",
222
+ });
223
+ return this;
224
+ }
225
+
226
+ instance<T>(identifier: ContainerIdentifier<T>, value: T): this {
227
+ this.removeIfBound(identifier);
228
+ this.bindings.set(identifier, { kind: "constant", value });
229
+ return this;
230
+ }
231
+
232
+ make<T>(identifier: ContainerIdentifier<T>): T {
233
+ const record = this.bindings.get(identifier);
234
+ if (record) {
235
+ return this.resolveRecord(record, identifier) as T;
236
+ }
237
+
238
+ if (typeof identifier === "function") {
239
+ return this.resolveClass(identifier as ContainerClass<T>);
240
+ }
241
+
242
+ throw new Error(
243
+ `Container binding [${String(identifier)}] is not registered.`,
244
+ );
245
+ }
246
+
247
+ get<T>(identifier: ContainerIdentifier<T>): T {
248
+ return this.make(identifier);
249
+ }
250
+
251
+ bound(identifier: ContainerIdentifier): boolean {
252
+ return this.bindings.has(identifier);
253
+ }
254
+
255
+ has(identifier: ContainerIdentifier): boolean {
256
+ return this.bound(identifier);
257
+ }
258
+
259
+ unbind(identifier: ContainerIdentifier): void {
260
+ this.bindings.delete(identifier);
261
+ }
262
+
263
+ unbindAll(): void {
264
+ this.bindings.clear();
265
+ this.resolving.clear();
266
+ }
267
+
268
+ flush(): void {
269
+ this.unbindAll();
270
+ }
271
+
272
+ getRawContainer(): unknown {
273
+ return this.bindings;
274
+ }
275
+
276
+ setRecord<T>(
277
+ identifier: ContainerIdentifier<T>,
278
+ record: BindingRecord<T>,
279
+ ): void {
280
+ this.bindings.set(identifier, record as BindingRecord);
281
+ }
282
+
283
+ protected removeIfBound(identifier: ContainerIdentifier): void {
284
+ this.bindings.delete(identifier);
285
+ }
286
+
287
+ protected resolveRecord<T>(
288
+ record: BindingRecord<T>,
289
+ identifier: ContainerIdentifier,
290
+ ): T {
291
+ if (record.kind === "constant") {
292
+ return record.value;
293
+ }
294
+
295
+ if (
296
+ record.scope === "singleton" &&
297
+ "cached" in record &&
298
+ typeof record.cached !== "undefined"
299
+ ) {
300
+ return record.cached;
301
+ }
302
+
303
+ const resolved =
304
+ record.kind === "class" ?
305
+ this.resolveClass(record.concrete)
306
+ : record.concrete(this);
307
+
308
+ if (record.scope === "singleton") {
309
+ record.cached = resolved;
310
+ }
311
+
312
+ return resolved;
313
+ }
314
+
315
+ protected resolveClass<T>(concrete: ContainerClass<T>): T {
316
+ if (this.resolving.has(concrete)) {
317
+ throw new Error(
318
+ `Circular dependency detected while resolving [${concrete.name || "anonymous"}].`,
319
+ );
320
+ }
321
+
322
+ this.resolving.add(concrete);
323
+
324
+ try {
325
+ const paramTypes =
326
+ (metadataReflect.getMetadata?.(
327
+ "design:paramtypes",
328
+ concrete,
329
+ ) as Array<ContainerIdentifier | undefined> | undefined) ?? [];
330
+ const injectTokens = readInjectTokens(concrete);
331
+ const args = paramTypes.map((designType, index) => {
332
+ const token = injectTokens.get(index) ?? designType;
333
+ if (!token) {
334
+ throw new Error(
335
+ `Cannot resolve parameter #${index} for [${concrete.name || "anonymous"}].`,
336
+ );
337
+ }
338
+
339
+ return this.make(token);
340
+ });
341
+
342
+ return new concrete(...args);
343
+ } finally {
344
+ this.resolving.delete(concrete);
345
+ }
346
+ }
347
+ }
@@ -0,0 +1,123 @@
1
+ import { Container, type BindToFluentSyntax, type Newable } from "inversify";
2
+ import type {
3
+ ContainerBindToSyntax,
4
+ ContainerConcrete,
5
+ ContainerContract,
6
+ ContainerFactory,
7
+ ContainerIdentifier,
8
+ } from "@/container/contract";
9
+
10
+ function isClassConcrete<T>(value: ContainerConcrete<T>): value is Newable<T> {
11
+ return (
12
+ typeof value === "function" &&
13
+ Object.prototype.hasOwnProperty.call(value, "prototype")
14
+ );
15
+ }
16
+
17
+ function isFactoryConcrete<T>(
18
+ value: ContainerConcrete<T>,
19
+ ): value is ContainerFactory<T> {
20
+ return typeof value === "function" && !isClassConcrete(value);
21
+ }
22
+
23
+ export default class InversifyContainer implements ContainerContract {
24
+ protected container: Container;
25
+
26
+ constructor(container = new Container({ defaultScope: "Singleton" })) {
27
+ this.container = container;
28
+ }
29
+
30
+ bind<T>(identifier: ContainerIdentifier<T>): ContainerBindToSyntax<T> {
31
+ return this.container.bind<T>(
32
+ identifier,
33
+ ) as unknown as BindToFluentSyntax<T> & ContainerBindToSyntax<T>;
34
+ }
35
+
36
+ singleton<T>(
37
+ identifier: ContainerIdentifier<T>,
38
+ concrete: ContainerConcrete<T>,
39
+ ): this {
40
+ this.removeIfBound(identifier);
41
+
42
+ const binding = this.container.bind(identifier);
43
+ if (isClassConcrete(concrete)) {
44
+ binding.to(concrete).inSingletonScope();
45
+ return this;
46
+ }
47
+
48
+ if (isFactoryConcrete(concrete)) {
49
+ binding.toDynamicValue(() => concrete(this)).inSingletonScope();
50
+ return this;
51
+ }
52
+
53
+ binding.toConstantValue(concrete as T);
54
+ return this;
55
+ }
56
+
57
+ transient<T>(
58
+ identifier: ContainerIdentifier<T>,
59
+ concrete: ContainerConcrete<T>,
60
+ ): this {
61
+ this.removeIfBound(identifier);
62
+
63
+ const binding = this.container.bind(identifier);
64
+ if (isClassConcrete(concrete)) {
65
+ binding.to(concrete).inTransientScope();
66
+ return this;
67
+ }
68
+
69
+ if (isFactoryConcrete(concrete)) {
70
+ binding.toDynamicValue(() => concrete(this)).inTransientScope();
71
+ return this;
72
+ }
73
+
74
+ binding.toDynamicValue(() => concrete as T).inTransientScope();
75
+ return this;
76
+ }
77
+
78
+ instance<T>(identifier: ContainerIdentifier<T>, value: T): this {
79
+ this.removeIfBound(identifier);
80
+ this.container.bind(identifier).toConstantValue(value);
81
+ return this;
82
+ }
83
+
84
+ make<T>(identifier: ContainerIdentifier<T>): T {
85
+ return this.container.get<T>(identifier);
86
+ }
87
+
88
+ get<T>(identifier: ContainerIdentifier<T>): T {
89
+ return this.make<T>(identifier);
90
+ }
91
+
92
+ bound(identifier: ContainerIdentifier): boolean {
93
+ return this.container.isBound(identifier);
94
+ }
95
+
96
+ has(identifier: ContainerIdentifier): boolean {
97
+ return this.bound(identifier);
98
+ }
99
+
100
+ unbind(identifier: ContainerIdentifier): void {
101
+ if (this.container.isBound(identifier)) {
102
+ this.container.unbind(identifier);
103
+ }
104
+ }
105
+
106
+ unbindAll(): void {
107
+ this.container.unbindAll();
108
+ }
109
+
110
+ flush(): void {
111
+ this.unbindAll();
112
+ }
113
+
114
+ getRawContainer(): unknown {
115
+ return this.container;
116
+ }
117
+
118
+ protected removeIfBound(identifier: ContainerIdentifier): void {
119
+ if (this.container.isBound(identifier)) {
120
+ this.container.unbind(identifier);
121
+ }
122
+ }
123
+ }
@@ -0,0 +1,58 @@
1
+ export type ContainerFactory<T> = (container: ContainerContract) => T;
2
+ export type ContainerClass<T> = new (...args: any[]) => T;
3
+ export type ContainerIdentifier<T = unknown> =
4
+ | string
5
+ | symbol
6
+ | ContainerClass<T>;
7
+ export type ContainerConcrete<T> = ContainerClass<T> | ContainerFactory<T> | T;
8
+
9
+ export interface ContainerScopeSyntax {
10
+ /** Set singleton scope for this binding. */
11
+ inSingletonScope(): void;
12
+ /** Set transient scope for this binding. */
13
+ inTransientScope(): void;
14
+ }
15
+
16
+ export interface ContainerBindToSyntax<T> {
17
+ /** Bind an identifier to a concrete class. */
18
+ to(concrete: ContainerClass<T>): ContainerScopeSyntax;
19
+ /** Bind an identifier to a dynamic factory callback. */
20
+ toDynamicValue(
21
+ concrete: (context: { container: ContainerContract }) => T,
22
+ ): ContainerScopeSyntax;
23
+ /** Bind an identifier to a constant shared value. */
24
+ toConstantValue(value: T): void;
25
+ }
26
+
27
+ export interface ContainerContract {
28
+ /** Register a binding fluent definition for an identifier. */
29
+ bind<T>(identifier: ContainerIdentifier<T>): ContainerBindToSyntax<T>;
30
+ /** Register a singleton binding using a class, factory, or value concrete. */
31
+ singleton<T>(
32
+ identifier: ContainerIdentifier<T>,
33
+ concrete: ContainerConcrete<T>,
34
+ ): this;
35
+ /** Register a transient binding using a class, factory, or value concrete. */
36
+ transient<T>(
37
+ identifier: ContainerIdentifier<T>,
38
+ concrete: ContainerConcrete<T>,
39
+ ): this;
40
+ /** Register an existing instance as a shared binding. */
41
+ instance<T>(identifier: ContainerIdentifier<T>, value: T): this;
42
+ /** Resolve an instance from the container. */
43
+ make<T>(identifier: ContainerIdentifier<T>): T;
44
+ /** Compatibility alias for make(). */
45
+ get<T>(identifier: ContainerIdentifier<T>): T;
46
+ /** Determine if an identifier is currently bound. */
47
+ bound(identifier: ContainerIdentifier): boolean;
48
+ /** Alias for bound(). */
49
+ has(identifier: ContainerIdentifier): boolean;
50
+ /** Remove a specific binding by identifier. */
51
+ unbind(identifier: ContainerIdentifier): void;
52
+ /** Remove all bindings from the container. */
53
+ unbindAll(): void;
54
+ /** Clear all bindings. */
55
+ flush(): void;
56
+ /** Expose the underlying container for advanced use. */
57
+ getRawContainer(): unknown;
58
+ }
@@ -0,0 +1,149 @@
1
+ import BuiltinContainer from "./adapters/builtin";
2
+ import type { ContainerContract } from "./contract";
3
+
4
+ export type ContainerAdapter = "auto" | "builtin" | "inversify";
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
+ type PackageJsonLike = {
17
+ dependencies?: Record<string, string>;
18
+ devDependencies?: Record<string, string>;
19
+ peerDependencies?: Record<string, string>;
20
+ };
21
+
22
+ type ContainerFactoryRegistry = {
23
+ inversify?: () => ContainerContract;
24
+ };
25
+
26
+ type GlobLoader = (
27
+ pattern: string | string[],
28
+ options?: { eager?: boolean },
29
+ ) => Record<string, unknown>;
30
+
31
+ export function mergeContainerRuntimeOptions(
32
+ options: ContainerRuntimeOptions = {},
33
+ ): ResolvedContainerRuntimeOptions {
34
+ return {
35
+ adapter: options.adapter ?? "auto",
36
+ factory: options.factory ?? null,
37
+ };
38
+ }
39
+
40
+ export function readPackageJsonCandidates(): PackageJsonLike[] {
41
+ const testValue = (
42
+ globalThis as {
43
+ __iocPackageJsonForTests?:
44
+ | PackageJsonLike
45
+ | PackageJsonLike[]
46
+ | (() => PackageJsonLike | PackageJsonLike[]);
47
+ }
48
+ ).__iocPackageJsonForTests;
49
+
50
+ if (typeof testValue === "function") {
51
+ const value = testValue();
52
+ return Array.isArray(value) ? value : [value];
53
+ }
54
+ if (testValue) {
55
+ return Array.isArray(testValue) ? testValue : [testValue];
56
+ }
57
+
58
+ const viteGlob = (
59
+ import.meta as ImportMeta & {
60
+ glob?: GlobLoader;
61
+ }
62
+ ).glob;
63
+ const testGlob = (
64
+ globalThis as {
65
+ __iocPackageJsonGlobForTests?: GlobLoader;
66
+ }
67
+ ).__iocPackageJsonGlobForTests;
68
+ const glob =
69
+ typeof viteGlob === "function" ? viteGlob
70
+ : typeof testGlob === "function" ? testGlob
71
+ : null;
72
+ if (!glob) {
73
+ return [];
74
+ }
75
+
76
+ const modules = glob("/package.json", { eager: true });
77
+ return Object.values(modules)
78
+ .map((value) => {
79
+ if (
80
+ value &&
81
+ typeof value === "object" &&
82
+ "default" in (value as Record<string, unknown>)
83
+ ) {
84
+ return (value as { default: unknown })
85
+ .default as PackageJsonLike;
86
+ }
87
+ return value as PackageJsonLike;
88
+ })
89
+ .filter((value) => Boolean(value && typeof value === "object"));
90
+ }
91
+
92
+ export function packageJsonHasDependency(
93
+ packageJson: PackageJsonLike,
94
+ dependency: string,
95
+ ): boolean {
96
+ return Boolean(
97
+ packageJson.dependencies?.[dependency] ||
98
+ packageJson.devDependencies?.[dependency] ||
99
+ packageJson.peerDependencies?.[dependency],
100
+ );
101
+ }
102
+
103
+ export function readContainerFactoryRegistry(): ContainerFactoryRegistry {
104
+ return (
105
+ (
106
+ globalThis as {
107
+ __iocContainerFactoryRegistry?: ContainerFactoryRegistry;
108
+ }
109
+ ).__iocContainerFactoryRegistry ?? {}
110
+ );
111
+ }
112
+
113
+ export function hasInversifyDependency(): boolean {
114
+ const packageJsonCandidates = readPackageJsonCandidates();
115
+ return packageJsonCandidates.some((packageJson) =>
116
+ packageJsonHasDependency(packageJson, "inversify"),
117
+ );
118
+ }
119
+
120
+ export function createRuntimeContainer(
121
+ options: ResolvedContainerRuntimeOptions,
122
+ ): ContainerContract {
123
+ if (typeof options.factory === "function") {
124
+ return options.factory();
125
+ }
126
+
127
+ if (options.adapter === "builtin") {
128
+ return new BuiltinContainer();
129
+ }
130
+
131
+ const inversifyFactory = readContainerFactoryRegistry().inversify;
132
+ const shouldUseInversify =
133
+ options.adapter === "inversify" ||
134
+ (options.adapter === "auto" && hasInversifyDependency());
135
+
136
+ if (shouldUseInversify) {
137
+ if (typeof inversifyFactory === "function") {
138
+ return inversifyFactory();
139
+ }
140
+
141
+ if (options.adapter === "inversify") {
142
+ throw new Error(
143
+ "Inversify adapter is not registered. Provide container.factory or register __iocContainerFactoryRegistry.inversify.",
144
+ );
145
+ }
146
+ }
147
+
148
+ return new BuiltinContainer();
149
+ }