@fluojs/di 2.0.0 → 3.0.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.
package/dist/errors.js CHANGED
@@ -85,12 +85,13 @@ export class ScopeMismatchError extends FluoCodeError {
85
85
  *
86
86
  * @remarks
87
87
  * The formatted message includes the full dependency path plus a first-party hint that points callers toward
88
- * extracting shared logic or using `forwardRef()` for intentional cycle deferral.
88
+ * extracting shared logic, introducing a mediator, or moving the interaction to a later boundary.
89
+ * `forwardRef()` only defers declaration-time token lookup and cannot resolve a true constructor cycle.
89
90
  */
90
91
  export class CircularDependencyError extends FluoCodeError {
91
92
  constructor(chain, detail) {
92
93
  const path = chain.map(token => formatTokenName(token)).join(' -> ');
93
- const hint = 'Break the cycle by extracting shared logic into a separate provider, or use forwardRef() to defer one side of the dependency.';
94
+ const hint = 'Break the constructor cycle by extracting shared logic into a separate provider, introducing a mediator, or moving the interaction to a later boundary. forwardRef() only defers declaration-time token lookup and cannot resolve a true constructor cycle.';
94
95
  super((detail ? `Circular dependency detected: ${path}. ${detail}` : `Circular dependency detected: ${path}`) + `\n Dependency chain: ${path}` + `\n Hint: ${hint}`, 'CIRCULAR_DEPENDENCY', {
95
96
  meta: {
96
97
  chain: chain.map(t => formatTokenName(t)),
@@ -1,3 +1,18 @@
1
+ import type { Token } from '@fluojs/core';
2
+ import { type MultiContributionResolverOwner } from './multi-contribution-registry.js';
3
+ /**
4
+ * Resolves one ordered multi-provider contribution through its owning container.
5
+ *
6
+ * This integration seam is for Fluo packages such as `@fluojs/runtime` and
7
+ * `@fluojs/testing`; application code must use `Container.resolve(...)`.
8
+ *
9
+ * @param container Container that owns the multi-provider registration.
10
+ * @param token Multi-provider token.
11
+ * @param contributionIndex Provider-order index for the contribution.
12
+ * @returns The canonical container-resolved contribution instance.
13
+ * @throws {ContainerResolutionError} When the container has no registered internal resolver.
14
+ */
15
+ export declare function resolveMultiContribution(container: MultiContributionResolverOwner, token: Token, contributionIndex: number): Promise<unknown>;
1
16
  export { validateProviderInputs } from './provider-normalization.js';
2
17
  export type { Provider } from './types.js';
3
18
  //# sourceMappingURL=internal.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"internal.d.ts","sourceRoot":"","sources":["../src/internal.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AACrE,YAAY,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"internal.d.ts","sourceRoot":"","sources":["../src/internal.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAG1C,OAAO,EACL,KAAK,8BAA8B,EAEpC,MAAM,kCAAkC,CAAC;AAE1C;;;;;;;;;;;GAWG;AACH,wBAAsB,wBAAwB,CAC5C,SAAS,EAAE,8BAA8B,EACzC,KAAK,EAAE,KAAK,EACZ,iBAAiB,EAAE,MAAM,GACxB,OAAO,CAAC,OAAO,CAAC,CAWlB;AAED,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AACrE,YAAY,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC"}
package/dist/internal.js CHANGED
@@ -1 +1,26 @@
1
+ import { ContainerResolutionError } from './errors.js';
2
+ import { multiContributionResolverFor } from './multi-contribution-registry.js';
3
+
4
+ /**
5
+ * Resolves one ordered multi-provider contribution through its owning container.
6
+ *
7
+ * This integration seam is for Fluo packages such as `@fluojs/runtime` and
8
+ * `@fluojs/testing`; application code must use `Container.resolve(...)`.
9
+ *
10
+ * @param container Container that owns the multi-provider registration.
11
+ * @param token Multi-provider token.
12
+ * @param contributionIndex Provider-order index for the contribution.
13
+ * @returns The canonical container-resolved contribution instance.
14
+ * @throws {ContainerResolutionError} When the container has no registered internal resolver.
15
+ */
16
+ export async function resolveMultiContribution(container, token, contributionIndex) {
17
+ const resolver = multiContributionResolverFor(container);
18
+ if (!resolver) {
19
+ throw new ContainerResolutionError('Container does not expose the internal multi-provider contribution resolver.', {
20
+ token,
21
+ hint: 'Create the container through the @fluojs/di Container constructor.'
22
+ });
23
+ }
24
+ return resolver(token, contributionIndex);
25
+ }
1
26
  export { validateProviderInputs } from './provider-normalization.js';
@@ -0,0 +1,26 @@
1
+ import type { Token } from '@fluojs/core';
2
+ type MultiContributionResolver = (token: Token, contributionIndex: number) => Promise<unknown>;
3
+ /**
4
+ * Object identity that owns a canonical multi-provider contribution resolver.
5
+ *
6
+ * @internal
7
+ */
8
+ export type MultiContributionResolverOwner = object;
9
+ /**
10
+ * Associates a container with its canonical multi-provider contribution resolver.
11
+ *
12
+ * @internal
13
+ * @param container Container that owns the resolver.
14
+ * @param resolver Container-bound resolver that preserves DI lifecycle invariants.
15
+ */
16
+ export declare function registerMultiContributionResolver(container: MultiContributionResolverOwner, resolver: MultiContributionResolver): void;
17
+ /**
18
+ * Retrieves the canonical multi-provider contribution resolver for a container.
19
+ *
20
+ * @internal
21
+ * @param container Container that owns the resolver.
22
+ * @returns The container-bound resolver, if the container registered one.
23
+ */
24
+ export declare function multiContributionResolverFor(container: MultiContributionResolverOwner): MultiContributionResolver | undefined;
25
+ export {};
26
+ //# sourceMappingURL=multi-contribution-registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"multi-contribution-registry.d.ts","sourceRoot":"","sources":["../src/multi-contribution-registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAE1C,KAAK,yBAAyB,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,iBAAiB,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;AAE/F;;;;GAIG;AACH,MAAM,MAAM,8BAA8B,GAAG,MAAM,CAAC;AAIpD;;;;;;GAMG;AACH,wBAAgB,iCAAiC,CAC/C,SAAS,EAAE,8BAA8B,EACzC,QAAQ,EAAE,yBAAyB,GAClC,IAAI,CAEN;AAED;;;;;;GAMG;AACH,wBAAgB,4BAA4B,CAC1C,SAAS,EAAE,8BAA8B,GACxC,yBAAyB,GAAG,SAAS,CAEvC"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Object identity that owns a canonical multi-provider contribution resolver.
3
+ *
4
+ * @internal
5
+ */
6
+
7
+ const multiContributionResolvers = new WeakMap();
8
+
9
+ /**
10
+ * Associates a container with its canonical multi-provider contribution resolver.
11
+ *
12
+ * @internal
13
+ * @param container Container that owns the resolver.
14
+ * @param resolver Container-bound resolver that preserves DI lifecycle invariants.
15
+ */
16
+ export function registerMultiContributionResolver(container, resolver) {
17
+ multiContributionResolvers.set(container, resolver);
18
+ }
19
+
20
+ /**
21
+ * Retrieves the canonical multi-provider contribution resolver for a container.
22
+ *
23
+ * @internal
24
+ * @param container Container that owns the resolver.
25
+ * @returns The container-bound resolver, if the container registered one.
26
+ */
27
+ export function multiContributionResolverFor(container) {
28
+ return multiContributionResolvers.get(container);
29
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"provider-normalization.d.ts","sourceRoot":"","sources":["../src/provider-normalization.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAA2B,kBAAkB,EAAiB,QAAQ,EAAE,MAAM,YAAY,CAAC;AA6JvG;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,GAAG,kBAAkB,CAiFxE;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,QAAQ,EAAE,GAAG,QAAQ,EAAE,CAMxE"}
1
+ {"version":3,"file":"provider-normalization.d.ts","sourceRoot":"","sources":["../src/provider-normalization.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAA2B,kBAAkB,EAAiB,QAAQ,EAAE,MAAM,YAAY,CAAC;AA6JvG;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,GAAG,kBAAkB,CAuFxE;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,QAAQ,EAAE,GAAG,QAAQ,EAAE,CAMxE"}
@@ -37,8 +37,8 @@ function isScope(value) {
37
37
  return value === 'singleton' || value === 'request' || value === 'transient';
38
38
  }
39
39
  function assertProviderToken(provider) {
40
- if (!('provide' in provider) || provider.provide == null) {
41
- throw new InvalidProviderError('Provider object must include a non-null provide token.');
40
+ if (!('provide' in provider) || !isToken(provider.provide)) {
41
+ throw new InvalidProviderError('Provider object must include a string, symbol, or constructable class provide token.');
42
42
  }
43
43
  }
44
44
  function assertProviderStrategy(provider) {
@@ -138,6 +138,11 @@ export function normalizeProvider(provider) {
138
138
  assertObjectProvider(objectProvider);
139
139
  const explicitScope = normalizeProviderScope(objectProvider.scope, objectProvider.provide);
140
140
  if ('useValue' in objectProvider) {
141
+ if (Object.hasOwn(objectProvider, 'inject')) {
142
+ throw new InvalidProviderError('Value providers must not declare inject dependencies.', {
143
+ token: objectProvider.provide
144
+ });
145
+ }
141
146
  return freezeNormalizedProvider({
142
147
  inject: [],
143
148
  multi: objectProvider.multi,
@@ -180,8 +185,8 @@ export function normalizeProvider(provider) {
180
185
  });
181
186
  }
182
187
  if ('useExisting' in objectProvider) {
183
- if (objectProvider.useExisting == null) {
184
- throw new InvalidProviderError('Alias provider useExisting must be a non-null token.', {
188
+ if (!isToken(objectProvider.useExisting)) {
189
+ throw new InvalidProviderError('Alias provider useExisting must be a string, symbol, or constructable class token.', {
185
190
  token: objectProvider.provide
186
191
  });
187
192
  }
package/package.json CHANGED
@@ -9,7 +9,7 @@
9
9
  "container",
10
10
  "provider"
11
11
  ],
12
- "version": "2.0.0",
12
+ "version": "3.0.0",
13
13
  "private": false,
14
14
  "license": "MIT",
15
15
  "repository": {
@@ -18,7 +18,7 @@
18
18
  "directory": "packages/di"
19
19
  },
20
20
  "engines": {
21
- "node": ">=20.0.0"
21
+ "node": ">=24.0.0 <27"
22
22
  },
23
23
  "publishConfig": {
24
24
  "access": "public"
@@ -40,15 +40,15 @@
40
40
  "dist"
41
41
  ],
42
42
  "dependencies": {
43
- "@fluojs/core": "^1.1.0"
43
+ "@fluojs/core": "^2.0.0"
44
44
  },
45
45
  "devDependencies": {
46
- "vitest": "^3.2.4"
46
+ "vitest": "^4.1.11"
47
47
  },
48
48
  "scripts": {
49
49
  "prebuild": "node ../../tooling/scripts/clean-dist.mjs",
50
50
  "build": "pnpm exec babel src --extensions .ts --ignore 'src/**/*.test.ts' --out-dir dist --config-file ../../tooling/babel/babel.config.cjs && pnpm exec tsc -p tsconfig.build.json",
51
- "typecheck": "pnpm exec tsc -p tsconfig.json --noEmit",
51
+ "typecheck": "pnpm exec tsc -p tsconfig.typecheck.json --noEmit",
52
52
  "test": "pnpm exec vitest run -c vitest.config.ts",
53
53
  "test:watch": "pnpm exec vitest -c vitest.config.ts"
54
54
  }