@raubjo/architect-core 0.1.0 → 0.1.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 +162 -0
- package/bun.lock +82 -1
- package/package.json +54 -6
- package/src/cache/cache.ts +2 -2
- package/src/cache/manager.ts +93 -83
- package/src/config/adapters/esm.ts +26 -0
- package/src/config/clone.ts +5 -5
- package/src/config/env.global.d.ts +2 -1
- package/src/config/env.ts +53 -55
- package/src/config/env_test.helpers.ts +58 -0
- package/src/config/repository.ts +180 -142
- package/src/container/adapters/builtin.ts +347 -0
- package/src/container/adapters/inversify.ts +123 -0
- package/src/container/contract.ts +58 -0
- package/src/container/runtime.ts +149 -0
- package/src/filesystem/adapters/local.ts +92 -83
- package/src/filesystem/adapters/local_test.helpers.ts +50 -0
- package/src/filesystem/filesystem.ts +11 -11
- package/src/foundation/application.ts +205 -175
- package/src/foundation/application_test.helpers.ts +31 -0
- package/src/index.ts +15 -6
- package/src/react.ts +2 -0
- package/src/renderers/adapters/react.tsx +35 -0
- package/src/renderers/adapters/solid.tsx +32 -0
- package/src/renderers/adapters/svelte.ts +44 -0
- package/src/renderers/adapters/vue.ts +28 -0
- package/src/renderers/contract.ts +15 -0
- package/src/runtimes/react.tsx +24 -12
- package/src/runtimes/solid.tsx +30 -0
- package/src/runtimes/svelte.ts +23 -0
- package/src/runtimes/vue.ts +20 -0
- package/src/solid.ts +2 -0
- package/src/storage/adapters/contract.ts +10 -0
- package/src/storage/adapters/indexed-db.ts +170 -156
- package/src/storage/adapters/local-storage.ts +34 -34
- package/src/storage/adapters/memory.ts +25 -25
- package/src/storage/manager.ts +65 -61
- package/src/storage/storage.ts +1 -8
- package/src/support/facades/cache.ts +40 -40
- package/src/support/facades/config.ts +78 -48
- package/src/support/facades/facade.ts +43 -28
- package/src/support/facades/storage.ts +41 -41
- package/src/support/service-provider.ts +11 -11
- package/src/support/str.ts +94 -90
- package/src/support/str_test.helpers.ts +26 -0
- package/src/svelte.ts +2 -0
- package/src/vue.ts +2 -0
- package/tsconfig.json +16 -0
- package/coverage/lcov.info +0 -1078
- package/src/config/app.ts +0 -5
- package/src/rendering/adapters/react.tsx +0 -27
- package/src/rendering/renderer.ts +0 -13
- package/src/support/providers/config-service-provider.ts +0 -19
- package/tests/application.test.ts +0 -236
- package/tests/cache-facade.test.ts +0 -45
- package/tests/cache.test.ts +0 -68
- package/tests/config-clone.test.ts +0 -31
- package/tests/config-env.test.ts +0 -88
- package/tests/config-facade.test.ts +0 -96
- package/tests/config-repository.test.ts +0 -124
- package/tests/facade-base.test.ts +0 -80
- package/tests/filesystem.test.ts +0 -81
- package/tests/runtime-react.test.tsx +0 -37
- package/tests/service-provider.test.ts +0 -23
- package/tests/storage-facade.test.ts +0 -46
- package/tests/storage.test.ts +0 -264
- package/tests/str.test.ts +0 -73
package/src/config/app.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import ReactDOM from "react-dom/client";
|
|
2
|
-
import { createElement } from "react";
|
|
3
|
-
import { ApplicationProvider } from "../../runtimes/react";
|
|
4
|
-
import type { RendererAdapter, RendererContext } from "../renderer";
|
|
5
|
-
import type { Cleanup } from "../../support/service-provider";
|
|
6
|
-
|
|
7
|
-
export default class ReactRenderer implements RendererAdapter {
|
|
8
|
-
constructor() {}
|
|
9
|
-
|
|
10
|
-
render({ RootComponent, container, rootElementId }: RendererContext): Cleanup {
|
|
11
|
-
const mountNode = document.getElementById(rootElementId);
|
|
12
|
-
if (!mountNode) {
|
|
13
|
-
throw new Error(`Missing mount node #${rootElementId}.`);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
const root = ReactDOM.createRoot(mountNode);
|
|
17
|
-
root.render(
|
|
18
|
-
createElement(
|
|
19
|
-
ApplicationProvider,
|
|
20
|
-
{ container },
|
|
21
|
-
createElement(RootComponent),
|
|
22
|
-
),
|
|
23
|
-
);
|
|
24
|
-
|
|
25
|
-
return () => root.unmount();
|
|
26
|
-
}
|
|
27
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import type { Cleanup, ServiceProviderContext } from "../support/service-provider";
|
|
2
|
-
import type { ReactElement } from "react";
|
|
3
|
-
|
|
4
|
-
export type RootComponent = () => ReactElement | null;
|
|
5
|
-
|
|
6
|
-
export type RendererContext = ServiceProviderContext & {
|
|
7
|
-
RootComponent: RootComponent;
|
|
8
|
-
rootElementId: string;
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
export interface RendererAdapter {
|
|
12
|
-
render(context: RendererContext): void | Cleanup;
|
|
13
|
-
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import appConfig from "../../config/app";
|
|
2
|
-
import ConfigRepository, { type ConfigItems } from "../../config/repository";
|
|
3
|
-
import ServiceProvider, { type ServiceProviderContext } from "../service-provider";
|
|
4
|
-
|
|
5
|
-
export default class ConfigServiceProvider extends ServiceProvider {
|
|
6
|
-
protected items: ConfigItems;
|
|
7
|
-
|
|
8
|
-
constructor(items: ConfigItems = { app: appConfig }) {
|
|
9
|
-
super();
|
|
10
|
-
this.items = items;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
register({ container }: ServiceProviderContext): void {
|
|
14
|
-
const repository = new ConfigRepository(this.items);
|
|
15
|
-
|
|
16
|
-
container.bind("config").toConstantValue(repository);
|
|
17
|
-
container.bind(ConfigRepository).toConstantValue(repository);
|
|
18
|
-
}
|
|
19
|
-
}
|
|
@@ -1,236 +0,0 @@
|
|
|
1
|
-
import { afterEach, describe, expect, test } from "bun:test";
|
|
2
|
-
import ReactDOM from "react-dom/client";
|
|
3
|
-
import ConfigRepository from "../src/config/repository";
|
|
4
|
-
import { __applicationTesting, Application } from "../src/foundation/application";
|
|
5
|
-
import ServiceProvider from "../src/support/service-provider";
|
|
6
|
-
|
|
7
|
-
describe("Application", () => {
|
|
8
|
-
afterEach(() => {
|
|
9
|
-
(globalThis as { window?: unknown; document?: unknown }).window = undefined;
|
|
10
|
-
(globalThis as { window?: unknown; document?: unknown }).document = undefined;
|
|
11
|
-
(
|
|
12
|
-
globalThis as { __iocConfigGlobForTests?: unknown }
|
|
13
|
-
).__iocConfigGlobForTests = undefined;
|
|
14
|
-
Application.clearConfigCache();
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
test("make throws when container is not initialized", () => {
|
|
18
|
-
expect(() => Application.make("config")).toThrow(
|
|
19
|
-
"Application container is not available. Call run() first.",
|
|
20
|
-
);
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
test("run with custom renderer executes lifecycle and cleanup in reverse order", () => {
|
|
24
|
-
const calls: string[] = [];
|
|
25
|
-
let beforeUnload: (() => void) | undefined;
|
|
26
|
-
|
|
27
|
-
(globalThis as { window: { addEventListener: (event: string, cb: () => void) => void } })
|
|
28
|
-
.window = {
|
|
29
|
-
addEventListener: (_event, cb) => {
|
|
30
|
-
beforeUnload = cb;
|
|
31
|
-
},
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
class DemoProvider extends ServiceProvider {
|
|
35
|
-
register({ container }: { container: { bind: (id: string) => { toConstantValue: (v: unknown) => void } } }) {
|
|
36
|
-
calls.push("provider.register");
|
|
37
|
-
container.bind("demo").toConstantValue("value");
|
|
38
|
-
return () => calls.push("provider.register.cleanup");
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
boot() {
|
|
42
|
-
calls.push("provider.boot");
|
|
43
|
-
return () => calls.push("provider.boot.cleanup");
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const app = Application.configure("./")
|
|
48
|
-
.withProviders([new DemoProvider()])
|
|
49
|
-
.withServices(({ container }) => {
|
|
50
|
-
calls.push("services");
|
|
51
|
-
container.bind("services.value").toConstantValue(1);
|
|
52
|
-
return () => calls.push("services.cleanup");
|
|
53
|
-
})
|
|
54
|
-
.withStartup(() => {
|
|
55
|
-
calls.push("startup");
|
|
56
|
-
return () => calls.push("startup.cleanup");
|
|
57
|
-
})
|
|
58
|
-
.withRoot(() => null)
|
|
59
|
-
.withRenderer({
|
|
60
|
-
render: () => {
|
|
61
|
-
calls.push("renderer");
|
|
62
|
-
return () => calls.push("renderer.cleanup");
|
|
63
|
-
},
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
const running = app.run();
|
|
67
|
-
expect(Application.make("demo")).toBe("value");
|
|
68
|
-
expect(Application.make("storage")).toBeTruthy();
|
|
69
|
-
expect(Application.make("cache")).toBeTruthy();
|
|
70
|
-
expect(running.container.get("services.value")).toBe(1);
|
|
71
|
-
expect(calls).toEqual([
|
|
72
|
-
"provider.register",
|
|
73
|
-
"services",
|
|
74
|
-
"provider.boot",
|
|
75
|
-
"startup",
|
|
76
|
-
"renderer",
|
|
77
|
-
]);
|
|
78
|
-
|
|
79
|
-
expect(typeof beforeUnload).toBe("function");
|
|
80
|
-
beforeUnload?.();
|
|
81
|
-
|
|
82
|
-
expect(calls).toEqual([
|
|
83
|
-
"provider.register",
|
|
84
|
-
"services",
|
|
85
|
-
"provider.boot",
|
|
86
|
-
"startup",
|
|
87
|
-
"renderer",
|
|
88
|
-
"renderer.cleanup",
|
|
89
|
-
"startup.cleanup",
|
|
90
|
-
"provider.boot.cleanup",
|
|
91
|
-
"services.cleanup",
|
|
92
|
-
"provider.register.cleanup",
|
|
93
|
-
]);
|
|
94
|
-
|
|
95
|
-
expect(() => Application.make("demo")).toThrow(
|
|
96
|
-
"Application container is not available. Call run() first.",
|
|
97
|
-
);
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
test("exposes helper behavior used for config discovery", () => {
|
|
101
|
-
expect(__applicationTesting.fileNameWithoutExtension("/src/config/app.ts")).toBe("app");
|
|
102
|
-
expect(__applicationTesting.normalizeBasePath("./")).toBe("");
|
|
103
|
-
expect(__applicationTesting.normalizeBasePath("./src")).toBe("src");
|
|
104
|
-
expect(__applicationTesting.isPathInConfigDirectories("/src/config/app.ts", "./")).toBe(
|
|
105
|
-
true,
|
|
106
|
-
);
|
|
107
|
-
expect(
|
|
108
|
-
__applicationTesting.isPathInConfigDirectories(
|
|
109
|
-
"/workspace/src/config/app.ts",
|
|
110
|
-
"/workspace",
|
|
111
|
-
),
|
|
112
|
-
).toBe(true);
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
test("loads config modules through glob loader and caches by basePath", () => {
|
|
116
|
-
(
|
|
117
|
-
globalThis as {
|
|
118
|
-
__iocConfigGlobForTests?: (
|
|
119
|
-
pattern: string | string[],
|
|
120
|
-
options?: { eager?: boolean },
|
|
121
|
-
) => Record<string, unknown>;
|
|
122
|
-
}
|
|
123
|
-
).__iocConfigGlobForTests = () => ({
|
|
124
|
-
"/src/config/app.ts": { default: { name: "From App Config" } },
|
|
125
|
-
"/src/config/cache.ts": { default: { store: "memory" } },
|
|
126
|
-
"/other/path/ignored.ts": { default: { nope: true } },
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
(globalThis as { window: { addEventListener: (event: string, cb: () => void) => void } })
|
|
130
|
-
.window = {
|
|
131
|
-
addEventListener: () => {},
|
|
132
|
-
};
|
|
133
|
-
|
|
134
|
-
const first = Application.configure("./src").run();
|
|
135
|
-
const second = Application.configure("./src").run();
|
|
136
|
-
|
|
137
|
-
const firstConfig = first.container.get(ConfigRepository);
|
|
138
|
-
const secondConfig = second.container.get(ConfigRepository);
|
|
139
|
-
|
|
140
|
-
expect(firstConfig.get("app")).toEqual({ name: "From App Config" });
|
|
141
|
-
expect(firstConfig.get("cache")).toEqual({ store: "memory" });
|
|
142
|
-
expect(firstConfig.get("ignored")).toBeNull();
|
|
143
|
-
expect(firstConfig.all()).toEqual(secondConfig.all());
|
|
144
|
-
expect(firstConfig.all()).not.toBe(secondConfig.all());
|
|
145
|
-
|
|
146
|
-
// Cached source config should be cloned into each app instance.
|
|
147
|
-
firstConfig.set("cache.store", "updated");
|
|
148
|
-
expect(secondConfig.get("cache.store")).toBe("memory");
|
|
149
|
-
|
|
150
|
-
first.stop();
|
|
151
|
-
second.stop();
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
test("renderer requires root component", () => {
|
|
155
|
-
(globalThis as { window: { addEventListener: (event: string, cb: () => void) => void } })
|
|
156
|
-
.window = {
|
|
157
|
-
addEventListener: () => {},
|
|
158
|
-
};
|
|
159
|
-
|
|
160
|
-
const app = Application.configure("./").withRenderer({ render: () => () => {} });
|
|
161
|
-
|
|
162
|
-
expect(() => app.run()).toThrow(
|
|
163
|
-
"Root component is required when using a custom renderer.",
|
|
164
|
-
);
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
test("default react renderer throws if mount node is missing", () => {
|
|
168
|
-
(globalThis as { window: { addEventListener: (event: string, cb: () => void) => void } })
|
|
169
|
-
.window = {
|
|
170
|
-
addEventListener: () => {},
|
|
171
|
-
};
|
|
172
|
-
(globalThis as { document: { getElementById: (id: string) => null } }).document = {
|
|
173
|
-
getElementById: () => null,
|
|
174
|
-
};
|
|
175
|
-
|
|
176
|
-
const app = Application.configure("./").withRoot(() => null);
|
|
177
|
-
|
|
178
|
-
expect(() => app.run()).toThrow("Missing mount node #root.");
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
test("default react renderer mounts and unmounts when stopped", () => {
|
|
182
|
-
let beforeUnload: (() => void) | undefined;
|
|
183
|
-
let rendered = 0;
|
|
184
|
-
let unmounted = 0;
|
|
185
|
-
|
|
186
|
-
(globalThis as { window: { addEventListener: (event: string, cb: () => void) => void } })
|
|
187
|
-
.window = {
|
|
188
|
-
addEventListener: (_event, cb) => {
|
|
189
|
-
beforeUnload = cb;
|
|
190
|
-
},
|
|
191
|
-
};
|
|
192
|
-
(globalThis as { document: { getElementById: (id: string) => object | null } }).document = {
|
|
193
|
-
getElementById: () => ({}),
|
|
194
|
-
};
|
|
195
|
-
|
|
196
|
-
const originalCreateRoot = (ReactDOM as { createRoot: (node: unknown) => unknown }).createRoot;
|
|
197
|
-
(ReactDOM as { createRoot: (node: unknown) => { render: () => void; unmount: () => void } })
|
|
198
|
-
.createRoot = () => ({
|
|
199
|
-
render: () => {
|
|
200
|
-
rendered += 1;
|
|
201
|
-
},
|
|
202
|
-
unmount: () => {
|
|
203
|
-
unmounted += 1;
|
|
204
|
-
},
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
try {
|
|
208
|
-
const running = Application.configure("./").withRoot(() => null).run();
|
|
209
|
-
expect(rendered).toBe(1);
|
|
210
|
-
running.stop();
|
|
211
|
-
expect(unmounted).toBe(1);
|
|
212
|
-
// Stop twice should still be safe if beforeunload callback also runs.
|
|
213
|
-
beforeUnload?.();
|
|
214
|
-
expect(unmounted).toBe(2);
|
|
215
|
-
} finally {
|
|
216
|
-
(
|
|
217
|
-
ReactDOM as { createRoot: (node: unknown) => unknown }
|
|
218
|
-
).createRoot = originalCreateRoot;
|
|
219
|
-
}
|
|
220
|
-
});
|
|
221
|
-
|
|
222
|
-
test("uses fallback no-op renderer cleanup when custom renderer returns nothing", () => {
|
|
223
|
-
(globalThis as { window: { addEventListener: (event: string, cb: () => void) => void } })
|
|
224
|
-
.window = {
|
|
225
|
-
addEventListener: (_event, cb) => {
|
|
226
|
-
cb();
|
|
227
|
-
},
|
|
228
|
-
};
|
|
229
|
-
|
|
230
|
-
const app = Application.configure("./")
|
|
231
|
-
.withRoot(() => null)
|
|
232
|
-
.withRenderer({ render: () => undefined });
|
|
233
|
-
|
|
234
|
-
expect(() => app.run()).not.toThrow();
|
|
235
|
-
});
|
|
236
|
-
});
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import { afterEach, describe, expect, test } from "bun:test";
|
|
2
|
-
import { Container } from "inversify";
|
|
3
|
-
import { Application } from "../src/foundation/application";
|
|
4
|
-
import CacheManager from "../src/cache/manager";
|
|
5
|
-
import Cache from "../src/support/facades/cache";
|
|
6
|
-
import Facade from "../src/support/facades/facade";
|
|
7
|
-
import MemoryStorageAdapter from "../src/storage/adapters/memory";
|
|
8
|
-
|
|
9
|
-
describe("Cache facade", () => {
|
|
10
|
-
afterEach(() => {
|
|
11
|
-
Facade.clearResolvedInstances();
|
|
12
|
-
(Application as unknown as { container: Container | null }).container = null;
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
test("resolves manager and delegates methods", async () => {
|
|
16
|
-
const manager = new CacheManager({ memory: new MemoryStorageAdapter() }, "memory");
|
|
17
|
-
const container = new Container();
|
|
18
|
-
container.bind("cache").toConstantValue(manager);
|
|
19
|
-
container.bind(CacheManager).toConstantValue(manager);
|
|
20
|
-
(Application as unknown as { container: Container | null }).container = container;
|
|
21
|
-
|
|
22
|
-
await Cache.set("name", "ioc");
|
|
23
|
-
expect(await Cache.get("name")).toBe("ioc");
|
|
24
|
-
expect(await Cache.has("name")).toBe(true);
|
|
25
|
-
expect(await Cache.keys()).toEqual(["name"]);
|
|
26
|
-
expect(Cache.store()).toBe(manager.store());
|
|
27
|
-
Cache.use("memory");
|
|
28
|
-
await Cache.delete("name");
|
|
29
|
-
expect(await Cache.get("name")).toBeNull();
|
|
30
|
-
await Cache.set("x", 1);
|
|
31
|
-
await Cache.clear();
|
|
32
|
-
expect(await Cache.keys()).toEqual([]);
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
test("uses expected facade accessor", () => {
|
|
36
|
-
expect((Cache as unknown as { getFacadeAccessor: () => string }).getFacadeAccessor()).toBe(
|
|
37
|
-
"cache",
|
|
38
|
-
);
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
test("facade class constructor is defined", () => {
|
|
42
|
-
const instance = new (Cache as unknown as { new (): object })();
|
|
43
|
-
expect(instance).toBeTruthy();
|
|
44
|
-
});
|
|
45
|
-
});
|
package/tests/cache.test.ts
DELETED
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import ConfigRepository from "../src/config/repository";
|
|
3
|
-
import CacheManager from "../src/cache/manager";
|
|
4
|
-
|
|
5
|
-
describe("CacheManager", () => {
|
|
6
|
-
test("uses cache.default and configured stores", async () => {
|
|
7
|
-
const manager = CacheManager.fromConfig(
|
|
8
|
-
new ConfigRepository({
|
|
9
|
-
cache: {
|
|
10
|
-
default: "persistent",
|
|
11
|
-
stores: {
|
|
12
|
-
persistent: { driver: "memory" },
|
|
13
|
-
fast: { driver: "memory" },
|
|
14
|
-
},
|
|
15
|
-
},
|
|
16
|
-
}),
|
|
17
|
-
);
|
|
18
|
-
|
|
19
|
-
await manager.set("key", "value");
|
|
20
|
-
expect(await manager.get("key")).toBe("value");
|
|
21
|
-
expect(await manager.has("key")).toBe(true);
|
|
22
|
-
expect(await manager.keys()).toEqual(["key"]);
|
|
23
|
-
await manager.delete("key");
|
|
24
|
-
expect(await manager.get("key")).toBeNull();
|
|
25
|
-
await manager.set("other", 1);
|
|
26
|
-
await manager.clear();
|
|
27
|
-
expect(await manager.keys()).toEqual([]);
|
|
28
|
-
|
|
29
|
-
manager.use("fast");
|
|
30
|
-
await manager.set("fast-key", 123);
|
|
31
|
-
expect(await manager.get("fast-key")).toBe(123);
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
test("falls back to default stores and memory default", async () => {
|
|
35
|
-
const manager = CacheManager.fromConfig(new ConfigRepository({}));
|
|
36
|
-
await manager.set("a", 1);
|
|
37
|
-
expect(await manager.get("a")).toBe(1);
|
|
38
|
-
expect(manager.store("memory")).toBeTruthy();
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
test("resolves missing driver/store behavior", () => {
|
|
42
|
-
const manager = CacheManager.fromConfig(
|
|
43
|
-
new ConfigRepository({
|
|
44
|
-
cache: {
|
|
45
|
-
default: "missing",
|
|
46
|
-
stores: {
|
|
47
|
-
only: { driver: "unknown" },
|
|
48
|
-
},
|
|
49
|
-
},
|
|
50
|
-
}),
|
|
51
|
-
);
|
|
52
|
-
|
|
53
|
-
expect(manager.store()).toBeTruthy();
|
|
54
|
-
expect(() => manager.store("nope")).toThrow("Cache store [nope] is not defined.");
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
test("handles non-object cache.stores by using defaults", () => {
|
|
58
|
-
const manager = CacheManager.fromConfig(
|
|
59
|
-
new ConfigRepository({
|
|
60
|
-
cache: {
|
|
61
|
-
stores: "bad-value",
|
|
62
|
-
},
|
|
63
|
-
}),
|
|
64
|
-
);
|
|
65
|
-
|
|
66
|
-
expect(manager.store("memory")).toBeTruthy();
|
|
67
|
-
});
|
|
68
|
-
});
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { afterEach, describe, expect, test } from "bun:test";
|
|
2
|
-
import { cloneConfigItems } from "../src/config/clone";
|
|
3
|
-
|
|
4
|
-
describe("cloneConfigItems", () => {
|
|
5
|
-
afterEach(() => {
|
|
6
|
-
(globalThis as { structuredClone?: typeof structuredClone }).structuredClone =
|
|
7
|
-
originalStructuredClone;
|
|
8
|
-
});
|
|
9
|
-
|
|
10
|
-
const originalStructuredClone = globalThis.structuredClone;
|
|
11
|
-
|
|
12
|
-
test("uses structuredClone when available", () => {
|
|
13
|
-
const input = { app: { name: "IOC" } };
|
|
14
|
-
|
|
15
|
-
const cloned = cloneConfigItems(input);
|
|
16
|
-
expect(cloned).toEqual(input);
|
|
17
|
-
expect(cloned).not.toBe(input);
|
|
18
|
-
expect(cloned.app).not.toBe(input.app);
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
test("falls back to JSON clone when structuredClone is unavailable", () => {
|
|
22
|
-
(globalThis as { structuredClone?: typeof structuredClone }).structuredClone = undefined;
|
|
23
|
-
|
|
24
|
-
const input = { app: { name: "IOC" } };
|
|
25
|
-
const cloned = cloneConfigItems(input);
|
|
26
|
-
|
|
27
|
-
expect(cloned).toEqual(input);
|
|
28
|
-
expect(cloned).not.toBe(input);
|
|
29
|
-
expect(cloned.app).not.toBe(input.app);
|
|
30
|
-
});
|
|
31
|
-
});
|
package/tests/config-env.test.ts
DELETED
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
import { afterEach, describe, expect, test } from "bun:test";
|
|
2
|
-
import { __envTesting, env, registerGlobalEnv } from "../src/config/env";
|
|
3
|
-
|
|
4
|
-
describe("env helper", () => {
|
|
5
|
-
const originalProcess = (globalThis as { process?: unknown }).process;
|
|
6
|
-
const originalGlobalEnv = (globalThis as { env?: unknown }).env;
|
|
7
|
-
const originalImportMetaEnv = (
|
|
8
|
-
globalThis as { __iocImportMetaEnvForTests?: unknown }
|
|
9
|
-
).__iocImportMetaEnvForTests;
|
|
10
|
-
|
|
11
|
-
afterEach(() => {
|
|
12
|
-
(globalThis as { process?: unknown }).process = originalProcess;
|
|
13
|
-
(globalThis as { env?: unknown }).env = originalGlobalEnv;
|
|
14
|
-
(
|
|
15
|
-
globalThis as { __iocImportMetaEnvForTests?: unknown }
|
|
16
|
-
).__iocImportMetaEnvForTests = originalImportMetaEnv;
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
test("returns default when key does not exist", () => {
|
|
20
|
-
(globalThis as { process?: { env: Record<string, unknown> } }).process = {
|
|
21
|
-
env: {},
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
expect(env("MISSING", "fallback")).toBe("fallback");
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
test("reads values from process env with laravel-like casts", () => {
|
|
28
|
-
(
|
|
29
|
-
globalThis as { __iocImportMetaEnvForTests?: unknown }
|
|
30
|
-
).__iocImportMetaEnvForTests = undefined;
|
|
31
|
-
(globalThis as { process?: { env: Record<string, unknown> } }).process = {
|
|
32
|
-
env: {
|
|
33
|
-
BOOL_TRUE: "true",
|
|
34
|
-
BOOL_FALSE: "(false)",
|
|
35
|
-
NULL_VAL: "null",
|
|
36
|
-
EMPTY_VAL: "(empty)",
|
|
37
|
-
RAW: "abc",
|
|
38
|
-
NUM: 123,
|
|
39
|
-
},
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
expect(env("BOOL_TRUE")).toBe(true);
|
|
43
|
-
expect(env("BOOL_FALSE")).toBe(false);
|
|
44
|
-
expect(env("NULL_VAL", "x")).toBeNull();
|
|
45
|
-
expect(env("EMPTY_VAL", "x")).toBe("");
|
|
46
|
-
expect(env("RAW")).toBe("abc");
|
|
47
|
-
expect(env("NUM")).toBe(123);
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
test("prefers import-meta env when key exists there", () => {
|
|
51
|
-
(
|
|
52
|
-
globalThis as { __iocImportMetaEnvForTests?: Record<string, unknown> }
|
|
53
|
-
).__iocImportMetaEnvForTests = {
|
|
54
|
-
CACHE_STORE: "(true)",
|
|
55
|
-
};
|
|
56
|
-
(globalThis as { process?: { env: Record<string, unknown> } }).process = {
|
|
57
|
-
env: {
|
|
58
|
-
CACHE_STORE: "false",
|
|
59
|
-
},
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
expect(env("CACHE_STORE")).toBe(true);
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
test("registers global env function without overriding existing one", () => {
|
|
66
|
-
(globalThis as { env?: unknown }).env = undefined;
|
|
67
|
-
registerGlobalEnv();
|
|
68
|
-
expect(typeof (globalThis as { env?: unknown }).env).toBe("function");
|
|
69
|
-
|
|
70
|
-
const custom = () => "custom";
|
|
71
|
-
(globalThis as { env?: unknown }).env = custom;
|
|
72
|
-
registerGlobalEnv();
|
|
73
|
-
expect((globalThis as { env?: unknown }).env).toBe(custom);
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
test("exposes env helper internals", () => {
|
|
77
|
-
expect(__envTesting.normalizeEnvValue("(true)")).toBe(true);
|
|
78
|
-
expect(typeof __envTesting.resolveImportMetaEnv()).toBe("object");
|
|
79
|
-
|
|
80
|
-
(globalThis as { process?: unknown }).process = undefined;
|
|
81
|
-
expect(__envTesting.resolveProcessEnv()).toEqual({});
|
|
82
|
-
|
|
83
|
-
(globalThis as { process?: { env: Record<string, unknown> } }).process = {
|
|
84
|
-
env: { APP_ENV: "local" },
|
|
85
|
-
};
|
|
86
|
-
expect(__envTesting.resolveProcessEnv()).toEqual({ APP_ENV: "local" });
|
|
87
|
-
});
|
|
88
|
-
});
|
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
import { afterEach, describe, expect, test } from "bun:test";
|
|
2
|
-
import { Container } from "inversify";
|
|
3
|
-
import ConfigRepository from "../src/config/repository";
|
|
4
|
-
import { Application } from "../src/foundation/application";
|
|
5
|
-
import Config from "../src/support/facades/config";
|
|
6
|
-
import Facade from "../src/support/facades/facade";
|
|
7
|
-
import ConfigServiceProvider from "../src/support/providers/config-service-provider";
|
|
8
|
-
|
|
9
|
-
describe("Config facade", () => {
|
|
10
|
-
afterEach(() => {
|
|
11
|
-
Facade.clearResolvedInstances();
|
|
12
|
-
(Application as unknown as { container: Container | null }).container = null;
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
test("resolves repository from the container using the config accessor", () => {
|
|
16
|
-
const container = new Container();
|
|
17
|
-
new ConfigServiceProvider({
|
|
18
|
-
app: {
|
|
19
|
-
name: "From Provider",
|
|
20
|
-
},
|
|
21
|
-
}).register({ container });
|
|
22
|
-
|
|
23
|
-
(Application as unknown as { container: Container | null }).container = container;
|
|
24
|
-
|
|
25
|
-
expect(Config.get("app.name")).toBe("From Provider");
|
|
26
|
-
expect(container.get(ConfigRepository).get("app.name")).toBe("From Provider");
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
test("uses expected facade accessor name", () => {
|
|
30
|
-
expect((Config as unknown as { getFacadeAccessor: () => string }).getFacadeAccessor()).toBe(
|
|
31
|
-
"config",
|
|
32
|
-
);
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
test("facade class constructor is defined", () => {
|
|
36
|
-
const instance = new (Config as unknown as { new (): object })();
|
|
37
|
-
expect(instance).toBeTruthy();
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
test("uses facade cache until resolved instances are cleared", () => {
|
|
41
|
-
const container = new Container();
|
|
42
|
-
container
|
|
43
|
-
.bind("config")
|
|
44
|
-
.toConstantValue(new ConfigRepository({ app: { name: "First" } }));
|
|
45
|
-
container
|
|
46
|
-
.bind(ConfigRepository)
|
|
47
|
-
.toConstantValue(new ConfigRepository({ app: { name: "First" } }));
|
|
48
|
-
(Application as unknown as { container: Container | null }).container = container;
|
|
49
|
-
|
|
50
|
-
expect(Config.get("app.name")).toBe("First");
|
|
51
|
-
|
|
52
|
-
// Rebind underlying container value; facade should still return cached first instance.
|
|
53
|
-
container.unbind("config");
|
|
54
|
-
container.bind("config").toConstantValue(new ConfigRepository({ app: { name: "Second" } }));
|
|
55
|
-
expect(Config.get("app.name")).toBe("First");
|
|
56
|
-
|
|
57
|
-
Facade.clearResolvedInstances();
|
|
58
|
-
expect(Config.get("app.name")).toBe("Second");
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
test("delegates full method surface to repository", () => {
|
|
62
|
-
const container = new Container();
|
|
63
|
-
const repository = new ConfigRepository({
|
|
64
|
-
app: {
|
|
65
|
-
name: "IOC",
|
|
66
|
-
retries: 3,
|
|
67
|
-
ratio: 1.5,
|
|
68
|
-
enabled: true,
|
|
69
|
-
tags: ["base"],
|
|
70
|
-
},
|
|
71
|
-
});
|
|
72
|
-
container.bind("config").toConstantValue(repository);
|
|
73
|
-
container.bind(ConfigRepository).toConstantValue(repository);
|
|
74
|
-
(Application as unknown as { container: Container | null }).container = container;
|
|
75
|
-
|
|
76
|
-
expect(Config.has("app.name")).toBe(true);
|
|
77
|
-
expect(Config.getMany(["app.name"])).toEqual({ "app.name": "IOC" });
|
|
78
|
-
expect(Config.string("app.name")).toBe("IOC");
|
|
79
|
-
expect(Config.integer("app.retries")).toBe(3);
|
|
80
|
-
expect(Config.float("app.ratio")).toBe(1.5);
|
|
81
|
-
expect(Config.boolean("app.enabled")).toBe(true);
|
|
82
|
-
expect(Config.array("app.tags")).toEqual(["base"]);
|
|
83
|
-
|
|
84
|
-
Config.set("app.name", "Changed");
|
|
85
|
-
Config.prepend("app.tags", "first");
|
|
86
|
-
Config.push("app.tags", "last");
|
|
87
|
-
|
|
88
|
-
expect(Config.get("app.name")).toBe("Changed");
|
|
89
|
-
expect(Config.array("app.tags")).toEqual(["first", "base", "last"]);
|
|
90
|
-
expect(Config.all()).toMatchObject({
|
|
91
|
-
app: {
|
|
92
|
-
name: "Changed",
|
|
93
|
-
},
|
|
94
|
-
});
|
|
95
|
-
});
|
|
96
|
-
});
|