@raubjo/architect-core 0.1.1 → 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.
- package/README.md +90 -36
- package/package.json +2 -1
- package/src/renderers/adapters/svelte.ts +30 -4
package/README.md
CHANGED
|
@@ -12,14 +12,19 @@ You keep inversion of control, lifecycle hooks, and runtime reactivity.
|
|
|
12
12
|
- Predictable lifecycle (`register` then `boot`)
|
|
13
13
|
- Framework runtime helpers (`react`, `solid`, `svelte`, `vue`)
|
|
14
14
|
- Config repository and facades
|
|
15
|
-
- Automatic config loading from your project `config` files
|
|
16
15
|
|
|
17
16
|
If you are familiar with Laravel service providers and container bindings, this should feel familiar.
|
|
18
17
|
|
|
19
18
|
## Install
|
|
20
19
|
|
|
21
20
|
```bash
|
|
22
|
-
bun add @raubjo/architect-core
|
|
21
|
+
bun add @raubjo/architect-core
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
If you want the Inversify adapter:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
bun add inversify reflect-metadata
|
|
23
28
|
```
|
|
24
29
|
|
|
25
30
|
For framework-specific runtime helpers, import from subpaths like `@raubjo/architect-core/react`.
|
|
@@ -27,10 +32,13 @@ For framework-specific runtime helpers, import from subpaths like `@raubjo/archi
|
|
|
27
32
|
## Quick start
|
|
28
33
|
|
|
29
34
|
```ts
|
|
30
|
-
import "reflect-metadata";
|
|
31
35
|
import { Application } from "@raubjo/architect-core";
|
|
32
36
|
|
|
33
|
-
const { container, stop } = Application.configure(
|
|
37
|
+
const { container, stop } = Application.configure({
|
|
38
|
+
config: {
|
|
39
|
+
app: { name: "IOC Application" },
|
|
40
|
+
},
|
|
41
|
+
})
|
|
34
42
|
.withProviders([])
|
|
35
43
|
.run();
|
|
36
44
|
|
|
@@ -41,6 +49,75 @@ const config = container.get("config");
|
|
|
41
49
|
stop();
|
|
42
50
|
```
|
|
43
51
|
|
|
52
|
+
## Container adapters
|
|
53
|
+
|
|
54
|
+
`Application` supports three container adapter modes:
|
|
55
|
+
|
|
56
|
+
- `builtin`: always use the built-in container implementation.
|
|
57
|
+
- `inversify`: require and use the registered Inversify adapter.
|
|
58
|
+
- `auto`: use Inversify when available, otherwise fallback to `builtin`.
|
|
59
|
+
|
|
60
|
+
### Builtin container
|
|
61
|
+
|
|
62
|
+
Builtin is the default/fallback container and supports:
|
|
63
|
+
|
|
64
|
+
- Constructor injection via `design:paramtypes` metadata
|
|
65
|
+
- `@inject(...)` parameter token overrides
|
|
66
|
+
- `singleton`, `transient`, and `instance` registration
|
|
67
|
+
- Fluent `bind(...).to(...)/toDynamicValue(...)/toConstantValue(...)`
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
import "reflect-metadata";
|
|
71
|
+
import { Application, injectDependency } from "@raubjo/architect-core";
|
|
72
|
+
|
|
73
|
+
class Logger {}
|
|
74
|
+
|
|
75
|
+
class Service {
|
|
76
|
+
constructor(
|
|
77
|
+
public readonly logger: Logger,
|
|
78
|
+
@injectDependency("config.appName") public readonly appName: string,
|
|
79
|
+
) {}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const { container } = Application.configure({
|
|
83
|
+
container: { adapter: "builtin" },
|
|
84
|
+
config: {
|
|
85
|
+
appName: "IOC Application",
|
|
86
|
+
},
|
|
87
|
+
})
|
|
88
|
+
.withServices(({ container }) => {
|
|
89
|
+
container.singleton(Logger, Logger);
|
|
90
|
+
container.transient(Service, Service);
|
|
91
|
+
container.instance("config.appName", "IOC Application");
|
|
92
|
+
})
|
|
93
|
+
.run();
|
|
94
|
+
|
|
95
|
+
const service = container.make(Service);
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Inversify container
|
|
99
|
+
|
|
100
|
+
To use Inversify, install `inversify` and `reflect-metadata`, then register a factory on `globalThis.__iocContainerFactoryRegistry`.
|
|
101
|
+
|
|
102
|
+
```ts
|
|
103
|
+
import "reflect-metadata";
|
|
104
|
+
import { Application } from "@raubjo/architect-core";
|
|
105
|
+
import InversifyContainer from "@raubjo/architect-core/container/adapters/inversify";
|
|
106
|
+
|
|
107
|
+
globalThis.__iocContainerFactoryRegistry = {
|
|
108
|
+
inversify: () => new InversifyContainer(),
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
const running = Application.configure({
|
|
112
|
+
container: { adapter: "inversify" },
|
|
113
|
+
config: {
|
|
114
|
+
app: { name: "IOC Application" },
|
|
115
|
+
},
|
|
116
|
+
}).run();
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
You can also keep `adapter: "auto"` and let runtime detection pick Inversify when `package.json` includes it.
|
|
120
|
+
|
|
44
121
|
## Service provider example
|
|
45
122
|
|
|
46
123
|
Use providers to define business rules and service construction once.
|
|
@@ -86,7 +163,7 @@ class PricingServiceProvider extends ServiceProvider {
|
|
|
86
163
|
```ts
|
|
87
164
|
import "reflect-metadata";
|
|
88
165
|
import { Application } from "@raubjo/architect-core";
|
|
89
|
-
import { ReactRenderer, useService } from "@raubjo/architect-core/react";
|
|
166
|
+
import { Renderer as ReactRenderer, useService } from "@raubjo/architect-core/react";
|
|
90
167
|
import PricingServiceProvider from "./providers/pricing-service-provider";
|
|
91
168
|
|
|
92
169
|
function QuotePanel() {
|
|
@@ -109,39 +186,16 @@ const app = Application.configure("./")
|
|
|
109
186
|
app.run();
|
|
110
187
|
```
|
|
111
188
|
|
|
112
|
-
## Config
|
|
189
|
+
## Config
|
|
113
190
|
|
|
114
|
-
`Application
|
|
115
|
-
|
|
116
|
-
When `run()` is called, config is assembled from:
|
|
117
|
-
|
|
118
|
-
1. Built-in default `app` config.
|
|
119
|
-
2. Any default exports found in:
|
|
120
|
-
- `${basePath}/config/*.{js,mjs,cjs,ts,mts,cts}`
|
|
121
|
-
- `${basePath}/src/config/*.{js,mjs,cjs,ts,mts,cts}`
|
|
122
|
-
|
|
123
|
-
Each file becomes a top-level config key based on the filename:
|
|
124
|
-
|
|
125
|
-
- `config/cache.ts` -> `config.get("cache")`
|
|
126
|
-
- `src/config/pricing.ts` -> `config.get("pricing")`
|
|
127
|
-
|
|
128
|
-
Example:
|
|
191
|
+
`Application` uses config passed directly to `configure`:
|
|
129
192
|
|
|
130
193
|
```ts
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
};
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
```ts
|
|
139
|
-
const app = Application.configure("./").run();
|
|
140
|
-
const config = app.container.get("config") as {
|
|
141
|
-
get: <T = unknown>(key: string, defaultValue?: T) => T;
|
|
142
|
-
};
|
|
143
|
-
|
|
144
|
-
config.get("pricing.taxRate"); // 0.0825
|
|
194
|
+
const app = Application.configure({
|
|
195
|
+
config: {
|
|
196
|
+
pricing: { taxRate: 0.0825, currency: "USD" },
|
|
197
|
+
},
|
|
198
|
+
}).run();
|
|
145
199
|
```
|
|
146
200
|
|
|
147
201
|
## Lifecycle order
|
|
@@ -159,4 +213,4 @@ Cleanup runs in reverse order when `stop()` is called.
|
|
|
159
213
|
## Notes
|
|
160
214
|
|
|
161
215
|
- `Application.make(...)` is available after `run()` and resolves from the active container.
|
|
162
|
-
-
|
|
216
|
+
- `Application.configure(basePath)` is still supported for compatibility, but config values should be passed via `configure({ config })`.
|
package/package.json
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { mount, unmount } from "svelte";
|
|
1
2
|
import type { Cleanup } from "../../support/service-provider";
|
|
2
3
|
import type Contract from "../contract";
|
|
3
4
|
import type { RendererContext } from "../contract";
|
|
@@ -12,6 +13,12 @@ type SvelteComponentConstructor = new (options: {
|
|
|
12
13
|
props?: Record<string, unknown>;
|
|
13
14
|
}) => SvelteComponentInstance;
|
|
14
15
|
|
|
16
|
+
type SvelteMount = (
|
|
17
|
+
component: unknown,
|
|
18
|
+
options: { target: Element; props?: Record<string, unknown> },
|
|
19
|
+
) => unknown;
|
|
20
|
+
type SvelteUnmount = (instance: unknown) => void | Promise<void>;
|
|
21
|
+
|
|
15
22
|
export default class SvelteRenderer implements Contract {
|
|
16
23
|
constructor() {}
|
|
17
24
|
|
|
@@ -25,11 +32,30 @@ export default class SvelteRenderer implements Contract {
|
|
|
25
32
|
throw new Error(`Missing mount node #${rootElementId}.`);
|
|
26
33
|
}
|
|
27
34
|
|
|
35
|
+
const props = { container };
|
|
36
|
+
const svelteMount = mount as unknown as SvelteMount | undefined;
|
|
37
|
+
const svelteUnmount = unmount as unknown as SvelteUnmount | undefined;
|
|
38
|
+
|
|
39
|
+
if (
|
|
40
|
+
typeof svelteMount === "function" &&
|
|
41
|
+
typeof svelteUnmount === "function"
|
|
42
|
+
) {
|
|
43
|
+
try {
|
|
44
|
+
const instance = svelteMount(RootComponent, {
|
|
45
|
+
target: mountNode,
|
|
46
|
+
props,
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
return () => {
|
|
50
|
+
void svelteUnmount(instance);
|
|
51
|
+
};
|
|
52
|
+
} catch (_error) {
|
|
53
|
+
// Fallback to the legacy component constructor API.
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
28
57
|
const Component = RootComponent as SvelteComponentConstructor;
|
|
29
|
-
const instance = new Component({
|
|
30
|
-
target: mountNode,
|
|
31
|
-
props: { container },
|
|
32
|
-
});
|
|
58
|
+
const instance = new Component({ target: mountNode, props });
|
|
33
59
|
|
|
34
60
|
return () => {
|
|
35
61
|
if (typeof instance.$destroy === "function") {
|