itd-api 0.7.2 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +72 -27
- package/dist/events/index.cjs +53 -0
- package/dist/events/index.d.cts +3 -0
- package/dist/events/index.d.ts +3 -0
- package/dist/events/index.js +5 -0
- package/dist/index.cjs +234 -200
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +70 -64
- package/dist/index.d.ts +70 -64
- package/dist/index.js +202 -179
- package/dist/index.js.map +1 -1
- package/dist/node/index.cjs +6 -5
- package/dist/node/index.cjs.map +1 -1
- package/dist/node/index.d.cts +2 -2
- package/dist/node/index.d.ts +2 -2
- package/dist/node/index.js +5 -4
- package/dist/node/index.js.map +1 -1
- package/dist/rest/index.cjs +32 -26
- package/dist/rest/index.cjs.map +1 -1
- package/dist/rest/index.d.cts +9 -7
- package/dist/rest/index.d.ts +9 -7
- package/dist/rest/index.js +19 -21
- package/dist/rest/index.js.map +1 -1
- package/dist/shared/{errors-DfU8M5eS.cjs → errors-BmP3TKoW.cjs} +3 -3
- package/dist/shared/{errors-DfU8M5eS.cjs.map → errors-BmP3TKoW.cjs.map} +1 -1
- package/dist/shared/{errors-Bhrd2fJd.js → errors-GI10kZxk.js} +3 -3
- package/dist/shared/{errors-Bhrd2fJd.js.map → errors-GI10kZxk.js.map} +1 -1
- package/dist/shared/{websocket-BLR8eVJV.js → events-C5wnTXPT.js} +770 -490
- package/dist/shared/events-C5wnTXPT.js.map +1 -0
- package/dist/shared/events-CqtkP65d.d.ts +698 -0
- package/dist/shared/events-DGAWlCaI.d.cts +698 -0
- package/dist/shared/{websocket-C_eI4H2o.cjs → events-FZfnEez0.cjs} +834 -518
- package/dist/shared/events-FZfnEez0.cjs.map +1 -0
- package/dist/shared/{storage-BPJR_k4-.cjs → key-value-store-B5GYVEYZ.cjs} +2 -86
- package/dist/shared/key-value-store-B5GYVEYZ.cjs.map +1 -0
- package/dist/shared/{storage-D86edNCB.js → key-value-store-Bt2PSMxY.js} +3 -69
- package/dist/shared/key-value-store-Bt2PSMxY.js.map +1 -0
- package/dist/shared/key-value-store-COZaNHZS.d.cts +69 -0
- package/dist/shared/key-value-store-COZaNHZS.d.ts +69 -0
- package/dist/shared/{multi-storage-DccjD7Ww.d.ts → multi-storage-BEL7tcKn.d.cts} +3 -2
- package/dist/shared/{multi-storage--yTEqiod.cjs → multi-storage-CDKLa956.cjs} +11 -10
- package/dist/shared/{multi-storage--yTEqiod.cjs.map → multi-storage-CDKLa956.cjs.map} +1 -1
- package/dist/shared/{multi-storage-CjAPB5Kq.d.cts → multi-storage-LkljOSzA.d.ts} +3 -2
- package/dist/shared/{multi-storage-CkvTUC5m.js → multi-storage-SoW05uzO.js} +5 -4
- package/dist/shared/{multi-storage-CkvTUC5m.js.map → multi-storage-SoW05uzO.js.map} +1 -1
- package/dist/shared/{options-Dg5N3r1V.cjs → options-DOJYtoti.cjs} +8 -6
- package/dist/shared/options-DOJYtoti.cjs.map +1 -0
- package/dist/shared/{options-DtATYdLr.js → options-DhgGg7Ms.js} +8 -6
- package/dist/shared/options-DhgGg7Ms.js.map +1 -0
- package/dist/shared/{cookies-DZwFq6kr.cjs → redact-BP00URmQ.cjs} +312 -2
- package/dist/shared/redact-BP00URmQ.cjs.map +1 -0
- package/dist/shared/{cookies-tX2sNwxb.js → redact-Ba5iSVYZ.js} +235 -3
- package/dist/shared/redact-Ba5iSVYZ.js.map +1 -0
- package/dist/shared/{render-CgwKdOzu.d.ts → render-B4qis7Sb.d.ts} +775 -313
- package/dist/shared/{render-DO0F5YSm.d.cts → render-DDwy2Ame.d.cts} +775 -313
- package/dist/shared/{render-mcuELiYi.cjs → render-Dqi8Owqk.cjs} +1464 -662
- package/dist/shared/render-Dqi8Owqk.cjs.map +1 -0
- package/dist/shared/{render-C6HRPs10.js → render-hiWFh2s4.js} +1366 -618
- package/dist/shared/render-hiWFh2s4.js.map +1 -0
- package/dist/shared/storage-BQrtKon9.d.cts +86 -0
- package/dist/shared/storage-CTRGT5wF.d.ts +86 -0
- package/dist/shared/storage-CfHKzVHf.js +71 -0
- package/dist/shared/storage-CfHKzVHf.js.map +1 -0
- package/dist/shared/storage-QNVxzpDB.cjs +88 -0
- package/dist/shared/storage-QNVxzpDB.cjs.map +1 -0
- package/dist/shared/{url-BaMCQpYH.cjs → url-Bsb3xE5U.cjs} +577 -466
- package/dist/shared/url-Bsb3xE5U.cjs.map +1 -0
- package/dist/shared/{url-IU0xN9wX.js → url-DP33mp3s.js} +508 -409
- package/dist/shared/url-DP33mp3s.js.map +1 -0
- package/dist/shared/{url-DTfZ2toq.d.ts → url-XBsMdAcv.d.cts} +1313 -1200
- package/dist/shared/{url-DTfZ2toq.d.cts → url-XBsMdAcv.d.ts} +1313 -1200
- package/dist/web/index.cjs +5 -4
- package/dist/web/index.cjs.map +1 -1
- package/dist/web/index.d.cts +1 -1
- package/dist/web/index.d.ts +1 -1
- package/dist/web/index.js +3 -2
- package/dist/web/index.js.map +1 -1
- package/package.json +16 -15
- package/dist/realtime/index.cjs +0 -165
- package/dist/realtime/index.cjs.map +0 -1
- package/dist/realtime/index.d.cts +0 -51
- package/dist/realtime/index.d.ts +0 -51
- package/dist/realtime/index.js +0 -120
- package/dist/realtime/index.js.map +0 -1
- package/dist/shared/auth-provider-CG8oCQ9F.cjs +0 -108
- package/dist/shared/auth-provider-CG8oCQ9F.cjs.map +0 -1
- package/dist/shared/auth-provider-mYqxsSVa.js +0 -91
- package/dist/shared/auth-provider-mYqxsSVa.js.map +0 -1
- package/dist/shared/cookies-DZwFq6kr.cjs.map +0 -1
- package/dist/shared/cookies-tX2sNwxb.js.map +0 -1
- package/dist/shared/options-Dg5N3r1V.cjs.map +0 -1
- package/dist/shared/options-DtATYdLr.js.map +0 -1
- package/dist/shared/render-C6HRPs10.js.map +0 -1
- package/dist/shared/render-mcuELiYi.cjs.map +0 -1
- package/dist/shared/storage-BPJR_k4-.cjs.map +0 -1
- package/dist/shared/storage-C_eICCep.d.cts +0 -152
- package/dist/shared/storage-C_eICCep.d.ts +0 -152
- package/dist/shared/storage-D86edNCB.js.map +0 -1
- package/dist/shared/url-BaMCQpYH.cjs.map +0 -1
- package/dist/shared/url-IU0xN9wX.js.map +0 -1
- package/dist/shared/websocket-BLR8eVJV.js.map +0 -1
- package/dist/shared/websocket-C_eI4H2o.cjs.map +0 -1
- package/dist/shared/websocket-DF7XIMiX.d.cts +0 -562
- package/dist/shared/websocket-DYKBr8HF.d.ts +0 -562
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"websocket-BLR8eVJV.js","names":["#listeners","#onError","#options","#hooks","#middleware","#handlers","#queue","#activeKeys","#drainWaiters","#keysFor","#pump","#resolveDrain","#active","#run","#deps","#options","#dispatcher","#emitter","#clock","#maxAttempts","#reportDispatchError","#handleOverflow","#status","#wanted","#generation","#starting","#attachEnvironmentListeners","#sync","#isCurrentGeneration","#run","#cancelTimer","#detachEnvironment","#controller","#attempt","#setStatus","#scheduleReconnect","#dispatch","#isCurrentConnection","#handleEvent","#emitEngine","#handleFailure","#refreshAndReconnect","#giveUp","#reconnect","#interval","#limit","#clock","#readUpdates","#readCount","#wait","#idleTimeout","#handshakeTimeout","#clock","#lastEventId","#handshake","#read","#deps","#engine","#handleFrame","#deliver","#syncUnreadCount","#selector","#routes","#fallback","#registration","#captureMiddleware","#middleware","#captureMiddleware","#path","#webSocketImpl","#auth","#idleTimeout","#keepAlive","#handshakeTimeout","#classifyOpenFailure","#clock","#globalImplementation","#urls","#listen"],"sources":["../../src/core/emitter.ts","../../src/realtime/middleware.ts","../../src/realtime/reconnect.ts","../../src/realtime/transports/transport.ts","../../src/realtime/updates.ts","../../src/realtime/engine.ts","../../src/realtime/transports/poll.ts","../../node_modules/eventsource-parser/dist/index.js","../../src/realtime/transports/sse.ts","../../src/realtime/stream.ts","../../src/realtime/router.ts","../../src/realtime/composer.ts","../../src/realtime/transports/websocket.ts"],"sourcesContent":["import type { Logger } from './options.js';\n\n/** Обработчик события. */\nexport type Listener<T> = (payload: T) => void;\n\n/** Функция отписки, которую возвращает подписка на событие. */\nexport type Unsubscribe = () => void;\n\n/**\n * Минимальный типизированный источник событий.\n *\n * Своя реализация вместо `EventTarget` и `EventEmitter`: первый есть не везде и требует\n * обёрток `CustomEvent`, второй существует только в Node. Нужны ровно подписка и рассылка.\n *\n * Исключение в обработчике не прерывает рассылку остальным и не роняет библиотеку.\n *\n * @typeParam Events карта «имя события → тип полезной нагрузки». Задаётся интерфейсом,\n * поэтому ограничение на индексную сигнатуру намеренно не накладывается.\n */\nexport class Emitter<Events> {\n readonly #listeners = new Map<keyof Events, Set<Listener<never>>>();\n readonly #onError: ((error: unknown) => void) | undefined;\n\n constructor(onListenerError?: (error: unknown) => void) {\n this.#onError = onListenerError;\n }\n\n /**\n * Подписывается на событие.\n *\n * @returns функция отписки\n *\n * @example\n * ```ts\n * const off = realtime.on('notification', (event) => console.log(event));\n * off();\n * ```\n */\n on<K extends keyof Events>(event: K, listener: Listener<Events[K]>): Unsubscribe {\n const set = this.#listeners.get(event) ?? new Set();\n set.add(listener as Listener<never>);\n this.#listeners.set(event, set);\n\n return () => this.off(event, listener);\n }\n\n /** Подписывается на одно срабатывание. */\n once<K extends keyof Events>(event: K, listener: Listener<Events[K]>): Unsubscribe {\n const off = this.on(event, (payload) => {\n off();\n listener(payload);\n });\n return off;\n }\n\n /** Отписывается от события. */\n off<K extends keyof Events>(event: K, listener: Listener<Events[K]>): void {\n this.#listeners.get(event)?.delete(listener as Listener<never>);\n }\n\n /** Рассылает событие подписчикам. */\n emit<K extends keyof Events>(event: K, payload: Events[K]): void {\n const set = this.#listeners.get(event);\n if (!set) return;\n\n // Копия нужна: обработчик вправе отписаться прямо во время рассылки.\n for (const listener of [...set]) {\n try {\n (listener as Listener<Events[K]>)(payload);\n } catch (error) {\n this.#onError?.(error);\n }\n }\n }\n\n /** Сколько подписчиков у события. */\n listenerCount(event: keyof Events): number {\n return this.#listeners.get(event)?.size ?? 0;\n }\n\n /** Снимает все подписки. */\n removeAllListeners(): void {\n this.#listeners.clear();\n }\n}\n\n/**\n * Сообщает об исключении из пользовательского обработчика события: пишет в логгер,\n * при его отсутствии — в консоль.\n *\n * Ошибка чужого обработчика не должна ронять библиотеку, но и пропадать молча ей нельзя:\n * иначе подписка, падающая на каждом событии, выглядит как её отсутствие.\n *\n * @param scope о чьих событиях речь — попадает в текст сообщения\n *\n * @internal\n */\nexport function reportListenerError(\n logger: Logger | undefined,\n scope: string,\n error: unknown,\n): void {\n const message = `Ошибка в обработчике события ${scope}`;\n if (logger) logger.error(message, error);\n else console.error(`[itd-api] ${message}`, error);\n}\n","import type { Unsubscribe } from '../core/emitter.js';\nimport { ItdConfigError } from '../core/errors.js';\nimport type { RealtimeContext, RealtimeContextBase } from './updates.js';\n\n/** Продолжает цепочку промежуточных обработчиков потока. */\nexport type RealtimeNext = () => Promise<void>;\n\n/** Обрабатывает обновление потока до его передачи подписчикам. */\nexport type RealtimeMiddleware<C extends RealtimeContextBase = RealtimeContext> = (\n context: C,\n next: RealtimeNext,\n) => void | Promise<void>;\n\n/** Объект, предоставляющий снимок промежуточного обработчика потока. */\nexport interface RealtimeMiddlewareObj<C extends RealtimeContextBase = RealtimeContext> {\n middleware(): RealtimeMiddleware<C>;\n}\n\n/** Асинхронный обработчик нормализованного обновления потока. */\nexport type RealtimeHandler<C extends RealtimeContextBase = RealtimeContext> = (\n context: C,\n) => unknown | Promise<unknown>;\n\n/** Условие отбора контекста потока. */\nexport type RealtimePredicate<C extends RealtimeContextBase = RealtimeContext> = (\n context: C,\n) => boolean;\n\n/** Проверка, сужающая тип контекста потока. */\nexport type RealtimeTypeGuard<C extends B, B extends RealtimeContextBase = RealtimeContext> = (\n context: B,\n) => context is C;\n\n/** Ключи, по которым обновления нельзя обрабатывать одновременно. */\nexport type RealtimeSequentializer<C extends RealtimeContextBase = RealtimeContext> = (\n context: C,\n) => PropertyKey | readonly PropertyKey[] | undefined;\n\nconst REALTIME_MIDDLEWARE_SNAPSHOT = Symbol('itd-api.realtime.middlewareSnapshot');\n\ntype SnapshottingRealtimeMiddleware<C extends RealtimeContextBase> = RealtimeMiddleware<C> & {\n [REALTIME_MIDDLEWARE_SNAPSHOT]?: () => RealtimeMiddleware<C>;\n};\n\n/** @internal */\nexport function captureRealtimeMiddleware<C extends RealtimeContextBase>(\n middleware: RealtimeMiddleware<C>,\n): RealtimeMiddleware<C> {\n const capture = (middleware as SnapshottingRealtimeMiddleware<C>)[REALTIME_MIDDLEWARE_SNAPSHOT];\n if (!capture) return middleware;\n\n const snapshot = capture();\n if (typeof snapshot !== 'function') {\n throw new TypeError('Снимок промежуточного обработчика должен быть функцией');\n }\n return snapshot;\n}\n\n/** @internal */\nexport function withRealtimeMiddlewareSnapshot<C extends RealtimeContextBase>(\n middleware: RealtimeMiddleware<C>,\n capture: () => RealtimeMiddleware<C>,\n): RealtimeMiddleware<C> {\n Object.defineProperty(middleware, REALTIME_MIDDLEWARE_SNAPSHOT, { value: capture });\n return middleware;\n}\n\n/** Создаёт отложенный промежуточный обработчик для объекта. @internal */\nexport function deferRealtimeMiddleware<C extends RealtimeContextBase>(\n source: RealtimeMiddlewareObj<C>,\n): RealtimeMiddleware<C> {\n const capture = (): RealtimeMiddleware<C> => {\n const middleware = source.middleware();\n if (typeof middleware !== 'function') {\n throw new ItdConfigError('realtime middleware() должен возвращать функцию обработки');\n }\n return captureRealtimeMiddleware(middleware);\n };\n\n const deferred: RealtimeMiddleware<C> = (context, next) => capture()(context, next);\n return withRealtimeMiddlewareSnapshot(deferred, capture);\n}\n\ninterface HandlerRegistration<C extends RealtimeContextBase> {\n readonly predicate: RealtimePredicate<C>;\n readonly handler: RealtimeHandler<C>;\n}\n\ninterface DispatchWork<C extends RealtimeContextBase> {\n readonly context: C;\n readonly middleware: readonly RealtimeMiddleware<C>[];\n readonly handlers: readonly HandlerRegistration<C>[];\n readonly keys: readonly PropertyKey[];\n /** Ключ снимка: ожидающая работа с тем же ключом заменяется новой. */\n readonly coalesceKey: PropertyKey | undefined;\n}\n\n/** Предел обновлений, ожидающих обработки. @internal */\nexport const MAX_PENDING_UPDATES = 256;\n\nexport interface RealtimeDispatcherOptions<C extends RealtimeContextBase = RealtimeContext> {\n concurrency: number;\n sequentialize?: RealtimeSequentializer<C> | undefined;\n}\n\nexport interface RealtimeDispatcherHooks<C extends RealtimeContextBase = RealtimeContext> {\n deliver: (context: C) => void;\n middlewareError: (error: unknown, context: C) => void;\n handlerError: (error: unknown, context: C) => void;\n /** Очередь достигла предела: обновление не принято, поток обязан прекратить их приём. */\n overflow: () => void;\n}\n\n/** Выполняет промежуточные обработчики по порядку и запрещает повторный вызов `next()`. */\nexport async function runRealtimeMiddleware<C extends RealtimeContextBase>(\n middleware: readonly RealtimeMiddleware<C>[],\n context: C,\n terminal: RealtimeNext,\n): Promise<void> {\n let lastIndex = -1;\n\n const dispatch = async (index: number): Promise<void> => {\n if (index <= lastIndex) throw new Error('next() в промежуточном обработчике вызван повторно');\n lastIndex = index;\n\n const current = middleware[index];\n if (!current) {\n await terminal();\n return;\n }\n\n let downstream: Promise<void> | undefined;\n let duplicateCalls: Promise<void> | undefined;\n let failure: { error: unknown } | undefined;\n\n const next: RealtimeNext = () => {\n if (!downstream) {\n downstream = dispatch(index + 1);\n return downstream;\n }\n\n const duplicate = Promise.reject(\n new Error('next() в одном промежуточном обработчике вызван повторно'),\n );\n duplicateCalls = Promise.all(duplicateCalls ? [duplicateCalls, duplicate] : [duplicate]).then(\n () => undefined,\n );\n return duplicate;\n };\n\n try {\n await current(context, next);\n } catch (error) {\n failure = { error };\n }\n\n try {\n await downstream;\n await duplicateCalls;\n } catch (error) {\n failure ??= { error };\n }\n\n if (failure) throw failure.error;\n };\n\n await dispatch(0);\n}\n\n/** Планирует нормализованные обновления и отслеживает незавершённые обработчики. */\nexport class RealtimeDispatcher<C extends RealtimeContextBase = RealtimeContext> {\n readonly #options: RealtimeDispatcherOptions<C>;\n readonly #hooks: RealtimeDispatcherHooks<C>;\n readonly #middleware: RealtimeMiddleware<C>[] = [];\n readonly #handlers: HandlerRegistration<C>[] = [];\n readonly #queue: DispatchWork<C>[] = [];\n readonly #activeKeys = new Set<PropertyKey>();\n readonly #drainWaiters = new Set<() => void>();\n\n #active = 0;\n\n constructor(options: RealtimeDispatcherOptions<C>, hooks: RealtimeDispatcherHooks<C>) {\n this.#options = options;\n this.#hooks = hooks;\n }\n\n use(middleware: RealtimeMiddleware<C>): Unsubscribe {\n this.#middleware.push(middleware);\n return () => {\n const index = this.#middleware.indexOf(middleware);\n if (index >= 0) this.#middleware.splice(index, 1);\n };\n }\n\n on(predicate: RealtimePredicate<C>, handler: RealtimeHandler<C>): Unsubscribe {\n const registration = { predicate, handler };\n this.#handlers.push(registration);\n\n return () => {\n const index = this.#handlers.indexOf(registration);\n if (index >= 0) this.#handlers.splice(index, 1);\n };\n }\n\n /**\n * Принимает обновление к обработке.\n *\n * @param coalesceKey ожидающая работа с тем же ключом заменяется новой\n */\n dispatch(context: C, coalesceKey?: PropertyKey): void {\n let keys: readonly PropertyKey[];\n let middleware: readonly RealtimeMiddleware<C>[];\n try {\n keys = this.#keysFor(context);\n middleware = this.#middleware.map(captureRealtimeMiddleware);\n } catch (error) {\n this.#hooks.middlewareError(error, context);\n return;\n }\n\n const replaced =\n coalesceKey === undefined\n ? -1\n : this.#queue.findIndex((pending) => pending.coalesceKey === coalesceKey);\n if (replaced < 0 && this.#queue.length >= MAX_PENDING_UPDATES) {\n this.#hooks.overflow();\n return;\n }\n\n const work: DispatchWork<C> = {\n context,\n middleware,\n handlers: [...this.#handlers],\n keys,\n coalesceKey,\n };\n if (replaced >= 0) this.#queue[replaced] = work;\n else this.#queue.push(work);\n\n this.#pump();\n }\n\n /** Отбрасывает обновления, обработка которых ещё не началась. */\n clearPending(): void {\n this.#queue.length = 0;\n this.#resolveDrain();\n }\n\n /** Ждёт завершения активных и поставленных в очередь обновлений. */\n drain(): Promise<void> {\n if (this.#active === 0 && this.#queue.length === 0) return Promise.resolve();\n return new Promise<void>((resolve) => this.#drainWaiters.add(resolve));\n }\n\n #keysFor(context: C): readonly PropertyKey[] {\n const value = this.#options.sequentialize?.(context);\n if (value === undefined) return [];\n\n const values = Array.isArray(value) ? value : [value];\n for (const key of values) {\n if (typeof key !== 'string' && typeof key !== 'number' && typeof key !== 'symbol') {\n throw new TypeError('sequentialize() должен возвращать PropertyKey или их список');\n }\n }\n return [...new Set(values)];\n }\n\n #pump(): void {\n while (this.#active < this.#options.concurrency) {\n const blockedKeys = new Set(this.#activeKeys);\n const index = this.#queue.findIndex(({ keys }) => {\n const runnable = keys.every((key) => !blockedKeys.has(key));\n if (!runnable) {\n for (const key of keys) blockedKeys.add(key);\n }\n return runnable;\n });\n if (index < 0) break;\n\n const [work] = this.#queue.splice(index, 1);\n if (!work) break;\n\n this.#active += 1;\n for (const key of work.keys) this.#activeKeys.add(key);\n void this.#run(work);\n }\n\n this.#resolveDrain();\n }\n\n async #run(work: DispatchWork<C>): Promise<void> {\n try {\n await runRealtimeMiddleware(work.middleware, work.context, async () => {\n for (const { predicate, handler } of work.handlers) {\n try {\n if (predicate(work.context)) await handler(work.context);\n } catch (error) {\n this.#hooks.handlerError(error, work.context);\n }\n }\n\n this.#hooks.deliver(work.context);\n });\n } catch (error) {\n this.#hooks.middlewareError(error, work.context);\n } finally {\n this.#active -= 1;\n for (const key of work.keys) this.#activeKeys.delete(key);\n this.#pump();\n }\n }\n\n #resolveDrain(): void {\n if (this.#active !== 0 || this.#queue.length !== 0) return;\n for (const resolve of this.#drainWaiters) resolve();\n this.#drainWaiters.clear();\n }\n}\n","/**\n * Паузы перед попытками переподключения, мс.\n *\n * Значения совпадают с теми, что использует сайт итд.com, — поведение библиотеки\n * не отличается от привычного пользователю.\n */\nexport const RECONNECT_BACKOFF = Object.freeze([1000, 2000, 4000, 8000, 16000, 30000]);\n\n/** Доля случайного разброса паузы. */\nexport const RECONNECT_JITTER = 0.3;\n\n/**\n * Сколько раз пытаться переподключиться подряд.\n *\n * После исчерпания поток сообщает `giveup` и ждёт ручного `connect()`.\n */\nexport const MAX_RECONNECT_ATTEMPTS = 15;\n\n/** Настройки переподключения. */\nexport interface ReconnectOptions {\n /** Таблица пауз. Последнее значение действует для всех дальнейших попыток. */\n backoff?: readonly number[];\n /** Доля разброса, 0…1. */\n jitter?: number;\n /** Предел числа попыток. */\n maxAttempts?: number;\n}\n\n/**\n * Вычисляет паузу перед попыткой переподключения.\n *\n * Разброс нужен, чтобы клиенты, отключившиеся одновременно из-за перезапуска сервера,\n * не вернулись тоже одновременно.\n *\n * @param attempt номер попытки, начиная с нуля\n * @param random источник случайности; подменяется в тестах\n */\nexport function reconnectDelay(\n attempt: number,\n options: ReconnectOptions = {},\n random: () => number = Math.random,\n): number {\n const backoff = options.backoff ?? RECONNECT_BACKOFF;\n const jitter = options.jitter ?? RECONNECT_JITTER;\n\n const base = backoff[Math.min(attempt, backoff.length - 1)] ?? 30_000;\n const spread = base * jitter * (random() * 2 - 1);\n\n return Math.max(0, Math.round(base + spread));\n}\n","import type { QueryParams } from '../../core/url.js';\nimport type { BuiltInOperationId } from '../../domain/operations.js';\n\n/** Запрос транспорта к конвейеру клиента. */\nexport interface RealtimeRequestInput {\n operationId: BuiltInOperationId;\n path: string;\n query?: QueryParams | undefined;\n /**\n * Отмена запроса. Опрос передаёт сигнал своего соединения; разовое чтение счётчика\n * непрочитанных отменять нечем — оно живёт вне цикла соединения.\n */\n signal?: AbortSignal | undefined;\n}\n\n/**\n * Порт к конвейеру клиента: очередь, авторизация, повторы, плагины и хуки.\n *\n * Ответ приходит уже разобранным и без обёртки `{ data: … }`, а неудача — типизированной\n * ошибкой библиотеки.\n */\nexport type RealtimeRequest = (input: RealtimeRequestInput) => Promise<unknown>;\n\n/** Событие, пришедшее по каналу реального времени. */\nexport interface TransportEvent {\n /** Имя события: `notification`, `unread_count` и другие. */\n name: string;\n /** Полезная нагрузка, уже разобранная из JSON. */\n data: unknown;\n}\n\n/** Что транспорт получает от клиента при подключении. */\nexport interface TransportContext {\n /** Базовый URL API. */\n baseUrl: string;\n /** Разрешено ли передавать токен этому сервису. */\n authorize: boolean;\n /** Реализация `fetch`. */\n fetch: typeof fetch;\n /**\n * Выполнение обычных HTTP-запросов транспорта через конвейер клиента.\n *\n * Есть только у потока, созданного клиентом: конвейер принадлежит ему.\n */\n request?: RealtimeRequest | undefined;\n /**\n * Общие заголовки клиента: `User-Agent`, `X-Device-Id`, заголовки конфигурации\n * и cookie для указанного адреса.\n */\n baseHeaders: (url: string) => Promise<Headers>;\n /** Текущий токен доступа. */\n getToken: () => Promise<string | null>;\n /** Отмена подключения. */\n signal: AbortSignal;\n /** Сообщает о полученном событии. */\n onEvent: (event: TransportEvent) => void;\n /** Сообщает о разобранном, но некорректном сообщении. Соединение при этом живёт. */\n onParseError: (error: unknown, raw: string) => void;\n /** Вызывается, когда соединение установлено. */\n onOpen: () => void;\n}\n\n/** Канал получения исходных событий в реальном времени. */\nexport interface RealtimeTransport {\n /** Понятное имя для логов и диагностики. */\n readonly name: string;\n\n /**\n * Держит соединение, пока оно живо.\n *\n * Должен завершиться, когда поток закрылся, и бросить исключение при ошибке.\n * Отмена через `context.signal` должна приводить к `AbortError`.\n */\n connect(context: TransportContext): Promise<void>;\n}\n\n/** Ошибка, по которой видно, что сервер отверг авторизацию потока. */\nexport class UnauthorizedStreamError extends Error {\n constructor() {\n super('Поток событий отверг токен доступа');\n this.name = 'UnauthorizedStreamError';\n }\n}\n","import { ItdConfigError } from '../core/errors.js';\nimport type { Notification } from '../models/notifications.js';\nimport type { NotificationEvent } from '../notifications/normalize.js';\nimport { readNotificationEvent, readUnreadCountEvent } from '../notifications/normalize.js';\nimport type { NotificationType } from '../types/enums.js';\nimport type { ItdRealtime } from './stream.js';\nimport type { TransportEvent } from './transports/transport.js';\n\n/** Типы нормализованных обновлений потока. */\nexport const RealtimeUpdateType = Object.freeze({\n Notification: 'notification',\n UnreadCount: 'unreadCount',\n Unknown: 'unknown',\n} as const);\n\n/** Источники нормализованных обновлений потока. */\nexport const RealtimeUpdateOrigin = Object.freeze({\n Stream: 'stream',\n Sync: 'sync',\n} as const);\nexport type RealtimeUpdateOrigin = (typeof RealtimeUpdateOrigin)[keyof typeof RealtimeUpdateOrigin];\n\n/** Уведомление с типом, суженным фильтром потока. */\nexport type NotificationOfType<T extends NotificationType> = Omit<Notification, 'type'> & {\n type: T;\n};\n\n/** Конверт уведомления с типом, суженным фильтром потока. */\nexport type NotificationEventOfType<T extends NotificationType> = Omit<\n NotificationEvent,\n 'notification'\n> & {\n notification: NotificationOfType<T>;\n};\n\n/** Нормализованное уведомление из потока. */\nexport interface RealtimeNotificationUpdate<T extends NotificationType = NotificationType> {\n readonly type: typeof RealtimeUpdateType.Notification;\n readonly data: NotificationEventOfType<T>;\n}\n\n/** Актуальное число непрочитанных уведомлений. */\nexport interface RealtimeUnreadCountUpdate {\n readonly type: typeof RealtimeUpdateType.UnreadCount;\n readonly data: number;\n}\n\n/** Неизвестное библиотеке событие потока. */\nexport interface RealtimeUnknownUpdate {\n readonly type: typeof RealtimeUpdateType.Unknown;\n readonly name: string;\n readonly data: unknown;\n}\n\n/** Данные, проходящие через промежуточные обработчики потока. */\nexport type RealtimeUpdate =\n | RealtimeNotificationUpdate\n | RealtimeUnreadCountUpdate\n | RealtimeUnknownUpdate;\n\n/** Тип нормализованного обновления потока. */\nexport type RealtimeUpdateType = RealtimeUpdate['type'];\n\n/** Обновление потока указанного типа. */\nexport type RealtimeUpdateOfType<T extends RealtimeUpdateType> = Extract<\n RealtimeUpdate,\n { type: T }\n>;\n\n/**\n * Общая форма контекста обработки: то, что есть у любого потока независимо от домена.\n *\n * Контекст — обычный объектный литерал, а не класс с геттерами и не `Object.freeze`:\n * плагины-флейворы присваивают в него свои поля (`ctx.session = …`), а `@itd-api/hydrate`\n * подменяет `update` и `stream` через `Object.defineProperty`.\n *\n * @typeParam U нормализованное обновление домена\n * @typeParam S поток, который его получил\n */\nexport interface RealtimeContextBase<U = unknown, S = unknown> {\n /** Нормализованные данные обновления. */\n readonly update: U;\n /** Поток, который получил обновление. */\n readonly stream: S;\n /** Исходный кадр транспорта. Для начальной REST-синхронизации равен `undefined`. */\n readonly raw: TransportEvent | undefined;\n /** Откуда получены данные. */\n readonly origin: RealtimeUpdateOrigin;\n}\n\n/** Контекст обработки одного обновления потока уведомлений. */\nexport type RealtimeContext<U extends RealtimeUpdate = RealtimeUpdate> = RealtimeContextBase<\n U,\n ItdRealtime\n>;\n\n/** Контекст уведомления с типом, суженным фильтром. */\nexport type RealtimeNotificationContext<T extends NotificationType = NotificationType> =\n RealtimeContext<RealtimeNotificationUpdate<T>>;\n\n/** Условия отбора уведомлений. Все указанные поля объединяются через логическое И. */\nexport interface RealtimeNotificationFilter<T extends NotificationType = NotificationType> {\n /** Один или несколько канонических типов уведомления. */\n type?: T | readonly T[];\n /** Идентификатор хотя бы одного участника уведомления. */\n actorId?: string;\n /** Идентификатор объекта события. */\n entityId?: string | null;\n /** Идентификатор родительского объекта. */\n parentEntityId?: string | null;\n /** Дополнительная проверка после сопоставления полей. */\n predicate?: (context: RealtimeNotificationContext<T>) => boolean;\n}\n\n/** Краткая или объектная форма фильтра уведомлений. */\nexport type RealtimeNotificationSelector<T extends NotificationType = NotificationType> =\n | T\n | readonly T[]\n | RealtimeNotificationFilter<T>;\n\n/** Проверяет форму фильтра уведомлений. */\nexport function validateNotificationSelector(selector: unknown): void {\n if (typeof selector === 'string') {\n if (selector.length === 0) throw new ItdConfigError('Тип уведомления не должен быть пустым');\n return;\n }\n\n if (Array.isArray(selector)) {\n if (selector.length === 0 || selector.some((type) => typeof type !== 'string' || !type)) {\n throw new ItdConfigError('Список типов уведомлений должен содержать непустые строки');\n }\n return;\n }\n\n if (typeof selector !== 'object' || selector === null) {\n throw new ItdConfigError('Фильтр уведомлений должен быть строкой, списком или объектом');\n }\n\n const filter = selector as Record<string, unknown>;\n if (filter.type !== undefined) validateNotificationSelector(filter.type);\n if (filter.actorId !== undefined && typeof filter.actorId !== 'string') {\n throw new ItdConfigError('Фильтр уведомлений: actorId должен быть строкой');\n }\n for (const field of ['entityId', 'parentEntityId'] as const) {\n const value = filter[field];\n if (value !== undefined && value !== null && typeof value !== 'string') {\n throw new ItdConfigError(`Фильтр уведомлений: ${field} должен быть строкой или null`);\n }\n }\n if (filter.predicate !== undefined && typeof filter.predicate !== 'function') {\n throw new ItdConfigError('Фильтр уведомлений: predicate должен быть функцией');\n }\n}\n\n/** Преобразует транспортный кадр в одно логическое обновление. */\nexport function readRealtimeUpdate(event: TransportEvent): RealtimeUpdate | undefined {\n if (event.name === 'notification') {\n return {\n type: RealtimeUpdateType.Notification,\n data: readNotificationEvent(event.data),\n };\n }\n\n if (event.name === 'unread_count') {\n const count = readUnreadCountEvent(event.data);\n return count === undefined ? undefined : { type: RealtimeUpdateType.UnreadCount, data: count };\n }\n\n return { type: RealtimeUpdateType.Unknown, name: event.name, data: event.data };\n}\n\n/** Проверяет объектный или краткий фильтр уведомления. */\nexport function matchesNotification<T extends NotificationType>(\n context: RealtimeNotificationContext,\n selector: RealtimeNotificationSelector<T>,\n): context is RealtimeNotificationContext<T> {\n const notification = context.update.data.notification;\n\n if (typeof selector === 'string') return notification.type === selector;\n if (Array.isArray(selector)) return selector.includes(notification.type as T);\n\n const filter = selector as RealtimeNotificationFilter<T>;\n const types = filter.type === undefined ? undefined : [filter.type].flat();\n\n if (types && !types.includes(notification.type as T)) return false;\n if (\n filter.actorId !== undefined &&\n !notification.actors.some(({ id }) => id === filter.actorId)\n ) {\n return false;\n }\n if (filter.entityId !== undefined && notification.entityId !== filter.entityId) return false;\n if (\n filter.parentEntityId !== undefined &&\n notification.parentEntityId !== filter.parentEntityId\n ) {\n return false;\n }\n\n return filter.predicate?.(context as RealtimeNotificationContext<T>) ?? true;\n}\n\n/** Сужает произвольный контекст потока до контекста уведомления. */\nexport function isNotificationContext(\n context: RealtimeContext,\n): context is RealtimeNotificationContext {\n return context.update.type === RealtimeUpdateType.Notification;\n}\n","import { type ItdClock, systemClock } from '../core/clock.js';\nimport { Emitter, type Listener, reportListenerError, type Unsubscribe } from '../core/emitter.js';\nimport { ItdConfigError } from '../core/errors.js';\nimport type { Logger } from '../core/options.js';\nimport { RealtimeStatus } from '../types/enums.js';\nimport {\n deferRealtimeMiddleware,\n RealtimeDispatcher,\n type RealtimeHandler,\n type RealtimeMiddleware,\n type RealtimeMiddlewareObj,\n type RealtimePredicate,\n type RealtimeSequentializer,\n} from './middleware.js';\nimport { MAX_RECONNECT_ATTEMPTS, type ReconnectOptions, reconnectDelay } from './reconnect.js';\nimport {\n type RealtimeRequest,\n type RealtimeTransport,\n type TransportEvent,\n UnauthorizedStreamError,\n} from './transports/transport.js';\nimport { type RealtimeContextBase, RealtimeUpdateOrigin } from './updates.js';\n\n/** Зачем движок просит домен синхронизироваться. */\nexport type RealtimeSyncReason = 'initial' | 'reconnect';\n\n/** Доставляет обновление, полученное во время синхронизации, если её запуск ещё актуален. */\ntype RealtimeSyncDispatch<U> = (update: U) => void;\n\n/**\n * События, которые движок рассылает сам.\n *\n * Ни одно из них не зависит от домена, поэтому они одинаковы у любого потока.\n * Домен расширяет эту карту своими событиями.\n */\nexport interface RealtimeEngineEvents<C extends RealtimeContextBase = RealtimeContextBase> {\n /** Изменилось состояние соединения. */\n status: RealtimeStatus;\n /** Соединение оборвалось; будет предпринята попытка переподключения. */\n error: { error: unknown; willReconnect: boolean };\n /** Сообщение не удалось разобрать. Соединение при этом продолжает работать. */\n parseError: { error: unknown; raw: string };\n /** Запланировано переподключение. */\n reconnect: { attempt: number; delay: number };\n /** Попытки исчерпаны — соединение восстановится только ручным `connect()`. */\n giveup: undefined;\n /** Любой исходный кадр транспорта. Отправляется до нормализации и обработчиков. */\n message: TransportEvent;\n /** Промежуточный обработчик потока завершился исключением. */\n middlewareError: { error: unknown; context: C };\n /** Обработчик обновления завершился исключением. */\n handlerError: { error: unknown; context: C };\n}\n\n/**\n * Что движок получает от домена.\n *\n * @typeParam U нормализованное обновление домена\n * @typeParam C контекст обработки, который домен собирает вокруг обновления\n *\n * @internal\n */\nexport interface RealtimeEngineDeps<U, C extends RealtimeContextBase<U, unknown>> {\n baseUrl: string;\n /** Разрешено ли транспорту передавать токен этому сервису. */\n authorize: boolean;\n fetch: typeof fetch;\n /** Конвейер клиента — см. {@link TransportContext.request}. */\n request?: RealtimeRequest | undefined;\n clock?: ItdClock | undefined;\n /** Общие заголовки клиента для адреса — см. {@link TransportContext.baseHeaders}. */\n baseHeaders: (url: string) => Promise<Headers>;\n getToken: () => Promise<string | null>;\n /** Обновляет токен после отказа авторизации. Возвращает `true`, если удалось. */\n refresh: () => Promise<boolean>;\n /** Вызывается при запуске ранее закрытого потока. */\n onConnect?: (() => void) | undefined;\n /** Вызывается при явном закрытии потока. */\n onClose?: (() => void) | undefined;\n logger?: Logger | undefined;\n\n transport: RealtimeTransport;\n\n /**\n * Доменная обработка сырого кадра до нормализации. `true` — кадр поглощён и дальше\n * не идёт. Нужна там, где кадр не является обновлением: у уведомлений так приходит\n * `connected`.\n */\n handleFrame?: ((event: TransportEvent) => boolean) | undefined;\n /** Превращает кадр в доменное обновление; `undefined` — кадр игнорируется. */\n readUpdate: (event: TransportEvent) => U | undefined;\n /**\n * Ключ коалесцирования: ожидающее обновление с тем же ключом заменяется новым.\n * `undefined` — обновление не коалесцируется.\n */\n coalesceKey?: ((update: U) => PropertyKey | undefined) | undefined;\n /** Собирает контекст. Точка, куда домен добавляет свои поля и действия. */\n createContext: (update: U, raw: TransportEvent | undefined, origin: RealtimeUpdateOrigin) => C;\n /** Доставляет обновление доменным подписчикам после цепочки обработчиков. */\n deliver: (update: U) => void;\n /**\n * Снимок и догон: до первого подключения и перед каждым переподключением.\n *\n * Идёт через обычный конвейер клиента, поэтому заодно обновляет протухший токен.\n * Полученные обновления передаются в `dispatch`: движок отбросит их, если за время\n * запроса поток успели закрыть или запустить заново. Исключение не отменяет подключение —\n * оно уходит в логгер.\n */\n sync?:\n | ((reason: RealtimeSyncReason, dispatch: RealtimeSyncDispatch<U>) => Promise<void>)\n | undefined;\n}\n\n/** Настройки движка: переподключение, параллелизм и реакция на среду. */\nexport interface RealtimeEngineOptions<C extends RealtimeContextBase = RealtimeContextBase>\n extends ReconnectOptions {\n /** Максимальное число одновременно обрабатываемых обновлений. По умолчанию 1. */\n concurrency?: number | undefined;\n /** Возвращает ключи обновлений, которые нельзя обрабатывать одновременно. */\n sequentialize?: RealtimeSequentializer<C> | undefined;\n /** Переподключаться, когда вкладка снова становится видимой. По умолчанию `true`. */\n reconnectOnVisible?: boolean | undefined;\n /** Переподключаться при восстановлении сети. По умолчанию `true`. */\n reconnectOnOnline?: boolean | undefined;\n}\n\n/** Проверяет настройки общей механики потока. */\nfunction validateRealtimeEngineOptions<C extends RealtimeContextBase>(\n options: RealtimeEngineOptions<C>,\n): void {\n const positiveInteger = (value: number | undefined, name: string): void => {\n if (value === undefined) return;\n if (!Number.isInteger(value) || value < 0) {\n throw new ItdConfigError(\n `realtime.${name} должен быть целым неотрицательным числом, получено: ${value}`,\n );\n }\n };\n const duration = (value: number, name: string): void => {\n if (!Number.isFinite(value) || value < 0) {\n throw new ItdConfigError(\n `realtime.${name} должен быть числом не меньше 0, получено: ${value}`,\n );\n }\n };\n\n positiveInteger(options.maxAttempts, 'maxAttempts');\n positiveInteger(options.concurrency, 'concurrency');\n if (options.concurrency === 0) {\n throw new ItdConfigError('realtime.concurrency должен быть больше нуля');\n }\n if (options.jitter !== undefined && !(options.jitter >= 0 && options.jitter <= 1)) {\n throw new ItdConfigError(\n `realtime.jitter должен быть в диапазоне 0…1, получено: ${options.jitter}`,\n );\n }\n if (options.backoff !== undefined) {\n if (!Array.isArray(options.backoff) || options.backoff.length === 0) {\n throw new ItdConfigError('realtime.backoff должен быть непустым списком пауз');\n }\n for (const delay of options.backoff) duration(delay, 'backoff');\n }\n if (options.sequentialize !== undefined && typeof options.sequentialize !== 'function') {\n throw new ItdConfigError('realtime.sequentialize должен быть функцией');\n }\n}\n\n/**\n * Общая механика потока событий: соединение, переподключение с backoff, обновление\n * токена, реакция на среду, статусы и очередь обработчиков.\n *\n * Ничего не знает о домене: что считать обновлением, как собрать контекст и кому его\n * доставить, решают {@link RealtimeEngineDeps}. Поэтому один и тот же движок несёт\n * и поток уведомлений, и любой другой.\n *\n * @internal\n */\nexport class RealtimeEngine<\n U,\n C extends RealtimeContextBase<U, unknown>,\n E extends RealtimeEngineEvents<C> = RealtimeEngineEvents<C>,\n> {\n readonly #deps: RealtimeEngineDeps<U, C>;\n readonly #options: RealtimeEngineOptions<C>;\n readonly #dispatcher: RealtimeDispatcher<C>;\n readonly #emitter: Emitter<E>;\n readonly #clock: ItdClock;\n readonly #maxAttempts: number;\n\n #controller: AbortController | undefined;\n /**\n * Хочет ли вызывающий код, чтобы соединение было живо.\n *\n * Отдельно от `#controller`, потому что тот появляется только после `await` внутри\n * {@link connect}. Без этого флага два вызова подряд проскочили бы проверку оба\n * и подняли два соединения, а `disconnect()` во время синхронизации не был бы замечен\n * и соединение поднялось бы уже после отмены.\n */\n #wanted = false;\n #status: RealtimeStatus = RealtimeStatus.Disconnected;\n #attempt = 0;\n #generation = 0;\n #starting: object | undefined;\n #cancelTimer: (() => void) | undefined;\n #detachEnvironment: (() => void) | undefined;\n\n constructor(deps: RealtimeEngineDeps<U, C>, options: RealtimeEngineOptions<C> = {}) {\n validateRealtimeEngineOptions(options);\n\n this.#deps = deps;\n this.#options = options;\n this.#clock = deps.clock ?? systemClock;\n this.#maxAttempts = options.maxAttempts ?? MAX_RECONNECT_ATTEMPTS;\n this.#emitter = new Emitter<E>((error) => reportListenerError(deps.logger, 'realtime', error));\n this.#dispatcher = new RealtimeDispatcher<C>(\n {\n concurrency: options.concurrency ?? 1,\n ...(options.sequentialize ? { sequentialize: options.sequentialize } : {}),\n },\n {\n deliver: (context) => this.#deps.deliver(context.update),\n middlewareError: (error, context) =>\n this.#reportDispatchError('middlewareError', error, context),\n handlerError: (error, context) => this.#reportDispatchError('handlerError', error, context),\n overflow: () => this.#handleOverflow(),\n },\n );\n }\n\n /** Текущее состояние соединения. */\n get status(): RealtimeStatus {\n return this.#status;\n }\n\n /** Имя используемого транспорта. */\n get transport(): string {\n return this.#deps.transport.name;\n }\n\n /** Подписывается на событие потока. @returns функция отписки */\n on<K extends keyof E>(event: K, listener: Listener<E[K]>): Unsubscribe {\n return this.#emitter.on(event, listener);\n }\n\n /** Подписывается на одно срабатывание события потока. */\n once<K extends keyof E>(event: K, listener: Listener<E[K]>): Unsubscribe {\n return this.#emitter.once(event, listener);\n }\n\n /** Рассылает доменное событие через общий эмиттер потока. */\n emit<K extends keyof E>(event: K, payload: E[K]): void {\n this.#emitter.emit(event, payload);\n }\n\n /** Снимает подписки на события. Обработчики обновлений и middleware остаются. */\n removeAllListeners(): void {\n this.#emitter.removeAllListeners();\n }\n\n /** Добавляет промежуточный обработчик. @returns функция его удаления */\n use(middleware: RealtimeMiddleware<C> | RealtimeMiddlewareObj<C>): Unsubscribe {\n if (typeof middleware === 'function') return this.#dispatcher.use(middleware);\n if (\n typeof middleware !== 'object' ||\n middleware === null ||\n typeof middleware.middleware !== 'function'\n ) {\n throw new ItdConfigError(\n 'realtime.use() принимает функцию обработки или объект с middleware()',\n );\n }\n return this.#dispatcher.use(deferRealtimeMiddleware(middleware));\n }\n\n /** Подписывает обработчик обновлений, подходящих под условие. */\n onUpdate(predicate: RealtimePredicate<C>, handler: RealtimeHandler<C>): Unsubscribe {\n return this.#dispatcher.on(predicate, handler);\n }\n\n /**\n * Поднимает соединение.\n *\n * Повторный вызов при уже живом соединении ничего не делает. Возвращает управление\n * сразу после запуска: соединение живёт в фоне.\n */\n async connect(): Promise<void> {\n if (this.#wanted) return;\n this.#wanted = true;\n const generation = ++this.#generation;\n const starting = {};\n this.#starting = starting;\n this.#deps.onConnect?.();\n\n this.#attachEnvironmentListeners();\n await this.#sync('initial', generation, starting);\n\n // Пока шла синхронизация, поток могли закрыть и даже запустить заново.\n if (this.#starting !== starting) return;\n this.#starting = undefined;\n if (this.#isCurrentGeneration(generation)) this.#run(generation);\n }\n\n /** Закрывает соединение и отменяет запланированные попытки. */\n disconnect(): void {\n this.#wanted = false;\n this.#generation += 1;\n this.#starting = undefined;\n\n if (this.#cancelTimer) {\n this.#cancelTimer();\n this.#cancelTimer = undefined;\n }\n\n this.#detachEnvironment?.();\n this.#detachEnvironment = undefined;\n\n this.#controller?.abort();\n this.#controller = undefined;\n this.#attempt = 0;\n this.#dispatcher.clearPending();\n\n this.#setStatus(RealtimeStatus.Disconnected);\n this.#deps.onClose?.();\n }\n\n /** Ждёт завершения всех принятых обновлений. */\n drain(): Promise<void> {\n return this.#dispatcher.drain();\n }\n\n /** Пропускает актуальное обновление через цепочку обработчиков. */\n #dispatch(update: U, raw: TransportEvent | undefined, origin: RealtimeUpdateOrigin): void {\n if (!this.#wanted) return;\n this.#dispatcher.dispatch(\n this.#deps.createContext(update, raw, origin),\n this.#deps.coalesceKey?.(update),\n );\n }\n\n /**\n * Очередь обновлений достигла предела: закрывает соединение и переподключается,\n * когда обработчики разберут очередь.\n */\n #handleOverflow(): void {\n const controller = this.#controller;\n if (!this.#wanted || !controller) return;\n\n const generation = this.#generation;\n controller.abort();\n this.#controller = undefined;\n this.#setStatus(RealtimeStatus.Error);\n\n const error = new Error('Обработчики не успевают за потоком: очередь обновлений переполнена');\n void this.#dispatcher.drain().then(() => {\n if (this.#isCurrentGeneration(generation) && !this.#controller) {\n this.#scheduleReconnect(error);\n }\n });\n }\n\n /** Выполняет доменную синхронизацию, не роняя подключение из-за её ошибки. */\n async #sync(reason: RealtimeSyncReason, generation: number, starting: object): Promise<void> {\n if (!this.#deps.sync) return;\n\n try {\n await this.#deps.sync(reason, (update) => {\n if (this.#starting !== starting || !this.#isCurrentGeneration(generation)) return;\n this.#dispatch(update, undefined, RealtimeUpdateOrigin.Sync);\n });\n } catch (error) {\n this.#deps.logger?.debug(`не удалось синхронизировать поток (${reason})`, error);\n }\n }\n\n /** Запускает попытку подключения; повторы планирует сам. */\n #run(generation = this.#generation): void {\n if (!this.#isCurrentGeneration(generation)) return;\n\n // Страховка от потерянного соединения: если предыдущее ещё живо, закрываем его,\n // иначе его AbortController остался бы недостижимым и поток — незакрытым.\n this.#controller?.abort();\n\n const controller = new AbortController();\n this.#controller = controller;\n this.#setStatus(RealtimeStatus.Connecting);\n\n void this.#deps.transport\n .connect({\n baseUrl: this.#deps.baseUrl,\n authorize: this.#deps.authorize,\n fetch: this.#deps.fetch,\n request: this.#deps.request,\n baseHeaders: this.#deps.baseHeaders,\n getToken: this.#deps.getToken,\n signal: controller.signal,\n onOpen: () => {\n if (!this.#isCurrentConnection(controller, generation)) return;\n this.#attempt = 0;\n this.#setStatus(RealtimeStatus.Connected);\n },\n onEvent: (event) => {\n if (this.#isCurrentConnection(controller, generation)) this.#handleEvent(event);\n },\n onParseError: (error, raw) => {\n if (this.#isCurrentConnection(controller, generation)) {\n this.#emitEngine('parseError', { error, raw });\n }\n },\n })\n .then(\n () => {\n // Штатное закрытие потока — тоже повод переподключиться.\n if (this.#isCurrentConnection(controller, generation)) {\n this.#handleFailure(new Error('Соединение с потоком событий закрыто'));\n }\n },\n (error: unknown) => {\n if (this.#isCurrentConnection(controller, generation)) this.#handleFailure(error);\n },\n );\n }\n\n #handleEvent(event: TransportEvent): void {\n if (!this.#wanted) return;\n this.#emitEngine('message', event);\n\n if (this.#deps.handleFrame?.(event)) return;\n\n const update = this.#deps.readUpdate(event);\n if (update !== undefined) this.#dispatch(update, event, RealtimeUpdateOrigin.Stream);\n }\n\n #reportDispatchError(\n event: 'middlewareError' | 'handlerError',\n error: unknown,\n context: C,\n ): void {\n if (this.#emitter.listenerCount(event) > 0) {\n this.#emitEngine(event, { error, context });\n return;\n }\n\n const message =\n event === 'middlewareError'\n ? 'Ошибка в промежуточном обработчике потока'\n : 'Ошибка в обработчике обновления потока';\n if (this.#deps.logger) this.#deps.logger.error(message, error);\n else console.error(`[itd-api] ${message}`, error);\n }\n\n #handleFailure(error: unknown): void {\n this.#controller = undefined;\n\n if (error instanceof UnauthorizedStreamError) {\n void this.#refreshAndReconnect(error);\n return;\n }\n\n this.#setStatus(RealtimeStatus.Error);\n this.#scheduleReconnect(error);\n }\n\n /** Обновляет токен и переподключается; при неудаче прекращает попытки. */\n async #refreshAndReconnect(error: unknown): Promise<void> {\n const generation = this.#generation;\n const starting = {};\n this.#starting = starting;\n this.#setStatus(RealtimeStatus.Error);\n\n const refreshed = await this.#deps.refresh().catch(() => false);\n\n if (this.#starting !== starting || !this.#isCurrentGeneration(generation)) return;\n this.#starting = undefined;\n\n if (!refreshed) {\n this.#giveUp(error);\n return;\n }\n\n this.#scheduleReconnect(error);\n }\n\n #scheduleReconnect(error: unknown): void {\n if (!this.#wanted) return;\n\n if (this.#attempt >= this.#maxAttempts) {\n this.#giveUp(error);\n return;\n }\n\n const delay = reconnectDelay(this.#attempt, this.#options);\n this.#attempt += 1;\n\n this.#emitEngine('error', { error, willReconnect: true });\n this.#emitEngine('reconnect', { attempt: this.#attempt, delay });\n\n this.#cancelTimer = this.#clock.schedule(() => {\n this.#cancelTimer = undefined;\n void this.#reconnect();\n }, delay);\n }\n\n /** Догон через REST идёт до сокета: он же обновляет протухший токен. */\n async #reconnect(): Promise<void> {\n if (!this.#wanted || this.#starting) return;\n\n const generation = this.#generation;\n const starting = {};\n this.#starting = starting;\n await this.#sync('reconnect', generation, starting);\n\n if (this.#starting !== starting) return;\n this.#starting = undefined;\n if (this.#isCurrentGeneration(generation)) this.#run(generation);\n }\n\n /**\n * Завершает автоматические попытки переподключения.\n *\n * Владелец узнаёт об этом так же, как при {@link disconnect}. `onClose` вызывается\n * до событий: обработчик `giveup` может тут же вызвать `connect()`.\n */\n #giveUp(error: unknown): void {\n this.#wanted = false;\n this.#generation += 1;\n this.#starting = undefined;\n this.#attempt = 0;\n\n this.#detachEnvironment?.();\n this.#detachEnvironment = undefined;\n\n this.#deps.onClose?.();\n\n this.#emitEngine('error', { error, willReconnect: false });\n this.#emitEngine('giveup', undefined);\n }\n\n /**\n * Подписывается на события среды.\n *\n * Возврат вкладки из фона и восстановление сети — самые частые причины «мёртвого»\n * соединения. У сайта итд.com такой обработки нет.\n */\n #attachEnvironmentListeners(): void {\n if (this.#detachEnvironment) return;\n\n const target = globalThis as unknown as {\n addEventListener?: (type: string, listener: () => void) => void;\n removeEventListener?: (type: string, listener: () => void) => void;\n document?: { visibilityState?: string };\n };\n\n if (typeof target.addEventListener !== 'function') return;\n\n const wake = () => {\n // Реагируем, только если соединения сейчас нет и попытка не запланирована.\n if (this.#controller || this.#cancelTimer || this.#starting) return;\n if (this.#status === RealtimeStatus.Disconnected) return;\n\n this.#attempt = 0;\n void this.#reconnect();\n };\n\n const onVisibility = () => {\n if (target.document?.visibilityState === 'visible') wake();\n };\n\n const listeners: [string, () => void][] = [];\n\n if (this.#options.reconnectOnVisible !== false && target.document) {\n listeners.push(['visibilitychange', onVisibility]);\n }\n if (this.#options.reconnectOnOnline !== false) {\n listeners.push(['online', wake]);\n }\n\n for (const [type, listener] of listeners) target.addEventListener(type, listener);\n\n this.#detachEnvironment = () => {\n for (const [type, listener] of listeners) target.removeEventListener?.(type, listener);\n };\n }\n\n #setStatus(status: RealtimeStatus): void {\n if (this.#status === status) return;\n this.#status = status;\n this.#emitEngine('status', status);\n }\n\n #isCurrentGeneration(generation: number): boolean {\n return this.#wanted && this.#generation === generation;\n }\n\n #isCurrentConnection(controller: AbortController, generation: number): boolean {\n return (\n this.#isCurrentGeneration(generation) &&\n this.#controller === controller &&\n !controller.signal.aborted\n );\n }\n\n /** Рассылает событие из общей части карты, не теряя доменные события типа `E`. */\n #emitEngine<K extends keyof RealtimeEngineEvents<C>>(\n event: K,\n payload: RealtimeEngineEvents<C>[K],\n ): void {\n // `E` гарантированно содержит общую карту. Приведение нужно только потому, что\n // TypeScript допускает теоретическое сужение унаследованного поля в подтипе `E`.\n (this.#emitter as unknown as Emitter<RealtimeEngineEvents<C>>).emit(event, payload);\n }\n}\n","import { type ItdClock, systemClock } from '../../core/clock.js';\nimport { ItdConfigError, isItdAuthError } from '../../core/errors.js';\nimport { pickArray, pickNumber } from '../../core/unwrap.js';\nimport type { RealtimeRequest, RealtimeTransport, TransportContext } from './transport.js';\nimport { UnauthorizedStreamError } from './transport.js';\n\n/** Настройки опроса. */\nexport interface PollTransportOptions {\n /** Часы опроса. Обычно подменяются только в тестах. */\n clock?: ItdClock;\n /** Как часто опрашивать сервер, мс. По умолчанию 15 000. */\n interval?: number;\n /** Сколько уведомлений запрашивать за раз. По умолчанию 20. */\n limit?: number;\n}\n\n/**\n * Запасной транспорт: обычный опрос REST вместо потока.\n *\n * Нужен там, где `fetch` не умеет отдавать тело по частям, — например в части сборок\n * React Native. Наружу выдаёт те же события, что и {@link SseTransport}, поэтому\n * вызывающий код разницы не замечает.\n *\n * Новыми считаются уведомления, которых не было в предыдущем ответе; чтобы список\n * известных не рос бесконечно, он ограничен последними двумя страницами.\n */\nexport class PollTransport implements RealtimeTransport {\n readonly name = 'poll';\n\n readonly #interval: number;\n readonly #limit: number;\n readonly #clock: ItdClock;\n\n constructor(options: PollTransportOptions = {}) {\n this.#clock = options.clock ?? systemClock;\n this.#interval = options.interval ?? 15_000;\n this.#limit = options.limit ?? 20;\n }\n\n async connect(context: TransportContext): Promise<void> {\n const request = context.request;\n if (!request) {\n throw new ItdConfigError(\n 'опрос уведомлений выполняется через конвейер клиента; создайте поток вызовом itd.realtime()',\n );\n }\n\n const seen = new Set<string>();\n let firstRun = true;\n let lastUnreadCount: number | undefined;\n\n while (!context.signal.aborted) {\n const payload = await this.#readUpdates(request, context.signal);\n\n // Соединение считается установленным только после первого успешного ответа: иначе\n // при постоянно недоступной сети каждая попытка обнуляла бы счётчик и maxAttempts\n // не срабатывал бы никогда.\n context.onOpen();\n\n const items = pickArray<Record<string, unknown>>(payload, 'notifications');\n\n for (const item of [...items].reverse()) {\n const id = typeof item.id === 'string' ? item.id : undefined;\n if (!id || seen.has(id)) continue;\n\n seen.add(id);\n // На первом проходе список уже существующих уведомлений не считается новыми\n // событиями: иначе подключение сразу выдало бы всю историю.\n if (!firstRun) context.onEvent({ name: 'notification', data: { payload: item } });\n }\n\n // Ограничиваем память: держим только последние две страницы идентификаторов.\n if (seen.size > this.#limit * 2) {\n const excess = [...seen].slice(0, seen.size - this.#limit * 2);\n for (const id of excess) seen.delete(id);\n }\n\n const count = await this.#readCount(request, context.signal);\n if (count !== undefined && count !== lastUnreadCount) {\n lastUnreadCount = count;\n context.onEvent({ name: 'unread_count', data: { payload: { count } } });\n }\n\n firstRun = false;\n await this.#wait(context.signal);\n }\n }\n\n async #readUpdates(request: RealtimeRequest, signal: AbortSignal): Promise<unknown> {\n try {\n return await request({\n operationId: 'realtime.poll.updates',\n path: '/api/notifications/',\n query: { limit: this.#limit, offset: 0 },\n signal,\n });\n } catch (error) {\n // Для потока это тот же отказ авторизации, что и `401` от сервера событий.\n if (isItdAuthError(error)) throw new UnauthorizedStreamError();\n throw error;\n }\n }\n\n async #readCount(request: RealtimeRequest, signal: AbortSignal): Promise<number | undefined> {\n try {\n const payload = await request({\n operationId: 'realtime.poll.unread',\n path: '/api/notifications/count',\n signal,\n });\n return pickNumber(payload, 'count', 0);\n } catch {\n // Счётчик — вспомогательная величина: его недоступность не должна рвать опрос.\n return undefined;\n }\n }\n\n /** Ждёт следующего опроса, прерываясь при отмене. */\n #wait(signal: AbortSignal): Promise<void> {\n // Подписка на уже сработавший сигнал не вызывается никогда — без этой проверки\n // отмена, пришедшая перед самым ожиданием, стоила бы лишнего интервала.\n if (signal.aborted) return Promise.resolve();\n\n return new Promise<void>((resolve) => {\n const cancel = this.#clock.schedule(finish, this.#interval);\n\n function finish() {\n cancel();\n signal.removeEventListener('abort', finish);\n resolve();\n }\n\n signal.addEventListener('abort', finish, { once: true });\n });\n }\n}\n","class ParseError extends Error {\n constructor(message, options) {\n super(message), this.name = \"ParseError\", this.type = options.type, this.field = options.field, this.value = options.value, this.line = options.line;\n }\n}\nconst LF = 10, CR = 13, SPACE = 32;\nfunction noop(_arg) {\n}\nfunction createParser(config) {\n if (typeof config == \"function\")\n throw new TypeError(\n \"`config` must be an object, got a function instead. Did you mean `createParser({onEvent: fn})`?\"\n );\n const { onEvent = noop, onError = noop, onRetry = noop, onComment, maxBufferSize } = config, pendingFragments = [];\n let pendingFragmentsLength = 0, isFirstChunk = !0, id, data = \"\", dataLines = 0, eventType, terminated = !1;\n function feed(chunk) {\n if (terminated)\n throw new Error(\n \"Cannot feed parser: it was terminated after exceeding the configured max buffer size. Call `reset()` to resume parsing.\"\n );\n if (isFirstChunk && (isFirstChunk = !1, chunk.charCodeAt(0) === 239 && chunk.charCodeAt(1) === 187 && chunk.charCodeAt(2) === 191 && (chunk = chunk.slice(3))), pendingFragments.length === 0) {\n const trailing2 = processLines(chunk);\n trailing2 !== \"\" && (pendingFragments.push(trailing2), pendingFragmentsLength = trailing2.length), checkBufferSize();\n return;\n }\n if (chunk.indexOf(`\n`) === -1 && chunk.indexOf(\"\\r\") === -1) {\n pendingFragments.push(chunk), pendingFragmentsLength += chunk.length, checkBufferSize();\n return;\n }\n pendingFragments.push(chunk);\n const input = pendingFragments.join(\"\");\n pendingFragments.length = 0, pendingFragmentsLength = 0;\n const trailing = processLines(input);\n trailing !== \"\" && (pendingFragments.push(trailing), pendingFragmentsLength = trailing.length), checkBufferSize();\n }\n function checkBufferSize() {\n maxBufferSize !== void 0 && (pendingFragmentsLength + data.length <= maxBufferSize || (terminated = !0, pendingFragments.length = 0, pendingFragmentsLength = 0, id = void 0, data = \"\", dataLines = 0, eventType = void 0, onError(\n new ParseError(`Buffered data exceeded max buffer size of ${maxBufferSize} characters`, {\n type: \"max-buffer-size-exceeded\"\n })\n )));\n }\n function processLines(chunk) {\n let searchIndex = 0;\n if (chunk.indexOf(\"\\r\") === -1) {\n let lfIndex = chunk.indexOf(`\n`, searchIndex);\n for (; lfIndex !== -1; ) {\n if (searchIndex === lfIndex) {\n dataLines > 0 && onEvent({ id, event: eventType, data }), id = void 0, data = \"\", dataLines = 0, eventType = void 0, searchIndex = lfIndex + 1, lfIndex = chunk.indexOf(`\n`, searchIndex);\n continue;\n }\n const firstCharCode = chunk.charCodeAt(searchIndex);\n if (isDataPrefix(chunk, searchIndex, firstCharCode)) {\n const valueStart = chunk.charCodeAt(searchIndex + 5) === SPACE ? searchIndex + 6 : searchIndex + 5, value = chunk.slice(valueStart, lfIndex);\n if (dataLines === 0 && chunk.charCodeAt(lfIndex + 1) === LF) {\n onEvent({ id, event: eventType, data: value }), id = void 0, data = \"\", eventType = void 0, searchIndex = lfIndex + 2, lfIndex = chunk.indexOf(`\n`, searchIndex);\n continue;\n }\n data = dataLines === 0 ? value : `${data}\n${value}`, dataLines++;\n } else isEventPrefix(chunk, searchIndex, firstCharCode) ? eventType = chunk.slice(\n chunk.charCodeAt(searchIndex + 6) === SPACE ? searchIndex + 7 : searchIndex + 6,\n lfIndex\n ) || void 0 : parseLine(chunk, searchIndex, lfIndex);\n searchIndex = lfIndex + 1, lfIndex = chunk.indexOf(`\n`, searchIndex);\n }\n return chunk.slice(searchIndex);\n }\n for (; searchIndex < chunk.length; ) {\n const crIndex = chunk.indexOf(\"\\r\", searchIndex), lfIndex = chunk.indexOf(`\n`, searchIndex);\n let lineEnd = -1;\n if (crIndex !== -1 && lfIndex !== -1 ? lineEnd = crIndex < lfIndex ? crIndex : lfIndex : crIndex !== -1 ? crIndex === chunk.length - 1 ? lineEnd = -1 : lineEnd = crIndex : lfIndex !== -1 && (lineEnd = lfIndex), lineEnd === -1)\n break;\n parseLine(chunk, searchIndex, lineEnd), searchIndex = lineEnd + 1, chunk.charCodeAt(searchIndex - 1) === CR && chunk.charCodeAt(searchIndex) === LF && searchIndex++;\n }\n return chunk.slice(searchIndex);\n }\n function parseLine(chunk, start, end) {\n if (start === end) {\n dispatchEvent();\n return;\n }\n const firstCharCode = chunk.charCodeAt(start);\n if (isDataPrefix(chunk, start, firstCharCode)) {\n const valueStart = chunk.charCodeAt(start + 5) === SPACE ? start + 6 : start + 5, value2 = chunk.slice(valueStart, end);\n data = dataLines === 0 ? value2 : `${data}\n${value2}`, dataLines++;\n return;\n }\n if (isEventPrefix(chunk, start, firstCharCode)) {\n eventType = chunk.slice(chunk.charCodeAt(start + 6) === SPACE ? start + 7 : start + 6, end) || void 0;\n return;\n }\n if (firstCharCode === 105 && chunk.charCodeAt(start + 1) === 100 && chunk.charCodeAt(start + 2) === 58) {\n const value2 = chunk.slice(chunk.charCodeAt(start + 3) === SPACE ? start + 4 : start + 3, end);\n id = value2.includes(\"\\0\") ? void 0 : value2;\n return;\n }\n if (firstCharCode === 58) {\n if (onComment) {\n const line2 = chunk.slice(start, end);\n onComment(line2.slice(chunk.charCodeAt(start + 1) === SPACE ? 2 : 1));\n }\n return;\n }\n const line = chunk.slice(start, end), fieldSeparatorIndex = line.indexOf(\":\");\n if (fieldSeparatorIndex === -1) {\n processField(line, \"\", line);\n return;\n }\n const field = line.slice(0, fieldSeparatorIndex), offset = line.charCodeAt(fieldSeparatorIndex + 1) === SPACE ? 2 : 1, value = line.slice(fieldSeparatorIndex + offset);\n processField(field, value, line);\n }\n function processField(field, value, line) {\n switch (field) {\n case \"event\":\n eventType = value || void 0;\n break;\n case \"data\":\n data = dataLines === 0 ? value : `${data}\n${value}`, dataLines++;\n break;\n case \"id\":\n id = value.includes(\"\\0\") ? void 0 : value;\n break;\n case \"retry\":\n /^\\d+$/.test(value) ? onRetry(parseInt(value, 10)) : onError(\n new ParseError(`Invalid \\`retry\\` value: \"${value}\"`, {\n type: \"invalid-retry\",\n value,\n line\n })\n );\n break;\n default:\n onError(\n new ParseError(\n `Unknown field \"${field.length > 20 ? `${field.slice(0, 20)}\\u2026` : field}\"`,\n { type: \"unknown-field\", field, value, line }\n )\n );\n break;\n }\n }\n function dispatchEvent() {\n dataLines > 0 && onEvent({\n id,\n event: eventType,\n data\n }), id = void 0, data = \"\", dataLines = 0, eventType = void 0;\n }\n function reset(options = {}) {\n if (options.consume && pendingFragments.length > 0) {\n const incompleteLine = pendingFragments.join(\"\");\n parseLine(incompleteLine, 0, incompleteLine.length);\n }\n isFirstChunk = !0, id = void 0, data = \"\", dataLines = 0, eventType = void 0, pendingFragments.length = 0, pendingFragmentsLength = 0, terminated = !1;\n }\n return { feed, reset };\n}\nfunction isDataPrefix(chunk, i, firstCharCode) {\n return firstCharCode === 100 && chunk.charCodeAt(i + 1) === 97 && chunk.charCodeAt(i + 2) === 116 && chunk.charCodeAt(i + 3) === 97 && chunk.charCodeAt(i + 4) === 58;\n}\nfunction isEventPrefix(chunk, i, firstCharCode) {\n return firstCharCode === 101 && chunk.charCodeAt(i + 1) === 118 && chunk.charCodeAt(i + 2) === 101 && chunk.charCodeAt(i + 3) === 110 && chunk.charCodeAt(i + 4) === 116 && chunk.charCodeAt(i + 5) === 58;\n}\nexport {\n ParseError,\n createParser\n};\n//# sourceMappingURL=index.js.map\n","import { createParser } from 'eventsource-parser';\nimport { type ItdClock, systemClock } from '../../core/clock.js';\nimport { joinUrl } from '../../core/url.js';\nimport {\n type RealtimeTransport,\n type TransportContext,\n UnauthorizedStreamError,\n} from './transport.js';\n\n/** Путь потока уведомлений. */\nexport const STREAM_PATH = '/api/notifications/stream';\n\n/** Настройки SSE-транспорта. */\nexport interface SseTransportOptions {\n /** Часы потока. Обычно подменяются только в тестах. */\n clock?: ItdClock;\n /**\n * Сколько миллисекунд ждать данных, прежде чем считать соединение мёртвым.\n *\n * Сервер не присылает keep-alive, а оборванное TCP-соединение может не закрыться\n * само — без этой проверки поток «тихо умирает» и новых уведомлений не приходит.\n * По умолчанию 90 000. `0` отключает проверку.\n */\n idleTimeout?: number;\n /**\n * Сколько миллисекунд ждать ответа на запрос потока, прежде чем оборвать попытку.\n *\n * Проверка молчания ({@link idleTimeout}) начинается только после получения тела ответа.\n * Если `fetch` завис на установке соединения, без этого таймаута переподключение не\n * запустится до системного сетевого таймаута. По умолчанию 20 000. `0` отключает проверку.\n */\n handshakeTimeout?: number;\n}\n\n/**\n * Транспорт поверх Server-Sent Events.\n *\n * Нативный `EventSource` не подходит: он не умеет отправлять заголовок `Authorization`,\n * а поток требует Bearer-токен. Поэтому используется `fetch` с чтением тела по частям.\n *\n * Разбор кадров делегирован `eventsource-parser`, который корректно обрабатывает\n * многострочный `data:`, перевод строки `\\r\\n`, `data:` без пробела и кадр, разорванный\n * между сетевыми чанками. В разборе на сайте итд.com каждый из этих случаев обрабатывается\n * неверно.\n */\nexport class SseTransport implements RealtimeTransport {\n readonly name = 'sse';\n\n readonly #idleTimeout: number;\n readonly #handshakeTimeout: number;\n readonly #clock: ItdClock;\n /** Идентификатор последнего события — отправляется при переподключении. */\n #lastEventId: string | undefined;\n\n constructor(options: SseTransportOptions = {}) {\n this.#clock = options.clock ?? systemClock;\n this.#idleTimeout = options.idleTimeout ?? 90_000;\n this.#handshakeTimeout = options.handshakeTimeout ?? 20_000;\n }\n\n async connect(context: TransportContext): Promise<void> {\n const token = context.authorize ? await context.getToken() : null;\n if (context.authorize && !token) throw new UnauthorizedStreamError();\n\n const url = joinUrl(context.baseUrl, STREAM_PATH);\n\n const headers = await context.baseHeaders(url);\n headers.set('Accept', 'text/event-stream');\n if (token) headers.set('Authorization', `Bearer ${token}`);\n headers.set('Cache-Control', 'no-cache');\n\n // Сервер пока не присылает id, но поддержка не мешает и пригодится, если начнёт.\n if (this.#lastEventId) headers.set('Last-Event-ID', this.#lastEventId);\n\n // Отдельный контроллер, связанный с сигналом потока: помимо внешней отмены он позволяет\n // оборвать зависшее рукопожатие своим таймаутом. Живёт до конца чтения тела.\n const controller = new AbortController();\n const relayAbort = () => controller.abort(context.signal.reason);\n if (context.signal.aborted) controller.abort(context.signal.reason);\n else context.signal.addEventListener('abort', relayAbort, { once: true });\n\n try {\n const response = await this.#handshake(url, headers, context, controller);\n\n if (context.authorize && response.status === 401) throw new UnauthorizedStreamError();\n if (!response.ok) throw new Error(`Поток уведомлений вернул статус ${response.status}`);\n if (!response.body) throw new Error('Ответ потока уведомлений пуст');\n\n context.onOpen();\n\n await this.#read(response.body, context);\n } finally {\n context.signal.removeEventListener('abort', relayAbort);\n }\n }\n\n /** Выполняет запрос потока, обрывая его, если ответ не пришёл за отведённое время. */\n async #handshake(\n url: string,\n headers: Headers,\n context: TransportContext,\n controller: AbortController,\n ): Promise<Response> {\n let expired = false;\n const cancelTimer =\n this.#handshakeTimeout > 0\n ? this.#clock.schedule(() => {\n expired = true;\n // Абортим связанный контроллер: `fetch` реджектит, и внешний код переподключится.\n controller.abort(new Error('Истёк таймаут рукопожатия SSE'));\n }, this.#handshakeTimeout)\n : undefined;\n\n try {\n return await context.fetch(url, { method: 'GET', headers, signal: controller.signal });\n } catch (error) {\n if (expired) throw new Error('Поток уведомлений не ответил: истёк таймаут рукопожатия');\n throw error;\n } finally {\n cancelTimer?.();\n }\n }\n\n async #read(body: ReadableStream<Uint8Array>, context: TransportContext): Promise<void> {\n const reader = body.getReader();\n const decoder = new TextDecoder();\n\n const parser = createParser({\n onEvent: (message) => {\n if (message.id) this.#lastEventId = message.id;\n\n let data: unknown;\n try {\n data = JSON.parse(message.data);\n } catch (error) {\n // Одно битое сообщение не повод рвать соединение.\n context.onParseError(error, message.data);\n return;\n }\n\n // Имя события может отсутствовать — тогда тип лежит внутри полезной нагрузки.\n const name =\n message.event ??\n (typeof data === 'object' && data !== null && 'type' in data\n ? String((data as { type: unknown }).type)\n : 'message');\n\n context.onEvent({ name, data });\n },\n });\n\n let cancelIdleTimer: (() => void) | undefined;\n\n const armIdleTimer = () => {\n if (this.#idleTimeout <= 0) return;\n cancelIdleTimer?.();\n\n cancelIdleTimer = this.#clock.schedule(() => {\n // Отмена чтения завершит цикл, и внешний код переподключится.\n void reader.cancel(new Error('Поток молчит дольше допустимого')).catch(() => {});\n }, this.#idleTimeout);\n };\n\n armIdleTimer();\n\n try {\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n\n armIdleTimer();\n parser.feed(decoder.decode(value, { stream: true }));\n }\n } finally {\n cancelIdleTimer?.();\n reader.releaseLock?.();\n }\n }\n}\n","import type { AuthIdentity } from '../core/auth-provider.js';\nimport type { ItdClock } from '../core/clock.js';\nimport type { Listener, Unsubscribe } from '../core/emitter.js';\nimport { ItdConfigError } from '../core/errors.js';\nimport type { Logger } from '../core/options.js';\nimport { supportsStreamingBody } from '../core/runtime.js';\nimport { pickString } from '../core/unwrap.js';\nimport type { NotificationEvent } from '../notifications/normalize.js';\nimport type { NotificationType, RealtimeStatus } from '../types/enums.js';\nimport { RealtimeEngine, type RealtimeEngineEvents, type RealtimeSyncReason } from './engine.js';\nimport type {\n RealtimeHandler,\n RealtimeMiddleware,\n RealtimeMiddlewareObj,\n RealtimePredicate,\n RealtimeSequentializer,\n RealtimeTypeGuard,\n} from './middleware.js';\nimport type { ReconnectOptions } from './reconnect.js';\nimport { PollTransport } from './transports/poll.js';\nimport { SseTransport } from './transports/sse.js';\nimport type { RealtimeRequest, RealtimeTransport, TransportEvent } from './transports/transport.js';\nimport {\n isNotificationContext,\n matchesNotification,\n type RealtimeContext,\n type RealtimeNotificationContext,\n type RealtimeNotificationSelector,\n type RealtimeUpdate,\n type RealtimeUpdateOfType,\n RealtimeUpdateType,\n readRealtimeUpdate,\n validateNotificationSelector,\n} from './updates.js';\n\n/**\n * События потока уведомлений.\n *\n * Общая часть — {@link RealtimeEngineEvents}: статусы, ошибки и переподключение одинаковы\n * у любого потока. Ниже — то, что есть только у уведомлений.\n */\nexport interface RealtimeEvents<C extends RealtimeContext = RealtimeContext>\n extends RealtimeEngineEvents<C> {\n /** Пришло новое уведомление. */\n notification: NotificationEvent;\n /**\n * Сервер подтвердил подключение и назвал получателя событий.\n *\n * Приходит первым кадром сразу после установки соединения.\n */\n ready: { userId: string | undefined };\n /**\n * Получено актуальное число непрочитанных.\n *\n * При подключении клиент может запросить начальное значение через REST. Затем событие\n * возникает, только если счётчик пришёл в потоке. В остальных случаях обновляйте его\n * в приложении либо запрашивайте `itd.notifications.count()`.\n */\n unreadCount: number;\n}\n\n/** Способ получения событий. */\nexport const RealtimeTransportKind = Object.freeze({\n /** Поток событий, если среда умеет читать тело по частям, иначе опрос. */\n Auto: 'auto',\n /** Поток `text/event-stream`. */\n Sse: 'sse',\n /** Периодический опрос REST. */\n Poll: 'poll',\n} as const);\nexport type RealtimeTransportKind =\n (typeof RealtimeTransportKind)[keyof typeof RealtimeTransportKind];\n\n/** Настройки потока уведомлений. */\nexport interface RealtimeOptions<C extends RealtimeContext = RealtimeContext>\n extends ReconnectOptions {\n /**\n * Транспорт. По умолчанию `auto`: поток событий, если среда умеет читать тело ответа\n * по частям, иначе опрос.\n *\n * Можно передать и свою реализацию {@link RealtimeTransport} — это пригодится, если\n * у платформы появится WebSocket либо нужен нестандартный способ доставки.\n */\n transport?: RealtimeTransportKind | RealtimeTransport;\n /**\n * Молчание сервера, после которого соединение считается мёртвым, мс. По умолчанию 90 000.\n *\n * Сервер не присылает keep-alive, поэтому без этой проверки оборванное соединение\n * может незаметно «зависнуть».\n */\n idleTimeout?: number;\n /**\n * Сколько ждать ответа на запрос потока, прежде чем оборвать попытку, мс. По умолчанию\n * 20 000. Защищает от зависания на установке соединения, когда {@link idleTimeout} ещё\n * не действует. `0` отключает проверку. Только для потокового транспорта.\n */\n handshakeTimeout?: number;\n /** Как часто опрашивать сервер, если используется запасной транспорт. */\n pollInterval?: number;\n /**\n * Запрашивать число непрочитанных при подключении. По умолчанию `true`.\n *\n * Так поступает сайт итд.com: поток присылает только новые события, а начальное\n * значение счётчика нужно получить отдельно.\n */\n syncCount?: boolean;\n /**\n * Переподключаться, когда вкладка снова становится видимой. По умолчанию `true`.\n *\n * Только в браузере. У сайта итд.com такой обработки нет, из-за чего вкладка,\n * пролежавшая в фоне, может остаться без соединения.\n */\n reconnectOnVisible?: boolean;\n /** Переподключаться при восстановлении сети. По умолчанию `true`. Только в браузере. */\n reconnectOnOnline?: boolean;\n /** Максимальное число одновременно обрабатываемых обновлений. По умолчанию 1. */\n concurrency?: number;\n /** Возвращает ключи обновлений, которые нельзя обрабатывать одновременно. */\n sequentialize?: RealtimeSequentializer<C>;\n}\n\n/** Проверяет настройки потока. @throws {ItdConfigError} при некорректных значениях */\nfunction validateRealtimeOptions<C extends RealtimeContext>(options: RealtimeOptions<C>): void {\n const duration = (value: number | undefined, name: string, min: number): void => {\n if (value === undefined) return;\n if (!Number.isFinite(value) || value < min) {\n throw new ItdConfigError(\n `realtime.${name} должен быть числом не меньше ${min}, получено: ${value}`,\n );\n }\n };\n\n duration(options.pollInterval, 'pollInterval', 1);\n duration(options.idleTimeout, 'idleTimeout', 0);\n duration(options.handshakeTimeout, 'handshakeTimeout', 0);\n}\n\n/** Что поток получает от клиента. */\nexport interface RealtimeDeps {\n baseUrl: string;\n /** Разрешено ли транспорту передавать токен этому сервису. */\n authorize?: boolean | undefined;\n fetch: typeof fetch;\n /** Конвейер клиента — см. {@link TransportContext.request}. */\n request?: RealtimeRequest | undefined;\n clock?: ItdClock;\n /** Общие заголовки клиента для адреса — см. {@link TransportContext.baseHeaders}. */\n baseHeaders: (url: string) => Promise<Headers>;\n /** Идентификаторы аккаунта и сессии создавшего поток клиента. */\n getAuthIdentity?: (() => AuthIdentity) | undefined;\n /** Непрозрачная область авторизации создавшего поток клиента. */\n getAuthScope?: (() => string) | undefined;\n getToken: () => Promise<string | null>;\n /** Обновляет токен после отказа авторизации. Возвращает `true`, если удалось. */\n refresh: () => Promise<boolean>;\n /** Загружает начальное число непрочитанных. */\n fetchUnreadCount: () => Promise<number>;\n /** Вызывается при явном закрытии потока. */\n onClose?: (() => void) | undefined;\n /** Вызывается при запуске ранее закрытого потока. */\n onConnect?: (() => void) | undefined;\n logger?: Logger | undefined;\n}\n\nconst REALTIME_CONNECT_GUARDS = new WeakMap<object, () => void>();\n\n/** Связывает поток с lifecycle создавшего его клиента. @internal */\nexport function setRealtimeConnectGuard<C extends RealtimeContext>(\n stream: ItdRealtime<C>,\n guard: () => void,\n): void {\n REALTIME_CONNECT_GUARDS.set(stream, guard);\n}\n\n/**\n * Поток уведомлений в реальном времени.\n *\n * Получается вызовом `itd.realtime()`. Соединение поднимается методом {@link connect}\n * и держится само: обрывы, обновление токена и повторные попытки библиотека берёт на себя.\n *\n * Параметр типа задаёт форму контекста: плагин может расширить её своими полями\n * (`ItdRealtime<RealtimeContext & SessionFlavor<S>>`) и типизировать обработчики.\n *\n * @example\n * ```ts\n * import { NotificationType } from 'itd-api';\n *\n * const stream = itd.realtime();\n *\n * stream.onNotification(NotificationType.PostComment, async ({ update }) => {\n * await saveCommentNotification(update.data.notification);\n * });\n * stream.on('status', (status) => console.log('соединение:', status));\n *\n * await stream.connect();\n * // …позже\n * stream.disconnect();\n * await stream.drain();\n * ```\n */\nexport class ItdRealtime<C extends RealtimeContext = RealtimeContext> {\n readonly #deps: RealtimeDeps;\n readonly #engine: RealtimeEngine<RealtimeUpdate, C, RealtimeEvents<C>>;\n\n constructor(deps: RealtimeDeps, options: RealtimeOptions<C> = {}) {\n validateRealtimeOptions(options);\n\n this.#deps = deps;\n this.#engine = new RealtimeEngine<RealtimeUpdate, C, RealtimeEvents<C>>(\n {\n baseUrl: deps.baseUrl,\n authorize: deps.authorize ?? true,\n fetch: deps.fetch,\n request: deps.request,\n clock: deps.clock,\n baseHeaders: deps.baseHeaders,\n getToken: deps.getToken,\n refresh: deps.refresh,\n onConnect: deps.onConnect,\n onClose: deps.onClose,\n logger: deps.logger,\n\n transport: createTransport(deps, options),\n handleFrame: (event) => this.#handleFrame(event),\n readUpdate: readRealtimeUpdate,\n coalesceKey: (update) =>\n update.type === RealtimeUpdateType.UnreadCount ? update.type : undefined,\n createContext: (update, raw, origin) =>\n // Флейворные поля появляются в контексте позже — их присваивают плагины\n // в своих middleware, поэтому здесь собирается базовая форма.\n ({ update, stream: this, raw, origin }) as unknown as C,\n deliver: (update) => this.#deliver(update),\n ...(options.syncCount === false\n ? {}\n : {\n sync: (reason: RealtimeSyncReason, dispatch: (update: RealtimeUpdate) => void) =>\n this.#syncUnreadCount(reason, dispatch),\n }),\n },\n options,\n );\n }\n\n /** Текущее состояние соединения. */\n get status(): RealtimeStatus {\n return this.#engine.status;\n }\n\n /** Имя используемого транспорта. */\n get transport(): string {\n return this.#engine.transport;\n }\n\n /** Базовый URL клиента, создавшего поток. @internal */\n get baseUrl(): string {\n return this.#deps.baseUrl;\n }\n\n /** Идентификаторы аккаунта и сессии клиента, создавшего поток. @internal */\n getAuthIdentity(): AuthIdentity | undefined {\n return this.#deps.getAuthIdentity?.();\n }\n\n /** Непрозрачная область авторизации создавшего поток клиента. @internal */\n getAuthScope(): string | undefined {\n return this.#deps.getAuthScope?.();\n }\n\n /** Подписывается на событие потока. @returns функция отписки */\n on<K extends keyof RealtimeEvents<C>>(\n event: K,\n listener: Listener<RealtimeEvents<C>[K]>,\n ): Unsubscribe {\n return this.#engine.on(event, listener);\n }\n\n /** Подписывается на одно срабатывание. */\n once<K extends keyof RealtimeEvents<C>>(\n event: K,\n listener: Listener<RealtimeEvents<C>[K]>,\n ): Unsubscribe {\n return this.#engine.once(event, listener);\n }\n\n /**\n * Добавляет промежуточный обработчик или объект, предоставляющий его через `middleware()`.\n *\n * Обработчики выполняются в порядке регистрации. Если `next()` не вызван, обновление не\n * передаётся дальше по цепочке, асинхронным обработчикам и слушателям событий.\n *\n * @returns функция удаления обработчика\n */\n use(middleware: RealtimeMiddleware<C> | RealtimeMiddlewareObj<C>): Unsubscribe {\n return this.#engine.use(middleware);\n }\n\n /** Подписывает асинхронный обработчик на все нормализованные обновления. */\n onUpdate(handler: RealtimeHandler<C>): Unsubscribe;\n /** Подписывает асинхронный обработчик на обновление указанного типа. */\n onUpdate<T extends RealtimeUpdateType>(\n type: T,\n handler: RealtimeHandler<C & RealtimeContext<RealtimeUpdateOfType<T>>>,\n ): Unsubscribe;\n /** Подписывает асинхронный обработчик по функции сужения типа. */\n onUpdate<N extends C>(guard: RealtimeTypeGuard<N, C>, handler: RealtimeHandler<N>): Unsubscribe;\n /** Подписывает асинхронный обработчик по пользовательскому условию. */\n onUpdate(predicate: RealtimePredicate<C>, handler: RealtimeHandler<C>): Unsubscribe;\n onUpdate(\n selectorOrHandler: RealtimeUpdateType | RealtimePredicate<C> | RealtimeHandler<C>,\n selectedHandler?: RealtimeHandler<C>,\n ): Unsubscribe {\n const selectAll = selectedHandler === undefined;\n const handler = selectAll ? (selectorOrHandler as RealtimeHandler<C>) : selectedHandler;\n if (typeof handler !== 'function') {\n throw new ItdConfigError('realtime.onUpdate() принимает функцию обработчика');\n }\n if (\n !selectAll &&\n typeof selectorOrHandler !== 'function' &&\n !Object.values(RealtimeUpdateType).includes(selectorOrHandler)\n ) {\n throw new ItdConfigError(`Неизвестный тип обновления потока: ${String(selectorOrHandler)}`);\n }\n\n let predicate: RealtimePredicate<C>;\n if (selectAll) {\n predicate = () => true;\n } else {\n const selector = selectorOrHandler as RealtimeUpdateType | RealtimePredicate<C>;\n predicate =\n typeof selector === 'function' ? selector : (context) => context.update.type === selector;\n }\n\n return this.#engine.onUpdate(predicate, handler);\n }\n\n /** Подписывает асинхронный обработчик на уведомления, подходящие под фильтр. */\n onNotification<T extends NotificationType>(\n selector: RealtimeNotificationSelector<T>,\n handler: RealtimeHandler<C & RealtimeNotificationContext<T>>,\n ): Unsubscribe;\n /** Подписывает асинхронный обработчик по функции сужения типа уведомления. */\n onNotification<N extends C & RealtimeNotificationContext>(\n guard: (context: C & RealtimeNotificationContext) => context is N,\n handler: RealtimeHandler<N>,\n ): Unsubscribe;\n /** Подписывает асинхронный обработчик по пользовательскому условию. */\n onNotification(\n predicate: (context: C & RealtimeNotificationContext) => boolean,\n handler: RealtimeHandler<C & RealtimeNotificationContext>,\n ): Unsubscribe;\n onNotification(\n selector:\n | RealtimeNotificationSelector\n | ((context: C & RealtimeNotificationContext) => boolean),\n handler: (context: never) => unknown | Promise<unknown>,\n ): Unsubscribe {\n if (typeof handler !== 'function') {\n throw new ItdConfigError('realtime.onNotification() принимает функцию обработчика');\n }\n if (typeof selector !== 'function') validateNotificationSelector(selector);\n\n const predicate: RealtimePredicate<C> = (context) => {\n if (!isNotificationContext(context)) return false;\n return typeof selector === 'function'\n ? selector(context)\n : matchesNotification(context, selector);\n };\n\n return this.#engine.onUpdate(predicate, handler as RealtimeHandler<C>);\n }\n\n /**\n * Поднимает соединение.\n *\n * Повторный вызов при уже живом соединении ничего не делает — это защита от двойного\n * подключения при перерисовке интерфейса.\n *\n * Возвращает управление сразу после запуска: соединение живёт в фоне.\n *\n * @throws если создавший поток клиент уже освобождён\n */\n async connect(): Promise<void> {\n REALTIME_CONNECT_GUARDS.get(this)?.();\n return this.#engine.connect();\n }\n\n /** Закрывает соединение и отменяет запланированные попытки. */\n disconnect(): void {\n this.#engine.disconnect();\n }\n\n /** Ждёт завершения всех принятых обновлений. */\n drain(): Promise<void> {\n return this.#engine.drain();\n }\n\n /** Снимает подписки `on()` и `once()`. Остальные обработчики остаются. */\n removeAllListeners(): void {\n this.#engine.removeAllListeners();\n }\n\n /** Кадр `connected` — не обновление, а подтверждение подключения. */\n #handleFrame(event: TransportEvent): boolean {\n if (event.name !== 'connected') return false;\n\n // Строго строка: `String(null)` дал бы подписчику осмысленно выглядящее «null».\n this.#engine.emit('ready', { userId: pickString(event.data, 'userId') });\n return true;\n }\n\n /**\n * Загружает начальное число непрочитанных.\n *\n * Только при первом подключении: поток присылает счётчик сам, а лишний запрос\n * на каждой попытке переподключения нагружал бы сервер во время недоступности сети.\n */\n async #syncUnreadCount(\n reason: RealtimeSyncReason,\n dispatch: (update: RealtimeUpdate) => void,\n ): Promise<void> {\n if (reason !== 'initial') return;\n\n const count = await this.#deps.fetchUnreadCount();\n dispatch({ type: RealtimeUpdateType.UnreadCount, data: count });\n }\n\n #deliver(update: RealtimeUpdate): void {\n if (update.type === RealtimeUpdateType.Notification) {\n this.#engine.emit('notification', update.data);\n if (update.data.unreadCount !== undefined) {\n this.#engine.emit('unreadCount', update.data.unreadCount);\n }\n return;\n }\n\n if (update.type === RealtimeUpdateType.UnreadCount) {\n this.#engine.emit('unreadCount', update.data);\n return;\n }\n\n if (update.type === RealtimeUpdateType.Unknown) return;\n\n assertNeverUpdate(update);\n }\n}\n\n/** Выбирает транспорт по настройкам и возможностям среды. */\nfunction createTransport<C extends RealtimeContext>(\n deps: RealtimeDeps,\n options: RealtimeOptions<C>,\n): RealtimeTransport {\n const kind = options.transport ?? RealtimeTransportKind.Auto;\n\n if (typeof kind === 'object') return kind;\n\n if (\n kind === RealtimeTransportKind.Poll ||\n (kind === RealtimeTransportKind.Auto && !supportsStreamingBody())\n ) {\n return new PollTransport({\n ...(deps.clock ? { clock: deps.clock } : {}),\n ...(options.pollInterval !== undefined ? { interval: options.pollInterval } : {}),\n });\n }\n\n return new SseTransport({\n ...(deps.clock ? { clock: deps.clock } : {}),\n ...(options.idleTimeout !== undefined ? { idleTimeout: options.idleTimeout } : {}),\n ...(options.handshakeTimeout !== undefined\n ? { handshakeTimeout: options.handshakeTimeout }\n : {}),\n });\n}\n\nfunction assertNeverUpdate(update: never): never {\n throw new TypeError(`Необработанное обновление потока: ${String(update)}`);\n}\n","import type { Unsubscribe } from '../core/emitter.js';\nimport { ItdConfigError } from '../core/errors.js';\nimport {\n captureRealtimeMiddleware,\n type RealtimeMiddleware,\n type RealtimeMiddlewareObj,\n runRealtimeMiddleware,\n withRealtimeMiddlewareSnapshot,\n} from './middleware.js';\nimport type { RealtimeContext, RealtimeContextBase } from './updates.js';\n\n/** Выбирает маршрут обновления. `undefined` и `null` означают отсутствие маршрута. */\nexport type RealtimeRouteSelector<\n K extends PropertyKey,\n C extends RealtimeContextBase = RealtimeContext,\n> = (context: C) => K | null | undefined | Promise<K | null | undefined>;\n\ninterface RouteRegistration<C extends RealtimeContextBase> {\n readonly middleware: readonly RealtimeMiddleware<C>[];\n}\n\n/**\n * Направляет обновления потока в именованные цепочки промежуточных обработчиков.\n *\n * @example\n * ```ts\n * import { RealtimeRouter, RealtimeUpdateType } from 'itd-api';\n *\n * const router = new RealtimeRouter((context) => context.update.type);\n * router.route(RealtimeUpdateType.Notification, async (context, next) => {\n * if (context.update.type === RealtimeUpdateType.Notification) {\n * await handleNotification(context.update.data.notification);\n * }\n * await next();\n * });\n * stream.use(router);\n * ```\n */\nexport class RealtimeRouter<\n K extends PropertyKey = PropertyKey,\n C extends RealtimeContextBase = RealtimeContext,\n> implements RealtimeMiddlewareObj<C>\n{\n readonly #selector: RealtimeRouteSelector<K, C>;\n readonly #routes = new Map<K, RouteRegistration<C>[]>();\n readonly #fallback: RouteRegistration<C>[] = [];\n\n constructor(selector: RealtimeRouteSelector<K, C>) {\n if (typeof selector !== 'function') {\n throw new ItdConfigError('RealtimeRouter принимает функцию выбора маршрута');\n }\n this.#selector = selector;\n }\n\n /** Добавляет промежуточные обработчики к маршруту и возвращает функцию их удаления. */\n route(key: K, ...middleware: readonly RealtimeMiddleware<C>[]): Unsubscribe {\n if (!isPropertyKey(key))\n throw new ItdConfigError('Ключ realtime route должен быть PropertyKey');\n const registration = this.#registration(middleware);\n const registrations = this.#routes.get(key) ?? [];\n registrations.push(registration);\n this.#routes.set(key, registrations);\n\n return () => {\n const current = this.#routes.get(key);\n if (!current) return;\n const index = current.indexOf(registration);\n if (index >= 0) current.splice(index, 1);\n if (current.length === 0) this.#routes.delete(key);\n };\n }\n\n /** Добавляет промежуточные обработчики для обновлений без зарегистрированного маршрута. */\n otherwise(...middleware: readonly RealtimeMiddleware<C>[]): Unsubscribe {\n const registration = this.#registration(middleware);\n this.#fallback.push(registration);\n\n return () => {\n const index = this.#fallback.indexOf(registration);\n if (index >= 0) this.#fallback.splice(index, 1);\n };\n }\n\n /** Возвращает снимок маршрутов для `stream.use(router)` или ручной композиции. */\n middleware(): RealtimeMiddleware<C> {\n const middleware: RealtimeMiddleware<C> = (context, next) =>\n this.#captureMiddleware()(context, next);\n return withRealtimeMiddlewareSnapshot(middleware, () => this.#captureMiddleware());\n }\n\n #captureMiddleware(): RealtimeMiddleware<C> {\n const routes = new Map<K, readonly RealtimeMiddleware<C>[]>();\n for (const [key, registrations] of this.#routes) {\n routes.set(\n key,\n registrations.flatMap(({ middleware }) => middleware).map(captureRealtimeMiddleware),\n );\n }\n const fallback = this.#fallback\n .flatMap(({ middleware }) => middleware)\n .map(captureRealtimeMiddleware);\n\n return async (context, next) => {\n const key = await this.#selector(context);\n if (key != null && !isPropertyKey(key)) {\n throw new ItdConfigError(\n 'Функция выбора маршрута должна возвращать PropertyKey, null или undefined',\n );\n }\n const route = key == null ? undefined : routes.get(key);\n const chain = route && route.length > 0 ? route : fallback;\n\n if (chain.length === 0) {\n await next();\n return;\n }\n\n await runRealtimeMiddleware(chain, context, next);\n };\n }\n\n #registration(middleware: readonly RealtimeMiddleware<C>[]): RouteRegistration<C> {\n if (middleware.length === 0) {\n throw new ItdConfigError('Маршрут должен содержать хотя бы один обработчик');\n }\n for (const item of middleware) {\n if (typeof item !== 'function') {\n throw new ItdConfigError('Маршрут принимает только функции обработки');\n }\n }\n return { middleware: [...middleware] };\n }\n}\n\nfunction isPropertyKey(value: unknown): value is PropertyKey {\n return typeof value === 'string' || typeof value === 'number' || typeof value === 'symbol';\n}\n","import { ItdConfigError } from '../core/errors.js';\nimport {\n captureRealtimeMiddleware,\n deferRealtimeMiddleware,\n type RealtimeMiddleware,\n type RealtimeMiddlewareObj,\n type RealtimeNext,\n type RealtimeTypeGuard,\n runRealtimeMiddleware,\n withRealtimeMiddlewareSnapshot,\n} from './middleware.js';\nimport { RealtimeRouter, type RealtimeRouteSelector } from './router.js';\nimport type { RealtimeContext, RealtimeContextBase } from './updates.js';\n\n/** Функция или объектный middleware, который можно добавить в {@link RealtimeComposer}. */\nexport type RealtimeMiddlewareLike<C extends RealtimeContextBase = RealtimeContext> =\n | RealtimeMiddleware<C>\n | RealtimeMiddlewareObj<C>;\n\n/** Один middleware или последовательность middleware для ветки composer. */\nexport type RealtimeMiddlewareGroup<C extends RealtimeContextBase = RealtimeContext> =\n | RealtimeMiddlewareLike<C>\n | readonly RealtimeMiddlewareLike<C>[];\n\n/** Синхронное или асинхронное условие ветвления composer. */\nexport type RealtimeFilter<C extends RealtimeContextBase = RealtimeContext> = (\n context: C,\n) => boolean | Promise<boolean>;\n\n/** Ошибка локальной realtime-ветки вместе с контекстом обрабатываемого обновления. */\nexport interface RealtimeErrorContext<C extends RealtimeContextBase = RealtimeContext> {\n /** Исходное исключение middleware. */\n readonly error: unknown;\n /** Контекст обновления, на котором завершилась ветка. */\n readonly context: C;\n}\n\n/** Обработчик локальной границы ошибок composer. */\nexport type RealtimeErrorBoundary<C extends RealtimeContextBase = RealtimeContext> = (\n failure: RealtimeErrorContext<C>,\n next: RealtimeNext,\n) => unknown | Promise<unknown>;\n\n/** Именованные ветки для {@link RealtimeComposer.route}. */\nexport type RealtimeRouteTable<\n K extends string | symbol,\n C extends RealtimeContextBase = RealtimeContext,\n> = Partial<Record<K, RealtimeMiddlewareGroup<C>>>;\n\nfunction middlewareFunction<C extends RealtimeContextBase>(\n middleware: RealtimeMiddlewareLike<C>,\n): RealtimeMiddleware<C> {\n if (typeof middleware === 'function') return middleware;\n if (\n typeof middleware !== 'object' ||\n middleware === null ||\n typeof middleware.middleware !== 'function'\n ) {\n throw new ItdConfigError(\n 'RealtimeComposer принимает функцию обработки или объект с middleware()',\n );\n }\n return deferRealtimeMiddleware(middleware);\n}\n\nfunction middlewareGroup<C extends RealtimeContextBase>(\n group: RealtimeMiddlewareGroup<C>,\n): RealtimeMiddleware<C>[] {\n const middleware = Array.isArray(group) ? group : [group];\n if (middleware.length === 0) {\n throw new ItdConfigError('Ветка RealtimeComposer должна содержать хотя бы один middleware');\n }\n return middleware.map((item) => middlewareFunction(item));\n}\n\ninterface BoundaryContinuation {\n readonly next: RealtimeNext;\n readonly called: () => boolean;\n readonly settled: () => Promise<void>;\n}\n\nfunction boundaryContinuation(): BoundaryContinuation {\n let called = false;\n let duplicate: Promise<void> | undefined;\n\n return {\n next: () => {\n if (!called) {\n called = true;\n return Promise.resolve();\n }\n\n const failure = Promise.reject(\n new Error('next() в обработчике realtime error boundary вызван повторно'),\n );\n duplicate = Promise.all(duplicate ? [duplicate, failure] : [failure]).then(() => undefined);\n return failure;\n },\n called: () => called,\n settled: async () => {\n await duplicate;\n },\n };\n}\n\nasync function runBoundaryStep(\n run: () => unknown | Promise<unknown>,\n continuation: BoundaryContinuation,\n): Promise<void> {\n let failure: { error: unknown } | undefined;\n try {\n await run();\n } catch (error) {\n failure = { error };\n }\n try {\n await continuation.settled();\n } catch (error) {\n failure ??= { error };\n }\n if (failure) throw failure.error;\n}\n\n/**\n * Собирает переиспользуемый feature-модуль из realtime middleware.\n *\n * Composer не открывает соединение и не планирует конкурентность: готовый объект подключается\n * через `stream.use(composer)`, а выполнение остаётся обязанностью существующего dispatcher.\n * Для каждого принятого update используется снимок всей вложенной структуры composer.\n *\n * @example\n * ```ts\n * const feature = new RealtimeComposer<AppRealtimeContext>();\n * const safe = feature.errorBoundary(reportFeatureError);\n * safe.filter(isPostUpdate).use(handlePost);\n * stream.use(feature);\n * ```\n */\nexport class RealtimeComposer<C extends RealtimeContextBase = RealtimeContext>\n implements RealtimeMiddlewareObj<C>\n{\n readonly #middleware: RealtimeMiddleware<C>[] = [];\n\n constructor(...middleware: readonly RealtimeMiddlewareLike<C>[]) {\n this.use(...middleware);\n }\n\n /** Добавляет middleware в конец текущей onion-цепочки. */\n use(...middleware: readonly RealtimeMiddlewareLike<C>[]): this {\n const normalized = middleware.map(middlewareFunction);\n this.#middleware.push(...normalized);\n return this;\n }\n\n /** Создаёт дочернюю ветку, выполняемую только когда type guard принимает контекст. */\n filter<N extends C>(\n predicate: RealtimeTypeGuard<N, C>,\n ...middleware: readonly RealtimeMiddlewareLike<N>[]\n ): RealtimeComposer<N>;\n /** Создаёт дочернюю ветку по синхронному или асинхронному условию. */\n filter(\n predicate: RealtimeFilter<C>,\n ...middleware: readonly RealtimeMiddlewareLike<C>[]\n ): RealtimeComposer<C>;\n filter<N extends C>(\n predicate: RealtimeFilter<C>,\n ...middleware: readonly RealtimeMiddlewareLike<N>[]\n ): RealtimeComposer<N> {\n if (typeof predicate !== 'function') {\n throw new ItdConfigError('RealtimeComposer.filter() принимает функцию условия');\n }\n\n const child = new RealtimeComposer<N>(...middleware);\n const source: RealtimeMiddlewareObj<C> = {\n middleware: () => {\n const nested = captureRealtimeMiddleware(child.middleware());\n return async (context, next) => {\n if (await predicate(context)) await nested(context as N, next);\n else await next();\n };\n },\n };\n this.#middleware.push(deferRealtimeMiddleware(source));\n return child;\n }\n\n /**\n * Направляет контекст в одну именованную ветку.\n *\n * Неизвестный ключ без fallback пропускает update следующему внешнему middleware. Для\n * динамической регистрации и числовых ключей используйте {@link RealtimeRouter} напрямую.\n */\n route<K extends string | symbol>(\n selector: RealtimeRouteSelector<K, C>,\n routes: RealtimeRouteTable<K, C>,\n fallback?: RealtimeMiddlewareGroup<C>,\n ): this {\n if (typeof routes !== 'object' || routes === null || Array.isArray(routes)) {\n throw new ItdConfigError('RealtimeComposer.route() принимает объект именованных веток');\n }\n\n const router = new RealtimeRouter<K, C>(selector);\n let registrations = 0;\n for (const key of Reflect.ownKeys(routes)) {\n const group = routes[key as K];\n if (group === undefined) continue;\n router.route(key as K, ...middlewareGroup(group));\n registrations += 1;\n }\n if (fallback !== undefined) {\n router.otherwise(...middlewareGroup(fallback));\n registrations += 1;\n }\n if (registrations === 0) {\n throw new ItdConfigError('RealtimeComposer.route() требует хотя бы одну ветку или fallback');\n }\n\n return this.use(router);\n }\n\n /**\n * Создаёт дочернюю ветку с локальной границей ошибок.\n *\n * Граница защищает только переданные и затем добавленные в возвращённый composer middleware.\n * Ошибки внешней цепочки намеренно не перехватываются. Обработчик может вызвать `next()`,\n * чтобы после ошибки продолжить внешнюю цепочку, либо повторно выбросить исключение. Внешняя\n * цепочка начинается после полного завершения защищённой ветки, а не входит в её onion-вызов.\n */\n errorBoundary(\n handler: RealtimeErrorBoundary<C>,\n ...middleware: readonly RealtimeMiddlewareLike<C>[]\n ): RealtimeComposer<C> {\n if (typeof handler !== 'function') {\n throw new ItdConfigError('RealtimeComposer.errorBoundary() принимает обработчик ошибки');\n }\n\n const child = new RealtimeComposer<C>(...middleware);\n const source: RealtimeMiddlewareObj<C> = {\n middleware: () => {\n const nested = captureRealtimeMiddleware(child.middleware());\n return async (context, next) => {\n let continuation = boundaryContinuation();\n try {\n await runBoundaryStep(() => nested(context, continuation.next), continuation);\n } catch (error) {\n continuation = boundaryContinuation();\n await runBoundaryStep(\n () => handler({ error, context }, continuation.next),\n continuation,\n );\n }\n\n if (continuation.called()) await next();\n };\n },\n };\n this.#middleware.push(deferRealtimeMiddleware(source));\n return child;\n }\n\n /** Возвращает snapshot-aware middleware для `stream.use()` или вложенного composer. */\n middleware(): RealtimeMiddleware<C> {\n const middleware: RealtimeMiddleware<C> = (context, next) =>\n this.#captureMiddleware()(context, next);\n return withRealtimeMiddlewareSnapshot(middleware, () => this.#captureMiddleware());\n }\n\n #captureMiddleware(): RealtimeMiddleware<C> {\n const snapshot = this.#middleware.map(captureRealtimeMiddleware);\n return (context, next) => runRealtimeMiddleware(snapshot, context, next);\n }\n}\n","import { type ItdClock, systemClock } from '../../core/clock.js';\nimport { ItdConfigError } from '../../core/errors.js';\nimport { maskSecret, redactUrl } from '../../core/redact.js';\nimport { buildQuery, joinUrl } from '../../core/url.js';\nimport {\n type RealtimeTransport,\n type TransportContext,\n UnauthorizedStreamError,\n} from './transport.js';\n\n/** Стандартный путь WebSocket-подключения. */\nexport const WEBSOCKET_PATH = '/api/ws';\n\n/** Предел кадров, ожидающих разбора. Переполнение закрывает соединение. @internal */\nexport const MAX_PENDING_FRAMES = 256;\n\n/** Дополнительные параметры конструктора, поддерживаемые Node-реализациями вроде `ws`. */\nexport interface WebSocketImplementationOptions {\n headers?: Record<string, string> | undefined;\n handshakeTimeout?: number | undefined;\n}\n\n/** Конструктор WebSocket, который можно передать вместо глобальной реализации. */\nexport interface WebSocketLike {\n new (\n url: string | URL,\n protocols?: string | string[],\n options?: WebSocketImplementationOptions,\n ): unknown;\n}\n\n/** Определяет, был ли отказ до открытия сокета вызван недействительным токеном. */\nexport type WebSocketOpenFailureClassifier = (\n error: unknown,\n signal: AbortSignal,\n) => boolean | Promise<boolean>;\n\n/** Настройки WebSocket-транспорта. */\nexport interface WebSocketTransportOptions {\n /** Путь апгрейда. По умолчанию `/api/ws`. */\n path?: string | undefined;\n /** Реализация WebSocket для сред без глобальной либо для передачи заголовков апгрейда. */\n webSocketImpl?: WebSocketLike | undefined;\n /** Способ передачи токена. `auto` выбирает заголовок при инъекции и query иначе. */\n auth?: 'query' | 'header' | 'auto' | undefined;\n /** Молчание открытого соединения до переподключения, мс. По умолчанию 90 000. */\n idleTimeout?: number | undefined;\n /** Период текстового `ping`, мс. По умолчанию 30 000. */\n keepAlive?: number | undefined;\n /** Максимальное время установки соединения, мс. По умолчанию 20 000. */\n handshakeTimeout?: number | undefined;\n /**\n * Проверяет отказ до `open`, когда среда скрыла HTTP-статус WebSocket-upgrade.\n * `true` преобразует отказ в {@link UnauthorizedStreamError}.\n */\n classifyOpenFailure?: WebSocketOpenFailureClassifier | undefined;\n /** Часы транспорта. Обычно подменяются только в тестах. */\n clock?: ItdClock | undefined;\n}\n\ninterface SocketMessageEvent {\n readonly data: unknown;\n}\n\ninterface SocketCloseEvent {\n readonly code?: unknown;\n readonly reason?: unknown;\n readonly wasClean?: unknown;\n}\n\ninterface WebSocketConnection {\n readonly readyState: number;\n binaryType?: string;\n addEventListener(type: string, listener: (event: unknown) => void): void;\n removeEventListener(type: string, listener: (event: unknown) => void): void;\n send(data: string): void;\n close(code?: number, reason?: string): void;\n}\n\nconst CONNECTING = 0;\nconst OPEN = 1;\nconst NORMAL_CLOSURE = 1000;\nconst UNAUTHORIZED_CLOSE_CODES = new Set([1008, 4401]);\n\nfunction validateDuration(value: number | undefined, name: string): void {\n if (value !== undefined && (!Number.isFinite(value) || value < 0)) {\n throw new ItdConfigError(\n `WebSocketTransport.${name} должен быть числом не меньше 0, получено: ${value}`,\n );\n }\n}\n\nfunction isWebSocketConnection(value: unknown): value is WebSocketConnection {\n if (typeof value !== 'object' || value === null) return false;\n const socket = value as Partial<WebSocketConnection>;\n return (\n typeof socket.readyState === 'number' &&\n typeof socket.addEventListener === 'function' &&\n typeof socket.removeEventListener === 'function' &&\n typeof socket.send === 'function' &&\n typeof socket.close === 'function'\n );\n}\n\nfunction abortError(signal: AbortSignal): unknown {\n if (signal.reason !== undefined) return signal.reason;\n const error = new Error('WebSocket-подключение отменено');\n error.name = 'AbortError';\n return error;\n}\n\nfunction errorMessage(error: unknown): string | undefined {\n if (error instanceof Error && error.message) return error.message;\n if (typeof error === 'object' && error !== null && 'message' in error) {\n const message = (error as { message?: unknown }).message;\n if (typeof message === 'string' && message) return message;\n }\n return undefined;\n}\n\nfunction safeDetail(error: unknown, url: string, token: string | null): string | undefined {\n const message = errorMessage(error);\n if (!message) return undefined;\n const safe = message.replaceAll(url, redactUrl(url));\n return token ? safe.replaceAll(token, maskSecret(token)) : safe;\n}\n\nfunction readCloseEvent(event: unknown): { code: number; reason: string; wasClean: boolean } {\n const close =\n typeof event === 'object' && event !== null ? (event as SocketCloseEvent) : undefined;\n return {\n code: typeof close?.code === 'number' ? close.code : 1006,\n reason: typeof close?.reason === 'string' ? close.reason : '',\n wasClean: close?.wasClean === true,\n };\n}\n\nfunction readMessage(event: unknown): string | Promise<string> | undefined {\n const data =\n typeof event === 'object' && event !== null && 'data' in event\n ? (event as SocketMessageEvent).data\n : undefined;\n\n if (typeof data === 'string') return data;\n if (data instanceof ArrayBuffer) return new TextDecoder().decode(data);\n if (ArrayBuffer.isView(data)) {\n return new TextDecoder().decode(new Uint8Array(data.buffer, data.byteOffset, data.byteLength));\n }\n if (typeof Blob !== 'undefined' && data instanceof Blob) return data.text();\n return undefined;\n}\n\nfunction eventName(data: unknown): string {\n return typeof data === 'object' && data !== null && 'type' in data\n ? String((data as { type: unknown }).type)\n : 'message';\n}\n\n/** Транспорт исходных realtime-событий поверх стандартного WebSocket. */\nexport class WebSocketTransport implements RealtimeTransport {\n readonly name = 'ws';\n\n readonly #path: string;\n readonly #webSocketImpl: WebSocketLike | undefined;\n readonly #auth: 'query' | 'header' | 'auto';\n readonly #idleTimeout: number;\n readonly #keepAlive: number;\n readonly #handshakeTimeout: number;\n readonly #classifyOpenFailure: WebSocketOpenFailureClassifier | undefined;\n readonly #clock: ItdClock;\n\n constructor(options: WebSocketTransportOptions = {}) {\n const path = options.path ?? WEBSOCKET_PATH;\n if (\n typeof path !== 'string' ||\n path.trim() === '' ||\n path.includes('?') ||\n path.includes('#') ||\n /^[a-z][a-z\\d+.-]*:/i.test(path)\n ) {\n throw new ItdConfigError('WebSocketTransport.path должен быть путём без query и fragment');\n }\n if (options.webSocketImpl !== undefined && typeof options.webSocketImpl !== 'function') {\n throw new ItdConfigError('WebSocketTransport.webSocketImpl должен быть конструктором');\n }\n if (\n options.classifyOpenFailure !== undefined &&\n typeof options.classifyOpenFailure !== 'function'\n ) {\n throw new ItdConfigError('WebSocketTransport.classifyOpenFailure должен быть функцией');\n }\n\n const auth = options.auth ?? 'auto';\n if (auth !== 'auto' && auth !== 'query' && auth !== 'header') {\n throw new ItdConfigError(`Неизвестный способ авторизации WebSocket: ${String(auth)}`);\n }\n if (auth === 'header' && !options.webSocketImpl) {\n throw new ItdConfigError(\n 'WebSocketTransport.auth=\"header\" требует webSocketImpl с поддержкой заголовков',\n );\n }\n\n validateDuration(options.idleTimeout, 'idleTimeout');\n validateDuration(options.keepAlive, 'keepAlive');\n validateDuration(options.handshakeTimeout, 'handshakeTimeout');\n\n this.#path = path;\n this.#webSocketImpl = options.webSocketImpl;\n this.#auth = auth;\n this.#idleTimeout = options.idleTimeout ?? 90_000;\n this.#keepAlive = options.keepAlive ?? 30_000;\n this.#handshakeTimeout = options.handshakeTimeout ?? 20_000;\n this.#classifyOpenFailure = options.classifyOpenFailure;\n this.#clock = options.clock ?? systemClock;\n }\n\n async connect(context: TransportContext): Promise<void> {\n if (context.signal.aborted) throw abortError(context.signal);\n\n const token = context.authorize ? await context.getToken() : null;\n if (context.signal.aborted) throw abortError(context.signal);\n if (context.authorize && !token) throw new UnauthorizedStreamError();\n\n const injected = this.#webSocketImpl !== undefined;\n const implementation = this.#webSocketImpl ?? this.#globalImplementation();\n const auth = this.#auth === 'auto' ? (injected ? 'header' : 'query') : this.#auth;\n const { requestUrl, socketUrl } = this.#urls(context.baseUrl);\n const url = auth === 'query' && token ? `${socketUrl}${buildQuery({ token })}` : socketUrl;\n\n let headers: Record<string, string> | undefined;\n if (injected) {\n const resolved = await context.baseHeaders(requestUrl);\n if (context.signal.aborted) throw abortError(context.signal);\n if (auth === 'header' && token) resolved.set('Authorization', `Bearer ${token}`);\n headers = Object.fromEntries(resolved.entries());\n }\n\n let value: unknown;\n try {\n value = injected\n ? new implementation(url, [], {\n ...(headers ? { headers } : {}),\n ...(this.#handshakeTimeout > 0 ? { handshakeTimeout: this.#handshakeTimeout } : {}),\n })\n : new implementation(url);\n } catch (error) {\n const detail = safeDetail(error, url, token);\n throw new Error(\n `Не удалось создать WebSocket ${redactUrl(url)}${detail ? `: ${detail}` : ''}`,\n );\n }\n\n if (!isWebSocketConnection(value)) {\n throw new ItdConfigError(\n 'webSocketImpl вернул объект без WebSocket-методов addEventListener, send и close',\n );\n }\n\n try {\n value.binaryType = 'arraybuffer';\n } catch {\n // Не все совместимые реализации позволяют менять способ выдачи бинарных кадров.\n }\n\n return this.#listen(value, url, token, context);\n }\n\n #globalImplementation(): WebSocketLike {\n const implementation = Reflect.get(globalThis, 'WebSocket');\n if (typeof implementation !== 'function') {\n throw new ItdConfigError(\n 'В этой среде нет WebSocket: передайте реализацию через webSocketImpl',\n );\n }\n return implementation as WebSocketLike;\n }\n\n #urls(baseUrl: string): { requestUrl: string; socketUrl: string } {\n const request = new URL(joinUrl(baseUrl, this.#path));\n const socket = new URL(request);\n if (request.protocol === 'https:') socket.protocol = 'wss:';\n else if (request.protocol === 'http:') socket.protocol = 'ws:';\n else {\n throw new ItdConfigError(\n `WebSocketTransport требует baseUrl с http или https, получено: ${request.protocol}`,\n );\n }\n return { requestUrl: request.toString(), socketUrl: socket.toString() };\n }\n\n #listen(\n socket: WebSocketConnection,\n url: string,\n token: string | null,\n context: TransportContext,\n ): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n let settled = false;\n let finishing = false;\n let opened = false;\n let discardMessages = false;\n let socketError: unknown;\n let messageQueue = Promise.resolve();\n let pendingFrames = 0;\n let cancelHandshake: (() => void) | undefined;\n let cancelIdle: (() => void) | undefined;\n let cancelKeepAlive: (() => void) | undefined;\n\n const stopSocketListeners = () => {\n cancelHandshake?.();\n cancelIdle?.();\n cancelKeepAlive?.();\n cancelHandshake = undefined;\n cancelIdle = undefined;\n cancelKeepAlive = undefined;\n socket.removeEventListener('open', onOpen);\n socket.removeEventListener('message', onMessage);\n socket.removeEventListener('error', onError);\n socket.removeEventListener('close', onClose);\n };\n\n const finish = (error?: unknown) => {\n if (settled) return;\n settled = true;\n stopSocketListeners();\n context.signal.removeEventListener('abort', onAbort);\n if (error !== undefined) reject(error);\n else resolve();\n };\n\n const closeWithError = (error: unknown, code = 4000, reason = 'transport error') => {\n if (settled || finishing) return;\n socketError = error;\n finishing = true;\n stopSocketListeners();\n try {\n if (socket.readyState === CONNECTING || socket.readyState === OPEN) {\n socket.close(code, reason);\n }\n } catch {\n // Ошибка уже сохранена и будет возвращена вызывающему коду.\n }\n void messageQueue.then(\n () => finish(error),\n (queueError: unknown) => finish(queueError),\n );\n };\n\n const armIdleTimer = () => {\n cancelIdle?.();\n if (this.#idleTimeout <= 0) return;\n cancelIdle = this.#clock.schedule(() => {\n closeWithError(\n new Error(`WebSocket ${redactUrl(url)} молчит дольше допустимого`),\n 4000,\n 'idle timeout',\n );\n }, this.#idleTimeout);\n };\n\n const scheduleKeepAlive = () => {\n if (this.#keepAlive <= 0 || settled || finishing) return;\n cancelKeepAlive = this.#clock.schedule(() => {\n if (settled || finishing || socket.readyState !== OPEN) return;\n try {\n socket.send('ping');\n } catch (error) {\n const detail = safeDetail(error, url, token);\n closeWithError(\n new Error(\n `Не удалось отправить WebSocket ping ${redactUrl(url)}` +\n (detail ? `: ${detail}` : ''),\n ),\n );\n return;\n }\n scheduleKeepAlive();\n }, this.#keepAlive);\n };\n\n const onOpen = () => {\n if (settled || finishing || opened) return;\n opened = true;\n cancelHandshake?.();\n cancelHandshake = undefined;\n armIdleTimer();\n scheduleKeepAlive();\n try {\n context.onOpen();\n } catch (error) {\n closeWithError(error);\n }\n };\n\n const onMessage = (event: unknown) => {\n if (settled || finishing) return;\n armIdleTimer();\n let pending: Promise<{ readonly raw: string } | { readonly error: unknown }> | undefined;\n try {\n const raw = readMessage(event);\n if (raw !== undefined) {\n pending = Promise.resolve(raw).then(\n (value) => ({ raw: value }),\n (error: unknown) => ({ error }),\n );\n }\n } catch (error) {\n pending = Promise.resolve({ error });\n }\n if (pending === undefined) return;\n\n if (pendingFrames >= MAX_PENDING_FRAMES) {\n closeWithError(\n new Error(`WebSocket ${redactUrl(url)}: очередь кадров переполнена`),\n 4000,\n 'queue overflow',\n );\n return;\n }\n\n pendingFrames += 1;\n messageQueue = messageQueue\n .then(async () => {\n pendingFrames -= 1;\n if (settled || discardMessages) return;\n\n const decoded = await pending;\n if ('error' in decoded) {\n context.onParseError(decoded.error, '[binary WebSocket frame]');\n return;\n }\n const { raw } = decoded;\n if (settled || discardMessages || raw === 'pong') return;\n\n let data: unknown;\n try {\n data = JSON.parse(raw) as unknown;\n } catch (error) {\n context.onParseError(error, raw);\n return;\n }\n context.onEvent({ name: eventName(data), data });\n })\n .catch((error: unknown) => {\n if (finishing) throw error;\n closeWithError(error);\n });\n };\n\n const onError = (event: unknown) => {\n if (settled || finishing) return;\n socketError =\n typeof event === 'object' && event !== null && 'error' in event\n ? (event as { error?: unknown }).error\n : event;\n };\n\n const finishClose = async (event: unknown): Promise<void> => {\n const { code, reason, wasClean } = readCloseEvent(event);\n let outcome: unknown;\n\n if (context.authorize && UNAUTHORIZED_CLOSE_CODES.has(code)) {\n outcome = new UnauthorizedStreamError();\n } else if (socketError !== undefined) {\n const detail = safeDetail(socketError, url, token);\n outcome = new Error(\n `WebSocket ${redactUrl(url)} завершился ошибкой${detail ? `: ${detail}` : ''}`,\n );\n } else if (opened && code === NORMAL_CLOSURE && wasClean) {\n outcome = undefined;\n } else {\n const safeReason = token ? reason.replaceAll(token, maskSecret(token)) : reason;\n outcome = new Error(\n `WebSocket ${redactUrl(url)} закрыт с кодом ${code}` +\n (safeReason ? `: ${safeReason}` : ''),\n );\n }\n\n if (\n !opened &&\n context.authorize &&\n !(outcome instanceof UnauthorizedStreamError) &&\n this.#classifyOpenFailure\n ) {\n try {\n if (await this.#classifyOpenFailure(outcome, context.signal)) {\n outcome = new UnauthorizedStreamError();\n }\n } catch {\n // Ошибка проверки не должна скрывать исходную ошибку соединения.\n }\n }\n\n if (context.signal.aborted) {\n discardMessages = true;\n finish(abortError(context.signal));\n return;\n }\n\n try {\n await messageQueue;\n finish(outcome);\n } catch (error) {\n finish(error);\n }\n };\n\n const onClose = (event: unknown) => {\n if (settled || finishing) return;\n if (context.signal.aborted) {\n discardMessages = true;\n finish(abortError(context.signal));\n return;\n }\n finishing = true;\n stopSocketListeners();\n void finishClose(event);\n };\n\n const onAbort = () => {\n if (settled) return;\n const error = abortError(context.signal);\n discardMessages = true;\n finishing = true;\n stopSocketListeners();\n try {\n if (socket.readyState === CONNECTING || socket.readyState === OPEN) {\n socket.close(NORMAL_CLOSURE, 'aborted');\n }\n } catch {\n // Отмена всё равно завершает connect(), даже если реализация уже закрыла сокет.\n }\n finish(error);\n };\n\n socket.addEventListener('open', onOpen);\n socket.addEventListener('message', onMessage);\n socket.addEventListener('error', onError);\n socket.addEventListener('close', onClose);\n context.signal.addEventListener('abort', onAbort, { once: true });\n\n if (this.#handshakeTimeout > 0) {\n cancelHandshake = this.#clock.schedule(() => {\n closeWithError(\n new Error(`Истёк таймаут установки WebSocket ${redactUrl(url)}`),\n 4000,\n 'handshake timeout',\n );\n }, this.#handshakeTimeout);\n }\n\n if (context.signal.aborted) onAbort();\n });\n }\n}\n"],"x_google_ignoreList":[7],"mappings":";;;;;;;;;;;;;;;AAmBA,IAAa,UAAb,MAA6B;CAC3B,6BAAsB,IAAI,IAAwC;CAClE;CAEA,YAAY,iBAA4C;EACtD,KAAKC,WAAW;CAClB;;;;;;;;;;;;CAaA,GAA2B,OAAU,UAA4C;EAC/E,MAAM,MAAM,KAAKD,WAAW,IAAI,KAAK,qBAAK,IAAI,IAAI;EAClD,IAAI,IAAI,QAA2B;EACnC,KAAKA,WAAW,IAAI,OAAO,GAAG;EAE9B,aAAa,KAAK,IAAI,OAAO,QAAQ;CACvC;;CAGA,KAA6B,OAAU,UAA4C;EACjF,MAAM,MAAM,KAAK,GAAG,QAAQ,YAAY;GACtC,IAAI;GACJ,SAAS,OAAO;EAClB,CAAC;EACD,OAAO;CACT;;CAGA,IAA4B,OAAU,UAAqC;EACzE,KAAKA,WAAW,IAAI,KAAK,CAAC,EAAE,OAAO,QAA2B;CAChE;;CAGA,KAA6B,OAAU,SAA0B;EAC/D,MAAM,MAAM,KAAKA,WAAW,IAAI,KAAK;EACrC,IAAI,CAAC,KAAK;EAGV,KAAK,MAAM,YAAY,CAAC,GAAG,GAAG,GAC5B,IAAI;GACF,SAAkC,OAAO;EAC3C,SAAS,OAAO;GACd,KAAKC,WAAW,KAAK;EACvB;CAEJ;;CAGA,cAAc,OAA6B;EACzC,OAAO,KAAKD,WAAW,IAAI,KAAK,CAAC,EAAE,QAAQ;CAC7C;;CAGA,qBAA2B;EACzB,KAAKA,WAAW,MAAM;CACxB;AACF;;;;;;;;;;;;AAaA,SAAgB,oBACd,QACA,OACA,OACM;CACN,MAAM,UAAU,gCAAgC;CAChD,IAAI,QAAQ,OAAO,MAAM,SAAS,KAAK;MAClC,QAAQ,MAAM,aAAa,WAAW,KAAK;AAClD;;;ACnEA,MAAM,+BAA+B,OAAO,qCAAqC;;AAOjF,SAAgB,0BACd,YACuB;CACvB,MAAM,UAAW,WAAiD;CAClE,IAAI,CAAC,SAAS,OAAO;CAErB,MAAM,WAAW,QAAQ;CACzB,IAAI,OAAO,aAAa,YACtB,MAAM,IAAI,UAAU,wDAAwD;CAE9E,OAAO;AACT;;AAGA,SAAgB,+BACd,YACA,SACuB;CACvB,OAAO,eAAe,YAAY,8BAA8B,EAAE,OAAO,QAAQ,CAAC;CAClF,OAAO;AACT;;AAGA,SAAgB,wBACd,QACuB;CACvB,MAAM,gBAAuC;EAC3C,MAAM,aAAa,OAAO,WAAW;EACrC,IAAI,OAAO,eAAe,YACxB,MAAM,IAAI,eAAe,2DAA2D;EAEtF,OAAO,0BAA0B,UAAU;CAC7C;CAEA,MAAM,YAAmC,SAAS,SAAS,QAAQ,CAAC,CAAC,SAAS,IAAI;CAClF,OAAO,+BAA+B,UAAU,OAAO;AACzD;;AAiCA,eAAsB,sBACpB,YACA,SACA,UACe;CACf,IAAI,YAAY;CAEhB,MAAM,WAAW,OAAO,UAAiC;EACvD,IAAI,SAAS,WAAW,MAAM,IAAI,MAAM,oDAAoD;EAC5F,YAAY;EAEZ,MAAM,UAAU,WAAW;EAC3B,IAAI,CAAC,SAAS;GACZ,MAAM,SAAS;GACf;EACF;EAEA,IAAI;EACJ,IAAI;EACJ,IAAI;EAEJ,MAAM,aAA2B;GAC/B,IAAI,CAAC,YAAY;IACf,aAAa,SAAS,QAAQ,CAAC;IAC/B,OAAO;GACT;GAEA,MAAM,YAAY,QAAQ,uBACxB,IAAI,MAAM,0DAA0D,CACtE;GACA,iBAAiB,QAAQ,IAAI,iBAAiB,CAAC,gBAAgB,SAAS,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,WACjF,KAAA,CACR;GACA,OAAO;EACT;EAEA,IAAI;GACF,MAAM,QAAQ,SAAS,IAAI;EAC7B,SAAS,OAAO;GACd,UAAU,EAAE,MAAM;EACpB;EAEA,IAAI;GACF,MAAM;GACN,MAAM;EACR,SAAS,OAAO;GACd,YAAY,EAAE,MAAM;EACtB;EAEA,IAAI,SAAS,MAAM,QAAQ;CAC7B;CAEA,MAAM,SAAS,CAAC;AAClB;;AAGA,IAAa,qBAAb,MAAiF;CAC/E;CACA;CACA,cAAgD,CAAC;CACjD,YAA+C,CAAC;CAChD,SAAqC,CAAC;CACtC,8BAAuB,IAAI,IAAiB;CAC5C,gCAAyB,IAAI,IAAgB;CAE7C,UAAU;CAEV,YAAY,SAAuC,OAAmC;EACpF,KAAKE,WAAW;EAChB,KAAKC,SAAS;CAChB;CAEA,IAAI,YAAgD;EAClD,KAAKC,YAAY,KAAK,UAAU;EAChC,aAAa;GACX,MAAM,QAAQ,KAAKA,YAAY,QAAQ,UAAU;GACjD,IAAI,SAAS,GAAG,KAAKA,YAAY,OAAO,OAAO,CAAC;EAClD;CACF;CAEA,GAAG,WAAiC,SAA0C;EAC5E,MAAM,eAAe;GAAE;GAAW;EAAQ;EAC1C,KAAKC,UAAU,KAAK,YAAY;EAEhC,aAAa;GACX,MAAM,QAAQ,KAAKA,UAAU,QAAQ,YAAY;GACjD,IAAI,SAAS,GAAG,KAAKA,UAAU,OAAO,OAAO,CAAC;EAChD;CACF;;;;;;CAOA,SAAS,SAAY,aAAiC;EACpD,IAAI;EACJ,IAAI;EACJ,IAAI;GACF,OAAO,KAAKI,SAAS,OAAO;GAC5B,aAAa,KAAKL,YAAY,IAAI,yBAAyB;EAC7D,SAAS,OAAO;GACd,KAAKD,OAAO,gBAAgB,OAAO,OAAO;GAC1C;EACF;EAEA,MAAM,WACJ,gBAAgB,KAAA,IACZ,KACA,KAAKG,OAAO,WAAW,YAAY,QAAQ,gBAAgB,WAAW;EAC5E,IAAI,WAAW,KAAK,KAAKA,OAAO,UAAA,KAA+B;GAC7D,KAAKH,OAAO,SAAS;GACrB;EACF;EAEA,MAAM,OAAwB;GAC5B;GACA;GACA,UAAU,CAAC,GAAG,KAAKE,SAAS;GAC5B;GACA;EACF;EACA,IAAI,YAAY,GAAG,KAAKC,OAAO,YAAY;OACtC,KAAKA,OAAO,KAAK,IAAI;EAE1B,KAAKI,MAAM;CACb;;CAGA,eAAqB;EACnB,KAAKJ,OAAO,SAAS;EACrB,KAAKK,cAAc;CACrB;;CAGA,QAAuB;EACrB,IAAI,KAAKC,YAAY,KAAK,KAAKN,OAAO,WAAW,GAAG,OAAO,QAAQ,QAAQ;EAC3E,OAAO,IAAI,SAAe,YAAY,KAAKE,cAAc,IAAI,OAAO,CAAC;CACvE;CAEA,SAAS,SAAoC;EAC3C,MAAM,QAAQ,KAAKN,SAAS,gBAAgB,OAAO;EACnD,IAAI,UAAU,KAAA,GAAW,OAAO,CAAC;EAEjC,MAAM,SAAS,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,KAAK;EACpD,KAAK,MAAM,OAAO,QAChB,IAAI,OAAO,QAAQ,YAAY,OAAO,QAAQ,YAAY,OAAO,QAAQ,UACvE,MAAM,IAAI,UAAU,6DAA6D;EAGrF,OAAO,CAAC,GAAG,IAAI,IAAI,MAAM,CAAC;CAC5B;CAEA,QAAc;EACZ,OAAO,KAAKU,UAAU,KAAKV,SAAS,aAAa;GAC/C,MAAM,cAAc,IAAI,IAAI,KAAKK,WAAW;GAC5C,MAAM,QAAQ,KAAKD,OAAO,WAAW,EAAE,WAAW;IAChD,MAAM,WAAW,KAAK,OAAO,QAAQ,CAAC,YAAY,IAAI,GAAG,CAAC;IAC1D,IAAI,CAAC,UACH,KAAK,MAAM,OAAO,MAAM,YAAY,IAAI,GAAG;IAE7C,OAAO;GACT,CAAC;GACD,IAAI,QAAQ,GAAG;GAEf,MAAM,CAAC,QAAQ,KAAKA,OAAO,OAAO,OAAO,CAAC;GAC1C,IAAI,CAAC,MAAM;GAEX,KAAKM,WAAW;GAChB,KAAK,MAAM,OAAO,KAAK,MAAM,KAAKL,YAAY,IAAI,GAAG;GACrD,KAAUM,KAAK,IAAI;EACrB;EAEA,KAAKF,cAAc;CACrB;CAEA,MAAME,KAAK,MAAsC;EAC/C,IAAI;GACF,MAAM,sBAAsB,KAAK,YAAY,KAAK,SAAS,YAAY;IACrE,KAAK,MAAM,EAAE,WAAW,aAAa,KAAK,UACxC,IAAI;KACF,IAAI,UAAU,KAAK,OAAO,GAAG,MAAM,QAAQ,KAAK,OAAO;IACzD,SAAS,OAAO;KACd,KAAKV,OAAO,aAAa,OAAO,KAAK,OAAO;IAC9C;IAGF,KAAKA,OAAO,QAAQ,KAAK,OAAO;GAClC,CAAC;EACH,SAAS,OAAO;GACd,KAAKA,OAAO,gBAAgB,OAAO,KAAK,OAAO;EACjD,UAAU;GACR,KAAKS,WAAW;GAChB,KAAK,MAAM,OAAO,KAAK,MAAM,KAAKL,YAAY,OAAO,GAAG;GACxD,KAAKG,MAAM;EACb;CACF;CAEA,gBAAsB;EACpB,IAAI,KAAKE,YAAY,KAAK,KAAKN,OAAO,WAAW,GAAG;EACpD,KAAK,MAAM,WAAW,KAAKE,eAAe,QAAQ;EAClD,KAAKA,cAAc,MAAM;CAC3B;AACF;;;;;;;;;ACvTA,MAAa,oBAAoB,OAAO,OAAO;CAAC;CAAM;CAAM;CAAM;CAAM;CAAO;AAAK,CAAC;;;;;;;;;;AA+BrF,SAAgB,eACd,SACA,UAA4B,CAAC,GAC7B,SAAuB,KAAK,QACpB;CACR,MAAM,UAAU,QAAQ,WAAW;CACnC,MAAM,SAAS,QAAQ,UAAA;CAEvB,MAAM,OAAO,QAAQ,KAAK,IAAI,SAAS,QAAQ,SAAS,CAAC,MAAM;CAC/D,MAAM,SAAS,OAAO,UAAU,OAAO,IAAI,IAAI;CAE/C,OAAO,KAAK,IAAI,GAAG,KAAK,MAAM,OAAO,MAAM,CAAC;AAC9C;;;;AC4BA,IAAa,0BAAb,cAA6C,MAAM;CACjD,cAAc;EACZ,MAAM,oCAAoC;EAC1C,KAAK,OAAO;CACd;AACF;;;;ACzEA,MAAa,qBAAqB,OAAO,OAAO;CAC9C,cAAc;CACd,aAAa;CACb,SAAS;AACX,CAAU;;AAGV,MAAa,uBAAuB,OAAO,OAAO;CAChD,QAAQ;CACR,MAAM;AACR,CAAU;;AAsGV,SAAgB,6BAA6B,UAAyB;CACpE,IAAI,OAAO,aAAa,UAAU;EAChC,IAAI,SAAS,WAAW,GAAG,MAAM,IAAI,eAAe,uCAAuC;EAC3F;CACF;CAEA,IAAI,MAAM,QAAQ,QAAQ,GAAG;EAC3B,IAAI,SAAS,WAAW,KAAK,SAAS,MAAM,SAAS,OAAO,SAAS,YAAY,CAAC,IAAI,GACpF,MAAM,IAAI,eAAe,2DAA2D;EAEtF;CACF;CAEA,IAAI,OAAO,aAAa,YAAY,aAAa,MAC/C,MAAM,IAAI,eAAe,8DAA8D;CAGzF,MAAM,SAAS;CACf,IAAI,OAAO,SAAS,KAAA,GAAW,6BAA6B,OAAO,IAAI;CACvE,IAAI,OAAO,YAAY,KAAA,KAAa,OAAO,OAAO,YAAY,UAC5D,MAAM,IAAI,eAAe,iDAAiD;CAE5E,KAAK,MAAM,SAAS,CAAC,YAAY,gBAAgB,GAAY;EAC3D,MAAM,QAAQ,OAAO;EACrB,IAAI,UAAU,KAAA,KAAa,UAAU,QAAQ,OAAO,UAAU,UAC5D,MAAM,IAAI,eAAe,uBAAuB,MAAM,8BAA8B;CAExF;CACA,IAAI,OAAO,cAAc,KAAA,KAAa,OAAO,OAAO,cAAc,YAChE,MAAM,IAAI,eAAe,oDAAoD;AAEjF;;AAGA,SAAgB,mBAAmB,OAAmD;CACpF,IAAI,MAAM,SAAS,gBACjB,OAAO;EACL,MAAM,mBAAmB;EACzB,MAAM,sBAAsB,MAAM,IAAI;CACxC;CAGF,IAAI,MAAM,SAAS,gBAAgB;EACjC,MAAM,QAAQ,qBAAqB,MAAM,IAAI;EAC7C,OAAO,UAAU,KAAA,IAAY,KAAA,IAAY;GAAE,MAAM,mBAAmB;GAAa,MAAM;EAAM;CAC/F;CAEA,OAAO;EAAE,MAAM,mBAAmB;EAAS,MAAM,MAAM;EAAM,MAAM,MAAM;CAAK;AAChF;;AAGA,SAAgB,oBACd,SACA,UAC2C;CAC3C,MAAM,eAAe,QAAQ,OAAO,KAAK;CAEzC,IAAI,OAAO,aAAa,UAAU,OAAO,aAAa,SAAS;CAC/D,IAAI,MAAM,QAAQ,QAAQ,GAAG,OAAO,SAAS,SAAS,aAAa,IAAS;CAE5E,MAAM,SAAS;CACf,MAAM,QAAQ,OAAO,SAAS,KAAA,IAAY,KAAA,IAAY,CAAC,OAAO,IAAI,CAAC,CAAC,KAAK;CAEzE,IAAI,SAAS,CAAC,MAAM,SAAS,aAAa,IAAS,GAAG,OAAO;CAC7D,IACE,OAAO,YAAY,KAAA,KACnB,CAAC,aAAa,OAAO,MAAM,EAAE,SAAS,OAAO,OAAO,OAAO,GAE3D,OAAO;CAET,IAAI,OAAO,aAAa,KAAA,KAAa,aAAa,aAAa,OAAO,UAAU,OAAO;CACvF,IACE,OAAO,mBAAmB,KAAA,KAC1B,aAAa,mBAAmB,OAAO,gBAEvC,OAAO;CAGT,OAAO,OAAO,YAAY,OAAyC,KAAK;AAC1E;;AAGA,SAAgB,sBACd,SACwC;CACxC,OAAO,QAAQ,OAAO,SAAS,mBAAmB;AACpD;;;;AChFA,SAAS,8BACP,SACM;CACN,MAAM,mBAAmB,OAA2B,SAAuB;EACzE,IAAI,UAAU,KAAA,GAAW;EACzB,IAAI,CAAC,OAAO,UAAU,KAAK,KAAK,QAAQ,GACtC,MAAM,IAAI,eACR,YAAY,KAAK,uDAAuD,OAC1E;CAEJ;CACA,MAAM,YAAY,OAAe,SAAuB;EACtD,IAAI,CAAC,OAAO,SAAS,KAAK,KAAK,QAAQ,GACrC,MAAM,IAAI,eACR,YAAY,KAAK,6CAA6C,OAChE;CAEJ;CAEA,gBAAgB,QAAQ,aAAa,aAAa;CAClD,gBAAgB,QAAQ,aAAa,aAAa;CAClD,IAAI,QAAQ,gBAAgB,GAC1B,MAAM,IAAI,eAAe,8CAA8C;CAEzE,IAAI,QAAQ,WAAW,KAAA,KAAa,EAAE,QAAQ,UAAU,KAAK,QAAQ,UAAU,IAC7E,MAAM,IAAI,eACR,0DAA0D,QAAQ,QACpE;CAEF,IAAI,QAAQ,YAAY,KAAA,GAAW;EACjC,IAAI,CAAC,MAAM,QAAQ,QAAQ,OAAO,KAAK,QAAQ,QAAQ,WAAW,GAChE,MAAM,IAAI,eAAe,oDAAoD;EAE/E,KAAK,MAAM,SAAS,QAAQ,SAAS,SAAS,OAAO,SAAS;CAChE;CACA,IAAI,QAAQ,kBAAkB,KAAA,KAAa,OAAO,QAAQ,kBAAkB,YAC1E,MAAM,IAAI,eAAe,6CAA6C;AAE1E;;;;;;;;;;;AAYA,IAAa,iBAAb,MAIE;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;;;;;;;;;CASA,UAAU;CACV,UAA0B,eAAe;CACzC,WAAW;CACX,cAAc;CACd;CACA;CACA;CAEA,YAAY,MAAgC,UAAoC,CAAC,GAAG;EAClF,8BAA8B,OAAO;EAErC,KAAKM,QAAQ;EACb,KAAKC,WAAW;EAChB,KAAKG,SAAS,KAAK,SAAS;EAC5B,KAAKC,eAAe,QAAQ,eAAA;EAC5B,KAAKF,WAAW,IAAI,SAAY,UAAU,oBAAoB,KAAK,QAAQ,YAAY,KAAK,CAAC;EAC7F,KAAKD,cAAc,IAAI,mBACrB;GACE,aAAa,QAAQ,eAAe;GACpC,GAAI,QAAQ,gBAAgB,EAAE,eAAe,QAAQ,cAAc,IAAI,CAAC;EAC1E,GACA;GACE,UAAU,YAAY,KAAKF,MAAM,QAAQ,QAAQ,MAAM;GACvD,kBAAkB,OAAO,YACvB,KAAKM,qBAAqB,mBAAmB,OAAO,OAAO;GAC7D,eAAe,OAAO,YAAY,KAAKA,qBAAqB,gBAAgB,OAAO,OAAO;GAC1F,gBAAgB,KAAKC,gBAAgB;EACvC,CACF;CACF;;CAGA,IAAI,SAAyB;EAC3B,OAAO,KAAKC;CACd;;CAGA,IAAI,YAAoB;EACtB,OAAO,KAAKR,MAAM,UAAU;CAC9B;;CAGA,GAAsB,OAAU,UAAuC;EACrE,OAAO,KAAKG,SAAS,GAAG,OAAO,QAAQ;CACzC;;CAGA,KAAwB,OAAU,UAAuC;EACvE,OAAO,KAAKA,SAAS,KAAK,OAAO,QAAQ;CAC3C;;CAGA,KAAwB,OAAU,SAAqB;EACrD,KAAKA,SAAS,KAAK,OAAO,OAAO;CACnC;;CAGA,qBAA2B;EACzB,KAAKA,SAAS,mBAAmB;CACnC;;CAGA,IAAI,YAA2E;EAC7E,IAAI,OAAO,eAAe,YAAY,OAAO,KAAKD,YAAY,IAAI,UAAU;EAC5E,IACE,OAAO,eAAe,YACtB,eAAe,QACf,OAAO,WAAW,eAAe,YAEjC,MAAM,IAAI,eACR,sEACF;EAEF,OAAO,KAAKA,YAAY,IAAI,wBAAwB,UAAU,CAAC;CACjE;;CAGA,SAAS,WAAiC,SAA0C;EAClF,OAAO,KAAKA,YAAY,GAAG,WAAW,OAAO;CAC/C;;;;;;;CAQA,MAAM,UAAyB;EAC7B,IAAI,KAAKO,SAAS;EAClB,KAAKA,UAAU;EACf,MAAM,aAAa,EAAE,KAAKC;EAC1B,MAAM,WAAW,CAAC;EAClB,KAAKC,YAAY;EACjB,KAAKX,MAAM,YAAY;EAEvB,KAAKY,4BAA4B;EACjC,MAAM,KAAKC,MAAM,WAAW,YAAY,QAAQ;EAGhD,IAAI,KAAKF,cAAc,UAAU;EACjC,KAAKA,YAAY,KAAA;EACjB,IAAI,KAAKG,qBAAqB,UAAU,GAAG,KAAKC,KAAK,UAAU;CACjE;;CAGA,aAAmB;EACjB,KAAKN,UAAU;EACf,KAAKC,eAAe;EACpB,KAAKC,YAAY,KAAA;EAEjB,IAAI,KAAKK,cAAc;GACrB,KAAKA,aAAa;GAClB,KAAKA,eAAe,KAAA;EACtB;EAEA,KAAKC,qBAAqB;EAC1B,KAAKA,qBAAqB,KAAA;EAE1B,KAAKC,aAAa,MAAM;EACxB,KAAKA,cAAc,KAAA;EACnB,KAAKC,WAAW;EAChB,KAAKjB,YAAY,aAAa;EAE9B,KAAKkB,WAAW,eAAe,YAAY;EAC3C,KAAKpB,MAAM,UAAU;CACvB;;CAGA,QAAuB;EACrB,OAAO,KAAKE,YAAY,MAAM;CAChC;;CAGA,UAAU,QAAW,KAAiC,QAAoC;EACxF,IAAI,CAAC,KAAKO,SAAS;EACnB,KAAKP,YAAY,SACf,KAAKF,MAAM,cAAc,QAAQ,KAAK,MAAM,GAC5C,KAAKA,MAAM,cAAc,MAAM,CACjC;CACF;;;;;CAMA,kBAAwB;EACtB,MAAM,aAAa,KAAKkB;EACxB,IAAI,CAAC,KAAKT,WAAW,CAAC,YAAY;EAElC,MAAM,aAAa,KAAKC;EACxB,WAAW,MAAM;EACjB,KAAKQ,cAAc,KAAA;EACnB,KAAKE,WAAW,eAAe,KAAK;EAEpC,MAAM,wBAAQ,IAAI,MAAM,oEAAoE;EAC5F,KAAUlB,YAAY,MAAM,CAAC,CAAC,WAAW;GACvC,IAAI,KAAKY,qBAAqB,UAAU,KAAK,CAAC,KAAKI,aACjD,KAAKG,mBAAmB,KAAK;EAEjC,CAAC;CACH;;CAGA,MAAMR,MAAM,QAA4B,YAAoB,UAAiC;EAC3F,IAAI,CAAC,KAAKb,MAAM,MAAM;EAEtB,IAAI;GACF,MAAM,KAAKA,MAAM,KAAK,SAAS,WAAW;IACxC,IAAI,KAAKW,cAAc,YAAY,CAAC,KAAKG,qBAAqB,UAAU,GAAG;IAC3E,KAAKQ,UAAU,QAAQ,KAAA,GAAW,qBAAqB,IAAI;GAC7D,CAAC;EACH,SAAS,OAAO;GACd,KAAKtB,MAAM,QAAQ,MAAM,sCAAsC,OAAO,IAAI,KAAK;EACjF;CACF;;CAGA,KAAK,aAAa,KAAKU,aAAmB;EACxC,IAAI,CAAC,KAAKI,qBAAqB,UAAU,GAAG;EAI5C,KAAKI,aAAa,MAAM;EAExB,MAAM,aAAa,IAAI,gBAAgB;EACvC,KAAKA,cAAc;EACnB,KAAKE,WAAW,eAAe,UAAU;EAEzC,KAAUpB,MAAM,UACb,QAAQ;GACP,SAAS,KAAKA,MAAM;GACpB,WAAW,KAAKA,MAAM;GACtB,OAAO,KAAKA,MAAM;GAClB,SAAS,KAAKA,MAAM;GACpB,aAAa,KAAKA,MAAM;GACxB,UAAU,KAAKA,MAAM;GACrB,QAAQ,WAAW;GACnB,cAAc;IACZ,IAAI,CAAC,KAAKuB,qBAAqB,YAAY,UAAU,GAAG;IACxD,KAAKJ,WAAW;IAChB,KAAKC,WAAW,eAAe,SAAS;GAC1C;GACA,UAAU,UAAU;IAClB,IAAI,KAAKG,qBAAqB,YAAY,UAAU,GAAG,KAAKC,aAAa,KAAK;GAChF;GACA,eAAe,OAAO,QAAQ;IAC5B,IAAI,KAAKD,qBAAqB,YAAY,UAAU,GAClD,KAAKE,YAAY,cAAc;KAAE;KAAO;IAAI,CAAC;GAEjD;EACF,CAAC,CAAC,CACD,WACO;GAEJ,IAAI,KAAKF,qBAAqB,YAAY,UAAU,GAClD,KAAKG,+BAAe,IAAI,MAAM,sCAAsC,CAAC;EAEzE,IACC,UAAmB;GAClB,IAAI,KAAKH,qBAAqB,YAAY,UAAU,GAAG,KAAKG,eAAe,KAAK;EAClF,CACF;CACJ;CAEA,aAAa,OAA6B;EACxC,IAAI,CAAC,KAAKjB,SAAS;EACnB,KAAKgB,YAAY,WAAW,KAAK;EAEjC,IAAI,KAAKzB,MAAM,cAAc,KAAK,GAAG;EAErC,MAAM,SAAS,KAAKA,MAAM,WAAW,KAAK;EAC1C,IAAI,WAAW,KAAA,GAAW,KAAKsB,UAAU,QAAQ,OAAO,qBAAqB,MAAM;CACrF;CAEA,qBACE,OACA,OACA,SACM;EACN,IAAI,KAAKnB,SAAS,cAAc,KAAK,IAAI,GAAG;GAC1C,KAAKsB,YAAY,OAAO;IAAE;IAAO;GAAQ,CAAC;GAC1C;EACF;EAEA,MAAM,UACJ,UAAU,oBACN,8CACA;EACN,IAAI,KAAKzB,MAAM,QAAQ,KAAKA,MAAM,OAAO,MAAM,SAAS,KAAK;OACxD,QAAQ,MAAM,aAAa,WAAW,KAAK;CAClD;CAEA,eAAe,OAAsB;EACnC,KAAKkB,cAAc,KAAA;EAEnB,IAAI,iBAAiB,yBAAyB;GAC5C,KAAUS,qBAAqB,KAAK;GACpC;EACF;EAEA,KAAKP,WAAW,eAAe,KAAK;EACpC,KAAKC,mBAAmB,KAAK;CAC/B;;CAGA,MAAMM,qBAAqB,OAA+B;EACxD,MAAM,aAAa,KAAKjB;EACxB,MAAM,WAAW,CAAC;EAClB,KAAKC,YAAY;EACjB,KAAKS,WAAW,eAAe,KAAK;EAEpC,MAAM,YAAY,MAAM,KAAKpB,MAAM,QAAQ,CAAC,CAAC,YAAY,KAAK;EAE9D,IAAI,KAAKW,cAAc,YAAY,CAAC,KAAKG,qBAAqB,UAAU,GAAG;EAC3E,KAAKH,YAAY,KAAA;EAEjB,IAAI,CAAC,WAAW;GACd,KAAKiB,QAAQ,KAAK;GAClB;EACF;EAEA,KAAKP,mBAAmB,KAAK;CAC/B;CAEA,mBAAmB,OAAsB;EACvC,IAAI,CAAC,KAAKZ,SAAS;EAEnB,IAAI,KAAKU,YAAY,KAAKd,cAAc;GACtC,KAAKuB,QAAQ,KAAK;GAClB;EACF;EAEA,MAAM,QAAQ,eAAe,KAAKT,UAAU,KAAKlB,QAAQ;EACzD,KAAKkB,YAAY;EAEjB,KAAKM,YAAY,SAAS;GAAE;GAAO,eAAe;EAAK,CAAC;EACxD,KAAKA,YAAY,aAAa;GAAE,SAAS,KAAKN;GAAU;EAAM,CAAC;EAE/D,KAAKH,eAAe,KAAKZ,OAAO,eAAe;GAC7C,KAAKY,eAAe,KAAA;GACpB,KAAUa,WAAW;EACvB,GAAG,KAAK;CACV;;CAGA,MAAMA,aAA4B;EAChC,IAAI,CAAC,KAAKpB,WAAW,KAAKE,WAAW;EAErC,MAAM,aAAa,KAAKD;EACxB,MAAM,WAAW,CAAC;EAClB,KAAKC,YAAY;EACjB,MAAM,KAAKE,MAAM,aAAa,YAAY,QAAQ;EAElD,IAAI,KAAKF,cAAc,UAAU;EACjC,KAAKA,YAAY,KAAA;EACjB,IAAI,KAAKG,qBAAqB,UAAU,GAAG,KAAKC,KAAK,UAAU;CACjE;;;;;;;CAQA,QAAQ,OAAsB;EAC5B,KAAKN,UAAU;EACf,KAAKC,eAAe;EACpB,KAAKC,YAAY,KAAA;EACjB,KAAKQ,WAAW;EAEhB,KAAKF,qBAAqB;EAC1B,KAAKA,qBAAqB,KAAA;EAE1B,KAAKjB,MAAM,UAAU;EAErB,KAAKyB,YAAY,SAAS;GAAE;GAAO,eAAe;EAAM,CAAC;EACzD,KAAKA,YAAY,UAAU,KAAA,CAAS;CACtC;;;;;;;CAQA,8BAAoC;EAClC,IAAI,KAAKR,oBAAoB;EAE7B,MAAM,SAAS;EAMf,IAAI,OAAO,OAAO,qBAAqB,YAAY;EAEnD,MAAM,aAAa;GAEjB,IAAI,KAAKC,eAAe,KAAKF,gBAAgB,KAAKL,WAAW;GAC7D,IAAI,KAAKH,YAAY,eAAe,cAAc;GAElD,KAAKW,WAAW;GAChB,KAAUU,WAAW;EACvB;EAEA,MAAM,qBAAqB;GACzB,IAAI,OAAO,UAAU,oBAAoB,WAAW,KAAK;EAC3D;EAEA,MAAM,YAAoC,CAAC;EAE3C,IAAI,KAAK5B,SAAS,uBAAuB,SAAS,OAAO,UACvD,UAAU,KAAK,CAAC,oBAAoB,YAAY,CAAC;EAEnD,IAAI,KAAKA,SAAS,sBAAsB,OACtC,UAAU,KAAK,CAAC,UAAU,IAAI,CAAC;EAGjC,KAAK,MAAM,CAAC,MAAM,aAAa,WAAW,OAAO,iBAAiB,MAAM,QAAQ;EAEhF,KAAKgB,2BAA2B;GAC9B,KAAK,MAAM,CAAC,MAAM,aAAa,WAAW,OAAO,sBAAsB,MAAM,QAAQ;EACvF;CACF;CAEA,WAAW,QAA8B;EACvC,IAAI,KAAKT,YAAY,QAAQ;EAC7B,KAAKA,UAAU;EACf,KAAKiB,YAAY,UAAU,MAAM;CACnC;CAEA,qBAAqB,YAA6B;EAChD,OAAO,KAAKhB,WAAW,KAAKC,gBAAgB;CAC9C;CAEA,qBAAqB,YAA6B,YAA6B;EAC7E,OACE,KAAKI,qBAAqB,UAAU,KACpC,KAAKI,gBAAgB,cACrB,CAAC,WAAW,OAAO;CAEvB;;CAGA,YACE,OACA,SACM;EAGN,KAAMf,SAAyD,KAAK,OAAO,OAAO;CACpF;AACF;;;;;;;;;;;;;ACxkBA,IAAa,gBAAb,MAAwD;CACtD,OAAgB;CAEhB;CACA;CACA;CAEA,YAAY,UAAgC,CAAC,GAAG;EAC9C,KAAK6B,SAAS,QAAQ,SAAS;EAC/B,KAAKF,YAAY,QAAQ,YAAY;EACrC,KAAKC,SAAS,QAAQ,SAAS;CACjC;CAEA,MAAM,QAAQ,SAA0C;EACtD,MAAM,UAAU,QAAQ;EACxB,IAAI,CAAC,SACH,MAAM,IAAI,eACR,6FACF;EAGF,MAAM,uBAAO,IAAI,IAAY;EAC7B,IAAI,WAAW;EACf,IAAI;EAEJ,OAAO,CAAC,QAAQ,OAAO,SAAS;GAC9B,MAAM,UAAU,MAAM,KAAKE,aAAa,SAAS,QAAQ,MAAM;GAK/D,QAAQ,OAAO;GAEf,MAAM,QAAQ,UAAmC,SAAS,eAAe;GAEzE,KAAK,MAAM,QAAQ,CAAC,GAAG,KAAK,CAAC,CAAC,QAAQ,GAAG;IACvC,MAAM,KAAK,OAAO,KAAK,OAAO,WAAW,KAAK,KAAK,KAAA;IACnD,IAAI,CAAC,MAAM,KAAK,IAAI,EAAE,GAAG;IAEzB,KAAK,IAAI,EAAE;IAGX,IAAI,CAAC,UAAU,QAAQ,QAAQ;KAAE,MAAM;KAAgB,MAAM,EAAE,SAAS,KAAK;IAAE,CAAC;GAClF;GAGA,IAAI,KAAK,OAAO,KAAKF,SAAS,GAAG;IAC/B,MAAM,SAAS,CAAC,GAAG,IAAI,CAAC,CAAC,MAAM,GAAG,KAAK,OAAO,KAAKA,SAAS,CAAC;IAC7D,KAAK,MAAM,MAAM,QAAQ,KAAK,OAAO,EAAE;GACzC;GAEA,MAAM,QAAQ,MAAM,KAAKG,WAAW,SAAS,QAAQ,MAAM;GAC3D,IAAI,UAAU,KAAA,KAAa,UAAU,iBAAiB;IACpD,kBAAkB;IAClB,QAAQ,QAAQ;KAAE,MAAM;KAAgB,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE;IAAE,CAAC;GACxE;GAEA,WAAW;GACX,MAAM,KAAKC,MAAM,QAAQ,MAAM;EACjC;CACF;CAEA,MAAMF,aAAa,SAA0B,QAAuC;EAClF,IAAI;GACF,OAAO,MAAM,QAAQ;IACnB,aAAa;IACb,MAAM;IACN,OAAO;KAAE,OAAO,KAAKF;KAAQ,QAAQ;IAAE;IACvC;GACF,CAAC;EACH,SAAS,OAAO;GAEd,IAAI,eAAe,KAAK,GAAG,MAAM,IAAI,wBAAwB;GAC7D,MAAM;EACR;CACF;CAEA,MAAMG,WAAW,SAA0B,QAAkD;EAC3F,IAAI;GACF,MAAM,UAAU,MAAM,QAAQ;IAC5B,aAAa;IACb,MAAM;IACN;GACF,CAAC;GACD,OAAO,WAAW,SAAS,SAAS,CAAC;EACvC,QAAQ;GAEN;EACF;CACF;;CAGA,MAAM,QAAoC;EAGxC,IAAI,OAAO,SAAS,OAAO,QAAQ,QAAQ;EAE3C,OAAO,IAAI,SAAe,YAAY;GACpC,MAAM,SAAS,KAAKF,OAAO,SAAS,QAAQ,KAAKF,SAAS;GAE1D,SAAS,SAAS;IAChB,OAAO;IACP,OAAO,oBAAoB,SAAS,MAAM;IAC1C,QAAQ;GACV;GAEA,OAAO,iBAAiB,SAAS,QAAQ,EAAE,MAAM,KAAK,CAAC;EACzD,CAAC;CACH;AACF;;;ACvIA,IAAM,aAAN,cAAyB,MAAM;CAC7B,YAAY,SAAS,SAAS;EAC5B,MAAM,OAAO,GAAG,KAAK,OAAO,cAAc,KAAK,OAAO,QAAQ,MAAM,KAAK,QAAQ,QAAQ,OAAO,KAAK,QAAQ,QAAQ,OAAO,KAAK,OAAO,QAAQ;CAClJ;AACF;AACA,MAAM,KAAK;AAAI,MAAA,KAAK;AAAI,MAAA,QAAQ;AAChC,SAAS,KAAK,MAAM,CACpB;AACA,SAAS,aAAa,QAAQ;CAC5B,IAAI,OAAO,UAAU,YACnB,MAAM,IAAI,UACR,iGACF;CACF,MAAM,EAAE,UAAU,MAAM,UAAU,MAAM,UAAU,MAAM,WAAW,kBAAkB,QAAQ,mBAAmB,CAAC;CACjH,IAAI,yBAAyB,GAAG,eAAe,CAAC,GAAG,IAAI,OAAO,IAAI,YAAY,GAAG,WAAW,aAAa,CAAC;CAC1G,SAAS,KAAK,OAAO;EACnB,IAAI,YACF,MAAM,IAAI,MACR,yHACF;EACF,IAAI,iBAAiB,eAAe,CAAC,GAAG,MAAM,WAAW,CAAC,MAAM,OAAO,MAAM,WAAW,CAAC,MAAM,OAAO,MAAM,WAAW,CAAC,MAAM,QAAQ,QAAQ,MAAM,MAAM,CAAC,KAAK,iBAAiB,WAAW,GAAG;GAC7L,MAAM,YAAY,aAAa,KAAK;GACpC,cAAc,OAAO,iBAAiB,KAAK,SAAS,GAAG,yBAAyB,UAAU,SAAS,gBAAgB;GACnH;EACF;EACA,IAAI,MAAM,QAAQ;CACrB,MAAM,MAAM,MAAM,QAAQ,IAAI,MAAM,IAAI;GACnC,iBAAiB,KAAK,KAAK,GAAG,0BAA0B,MAAM,QAAQ,gBAAgB;GACtF;EACF;EACA,iBAAiB,KAAK,KAAK;EAC3B,MAAM,QAAQ,iBAAiB,KAAK,EAAE;EACtC,iBAAiB,SAAS,GAAG,yBAAyB;EACtD,MAAM,WAAW,aAAa,KAAK;EACnC,aAAa,OAAO,iBAAiB,KAAK,QAAQ,GAAG,yBAAyB,SAAS,SAAS,gBAAgB;CAClH;CACA,SAAS,kBAAkB;EACzB,kBAAkB,KAAK,MAAM,yBAAyB,KAAK,UAAU,kBAAkB,aAAa,CAAC,GAAG,iBAAiB,SAAS,GAAG,yBAAyB,GAAG,KAAK,KAAK,GAAG,OAAO,IAAI,YAAY,GAAG,YAAY,KAAK,GAAG,QAC1N,IAAI,WAAW,6CAA6C,cAAc,cAAc,EACtF,MAAM,2BACR,CAAC,CACH;CACF;CACA,SAAS,aAAa,OAAO;EAC3B,IAAI,cAAc;EAClB,IAAI,MAAM,QAAQ,IAAI,MAAM,IAAI;GAC9B,IAAI,UAAU,MAAM,QAAQ;GAC/B,WAAW;GACR,OAAO,YAAY,KAAM;IACvB,IAAI,gBAAgB,SAAS;KAC3B,YAAY,KAAK,QAAQ;MAAE;MAAI,OAAO;MAAW;KAAK,CAAC,GAAG,KAAK,KAAK,GAAG,OAAO,IAAI,YAAY,GAAG,YAAY,KAAK,GAAG,cAAc,UAAU,GAAG,UAAU,MAAM,QAAQ;GAC/K,WAAW;KACJ;IACF;IACA,MAAM,gBAAgB,MAAM,WAAW,WAAW;IAClD,IAAI,aAAa,OAAO,aAAa,aAAa,GAAG;KACnD,MAAM,aAAa,MAAM,WAAW,cAAc,CAAC,MAAM,QAAQ,cAAc,IAAI,cAAc,GAAG,QAAQ,MAAM,MAAM,YAAY,OAAO;KAC3I,IAAI,cAAc,KAAK,MAAM,WAAW,UAAU,CAAC,MAAM,IAAI;MAC3D,QAAQ;OAAE;OAAI,OAAO;OAAW,MAAM;MAAM,CAAC,GAAG,KAAK,KAAK,GAAG,OAAO,IAAI,YAAY,KAAK,GAAG,cAAc,UAAU,GAAG,UAAU,MAAM,QAAQ;GACxJ,WAAW;MACF;KACF;KACA,OAAO,cAAc,IAAI,QAAQ,GAAG,KAAK;EACjD,SAAS;IACH,OAAO,cAAc,OAAO,aAAa,aAAa,IAAI,YAAY,MAAM,MAC1E,MAAM,WAAW,cAAc,CAAC,MAAM,QAAQ,cAAc,IAAI,cAAc,GAC9E,OACF,KAAK,KAAK,IAAI,UAAU,OAAO,aAAa,OAAO;IACnD,cAAc,UAAU,GAAG,UAAU,MAAM,QAAQ;GACxD,WAAW;GACR;GACA,OAAO,MAAM,MAAM,WAAW;EAChC;EACA,OAAO,cAAc,MAAM,SAAU;GACnC,MAAM,UAAU,MAAM,QAAQ,MAAM,WAAW,GAAG,UAAU,MAAM,QAAQ;GAC7E,WAAW;GACR,IAAI,UAAU;GACd,IAAI,YAAY,MAAM,YAAY,KAAK,UAAU,UAAU,UAAU,UAAU,UAAU,YAAY,KAAK,YAAY,MAAM,SAAS,IAAI,UAAU,KAAK,UAAU,UAAU,YAAY,OAAO,UAAU,UAAU,YAAY,IAC7N;GACF,UAAU,OAAO,aAAa,OAAO,GAAG,cAAc,UAAU,GAAG,MAAM,WAAW,cAAc,CAAC,MAAM,MAAM,MAAM,WAAW,WAAW,MAAM,MAAM;EACzJ;EACA,OAAO,MAAM,MAAM,WAAW;CAChC;CACA,SAAS,UAAU,OAAO,OAAO,KAAK;EACpC,IAAI,UAAU,KAAK;GACjB,cAAc;GACd;EACF;EACA,MAAM,gBAAgB,MAAM,WAAW,KAAK;EAC5C,IAAI,aAAa,OAAO,OAAO,aAAa,GAAG;GAC7C,MAAM,aAAa,MAAM,WAAW,QAAQ,CAAC,MAAM,QAAQ,QAAQ,IAAI,QAAQ,GAAG,SAAS,MAAM,MAAM,YAAY,GAAG;GACtH,OAAO,cAAc,IAAI,SAAS,GAAG,KAAK;EAC9C,UAAU;GACN;EACF;EACA,IAAI,cAAc,OAAO,OAAO,aAAa,GAAG;GAC9C,YAAY,MAAM,MAAM,MAAM,WAAW,QAAQ,CAAC,MAAM,QAAQ,QAAQ,IAAI,QAAQ,GAAG,GAAG,KAAK,KAAK;GACpG;EACF;EACA,IAAI,kBAAkB,OAAO,MAAM,WAAW,QAAQ,CAAC,MAAM,OAAO,MAAM,WAAW,QAAQ,CAAC,MAAM,IAAI;GACtG,MAAM,SAAS,MAAM,MAAM,MAAM,WAAW,QAAQ,CAAC,MAAM,QAAQ,QAAQ,IAAI,QAAQ,GAAG,GAAG;GAC7F,KAAK,OAAO,SAAS,IAAI,IAAI,KAAK,IAAI;GACtC;EACF;EACA,IAAI,kBAAkB,IAAI;GACxB,IAAI,WAAW;IACb,MAAM,QAAQ,MAAM,MAAM,OAAO,GAAG;IACpC,UAAU,MAAM,MAAM,MAAM,WAAW,QAAQ,CAAC,MAAM,QAAQ,IAAI,CAAC,CAAC;GACtE;GACA;EACF;EACA,MAAM,OAAO,MAAM,MAAM,OAAO,GAAG,GAAG,sBAAsB,KAAK,QAAQ,GAAG;EAC5E,IAAI,wBAAwB,IAAI;GAC9B,aAAa,MAAM,IAAI,IAAI;GAC3B;EACF;EACA,MAAM,QAAQ,KAAK,MAAM,GAAG,mBAAmB,GAAG,SAAS,KAAK,WAAW,sBAAsB,CAAC,MAAM,QAAQ,IAAI;EACpH,aAAa,OADkH,KAAK,MAAM,sBAAsB,MACxI,GAAG,IAAI;CACjC;CACA,SAAS,aAAa,OAAO,OAAO,MAAM;EACxC,QAAQ,OAAR;GACE,KAAK;IACH,YAAY,SAAS,KAAK;IAC1B;GACF,KAAK;IACH,OAAO,cAAc,IAAI,QAAQ,GAAG,KAAK;EAC/C,SAAS;IACH;GACF,KAAK;IACH,KAAK,MAAM,SAAS,IAAI,IAAI,KAAK,IAAI;IACrC;GACF,KAAK;IACH,QAAQ,KAAK,KAAK,IAAI,QAAQ,SAAS,OAAO,EAAE,CAAC,IAAI,QACnD,IAAI,WAAW,6BAA6B,MAAM,IAAI;KACpD,MAAM;KACN;KACA;IACF,CAAC,CACH;IACA;GACF,SACE,QACE,IAAI,WACF,kBAAkB,MAAM,SAAS,KAAK,GAAG,MAAM,MAAM,GAAG,EAAE,EAAE,UAAU,MAAM,IAC5E;IAAE,MAAM;IAAiB;IAAO;IAAO;GAAK,CAC9C,CACF;EAEJ;CACF;CACA,SAAS,gBAAgB;EACvB,YAAY,KAAK,QAAQ;GACvB;GACA,OAAO;GACP;EACF,CAAC,GAAG,KAAK,KAAK,GAAG,OAAO,IAAI,YAAY,GAAG,YAAY,KAAK;CAC9D;CACA,SAAS,MAAM,UAAU,CAAC,GAAG;EAC3B,IAAI,QAAQ,WAAW,iBAAiB,SAAS,GAAG;GAClD,MAAM,iBAAiB,iBAAiB,KAAK,EAAE;GAC/C,UAAU,gBAAgB,GAAG,eAAe,MAAM;EACpD;EACA,eAAe,CAAC,GAAG,KAAK,KAAK,GAAG,OAAO,IAAI,YAAY,GAAG,YAAY,KAAK,GAAG,iBAAiB,SAAS,GAAG,yBAAyB,GAAG,aAAa,CAAC;CACvJ;CACA,OAAO;EAAE;EAAM;CAAM;AACvB;AACA,SAAS,aAAa,OAAO,GAAG,eAAe;CAC7C,OAAO,kBAAkB,OAAO,MAAM,WAAW,IAAI,CAAC,MAAM,MAAM,MAAM,WAAW,IAAI,CAAC,MAAM,OAAO,MAAM,WAAW,IAAI,CAAC,MAAM,MAAM,MAAM,WAAW,IAAI,CAAC,MAAM;AACrK;AACA,SAAS,cAAc,OAAO,GAAG,eAAe;CAC9C,OAAO,kBAAkB,OAAO,MAAM,WAAW,IAAI,CAAC,MAAM,OAAO,MAAM,WAAW,IAAI,CAAC,MAAM,OAAO,MAAM,WAAW,IAAI,CAAC,MAAM,OAAO,MAAM,WAAW,IAAI,CAAC,MAAM,OAAO,MAAM,WAAW,IAAI,CAAC,MAAM;AAC1M;;;;ACjKA,MAAa,cAAc;;;;;;;;;;;;AAmC3B,IAAa,eAAb,MAAuD;CACrD,OAAgB;CAEhB;CACA;CACA;;CAEA;CAEA,YAAY,UAA+B,CAAC,GAAG;EAC7C,KAAKQ,SAAS,QAAQ,SAAS;EAC/B,KAAKF,eAAe,QAAQ,eAAe;EAC3C,KAAKC,oBAAoB,QAAQ,oBAAoB;CACvD;CAEA,MAAM,QAAQ,SAA0C;EACtD,MAAM,QAAQ,QAAQ,YAAY,MAAM,QAAQ,SAAS,IAAI;EAC7D,IAAI,QAAQ,aAAa,CAAC,OAAO,MAAM,IAAI,wBAAwB;EAEnE,MAAM,MAAM,QAAQ,QAAQ,SAAS,WAAW;EAEhD,MAAM,UAAU,MAAM,QAAQ,YAAY,GAAG;EAC7C,QAAQ,IAAI,UAAU,mBAAmB;EACzC,IAAI,OAAO,QAAQ,IAAI,iBAAiB,UAAU,OAAO;EACzD,QAAQ,IAAI,iBAAiB,UAAU;EAGvC,IAAI,KAAKE,cAAc,QAAQ,IAAI,iBAAiB,KAAKA,YAAY;EAIrE,MAAM,aAAa,IAAI,gBAAgB;EACvC,MAAM,mBAAmB,WAAW,MAAM,QAAQ,OAAO,MAAM;EAC/D,IAAI,QAAQ,OAAO,SAAS,WAAW,MAAM,QAAQ,OAAO,MAAM;OAC7D,QAAQ,OAAO,iBAAiB,SAAS,YAAY,EAAE,MAAM,KAAK,CAAC;EAExE,IAAI;GACF,MAAM,WAAW,MAAM,KAAKC,WAAW,KAAK,SAAS,SAAS,UAAU;GAExE,IAAI,QAAQ,aAAa,SAAS,WAAW,KAAK,MAAM,IAAI,wBAAwB;GACpF,IAAI,CAAC,SAAS,IAAI,MAAM,IAAI,MAAM,mCAAmC,SAAS,QAAQ;GACtF,IAAI,CAAC,SAAS,MAAM,MAAM,IAAI,MAAM,+BAA+B;GAEnE,QAAQ,OAAO;GAEf,MAAM,KAAKC,MAAM,SAAS,MAAM,OAAO;EACzC,UAAU;GACR,QAAQ,OAAO,oBAAoB,SAAS,UAAU;EACxD;CACF;;CAGA,MAAMD,WACJ,KACA,SACA,SACA,YACmB;EACnB,IAAI,UAAU;EACd,MAAM,cACJ,KAAKH,oBAAoB,IACrB,KAAKC,OAAO,eAAe;GACzB,UAAU;GAEV,WAAW,sBAAM,IAAI,MAAM,+BAA+B,CAAC;EAC7D,GAAG,KAAKD,iBAAiB,IACzB,KAAA;EAEN,IAAI;GACF,OAAO,MAAM,QAAQ,MAAM,KAAK;IAAE,QAAQ;IAAO;IAAS,QAAQ,WAAW;GAAO,CAAC;EACvF,SAAS,OAAO;GACd,IAAI,SAAS,MAAM,IAAI,MAAM,yDAAyD;GACtF,MAAM;EACR,UAAU;GACR,cAAc;EAChB;CACF;CAEA,MAAMI,MAAM,MAAkC,SAA0C;EACtF,MAAM,SAAS,KAAK,UAAU;EAC9B,MAAM,UAAU,IAAI,YAAY;EAEhC,MAAM,SAAS,aAAa,EAC1B,UAAU,YAAY;GACpB,IAAI,QAAQ,IAAI,KAAKF,eAAe,QAAQ;GAE5C,IAAI;GACJ,IAAI;IACF,OAAO,KAAK,MAAM,QAAQ,IAAI;GAChC,SAAS,OAAO;IAEd,QAAQ,aAAa,OAAO,QAAQ,IAAI;IACxC;GACF;GAGA,MAAM,OACJ,QAAQ,UACP,OAAO,SAAS,YAAY,SAAS,QAAQ,UAAU,OACpD,OAAQ,KAA2B,IAAI,IACvC;GAEN,QAAQ,QAAQ;IAAE;IAAM;GAAK,CAAC;EAChC,EACF,CAAC;EAED,IAAI;EAEJ,MAAM,qBAAqB;GACzB,IAAI,KAAKH,gBAAgB,GAAG;GAC5B,kBAAkB;GAElB,kBAAkB,KAAKE,OAAO,eAAe;IAE3C,OAAY,uBAAO,IAAI,MAAM,iCAAiC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC;GACjF,GAAG,KAAKF,YAAY;EACtB;EAEA,aAAa;EAEb,IAAI;GACF,SAAS;IACP,MAAM,EAAE,MAAM,UAAU,MAAM,OAAO,KAAK;IAC1C,IAAI,MAAM;IAEV,aAAa;IACb,OAAO,KAAK,QAAQ,OAAO,OAAO,EAAE,QAAQ,KAAK,CAAC,CAAC;GACrD;EACF,UAAU;GACR,kBAAkB;GAClB,OAAO,cAAc;EACvB;CACF;AACF;;;;ACpHA,MAAa,wBAAwB,OAAO,OAAO;;CAEjD,MAAM;;CAEN,KAAK;;CAEL,MAAM;AACR,CAAU;;AAqDV,SAAS,wBAAmD,SAAmC;CAC7F,MAAM,YAAY,OAA2B,MAAc,QAAsB;EAC/E,IAAI,UAAU,KAAA,GAAW;EACzB,IAAI,CAAC,OAAO,SAAS,KAAK,KAAK,QAAQ,KACrC,MAAM,IAAI,eACR,YAAY,KAAK,gCAAgC,IAAI,cAAc,OACrE;CAEJ;CAEA,SAAS,QAAQ,cAAc,gBAAgB,CAAC;CAChD,SAAS,QAAQ,aAAa,eAAe,CAAC;CAC9C,SAAS,QAAQ,kBAAkB,oBAAoB,CAAC;AAC1D;AA6BA,MAAM,0CAA0B,IAAI,QAA4B;;AAGhE,SAAgB,wBACd,QACA,OACM;CACN,wBAAwB,IAAI,QAAQ,KAAK;AAC3C;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4BA,IAAa,cAAb,MAAsE;CACpE;CACA;CAEA,YAAY,MAAoB,UAA8B,CAAC,GAAG;EAChE,wBAAwB,OAAO;EAE/B,KAAKM,QAAQ;EACb,KAAKC,UAAU,IAAI,eACjB;GACE,SAAS,KAAK;GACd,WAAW,KAAK,aAAa;GAC7B,OAAO,KAAK;GACZ,SAAS,KAAK;GACd,OAAO,KAAK;GACZ,aAAa,KAAK;GAClB,UAAU,KAAK;GACf,SAAS,KAAK;GACd,WAAW,KAAK;GAChB,SAAS,KAAK;GACd,QAAQ,KAAK;GAEb,WAAW,gBAAgB,MAAM,OAAO;GACxC,cAAc,UAAU,KAAKC,aAAa,KAAK;GAC/C,YAAY;GACZ,cAAc,WACZ,OAAO,SAAS,mBAAmB,cAAc,OAAO,OAAO,KAAA;GACjE,gBAAgB,QAAQ,KAAK,YAG1B;IAAE;IAAQ,QAAQ;IAAM;IAAK;GAAO;GACvC,UAAU,WAAW,KAAKC,SAAS,MAAM;GACzC,GAAI,QAAQ,cAAc,QACtB,CAAC,IACD,EACE,OAAO,QAA4B,aACjC,KAAKC,iBAAiB,QAAQ,QAAQ,EAC1C;EACN,GACA,OACF;CACF;;CAGA,IAAI,SAAyB;EAC3B,OAAO,KAAKH,QAAQ;CACtB;;CAGA,IAAI,YAAoB;EACtB,OAAO,KAAKA,QAAQ;CACtB;;CAGA,IAAI,UAAkB;EACpB,OAAO,KAAKD,MAAM;CACpB;;CAGA,kBAA4C;EAC1C,OAAO,KAAKA,MAAM,kBAAkB;CACtC;;CAGA,eAAmC;EACjC,OAAO,KAAKA,MAAM,eAAe;CACnC;;CAGA,GACE,OACA,UACa;EACb,OAAO,KAAKC,QAAQ,GAAG,OAAO,QAAQ;CACxC;;CAGA,KACE,OACA,UACa;EACb,OAAO,KAAKA,QAAQ,KAAK,OAAO,QAAQ;CAC1C;;;;;;;;;CAUA,IAAI,YAA2E;EAC7E,OAAO,KAAKA,QAAQ,IAAI,UAAU;CACpC;CAaA,SACE,mBACA,iBACa;EACb,MAAM,YAAY,oBAAoB,KAAA;EACtC,MAAM,UAAU,YAAa,oBAA2C;EACxE,IAAI,OAAO,YAAY,YACrB,MAAM,IAAI,eAAe,mDAAmD;EAE9E,IACE,CAAC,aACD,OAAO,sBAAsB,cAC7B,CAAC,OAAO,OAAO,kBAAkB,CAAC,CAAC,SAAS,iBAAiB,GAE7D,MAAM,IAAI,eAAe,sCAAsC,OAAO,iBAAiB,GAAG;EAG5F,IAAI;EACJ,IAAI,WACF,kBAAkB;OACb;GACL,MAAM,WAAW;GACjB,YACE,OAAO,aAAa,aAAa,YAAY,YAAY,QAAQ,OAAO,SAAS;EACrF;EAEA,OAAO,KAAKA,QAAQ,SAAS,WAAW,OAAO;CACjD;CAiBA,eACE,UAGA,SACa;EACb,IAAI,OAAO,YAAY,YACrB,MAAM,IAAI,eAAe,yDAAyD;EAEpF,IAAI,OAAO,aAAa,YAAY,6BAA6B,QAAQ;EAEzE,MAAM,aAAmC,YAAY;GACnD,IAAI,CAAC,sBAAsB,OAAO,GAAG,OAAO;GAC5C,OAAO,OAAO,aAAa,aACvB,SAAS,OAAO,IAChB,oBAAoB,SAAS,QAAQ;EAC3C;EAEA,OAAO,KAAKA,QAAQ,SAAS,WAAW,OAA6B;CACvE;;;;;;;;;;;CAYA,MAAM,UAAyB;EAC7B,wBAAwB,IAAI,IAAI,CAAC,GAAG;EACpC,OAAO,KAAKA,QAAQ,QAAQ;CAC9B;;CAGA,aAAmB;EACjB,KAAKA,QAAQ,WAAW;CAC1B;;CAGA,QAAuB;EACrB,OAAO,KAAKA,QAAQ,MAAM;CAC5B;;CAGA,qBAA2B;EACzB,KAAKA,QAAQ,mBAAmB;CAClC;;CAGA,aAAa,OAAgC;EAC3C,IAAI,MAAM,SAAS,aAAa,OAAO;EAGvC,KAAKA,QAAQ,KAAK,SAAS,EAAE,QAAQ,WAAW,MAAM,MAAM,QAAQ,EAAE,CAAC;EACvE,OAAO;CACT;;;;;;;CAQA,MAAMG,iBACJ,QACA,UACe;EACf,IAAI,WAAW,WAAW;EAE1B,MAAM,QAAQ,MAAM,KAAKJ,MAAM,iBAAiB;EAChD,SAAS;GAAE,MAAM,mBAAmB;GAAa,MAAM;EAAM,CAAC;CAChE;CAEA,SAAS,QAA8B;EACrC,IAAI,OAAO,SAAS,mBAAmB,cAAc;GACnD,KAAKC,QAAQ,KAAK,gBAAgB,OAAO,IAAI;GAC7C,IAAI,OAAO,KAAK,gBAAgB,KAAA,GAC9B,KAAKA,QAAQ,KAAK,eAAe,OAAO,KAAK,WAAW;GAE1D;EACF;EAEA,IAAI,OAAO,SAAS,mBAAmB,aAAa;GAClD,KAAKA,QAAQ,KAAK,eAAe,OAAO,IAAI;GAC5C;EACF;EAEA,IAAI,OAAO,SAAS,mBAAmB,SAAS;EAEhD,kBAAkB,MAAM;CAC1B;AACF;;AAGA,SAAS,gBACP,MACA,SACmB;CACnB,MAAM,OAAO,QAAQ,aAAa,sBAAsB;CAExD,IAAI,OAAO,SAAS,UAAU,OAAO;CAErC,IACE,SAAS,sBAAsB,QAC9B,SAAS,sBAAsB,QAAQ,CAAC,sBAAsB,GAE/D,OAAO,IAAI,cAAc;EACvB,GAAI,KAAK,QAAQ,EAAE,OAAO,KAAK,MAAM,IAAI,CAAC;EAC1C,GAAI,QAAQ,iBAAiB,KAAA,IAAY,EAAE,UAAU,QAAQ,aAAa,IAAI,CAAC;CACjF,CAAC;CAGH,OAAO,IAAI,aAAa;EACtB,GAAI,KAAK,QAAQ,EAAE,OAAO,KAAK,MAAM,IAAI,CAAC;EAC1C,GAAI,QAAQ,gBAAgB,KAAA,IAAY,EAAE,aAAa,QAAQ,YAAY,IAAI,CAAC;EAChF,GAAI,QAAQ,qBAAqB,KAAA,IAC7B,EAAE,kBAAkB,QAAQ,iBAAiB,IAC7C,CAAC;CACP,CAAC;AACH;AAEA,SAAS,kBAAkB,QAAsB;CAC/C,MAAM,IAAI,UAAU,qCAAqC,OAAO,MAAM,GAAG;AAC3E;;;;;;;;;;;;;;;;;;;;ACvbA,IAAa,iBAAb,MAIA;CACE;CACA,0BAAmB,IAAI,IAA+B;CACtD,YAA6C,CAAC;CAE9C,YAAY,UAAuC;EACjD,IAAI,OAAO,aAAa,YACtB,MAAM,IAAI,eAAe,kDAAkD;EAE7E,KAAKI,YAAY;CACnB;;CAGA,MAAM,KAAQ,GAAG,YAA2D;EAC1E,IAAI,CAAC,cAAc,GAAG,GACpB,MAAM,IAAI,eAAe,6CAA6C;EACxE,MAAM,eAAe,KAAKG,cAAc,UAAU;EAClD,MAAM,gBAAgB,KAAKF,QAAQ,IAAI,GAAG,KAAK,CAAC;EAChD,cAAc,KAAK,YAAY;EAC/B,KAAKA,QAAQ,IAAI,KAAK,aAAa;EAEnC,aAAa;GACX,MAAM,UAAU,KAAKA,QAAQ,IAAI,GAAG;GACpC,IAAI,CAAC,SAAS;GACd,MAAM,QAAQ,QAAQ,QAAQ,YAAY;GAC1C,IAAI,SAAS,GAAG,QAAQ,OAAO,OAAO,CAAC;GACvC,IAAI,QAAQ,WAAW,GAAG,KAAKA,QAAQ,OAAO,GAAG;EACnD;CACF;;CAGA,UAAU,GAAG,YAA2D;EACtE,MAAM,eAAe,KAAKE,cAAc,UAAU;EAClD,KAAKD,UAAU,KAAK,YAAY;EAEhC,aAAa;GACX,MAAM,QAAQ,KAAKA,UAAU,QAAQ,YAAY;GACjD,IAAI,SAAS,GAAG,KAAKA,UAAU,OAAO,OAAO,CAAC;EAChD;CACF;;CAGA,aAAoC;EAClC,MAAM,cAAqC,SAAS,SAClD,KAAKE,mBAAmB,CAAC,CAAC,SAAS,IAAI;EACzC,OAAO,+BAA+B,kBAAkB,KAAKA,mBAAmB,CAAC;CACnF;CAEA,qBAA4C;EAC1C,MAAM,yBAAS,IAAI,IAAyC;EAC5D,KAAK,MAAM,CAAC,KAAK,kBAAkB,KAAKH,SACtC,OAAO,IACL,KACA,cAAc,SAAS,EAAE,iBAAiB,UAAU,CAAC,CAAC,IAAI,yBAAyB,CACrF;EAEF,MAAM,WAAW,KAAKC,UACnB,SAAS,EAAE,iBAAiB,UAAU,CAAC,CACvC,IAAI,yBAAyB;EAEhC,OAAO,OAAO,SAAS,SAAS;GAC9B,MAAM,MAAM,MAAM,KAAKF,UAAU,OAAO;GACxC,IAAI,OAAO,QAAQ,CAAC,cAAc,GAAG,GACnC,MAAM,IAAI,eACR,2EACF;GAEF,MAAM,QAAQ,OAAO,OAAO,KAAA,IAAY,OAAO,IAAI,GAAG;GACtD,MAAM,QAAQ,SAAS,MAAM,SAAS,IAAI,QAAQ;GAElD,IAAI,MAAM,WAAW,GAAG;IACtB,MAAM,KAAK;IACX;GACF;GAEA,MAAM,sBAAsB,OAAO,SAAS,IAAI;EAClD;CACF;CAEA,cAAc,YAAoE;EAChF,IAAI,WAAW,WAAW,GACxB,MAAM,IAAI,eAAe,kDAAkD;EAE7E,KAAK,MAAM,QAAQ,YACjB,IAAI,OAAO,SAAS,YAClB,MAAM,IAAI,eAAe,4CAA4C;EAGzE,OAAO,EAAE,YAAY,CAAC,GAAG,UAAU,EAAE;CACvC;AACF;AAEA,SAAS,cAAc,OAAsC;CAC3D,OAAO,OAAO,UAAU,YAAY,OAAO,UAAU,YAAY,OAAO,UAAU;AACpF;;;ACvFA,SAAS,mBACP,YACuB;CACvB,IAAI,OAAO,eAAe,YAAY,OAAO;CAC7C,IACE,OAAO,eAAe,YACtB,eAAe,QACf,OAAO,WAAW,eAAe,YAEjC,MAAM,IAAI,eACR,wEACF;CAEF,OAAO,wBAAwB,UAAU;AAC3C;AAEA,SAAS,gBACP,OACyB;CACzB,MAAM,aAAa,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,KAAK;CACxD,IAAI,WAAW,WAAW,GACxB,MAAM,IAAI,eAAe,iEAAiE;CAE5F,OAAO,WAAW,KAAK,SAAS,mBAAmB,IAAI,CAAC;AAC1D;AAQA,SAAS,uBAA6C;CACpD,IAAI,SAAS;CACb,IAAI;CAEJ,OAAO;EACL,YAAY;GACV,IAAI,CAAC,QAAQ;IACX,SAAS;IACT,OAAO,QAAQ,QAAQ;GACzB;GAEA,MAAM,UAAU,QAAQ,uBACtB,IAAI,MAAM,8DAA8D,CAC1E;GACA,YAAY,QAAQ,IAAI,YAAY,CAAC,WAAW,OAAO,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,WAAW,KAAA,CAAS;GAC1F,OAAO;EACT;EACA,cAAc;EACd,SAAS,YAAY;GACnB,MAAM;EACR;CACF;AACF;AAEA,eAAe,gBACb,KACA,cACe;CACf,IAAI;CACJ,IAAI;EACF,MAAM,IAAI;CACZ,SAAS,OAAO;EACd,UAAU,EAAE,MAAM;CACpB;CACA,IAAI;EACF,MAAM,aAAa,QAAQ;CAC7B,SAAS,OAAO;EACd,YAAY,EAAE,MAAM;CACtB;CACA,IAAI,SAAS,MAAM,QAAQ;AAC7B;;;;;;;;;;;;;;;;AAiBA,IAAa,mBAAb,MAAa,iBAEb;CACE,cAAgD,CAAC;CAEjD,YAAY,GAAG,YAAkD;EAC/D,KAAK,IAAI,GAAG,UAAU;CACxB;;CAGA,IAAI,GAAG,YAAwD;EAC7D,MAAM,aAAa,WAAW,IAAI,kBAAkB;EACpD,KAAKK,YAAY,KAAK,GAAG,UAAU;EACnC,OAAO;CACT;CAYA,OACE,WACA,GAAG,YACkB;EACrB,IAAI,OAAO,cAAc,YACvB,MAAM,IAAI,eAAe,qDAAqD;EAGhF,MAAM,QAAQ,IAAI,iBAAoB,GAAG,UAAU;EAUnD,KAAKA,YAAY,KAAK,wBAAwB,EAR5C,kBAAkB;GAChB,MAAM,SAAS,0BAA0B,MAAM,WAAW,CAAC;GAC3D,OAAO,OAAO,SAAS,SAAS;IAC9B,IAAI,MAAM,UAAU,OAAO,GAAG,MAAM,OAAO,SAAc,IAAI;SACxD,MAAM,KAAK;GAClB;EACF,EAEiD,CAAC,CAAC;EACrD,OAAO;CACT;;;;;;;CAQA,MACE,UACA,QACA,UACM;EACN,IAAI,OAAO,WAAW,YAAY,WAAW,QAAQ,MAAM,QAAQ,MAAM,GACvE,MAAM,IAAI,eAAe,6DAA6D;EAGxF,MAAM,SAAS,IAAI,eAAqB,QAAQ;EAChD,IAAI,gBAAgB;EACpB,KAAK,MAAM,OAAO,QAAQ,QAAQ,MAAM,GAAG;GACzC,MAAM,QAAQ,OAAO;GACrB,IAAI,UAAU,KAAA,GAAW;GACzB,OAAO,MAAM,KAAU,GAAG,gBAAgB,KAAK,CAAC;GAChD,iBAAiB;EACnB;EACA,IAAI,aAAa,KAAA,GAAW;GAC1B,OAAO,UAAU,GAAG,gBAAgB,QAAQ,CAAC;GAC7C,iBAAiB;EACnB;EACA,IAAI,kBAAkB,GACpB,MAAM,IAAI,eAAe,kEAAkE;EAG7F,OAAO,KAAK,IAAI,MAAM;CACxB;;;;;;;;;CAUA,cACE,SACA,GAAG,YACkB;EACrB,IAAI,OAAO,YAAY,YACrB,MAAM,IAAI,eAAe,8DAA8D;EAGzF,MAAM,QAAQ,IAAI,iBAAoB,GAAG,UAAU;EAoBnD,KAAKA,YAAY,KAAK,wBAAwB,EAlB5C,kBAAkB;GAChB,MAAM,SAAS,0BAA0B,MAAM,WAAW,CAAC;GAC3D,OAAO,OAAO,SAAS,SAAS;IAC9B,IAAI,eAAe,qBAAqB;IACxC,IAAI;KACF,MAAM,sBAAsB,OAAO,SAAS,aAAa,IAAI,GAAG,YAAY;IAC9E,SAAS,OAAO;KACd,eAAe,qBAAqB;KACpC,MAAM,sBACE,QAAQ;MAAE;MAAO;KAAQ,GAAG,aAAa,IAAI,GACnD,YACF;IACF;IAEA,IAAI,aAAa,OAAO,GAAG,MAAM,KAAK;GACxC;EACF,EAEiD,CAAC,CAAC;EACrD,OAAO;CACT;;CAGA,aAAoC;EAClC,MAAM,cAAqC,SAAS,SAClD,KAAKC,mBAAmB,CAAC,CAAC,SAAS,IAAI;EACzC,OAAO,+BAA+B,kBAAkB,KAAKA,mBAAmB,CAAC;CACnF;CAEA,qBAA4C;EAC1C,MAAM,WAAW,KAAKD,YAAY,IAAI,yBAAyB;EAC/D,QAAQ,SAAS,SAAS,sBAAsB,UAAU,SAAS,IAAI;CACzE;AACF;AChMA,MAAM,aAAa;AACnB,MAAM,OAAO;AACb,MAAM,iBAAiB;AACvB,MAAM,2CAA2B,IAAI,IAAI,CAAC,MAAM,IAAI,CAAC;AAErD,SAAS,iBAAiB,OAA2B,MAAoB;CACvE,IAAI,UAAU,KAAA,MAAc,CAAC,OAAO,SAAS,KAAK,KAAK,QAAQ,IAC7D,MAAM,IAAI,eACR,sBAAsB,KAAK,6CAA6C,OAC1E;AAEJ;AAEA,SAAS,sBAAsB,OAA8C;CAC3E,IAAI,OAAO,UAAU,YAAY,UAAU,MAAM,OAAO;CACxD,MAAM,SAAS;CACf,OACE,OAAO,OAAO,eAAe,YAC7B,OAAO,OAAO,qBAAqB,cACnC,OAAO,OAAO,wBAAwB,cACtC,OAAO,OAAO,SAAS,cACvB,OAAO,OAAO,UAAU;AAE5B;AAEA,SAAS,WAAW,QAA8B;CAChD,IAAI,OAAO,WAAW,KAAA,GAAW,OAAO,OAAO;CAC/C,MAAM,wBAAQ,IAAI,MAAM,gCAAgC;CACxD,MAAM,OAAO;CACb,OAAO;AACT;AAEA,SAAS,aAAa,OAAoC;CACxD,IAAI,iBAAiB,SAAS,MAAM,SAAS,OAAO,MAAM;CAC1D,IAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,aAAa,OAAO;EACrE,MAAM,UAAW,MAAgC;EACjD,IAAI,OAAO,YAAY,YAAY,SAAS,OAAO;CACrD;AAEF;AAEA,SAAS,WAAW,OAAgB,KAAa,OAA0C;CACzF,MAAM,UAAU,aAAa,KAAK;CAClC,IAAI,CAAC,SAAS,OAAO,KAAA;CACrB,MAAM,OAAO,QAAQ,WAAW,KAAK,UAAU,GAAG,CAAC;CACnD,OAAO,QAAQ,KAAK,WAAW,OAAO,WAAW,KAAK,CAAC,IAAI;AAC7D;AAEA,SAAS,eAAe,OAAqE;CAC3F,MAAM,QACJ,OAAO,UAAU,YAAY,UAAU,OAAQ,QAA6B,KAAA;CAC9E,OAAO;EACL,MAAM,OAAO,OAAO,SAAS,WAAW,MAAM,OAAO;EACrD,QAAQ,OAAO,OAAO,WAAW,WAAW,MAAM,SAAS;EAC3D,UAAU,OAAO,aAAa;CAChC;AACF;AAEA,SAAS,YAAY,OAAsD;CACzE,MAAM,OACJ,OAAO,UAAU,YAAY,UAAU,QAAQ,UAAU,QACpD,MAA6B,OAC9B,KAAA;CAEN,IAAI,OAAO,SAAS,UAAU,OAAO;CACrC,IAAI,gBAAgB,aAAa,OAAO,IAAI,YAAY,CAAC,CAAC,OAAO,IAAI;CACrE,IAAI,YAAY,OAAO,IAAI,GACzB,OAAO,IAAI,YAAY,CAAC,CAAC,OAAO,IAAI,WAAW,KAAK,QAAQ,KAAK,YAAY,KAAK,UAAU,CAAC;CAE/F,IAAI,OAAO,SAAS,eAAe,gBAAgB,MAAM,OAAO,KAAK,KAAK;AAE5E;AAEA,SAAS,UAAU,MAAuB;CACxC,OAAO,OAAO,SAAS,YAAY,SAAS,QAAQ,UAAU,OAC1D,OAAQ,KAA2B,IAAI,IACvC;AACN;;AAGA,IAAa,qBAAb,MAA6D;CAC3D,OAAgB;CAEhB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA,YAAY,UAAqC,CAAC,GAAG;EACnD,MAAM,OAAO,QAAQ,QAAA;EACrB,IACE,OAAO,SAAS,YAChB,KAAK,KAAK,MAAM,MAChB,KAAK,SAAS,GAAG,KACjB,KAAK,SAAS,GAAG,KACjB,sBAAsB,KAAK,IAAI,GAE/B,MAAM,IAAI,eAAe,gEAAgE;EAE3F,IAAI,QAAQ,kBAAkB,KAAA,KAAa,OAAO,QAAQ,kBAAkB,YAC1E,MAAM,IAAI,eAAe,4DAA4D;EAEvF,IACE,QAAQ,wBAAwB,KAAA,KAChC,OAAO,QAAQ,wBAAwB,YAEvC,MAAM,IAAI,eAAe,6DAA6D;EAGxF,MAAM,OAAO,QAAQ,QAAQ;EAC7B,IAAI,SAAS,UAAU,SAAS,WAAW,SAAS,UAClD,MAAM,IAAI,eAAe,6CAA6C,OAAO,IAAI,GAAG;EAEtF,IAAI,SAAS,YAAY,CAAC,QAAQ,eAChC,MAAM,IAAI,eACR,kFACF;EAGF,iBAAiB,QAAQ,aAAa,aAAa;EACnD,iBAAiB,QAAQ,WAAW,WAAW;EAC/C,iBAAiB,QAAQ,kBAAkB,kBAAkB;EAE7D,KAAKE,QAAQ;EACb,KAAKC,iBAAiB,QAAQ;EAC9B,KAAKC,QAAQ;EACb,KAAKC,eAAe,QAAQ,eAAe;EAC3C,KAAKC,aAAa,QAAQ,aAAa;EACvC,KAAKC,oBAAoB,QAAQ,oBAAoB;EACrD,KAAKC,uBAAuB,QAAQ;EACpC,KAAKC,SAAS,QAAQ,SAAS;CACjC;CAEA,MAAM,QAAQ,SAA0C;EACtD,IAAI,QAAQ,OAAO,SAAS,MAAM,WAAW,QAAQ,MAAM;EAE3D,MAAM,QAAQ,QAAQ,YAAY,MAAM,QAAQ,SAAS,IAAI;EAC7D,IAAI,QAAQ,OAAO,SAAS,MAAM,WAAW,QAAQ,MAAM;EAC3D,IAAI,QAAQ,aAAa,CAAC,OAAO,MAAM,IAAI,wBAAwB;EAEnE,MAAM,WAAW,KAAKN,mBAAmB,KAAA;EACzC,MAAM,iBAAiB,KAAKA,kBAAkB,KAAKO,sBAAsB;EACzE,MAAM,OAAO,KAAKN,UAAU,SAAU,WAAW,WAAW,UAAW,KAAKA;EAC5E,MAAM,EAAE,YAAY,cAAc,KAAKO,MAAM,QAAQ,OAAO;EAC5D,MAAM,MAAM,SAAS,WAAW,QAAQ,GAAG,YAAY,WAAW,EAAE,MAAM,CAAC,MAAM;EAEjF,IAAI;EACJ,IAAI,UAAU;GACZ,MAAM,WAAW,MAAM,QAAQ,YAAY,UAAU;GACrD,IAAI,QAAQ,OAAO,SAAS,MAAM,WAAW,QAAQ,MAAM;GAC3D,IAAI,SAAS,YAAY,OAAO,SAAS,IAAI,iBAAiB,UAAU,OAAO;GAC/E,UAAU,OAAO,YAAY,SAAS,QAAQ,CAAC;EACjD;EAEA,IAAI;EACJ,IAAI;GACF,QAAQ,WACJ,IAAI,eAAe,KAAK,CAAC,GAAG;IAC1B,GAAI,UAAU,EAAE,QAAQ,IAAI,CAAC;IAC7B,GAAI,KAAKJ,oBAAoB,IAAI,EAAE,kBAAkB,KAAKA,kBAAkB,IAAI,CAAC;GACnF,CAAC,IACD,IAAI,eAAe,GAAG;EAC5B,SAAS,OAAO;GACd,MAAM,SAAS,WAAW,OAAO,KAAK,KAAK;GAC3C,MAAM,IAAI,MACR,gCAAgC,UAAU,GAAG,IAAI,SAAS,KAAK,WAAW,IAC5E;EACF;EAEA,IAAI,CAAC,sBAAsB,KAAK,GAC9B,MAAM,IAAI,eACR,kFACF;EAGF,IAAI;GACF,MAAM,aAAa;EACrB,QAAQ,CAER;EAEA,OAAO,KAAKK,QAAQ,OAAO,KAAK,OAAO,OAAO;CAChD;CAEA,wBAAuC;EACrC,MAAM,iBAAiB,QAAQ,IAAI,YAAY,WAAW;EAC1D,IAAI,OAAO,mBAAmB,YAC5B,MAAM,IAAI,eACR,sEACF;EAEF,OAAO;CACT;CAEA,MAAM,SAA4D;EAChE,MAAM,UAAU,IAAI,IAAI,QAAQ,SAAS,KAAKV,KAAK,CAAC;EACpD,MAAM,SAAS,IAAI,IAAI,OAAO;EAC9B,IAAI,QAAQ,aAAa,UAAU,OAAO,WAAW;OAChD,IAAI,QAAQ,aAAa,SAAS,OAAO,WAAW;OAEvD,MAAM,IAAI,eACR,kEAAkE,QAAQ,UAC5E;EAEF,OAAO;GAAE,YAAY,QAAQ,SAAS;GAAG,WAAW,OAAO,SAAS;EAAE;CACxE;CAEA,QACE,QACA,KACA,OACA,SACe;EACf,OAAO,IAAI,SAAe,SAAS,WAAW;GAC5C,IAAI,UAAU;GACd,IAAI,YAAY;GAChB,IAAI,SAAS;GACb,IAAI,kBAAkB;GACtB,IAAI;GACJ,IAAI,eAAe,QAAQ,QAAQ;GACnC,IAAI,gBAAgB;GACpB,IAAI;GACJ,IAAI;GACJ,IAAI;GAEJ,MAAM,4BAA4B;IAChC,kBAAkB;IAClB,aAAa;IACb,kBAAkB;IAClB,kBAAkB,KAAA;IAClB,aAAa,KAAA;IACb,kBAAkB,KAAA;IAClB,OAAO,oBAAoB,QAAQ,MAAM;IACzC,OAAO,oBAAoB,WAAW,SAAS;IAC/C,OAAO,oBAAoB,SAAS,OAAO;IAC3C,OAAO,oBAAoB,SAAS,OAAO;GAC7C;GAEA,MAAM,UAAU,UAAoB;IAClC,IAAI,SAAS;IACb,UAAU;IACV,oBAAoB;IACpB,QAAQ,OAAO,oBAAoB,SAAS,OAAO;IACnD,IAAI,UAAU,KAAA,GAAW,OAAO,KAAK;SAChC,QAAQ;GACf;GAEA,MAAM,kBAAkB,OAAgB,OAAO,KAAM,SAAS,sBAAsB;IAClF,IAAI,WAAW,WAAW;IAC1B,cAAc;IACd,YAAY;IACZ,oBAAoB;IACpB,IAAI;KACF,IAAI,OAAO,eAAe,cAAc,OAAO,eAAe,MAC5D,OAAO,MAAM,MAAM,MAAM;IAE7B,QAAQ,CAER;IACA,aAAkB,WACV,OAAO,KAAK,IACjB,eAAwB,OAAO,UAAU,CAC5C;GACF;GAEA,MAAM,qBAAqB;IACzB,aAAa;IACb,IAAI,KAAKG,gBAAgB,GAAG;IAC5B,aAAa,KAAKI,OAAO,eAAe;KACtC,+BACE,IAAI,MAAM,aAAa,UAAU,GAAG,EAAE,2BAA2B,GACjE,KACA,cACF;IACF,GAAG,KAAKJ,YAAY;GACtB;GAEA,MAAM,0BAA0B;IAC9B,IAAI,KAAKC,cAAc,KAAK,WAAW,WAAW;IAClD,kBAAkB,KAAKG,OAAO,eAAe;KAC3C,IAAI,WAAW,aAAa,OAAO,eAAe,MAAM;KACxD,IAAI;MACF,OAAO,KAAK,MAAM;KACpB,SAAS,OAAO;MACd,MAAM,SAAS,WAAW,OAAO,KAAK,KAAK;MAC3C,+BACE,IAAI,MACF,uCAAuC,UAAU,GAAG,OACjD,SAAS,KAAK,WAAW,GAC9B,CACF;MACA;KACF;KACA,kBAAkB;IACpB,GAAG,KAAKH,UAAU;GACpB;GAEA,MAAM,eAAe;IACnB,IAAI,WAAW,aAAa,QAAQ;IACpC,SAAS;IACT,kBAAkB;IAClB,kBAAkB,KAAA;IAClB,aAAa;IACb,kBAAkB;IAClB,IAAI;KACF,QAAQ,OAAO;IACjB,SAAS,OAAO;KACd,eAAe,KAAK;IACtB;GACF;GAEA,MAAM,aAAa,UAAmB;IACpC,IAAI,WAAW,WAAW;IAC1B,aAAa;IACb,IAAI;IACJ,IAAI;KACF,MAAM,MAAM,YAAY,KAAK;KAC7B,IAAI,QAAQ,KAAA,GACV,UAAU,QAAQ,QAAQ,GAAG,CAAC,CAAC,MAC5B,WAAW,EAAE,KAAK,MAAM,KACxB,WAAoB,EAAE,MAAM,EAC/B;IAEJ,SAAS,OAAO;KACd,UAAU,QAAQ,QAAQ,EAAE,MAAM,CAAC;IACrC;IACA,IAAI,YAAY,KAAA,GAAW;IAE3B,IAAI,iBAAA,KAAqC;KACvC,+BACE,IAAI,MAAM,aAAa,UAAU,GAAG,EAAE,6BAA6B,GACnE,KACA,gBACF;KACA;IACF;IAEA,iBAAiB;IACjB,eAAe,aACZ,KAAK,YAAY;KAChB,iBAAiB;KACjB,IAAI,WAAW,iBAAiB;KAEhC,MAAM,UAAU,MAAM;KACtB,IAAI,WAAW,SAAS;MACtB,QAAQ,aAAa,QAAQ,OAAO,0BAA0B;MAC9D;KACF;KACA,MAAM,EAAE,QAAQ;KAChB,IAAI,WAAW,mBAAmB,QAAQ,QAAQ;KAElD,IAAI;KACJ,IAAI;MACF,OAAO,KAAK,MAAM,GAAG;KACvB,SAAS,OAAO;MACd,QAAQ,aAAa,OAAO,GAAG;MAC/B;KACF;KACA,QAAQ,QAAQ;MAAE,MAAM,UAAU,IAAI;MAAG;KAAK,CAAC;IACjD,CAAC,CAAC,CACD,OAAO,UAAmB;KACzB,IAAI,WAAW,MAAM;KACrB,eAAe,KAAK;IACtB,CAAC;GACL;GAEA,MAAM,WAAW,UAAmB;IAClC,IAAI,WAAW,WAAW;IAC1B,cACE,OAAO,UAAU,YAAY,UAAU,QAAQ,WAAW,QACrD,MAA8B,QAC/B;GACR;GAEA,MAAM,cAAc,OAAO,UAAkC;IAC3D,MAAM,EAAE,MAAM,QAAQ,aAAa,eAAe,KAAK;IACvD,IAAI;IAEJ,IAAI,QAAQ,aAAa,yBAAyB,IAAI,IAAI,GACxD,UAAU,IAAI,wBAAwB;SACjC,IAAI,gBAAgB,KAAA,GAAW;KACpC,MAAM,SAAS,WAAW,aAAa,KAAK,KAAK;KACjD,0BAAU,IAAI,MACZ,aAAa,UAAU,GAAG,EAAE,qBAAqB,SAAS,KAAK,WAAW,IAC5E;IACF,OAAO,IAAI,UAAU,SAAS,kBAAkB,UAC9C,UAAU,KAAA;SACL;KACL,MAAM,aAAa,QAAQ,OAAO,WAAW,OAAO,WAAW,KAAK,CAAC,IAAI;KACzE,0BAAU,IAAI,MACZ,aAAa,UAAU,GAAG,EAAE,kBAAkB,UAC3C,aAAa,KAAK,eAAe,GACtC;IACF;IAEA,IACE,CAAC,UACD,QAAQ,aACR,EAAE,mBAAmB,4BACrB,KAAKE,sBAEL,IAAI;KACF,IAAI,MAAM,KAAKA,qBAAqB,SAAS,QAAQ,MAAM,GACzD,UAAU,IAAI,wBAAwB;IAE1C,QAAQ,CAER;IAGF,IAAI,QAAQ,OAAO,SAAS;KAC1B,kBAAkB;KAClB,OAAO,WAAW,QAAQ,MAAM,CAAC;KACjC;IACF;IAEA,IAAI;KACF,MAAM;KACN,OAAO,OAAO;IAChB,SAAS,OAAO;KACd,OAAO,KAAK;IACd;GACF;GAEA,MAAM,WAAW,UAAmB;IAClC,IAAI,WAAW,WAAW;IAC1B,IAAI,QAAQ,OAAO,SAAS;KAC1B,kBAAkB;KAClB,OAAO,WAAW,QAAQ,MAAM,CAAC;KACjC;IACF;IACA,YAAY;IACZ,oBAAoB;IACpB,YAAiB,KAAK;GACxB;GAEA,MAAM,gBAAgB;IACpB,IAAI,SAAS;IACb,MAAM,QAAQ,WAAW,QAAQ,MAAM;IACvC,kBAAkB;IAClB,YAAY;IACZ,oBAAoB;IACpB,IAAI;KACF,IAAI,OAAO,eAAe,cAAc,OAAO,eAAe,MAC5D,OAAO,MAAM,gBAAgB,SAAS;IAE1C,QAAQ,CAER;IACA,OAAO,KAAK;GACd;GAEA,OAAO,iBAAiB,QAAQ,MAAM;GACtC,OAAO,iBAAiB,WAAW,SAAS;GAC5C,OAAO,iBAAiB,SAAS,OAAO;GACxC,OAAO,iBAAiB,SAAS,OAAO;GACxC,QAAQ,OAAO,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;GAEhE,IAAI,KAAKD,oBAAoB,GAC3B,kBAAkB,KAAKE,OAAO,eAAe;IAC3C,+BACE,IAAI,MAAM,qCAAqC,UAAU,GAAG,GAAG,GAC/D,KACA,mBACF;GACF,GAAG,KAAKF,iBAAiB;GAG3B,IAAI,QAAQ,OAAO,SAAS,QAAQ;EACtC,CAAC;CACH;AACF"}
|