@djodjonx/neo-syringe 1.1.5 → 1.2.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 (61) hide show
  1. package/.github/workflows/ci.yml +6 -5
  2. package/.github/workflows/docs.yml +59 -0
  3. package/CHANGELOG.md +27 -0
  4. package/README.md +74 -740
  5. package/dist/{GraphValidator-G0F4QiLk.cjs → GraphValidator-CV4VoJl0.cjs} +18 -10
  6. package/dist/{GraphValidator-C8ldJtNp.mjs → GraphValidator-DXqqkNdS.mjs} +18 -10
  7. package/dist/cli/index.cjs +16 -1
  8. package/dist/cli/index.mjs +16 -1
  9. package/dist/index.d.cts +31 -5
  10. package/dist/index.d.mts +31 -5
  11. package/dist/lsp/index.cjs +1 -1
  12. package/dist/lsp/index.mjs +1 -1
  13. package/dist/unplugin/index.cjs +33 -9
  14. package/dist/unplugin/index.d.cts +7 -5
  15. package/dist/unplugin/index.d.mts +7 -5
  16. package/dist/unplugin/index.mjs +33 -9
  17. package/docs/.vitepress/config.ts +109 -0
  18. package/docs/.vitepress/theme/custom.css +150 -0
  19. package/docs/.vitepress/theme/index.ts +17 -0
  20. package/docs/api/configuration.md +274 -0
  21. package/docs/api/functions.md +291 -0
  22. package/docs/api/types.md +158 -0
  23. package/docs/guide/basic-usage.md +267 -0
  24. package/docs/guide/cli.md +174 -0
  25. package/docs/guide/generated-code.md +284 -0
  26. package/docs/guide/getting-started.md +171 -0
  27. package/docs/guide/ide-plugin.md +203 -0
  28. package/docs/guide/injection-types.md +287 -0
  29. package/docs/guide/legacy-migration.md +333 -0
  30. package/docs/guide/lifecycle.md +223 -0
  31. package/docs/guide/parent-container.md +321 -0
  32. package/docs/guide/scoped-injections.md +271 -0
  33. package/docs/guide/what-is-neo-syringe.md +162 -0
  34. package/docs/guide/why-neo-syringe.md +219 -0
  35. package/docs/index.md +138 -0
  36. package/docs/public/logo.png +0 -0
  37. package/package.json +15 -12
  38. package/src/analyzer/Analyzer.ts +20 -10
  39. package/src/analyzer/types.ts +55 -49
  40. package/src/cli/index.ts +15 -0
  41. package/src/generator/Generator.ts +24 -2
  42. package/src/generator/GraphValidator.ts +6 -2
  43. package/src/types.ts +30 -4
  44. package/src/unplugin/index.ts +13 -41
  45. package/tests/analyzer/Analyzer.test.ts +4 -4
  46. package/tests/analyzer/AnalyzerDeclarative.test.ts +1 -1
  47. package/tests/analyzer/Factory.test.ts +2 -2
  48. package/tests/analyzer/Scoped.test.ts +434 -0
  49. package/tests/cli/cli.test.ts +91 -0
  50. package/tests/e2e/container-integration.test.ts +21 -21
  51. package/tests/e2e/generated-code.test.ts +7 -7
  52. package/tests/e2e/scoped.test.ts +370 -0
  53. package/tests/e2e/snapshots.test.ts +2 -2
  54. package/tests/e2e/standalone.test.ts +2 -2
  55. package/tests/generator/ExternalGenerator.test.ts +1 -1
  56. package/tests/generator/FactoryGenerator.test.ts +6 -6
  57. package/tests/generator/Generator.test.ts +2 -2
  58. package/tests/generator/GeneratorDeclarative.test.ts +1 -1
  59. package/tests/generator/GraphValidator.test.ts +1 -1
  60. package/tsconfig.json +2 -1
  61. package/typedoc.json +0 -5
