ripple-di 1.0.1 → 1.1.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/README.md CHANGED
@@ -61,6 +61,7 @@ The package is published as ESM.
61
61
  | Define an input, derived value, or service | `defineDependency`
62
62
  | Supply the application's values at startup | `install` with `provide` or `provideFactory`
63
63
  | Replace values for one callback | `withOverrides` with `provide` or `provideFactory`
64
+ | Run after the current scope can close | `withDetachedOverrides`
64
65
  | Keep one scope open across several operations | `createScope`
65
66
  | Shut everything down | `dispose`
66
67
 
@@ -172,6 +173,32 @@ Use such a value inside the callback, and use `createScope` when it has to outli
172
173
  Wrapping the value in an object prevents it from being awaited, but the temporary scope still closes before the caller receives it.
173
174
  Anything that scope owned has already been cleaned up.
174
175
 
176
+ ### Run outside the current scope
177
+
178
+ `withDetachedOverrides` runs work in a temporary scope that does not inherit the current ambient scope.
179
+ Use it when work must continue after a request or another scoped operation can finish.
180
+ Capture every request value the work needs and provide it explicitly.
181
+
182
+ ```ts
183
+ import { provide, withDetachedOverrides } from "ripple-di"
184
+
185
+ const tenant = useTenant()
186
+
187
+ const backgroundTask = withDetachedOverrides(
188
+ provide(useTenant, tenant),
189
+ () => updateTenantSearchIndex(),
190
+ )
191
+
192
+ trackBackgroundTask(backgroundTask)
193
+ ```
194
+
195
+ The detached scope inherits from the active installation, or from the runtime root when no installation is active.
196
+ It remains part of that lifecycle: closing the installation or calling `dispose()` force-closes it.
197
+ When no installation is active, an unfinished detached scope also prevents `install()` until its callback and cleanup finish.
198
+
199
+ The returned promise settles after the callback and cleanup finish.
200
+ Keep or observe it so callback and cleanup failures are handled.
201
+
175
202
  ## Where a value belongs
176
203
 
177
204
  The same tracked dependency calls that decide when a factory result must be rebuilt also decide which lifecycle owns it and invokes its disposer.
@@ -581,6 +608,7 @@ Every runtime has the same methods, and each has a module-level counterpart that
581
608
  - `resolve`
582
609
  - `createScope`
583
610
  - `withOverrides`
611
+ - `withDetachedOverrides`
584
612
  - `createValueOverride`
585
613
  - `createOverrideRunner`
586
614
  - `dispose`
