dalila 1.3.1 → 1.3.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.
@@ -1,72 +1,82 @@
1
1
  /**
2
- * Auto-scoping Context API
2
+ * Auto-scoping Context API.
3
3
  *
4
- * Simplifica uso de provide/inject removendo necessidade de withScope explícito
5
- * quando usando patterns comuns.
4
+ * Goal:
5
+ * - Remove the need for explicit `withScope()` in common app code.
6
+ *
7
+ * Auto-scope rules:
8
+ * - `provide()` outside a scope creates a global root scope (warns once in dev).
9
+ * - `inject()` outside a scope never creates global state; it only reads an existing global root.
10
+ *
11
+ * Rationale:
12
+ * - Apps often want "global-ish" DI without Provider pyramids.
13
+ * - Still keep things lifecycle-safe: the global scope can be disposed on page unload.
6
14
  */
7
15
  import { type Scope } from '../core/scope.js';
8
- import { createContext, type ContextToken } from './context.js';
16
+ import { createContext, type ContextToken, type TryInjectResult } from './context.js';
17
+ export type AutoScopePolicy = "warn" | "throw" | "silent";
18
+ export declare function setAutoScopePolicy(policy: AutoScopePolicy): void;
9
19
  /**
10
- * Provide with auto-scope
11
- *
12
- * Se chamado fora de scope, cria um global scope automaticamente
13
- *
14
- * @example
15
- * ```typescript
16
- * // ✅ ANTES (verboso)
17
- * const scope = createScope();
18
- * withScope(scope, () => {
19
- * provide(Theme, 'dark');
20
- * });
20
+ * Provide with auto-scope.
21
21
  *
22
- * // ✅ DEPOIS (simples)
23
- * provide(Theme, 'dark'); // auto-scope!
24
- * ```
22
+ * Semantics:
23
+ * - Inside a scope: behaves like the raw `provide()`.
24
+ * - Outside a scope: creates/uses the global root scope (warns once in dev).
25
25
  */
26
26
  export declare function provide<T>(token: ContextToken<T>, value: T): void;
27
27
  /**
28
- * Inject with auto-scope
28
+ * Provide explicitly in the global root scope.
29
29
  *
30
- * Se chamado fora de scope, tenta buscar no global scope
31
- * Se não encontrar, lança erro DESCRITIVO
30
+ * Semantics:
31
+ * - Always uses the detached global scope (creates if needed).
32
+ * - Never warns.
33
+ */
34
+ export declare function provideGlobal<T>(token: ContextToken<T>, value: T): void;
35
+ /**
36
+ * Inject with auto-scope.
37
+ *
38
+ * Semantics:
39
+ * - Inside a scope: behaves like raw `inject()`, but throws a more descriptive error.
40
+ * - Outside a scope:
41
+ * - If a global scope exists: reads from it (safe-by-default).
42
+ * - If no global scope exists: throws with guidance (does NOT create global state).
32
43
  */
33
44
  export declare function inject<T>(token: ContextToken<T>): T;
34
45
  /**
35
- * Shorthand for createScope + withScope
46
+ * Try to inject a context value with auto-scope.
36
47
  *
37
- * @example
38
- * ```typescript
39
- * // ANTES (verboso)
40
- * const appScope = createScope();
41
- * withScope(appScope, () => {
42
- * provide(Theme, 'dark');
43
- * createApp();
44
- * });
48
+ * Semantics:
49
+ * - Returns { found: true, value } when the token is found.
50
+ * - Returns { found: false, value: undefined } when not found.
51
+ * - Works both inside and outside scopes (reads global if exists).
52
+ */
53
+ export declare function tryInject<T>(token: ContextToken<T>): TryInjectResult<T>;
54
+ /**
55
+ * Inject explicitly from the global root scope.
56
+ *
57
+ * Semantics:
58
+ * - Reads only the global root scope.
59
+ * - Throws if no global scope exists yet.
60
+ */
61
+ export declare function injectGlobal<T>(token: ContextToken<T>): T;
62
+ /**
63
+ * Convenience helper: create a scope, run `fn` inside it, and return `{ result, dispose }`.
45
64
  *
46
- * // ✅ DEPOIS (conciso)
47
- * scope(() => {
48
- * provide(Theme, 'dark');
49
- * createApp();
50
- * });
51
- * ```
65
+ * Semantics:
66
+ * - If `fn` throws, the scope is disposed and the error is rethrown.
67
+ * - Caller owns disposal.
52
68
  */
