@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 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 inversify reflect-metadata
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 auto-loading from `basePath/config`
189
+ ## Config
113
190
 
114
- `Application.configure(basePath)` controls where config files are discovered.
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
- // ./config/pricing.ts
132
- export default {
133
- taxRate: 0.0825,
134
- currency: "USD",
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
- - Config source files are cached by `basePath`; use `Application.clearConfigCache()` in tests when needed.
216
+ - `Application.configure(basePath)` is still supported for compatibility, but config values should be passed via `configure({ config })`.
package/package.json CHANGED
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "name": "@raubjo/architect-core",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "private": false,
5
+ "license": "MIT",
5
6
  "type": "module",
6
7
  "publishConfig": {
7
8
  "access": "public"
@@ -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") {