@fluixi/di 0.0.0-stage → 1.0.0-alpha.99

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 (59) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +43 -2
  3. package/dist/errors.cjs +36 -0
  4. package/dist/errors.d.ts +31 -0
  5. package/dist/errors.d.ts.map +1 -0
  6. package/dist/errors.js +34 -0
  7. package/dist/errors.mjs +15 -0
  8. package/dist/hierarchy.cjs +291 -0
  9. package/dist/hierarchy.d.ts +11 -0
  10. package/dist/hierarchy.d.ts.map +1 -0
  11. package/dist/hierarchy.js +47 -0
  12. package/dist/hierarchy.mjs +271 -0
  13. package/dist/index.cjs +374 -0
  14. package/dist/index.d.ts +17 -0
  15. package/dist/index.d.ts.map +1 -0
  16. package/dist/index.js +15 -0
  17. package/dist/index.mjs +352 -0
  18. package/dist/initializers.cjs +289 -0
  19. package/dist/initializers.d.ts +36 -0
  20. package/dist/initializers.d.ts.map +1 -0
  21. package/dist/initializers.js +45 -0
  22. package/dist/initializers.mjs +267 -0
  23. package/dist/inject.cjs +352 -0
  24. package/dist/inject.d.ts +151 -0
  25. package/dist/inject.d.ts.map +1 -0
  26. package/dist/inject.js +174 -0
  27. package/dist/inject.mjs +332 -0
  28. package/dist/injector.cjs +259 -0
  29. package/dist/injector.d.ts +73 -0
  30. package/dist/injector.d.ts.map +1 -0
  31. package/dist/injector.js +213 -0
  32. package/dist/injector.mjs +239 -0
  33. package/dist/provider.cjs +18 -0
  34. package/dist/provider.d.ts +97 -0
  35. package/dist/provider.d.ts.map +1 -0
  36. package/dist/provider.js +1 -0
  37. package/dist/provider.mjs +0 -0
  38. package/dist/state.cjs +45 -0
  39. package/dist/state.d.ts +35 -0
  40. package/dist/state.d.ts.map +1 -0
  41. package/dist/state.js +26 -0
  42. package/dist/state.mjs +24 -0
  43. package/dist/testing.cjs +275 -0
  44. package/dist/testing.d.ts +50 -0
  45. package/dist/testing.d.ts.map +1 -0
  46. package/dist/testing.js +48 -0
  47. package/dist/testing.mjs +253 -0
  48. package/dist/token.cjs +28 -0
  49. package/dist/token.d.ts +67 -0
  50. package/dist/token.d.ts.map +1 -0
  51. package/dist/token.js +33 -0
  52. package/dist/token.mjs +7 -0
  53. package/dist/tokens.cjs +35 -0
  54. package/dist/tokens.d.ts +53 -0
  55. package/dist/tokens.d.ts.map +1 -0
  56. package/dist/tokens.js +26 -0
  57. package/dist/tokens.mjs +12 -0
  58. package/dist/tsconfig.lib.tsbuildinfo +1 -0
  59. package/package.json +55 -3