53
69
  export declare function scope<T>(fn: () => T): {
54
70
  result: T;
55
71
  dispose: () => void;
56
72
  };
57
73
  /**
58
- * Create a scoped provider component helper
74
+ * Provider helper that bundles:
75
+ * - a dedicated provider scope
76
+ * - a value created by `setup()`
77
+ * - registration via `provide()`
59
78
  *
60
- * @example
61
- * ```typescript
62
- * const ThemeProvider = createProvider(ThemeContext, () => {
63
- * const theme = signal('dark');
64
- * return { theme };
65
- * });
66
- *
67
- * const { value, dispose } = ThemeProvider.create();
68
- * // use value.theme()
69
- * ```
79
+ * Useful for "feature modules" that want to expose a typed dependency with explicit lifetime.
70
80
  */
71
81
  export declare function createProvider<T>(token: ContextToken<T>, setup: () => T): {
72
82
  create: () => {
@@ -76,11 +86,17 @@ export declare function createProvider<T>(token: ContextToken<T>, setup: () => T
76
86
  use: () => T;
77
87
  };
78
88
  /**
79
- * Get global scope (for debugging/advanced use)
89
+ * Returns the global root scope (if it exists).
90
+ * Intended for debugging/advanced usage only.
80
91
  */
81
92
  export declare function getGlobalScope(): Scope | null;
82
93
  /**
83
- * Reset global scope (for testing)
94
+ * Returns true if a global root scope exists.
95
+ */
96
+ export declare function hasGlobalScope(): boolean;
97
+ /**
98
+ * Resets the global root scope.
99
+ * Intended for tests to ensure isolation between runs.
84
100
  */
85
101
  export declare function resetGlobalScope(): void;
86
102
  export { createContext };
@@ -1,23 +1,51 @@
1
1
  /**
2
- * Auto-scoping Context API
2
+ * Auto-scoping Context API.
3
3
  *
4
- * Simplifica uso de provide/inject removendo necessidade de withScope explícito
5
- * quando usando patterns comuns.
4
+ * Goal:
5
+ * - Remove the need for explicit `withScope()` in common app code.
6
+ *
7
+ * Auto-scope rules:
8
+ * - `provide()` outside a scope creates a global root scope (warns once in dev).
9
+ * - `inject()` outside a scope never creates global state; it only reads an existing global root.
10
+ *
11
+ * Rationale:
12
+ * - Apps often want "global-ish" DI without Provider pyramids.
13
+ * - Still keep things lifecycle-safe: the global scope can be disposed on page unload.
6
14
  */
7
- import { getCurrentScope, createScope, withScope } from '../core/scope.js';
15
+ import { getCurrentScope, createScope, isScopeDisposed, withScope } from '../core/scope.js';
8
16
  import { isInDevMode } from '../core/dev.js';
9
- import { createContext, provide as rawProvide, inject as rawInject } from './context.js';
17
+ import { createContext, provide as rawProvide, tryInject as rawTryInject, debugListAvailableContexts, } from './context.js';
18
+ /**
19
+ * Symbol key for storing the global root scope.
20
+ * Using Symbol.for() allows multiple instances of Dalila to share the same global scope,
21
+ * preventing conflicts when multiple libs use Dalila in the same app.
22
+ */
23
+ const DALILA_GLOBAL_SCOPE_KEY = Symbol.for('dalila:global-scope');
24
+ /**
25
+ * Global storage for the root scope using the symbol key.
26
+ * This allows sharing across multiple Dalila instances.
27
+ */
28
+ const globalStorage = globalThis;
10
29
  /**
11
- * Global root scope (lazy initialized)
12
- * Usado quando provide/inject são chamados sem scope
30
+ * Global root scope (lazy initialized).
13
31
  *
14
- * Auto-scope rules:
15
- * - provide() outside a scope creates a global root scope (warns once in dev).
16
- * - inject() outside a scope never creates global state; it only reads an existing global root.
32
+ * Used only when `provide()` is called outside a scope.
33
+ * `inject()` is safe-by-default and never creates this scope.
17
34
  */
18
- let globalRootScope = null;
35
+ function getGlobalRootScope() {
36
+ return globalStorage[DALILA_GLOBAL_SCOPE_KEY] ?? null;
37
+ }
38
+ function setGlobalRootScope(scope) {
39
+ globalStorage[DALILA_GLOBAL_SCOPE_KEY] = scope;
40
+ }
41
+ /** Dev warning guard so we only warn once per page lifecycle. */
19
42
  let warnedGlobalProvide = false;
43
+ /** Browser-only unload handler for global scope cleanup. */
20
44
  let beforeUnloadHandler = null;
45
+ let autoScopePolicy = "throw";
46
+ export function setAutoScopePolicy(policy) {
47
+ autoScopePolicy = policy;
48
+ }
21
49
  function warnGlobalProvideOnce() {
22
50
  if (!isInDevMode())
23
51
  return;
@@ -25,7 +53,8 @@ function warnGlobalProvideOnce() {
25
53
  return;
26
54
  warnedGlobalProvide = true;
27
55
  console.warn('[Dalila] provide() called outside a scope. Using a global root scope (auto-scope). ' +
28
- 'Prefer scope(() => { ... }) for lifecycle-safe cleanup.');
56
+ 'Prefer scope(() => { ... }) or provideGlobal() for explicit globals. ' +
57
+ 'You can also setAutoScopePolicy("throw") to prevent accidental globals.');
29
58
  }
30
59
  function detachBeforeUnloadListener() {
31
60
  if (typeof window === 'undefined')
@@ -37,21 +66,36 @@ function detachBeforeUnloadListener() {
37
66
  window.removeEventListener('beforeunload', beforeUnloadHandler);
38
67
  beforeUnloadHandler = null;
39
68
  }
40
- // Note: only provide() creates the global scope; inject() never creates it (safe-by-default).
69
+ /**
70
+ * Returns the global root scope, creating it if needed.
71
+ *
72
+ * Notes:
73
+ * - Only `provide()` is allowed to create the global scope.
74
+ * - On browsers, we dispose the global scope on `beforeunload` to release resources.
75
+ * - We intentionally swallow errors during unload to avoid noisy teardown failures.
76
+ */
77
+ // Note: provideGlobal() can also create this scope explicitly.
41
78
  function getOrCreateGlobalScope() {
79
+ let globalRootScope = getGlobalRootScope();
80
+ if (globalRootScope && isScopeDisposed(globalRootScope)) {
81
+ detachBeforeUnloadListener();
82
+ setGlobalRootScope(null);
83
+ warnedGlobalProvide = false;
84
+ globalRootScope = null;
85
+ }
42
86
  if (!globalRootScope) {
43
- globalRootScope = createScope();
87
+ globalRootScope = createScope(null);
88
+ setGlobalRootScope(globalRootScope);
44
89
  if (typeof window !== 'undefined' && window.addEventListener && !beforeUnloadHandler) {
45
- // Cleanup on page unload (browser only)
46
90
  beforeUnloadHandler = () => {
47
91
  detachBeforeUnloadListener();
48
92
  try {
49
- globalRootScope?.dispose();
93
+ getGlobalRootScope()?.dispose();
50
94
  }
51
95
  catch {
52
- // do not throw during unload
96
+ // Do not throw during unload.
53
97
  }
54
- globalRootScope = null;
98
+ setGlobalRootScope(null);
55
99
  warnedGlobalProvide = false;
56
100
  };
57
101
  window.addEventListener('beforeunload', beforeUnloadHandler);
@@ -60,31 +104,25 @@ function getOrCreateGlobalScope() {
60
104
  return globalRootScope;
61
105
  }
62
106
  /**
63
- * Provide with auto-scope
64
- *
65
- * Se chamado fora de scope, cria um global scope automaticamente
107
+ * Provide with auto-scope.
66
108
  *
67
- * @example
68
- * ```typescript
69
- * // ANTES (verboso)
70
- * const scope = createScope();
71
- * withScope(scope, () => {
72
- * provide(Theme, 'dark');
73
- * });
74
- *
75
- * // ✅ DEPOIS (simples)
76
- * provide(Theme, 'dark'); // auto-scope!
77
- * ```
109
+ * Semantics:
110
+ * - Inside a scope: behaves like the raw `provide()`.
111
+ * - Outside a scope: creates/uses the global root scope (warns once in dev).
78
112
  */
79
113
  export function provide(token, value) {
80
114
  const currentScope = getCurrentScope();
81
115
  if (currentScope) {
82
- // Inside a scope - use normal provide
83
116
  rawProvide(token, value);
84
117
  }
85
118
  else {
86
- // Outside scope - use global scope
87
- warnGlobalProvideOnce();
119
+ if (autoScopePolicy === "throw") {
120
+ throw new Error("[Dalila] provide() called outside a scope. " +
121
+ "Use scope(() => provide(...)) or provideGlobal() instead.");
122
+ }
123
+ if (autoScopePolicy === "warn") {
124
+ warnGlobalProvideOnce();
125
+ }
88
126
  const globalScope = getOrCreateGlobalScope();
89
127
  withScope(globalScope, () => {
90
128
  rawProvide(token, value);
@@ -92,55 +130,121 @@ export function provide(token, value) {
92
130
  }
93
131
  }
94
132
  /**
95
- * Inject with auto-scope
133
+ * Provide explicitly in the global root scope.
96
134
  *
97
- * Se chamado fora de scope, tenta buscar no global scope
98
- * Se não encontrar, lança erro DESCRITIVO
135
+ * Semantics:
136
+ * - Always uses the detached global scope (creates if needed).
137
+ * - Never warns.
138
+ */
139
+ export function provideGlobal(token, value) {
140
+ const globalScope = getOrCreateGlobalScope();
141
+ withScope(globalScope, () => {
142
+ rawProvide(token, value);
143
+ });
144
+ }
145
+ /**
146
+ * Inject with auto-scope.
147
+ *
148
+ * Semantics:
149
+ * - Inside a scope: behaves like raw `inject()`, but throws a more descriptive error.
150
+ * - Outside a scope:
151
+ * - If a global scope exists: reads from it (safe-by-default).
152
+ * - If no global scope exists: throws with guidance (does NOT create global state).
99
153
  */
100
154
  export function inject(token) {
101
- const currentScope = getCurrentScope();
102
- if (currentScope) {
103
- // Inside a scope - use normal inject
104
- try {
105
- return rawInject(token);
106
- }
107
- catch {
108
- // Enhanced error message
109
- throw new Error(createContextNotFoundError(token));
110
- }
155
+ if (getCurrentScope()) {
156
+ const result = rawTryInject(token);
157
+ if (result.found)
158
+ return result.value;
159
+ throw new Error(createContextNotFoundError(token));
160
+ }
161
+ let globalRootScope = getGlobalRootScope();
162
+ if (globalRootScope && isScopeDisposed(globalRootScope)) {
163
+ setGlobalRootScope(null);
164
+ warnedGlobalProvide = false;
165
+ globalRootScope = null;
111
166
  }
112
- // Outside scope - try global scope
113
167
  if (!globalRootScope) {
168
+ // Check for default value before throwing
169
+ if (token.defaultValue !== undefined) {
170
+ return token.defaultValue;
171
+ }
114
172
  throw createInjectOutsideScopeError(token, 'No global scope exists yet.');
115
173
  }
116
- try {
117
- return withScope(globalRootScope, () => rawInject(token));
118
- }
119
- catch {
174
+ return withScope(globalRootScope, () => {
175
+ const result = rawTryInject(token);
176
+ if (result.found)
177
+ return result.value;
120
178
  throw createInjectOutsideScopeError(token);
179
+ });
180
+ }
181
+ /**
182
+ * Try to inject a context value with auto-scope.
183
+ *
184
+ * Semantics:
185
+ * - Returns { found: true, value } when the token is found.
186
+ * - Returns { found: false, value: undefined } when not found.
187
+ * - Works both inside and outside scopes (reads global if exists).
188
+ */
189
+ export function tryInject(token) {
190
+ if (getCurrentScope()) {
191
+ return rawTryInject(token);
121
192
  }
193
+ let globalRootScope = getGlobalRootScope();
194
+ if (globalRootScope && isScopeDisposed(globalRootScope)) {
195
+ setGlobalRootScope(null);
196
+ warnedGlobalProvide = false;
197
+ globalRootScope = null;
198
+ }
199
+ if (!globalRootScope) {
200
+ // Check for default value
201
+ if (token.defaultValue !== undefined) {
202
+ return { found: true, value: token.defaultValue };
203
+ }
204
+ return { found: false, value: undefined };
205
+ }
206
+ return withScope(globalRootScope, () => rawTryInject(token));
122
207
  }
123
208
  /**
124
- * Shorthand for createScope + withScope
209
+ * Inject explicitly from the global root scope.
125
210
  *
126
- * @example
127
- * ```typescript
128
- * // ANTES (verboso)
129
- * const appScope = createScope();
130
- * withScope(appScope, () => {
131
- * provide(Theme, 'dark');
132
- * createApp();
133
- * });
211
+ * Semantics:
212
+ * - Reads only the global root scope.
213
+ * - Throws if no global scope exists yet.
214
+ */
215
+ export function injectGlobal(token) {
216
+ let globalRootScope = getGlobalRootScope();
217
+ if (globalRootScope && isScopeDisposed(globalRootScope)) {
218
+ setGlobalRootScope(null);
219
+ warnedGlobalProvide = false;
220
+ globalRootScope = null;
221
+ }
222
+ if (!globalRootScope) {
223
+ // Check for default value before throwing
224
+ if (token.defaultValue !== undefined) {
225
+ return token.defaultValue;
226
+ }
227
+ throw new Error("[Dalila] injectGlobal() called but no global scope exists yet. " +
228
+ "Use provideGlobal() first.");
229
+ }
230
+ return withScope(globalRootScope, () => {
231
+ const result = rawTryInject(token);
232
+ if (result.found)
233
+ return result.value;
234
+ throw new Error("[Dalila] injectGlobal() token not found in global scope. " +
235
+ "Provide it via provideGlobal() or call inject() inside the correct scope.");
236
+ });
237
+ }
238
+ /**
239
+ * Convenience helper: create a scope, run `fn` inside it, and return `{ result, dispose }`.
134
240
  *
135
- * // ✅ DEPOIS (conciso)
136
- * scope(() => {
137
- * provide(Theme, 'dark');
138
- * createApp();
139
- * });
140
- * ```
241
+ * Semantics:
242
+ * - If `fn` throws, the scope is disposed and the error is rethrown.
243
+ * - Caller owns disposal.
141
244
  */
142
245
  export function scope(fn) {
143
- const newScope = createScope();
246
+ const parent = getCurrentScope();
247
+ const newScope = createScope(parent ?? null);
144
248
  try {
145
249
  const result = withScope(newScope, fn);
146
250
  return {
@@ -154,23 +258,18 @@ export function scope(fn) {
154
258
  }
155
259
  }
156
260
  /**
157
- * Create a scoped provider component helper
158
- *
159
- * @example
160
- * ```typescript
161
- * const ThemeProvider = createProvider(ThemeContext, () => {
162
- * const theme = signal('dark');
163
- * return { theme };
164
- * });
261
+ * Provider helper that bundles:
262
+ * - a dedicated provider scope
263
+ * - a value created by `setup()`
264
+ * - registration via `provide()`
165
265
  *
166
- * const { value, dispose } = ThemeProvider.create();
167
- * // use value.theme()
168
- * ```
266
+ * Useful for "feature modules" that want to expose a typed dependency with explicit lifetime.
169
267
  */
170
268
  export function createProvider(token, setup) {
171
269
  return {
172
270
  create() {
173
- const providerScope = createScope();
271
+ const parent = getCurrentScope();
272
+ const providerScope = createScope(parent ?? null);
174
273
  const value = withScope(providerScope, () => {
175
274
  const result = setup();
176
275
  provide(token, result);
@@ -187,7 +286,7 @@ export function createProvider(token, setup) {
187
286
  };
188
287
  }
189
288
  /**
190
- * Create enhanced error message for context not found
289
+ * Builds a descriptive error message for missing contexts inside a scope hierarchy.
191
290
  */
192
291
  function createContextNotFoundError(token) {
193
292
  const contextName = token.name || 'unnamed';
@@ -198,8 +297,19 @@ function createContextNotFoundError(token) {
198
297
  message += ` 3. The scope where provide() was called has already been disposed\n\n`;
199
298
  message += `How to fix:\n`;
200
299
  message += ` • Make sure provide() is called in a parent scope\n`;
201
- message += ` • Use scope(() => { provide(...); inject(...); }) to ensure same hierarchy\n`;
300
+ message += ` • Use scope(() => { provide(...); inject(...); }) to ensure the same hierarchy\n`;
202
301
  message += ` • Check that the scope hasn't been disposed\n\n`;
302
+ if (isInDevMode()) {
303
+ const levels = debugListAvailableContexts(8);
304
+ if (levels.length > 0) {
305
+ message += `Available contexts by depth:\n`;
306
+ for (const level of levels) {
307
+ const names = level.tokens.map((t) => t.name).join(', ') || '(none)';
308
+ message += ` depth ${level.depth}: ${names}\n`;
309
+ }
310
+ message += `\n`;
311
+ }
312
+ }
203
313
  message += `Learn more: https://github.com/evertondsvieira/dalila/blob/main/docs/context.md`;
204
314
  return message;
205
315
  }
@@ -213,21 +323,31 @@ function createInjectOutsideScopeError(token, extra) {
213
323
  `Learn more: https://github.com/evertondsvieira/dalila/blob/main/docs/context.md`);
214
324
  }
215
325
  /**
216
- * Get global scope (for debugging/advanced use)
326
+ * Returns the global root scope (if it exists).
327
+ * Intended for debugging/advanced usage only.
217
328
  */
218
329
  export function getGlobalScope() {
219
- return globalRootScope;
330
+ return getGlobalRootScope();
220
331
  }
221
332
  /**
222
- * Reset global scope (for testing)
333
+ * Returns true if a global root scope exists.
334
+ */
335
+ export function hasGlobalScope() {
336
+ return getGlobalRootScope() != null;
337
+ }
338
+ /**
339
+ * Resets the global root scope.
340
+ * Intended for tests to ensure isolation between runs.
223
341
  */
224
342
  export function resetGlobalScope() {
225
343
  detachBeforeUnloadListener();
344
+ const globalRootScope = getGlobalRootScope();
226
345
  if (globalRootScope) {
227
346
  globalRootScope.dispose();
228
- globalRootScope = null;
347
+ setGlobalRootScope(null);
229
348
  }
230
349
  warnedGlobalProvide = false;
350
+ autoScopePolicy = "throw";
231
351
  }
232
- // Re-export createContext for convenience
352
+ // Re-export createContext for convenience.
233
353
  export { createContext };
@@ -1,7 +1,111 @@
1
+ import { type Scope } from "../core/scope.js";
2
+ /**
3
+ * Context (Dependency Injection) — scope-based and hierarchical.
4
+ *
5
+ * Mental model:
6
+ * - A context value lives in the current Scope.
7
+ * - Child scopes can read values provided by ancestors (via Scope.parent chain).
8
+ * - Values are cleaned up automatically when the owning scope is disposed.
9
+ *
10
+ * Constraints (raw API):
11
+ * - `provide()` MUST be called inside a scope.
12
+ * - `inject()` MUST be called inside a scope.
13
+ *
14
+ * (Auto-scope behavior belongs in `dalila/context` wrapper — not here.)
15
+ */
16
+ /**
17
+ * Branded type marker for compile-time type safety.
18
+ * Using a unique symbol ensures tokens are not interchangeable even if they have the same T.
19
+ */
20
+ declare const ContextBrand: unique symbol;
21
+ /**
22
+ * ContextToken:
23
+ * - `name` is optional (used for debugging/errors)
24
+ * - `defaultValue` is optional (returned when no provider is found)
25
+ * - Branded with a unique symbol for type safety
26
+ */
1
27
  export interface ContextToken<T> {
2
28
  readonly name?: string;
3
- readonly _brand: T;
29
+ readonly defaultValue?: T;
30
+ readonly [ContextBrand]: T;
4
31
  }
5
- export declare function createContext<T>(name?: string): ContextToken<T>;
32
+ /**
33
+ * Create a new context token.
34
+ *
35
+ * Notes:
36
+ * - Tokens are identity-based: the same token instance is the key.
37
+ * - `name` is for developer-facing errors and debugging only.
38
+ * - `defaultValue` is returned by inject/tryInject when no provider is found.
39
+ */
40
+ export declare function createContext<T>(name?: string, defaultValue?: T): ContextToken<T>;
41
+ /**
42
+ * Configure the depth at which context lookup warns about deep hierarchies.
43
+ * Set to Infinity to disable the warning.
44
+ */
45
+ export declare function setDeepHierarchyWarnDepth(depth: number): void;
46
+ /**
47
+ * Provide a context value in the current scope.
48
+ *
49
+ * Rules:
50
+ * - Must be called inside a scope.
51
+ * - Overrides any existing value for the same token in this scope.
52
+ */
6
53
  export declare function provide<T>(token: ContextToken<T>, value: T): void;
54
+ /**
55
+ * Inject a context value from the current scope hierarchy.
56
+ *
57
+ * Rules:
58
+ * - Must be called inside a scope.
59
+ * - Walks up the parent chain until it finds the token.
60
+ * - If not found and token has a defaultValue, returns that.
61
+ * - Throws a descriptive error if not found and no default.
62
+ */
7
63
  export declare function inject<T>(token: ContextToken<T>): T;
64
+ /**
65
+ * Result of tryInject - distinguishes between "not found" and "found with undefined value".
66
+ */
67
+ export type TryInjectResult<T> = {
68
+ found: true;
69
+ value: T;
70
+ } | {
71
+ found: false;
72
+ value: undefined;
73
+ };
74
+ /**
75
+ * Inject a context value from the current scope hierarchy if present.
76
+ *
77
+ * Rules:
78
+ * - Must be called inside a scope.
79
+ * - Returns { found: true, value } when found.
80
+ * - Returns { found: false, value: undefined } when not found (or uses defaultValue if available).
81
+ */
82
+ export declare function tryInject<T>(token: ContextToken<T>): TryInjectResult<T>;
83
+ /**
84
+ * Inject a context value and return metadata about where it was resolved.
85
+ */
86
+ export declare function injectMeta<T>(token: ContextToken<T>): {
87
+ value: T;
88
+ ownerScope: Scope;
89
+ depth: number;
90
+ };
91
+ /**
92
+ * Debug helper: list available context tokens per depth.
93
+ */
94
+ export declare function debugListAvailableContexts(maxPerLevel?: number): Array<{
95
+ depth: number;
96
+ tokens: {
97
+ name: string;
98
+ token: ContextToken<any>;
99
+ }[];
100
+ }>;
101
+ /**
102
+ * Alias for debugListAvailableContexts (public helper name).
103
+ */
104
+ export declare function listAvailableContexts(maxPerLevel?: number): Array<{
105
+ depth: number;
106
+ tokens: {
107
+ name: string;
108
+ token: ContextToken<any>;
109
+ }[];
110
+ }>;
111
+ export {};