ioc-manifest 0.3.0

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 (148) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +350 -0
  3. package/bin/ioc.cjs +19 -0
  4. package/dist/cli/ioc.d.ts +3 -0
  5. package/dist/cli/ioc.d.ts.map +1 -0
  6. package/dist/cli/ioc.js +86 -0
  7. package/dist/cli/ioc.js.map +1 -0
  8. package/dist/cli/parseIocCli.d.ts +21 -0
  9. package/dist/cli/parseIocCli.d.ts.map +1 -0
  10. package/dist/cli/parseIocCli.js +76 -0
  11. package/dist/cli/parseIocCli.js.map +1 -0
  12. package/dist/config/iocConfig.d.ts +97 -0
  13. package/dist/config/iocConfig.d.ts.map +1 -0
  14. package/dist/config/iocConfig.js +65 -0
  15. package/dist/config/iocConfig.js.map +1 -0
  16. package/dist/config/loadIocConfig.d.ts +25 -0
  17. package/dist/config/loadIocConfig.d.ts.map +1 -0
  18. package/dist/config/loadIocConfig.js +245 -0
  19. package/dist/config/loadIocConfig.js.map +1 -0
  20. package/dist/config/parseDiscoveryScanDirs.d.ts +6 -0
  21. package/dist/config/parseDiscoveryScanDirs.d.ts.map +1 -0
  22. package/dist/config/parseDiscoveryScanDirs.js +78 -0
  23. package/dist/config/parseDiscoveryScanDirs.js.map +1 -0
  24. package/dist/core/defaultImplementationSelection.d.ts +14 -0
  25. package/dist/core/defaultImplementationSelection.d.ts.map +1 -0
  26. package/dist/core/defaultImplementationSelection.js +47 -0
  27. package/dist/core/defaultImplementationSelection.js.map +1 -0
  28. package/dist/core/index.d.ts +4 -0
  29. package/dist/core/index.d.ts.map +1 -0
  30. package/dist/core/index.js +3 -0
  31. package/dist/core/index.js.map +1 -0
  32. package/dist/core/manifest.d.ts +79 -0
  33. package/dist/core/manifest.d.ts.map +1 -0
  34. package/dist/core/manifest.js +3 -0
  35. package/dist/core/manifest.js.map +1 -0
  36. package/dist/core/resolver.d.ts +18 -0
  37. package/dist/core/resolver.d.ts.map +1 -0
  38. package/dist/core/resolver.js +27 -0
  39. package/dist/core/resolver.js.map +1 -0
  40. package/dist/generator/contractTypeSourceFile.d.ts +16 -0
  41. package/dist/generator/contractTypeSourceFile.d.ts.map +1 -0
  42. package/dist/generator/contractTypeSourceFile.js +236 -0
  43. package/dist/generator/contractTypeSourceFile.js.map +1 -0
  44. package/dist/generator/discoverFactories/discoverFactories.d.ts +18 -0
  45. package/dist/generator/discoverFactories/discoverFactories.d.ts.map +1 -0
  46. package/dist/generator/discoverFactories/discoverFactories.js +108 -0
  47. package/dist/generator/discoverFactories/discoverFactories.js.map +1 -0
  48. package/dist/generator/discoverFactories/discoveryOutcomeTypes.d.ts +47 -0
  49. package/dist/generator/discoverFactories/discoveryOutcomeTypes.d.ts.map +1 -0
  50. package/dist/generator/discoverFactories/discoveryOutcomeTypes.js +19 -0
  51. package/dist/generator/discoverFactories/discoveryOutcomeTypes.js.map +1 -0
  52. package/dist/generator/discoverFactories/inferFactoryDependencyContracts.d.ts +11 -0
  53. package/dist/generator/discoverFactories/inferFactoryDependencyContracts.d.ts.map +1 -0
  54. package/dist/generator/discoverFactories/inferFactoryDependencyContracts.js +118 -0
  55. package/dist/generator/discoverFactories/inferFactoryDependencyContracts.js.map +1 -0
  56. package/dist/generator/discoverFactories/scanFactoryFile.d.ts +18 -0
  57. package/dist/generator/discoverFactories/scanFactoryFile.d.ts.map +1 -0
  58. package/dist/generator/discoverFactories/scanFactoryFile.js +379 -0
  59. package/dist/generator/discoverFactories/scanFactoryFile.js.map +1 -0
  60. package/dist/generator/generateManifest.d.ts +13 -0
  61. package/dist/generator/generateManifest.d.ts.map +1 -0
  62. package/dist/generator/generateManifest.js +88 -0
  63. package/dist/generator/generateManifest.js.map +1 -0
  64. package/dist/generator/iocProgramContext.d.ts +11 -0
  65. package/dist/generator/iocProgramContext.d.ts.map +1 -0
  66. package/dist/generator/iocProgramContext.js +81 -0
  67. package/dist/generator/iocProgramContext.js.map +1 -0
  68. package/dist/generator/manifestOptions.d.ts +26 -0
  69. package/dist/generator/manifestOptions.d.ts.map +1 -0
  70. package/dist/generator/manifestOptions.js +86 -0
  71. package/dist/generator/manifestOptions.js.map +1 -0
  72. package/dist/generator/manifestPaths.d.ts +94 -0
  73. package/dist/generator/manifestPaths.d.ts.map +1 -0
  74. package/dist/generator/manifestPaths.js +377 -0
  75. package/dist/generator/manifestPaths.js.map +1 -0
  76. package/dist/generator/naming.d.ts +9 -0
  77. package/dist/generator/naming.d.ts.map +1 -0
  78. package/dist/generator/naming.js +46 -0
  79. package/dist/generator/naming.js.map +1 -0
  80. package/dist/generator/resolveRegistrationPlan.d.ts +66 -0
  81. package/dist/generator/resolveRegistrationPlan.d.ts.map +1 -0
  82. package/dist/generator/resolveRegistrationPlan.js +377 -0
  83. package/dist/generator/resolveRegistrationPlan.js.map +1 -0
  84. package/dist/generator/types.d.ts +37 -0
  85. package/dist/generator/types.d.ts.map +1 -0
  86. package/dist/generator/types.js +2 -0
  87. package/dist/generator/types.js.map +1 -0
  88. package/dist/generator/writeManifest.d.ts +19 -0
  89. package/dist/generator/writeManifest.d.ts.map +1 -0
  90. package/dist/generator/writeManifest.js +405 -0
  91. package/dist/generator/writeManifest.js.map +1 -0
  92. package/dist/groups/baseTypeAssignability.d.ts +48 -0
  93. package/dist/groups/baseTypeAssignability.d.ts.map +1 -0
  94. package/dist/groups/baseTypeAssignability.js +145 -0
  95. package/dist/groups/baseTypeAssignability.js.map +1 -0
  96. package/dist/groups/resolveGroupPlan.d.ts +85 -0
  97. package/dist/groups/resolveGroupPlan.d.ts.map +1 -0
  98. package/dist/groups/resolveGroupPlan.js +215 -0
  99. package/dist/groups/resolveGroupPlan.js.map +1 -0
  100. package/dist/index.d.ts +22 -0
  101. package/dist/index.d.ts.map +1 -0
  102. package/dist/index.js +20 -0
  103. package/dist/index.js.map +1 -0
  104. package/dist/inspection/formatReports.d.ts +14 -0
  105. package/dist/inspection/formatReports.d.ts.map +1 -0
  106. package/dist/inspection/formatReports.js +115 -0
  107. package/dist/inspection/formatReports.js.map +1 -0
  108. package/dist/inspection/index.d.ts +8 -0
  109. package/dist/inspection/index.d.ts.map +1 -0
  110. package/dist/inspection/index.js +8 -0
  111. package/dist/inspection/index.js.map +1 -0
  112. package/dist/inspection/reports.d.ts +40 -0
  113. package/dist/inspection/reports.d.ts.map +1 -0
  114. package/dist/inspection/reports.js +95 -0
  115. package/dist/inspection/reports.js.map +1 -0
  116. package/dist/inspection/runDiscoveryAnalysis.d.ts +36 -0
  117. package/dist/inspection/runDiscoveryAnalysis.d.ts.map +1 -0
  118. package/dist/inspection/runDiscoveryAnalysis.js +48 -0
  119. package/dist/inspection/runDiscoveryAnalysis.js.map +1 -0
  120. package/dist/inspection/validateManifest.d.ts +8 -0
  121. package/dist/inspection/validateManifest.d.ts.map +1 -0
  122. package/dist/inspection/validateManifest.js +46 -0
  123. package/dist/inspection/validateManifest.js.map +1 -0
  124. package/dist/runtime/bootstrap.d.ts +13 -0
  125. package/dist/runtime/bootstrap.d.ts.map +1 -0
  126. package/dist/runtime/bootstrap.js +259 -0
  127. package/dist/runtime/bootstrap.js.map +1 -0
  128. package/dist/runtime/index.d.ts +4 -0
  129. package/dist/runtime/index.d.ts.map +1 -0
  130. package/dist/runtime/index.js +4 -0
  131. package/dist/runtime/index.js.map +1 -0
  132. package/dist/runtime/iocResolutionError.d.ts +54 -0
  133. package/dist/runtime/iocResolutionError.d.ts.map +1 -0
  134. package/dist/runtime/iocResolutionError.js +266 -0
  135. package/dist/runtime/iocResolutionError.js.map +1 -0
  136. package/dist/runtime/iocResolutionStack.d.ts +21 -0
  137. package/dist/runtime/iocResolutionStack.d.ts.map +1 -0
  138. package/dist/runtime/iocResolutionStack.js +17 -0
  139. package/dist/runtime/iocResolutionStack.js.map +1 -0
  140. package/dist/runtime/iocRuntimeErrors.d.ts +35 -0
  141. package/dist/runtime/iocRuntimeErrors.d.ts.map +1 -0
  142. package/dist/runtime/iocRuntimeErrors.js +27 -0
  143. package/dist/runtime/iocRuntimeErrors.js.map +1 -0
  144. package/dist/runtime/registrationKeyIndex.d.ts +12 -0
  145. package/dist/runtime/registrationKeyIndex.d.ts.map +1 -0
  146. package/dist/runtime/registrationKeyIndex.js +19 -0
  147. package/dist/runtime/registrationKeyIndex.js.map +1 -0
  148. package/package.json +67 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ioc-manifest contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,350 @@