package/dist/index.mjs ADDED
@@ -0,0 +1,352 @@
1
+ /*! @fluixi/di v1.0.0-alpha.99 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
2
+ var __typeError = (msg) => {
3
+ throw TypeError(msg);
4
+ };
5
+ var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
6
+ var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
7
+ var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
8
+ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
9
+ var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
10
+
11
+ // src/token.ts
12
+ function injectionToken(name, options) {
13
+ return { id: Symbol(name), name, factory: options?.factory, scope: options?.scope ?? "root" };
14
+ }
15
+
16
+ // src/errors.ts
17
+ var InjectionError = class extends Error {
18
+ /**
19
+ * @param message - What went wrong, with the path rendered into it.
20
+ * @param path - The tokens being resolved, outermost first; the last one is the one that failed.
21
+ */
22
+ constructor(message, path) {
23
+ super(message);
24
+ this.path = path;
25
+ this.name = "InjectionError";
26
+ }
27
+ };
28
+
29
+ // src/tokens.ts
30
+ var DestroyRef = injectionToken("DestroyRef");
31
+ var INJECTOR = injectionToken("Injector");
32
+
33
+ // src/injector.ts
34
+ import { openReactiveScope, withReactiveScope } from "@fluixi/reactive/signal";
35
+
36
+ // src/hierarchy.ts
37
+ import { getRequestContext, onRequestEnd } from "@fluixi/dom";
38
+ import { getOwner } from "@fluixi/reactive/signal";
39
+
40
+ // src/state.ts
41
+ function freshState() {
42
+ return {
43
+ platformProviders: [],
44
+ rootProviders: [],
45
+ platform: null,
46
+ clientRoot: null,
47
+ requestRoots: /* @__PURE__ */ new WeakMap(),
48
+ ownerInjectors: /* @__PURE__ */ new WeakMap(),
49
+ moduleScopes: /* @__PURE__ */ new WeakMap(),
50
+ overrides: /* @__PURE__ */ new Map(),
51
+ explicit: null,
52
+ path: []
53
+ };
54
+ }
55
+ var state = globalThis[/* @__PURE__ */ Symbol.for("fluixi.di")] ??= freshState();
56
+ function setModuleScope(provider, scope) {
57
+ state.moduleScopes.set(provider, scope);
58
+ }
59
+
60
+ // src/hierarchy.ts
61
+ function platformInjector() {
62
+ return state.platform ??= new Injector(null, "platform").register(state.platformProviders);
63
+ }
64
+ function rootInjector() {
65
+ const ctx = getRequestContext();
66
+ if (ctx) {
67
+ let inj = state.requestRoots.get(ctx);
68
+ if (!inj) {
69
+ const root = new Injector(platformInjector(), "root").register(state.rootProviders);
70
+ state.requestRoots.set(ctx, root);
71
+ onRequestEnd(() => {
72
+ state.requestRoots.delete(ctx);
73
+ root.dispose();
74
+ });
75
+ inj = root;
76
+ }
77
+ return inj;
78
+ }
79
+ return state.clientRoot ??= new Injector(platformInjector(), "root").register(state.rootProviders);
80
+ }
81
+ function nearestOwnerInjector(owner) {
82
+ let o = owner;
83
+ while (o) {
84
+ const inj = state.ownerInjectors.get(o);
85
+ if (inj) return inj;
86
+ o = o.parent;
87
+ }
88
+ return null;
89
+ }
90
+ function currentInjector() {
91
+ return state.explicit ?? nearestOwnerInjector(getOwner()) ?? rootInjector();
92
+ }
93
+
94
+ // src/injector.ts
95
+ var _records, _created, _hooks, _destroyed, _Injector_instances, home_fn, fromProvider_fn, build_fn, make_fn;
96
+ var Injector = class {
97
+ /**
98
+ * @param parent - Where a token this injector does not hold is looked for next; `null` for the
99
+ * top of a chain.
100
+ * @param kind - Where the injector sits in the hierarchy; see {@link InjectorKind}.
101
+ */
102
+ constructor(parent = null, kind = "standalone") {
103
+ this.parent = parent;
104
+ this.kind = kind;
105
+ __privateAdd(this, _Injector_instances);
106
+ __privateAdd(this, _records, /* @__PURE__ */ new Map());
107
+ __privateAdd(this, _created, []);
108
+ __privateAdd(this, _hooks, []);
109
+ __privateAdd(this, _destroyed, false);
110
+ const self = this;
111
+ this.destroyRef = {
112
+ onDestroy(fn) {
113
+ __privateGet(self, _hooks).push(fn);
114
+ return () => {
115
+ const i = __privateGet(self, _hooks).indexOf(fn);
116
+ if (i >= 0) __privateGet(self, _hooks).splice(i, 1);
117
+ };
118
+ },
119
+ get destroyed() {
120
+ return __privateGet(self, _destroyed);
121
+ }
122
+ };
123
+ }
124
+ /** Whether {@link Injector.dispose} has run. A disposed injector refuses every `get`. */
125
+ get destroyed() {
126
+ return __privateGet(this, _destroyed);
127
+ }
128
+ /**
129
+ * Add providers to this injector. A provider for a token already held replaces it, except
130
+ * `multi` providers, which extend the token's list. A provider whose token has a test override
131
+ * is replaced by the override.
132
+ *
133
+ * @param providers - The providers to add, in order.
134
+ * @returns This injector, for chaining.
135
+ */
136
+ register(providers) {
137
+ for (const given of providers) {
138
+ const p = state.overrides.get(given.provide.id) ?? given;
139
+ if (p.multi) {
140
+ const rec = __privateGet(this, _records).get(p.provide.id);
141
+ if (rec?.multi) {
142
+ rec.providers.push(p);
143
+ rec.built = false;
144
+ } else __privateGet(this, _records).set(p.provide.id, { multi: true, providers: [p], built: false, building: false, value: [] });
145
+ } else {
146
+ __privateGet(this, _records).set(p.provide.id, { multi: false, provider: p, built: false, building: false, value: void 0 });
147
+ }
148
+ }
149
+ return this;
150
+ }
151
+ get(token, options = {}) {
152
+ var _a, _b, _c;
153
+ if (__privateGet(this, _destroyed)) throw new InjectionError(`[fluixi/di] "${token.name}" requested from a disposed injector.`, [...state.path, token.name]);
154
+ if (token.id === INJECTOR.id) return this;
155
+ if (token.id === DestroyRef.id) return this.destroyRef;
156
+ let inj = options.skipSelf ? this.parent : this;
157
+ while (inj) {
158
+ const rec = __privateGet(inj, _records).get(token.id);
159
+ if (rec) return __privateMethod(_a = inj, _Injector_instances, build_fn).call(_a, token, rec);
160
+ if (options.self || options.host && inj.kind === "owner") break;
161
+ inj = inj.parent;
162
+ }
163
+ const override = state.overrides.get(token.id);
164
+ if (override && !options.self) return __privateMethod(_b = __privateMethod(this, _Injector_instances, home_fn).call(this, token.scope), _Injector_instances, fromProvider_fn).call(_b, token, override);
165
+ if (token.factory && !options.self && !options.host) {
166
+ return __privateMethod(_c = __privateMethod(this, _Injector_instances, home_fn).call(this, token.scope), _Injector_instances, fromProvider_fn).call(_c, token, { provide: token, useFactory: token.factory });
167
+ }
168
+ if (options.optional) return null;
169
+ const path = [...state.path, token.name];
170
+ throw new InjectionError(
171
+ `[fluixi/di] No provider for "${token.name}" (${path.join(" → ")}). Provide it in an app module, with provide() or provideRoot(), or give the token a { factory }.`,
172
+ path
173
+ );
174
+ }
175
+ /**
176
+ * Release this injector: run the hooks registered on its `DestroyRef`, latest first, then the
177
+ * `onDestroy()` method of every service it built, latest built first, then forget its
178
+ * providers. A hook or `onDestroy()` that throws is logged and the rest still run. Running it
179
+ * again does nothing.
180
+ *
181
+ * Component injectors are disposed when the component unmounts, request roots when the request
182
+ * ends; call it yourself only for injectors you created.
183
+ */
184
+ dispose() {
185
+ if (__privateGet(this, _destroyed)) return;
186
+ __privateSet(this, _destroyed, true);
187
+ const fail = (error) => console.error("[fluixi/di] a destroy hook threw:", error);
188
+ for (const hook of __privateGet(this, _hooks).splice(0).reverse()) {
189
+ try {
190
+ hook();
191
+ } catch (error) {
192
+ fail(error);
193
+ }
194
+ }
195
+ for (const value of __privateGet(this, _created).splice(0).reverse()) {
196
+ const service = value;
197
+ if (service && typeof service.onDestroy === "function") {
198
+ try {
199
+ service.onDestroy();
200
+ } catch (error) {
201
+ fail(error);
202
+ }
203
+ }
204
+ }
205
+ __privateGet(this, _records).clear();
206
+ }
207
+ };
208
+ _records = new WeakMap();
209
+ _created = new WeakMap();
210
+ _hooks = new WeakMap();
211
+ _destroyed = new WeakMap();
212
+ _Injector_instances = new WeakSet();
213
+ /** The injector a self-providing token builds in: the nearest root, or the platform. */
214
+ home_fn = function(scope) {
215
+ let inj = this;
216
+ if (scope === "platform") {
217
+ while (inj.parent) inj = inj.parent;
218
+ return inj.kind === "platform" ? inj : platformInjector();
219
+ }
220
+ let probe = this;
221
+ while (probe) {
222
+ if (probe.kind === "root") return probe;
223
+ probe = probe.parent;
224
+ }
225
+ while (inj.parent && inj.parent.kind !== "platform") inj = inj.parent;
226
+ return inj;
227
+ };
228
+ fromProvider_fn = function(token, provider) {
229
+ let rec = __privateGet(this, _records).get(token.id);
230
+ if (!rec) {
231
+ rec = { multi: false, provider, built: false, building: false, value: void 0 };
232
+ __privateGet(this, _records).set(token.id, rec);
233
+ }
234
+ return __privateMethod(this, _Injector_instances, build_fn).call(this, token, rec);
235
+ };
236
+ build_fn = function(token, rec) {
237
+ if (rec.built) return rec.value;
238
+ if (rec.building) {
239
+ const path = [...state.path, token.name];
240
+ throw new InjectionError(`[fluixi/di] Circular dependency: ${path.join(" → ")}.`, path);
241
+ }
242
+ rec.building = true;
243
+ state.path.push(token.name);
244
+ const previous = state.explicit;
245
+ state.explicit = this;
246
+ try {
247
+ if (rec.multi) {
248
+ rec.value = rec.providers.map((p) => __privateMethod(this, _Injector_instances, make_fn).call(this, token, p));
249
+ } else {
250
+ const module = state.moduleScopes.get(rec.provider);
251
+ rec.value = module ? withReactiveScope(module, () => __privateMethod(this, _Injector_instances, make_fn).call(this, token, rec.provider)) : __privateMethod(this, _Injector_instances, make_fn).call(this, token, rec.provider);
252
+ }
253
+ rec.built = true;
254
+ return rec.value;
255
+ } finally {
256
+ state.explicit = previous;
257
+ state.path.pop();
258
+ rec.building = false;
259
+ }
260
+ };
261
+ make_fn = function(token, p) {
262
+ if ("useValue" in p) return p.useValue;
263
+ if ("useExisting" in p) return this.get(p.useExisting);
264
+ const deps = (p.deps ?? []).map((d) => "token" in d ? this.get(d.token, d) : this.get(d));
265
+ const scope = openReactiveScope({ kind: "service", name: token.name, detail: "useClass" in p ? "useClass" : "useFactory" });
266
+ const value = withReactiveScope(scope, () => "useClass" in p ? new p.useClass(...deps) : p.useFactory(...deps));
267
+ __privateGet(this, _created).push(value);
268
+ return value;
269
+ };
270
+
271
+ // src/inject.ts
272
+ import { getRequestContext as getRequestContext2 } from "@fluixi/dom";
273
+ import { getOwner as getOwner2, onCleanup } from "@fluixi/reactive/signal";
274
+ function inject(token, options) {
275
+ return currentInjector().get(token, options ?? {});
276
+ }
277
+ function provide(providers) {
278
+ const owner = getOwner2();
279
+ if (!owner) {
280
+ throw new Error("[fluixi/di] provide() runs inside a component. Use provideRoot() for services of the whole app.");
281
+ }
282
+ let inj = state.ownerInjectors.get(owner);
283
+ if (!inj) {
284
+ const created = new Injector(nearestOwnerInjector(owner.parent) ?? rootInjector(), "owner");
285
+ state.ownerInjectors.set(owner, created);
286
+ onCleanup(() => created.dispose());
287
+ inj = created;
288
+ }
289
+ return inj.register(providers);
290
+ }
291
+ function provideRoot(providers) {
292
+ state.rootProviders.push(...providers);
293
+ state.clientRoot?.register(providers);
294
+ const ctx = getRequestContext2();
295
+ if (ctx) state.requestRoots.get(ctx)?.register(providers);
296
+ }
297
+ function providePlatform(providers) {
298
+ state.platformProviders.push(...providers);
299
+ state.platform?.register(providers);
300
+ }
301
+ function runInInjectionContext(injector, fn) {
302
+ const previous = state.explicit;
303
+ state.explicit = injector;
304
+ try {
305
+ return fn();
306
+ } finally {
307
+ state.explicit = previous;
308
+ }
309
+ }
310
+ function isInInjectionContext() {
311
+ return state.explicit !== null || getOwner2() !== null || getRequestContext2() !== void 0;
312
+ }
313
+ function assertInInjectionContext(name) {
314
+ if (!isInInjectionContext()) {
315
+ throw new Error(`[fluixi/di] ${name}() runs inside a component, a request or runInInjectionContext().`);
316
+ }
317
+ }
318
+ function getInjector() {
319
+ return currentInjector();
320
+ }
321
+ function createInjector(providers, parent) {
322
+ return new Injector(parent === void 0 ? rootInjector() : parent, "standalone").register(providers);
323
+ }
324
+
325
+ // src/initializers.ts
326
+ var APP_INITIALIZER = injectionToken("AppInitializer");
327
+ function provideAppInitializer(fn) {
328
+ return { provide: APP_INITIALIZER, useValue: fn, multi: true };
329
+ }
330
+ async function runAppInitializers(injector = rootInjector()) {
331
+ for (const run of injector.get(APP_INITIALIZER, { optional: true }) ?? []) await run();
332
+ }
333
+ export {
334
+ APP_INITIALIZER,
335
+ DestroyRef,
336
+ INJECTOR,
337
+ InjectionError,
338
+ Injector,
339
+ assertInInjectionContext,
340
+ createInjector,
341
+ getInjector,
342
+ inject,
343
+ injectionToken,
344
+ isInInjectionContext,
345
+ provide,
346
+ provideAppInitializer,
347
+ providePlatform,
348
+ provideRoot,
349
+ runAppInitializers,
350
+ runInInjectionContext,
351
+ setModuleScope
352
+ };
@@ -0,0 +1,289 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __typeError = (msg) => {
7
+ throw TypeError(msg);
8
+ };
9
+ var __export = (target, all) => {
10
+ for (var name in all)
11
+ __defProp(target, name, { get: all[name], enumerable: true });
12
+ };
13
+ var __copyProps = (to, from, except, desc) => {
14
+ if (from && typeof from === "object" || typeof from === "function") {
15
+ for (let key of __getOwnPropNames(from))
16
+ if (!__hasOwnProp.call(to, key) && key !== except)
17
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
18
+ }
19
+ return to;
20
+ };
21
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
22
+ var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
23
+ var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
24
+ var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
25
+ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
26
+ var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
27
+
28
+ // src/initializers.ts
29
+ var initializers_exports = {};
30
+ __export(initializers_exports, {
31
+ APP_INITIALIZER: () => APP_INITIALIZER,
32
+ provideAppInitializer: () => provideAppInitializer,
33
+ runAppInitializers: () => runAppInitializers
34
+ });
35
+ module.exports = __toCommonJS(initializers_exports);
36
+
37
+ // src/hierarchy.ts
38
+ var import_dom = require("@fluixi/dom");
39
+ var import_signal2 = require("@fluixi/reactive/signal");
40
+
41
+ // src/injector.ts
42
+ var import_signal = require("@fluixi/reactive/signal");
43
+
44
+ // src/errors.ts
45
+ var InjectionError = class extends Error {
46
+ /**
47
+ * @param message - What went wrong, with the path rendered into it.
48
+ * @param path - The tokens being resolved, outermost first; the last one is the one that failed.
49
+ */
50
+ constructor(message, path) {
51
+ super(message);
52
+ this.path = path;
53
+ this.name = "InjectionError";
54
+ }
55
+ };
56
+
57
+ // src/state.ts
58
+ function freshState() {
59
+ return {
60
+ platformProviders: [],
61
+ rootProviders: [],
62
+ platform: null,
63
+ clientRoot: null,
64
+ requestRoots: /* @__PURE__ */ new WeakMap(),
65
+ ownerInjectors: /* @__PURE__ */ new WeakMap(),
66
+ moduleScopes: /* @__PURE__ */ new WeakMap(),
67
+ overrides: /* @__PURE__ */ new Map(),
68
+ explicit: null,
69
+ path: []
70
+ };
71
+ }
72
+ var state = globalThis[/* @__PURE__ */ Symbol.for("fluixi.di")] ??= freshState();
73
+
74
+ // src/token.ts
75
+ function injectionToken(name, options) {
76
+ return { id: Symbol(name), name, factory: options?.factory, scope: options?.scope ?? "root" };
77
+ }
78
+
79
+ // src/tokens.ts
80
+ var DestroyRef = injectionToken("DestroyRef");
81
+ var INJECTOR = injectionToken("Injector");
82
+
83
+ // src/injector.ts
84
+ var _records, _created, _hooks, _destroyed, _Injector_instances, home_fn, fromProvider_fn, build_fn, make_fn;
85
+ var Injector = class {
86
+ /**
87
+ * @param parent - Where a token this injector does not hold is looked for next; `null` for the
88
+ * top of a chain.
89
+ * @param kind - Where the injector sits in the hierarchy; see {@link InjectorKind}.
90
+ */
91
+ constructor(parent = null, kind = "standalone") {
92
+ this.parent = parent;
93
+ this.kind = kind;
94
+ __privateAdd(this, _Injector_instances);
95
+ __privateAdd(this, _records, /* @__PURE__ */ new Map());
96
+ __privateAdd(this, _created, []);
97
+ __privateAdd(this, _hooks, []);
98
+ __privateAdd(this, _destroyed, false);
99
+ const self = this;
100
+ this.destroyRef = {
101
+ onDestroy(fn) {
102
+ __privateGet(self, _hooks).push(fn);
103
+ return () => {
104
+ const i = __privateGet(self, _hooks).indexOf(fn);
105
+ if (i >= 0) __privateGet(self, _hooks).splice(i, 1);
106
+ };
107
+ },
108
+ get destroyed() {
109
+ return __privateGet(self, _destroyed);
110
+ }
111
+ };
112
+ }
113
+ /** Whether {@link Injector.dispose} has run. A disposed injector refuses every `get`. */
114
+ get destroyed() {
115
+ return __privateGet(this, _destroyed);
116
+ }
117
+ /**
118
+ * Add providers to this injector. A provider for a token already held replaces it, except
119
+ * `multi` providers, which extend the token's list. A provider whose token has a test override
120
+ * is replaced by the override.
121
+ *
122
+ * @param providers - The providers to add, in order.
123
+ * @returns This injector, for chaining.
124
+ */
125
+ register(providers) {
126
+ for (const given of providers) {
127
+ const p = state.overrides.get(given.provide.id) ?? given;
128
+ if (p.multi) {
129
+ const rec = __privateGet(this, _records).get(p.provide.id);
130
+ if (rec?.multi) {
131
+ rec.providers.push(p);
132
+ rec.built = false;
133
+ } else __privateGet(this, _records).set(p.provide.id, { multi: true, providers: [p], built: false, building: false, value: [] });
134
+ } else {
135
+ __privateGet(this, _records).set(p.provide.id, { multi: false, provider: p, built: false, building: false, value: void 0 });
136
+ }
137
+ }
138
+ return this;
139
+ }
140
+ get(token, options = {}) {
141
+ var _a, _b, _c;
142
+ if (__privateGet(this, _destroyed)) throw new InjectionError(`[fluixi/di] "${token.name}" requested from a disposed injector.`, [...state.path, token.name]);
143
+ if (token.id === INJECTOR.id) return this;
144
+ if (token.id === DestroyRef.id) return this.destroyRef;
145
+ let inj = options.skipSelf ? this.parent : this;
146
+ while (inj) {
147
+ const rec = __privateGet(inj, _records).get(token.id);
148
+ if (rec) return __privateMethod(_a = inj, _Injector_instances, build_fn).call(_a, token, rec);
149
+ if (options.self || options.host && inj.kind === "owner") break;
150
+ inj = inj.parent;
151
+ }
152
+ const override = state.overrides.get(token.id);
153
+ if (override && !options.self) return __privateMethod(_b = __privateMethod(this, _Injector_instances, home_fn).call(this, token.scope), _Injector_instances, fromProvider_fn).call(_b, token, override);
154
+ if (token.factory && !options.self && !options.host) {
155
+ return __privateMethod(_c = __privateMethod(this, _Injector_instances, home_fn).call(this, token.scope), _Injector_instances, fromProvider_fn).call(_c, token, { provide: token, useFactory: token.factory });
156
+ }
157
+ if (options.optional) return null;
158
+ const path = [...state.path, token.name];
159
+ throw new InjectionError(
160
+ `[fluixi/di] No provider for "${token.name}" (${path.join(" → ")}). Provide it in an app module, with provide() or provideRoot(), or give the token a { factory }.`,
161
+ path
162
+ );
163
+ }
164
+ /**
165
+ * Release this injector: run the hooks registered on its `DestroyRef`, latest first, then the
166
+ * `onDestroy()` method of every service it built, latest built first, then forget its
167
+ * providers. A hook or `onDestroy()` that throws is logged and the rest still run. Running it
168
+ * again does nothing.
169
+ *
170
+ * Component injectors are disposed when the component unmounts, request roots when the request
171
+ * ends; call it yourself only for injectors you created.
172
+ */
173
+ dispose() {
174
+ if (__privateGet(this, _destroyed)) return;
175
+ __privateSet(this, _destroyed, true);
176
+ const fail = (error) => console.error("[fluixi/di] a destroy hook threw:", error);
177
+ for (const hook of __privateGet(this, _hooks).splice(0).reverse()) {
178
+ try {
179
+ hook();
180
+ } catch (error) {
181
+ fail(error);
182
+ }
183
+ }
184
+ for (const value of __privateGet(this, _created).splice(0).reverse()) {
185
+ const service = value;
186
+ if (service && typeof service.onDestroy === "function") {
187
+ try {
188
+ service.onDestroy();
189
+ } catch (error) {
190
+ fail(error);
191
+ }
192
+ }
193
+ }
194
+ __privateGet(this, _records).clear();
195
+ }
196
+ };
197
+ _records = new WeakMap();
198
+ _created = new WeakMap();
199
+ _hooks = new WeakMap();
200
+ _destroyed = new WeakMap();
201
+ _Injector_instances = new WeakSet();
202
+ /** The injector a self-providing token builds in: the nearest root, or the platform. */
203
+ home_fn = function(scope) {
204
+ let inj = this;
205
+ if (scope === "platform") {
206
+ while (inj.parent) inj = inj.parent;
207
+ return inj.kind === "platform" ? inj : platformInjector();
208
+ }
209
+ let probe = this;
210
+ while (probe) {
211
+ if (probe.kind === "root") return probe;
212
+ probe = probe.parent;
213
+ }
214
+ while (inj.parent && inj.parent.kind !== "platform") inj = inj.parent;
215
+ return inj;
216
+ };
217
+ fromProvider_fn = function(token, provider) {
218
+ let rec = __privateGet(this, _records).get(token.id);
219
+ if (!rec) {
220
+ rec = { multi: false, provider, built: false, building: false, value: void 0 };
221
+ __privateGet(this, _records).set(token.id, rec);
222
+ }
223
+ return __privateMethod(this, _Injector_instances, build_fn).call(this, token, rec);
224
+ };
225
+ build_fn = function(token, rec) {
226
+ if (rec.built) return rec.value;
227
+ if (rec.building) {
228
+ const path = [...state.path, token.name];
229
+ throw new InjectionError(`[fluixi/di] Circular dependency: ${path.join(" → ")}.`, path);
230
+ }
231
+ rec.building = true;
232
+ state.path.push(token.name);
233
+ const previous = state.explicit;
234
+ state.explicit = this;
235
+ try {
236
+ if (rec.multi) {
237
+ rec.value = rec.providers.map((p) => __privateMethod(this, _Injector_instances, make_fn).call(this, token, p));
238
+ } else {
239
+ const module2 = state.moduleScopes.get(rec.provider);
240
+ rec.value = module2 ? (0, import_signal.withReactiveScope)(module2, () => __privateMethod(this, _Injector_instances, make_fn).call(this, token, rec.provider)) : __privateMethod(this, _Injector_instances, make_fn).call(this, token, rec.provider);
241
+ }
242
+ rec.built = true;
243
+ return rec.value;
244
+ } finally {
245
+ state.explicit = previous;
246
+ state.path.pop();
247
+ rec.building = false;
248
+ }
249
+ };
250
+ make_fn = function(token, p) {
251
+ if ("useValue" in p) return p.useValue;
252
+ if ("useExisting" in p) return this.get(p.useExisting);
253
+ const deps = (p.deps ?? []).map((d) => "token" in d ? this.get(d.token, d) : this.get(d));
254
+ const scope = (0, import_signal.openReactiveScope)({ kind: "service", name: token.name, detail: "useClass" in p ? "useClass" : "useFactory" });
255
+ const value = (0, import_signal.withReactiveScope)(scope, () => "useClass" in p ? new p.useClass(...deps) : p.useFactory(...deps));
256
+ __privateGet(this, _created).push(value);
257
+ return value;
258
+ };
259
+
260
+ // src/hierarchy.ts
261
+ function platformInjector() {
262
+ return state.platform ??= new Injector(null, "platform").register(state.platformProviders);
263
+ }
264
+ function rootInjector() {
265
+ const ctx = (0, import_dom.getRequestContext)();
266
+ if (ctx) {
267
+ let inj = state.requestRoots.get(ctx);
268
+ if (!inj) {
269
+ const root = new Injector(platformInjector(), "root").register(state.rootProviders);
270
+ state.requestRoots.set(ctx, root);
271
+ (0, import_dom.onRequestEnd)(() => {
272
+ state.requestRoots.delete(ctx);
273
+ root.dispose();
274
+ });
275
+ inj = root;
276
+ }
277
+ return inj;
278
+ }
279
+ return state.clientRoot ??= new Injector(platformInjector(), "root").register(state.rootProviders);
280
+ }
281
+
282
+ // src/initializers.ts
283
+ var APP_INITIALIZER = injectionToken("AppInitializer");
284
+ function provideAppInitializer(fn) {
285
+ return { provide: APP_INITIALIZER, useValue: fn, multi: true };
286
+ }
287
+ async function runAppInitializers(injector = rootInjector()) {
288
+ for (const run of injector.get(APP_INITIALIZER, { optional: true }) ?? []) await run();
289
+ }
@@ -0,0 +1,36 @@
1
+ import type { Injector } from './injector.js';
2
+ import type { Provider } from './provider.js';
3
+ import { type Token } from './token.js';
4
+ /**
5
+ * The functions given to {@link provideAppInitializer}, as a `multi` token: `inject(APP_INITIALIZER)`
6
+ * answers them in registration order. Read by {@link runAppInitializers}; rarely used directly.
7
+ */
8
+ export declare const APP_INITIALIZER: Token<Array<() => unknown>>;
9
+ /**
10
+ * A provider that runs `fn` before the app renders. List it with the app's root providers; the
11
+ * framework calls {@link runAppInitializers} before the first render. `fn` runs in the root's
12
+ * injection context, so it may call `inject()`.
13
+ *
14
+ * @param fn - The work to run. May return a promise, which is awaited before the next initializer.
15
+ * @returns A provider to pass to `provideRoot()` or an app module's providers.
16
+ *
17
+ * @example
18
+ * ```ts
19
+ * provideRoot([
20
+ * provideAppInitializer(async () => {
21
+ * await inject(Settings).load();
22
+ * }),
23
+ * ]);
24
+ * ```
25
+ */
26
+ export declare function provideAppInitializer(fn: () => unknown): Provider<() => unknown>;
27
+ /**
28
+ * Run every app initializer `injector` holds, one after another in registration order, awaiting
29
+ * each. The first that throws or rejects stops the rest, and the returned promise rejects with
30
+ * its error: an app that could not initialize should not render as if it had.
31
+ *
32
+ * @param injector - Whose initializers to run. Defaults to the current root.
33
+ * @returns A promise settled when every initializer has finished.
34
+ */
35
+ export declare function runAppInitializers(injector?: Injector): Promise<void>;
36
+ //# sourceMappingURL=initializers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"initializers.d.ts","sourceRoot":"","sources":["../src/initializers.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAkB,KAAK,KAAK,EAAE,MAAM,YAAY,CAAC;AAExD;;;GAGG;AACH,eAAO,MAAM,eAAe,EAAE,KAAK,CAAC,KAAK,CAAC,MAAM,OAAO,CAAC,CAA0D,CAAC;AAEnH;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,qBAAqB,CAAC,EAAE,EAAE,MAAM,OAAO,GAAG,QAAQ,CAAC,MAAM,OAAO,CAAC,CAEhF;AAED;;;;;;;GAOG;AACH,wBAAsB,kBAAkB,CAAC,QAAQ,GAAE,QAAyB,GAAG,OAAO,CAAC,IAAI,CAAC,CAE3F"}