package/dist/index.d.mts CHANGED
@@ -189,6 +189,13 @@ interface Runtime {
189
189
  * for the callback afterward.
190
190
  */
191
191
  withOverrides<TCallbackResult>(provisions: ProvisionInput, callback: (scope: Scope) => TCallbackResult): Promise<Awaited<TCallbackResult>>;
192
+ /**
193
+ * Runs a callback in a temporary child of the runtime's current base scope.
194
+ *
195
+ * The callback does not inherit the current ambient scope, but its scope
196
+ * remains owned by the active installation or runtime root.
197
+ */
198
+ withDetachedOverrides<TCallbackResult>(provisions: ProvisionInput, callback: (scope: Scope) => TCallbackResult): Promise<Awaited<TCallbackResult>>;
192
199
  /**
193
200
  * Prepares overrides that are applied again to each call of the returned
194
201
  * runner.
@@ -240,6 +247,13 @@ declare function createScope(provisions?: ProvisionInput): Scope;
240
247
  * callbacks, and are cleaned up when the callback finishes.
241
248
  */
242
249
  declare function withOverrides<TCallbackResult>(provisions: ProvisionInput, callback: (scope: Scope) => TCallbackResult): Promise<Awaited<TCallbackResult>>;
250
+ /**
251
+ * Runs a callback with overrides outside the current ambient scope.
252
+ *
253
+ * The temporary scope inherits from the active installation or runtime root
254
+ * and remains part of that lifecycle until the callback finishes.
255
+ */
256
+ declare function withDetachedOverrides<TCallbackResult>(provisions: ProvisionInput, callback: (scope: Scope) => TCallbackResult): Promise<Awaited<TCallbackResult>>;
243
257
  /**
244
258
  * Prepares dependency overrides that are applied again to each call of the
245
259
  * returned runner.
@@ -376,4 +390,4 @@ declare class LeakedChildScopeError extends RippleError {
376
390
  constructor(scopeName: string, leakedChildCount: number);
377
391
  }
378
392
  //#endregion
379
- export { type AsValue, AsyncFactoryError, CrossRuntimeDependencyError, CrossScopeResolutionError, type Dependency, DependencyCycleError, type DependencyOptions, type Disposer, DisposerContextError, DuplicateProviderError, FactoryError, type FactoryResult, FactoryScopeOperationError, Installation, InstallationConflictError, LeakedChildScopeError, MissingProviderError, type OverrideRunner, OwnedProvisionReuseError, type ProvideOptions, type Provision, type ProvisionFactory, type ProvisionInput, RippleError, Runtime, RuntimeOptions, type Scope, ScopeClosedError, type ScopeState, type ValueOverride, asValue, createOverrideRunner, createRuntime, createScope, createValueOverride, defineDependency, dispose, install, provide, provideFactory, resolve, withOverrides };
393
+ export { type AsValue, AsyncFactoryError, CrossRuntimeDependencyError, CrossScopeResolutionError, type Dependency, DependencyCycleError, type DependencyOptions, type Disposer, DisposerContextError, DuplicateProviderError, FactoryError, type FactoryResult, FactoryScopeOperationError, Installation, InstallationConflictError, LeakedChildScopeError, MissingProviderError, type OverrideRunner, OwnedProvisionReuseError, type ProvideOptions, type Provision, type ProvisionFactory, type ProvisionInput, RippleError, Runtime, RuntimeOptions, type Scope, ScopeClosedError, type ScopeState, type ValueOverride, asValue, createOverrideRunner, createRuntime, createScope, createValueOverride, defineDependency, dispose, install, provide, provideFactory, resolve, withDetachedOverrides, withOverrides };
package/dist/index.mjs CHANGED
@@ -817,6 +817,10 @@ var RuntimeImpl = class {
817
817
  this.assertScopeManagementAllowed("Runtime.withOverrides");
818
818
  return withChildScope(this.currentAmbientScope(), provisions, callback);
819
819
  }
820
+ withDetachedOverrides(provisions, callback) {
821
+ this.assertScopeManagementAllowed("Runtime.withDetachedOverrides");
822
+ return withChildScope(this.baseScope(), provisions, callback);
823
+ }
820
824
  createOverrideRunner(factory) {
821
825
  return createOverrideRunnerFor(this, factory);
822
826
  }
@@ -956,6 +960,15 @@ function withOverrides(provisions, callback) {
956
960
  return globalRuntime.withOverrides(provisions, callback);
957
961
  }
958
962
  /**
963
+ * Runs a callback with overrides outside the current ambient scope.
964
+ *
965
+ * The temporary scope inherits from the active installation or runtime root
966
+ * and remains part of that lifecycle until the callback finishes.
967
+ */
968
+ function withDetachedOverrides(provisions, callback) {
969
+ return globalRuntime.withDetachedOverrides(provisions, callback);
970
+ }
971
+ /**
959
972
  * Prepares dependency overrides that are applied again to each call of the
960
973
  * returned runner.
961
974
  *
@@ -980,4 +993,4 @@ function dispose() {
980
993
  return globalRuntime.dispose();
981
994
  }
982
995
  //#endregion
983
- export { AsyncFactoryError, CrossRuntimeDependencyError, CrossScopeResolutionError, DependencyCycleError, DisposerContextError, DuplicateProviderError, FactoryError, FactoryScopeOperationError, InstallationConflictError, LeakedChildScopeError, MissingProviderError, OwnedProvisionReuseError, RippleError, ScopeClosedError, asValue, createOverrideRunner, createRuntime, createScope, createValueOverride, defineDependency, dispose, install, provide, provideFactory, resolve, withOverrides };
996
+ export { AsyncFactoryError, CrossRuntimeDependencyError, CrossScopeResolutionError, DependencyCycleError, DisposerContextError, DuplicateProviderError, FactoryError, FactoryScopeOperationError, InstallationConflictError, LeakedChildScopeError, MissingProviderError, OwnedProvisionReuseError, RippleError, ScopeClosedError, asValue, createOverrideRunner, createRuntime, createScope, createValueOverride, defineDependency, dispose, install, provide, provideFactory, resolve, withDetachedOverrides, withOverrides };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "ripple-di",
3
3
  "type": "module",
4
- "version": "1.0.1",
4
+ "version": "1.1.0",
5
5
  "description": "Scoped dependency injection for TypeScript with automatic dependency tracking, lifecycle-aware cleanup, and no container lookups in application code.",
6
6
  "keywords": [
7
7
  "dependency-injection",