1
+ # ioc-manifest
2
+
3
+ **Build-time discovery and codegen for Awilix (PROXY mode).** This package generates a typed registration manifest from your TypeScript sources. It is **not** an IoC container and **does not replace Awilix** — you still create an Awilix container and call `registerIocFromManifest` so discovered factories are registered with correct keys and lifetimes.
4
+
5
+ At a glance:
6
+
7
+ - **Discovers** `build*` (configurable) factory exports under `discovery.scanDirs`
8
+ - **Infers** contracts from return types and dependency parameters
9
+ - **Emits** `ioc-manifest.ts` (data for registration) and `ioc-registry.types.ts` (typed cradle)
10
+ - **Registers** everything into Awilix via the small runtime helper in this package
11
+
12
+ ---
13
+
14
+ ## What this is / is not
15
+
16
+ | This package | Not this package |
17
+ | --- | --- |
18
+ | Codegen + conventions + validation | A general-purpose DI container |
19
+ | Works with **Awilix PROXY** mode and flat resolvers | Class/decorator frameworks (NestJS, Inversify, …) |
20
+ | **Production policy** in **`ioc.config` only** | Per-factory registration metadata in source files |
21
+
22
+ **Factories must stay plain exports** (typically arrow functions). **Do not** attach `lifetime`, `scope`, `name`, `accessKey`, or other registration options to factory definitions. Those belong only in **`ioc.config.ts`** (`registrations`, `discovery.scanDirs[].scope`, groups, etc.). The generator does not look for a `{ factory, lifetime }` object pattern in your codebase.
23
+
24
+ ---
25
+
26
+ ## Mental model
27
+
28
+ ```
29
+ Factory export → inferred contract → registration plan (config + conventions)
30
+ → generated manifest → Awilix container (your app)
31
+ ```
32
+
33
+ You write small **factory functions**. The TypeScript program is analyzed at codegen time. The output manifest lists modules, registration keys, lifetimes, and defaults; the runtime applies that list to Awilix.
34
+
35
+ ---
36
+
37
+ ## Conventions
38
+
39
+ ### Factory shape
40
+
41
+ Use a **function** (named `build…` by default) with an explicit or inferable return type. Dependencies are taken by **parameter destructuring** in PROXY mode; after registration, Awilix passes a cradle object whose properties match your generated keys.
42
+
43
+ **Good — plain export, policy in `ioc.config`:**
44
+
45
+ ```ts
46
+ import type { UserRepository } from "./UserRepository.js";
47
+ import type { UserService } from "./UserService.js";
48
+ import type { IocGeneratedCradle } from "../generated/ioc-registry.types.js";
49
+
50
+ export const buildUserService = ({
51
+ userRepository,
52
+ }: IocGeneratedCradle): UserService => {
53
+ return {
54
+ getUser: (id) => userRepository.findById(id),
55
+ };
56
+ };
57
+ ```
58
+
59
+ **Not documented or supported — registration data on the factory:**
60
+
61
+ ```ts
62
+ // Do not do this. Lifetime, scope, and keys belong in ioc.config only.
63
+ export const buildThing = {
64
+ lifetime: "scoped",
65
+ factory: () => {
66
+ /* … */
67
+ },
68
+ };
69
+ ```
70
+
71
+ Class-based constructor injection is out of scope for the intended workflow; you can use classes as *return values*, but the primary pattern is factories + PROXY cradle.
72
+
73
+ ### Key derivation
74
+
75
+ For a factory `buildHttpClient` returning `MyService`:
76
+
77
+ - **Contract** → `MyService` (return type symbol name)
78
+ - **Implementation id** → `httpClient` (export name with prefix stripped, first char lowercased)
79
+ - **Default access key** → `myService` (camel-cased contract name)
80
+
81
+ Resolvers:
82
+
83
+ - `container.resolve("myService")` — default implementation for that contract (unless overridden)
84
+ - `container.resolve("httpClient")` — this implementation by registration key
85
+ - `container.resolve("myServices")` — array when multiple implementations exist and a plural collection key applies
86
+
87
+ Exact keys and lifetimes can be overridden in **`ioc.config`** (see below).
88
+
89
+ ---
90
+
91
+ ## `ioc.config` — single source of policy
92
+
93
+ Put **`ioc.config.ts`** at the package root or under `src/` (see path resolution below). Export **`defineIocConfig({...})`** or a plain object (validation is the same).
94
+
95
+ Only these top-level keys are allowed: `discovery`, `registrations`, `groups`. Unknown keys fail validation with `[ioc-config]`.
96
+
97
+ ### `discovery`
98
+
99
+ | Field | Purpose |
100
+ | --- | --- |
101
+ | `scanDirs` | **Required.** Non-empty string, string array, or array of objects `{ path, importPrefix?, importMode?, scope? }` (see below). Paths are relative to the **package root** (parent of `src/` when config lives in `src/`), unless absolute. |
102
+ | `includes` / `excludes` | Optional glob lists for discovery (defaults exist when omitted — tighten for your repo). |
103
+ | `factoryPrefix` | Prefix for factory exports (default `build`). |
104
+ | `generatedDir` | Where `ioc-manifest.ts` and `ioc-registry.types.ts` are written (default `generated`). |
105
+ | `workspacePackageImportBases` | Optional `{ root, importBase }[]` so contract types emitted under a package root use a **public** bare specifier instead of deep relative paths. Each entry allows only `root` and `importBase`; both must be non-empty strings. |
106
+
107
+ #### Discovery roots and `scanDirs`
108
+
109
+ Each entry defines a **discovery root**: factories under that tree are discovered using the same TS program as your app. If you use **object** entries:
110
+
111
+ - **`scope`** — default Awilix lifetime (`singleton` \| `scoped` \| `transient`) for factories whose source file resolves to this root. Per-implementation `registrations[Contract][impl].lifetime` wins. If one file could match multiple roots with different `scope` values, generation fails with an actionable error.
112
+ - **`importPrefix` + `importMode`** — control how **generated imports** refer to factory modules. When `importPrefix` is set, `importMode` must be `root` (emit exactly that prefix) or `subpath` (emit `${importPrefix}/${pathFromScanRoot}`). `importPrefix` cannot be used without `importMode`. `importMode: "root"` without `importPrefix` is rejected.
113
+
114
+ Relative imports in generated files are always written so consumers depend on **supported** paths (package root, aliases you configured) — not on private paths inside `node_modules`.
115
+
116
+ ### `registrations`
117
+
118
+ Override **defaults**, **lifetimes**, **Awilix registration keys** (`name`), and **contract-level** settings.
119
+
120
+ - Keys at `registrations[ContractName]` are **implementation names** from discovery (`buildX` → `x`), except the reserved **`$contract`** object for contract-level options.
121
+ - Under each implementation, only **`name`**, **`lifetime`**, **`default`** are allowed.
122
+ - Under **`$contract`**, only **`accessKey`** is allowed (non-empty string; cannot be `"$contract"`).
123
+
124
+ **`accessKey`** — cradle / default-slot property for the contract when it should differ from the camel-cased contract name (e.g. singular `database` while the type is `Knex`).
125
+
126
+ **`$contract` example:**
127
+
128
+ ```ts
129
+ registrations: {
130
+ Knex: {
131
+ $contract: { accessKey: "database" },
132
+ pg: { default: true, lifetime: "singleton" },
133
+ },
134
+ },
135
+ ```
136
+
137
+ ### Default implementation when multiple factories share a contract
138
+
139
+ Order of precedence:
140
+
141
+ 1. **`default: true`** on exactly one implementation under `registrations[Contract]` (after merges).
142
+ 2. **Convention** — implementation whose registration key equals the camel-cased contract name (e.g. `myService` for `MyService`).
143
+ 3. **Single implementation** — if only one exists, it becomes the default.
144
+
145
+ If more than one row is marked `default: true`, or the choice is otherwise ambiguous, generation fails with a clear error. **Defaults are configured only in `ioc.config`**, not in factory source files.
146
+
147
+ ### Groups
148
+
149
+ Optional `groups` map names to `{ kind: "collection" \| "object", baseType: "SomeType" }` so multiple contracts assignable to `SomeType` appear as an array (`collection`) or object (`object`) on the cradle. Only `kind` and `baseType` are allowed per entry.
150
+
151
+ ---
152
+
153
+ ## Generated files
154
+
155
+ | File | Role |
156
+ | --- | --- |
157
+ | `ioc-manifest.ts` | Imports factory modules and exports `iocManifest` consumed by `registerIocFromManifest` |
158
+ | `ioc-registry.types.ts` | `IocGeneratedCradle` and related types for `container.resolve` |
159
+
160
+ Treat them as build artifacts: **do not edit by hand**. Regenerate after changing factories or config.
161
+
162
+ Imports in generated modules use types from the **`ioc-manifest`** package (`IocGeneratedContainerManifest`, etc.) — not deep imports into this repo’s `src/`.
163
+
164
+ ---
165
+
166
+ ## Quick start
167
+
168
+ ### 1. Configure
169
+
170
+ ```ts
171
+ // src/ioc.config.ts
172
+ import { defineIocConfig } from "ioc-manifest";
173
+
174
+ export default defineIocConfig({
175
+ discovery: {
176
+ scanDirs: "src",
177
+ includes: ["**/*.{ts,tsx}"],
178
+ excludes: ["**/*.test.ts", "generated/**/*"],
179
+ factoryPrefix: "build",
180
+ generatedDir: "generated",
181
+ },
182
+ });
183
+ ```
184
+
185
+ ### 2. Factories
186
+
187
+ ```ts
188
+ import type { MyService } from "./MyService.js";
189
+
190
+ export const buildHttpClient = (): MyService => {
191
+ return { doThing: () => "hello" };
192
+ };
193
+ ```
194
+
195
+ ### 3. Generate
196
+
197
+ Programmatic:
198
+
199
+ ```ts
200
+ import { generateManifest } from "ioc-manifest";
201
+
202
+ await generateManifest();
203
+ ```
204
+
205
+ Or run your own script that calls `generateManifest({ iocConfigPath: "…" })` when needed. The repo uses `tsx` to run an internal generator script; consumers typically wire `generateManifest` into a `package.json` script.
206
+
207
+ To debug the generator CLI with full stack traces, run with **`IOC_DEBUG=1`** (otherwise only the error message is printed to stderr).
208
+
209
+ ### 4. Bootstrap Awilix
210
+
211
+ ```ts
212
+ import { createContainer, InjectionMode } from "awilix";
213
+ import { registerIocFromManifest } from "ioc-manifest";
214
+ import { iocManifest } from "./generated/ioc-manifest.js";
215
+ import type { IocGeneratedCradle } from "./generated/ioc-registry.types.js";
216
+
217
+ const container = createContainer<IocGeneratedCradle>({
218
+ injectionMode: InjectionMode.PROXY,
219
+ });
220
+
221
+ registerIocFromManifest(container, iocManifest);
222
+
223
+ const svc = container.resolve("myService");
224
+ ```
225
+
226
+ (Use your project’s actual `InjectionMode` import from `awilix`.)
227
+
228
+ ---
229
+
230
+ ## CLI: `ioc`
231
+
232
+ The `ioc` binary is included for inspecting manifests and discovery. **Help** (exit **0**):
233
+
234
+ ```bash
235
+ npx ioc
236
+ npx ioc --help # same as -h
237
+ npx ioc -h
238
+ npx ioc inspect --help
239
+ npx ioc inspect -h
240
+ ```
241
+
242
+ **Inspect** (same flags as always):
243
+
244
+ ```bash
245
+ npx ioc inspect
246
+ npx ioc inspect --discovery
247
+ npx ioc inspect --config ./src/ioc.config.ts --project ./packages/api
248
+ # Equivalent: ... -c ./src/ioc.config.ts
249
+ ```
250
+
251
+ | Flag | Meaning |
252
+ | --- | --- |
253
+ | `--discovery` | Re-run factory discovery / registration planning; do **not** read the generated manifest. |
254
+ | `--config PATH` , `-c PATH` | Explicit `ioc.config.ts` path. |
255
+ | `--project PATH` | Directory used to resolve config (default: current directory). |
256
+
257
+ - **`ioc inspect`** — loads the **generated** `ioc-manifest.ts` from disk (same path resolution as generation), prints a contract/implementation summary.
258
+ - **`ioc inspect --discovery`** — re-runs discovery and registration planning without reading the manifest (good for “why didn’t this register?”).
259
+
260
+ Malformed CLI usage (wrong command / unknown flag) exits **1** with a short message plus a usage hint; **help** and successful **inspect** exit **0**. Manifest or environment failures from Node loaders also exit **1** unless you set **`IOC_DEBUG=1`** for full stack traces.
261
+
262
+ ---
263
+
264
+ ## Larger example (repositories / services / routes)
265
+
266
+ **`src/repos/buildUserRepository.ts`**
267
+
268
+ ```ts
269
+ import type { UserRepository } from "./UserRepository.js";
270
+
271
+ export const buildUserRepository = (): UserRepository => ({
272
+ findById: async () => undefined,
273
+ });
274
+ ```
275
+
276
+ **`src/services/buildUserService.ts`**
277
+
278
+ ```ts
279
+ import type { UserRepository } from "../repos/UserRepository.js";
280
+ import type { UserService } from "./UserService.js";
281
+ import type { IocGeneratedCradle } from "../../generated/ioc-registry.types.js";
282
+
283
+ export const buildUserService = ({
284
+ userRepository,
285
+ }: IocGeneratedCradle): UserService => ({
286
+ get: (id) => userRepository.findById(id),
287
+ });
288
+ ```
289
+
290
+ **`ioc.config.ts`** (lifetimes and defaults here, not in the files above):
291
+
292
+ ```ts
293
+ export default defineIocConfig({
294
+ discovery: { scanDirs: "src", generatedDir: "generated" },
295
+ registrations: {
296
+ UserService: {
297
+ userService: { default: true, lifetime: "scoped" },
298
+ },
299
+ UserRepository: {
300
+ userRepository: { lifetime: "singleton" },
301
+ },
302
+ },
303
+ });
304
+ ```
305
+
306
+ ---
307
+
308
+ ## Error handling and exit codes
309
+
310
+ - Messages for config problems are prefixed **`[ioc-config]`**.
311
+ - Messages for discovery / planning / manifest issues are prefixed **`[ioc]`**.
312
+ - CLI **`ioc`** exits **`0`** for **`-h` / `--help`** and successful **`inspect`**; exits **`1`** on invalid CLI usage or failed inspect/discovery runs.
313
+ - Runtime resolution errors (`IocResolutionError`) include dependency chains for debugging.
314
+
315
+ ---
316
+
317
+ ## Pitfalls and troubleshooting
318
+
319
+ - **Manifest out of date** — Regenerate after editing factories or `ioc.config`.
320
+ - **Contract not discovered** — Return type must resolve to a named type; contract symbol must be in scope (imported or declared in the same file).
321
+ - **Duplicate registration keys or implementation names** — Rename exports or use `registrations` `name` overrides so Awilix keys stay globally unique.
322
+ - **Overlapping `scanDirs` with different `scope`** — Narrow roots or set lifetimes per implementation in `registrations`.
323
+ - **`registrations` for unknown contracts** — Keys must match discovered contract type names; typos fail with a list of discovered contracts.
324
+ - **Inspect shows `lifetimeSource: factory-config`** — That label means the lifetime came from **`ioc.config`**, not from a factory file (historical identifier in inspect output).
325
+
326
+ ---
327
+
328
+ ## Installation
329
+
330
+ ```bash
331
+ npm install ioc-manifest
332
+ ```
333
+
334
+ Peer expectation: your app already uses **Awilix** (this package lists `awilix` as a dependency for types/runtime alignment).
335
+
336
+ ---
337
+
338
+ ## Scripts (this repo)
339
+
340
+ - **`npm run build`** — compile `src/` to `dist/`
341
+ - **`npm test`** — unit and integration tests
342
+ - **`npm run pack:dev`** — build and `npm pack` for a dry-run tarball
343
+
344
+ Consumers normally depend on the published package; only `dist/`, `bin/`, `LICENSE`, and `README.md` are intended for the registry artifact.
345
+
346
+ ---
347
+
348
+ ## License
349
+
350
+ MIT — see [LICENSE](./LICENSE).
package/bin/ioc.cjs ADDED
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ const { spawnSync } = require("node:child_process");
5
+ const path = require("node:path");
6
+
7
+ const pkgRoot = path.join(__dirname, "..");
8
+ const cliJs = path.join(pkgRoot, "dist", "cli", "ioc.js");
9
+
10
+ const result = spawnSync(
11
+ process.execPath,
12
+ [cliJs, ...process.argv.slice(2)],
13
+ { stdio: "inherit" },
14
+ );
15
+
16
+ if (result.error) {
17
+ throw result.error;
18
+ }
19
+ process.exit(result.status === null ? 1 : result.status);
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=ioc.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ioc.d.ts","sourceRoot":"","sources":["../../src/cli/ioc.ts"],"names":[],"mappings":""}
@@ -0,0 +1,86 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * @fileoverview `ioc` CLI: inspects a **generated** manifest on disk (`iocManifest` export).
4
+ *
5
+ * - `ioc inspect` — human-readable contract / implementation summary + manifest validation.
6
+ * - `ioc inspect --discovery` — re-runs source discovery (no manifest read) for drift analysis.
7
+ *
8
+ * Resolves config like generation (`tryLoadIocConfig`, optional `--config`), walking up from cwd
9
+ * (or `--project`) to find `ioc.config.ts` in a monorepo.
10
+ */
11
+ import path from "node:path";
12
+ import { pathToFileURL } from "node:url";
13
+ import { IOC_CLI_HELP_TEXT, parseIocCliArgv, } from "./parseIocCli.js";
14
+ import { resolveIocConfigPath, resolveProjectRootFromIocConfigPath, tryLoadIocConfig, } from "../config/loadIocConfig.js";
15
+ import { mergeManifestOptionsWithIocConfig, resolveManifestOptions, } from "../generator/manifestOptions.js";
16
+ import { buildDiscoveryReport, buildInspectionReport, formatDiscoveryReport, formatInspectionReport, formatRegistrationLifetimeInspect, } from "../inspection/index.js";
17
+ import { resolveDiscoveryManifestContext, runDiscoveryAnalysis, } from "../inspection/runDiscoveryAnalysis.js";
18
+ const formatResolvedScanDir = (e) => {
19
+ let base = e.absPath;
20
+ if (e.scope !== undefined) {
21
+ base = `${base} [scope=${e.scope}]`;
22
+ }
23
+ if (e.importPrefix !== undefined && e.importMode !== undefined) {
24
+ return `${base} → ${e.importPrefix} (${e.importMode})`;
25
+ }
26
+ return base;
27
+ };
28
+ const logInspectContext = (cfgPath, scanDirs) => {
29
+ console.error(`[ioc inspect] resolved config: ${cfgPath}`);
30
+ console.error(`[ioc inspect] resolved discovery scanDirs: ${scanDirs.map(formatResolvedScanDir).join("; ")}`);
31
+ };
32
+ const loadGeneratedManifestModule = async (iocConfigPath, searchStartDir) => {
33
+ const searchStart = path.resolve(searchStartDir ?? process.cwd());
34
+ const cfgPath = resolveIocConfigPath(searchStart, iocConfigPath);
35
+ const config = await tryLoadIocConfig(cfgPath);
36
+ const projectRoot = config
37
+ ? resolveProjectRootFromIocConfigPath(cfgPath)
38
+ : searchStart;
39
+ const base = resolveManifestOptions({
40
+ paths: { projectRoot },
41
+ });
42
+ const options = config
43
+ ? mergeManifestOptionsWithIocConfig(base, config)
44
+ : base;
45
+ logInspectContext(cfgPath, options.paths.scanDirs);
46
+ const manifestPath = path.resolve(options.paths.manifestOutPath);
47
+ const main = (await import(pathToFileURL(manifestPath).href));
48
+ return main;
49
+ };
50
+ const main = async () => {
51
+ const parsed = parseIocCliArgv(process.argv);
52
+ if (parsed.kind === "help") {
53
+ console.log(IOC_CLI_HELP_TEXT.trimEnd());
54
+ return;
55
+ }
56
+ const cli = parsed.options;
57
+ const searchStart = path.resolve(cli.projectDir ?? process.cwd());
58
+ if (cli.discovery) {
59
+ const resolved = await resolveDiscoveryManifestContext({
60
+ iocConfigPath: cli.iocConfigPath,
61
+ searchStartDir: searchStart,
62
+ });
63
+ logInspectContext(resolved.cfgPath, resolved.options.paths.scanDirs);
64
+ const analysis = await runDiscoveryAnalysis({
65
+ reuseResolution: resolved,
66
+ });
67
+ const report = buildDiscoveryReport(analysis);
68
+ console.log(formatDiscoveryReport(report));
69
+ console.log("");
70
+ console.log(formatRegistrationLifetimeInspect(analysis.registrationPlan));
71
+ return;
72
+ }
73
+ const mainMod = await loadGeneratedManifestModule(cli.iocConfigPath, searchStart);
74
+ const report = buildInspectionReport(mainMod.iocManifest.contracts);
75
+ console.log(formatInspectionReport(report));
76
+ };
77
+ main().catch((error) => {
78
+ if (process.env.IOC_DEBUG === "1") {
79
+ console.error(error);
80
+ }
81
+ else {
82
+ console.error(error instanceof Error ? error.message : error);
83
+ }
84
+ process.exit(1);
85
+ });
86
+ //# sourceMappingURL=ioc.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ioc.js","sourceRoot":"","sources":["../../src/cli/ioc.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * @fileoverview Minimal argv parsing for the `ioc` CLI (`inspect` command and `-h/--help`).
3
+ */
4
+ /** Printed for `-h`, `--help`, or bare `ioc` — successful exit 0 */
5
+ export declare const IOC_CLI_HELP_TEXT = "ioc \u2014 inspect Awilix manifests produced by ioc-manifest\n\nUsage:\n ioc [--help|-h]\n ioc inspect [--discovery] [--config <path> | -c <path>] [--project <path>]\n ioc inspect [--help|-h]\n\nCommands:\n ioc inspect Load generated ioc-manifest.ts (unless --discovery), print summary.\n\nOptions:\n --discovery Re-run discovery and registration planning; do not read manifest.\n --config PATH -c Path to ioc.config.ts\n --project PATH Directory to resolve config from (default: cwd)\n\nErrors:\n Set IOC_DEBUG=1 for stack traces alongside messages.\n";
6
+ export type IocInspectCliOptions = {
7
+ iocConfigPath?: string;
8
+ projectDir?: string;
9
+ discovery: boolean;
10
+ };
11
+ export type ParseIocCliArgvResult = {
12
+ kind: "help";
13
+ } | {
14
+ kind: "inspect";
15
+ options: IocInspectCliOptions;
16
+ };
17
+ /**
18
+ * Parses `process.argv`-style arrays (starts with executable and script paths).
19
+ */
20
+ export declare const parseIocCliArgv: (argv: readonly string[]) => ParseIocCliArgvResult;
21
+ //# sourceMappingURL=parseIocCli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parseIocCli.d.ts","sourceRoot":"","sources":["../../src/cli/parseIocCli.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,oEAAoE;AACpE,eAAO,MAAM,iBAAiB,6kBAiB7B,CAAC;AAQF,MAAM,MAAM,oBAAoB,GAAG;IACjC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAC7B;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,OAAO,EAAE,oBAAoB,CAAA;CAAE,CAAC;AAKvD;;GAEG;AACH,eAAO,MAAM,eAAe,GAC1B,MAAM,SAAS,MAAM,EAAE,KACtB,qBAsDF,CAAC"}
@@ -0,0 +1,76 @@
1
+ /**
2
+ * @fileoverview Minimal argv parsing for the `ioc` CLI (`inspect` command and `-h/--help`).
3
+ */
4
+ /** Printed for `-h`, `--help`, or bare `ioc` — successful exit 0 */
5
+ export const IOC_CLI_HELP_TEXT = `ioc — inspect Awilix manifests produced by ioc-manifest
6
+
7
+ Usage:
8
+ ioc [--help|-h]
9
+ ioc inspect [--discovery] [--config <path> | -c <path>] [--project <path>]
10
+ ioc inspect [--help|-h]
11
+
12
+ Commands:
13
+ ioc inspect Load generated ioc-manifest.ts (unless --discovery), print summary.
14
+
15
+ Options:
16
+ --discovery Re-run discovery and registration planning; do not read manifest.
17
+ --config PATH -c Path to ioc.config.ts
18
+ --project PATH Directory to resolve config from (default: cwd)
19
+
20
+ Errors:
21
+ Set IOC_DEBUG=1 for stack traces alongside messages.
22
+ `;
23
+ const isHelpFlag = (s) => s === "--help" || s === "-h";
24
+ const conciseUsageTail = () => '\nUsage: ioc (--help|-h) | ioc inspect [--discovery] [--config <path>|-c <path>] [--project <path>] | ioc inspect (--help|-h)';
25
+ const cliParseError = (detail) => new Error(`${detail}${conciseUsageTail()}`);
26
+ /**
27
+ * Parses `process.argv`-style arrays (starts with executable and script paths).
28
+ */
29
+ export const parseIocCliArgv = (argv) => {
30
+ const args = argv.slice(2);
31
+ if (args.length === 0 || (args.length === 1 && isHelpFlag(args[0] ?? ""))) {
32
+ return { kind: "help" };
33
+ }
34
+ if (args[0] === "inspect" && args.slice(1).some(isHelpFlag)) {
35
+ return { kind: "help" };
36
+ }
37
+ const command = args[0];
38
+ if (command !== "inspect") {
39
+ throw cliParseError(`Unknown command ${JSON.stringify(command)}. Supported: inspect.`);
40
+ }
41
+ let iocConfigPath;
42
+ let projectDir;
43
+ let discovery = false;
44
+ for (let i = 1; i < args.length; i += 1) {
45
+ const a = args[i];
46
+ if (a === undefined) {
47
+ break;
48
+ }
49
+ if (a === "--discovery") {
50
+ discovery = true;
51
+ continue;
52
+ }
53
+ if ((a === "--config" || a === "-c") && args[i + 1]) {
54
+ iocConfigPath = args[i + 1];
55
+ i += 1;
56
+ continue;
57
+ }
58
+ if (a === "--project" && args[i + 1]) {
59
+ projectDir = args[i + 1];
60
+ i += 1;
61
+ continue;
62
+ }
63
+ if (a.startsWith("-")) {
64
+ throw cliParseError(`Unknown flag ${JSON.stringify(a)}.`);
65
+ }
66
+ }
67
+ return {
68
+ kind: "inspect",
69
+ options: {
70
+ ...(iocConfigPath !== undefined ? { iocConfigPath } : {}),
71
+ ...(projectDir !== undefined ? { projectDir } : {}),
72
+ discovery,
73
+ },
74
+ };
75
+ };
76
+ //# sourceMappingURL=parseIocCli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parseIocCli.js","sourceRoot":"","sources":["../../src/cli/parseIocCli.ts"],"names":[],"mappings":"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"}