@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.
@@ -0,0 +1,415 @@
1
+ import { Token, Container } from '@foxford/ioc';
2
+
3
+ /**
4
+ * Интерфейс для объектов с жизненным циклом страницы.
5
+ * Реализуется сервисами и ViewModel, чтобы Core мог управлять их lifecycle.
6
+ */
7
+ interface Activatable {
8
+ /**
9
+ * Вызывается при активации страницы (SSR и клиент).
10
+ * @param params - Параметры маршрута (например, { id: '42' })
11
+ */
12
+ activate(params: Record<string, string>): void | Promise<void>;
13
+ /**
14
+ * Вызывается только на клиенте после hydration (опционально).
15
+ * @param params - Параметры маршрута
16
+ */
17
+ activateClient?(params: Record<string, string>): void | Promise<void>;
18
+ /**
19
+ * Вызывается при уходе со страницы (опционально).
20
+ */
21
+ deactivate?(): void | Promise<void>;
22
+ }
23
+
24
+ /**
25
+ * Запись слота в реестре: имя + непрозрачная ссылка на view.
26
+ *
27
+ * `view` намеренно `unknown` — ядро не знает, что это компонент React/Vue/…: рендерит
28
+ * его view-адаптер (например `Slot` из `@foxford/den-react`). Так слот host-агностичен.
29
+ */
30
+ interface SlotEntry {
31
+ /** Имя слота — по нему оркестратор решает, чем его заполнить. */
32
+ name: string;
33
+ /** Непрозрачная ссылка на view слота (компонент view-фреймворка). */
34
+ view: unknown;
35
+ }
36
+ /**
37
+ * Per-container реестр слотов.
38
+ *
39
+ * `defineSlot(...).register(container)` кладёт слот сюда; оркестратор (например
40
+ * федерация core-app) резолвит реестр из контейнера и перечисляет все зарегистрированные
41
+ * слоты — без сканирования исходников, чистый DI. Живёт столько же, сколько контейнер.
42
+ */
43
+ declare class SlotRegistry {
44
+ /** Слоты по имени. Повторная регистрация с тем же именем перезаписывает. */
45
+ private readonly slots;
46
+ /**
47
+ * Регистрирует слот.
48
+ * @param entry - Запись слота (имя + непрозрачный view)
49
+ */
50
+ add(entry: SlotEntry): void;
51
+ /**
52
+ * Возвращает слот по имени.
53
+ * @param name - Имя слота
54
+ */
55
+ get(name: string): SlotEntry | undefined;
56
+ /**
57
+ * Возвращает все зарегистрированные слоты.
58
+ */
59
+ all(): readonly SlotEntry[];
60
+ }
61
+
62
+ /**
63
+ * Кэш сериализованных стейтов страниц.
64
+ * Используется для восстановления состояния при возврате на ранее посещённую страницу.
65
+ * Реализует LRU-стратегию с ограничением по количеству записей.
66
+ */
67
+ declare class StateCache {
68
+ private cache;
69
+ private readonly maxSize;
70
+ /**
71
+ * @param maxSize - Максимальное количество кэшированных страниц (по умолчанию 10)
72
+ */
73
+ constructor(maxSize?: number);
74
+ /**
75
+ * Сохраняет сериализованный стейт страницы.
76
+ * @param routeId - Идентификатор маршрута
77
+ * @param state - Сериализованный стейт (результат toJSON() от VM)
78
+ */
79
+ set(routeId: string, state: Record<string, unknown>): void;
80
+ /**
81
+ * Возвращает закэшированный стейт страницы или undefined.
82
+ * @param routeId - Идентификатор маршрута
83
+ */
84
+ get(routeId: string): Record<string, unknown> | undefined;
85
+ /**
86
+ * Удаляет кэш для страницы.
87
+ * @param routeId - Идентификатор маршрута
88
+ */
89
+ delete(routeId: string): void;
90
+ /** Очищает весь кэш. */
91
+ clear(): void;
92
+ }
93
+
94
+ /**
95
+ * Участник сериализации состояния — сервис, объявивший serialize/deserialize.
96
+ * Методы замкнуты на разрешённые зависимости сервиса и на его экземпляр,
97
+ * поэтому сериализатору не нужно повторно резолвить токены.
98
+ */
99
+ interface StateParticipant {
100
+ /** Возвращает сериализуемое состояние (если сервис объявил serialize). */
101
+ toJSON?(): unknown;
102
+ /** Восстанавливает состояние из данных (если сервис объявил deserialize). */
103
+ fromJSON?(data: unknown): void;
104
+ /** Активирует сервис при входе на страницу (lifecycle-хук activate). */
105
+ activate?(params: Record<string, string>): void | Promise<void>;
106
+ }
107
+ /**
108
+ * Реестр участников сериализации в рамках одного контейнера.
109
+ *
110
+ * Живёт ровно столько, сколько контейнер (page/request), в который он забинжен
111
+ * контейнер-менеджером. Уничтожается вместе с контейнером через dispose — поэтому
112
+ * ссылки на страничные сервисы не утекают в app-scope, в отличие от прежнего
113
+ * app-scope Map в StateSerializerImpl.
114
+ */
115
+ declare class StateRegistry {
116
+ /** Участники: стабильный ключ (token.description) → участник. */
117
+ private readonly participants;
118
+ /**
119
+ * Регистрирует участника сериализации.
120
+ * Повторная регистрация с тем же ключом перезаписывает предыдущего.
121
+ * @param key - Стабильный ключ (обычно token.description)
122
+ * @param participant - Участник с хуками toJSON/fromJSON/activate
123
+ */
124
+ add(key: string, participant: StateParticipant): void;
125
+ /**
126
+ * Возвращает всех зарегистрированных участников (ключ → участник).
127
+ */
128
+ all(): ReadonlyMap<string, StateParticipant>;
129
+ }
130
+
131
+ /**
132
+ * Контекст HTTP-запроса — заголовки и cookies, переданные сервером.
133
+ * На клиенте содержит пустые объекты.
134
+ */
135
+ interface RequestContext {
136
+ /** HTTP-заголовки запроса (в нижнем регистре) */
137
+ headers: Record<string, string>;
138
+ /** Распарсенные cookies из заголовка Cookie */
139
+ cookies: Record<string, string>;
140
+ }
141
+ /**
142
+ * Результат проверки guard.
143
+ * - `{ allowed: true }` — переход разрешён
144
+ * - `{ allowed: false, redirect: string }` — переход запрещён, перенаправить по redirect
145
+ */
146
+ type GuardResult = {
147
+ allowed: true;
148
+ } | {
149
+ allowed: false;
150
+ redirect: string;
151
+ };
152
+ /**
153
+ * Guard — функция проверки доступа к странице.
154
+ * Вызывается до активации страницы.
155
+ */
156
+ interface Guard {
157
+ /**
158
+ * Проверяет доступ к маршруту.
159
+ * @param params - Параметры маршрута
160
+ * @returns Результат проверки
161
+ */
162
+ check(params: Record<string, string>): GuardResult | Promise<GuardResult>;
163
+ }
164
+ /**
165
+ * Запись в истории навигации.
166
+ */
167
+ interface NavigationEntry {
168
+ /** Маршрут, с которого произошёл переход */
169
+ from: string | null;
170
+ /** Маршрут, на который произошёл переход */
171
+ to: string;
172
+ /** Параметры маршрута назначения */
173
+ params: Record<string, string>;
174
+ /** Временная метка перехода (ms) */
175
+ timestamp: number;
176
+ }
177
+ /**
178
+ * Менеджер дочерних контейнеров для страниц и запросов.
179
+ */
180
+ interface ContainerManager {
181
+ /**
182
+ * Возвращает app-контейнер (синглтон).
183
+ */
184
+ getAppContainer(): Container;
185
+ /**
186
+ * Возвращает существующий дочерний контейнер страницы или undefined.
187
+ * @param routeId - Идентификатор маршрута
188
+ */
189
+ getPageContainer(routeId: string): Container | undefined;
190
+ /**
191
+ * Создаёт дочерний контейнер для страницы.
192
+ * Если контейнер для данного routeId уже существует, возвращает его.
193
+ * @param routeId - Идентификатор маршрута
194
+ */
195
+ createPageContainer(routeId: string): Container;
196
+ /**
197
+ * Уничтожает дочерний контейнер страницы и освобождает ресурсы.
198
+ * @param routeId - Идентификатор маршрута
199
+ */
200
+ destroyPageContainer(routeId: string): Promise<void>;
201
+ /**
202
+ * Создаёт дочерний контейнер для HTTP-запроса с забинженным RequestContext.
203
+ * Если контейнер для данного requestId уже существует, возвращает существующий.
204
+ * @param requestId - Уникальный идентификатор запроса
205
+ * @param context - Контекст запроса (заголовки и cookies)
206
+ */
207
+ createRequestContainer(requestId: string, context: RequestContext): Container;
208
+ /**
209
+ * Возвращает существующий контейнер запроса или undefined.
210
+ * @param requestId - Уникальный идентификатор запроса
211
+ */
212
+ getRequestContainer(requestId: string): Container | undefined;
213
+ /**
214
+ * Уничтожает контейнер запроса и освобождает ресурсы.
215
+ * Если контейнер не найден — no-op.
216
+ * @param requestId - Уникальный идентификатор запроса
217
+ */
218
+ destroyRequestContainer(requestId: string): Promise<void>;
219
+ /**
220
+ * Создаёт page container для SSR как дочерний от заданного родителя (обычно request container).
221
+ * Отслеживается менеджером — может быть уничтожен через destroySsrPageContainer после рендера.
222
+ * Если контейнер для данного requestId уже существует, возвращает существующий.
223
+ * @param requestId - Уникальный идентификатор запроса
224
+ * @param parent - Родительский контейнер
225
+ */
226
+ createSsrPageContainer(requestId: string, parent: Container): Container;
227
+ /**
228
+ * Уничтожает SSR page container и освобождает ресурсы (Disposable VM, сервисы).
229
+ * Вызывается в onAfterRenderHtml после завершения SSR-рендера.
230
+ * Если контейнер не найден — no-op.
231
+ * @param requestId - Уникальный идентификатор запроса
232
+ */
233
+ destroySsrPageContainer(requestId: string): Promise<void>;
234
+ }
235
+ /**
236
+ * Запускатель guards для страниц.
237
+ */
238
+ interface GuardRunner {
239
+ /**
240
+ * Параллельно выполняет все guards и возвращает их результаты.
241
+ * Если передан container — guards резолвятся из него (request-scope).
242
+ * Без container — guards резолвятся из app-контейнера (поведение по умолчанию).
243
+ * @param tokens - Токены guards
244
+ * @param params - Параметры маршрута
245
+ * @param container - Опциональный контейнер для резолвинга guards (например, request container)
246
+ */
247
+ runGuards(tokens: Token<Guard>[], params: Record<string, string>, container?: Container): Promise<GuardResult[]>;
248
+ }
249
+ /**
250
+ * Объект, поддерживающий освобождение ресурсов.
251
+ * Возвращается методами регистрации для отмены подписки.
252
+ */
253
+ interface Disposable {
254
+ /** Освобождает ресурсы и отменяет регистрацию. */
255
+ dispose(): void;
256
+ }
257
+ /**
258
+ * Перехватчик навигации — вызывается перед каждым переходом (leave guard).
259
+ */
260
+ interface NavigationInterceptor {
261
+ /**
262
+ * Проверяет, разрешён ли переход.
263
+ * @param from - Текущий маршрут (откуда), null при первом переходе
264
+ * @param to - Целевой маршрут (куда)
265
+ * @returns Объект с результатом проверки
266
+ */
267
+ beforeNavigate(from: string | null, to: string): Promise<{
268
+ allow: boolean;
269
+ reason?: string;
270
+ }>;
271
+ }
272
+ /**
273
+ * Результат проверки leave guards.
274
+ * - `{ allow: true }` — переход разрешён
275
+ * - `{ allow: false, reason? }` — переход заблокирован
276
+ */
277
+ type LeaveGuardResult = {
278
+ allow: true;
279
+ } | {
280
+ allow: false;
281
+ reason?: string;
282
+ };
283
+ /**
284
+ * Менеджер навигации — хранит историю переходов и оповещает подписчиков.
285
+ */
286
+ interface NavigationManager {
287
+ /** История переходов (не изменяется снаружи) */
288
+ readonly history: NavigationEntry[];
289
+ /** Текущая запись навигации */
290
+ current: NavigationEntry | null;
291
+ /**
292
+ * Подписывается на событие навигации.
293
+ * @param handler - Обработчик события
294
+ * @returns Функция отписки
295
+ */
296
+ onNavigate(handler: (entry: NavigationEntry) => void): () => void;
297
+ /**
298
+ * Записывает переход и оповещает подписчиков.
299
+ * @param from - Маршрут отправления
300
+ * @param to - Маршрут назначения
301
+ * @param params - Параметры маршрута
302
+ */
303
+ recordNavigation(from: string | null, to: string, params: Record<string, string>): void;
304
+ /**
305
+ * Проверяет актуальность текущего состояния (stub: всегда true).
306
+ */
307
+ checkFreshness(): Promise<boolean>;
308
+ /**
309
+ * Инициализирует обработчики браузерных событий навигации (только на клиенте).
310
+ */
311
+ initClientListeners(): void;
312
+ /**
313
+ * Регистрирует interceptor для перехвата навигации (leave guard).
314
+ * @param handler - Перехватчик навигации
315
+ * @returns Disposable для отмены регистрации
316
+ */
317
+ intercept(handler: NavigationInterceptor): Disposable;
318
+ /**
319
+ * Запускает все зарегистрированные leave guards последовательно.
320
+ * При первом заблокированном — возвращает `{ allow: false, reason? }`.
321
+ * @param from - Маршрут отправления
322
+ * @param to - Маршрут назначения
323
+ * @returns Результат проверки
324
+ */
325
+ runLeaveGuards(from: string | null, to: string): Promise<LeaveGuardResult>;
326
+ }
327
+ /**
328
+ * Сериализатор состояния для SSR.
329
+ * Протокол: plain objects.
330
+ *
331
+ * Не хранит состояние сам — работает поверх StateRegistry конкретного контейнера
332
+ * (page/request). Участники сериализации регистрируются в реестр контейнера через
333
+ * defineService({ serialize, deserialize }) при создании экземпляра сервиса, поэтому
334
+ * их время жизни ограничено контейнером и утечки в app-scope не происходит.
335
+ */
336
+ interface StateSerializer {
337
+ /**
338
+ * Сериализует состояние участников из StateRegistry контейнера в plain object.
339
+ * Ключ записи — token.description сервиса.
340
+ * @param container - Контейнер, из реестра которого извлекаются данные
341
+ */
342
+ serialize(container: Container): Record<string, unknown>;
343
+ /**
344
+ * Восстанавливает состояние участников из plain object в контейнер.
345
+ * Вызывает deserialize только у тех участников, чей ключ есть в state.
346
+ * @param container - Целевой контейнер
347
+ * @param state - Сериализованное состояние
348
+ */
349
+ hydrate(container: Container, state: Record<string, unknown>): void;
350
+ /**
351
+ * Вызывает activate(params) на всех участниках реестра контейнера параллельно.
352
+ * Используется в Vike-хуках для активации страничных сервисов при входе на страницу.
353
+ * @param container - Контейнер, участники которого активируются
354
+ * @param params - Параметры маршрута (например, { id: '42' })
355
+ */
356
+ activateAll(container: Container, params: Record<string, string>): Promise<void>;
357
+ }
358
+ /**
359
+ * Основной объект Core Runtime.
360
+ * Создаётся фабрикой createCore и предоставляет доступ ко всем менеджерам.
361
+ */
362
+ interface Core {
363
+ /** App-контейнер (IoC) */
364
+ container: Container;
365
+ /** Менеджер контейнеров страниц */
366
+ containerManager: ContainerManager;
367
+ /** Запускатель guards */
368
+ guardRunner: GuardRunner;
369
+ /** Менеджер навигации */
370
+ navigationManager: NavigationManager;
371
+ /** Кэш стейтов страниц для state persistence при back-навигации */
372
+ stateCache: StateCache;
373
+ /** Сериализатор состояния */
374
+ stateSerializer: StateSerializer;
375
+ }
376
+ /**
377
+ * Токен для регистрации ContainerManager в IoC-контейнере.
378
+ */
379
+ declare const ContainerManagerToken: Token<ContainerManager>;
380
+ /**
381
+ * Токен для регистрации GuardRunner в IoC-контейнере.
382
+ */
383
+ declare const GuardRunnerToken: Token<GuardRunner>;
384
+ /**
385
+ * Токен для регистрации NavigationManager в IoC-контейнере.
386
+ */
387
+ declare const NavigationManagerToken: Token<NavigationManager>;
388
+ /**
389
+ * Токен для регистрации StateSerializer в IoC-контейнере.
390
+ */
391
+ declare const StateSerializerToken: Token<StateSerializer>;
392
+ /**
393
+ * Токен per-container реестра участников сериализации.
394
+ * Свежий StateRegistry биндится контейнер-менеджером в каждый page/request-контейнер
395
+ * и уничтожается вместе с ним.
396
+ */
397
+ declare const StateRegistryToken: Token<StateRegistry>;
398
+ /**
399
+ * Токен per-container реестра слотов.
400
+ * `defineSlot(...).register(container)` кладёт слот сюда (get-or-create), оркестратор
401
+ * резолвит реестр и перечисляет зарегистрированные слоты.
402
+ */
403
+ declare const SlotRegistryToken: Token<SlotRegistry>;
404
+ /**
405
+ * Токен для регистрации Activatable-объектов в контейнере.
406
+ */
407
+ declare const ActivatableToken: Token<Activatable>;
408
+ /**
409
+ * Токен для доступа к RequestContext из IoC-контейнера.
410
+ * На сервере содержит headers/cookies текущего HTTP-запроса.
411
+ * На клиенте — пустые объекты.
412
+ */
413
+ declare const RequestContextToken: Token<RequestContext>;
414
+
415
+ export { type Activatable as A, type Core as C, type Disposable as D, type GuardRunner as G, type LeaveGuardResult as L, type NavigationManager as N, type RequestContext as R, type StateSerializer as S, type ContainerManager as a, type Guard as b, type GuardResult as c, type NavigationEntry as d, type NavigationInterceptor as e, ActivatableToken as f, ContainerManagerToken as g, GuardRunnerToken as h, NavigationManagerToken as i, RequestContextToken as j, type SlotEntry as k, SlotRegistry as l, SlotRegistryToken as m, StateCache as n, type StateParticipant as o, StateRegistry as p, StateRegistryToken as q, StateSerializerToken as r };