@foxford/den 1.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/index.cjs ADDED
@@ -0,0 +1,1027 @@
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 __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var __async = (__this, __arguments, generator) => {
20
+ return new Promise((resolve, reject) => {
21
+ var fulfilled = (value) => {
22
+ try {
23
+ step(generator.next(value));
24
+ } catch (e) {
25
+ reject(e);
26
+ }
27
+ };
28
+ var rejected = (value) => {
29
+ try {
30
+ step(generator.throw(value));
31
+ } catch (e) {
32
+ reject(e);
33
+ }
34
+ };
35
+ var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
36
+ step((generator = generator.apply(__this, __arguments)).next());
37
+ });
38
+ };
39
+
40
+ // src/index.ts
41
+ var src_exports = {};
42
+ __export(src_exports, {
43
+ ActivatableToken: () => ActivatableToken,
44
+ ContainerManagerImpl: () => ContainerManagerImpl,
45
+ ContainerManagerToken: () => ContainerManagerToken,
46
+ GuardRunnerImpl: () => GuardRunnerImpl,
47
+ GuardRunnerToken: () => GuardRunnerToken,
48
+ NavigationManagerImpl: () => NavigationManagerImpl,
49
+ NavigationManagerToken: () => NavigationManagerToken,
50
+ RequestContextToken: () => RequestContextToken,
51
+ SlotRegistry: () => SlotRegistry,
52
+ SlotRegistryToken: () => SlotRegistryToken,
53
+ StateCache: () => StateCache,
54
+ StateRegistry: () => StateRegistry,
55
+ StateRegistryToken: () => StateRegistryToken,
56
+ StateSerializerImpl: () => StateSerializerImpl,
57
+ StateSerializerToken: () => StateSerializerToken,
58
+ createCore: () => createCore,
59
+ defaultGlobalResolver: () => defaultGlobalResolver,
60
+ defineActionGuard: () => defineActionGuard,
61
+ defineApp: () => defineApp,
62
+ defineExtension: () => defineExtension,
63
+ defineGuard: () => defineGuard,
64
+ defineLayer: () => defineLayer,
65
+ defineLayout: () => defineLayout,
66
+ defineRepository: () => defineRepository,
67
+ defineService: () => defineService,
68
+ defineSlot: () => defineSlot,
69
+ defineViewModel: () => defineViewModel
70
+ });
71
+ module.exports = __toCommonJS(src_exports);
72
+
73
+ // src/core/types.ts
74
+ var import_ioc = require("@foxford/ioc");
75
+ var ContainerManagerToken = (0, import_ioc.createToken)("core:ContainerManager");
76
+ var GuardRunnerToken = (0, import_ioc.createToken)("core:GuardRunner");
77
+ var NavigationManagerToken = (0, import_ioc.createToken)("core:NavigationManager");
78
+ var StateSerializerToken = (0, import_ioc.createToken)("core:StateSerializer");
79
+ var StateRegistryToken = (0, import_ioc.createToken)("core:StateRegistry");
80
+ var SlotRegistryToken = (0, import_ioc.createToken)("core:SlotRegistry");
81
+ var ActivatableToken = (0, import_ioc.createToken)("core:Activatable");
82
+ var RequestContextToken = (0, import_ioc.createToken)("core:RequestContext");
83
+
84
+ // src/core/state-registry.ts
85
+ var StateRegistry = class {
86
+ constructor() {
87
+ /** Участники: стабильный ключ (token.description) → участник. */
88
+ this.participants = /* @__PURE__ */ new Map();
89
+ }
90
+ /**
91
+ * Регистрирует участника сериализации.
92
+ * Повторная регистрация с тем же ключом перезаписывает предыдущего.
93
+ * @param key - Стабильный ключ (обычно token.description)
94
+ * @param participant - Участник с хуками toJSON/fromJSON/activate
95
+ */
96
+ add(key, participant) {
97
+ this.participants.set(key, participant);
98
+ }
99
+ /**
100
+ * Возвращает всех зарегистрированных участников (ключ → участник).
101
+ */
102
+ all() {
103
+ return this.participants;
104
+ }
105
+ };
106
+
107
+ // src/core/container-manager.ts
108
+ var ContainerManagerImpl = class {
109
+ /**
110
+ * @param appContainer - Корневой app-контейнер
111
+ */
112
+ constructor(appContainer) {
113
+ /** Map дочерних контейнеров страниц: routeId → Container */
114
+ this.pageContainers = /* @__PURE__ */ new Map();
115
+ /** Map контейнеров запросов: requestId → Container */
116
+ this.requestContainers = /* @__PURE__ */ new Map();
117
+ /** Map SSR page контейнеров: requestId → Container (child от request container) */
118
+ this.ssrPageContainers = /* @__PURE__ */ new Map();
119
+ this.appContainer = appContainer;
120
+ }
121
+ /**
122
+ * Возвращает app-контейнер (синглтон).
123
+ */
124
+ getAppContainer() {
125
+ return this.appContainer;
126
+ }
127
+ /**
128
+ * Возвращает существующий дочерний контейнер страницы или undefined.
129
+ * @param routeId - Идентификатор маршрута
130
+ */
131
+ getPageContainer(routeId) {
132
+ return this.pageContainers.get(routeId);
133
+ }
134
+ /**
135
+ * Создаёт дочерний контейнер для страницы.
136
+ * Если контейнер для данного routeId уже существует, возвращает существующий.
137
+ * @param routeId - Идентификатор маршрута
138
+ */
139
+ createPageContainer(routeId) {
140
+ const existing = this.pageContainers.get(routeId);
141
+ if (existing !== void 0) {
142
+ return existing;
143
+ }
144
+ const child = this.appContainer.createChild();
145
+ child.bind(StateRegistryToken).toValue(new StateRegistry());
146
+ this.pageContainers.set(routeId, child);
147
+ return child;
148
+ }
149
+ /**
150
+ * Уничтожает дочерний контейнер страницы и освобождает его ресурсы.
151
+ * Если контейнер не найден — no-op.
152
+ * @param routeId - Идентификатор маршрута
153
+ */
154
+ destroyPageContainer(routeId) {
155
+ return __async(this, null, function* () {
156
+ const container = this.pageContainers.get(routeId);
157
+ if (container === void 0) {
158
+ return;
159
+ }
160
+ this.pageContainers.delete(routeId);
161
+ yield container.dispose();
162
+ });
163
+ }
164
+ /**
165
+ * Создаёт дочерний контейнер для HTTP-запроса с забинженным RequestContext.
166
+ * Если контейнер для данного requestId уже существует, возвращает существующий.
167
+ * @param requestId - Уникальный идентификатор запроса
168
+ * @param context - Контекст запроса (заголовки и cookies)
169
+ */
170
+ createRequestContainer(requestId, context) {
171
+ const existing = this.requestContainers.get(requestId);
172
+ if (existing !== void 0) {
173
+ return existing;
174
+ }
175
+ const child = this.appContainer.createChild();
176
+ child.bind(RequestContextToken).toValue(context);
177
+ child.bind(StateRegistryToken).toValue(new StateRegistry());
178
+ this.requestContainers.set(requestId, child);
179
+ return child;
180
+ }
181
+ /**
182
+ * Возвращает существующий контейнер запроса или undefined.
183
+ * @param requestId - Уникальный идентификатор запроса
184
+ */
185
+ getRequestContainer(requestId) {
186
+ return this.requestContainers.get(requestId);
187
+ }
188
+ /**
189
+ * Уничтожает контейнер запроса и освобождает ресурсы.
190
+ * Если контейнер не найден — no-op.
191
+ * @param requestId - Уникальный идентификатор запроса
192
+ */
193
+ destroyRequestContainer(requestId) {
194
+ return __async(this, null, function* () {
195
+ const container = this.requestContainers.get(requestId);
196
+ if (container === void 0) {
197
+ return;
198
+ }
199
+ this.requestContainers.delete(requestId);
200
+ yield container.dispose();
201
+ });
202
+ }
203
+ /**
204
+ * Создаёт page container для SSR как дочерний от заданного родителя.
205
+ * Отслеживается менеджером — может быть уничтожен через destroySsrPageContainer.
206
+ * Если контейнер для данного requestId уже существует, возвращает существующий.
207
+ * @param requestId - Уникальный идентификатор запроса
208
+ * @param parent - Родительский контейнер (обычно request container)
209
+ */
210
+ createSsrPageContainer(requestId, parent) {
211
+ const existing = this.ssrPageContainers.get(requestId);
212
+ if (existing !== void 0) {
213
+ return existing;
214
+ }
215
+ const child = parent.createChild();
216
+ child.bind(StateRegistryToken).toValue(new StateRegistry());
217
+ this.ssrPageContainers.set(requestId, child);
218
+ return child;
219
+ }
220
+ /**
221
+ * Уничтожает SSR page container и освобождает ресурсы (Disposable VM, сервисы).
222
+ * Вызывается в onAfterRenderHtml после завершения SSR-рендера.
223
+ * Если контейнер не найден — no-op.
224
+ * @param requestId - Уникальный идентификатор запроса
225
+ */
226
+ destroySsrPageContainer(requestId) {
227
+ return __async(this, null, function* () {
228
+ const container = this.ssrPageContainers.get(requestId);
229
+ if (container === void 0) {
230
+ return;
231
+ }
232
+ this.ssrPageContainers.delete(requestId);
233
+ yield container.dispose();
234
+ });
235
+ }
236
+ };
237
+
238
+ // src/core/guard-runner.ts
239
+ var GuardRunnerImpl = class {
240
+ /**
241
+ * @param container - App-контейнер, из которого резолвятся guards
242
+ */
243
+ constructor(container) {
244
+ this.container = container;
245
+ }
246
+ /**
247
+ * Параллельно выполняет все guards и возвращает их результаты.
248
+ * Если передан container — guards резолвятся из него (например, request-scope container).
249
+ * Без container — guards резолвятся из app-контейнера.
250
+ * @param tokens - Токены guards
251
+ * @param params - Параметры маршрута
252
+ * @param container - Опциональный контейнер для резолвинга guards
253
+ */
254
+ runGuards(tokens, params, container) {
255
+ return __async(this, null, function* () {
256
+ const resolveFrom = container != null ? container : this.container;
257
+ const results = yield Promise.all(
258
+ tokens.map((token) => __async(this, null, function* () {
259
+ const guard = yield resolveFrom.resolve(token);
260
+ return guard.check(params);
261
+ }))
262
+ );
263
+ return results;
264
+ });
265
+ }
266
+ };
267
+
268
+ // src/core/navigation-manager.ts
269
+ function isPlainLeftClick(event) {
270
+ return !event.defaultPrevented && event.button === 0 && !event.metaKey && !event.ctrlKey && !event.shiftKey && !event.altKey;
271
+ }
272
+ function getNavigableAnchor(event) {
273
+ var _a, _b;
274
+ const target = (_b = (_a = event.target) == null ? void 0 : _a.closest) == null ? void 0 : _b.call(_a, "a");
275
+ if (!(target instanceof HTMLAnchorElement)) {
276
+ return null;
277
+ }
278
+ if (target.target && target.target !== "_self") {
279
+ return null;
280
+ }
281
+ if (target.hasAttribute("download")) {
282
+ return null;
283
+ }
284
+ return target;
285
+ }
286
+ var NavigationManagerImpl = class {
287
+ constructor() {
288
+ /** Стек истории навигации */
289
+ this._history = [];
290
+ /** Текущая запись навигации */
291
+ this.current = null;
292
+ /** Список обработчиков навигации */
293
+ this.handlers = [];
294
+ /** Список зарегистрированных interceptors (leave guards) */
295
+ this.interceptors = [];
296
+ /** Функция очистки popstate listener (только на клиенте) */
297
+ this.removePopstateListener = null;
298
+ /** Функция очистки beforeunload listener (только на клиенте) */
299
+ this.removeBeforeunloadListener = null;
300
+ /** Функция очистки click listener (только на клиенте) */
301
+ this.removeClickListener = null;
302
+ }
303
+ /**
304
+ * История переходов (только для чтения снаружи).
305
+ */
306
+ get history() {
307
+ return this._history;
308
+ }
309
+ /**
310
+ * Подписывается на событие навигации.
311
+ * @param handler - Обработчик события
312
+ * @returns Функция отписки
313
+ */
314
+ onNavigate(handler) {
315
+ this.handlers.push(handler);
316
+ return () => {
317
+ const index = this.handlers.indexOf(handler);
318
+ if (index !== -1) {
319
+ this.handlers.splice(index, 1);
320
+ }
321
+ };
322
+ }
323
+ /**
324
+ * Записывает переход в историю и оповещает подписчиков.
325
+ * @param from - Маршрут отправления (null — начало сессии)
326
+ * @param to - Маршрут назначения
327
+ * @param params - Параметры маршрута
328
+ */
329
+ recordNavigation(from, to, params) {
330
+ const entry = {
331
+ from,
332
+ params,
333
+ timestamp: Date.now(),
334
+ to
335
+ };
336
+ this._history.push(entry);
337
+ this.current = entry;
338
+ for (const handler of this.handlers) {
339
+ handler(entry);
340
+ }
341
+ }
342
+ /**
343
+ * Проверяет актуальность текущего состояния.
344
+ * Состояние считается актуальным если история и текущий элемент согласованы.
345
+ */
346
+ checkFreshness() {
347
+ return __async(this, null, function* () {
348
+ var _a;
349
+ const lastEntry = (_a = this._history.at(-1)) != null ? _a : null;
350
+ return lastEntry === this.current;
351
+ });
352
+ }
353
+ /**
354
+ * Регистрирует interceptor для перехвата навигации (leave guard).
355
+ * @param handler - Перехватчик навигации
356
+ * @returns Disposable для отмены регистрации
357
+ */
358
+ intercept(handler) {
359
+ this.interceptors.push(handler);
360
+ return {
361
+ dispose: () => {
362
+ const index = this.interceptors.indexOf(handler);
363
+ if (index !== -1) {
364
+ this.interceptors.splice(index, 1);
365
+ }
366
+ }
367
+ };
368
+ }
369
+ /**
370
+ * Запускает все зарегистрированные leave guards последовательно.
371
+ * При первом заблокированном возвращает `{ allow: false, reason? }`.
372
+ * @param from - Маршрут отправления
373
+ * @param to - Маршрут назначения
374
+ * @returns Результат проверки
375
+ */
376
+ runLeaveGuards(from, to) {
377
+ return __async(this, null, function* () {
378
+ for (const interceptor of this.interceptors) {
379
+ const result = yield interceptor.beforeNavigate(from, to);
380
+ if (!result.allow) {
381
+ return { allow: false, reason: result.reason };
382
+ }
383
+ }
384
+ return { allow: true };
385
+ });
386
+ }
387
+ /**
388
+ * Инициализирует обработчики браузерных событий навигации.
389
+ * Обрабатывает: popstate (с rollback при blocked), beforeunload,
390
+ * делегирование кликов по <a>-элементам.
391
+ * SSR-safe: все browser API за проверкой typeof window.
392
+ * Вызывать только на клиенте.
393
+ */
394
+ initClientListeners() {
395
+ var _a, _b, _c;
396
+ if (typeof window === "undefined") {
397
+ return;
398
+ }
399
+ (_a = this.removePopstateListener) == null ? void 0 : _a.call(this);
400
+ (_b = this.removeBeforeunloadListener) == null ? void 0 : _b.call(this);
401
+ (_c = this.removeClickListener) == null ? void 0 : _c.call(this);
402
+ const handlePopstate = () => {
403
+ var _a2, _b2;
404
+ const to = window.location.pathname;
405
+ const from = (_b2 = (_a2 = this.current) == null ? void 0 : _a2.to) != null ? _b2 : null;
406
+ if (this.interceptors.length === 0) {
407
+ this.recordNavigation(from, to, {});
408
+ return;
409
+ }
410
+ void this.runLeaveGuards(from, to).then((result) => {
411
+ if (!result.allow) {
412
+ if (from !== null) {
413
+ history.pushState(null, "", from);
414
+ }
415
+ return;
416
+ }
417
+ this.recordNavigation(from, to, {});
418
+ }).catch((error) => {
419
+ console.error("[den] \u041E\u0448\u0438\u0431\u043A\u0430 \u0432 leave guard (popstate):", error);
420
+ });
421
+ };
422
+ const handleBeforeunload = (event) => {
423
+ if (this.interceptors.length > 0) {
424
+ event.preventDefault();
425
+ event.returnValue = "";
426
+ }
427
+ };
428
+ const handleClick = (event) => {
429
+ var _a2, _b2;
430
+ if (!isPlainLeftClick(event)) return;
431
+ const target = getNavigableAnchor(event);
432
+ if (target === null) return;
433
+ const { href } = target;
434
+ if (!href) return;
435
+ let url;
436
+ try {
437
+ url = new URL(href);
438
+ } catch (e) {
439
+ return;
440
+ }
441
+ if (url.origin !== window.location.origin) return;
442
+ if (this.interceptors.length === 0) return;
443
+ const to = url.pathname;
444
+ const from = (_b2 = (_a2 = this.current) == null ? void 0 : _a2.to) != null ? _b2 : null;
445
+ event.preventDefault();
446
+ void this.runLeaveGuards(from, to).then((result) => {
447
+ if (result.allow) {
448
+ window.dispatchEvent(new CustomEvent("den:navigate", { detail: { href } }));
449
+ }
450
+ }).catch((error) => {
451
+ console.error("[den] \u041E\u0448\u0438\u0431\u043A\u0430 \u0432 leave guard (click):", error);
452
+ });
453
+ };
454
+ window.addEventListener("popstate", handlePopstate);
455
+ window.addEventListener("beforeunload", handleBeforeunload);
456
+ document.addEventListener("click", handleClick);
457
+ this.removePopstateListener = () => window.removeEventListener("popstate", handlePopstate);
458
+ this.removeBeforeunloadListener = () => window.removeEventListener("beforeunload", handleBeforeunload);
459
+ this.removeClickListener = () => document.removeEventListener("click", handleClick);
460
+ }
461
+ };
462
+
463
+ // src/core/state-serializer.ts
464
+ var StateSerializerImpl = class {
465
+ /**
466
+ * Достаёт StateRegistry из контейнера, если он там забинжен.
467
+ * Реестр биндится как toValue (Singleton), поэтому resolve синхронен; на всякий
468
+ * случай отсекаем Promise-ветку, чтобы контракт serialize/hydrate оставался синхронным.
469
+ *
470
+ * Не использует this: сериализатор stateless (состояние живёт в контейнере), но
471
+ * остаётся классом ради биндинга по StateSerializerToken в IoC.
472
+ * @param container - Контейнер, из которого берётся реестр
473
+ */
474
+ // eslint-disable-next-line @typescript-eslint/class-methods-use-this
475
+ getRegistry(container) {
476
+ if (!container.has(StateRegistryToken)) {
477
+ return void 0;
478
+ }
479
+ const resolved = container.resolve(StateRegistryToken);
480
+ return resolved instanceof Promise ? void 0 : resolved;
481
+ }
482
+ /**
483
+ * Сериализует состояние участников контейнера в plain object.
484
+ * Включает только тех, кто объявил serialize (участник имеет toJSON).
485
+ * @param container - Контейнер, из реестра которого извлекаются данные
486
+ */
487
+ serialize(container) {
488
+ const state = {};
489
+ const registry = this.getRegistry(container);
490
+ if (registry === void 0) {
491
+ return state;
492
+ }
493
+ for (const [key, participant] of registry.all()) {
494
+ if (participant.toJSON !== void 0) {
495
+ state[key] = participant.toJSON();
496
+ }
497
+ }
498
+ return state;
499
+ }
500
+ /**
501
+ * Восстанавливает состояние участников контейнера из plain object.
502
+ * Вызывает fromJSON только у тех, чей ключ присутствует в state и кто объявил deserialize.
503
+ * @param container - Целевой контейнер
504
+ * @param state - Сериализованное состояние
505
+ */
506
+ hydrate(container, state) {
507
+ const registry = this.getRegistry(container);
508
+ if (registry === void 0) {
509
+ return;
510
+ }
511
+ for (const [key, participant] of registry.all()) {
512
+ if (key in state && participant.fromJSON !== void 0) {
513
+ participant.fromJSON(state[key]);
514
+ }
515
+ }
516
+ }
517
+ /**
518
+ * Вызывает activate(params) на всех участниках реестра контейнера параллельно.
519
+ * @param container - Контейнер, участники которого активируются
520
+ * @param params - Параметры маршрута
521
+ */
522
+ activateAll(container, params) {
523
+ return __async(this, null, function* () {
524
+ const registry = this.getRegistry(container);
525
+ if (registry === void 0) {
526
+ return;
527
+ }
528
+ yield Promise.all([...registry.all().values()].map((participant) => {
529
+ var _a;
530
+ return (_a = participant.activate) == null ? void 0 : _a.call(participant, params);
531
+ }));
532
+ });
533
+ }
534
+ };
535
+
536
+ // src/core/slot-registry.ts
537
+ var SlotRegistry = class {
538
+ constructor() {
539
+ /** Слоты по имени. Повторная регистрация с тем же именем перезаписывает. */
540
+ this.slots = /* @__PURE__ */ new Map();
541
+ }
542
+ /**
543
+ * Регистрирует слот.
544
+ * @param entry - Запись слота (имя + непрозрачный view)
545
+ */
546
+ add(entry) {
547
+ this.slots.set(entry.name, entry);
548
+ }
549
+ /**
550
+ * Возвращает слот по имени.
551
+ * @param name - Имя слота
552
+ */
553
+ get(name) {
554
+ return this.slots.get(name);
555
+ }
556
+ /**
557
+ * Возвращает все зарегистрированные слоты.
558
+ */
559
+ all() {
560
+ return [...this.slots.values()];
561
+ }
562
+ };
563
+
564
+ // src/core/state-cache.ts
565
+ var StateCache = class {
566
+ /**
567
+ * @param maxSize - Максимальное количество кэшированных страниц (по умолчанию 10)
568
+ */
569
+ constructor(maxSize = 10) {
570
+ this.cache = /* @__PURE__ */ new Map();
571
+ this.maxSize = maxSize;
572
+ }
573
+ /**
574
+ * Сохраняет сериализованный стейт страницы.
575
+ * @param routeId - Идентификатор маршрута
576
+ * @param state - Сериализованный стейт (результат toJSON() от VM)
577
+ */
578
+ set(routeId, state) {
579
+ this.cache.delete(routeId);
580
+ if (this.cache.size >= this.maxSize) {
581
+ const oldestKey = this.cache.keys().next().value;
582
+ this.cache.delete(oldestKey);
583
+ }
584
+ this.cache.set(routeId, state);
585
+ }
586
+ /**
587
+ * Возвращает закэшированный стейт страницы или undefined.
588
+ * @param routeId - Идентификатор маршрута
589
+ */
590
+ get(routeId) {
591
+ const state = this.cache.get(routeId);
592
+ if (state !== void 0) {
593
+ this.cache.delete(routeId);
594
+ this.cache.set(routeId, state);
595
+ }
596
+ return state;
597
+ }
598
+ /**
599
+ * Удаляет кэш для страницы.
600
+ * @param routeId - Идентификатор маршрута
601
+ */
602
+ delete(routeId) {
603
+ this.cache.delete(routeId);
604
+ }
605
+ /** Очищает весь кэш. */
606
+ clear() {
607
+ this.cache.clear();
608
+ }
609
+ };
610
+
611
+ // src/core/create-core.ts
612
+ var import_ioc2 = require("@foxford/ioc");
613
+ function createCore(options = {}) {
614
+ var _a;
615
+ const container = (_a = options.container) != null ? _a : new import_ioc2.Container();
616
+ const containerManager = new ContainerManagerImpl(container);
617
+ const guardRunner = new GuardRunnerImpl(container);
618
+ const navigationManager = new NavigationManagerImpl();
619
+ const stateSerializer = new StateSerializerImpl();
620
+ const stateCache = new StateCache();
621
+ container.bind(ContainerManagerToken).toValue(containerManager);
622
+ container.bind(GuardRunnerToken).toValue(guardRunner);
623
+ container.bind(NavigationManagerToken).toValue(navigationManager);
624
+ container.bind(StateSerializerToken).toValue(stateSerializer);
625
+ return {
626
+ container,
627
+ containerManager,
628
+ guardRunner,
629
+ navigationManager,
630
+ stateCache,
631
+ stateSerializer
632
+ };
633
+ }
634
+
635
+ // src/core/default-resolver.ts
636
+ var import_ioc3 = require("@foxford/ioc");
637
+ var defaultGlobalResolver = (token) => {
638
+ var _a;
639
+ let container = (0, import_ioc3.getGlobalContainer)();
640
+ while (container) {
641
+ if (container.has(token)) {
642
+ return container.resolve(token);
643
+ }
644
+ const parent = (_a = container.getParent) == null ? void 0 : _a.call(container);
645
+ if (!parent) break;
646
+ container = parent;
647
+ }
648
+ return new Promise(() => {
649
+ });
650
+ };
651
+
652
+ // src/define-service.ts
653
+ var import_ioc5 = require("@foxford/ioc");
654
+
655
+ // src/logger.ts
656
+ var import_ioc4 = require("@foxford/ioc");
657
+ var import_logger = require("@foxford/logger");
658
+ var logger = import_logger.log.getLogger("@foxford/den");
659
+ var LoggerToken = (0, import_ioc4.createToken)("den:Logger");
660
+ function scopeLogger(container) {
661
+ if (container.has(LoggerToken)) {
662
+ const resolved = container.resolve(LoggerToken);
663
+ if (!(resolved instanceof Promise)) {
664
+ return resolved;
665
+ }
666
+ }
667
+ return logger;
668
+ }
669
+ function unitLogger(container, name) {
670
+ var _a;
671
+ const base = scopeLogger(container);
672
+ return (_a = base.getLogger(name)) != null ? _a : base;
673
+ }
674
+
675
+ // src/define-service.ts
676
+ function defineService(token, options) {
677
+ const { requires, create, activate, deactivate, serialize, deserialize, scope } = options;
678
+ const requireTokens = requires != null ? requires : [];
679
+ const participatesInState = serialize !== void 0 || deserialize !== void 0;
680
+ return {
681
+ __meta: { requires: requireTokens, scope: scope != null ? scope : "app" },
682
+ __type: "service",
683
+ /**
684
+ * Регистрирует сервис в IoC-контейнере как Singleton.
685
+ * Если передана create — биндит экземпляр класса напрямую.
686
+ * Иначе — биндит Activatable-обёртку с хуками lifecycle.
687
+ * Если объявлены serialize/deserialize — при создании экземпляра сервис
688
+ * регистрируется участником в StateRegistry этого контейнера.
689
+ * @param container - Контейнер для регистрации
690
+ */
691
+ register(container) {
692
+ const unitLog = unitLogger(container, token.description);
693
+ const factory = (0, import_ioc5.inject)(requireTokens, (...args) => {
694
+ unitLog.debug(`\u0441\u043E\u0437\u0434\u0430\u043D\u0438\u0435 \u0441\u0435\u0440\u0432\u0438\u0441\u0430 (${create ? "\u0444\u0430\u0431\u0440\u0438\u043A\u0430" : "lifecycle-\u043E\u0431\u0451\u0440\u0442\u043A\u0430"})`);
695
+ let instance;
696
+ if (create) {
697
+ instance = create(args);
698
+ } else {
699
+ const service = {
700
+ activate(params) {
701
+ return activate == null ? void 0 : activate(args, params);
702
+ },
703
+ deactivate() {
704
+ return deactivate == null ? void 0 : deactivate(args);
705
+ }
706
+ };
707
+ instance = service;
708
+ }
709
+ if (participatesInState) {
710
+ unitLog.debug("\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044F \u0443\u0447\u0430\u0441\u0442\u043D\u0438\u043A\u043E\u043C StateRegistry (serialize/deserialize)");
711
+ registerStateParticipant(container, token, instance, args, serialize, deserialize);
712
+ }
713
+ return instance;
714
+ });
715
+ container.bind(token).toFactory(factory, { lifecycle: import_ioc5.Lifecycle.Singleton });
716
+ unitLog.debug(`\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044F \u0441\u0435\u0440\u0432\u0438\u0441\u0430 (scope=${scope != null ? scope : "app"}, requires=${requireTokens.length})`);
717
+ },
718
+ token
719
+ };
720
+ }
721
+ function registerStateParticipant(container, token, instance, deps, serialize, deserialize) {
722
+ if (!container.has(StateRegistryToken)) {
723
+ return;
724
+ }
725
+ const resolved = container.resolve(StateRegistryToken);
726
+ if (resolved instanceof Promise) {
727
+ return;
728
+ }
729
+ ;
730
+ resolved.add(token.description, {
731
+ activate: (params) => {
732
+ var _a;
733
+ return (_a = instance.activate) == null ? void 0 : _a.call(instance, params);
734
+ },
735
+ fromJSON: deserialize ? (data) => deserialize(deps, data) : void 0,
736
+ toJSON: serialize ? () => serialize(deps) : void 0
737
+ });
738
+ }
739
+
740
+ // src/define-view-model.ts
741
+ var import_ioc6 = require("@foxford/ioc");
742
+
743
+ // src/adapter.ts
744
+ var activeResolver = null;
745
+ function resolveDefinition(token) {
746
+ if (activeResolver === null) {
747
+ throw new Error(
748
+ `den/adapter: \u0440\u0435\u0437\u043E\u043B\u0432\u0435\u0440 \u043D\u0435 \u0443\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D \u0434\u043B\u044F \xAB${token.description}\xBB \u2014 \u043D\u0438 \u043E\u0434\u0438\u043D host-\u0430\u0434\u0430\u043F\u0442\u0435\u0440 \u043D\u0435 \u0432\u044B\u0437\u0432\u0430\u043B installResolver()`
749
+ );
750
+ }
751
+ return activeResolver(token);
752
+ }
753
+
754
+ // src/define-view-model.ts
755
+ function defineViewModel(token, options) {
756
+ const { requires, create } = options;
757
+ const requireTokens = requires != null ? requires : [];
758
+ return {
759
+ __meta: { requires: requireTokens },
760
+ __type: "view-model",
761
+ /**
762
+ * Регистрирует VM в IoC-контейнере как Singleton.
763
+ * Зависимости из requires резолвятся автоматически через inject.
764
+ * @param container - Контейнер для регистрации
765
+ */
766
+ register(container) {
767
+ const unitLog = unitLogger(container, token.description);
768
+ const factory = (0, import_ioc6.inject)(requireTokens, (...args) => {
769
+ unitLog.debug("\u0441\u043E\u0437\u0434\u0430\u043D\u0438\u0435 view-model");
770
+ return create(args);
771
+ });
772
+ container.bind(token).toFactory(factory, { lifecycle: import_ioc6.Lifecycle.Singleton });
773
+ unitLog.debug(`\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044F view-model (requires=${requireTokens.length})`);
774
+ },
775
+ /**
776
+ * Резолвит VM через стратегию активного host-адаптера.
777
+ * Вызывайте когда контейнер уже инициализирован (в render-контексте).
778
+ *
779
+ * Дескриптор не знает, откуда берётся инстанс — стратегию ставит адаптер
780
+ * через `installResolver()` (см. `@foxford/den/adapter`):
781
+ * - Vike (дефолт) — async/global: подъём по parent-chain, pending Promise при промахе;
782
+ * - Next — sync/island: контейнер острова из React-контекста.
783
+ *
784
+ * @returns Экземпляр VM или Promise с экземпляром
785
+ */
786
+ resolve() {
787
+ return resolveDefinition(token);
788
+ },
789
+ token,
790
+ /**
791
+ * Псевдоним для resolve(). Резолвит VM через стратегию активного адаптера.
792
+ * @returns Экземпляр VM или Promise с экземпляром
793
+ */
794
+ use() {
795
+ return this.resolve();
796
+ }
797
+ };
798
+ }
799
+
800
+ // src/define-guard.ts
801
+ var import_ioc7 = require("@foxford/ioc");
802
+ function defineGuard(token, options) {
803
+ const { requires, create, check } = options;
804
+ const requireTokens = requires != null ? requires : [];
805
+ return {
806
+ __meta: { requires: requireTokens },
807
+ __type: "guard",
808
+ /**
809
+ * Регистрирует Guard в IoC-контейнере.
810
+ * Если передана create — биндит через factory (Singleton) с инъецированными зависимостями.
811
+ * Иначе — биндит инлайн Guard как value.
812
+ * @param container - Контейнер для регистрации
813
+ */
814
+ register(container) {
815
+ if (create) {
816
+ const factory = (0, import_ioc7.inject)(requireTokens, (...args) => create(args));
817
+ container.bind(token).toFactory(factory, { lifecycle: import_ioc7.Lifecycle.Singleton });
818
+ } else if (check) {
819
+ const guard = { check };
820
+ container.bind(token).toValue(guard);
821
+ }
822
+ },
823
+ token
824
+ };
825
+ }
826
+
827
+ // src/define-repository.ts
828
+ var import_ioc8 = require("@foxford/ioc");
829
+ function defineRepository(token, options) {
830
+ const { requires, create, scope } = options;
831
+ const requireTokens = requires != null ? requires : [];
832
+ return {
833
+ __meta: { requires: requireTokens, scope: scope != null ? scope : "app" },
834
+ __type: "repository",
835
+ /**
836
+ * Регистрирует репозиторий в IoC-контейнере как Singleton.
837
+ * Зависимости из requires резолвятся автоматически через inject.
838
+ * @param container - Контейнер для регистрации
839
+ */
840
+ register(container) {
841
+ const unitLog = unitLogger(container, token.description);
842
+ const factory = (0, import_ioc8.inject)(requireTokens, (...args) => {
843
+ unitLog.debug("\u0441\u043E\u0437\u0434\u0430\u043D\u0438\u0435 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u044F");
844
+ return create(args);
845
+ });
846
+ container.bind(token).toFactory(factory, { lifecycle: import_ioc8.Lifecycle.Singleton });
847
+ unitLog.debug(`\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044F \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u044F (scope=${scope != null ? scope : "app"}, requires=${requireTokens.length})`);
848
+ },
849
+ token
850
+ };
851
+ }
852
+
853
+ // src/define-app.ts
854
+ function defineApp(token, options = {}) {
855
+ var _a, _b, _c;
856
+ const eager = (_a = options.eager) != null ? _a : [];
857
+ const lazy = (_b = options.lazy) != null ? _b : [];
858
+ const guards = (_c = options.guards) != null ? _c : [];
859
+ const config = { eager, guards, lazy };
860
+ return {
861
+ __meta: { eager, guards, lazy, requires: [] },
862
+ __type: "app",
863
+ /**
864
+ * Регистрирует AppConfig как value в IoC-контейнере.
865
+ * Позволяет runtime получать конфигурацию приложения через контейнер.
866
+ * @param container - Контейнер для регистрации
867
+ */
868
+ register(container) {
869
+ container.bind(token).toValue(config);
870
+ },
871
+ token
872
+ };
873
+ }
874
+
875
+ // src/define-extension.ts
876
+ var import_ioc9 = require("@foxford/ioc");
877
+ function defineExtension(token, options = {}) {
878
+ const { requires, activate, deactivate } = options;
879
+ const requireTokens = requires != null ? requires : [];
880
+ return {
881
+ __meta: { requires: requireTokens },
882
+ __type: "extension",
883
+ /**
884
+ * Регистрирует ExtensionConfig как Singleton в IoC-контейнере.
885
+ * Создаёт Activatable-обёртку с хуками lifecycle.
886
+ * @param container - Контейнер для регистрации
887
+ */
888
+ register(container) {
889
+ const factory = (0, import_ioc9.inject)(requireTokens, (...args) => {
890
+ const config = {
891
+ __extensionConfig: true,
892
+ activate() {
893
+ return activate == null ? void 0 : activate(args);
894
+ },
895
+ deactivate() {
896
+ return deactivate == null ? void 0 : deactivate(args);
897
+ }
898
+ };
899
+ return config;
900
+ });
901
+ container.bind(token).toFactory(factory, { lifecycle: import_ioc9.Lifecycle.Singleton });
902
+ },
903
+ token
904
+ };
905
+ }
906
+
907
+ // src/define-action-guard.ts
908
+ var import_ioc10 = require("@foxford/ioc");
909
+ function defineActionGuard(token, options) {
910
+ const { requires, intercept } = options;
911
+ const requireTokens = requires != null ? requires : [];
912
+ return {
913
+ __meta: { requires: requireTokens },
914
+ __type: "action-guard",
915
+ /**
916
+ * Регистрирует ActionGuard в IoC-контейнере как Singleton.
917
+ * Зависимости резолвятся автоматически через inject.
918
+ * @param container - Контейнер для регистрации
919
+ */
920
+ register(container) {
921
+ const factory = (0, import_ioc10.inject)(requireTokens, (...args) => {
922
+ const guard = {
923
+ intercept(next) {
924
+ return intercept(args, next);
925
+ }
926
+ };
927
+ return guard;
928
+ });
929
+ container.bind(token).toFactory(factory, { lifecycle: import_ioc10.Lifecycle.Singleton });
930
+ },
931
+ token
932
+ };
933
+ }
934
+
935
+ // src/define-layout.ts
936
+ function defineLayout(token, options = {}) {
937
+ var _a, _b;
938
+ const viewModels = (_a = options.viewModels) != null ? _a : [];
939
+ const slots = (_b = options.slots) != null ? _b : {};
940
+ const config = { slots, viewModels };
941
+ return {
942
+ __meta: { requires: [], slots, viewModels },
943
+ __type: "layout",
944
+ /**
945
+ * Регистрирует LayoutConfig как value в IoC-контейнере.
946
+ * Позволяет runtime получать конфигурацию layout через контейнер.
947
+ * @param container - Контейнер для регистрации
948
+ */
949
+ register(container) {
950
+ container.bind(token).toValue(config);
951
+ },
952
+ token
953
+ };
954
+ }
955
+
956
+ // src/define-slot.ts
957
+ function defineSlot({ name, view, units = [] }) {
958
+ return {
959
+ __type: "slot",
960
+ name,
961
+ register(container) {
962
+ const slotLog = unitLogger(container, `slot:${name}`);
963
+ for (const unit of units) {
964
+ unit.register(container);
965
+ }
966
+ const registry = container.has(SlotRegistryToken) ? container.resolve(SlotRegistryToken) : (() => {
967
+ const created = new SlotRegistry();
968
+ container.bind(SlotRegistryToken).toValue(created);
969
+ slotLog.debug("\u0441\u043E\u0437\u0434\u0430\u043D SlotRegistry \u043A\u043E\u043D\u0442\u0435\u0439\u043D\u0435\u0440\u0430");
970
+ return created;
971
+ })();
972
+ registry.add({ name, view });
973
+ slotLog.debug(`\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044F \u0441\u043B\u043E\u0442\u0430 (units=${units.length})`);
974
+ },
975
+ units,
976
+ view
977
+ };
978
+ }
979
+
980
+ // src/define-layer.ts
981
+ function defineLayer(token, options) {
982
+ const { name, adapter } = options;
983
+ const config = { adapter, name };
984
+ return {
985
+ __meta: { name, requires: [] },
986
+ __type: "layer",
987
+ /**
988
+ * Регистрирует LayerConfig как value в IoC-контейнере.
989
+ * Runtime использует конфиг для инициализации view-адаптера.
990
+ * @param container - Контейнер для регистрации
991
+ */
992
+ register(container) {
993
+ container.bind(token).toValue(config);
994
+ },
995
+ token
996
+ };
997
+ }
998
+ // Annotate the CommonJS export names for ESM import in node:
999
+ 0 && (module.exports = {
1000
+ ActivatableToken,
1001
+ ContainerManagerImpl,
1002
+ ContainerManagerToken,
1003
+ GuardRunnerImpl,
1004
+ GuardRunnerToken,
1005
+ NavigationManagerImpl,
1006
+ NavigationManagerToken,
1007
+ RequestContextToken,
1008
+ SlotRegistry,
1009
+ SlotRegistryToken,
1010
+ StateCache,
1011
+ StateRegistry,
1012
+ StateRegistryToken,
1013
+ StateSerializerImpl,
1014
+ StateSerializerToken,
1015
+ createCore,
1016
+ defaultGlobalResolver,
1017
+ defineActionGuard,
1018
+ defineApp,
1019
+ defineExtension,
1020
+ defineGuard,
1021
+ defineLayer,
1022
+ defineLayout,
1023
+ defineRepository,
1024
+ defineService,
1025
+ defineSlot,
1026
+ defineViewModel
1027
+ });