@@ -204,16 +204,19 @@ var Analyzer = class {
204
204
  parseInjectionObject(obj, graph) {
205
205
  let tokenNode;
206
206
  let providerNode;
207
- let scope = "singleton";
207
+ let lifecycle = "singleton";
208
208
  let useFactory = false;
209
+ let isScoped = false;
209
210
  for (const prop of obj.properties) {
210
211
  if (!typescript.isPropertyAssignment(prop) || !typescript.isIdentifier(prop.name)) continue;
211
212
  if (prop.name.text === "token") tokenNode = prop.initializer;
212
213
  else if (prop.name.text === "provider") providerNode = prop.initializer;
213
- else if (prop.name.text === "scope" && typescript.isStringLiteral(prop.initializer)) {
214
- if (prop.initializer.text === "transient") scope = "transient";
214
+ else if (prop.name.text === "lifecycle" && typescript.isStringLiteral(prop.initializer)) {
215
+ if (prop.initializer.text === "transient") lifecycle = "transient";
215
216
  } else if (prop.name.text === "useFactory") {
216
217
  if (prop.initializer.kind === typescript.SyntaxKind.TrueKeyword) useFactory = true;
218
+ } else if (prop.name.text === "scoped") {
219
+ if (prop.initializer.kind === typescript.SyntaxKind.TrueKeyword) isScoped = true;
217
220
  }
218
221
  }
219
222
  if (!tokenNode) return;
@@ -250,17 +253,18 @@ var Analyzer = class {
250
253
  factorySource = providerNode.getText();
251
254
  type = "factory";
252
255
  if (tokenId) {
253
- if (graph.nodes.has(tokenId)) throw new Error(`Duplicate registration: '${tokenId}' is already registered.`);
256
+ if (graph.nodes.has(tokenId) && !isScoped) throw new Error(`Duplicate registration: '${tokenId}' is already registered.`);
254
257
  const definition = {
255
258
  tokenId,
256
259
  tokenSymbol: tokenSymbol ? this.resolveSymbol(tokenSymbol) : void 0,
257
260
  registrationNode: obj,
258
261
  type: "factory",
259
- scope,
262
+ lifecycle,
260
263
  isInterfaceToken,
261
264
  isValueToken,
262
265
  isFactory: true,
263
- factorySource
266
+ factorySource,
267
+ isScoped
264
268
  };
265
269
  graph.nodes.set(tokenId, {
266
270
  service: definition,
@@ -282,15 +286,16 @@ var Analyzer = class {
282
286
  type = "autowire";
283
287
  }
284
288
  if (tokenId && implementationSymbol) {
285
- if (graph.nodes.has(tokenId)) throw new Error(`Duplicate registration: '${tokenId}' is already registered.`);
289
+ if (graph.nodes.has(tokenId) && !isScoped) throw new Error(`Duplicate registration: '${tokenId}' is already registered.`);
286
290
  const definition = {
287
291
  tokenId,
288
292
  implementationSymbol: this.resolveSymbol(implementationSymbol),
289
293
  tokenSymbol: tokenSymbol ? this.resolveSymbol(tokenSymbol) : void 0,
290
294
  registrationNode: obj,
291
295
  type,
292
- scope,
293
- isInterfaceToken: isInterfaceToken || typescript.isCallExpression(tokenNode) && this.isUseInterfaceCall(tokenNode)
296
+ lifecycle,
297
+ isInterfaceToken: isInterfaceToken || typescript.isCallExpression(tokenNode) && this.isUseInterfaceCall(tokenNode),
298
+ isScoped
294
299
  };
295
300
  graph.nodes.set(tokenId, {
296
301
  service: definition,
@@ -433,7 +438,10 @@ var GraphValidator = class {
433
438
  const visited = /* @__PURE__ */ new Set();
434
439
  const recursionStack = /* @__PURE__ */ new Set();
435
440
  const parentTokens = graph.parentProvidedTokens ?? /* @__PURE__ */ new Set();
436
- for (const nodeId of graph.nodes.keys()) if (parentTokens.has(nodeId)) throw new Error(`Duplicate registration: '${nodeId}' is already registered in the parent container. Remove the local registration or use a different token.`);
441
+ for (const [nodeId, node] of graph.nodes) if (parentTokens.has(nodeId)) {
442
+ if (node.service.isScoped) continue;
443
+ throw new Error(`Duplicate registration: '${nodeId}' is already registered in the parent container. Use 'scoped: true' to override the parent's registration intentionally.`);
444
+ }
437
445
  for (const [nodeId, node] of graph.nodes) for (const depId of node.dependencies) {
438
446
  const isProvidedLocally = graph.nodes.has(depId);
439
447
  const isProvidedByParent = parentTokens.has(depId);
@@ -176,16 +176,19 @@ var Analyzer = class {
176
176
  parseInjectionObject(obj, graph) {
177
177
  let tokenNode;
178
178
  let providerNode;
179
- let scope = "singleton";
179
+ let lifecycle = "singleton";
180
180
  let useFactory = false;
181
+ let isScoped = false;
181
182
  for (const prop of obj.properties) {
182
183
  if (!ts.isPropertyAssignment(prop) || !ts.isIdentifier(prop.name)) continue;
183
184
  if (prop.name.text === "token") tokenNode = prop.initializer;
184
185
  else if (prop.name.text === "provider") providerNode = prop.initializer;
185
- else if (prop.name.text === "scope" && ts.isStringLiteral(prop.initializer)) {
186
- if (prop.initializer.text === "transient") scope = "transient";
186
+ else if (prop.name.text === "lifecycle" && ts.isStringLiteral(prop.initializer)) {
187
+ if (prop.initializer.text === "transient") lifecycle = "transient";
187
188
  } else if (prop.name.text === "useFactory") {
188
189
  if (prop.initializer.kind === ts.SyntaxKind.TrueKeyword) useFactory = true;
190
+ } else if (prop.name.text === "scoped") {
191
+ if (prop.initializer.kind === ts.SyntaxKind.TrueKeyword) isScoped = true;
189
192
  }
190
193
  }
191
194
  if (!tokenNode) return;
@@ -222,17 +225,18 @@ var Analyzer = class {
222
225
  factorySource = providerNode.getText();
223
226
  type = "factory";
224
227
  if (tokenId) {
225
- if (graph.nodes.has(tokenId)) throw new Error(`Duplicate registration: '${tokenId}' is already registered.`);
228
+ if (graph.nodes.has(tokenId) && !isScoped) throw new Error(`Duplicate registration: '${tokenId}' is already registered.`);
226
229
  const definition = {
227
230
  tokenId,
228
231
  tokenSymbol: tokenSymbol ? this.resolveSymbol(tokenSymbol) : void 0,
229
232
  registrationNode: obj,
230
233
  type: "factory",
231
- scope,
234
+ lifecycle,
232
235
  isInterfaceToken,
233
236
  isValueToken,
234
237
  isFactory: true,
235
- factorySource
238
+ factorySource,
239
+ isScoped
236
240
  };
237
241
  graph.nodes.set(tokenId, {
238
242
  service: definition,
@@ -254,15 +258,16 @@ var Analyzer = class {
254
258
  type = "autowire";
255
259
  }
256
260
  if (tokenId && implementationSymbol) {
257
- if (graph.nodes.has(tokenId)) throw new Error(`Duplicate registration: '${tokenId}' is already registered.`);
261
+ if (graph.nodes.has(tokenId) && !isScoped) throw new Error(`Duplicate registration: '${tokenId}' is already registered.`);
258
262
  const definition = {
259
263
  tokenId,
260
264
  implementationSymbol: this.resolveSymbol(implementationSymbol),
261
265
  tokenSymbol: tokenSymbol ? this.resolveSymbol(tokenSymbol) : void 0,
262
266
  registrationNode: obj,
263
267
  type,
264
- scope,
265
- isInterfaceToken: isInterfaceToken || ts.isCallExpression(tokenNode) && this.isUseInterfaceCall(tokenNode)
268
+ lifecycle,
269
+ isInterfaceToken: isInterfaceToken || ts.isCallExpression(tokenNode) && this.isUseInterfaceCall(tokenNode),
270
+ isScoped
266
271
  };
267
272
  graph.nodes.set(tokenId, {
268
273
  service: definition,
@@ -405,7 +410,10 @@ var GraphValidator = class {
405
410
  const visited = /* @__PURE__ */ new Set();
406
411
  const recursionStack = /* @__PURE__ */ new Set();
407
412
  const parentTokens = graph.parentProvidedTokens ?? /* @__PURE__ */ new Set();
408
- for (const nodeId of graph.nodes.keys()) if (parentTokens.has(nodeId)) throw new Error(`Duplicate registration: '${nodeId}' is already registered in the parent container. Remove the local registration or use a different token.`);
413
+ for (const [nodeId, node] of graph.nodes) if (parentTokens.has(nodeId)) {
414
+ if (node.service.isScoped) continue;
415
+ throw new Error(`Duplicate registration: '${nodeId}' is already registered in the parent container. Use 'scoped: true' to override the parent's registration intentionally.`);
416
+ }
409
417
  for (const [nodeId, node] of graph.nodes) for (const depId of node.dependencies) {
410
418
  const isProvidedLocally = graph.nodes.has(depId);
411
419
  const isProvidedByParent = parentTokens.has(depId);
@@ -1,9 +1,24 @@
1
1
  #!/usr/bin/env node
2
- const require_GraphValidator = require('../GraphValidator-G0F4QiLk.cjs');
2
+ const require_GraphValidator = require('../GraphValidator-CV4VoJl0.cjs');
3
3
  let typescript = require("typescript");
4
4
  typescript = require_GraphValidator.__toESM(typescript);
5
5
 
6
6
  //#region src/cli/index.ts
7
+ /**
8
+ * Neo-Syringe CLI
9
+ *
10
+ * Validates the dependency graph for a TypeScript project.
11
+ * Detects circular dependencies, missing bindings, and duplicate registrations.
12
+ *
13
+ * @example
14
+ * ```bash
15
+ * npx neo-syringe
16
+ * ```
17
+ */
18
+ /**
19
+ * CLI entry point.
20
+ * Reads tsconfig.json, analyzes the project, and validates the dependency graph.
21
+ */
7
22
  function main() {
8
23
  const cwd = process.cwd();
9
24
  const configPath = typescript.findConfigFile(cwd, typescript.sys.fileExists, "tsconfig.json");
@@ -1,8 +1,23 @@
1
1
  #!/usr/bin/env node
2
- import { n as Analyzer, t as GraphValidator } from "../GraphValidator-C8ldJtNp.mjs";
2
+ import { n as Analyzer, t as GraphValidator } from "../GraphValidator-DXqqkNdS.mjs";
3
3
  import * as ts from "typescript";
4
4
 
5
5
  //#region src/cli/index.ts
6
+ /**
7
+ * Neo-Syringe CLI
8
+ *
9
+ * Validates the dependency graph for a TypeScript project.
10
+ * Detects circular dependencies, missing bindings, and duplicate registrations.
11
+ *
12
+ * @example
13
+ * ```bash
14
+ * npx neo-syringe
15
+ * ```
16
+ */
17
+ /**
18
+ * CLI entry point.
19
+ * Reads tsconfig.json, analyzes the project, and validates the dependency graph.
20
+ */
6
21
  function main() {
7
22
  const cwd = process.cwd();
8
23
  const configPath = ts.findConfigFile(cwd, ts.sys.fileExists, "tsconfig.json");
package/dist/index.d.cts CHANGED
@@ -3,13 +3,13 @@
3
3
  * Represents a generic class constructor.
4
4
  * @template T - The type of the instance created by the constructor.
5
5
  */
6
- type Constructor<T = unknown> = new (...args: unknown[]) => T;
6
+ type Constructor<T = any> = new (...args: any[]) => T;
7
7
  /**
8
- * Defines the lifecycle scope of a service.
8
+ * Defines the lifecycle of a service.
9
9
  * - `singleton`: One instance per container.
10
10
  * - `transient`: A new instance every time it is resolved.
11
11
  */
12
- type Scope = 'singleton' | 'transient';
12
+ type Lifecycle = 'singleton' | 'transient';
13
13
  /**
14
14
  * The dependency injection container interface.
15
15
  * Provides access to the registered services.
@@ -74,7 +74,33 @@ interface Injection<T = any> {
74
74
  * Required when provider is a function, not a class.
75
75
  */
76
76
  useFactory?: boolean;
77
- scope?: Scope;
77
+ /**
78
+ * Lifecycle of the service.
79
+ * - `singleton`: One instance per container (default).
80
+ * - `transient`: A new instance every time it is resolved.
81
+ */
82
+ lifecycle?: Lifecycle;
83
+ /**
84
+ * If true, this injection is scoped to this container only.
85
+ * Allows overriding a token from a parent container without causing a duplicate error.
86
+ * The local instance will be used instead of delegating to the parent.
87
+ *
88
+ * @example
89
+ * ```typescript
90
+ * const parent = defineBuilderConfig({
91
+ * injections: [{ token: useInterface<ILogger>(), provider: ConsoleLogger }]
92
+ * });
93
+ *
94
+ * const child = defineBuilderConfig({
95
+ * useContainer: parent,
96
+ * injections: [
97
+ * // Override parent's ILogger with a local FileLogger
98
+ * { token: useInterface<ILogger>(), provider: FileLogger, scoped: true }
99
+ * ]
100
+ * });
101
+ * ```
102
+ */
103
+ scoped?: boolean;
78
104
  }
79
105
  /**
80
106
  * Partial configuration that can be shared/extended.
@@ -139,4 +165,4 @@ declare function useProperty<T, C extends Constructor<any>>(targetClass: C, para
139
165
  */
140
166
  declare function declareContainerTokens<T extends Record<string, any>>(container: any): T & { [K in keyof T]: T[K] };
141
167
  //#endregion
142
- export { BuilderConfig, Constructor, Container, Factory, Injection, InterfaceToken, PartialConfig, PropertyToken, Provider, Scope, Token, declareContainerTokens, defineBuilderConfig, definePartialConfig, useInterface, useProperty };
168
+ export { BuilderConfig, Constructor, Container, Factory, Injection, InterfaceToken, Lifecycle, PartialConfig, PropertyToken, Provider, Token, declareContainerTokens, defineBuilderConfig, definePartialConfig, useInterface, useProperty };
package/dist/index.d.mts CHANGED
@@ -3,13 +3,13 @@
3
3
  * Represents a generic class constructor.
4
4
  * @template T - The type of the instance created by the constructor.
5
5
  */
6
- type Constructor<T = unknown> = new (...args: unknown[]) => T;
6
+ type Constructor<T = any> = new (...args: any[]) => T;
7
7
  /**
8
- * Defines the lifecycle scope of a service.
8
+ * Defines the lifecycle of a service.
9
9
  * - `singleton`: One instance per container.
10
10
  * - `transient`: A new instance every time it is resolved.
11
11
  */
12
- type Scope = 'singleton' | 'transient';
12
+ type Lifecycle = 'singleton' | 'transient';
13
13
  /**
14
14
  * The dependency injection container interface.
15
15
  * Provides access to the registered services.
@@ -74,7 +74,33 @@ interface Injection<T = any> {
74
74
  * Required when provider is a function, not a class.
75
75
  */
76
76
  useFactory?: boolean;
77
- scope?: Scope;
77
+ /**
78
+ * Lifecycle of the service.
79
+ * - `singleton`: One instance per container (default).
80
+ * - `transient`: A new instance every time it is resolved.
81
+ */
82
+ lifecycle?: Lifecycle;
83
+ /**
84
+ * If true, this injection is scoped to this container only.
85
+ * Allows overriding a token from a parent container without causing a duplicate error.
86
+ * The local instance will be used instead of delegating to the parent.
87
+ *
88
+ * @example
89
+ * ```typescript
90
+ * const parent = defineBuilderConfig({
91
+ * injections: [{ token: useInterface<ILogger>(), provider: ConsoleLogger }]
92
+ * });
93
+ *
94
+ * const child = defineBuilderConfig({
95
+ * useContainer: parent,
96
+ * injections: [
97
+ * // Override parent's ILogger with a local FileLogger
98
+ * { token: useInterface<ILogger>(), provider: FileLogger, scoped: true }
99
+ * ]
100
+ * });
101
+ * ```
102
+ */
103
+ scoped?: boolean;
78
104
  }
79
105
  /**
80
106
  * Partial configuration that can be shared/extended.
@@ -139,4 +165,4 @@ declare function useProperty<T, C extends Constructor<any>>(targetClass: C, para
139
165
  */
140
166
  declare function declareContainerTokens<T extends Record<string, any>>(container: any): T & { [K in keyof T]: T[K] };
141
167
  //#endregion
142
- export { BuilderConfig, Constructor, Container, Factory, Injection, InterfaceToken, PartialConfig, PropertyToken, Provider, Scope, Token, declareContainerTokens, defineBuilderConfig, definePartialConfig, useInterface, useProperty };
168
+ export { BuilderConfig, Constructor, Container, Factory, Injection, InterfaceToken, Lifecycle, PartialConfig, PropertyToken, Provider, Token, declareContainerTokens, defineBuilderConfig, definePartialConfig, useInterface, useProperty };
@@ -1,4 +1,4 @@
1
- const require_GraphValidator = require('../GraphValidator-G0F4QiLk.cjs');
1
+ const require_GraphValidator = require('../GraphValidator-CV4VoJl0.cjs');
2
2
 
3
3
  //#region src/lsp/index.ts
4
4
  /**
@@ -1,4 +1,4 @@
1
- import { n as Analyzer, t as GraphValidator } from "../GraphValidator-C8ldJtNp.mjs";
1
+ import { n as Analyzer, t as GraphValidator } from "../GraphValidator-DXqqkNdS.mjs";
2
2
 
3
3
  //#region src/lsp/index.ts
4
4
  /**
@@ -1,16 +1,29 @@
1
- const require_GraphValidator = require('../GraphValidator-G0F4QiLk.cjs');
1
+ const require_GraphValidator = require('../GraphValidator-CV4VoJl0.cjs');
2
2
  let unplugin = require("unplugin");
3
3
  let typescript = require("typescript");
4
4
  typescript = require_GraphValidator.__toESM(typescript);
5
5
 
6
6
  //#region src/generator/Generator.ts
7
7
  /**
8
- * Generates the TypeScript code for the dependency injection container.
8
+ * Generates TypeScript code for the dependency injection container.
9
+ *
10
+ * Takes a validated dependency graph and produces:
11
+ * - Import statements for all dependencies
12
+ * - Factory functions for each service
13
+ * - A NeoContainer class with resolve logic
9
14
  */
10
15
  var Generator = class {
16
+ /**
17
+ * Creates a new Generator.
18
+ * @param graph - The validated dependency graph to generate code from.
19
+ */
11
20
  constructor(graph) {
12
21
  this.graph = graph;
13
22
  }
23
+ /**
24
+ * Generates the complete container code as a string.
25
+ * @returns TypeScript source code for the generated container.
26
+ */
14
27
  generate() {
15
28
  const sorted = this.topologicalSort();
16
29
  const imports = /* @__PURE__ */ new Map();
@@ -53,7 +66,7 @@ function ${factoryId}(container: NeoContainer) {
53
66
  return new ${className}(${args});
54
67
  }`);
55
68
  }
56
- const isTransient = node.service.scope === "transient";
69
+ const isTransient = node.service.lifecycle === "transient";
57
70
  let tokenKey;
58
71
  let tokenCheck;
59
72
  if (node.service.isInterfaceToken) {
@@ -147,6 +160,10 @@ export class NeoContainer {
147
160
  export const container = new NeoContainer(${containerArgs});
148
161
  `;
149
162
  }
163
+ /**
164
+ * Sorts services in topological order (dependencies before dependents).
165
+ * @returns Array of TokenIds in dependency order.
166
+ */
150
167
  topologicalSort() {
151
168
  const visited = /* @__PURE__ */ new Set();
152
169
  const sorted = [];
@@ -162,6 +179,11 @@ export const container = new NeoContainer(${containerArgs});
162
179
  for (const id of this.graph.nodes.keys()) visit(id);
163
180
  return sorted;
164
181
  }
182
+ /**
183
+ * Creates a valid JavaScript function name from a token ID.
184
+ * @param tokenId - The token identifier.
185
+ * @returns A sanitized factory function name.
186
+ */
165
187
  getFactoryName(tokenId) {
166
188
  return `create_${tokenId.replace(/[^a-zA-Z0-9]/g, "_")}`;
167
189
  }
@@ -170,21 +192,23 @@ export const container = new NeoContainer(${containerArgs});
170
192
  //#endregion
171
193
  //#region src/unplugin/index.ts
172
194
  /**
173
- * The Unplugin factory for Neo-Syringe.
195
+ * Neo-Syringe build plugin for Vite, Rollup, Webpack, and other bundlers.
174
196
  *
175
- * This plugin integrates with Vite, Rollup, Webpack, etc.
176
- * It intercepts files containing `createContainer` calls, analyzes them,
177
- * and replaces the runtime configuration code with the generated dependency graph.
197
+ * Intercepts files containing `defineBuilderConfig` calls, analyzes the
198
+ * dependency graph, validates it, and replaces the configuration with
199
+ * generated factory code.
178
200
  *
179
201
  * @example
202
+ * ```typescript
180
203
  * // vite.config.ts
181
204
  * import { neoSyringePlugin } from '@djodjonx/neo-syringe/plugin';
182
205
  *
183
206
  * export default defineConfig({
184
- * plugins: [neoSyringePlugin.vite()],
207
+ * plugins: [neoSyringePlugin.vite()]
185
208
  * });
209
+ * ```
186
210
  */
187
- const neoSyringePlugin = (0, unplugin.createUnplugin)((_options) => {
211
+ const neoSyringePlugin = (0, unplugin.createUnplugin)(() => {
188
212
  return {
189
213
  name: "neo-syringe-plugin",
190
214
  transformInclude(id) {
@@ -2,19 +2,21 @@ import * as unplugin0 from "unplugin";
2
2
 
3
3
  //#region src/unplugin/index.d.ts
4
4
  /**
5
- * The Unplugin factory for Neo-Syringe.
5
+ * Neo-Syringe build plugin for Vite, Rollup, Webpack, and other bundlers.
6
6
  *
7
- * This plugin integrates with Vite, Rollup, Webpack, etc.
8
- * It intercepts files containing `createContainer` calls, analyzes them,
9
- * and replaces the runtime configuration code with the generated dependency graph.
7
+ * Intercepts files containing `defineBuilderConfig` calls, analyzes the
8
+ * dependency graph, validates it, and replaces the configuration with
9
+ * generated factory code.
10
10
  *
11
11
  * @example
12
+ * ```typescript
12
13
  * // vite.config.ts
13
14
  * import { neoSyringePlugin } from '@djodjonx/neo-syringe/plugin';
14
15
  *
15
16
  * export default defineConfig({
16
- * plugins: [neoSyringePlugin.vite()],
17
+ * plugins: [neoSyringePlugin.vite()]
17
18
  * });
19
+ * ```
18
20
  */
19
21
  declare const neoSyringePlugin: unplugin0.UnpluginInstance<unknown, boolean>;
20
22
  //#endregion
@@ -2,19 +2,21 @@ import * as unplugin0 from "unplugin";
2
2
 
3
3
  //#region src/unplugin/index.d.ts
4
4
  /**
5
- * The Unplugin factory for Neo-Syringe.
5
+ * Neo-Syringe build plugin for Vite, Rollup, Webpack, and other bundlers.
6
6
  *
7
- * This plugin integrates with Vite, Rollup, Webpack, etc.
8
- * It intercepts files containing `createContainer` calls, analyzes them,
9
- * and replaces the runtime configuration code with the generated dependency graph.
7
+ * Intercepts files containing `defineBuilderConfig` calls, analyzes the
8
+ * dependency graph, validates it, and replaces the configuration with
9
+ * generated factory code.
10
10
  *
11
11
  * @example
12
+ * ```typescript
12
13
  * // vite.config.ts
13
14
  * import { neoSyringePlugin } from '@djodjonx/neo-syringe/plugin';
14
15
  *
15
16
  * export default defineConfig({
16
- * plugins: [neoSyringePlugin.vite()],
17
+ * plugins: [neoSyringePlugin.vite()]
17
18
  * });
19
+ * ```
18
20
  */
19
21
  declare const neoSyringePlugin: unplugin0.UnpluginInstance<unknown, boolean>;
20
22
  //#endregion
@@ -1,15 +1,28 @@
1
- import { n as Analyzer, t as GraphValidator } from "../GraphValidator-C8ldJtNp.mjs";
1
+ import { n as Analyzer, t as GraphValidator } from "../GraphValidator-DXqqkNdS.mjs";
2
2
  import { createUnplugin } from "unplugin";
3
3
  import * as ts from "typescript";
4
4
 
5
5
  //#region src/generator/Generator.ts
6
6
  /**
7
- * Generates the TypeScript code for the dependency injection container.
7
+ * Generates TypeScript code for the dependency injection container.
8
+ *
9
+ * Takes a validated dependency graph and produces:
10
+ * - Import statements for all dependencies
11
+ * - Factory functions for each service
12
+ * - A NeoContainer class with resolve logic
8
13
  */
9
14
  var Generator = class {
15
+ /**
16
+ * Creates a new Generator.
17
+ * @param graph - The validated dependency graph to generate code from.
18
+ */
10
19
  constructor(graph) {
11
20
  this.graph = graph;
12
21
  }
22
+ /**
23
+ * Generates the complete container code as a string.
24
+ * @returns TypeScript source code for the generated container.
25
+ */
13
26
  generate() {
14
27
  const sorted = this.topologicalSort();
15
28
  const imports = /* @__PURE__ */ new Map();
@@ -52,7 +65,7 @@ function ${factoryId}(container: NeoContainer) {
52
65
  return new ${className}(${args});
53
66
  }`);
54
67
  }
55
- const isTransient = node.service.scope === "transient";
68
+ const isTransient = node.service.lifecycle === "transient";
56
69
  let tokenKey;
57
70
  let tokenCheck;
58
71
  if (node.service.isInterfaceToken) {
@@ -146,6 +159,10 @@ export class NeoContainer {
146
159
  export const container = new NeoContainer(${containerArgs});
147
160
  `;
148
161
  }
162
+ /**
163
+ * Sorts services in topological order (dependencies before dependents).
164
+ * @returns Array of TokenIds in dependency order.
165
+ */
149
166
  topologicalSort() {
150
167
  const visited = /* @__PURE__ */ new Set();
151
168
  const sorted = [];
@@ -161,6 +178,11 @@ export const container = new NeoContainer(${containerArgs});
161
178
  for (const id of this.graph.nodes.keys()) visit(id);
162
179
  return sorted;
163
180
  }
181
+ /**
182
+ * Creates a valid JavaScript function name from a token ID.
183
+ * @param tokenId - The token identifier.
184
+ * @returns A sanitized factory function name.
185
+ */
164
186
  getFactoryName(tokenId) {
165
187
  return `create_${tokenId.replace(/[^a-zA-Z0-9]/g, "_")}`;
166
188
  }
@@ -169,21 +191,23 @@ export const container = new NeoContainer(${containerArgs});
169
191
  //#endregion
170
192
  //#region src/unplugin/index.ts
171
193
  /**
172
- * The Unplugin factory for Neo-Syringe.
194
+ * Neo-Syringe build plugin for Vite, Rollup, Webpack, and other bundlers.
173
195
  *
174
- * This plugin integrates with Vite, Rollup, Webpack, etc.
175
- * It intercepts files containing `createContainer` calls, analyzes them,
176
- * and replaces the runtime configuration code with the generated dependency graph.
196
+ * Intercepts files containing `defineBuilderConfig` calls, analyzes the
197
+ * dependency graph, validates it, and replaces the configuration with
198
+ * generated factory code.
177
199
  *
178
200
  * @example
201
+ * ```typescript
179
202
  * // vite.config.ts
180
203
  * import { neoSyringePlugin } from '@djodjonx/neo-syringe/plugin';
181
204
  *
182
205
  * export default defineConfig({
183
- * plugins: [neoSyringePlugin.vite()],
206
+ * plugins: [neoSyringePlugin.vite()]
184
207
  * });
208
+ * ```
185
209
  */
186
- const neoSyringePlugin = createUnplugin((_options) => {
210
+ const neoSyringePlugin = createUnplugin(() => {
187
211
  return {
188
212
  name: "neo-syringe-plugin",
189
213
  transformInclude(id) {