moost 0.6.53 → 0.7.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/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
+ import * as _wooksjs_event_core from '@wooksjs/event-core';
1
2
  import { EventContext, Logger, Accessor, Key } from '@wooksjs/event-core';
2
- export { Cached, ContextInjector, EventContext, EventContextOptions, Key, Logger, cached, createEventContext, current, eventTypeKey, getContextInjector, key, replaceContextInjector, resetContextInjector, run, useLogger } from '@wooksjs/event-core';
3
+ export { Cached, ContextInjector, EventContext, EventContextOptions, Key, Logger, TLocaleInput, cached, createEventContext, current, eventTypeKey, getContextInjector, key, replaceContextInjector, resetContextInjector, run, useLocale, useLogger } from '@wooksjs/event-core';
3
4
  import { TProvideRegistry, TReplaceRegistry, Infact, TProvideFn } from '@prostojs/infact';
4
5
  export { TProvideRegistry, createProvideRegistry, createReplaceRegistry, getClassKey } from '@prostojs/infact';
5
6
  import * as _prostojs_mate from '@prostojs/mate';
@@ -280,8 +281,19 @@ interface TPipeData {
280
281
  declare const resolvePipe: TPipeFn<TEmpty>;
281
282
 
282
283
  type TInterceptorBeforeFn = (reply: (response: TAny) => void) => void | Promise<void>;
283
- type TInterceptorAfterFn = (response: TAny, reply: (response: TAny) => void) => void | Promise<void>;
284
- type TInterceptorErrorFn = (error: Error, reply: (response: TAny) => void) => void | Promise<void>;
284
+ /**
285
+ * After hook: runs when the CURRENT value of the after/error chain is not an `Error`.
286
+ * `response` is the handler result as replaced by the hooks that ran before this one;
287
+ * `original` is the value that entered the chain. A throw, or a `reply()` with an `Error`,
288
+ * switches the rest of the chain to error hooks.
289
+ */
290
+ type TInterceptorAfterFn = (response: TAny, reply: (response: TAny) => void, original?: TAny) => void | Promise<void>;
291
+ /**
292
+ * Error hook: runs when the CURRENT value of the after/error chain is an `Error` — raised by
293
+ * the handler, a before hook, or an after/error hook that ran before this one. `original` is
294
+ * the value that entered the chain. A `reply()` with a non-Error value recovers the chain.
295
+ */
296
+ type TInterceptorErrorFn = (error: Error, reply: (response: TAny) => void, original?: TAny) => void | Promise<void>;
285
297
  /**
286
298
  * Object-based interceptor definition.
287
299
  *
@@ -296,7 +308,7 @@ type TInterceptorErrorFn = (error: Error, reply: (response: TAny) => void) => vo
296
308
  * after(response, reply) {
297
309
  * reply(transform(response))
298
310
  * },
299
- * error(error, reply) {
311
+ * error(error, reply, original) {
300
312
  * reply(formatError(error))
301
313
  * },
302
314
  * }
@@ -529,6 +541,16 @@ interface TDisposeOptions {
529
541
  */
530
542
  onError?: 'warn' | 'throw';
531
543
  }
544
+ /**
545
+ * @internal One dispose hook: `instance[method]`, or `fn` for a plain-function hook
546
+ * (`Moost.addDisposeHook`, where `instance`/`method` only identify it in failures).
547
+ */
548
+ interface TDisposeHook {
549
+ instance: object;
550
+ method: string | symbol;
551
+ priority: number;
552
+ fn?: () => unknown;
553
+ }
532
554
  /**
533
555
  * Runs the dispose hooks of every given instance, once per instance, ever.
534
556
  *
@@ -548,6 +570,17 @@ interface TDisposeOptions {
548
570
  */
549
571
  declare function disposeInstances(instances: Iterable<object>, opts?: TDisposeOptions): Promise<TDisposeResult>;
550
572
 
573
+ /**
574
+ * Event kind of a run started with {@link Moost.runDetached} (`eventTypeKey` = `'DETACHED'`).
575
+ * Its own event, with no request: HTTP composables are not available inside.
576
+ */
577
+ declare const detachedKind: _wooksjs_event_core.EventKind<{}>;
578
+ /** Options for {@link Moost.runDetached}. */
579
+ interface TRunDetachedOptions {
580
+ /** Logger of the run (`useLogger()` inside, and where a failure is logged). Default: `app.getLogger(name)`. */
581
+ logger?: TConsoleBase;
582
+ }
583
+
551
584
  interface TMoostOptions {
552
585
  /**
553
586
  * Global path prefix — mounts the whole app under one path segment.
@@ -734,6 +767,10 @@ declare class Moost extends Hookable {
734
767
  protected singletonInstances: Set<object>;
735
768
  /** In-flight/settled `dispose()` run — makes disposal idempotent. */
736
769
  protected disposePromise?: Promise<void>;
770
+ /** Functions registered with {@link addDisposeHook}. */
771
+ protected disposeHooks: TDisposeHook[];
772
+ /** Detached runs in flight — {@link dispose} waits for them before the instance hooks. */
773
+ protected detachedRuns: Set<Promise<unknown>>;
737
774
  constructor(options?: TMoostOptions | undefined);
738
775
  _fireEventStart(source: TMoostAdapter<unknown>): void;
739
776
  _fireEventEnd(source: TMoostAdapter<unknown>): void;
@@ -803,6 +840,51 @@ declare class Moost extends Hookable {
803
840
  addInitHook(fn: (app: this) => unknown, opts?: {
804
841
  priority?: number;
805
842
  }): void;
843
+ /**
844
+ * ### addDisposeHook
845
+ * Programmatic counterpart of `@MoostDispose`: runs `fn(app)` once on
846
+ * {@link dispose} — after every adapter's `onDispose` (no new work comes in), in the
847
+ * same pass as the `@MoostDispose` instance hooks, by ascending `priority`
848
+ * (default 0; instance hooks first on ties). Awaited; a failure is collected into
849
+ * the `AggregateError` `dispose()` rejects with, after the other hooks ran.
850
+ *
851
+ * Use it for resources the app itself owns — a timer, a periodic sweep, a queue
852
+ * consumer started in an init hook.
853
+ *
854
+ * Hooks registered after `dispose()` was called do not run.
855
+ *
856
+ * ```ts
857
+ * app.addInitHook(() => {
858
+ * const timer = setInterval(() => void app.runDetached('sweep', sweepExpired), 60_000)
859
+ * app.addDisposeHook(() => clearInterval(timer))
860
+ * })
861
+ * ```
862
+ */
863
+ addDisposeHook(fn: (app: this) => unknown, opts?: {
864
+ priority?: number;
865
+ }): void;
866
+ /**
867
+ * ### runDetached
868
+ * Runs `fn` as its own event, detached from the caller: a fresh ROOT event context
869
+ * (kind {@link detachedKind}, not a child of the calling request, so nothing it does is tied
870
+ * to the request's lifetime), with its own `FOR_EVENT` DI scope — released when `fn`
871
+ * settles — and the app as controller context (`useControllerContext().instantiate()` works).
872
+ * Context-injector instrumentation (OpenTelemetry) sees it as a `DETACHED` event.
873
+ *
874
+ * Call it from a handler to do work after the response, without awaiting it. The returned
875
+ * promise resolves with `fn`'s result, or `undefined` when it fails — a failure is logged
876
+ * (`opts.logger`, default the app logger topic `name`) and never rejects, so a forgotten
877
+ * `await` cannot crash the process. It is not a job queue: nothing persists or retries it.
878
+ *
879
+ * ```ts
880
+ * @Post('signup')
881
+ * signup(@Body() user: TUser) {
882
+ * void this.app.runDetached('welcome-mail', () => this.mailer.sendWelcome(user.email))
883
+ * return { ok: true }
884
+ * }
885
+ * ```
886
+ */
887
+ runDetached<R>(name: string, fn: () => R | Promise<R>, opts?: TRunDetachedOptions): Promise<Awaited<R> | undefined>;
806
888
  /**
807
889
  * ### dispose
808
890
  * Graceful shutdown: stops the adapters, then runs every `@MoostDispose`
@@ -812,7 +894,8 @@ declare class Moost extends Hookable {
812
894
  * `[Symbol.dispose]()` method is disposed through that.
813
895
  *
814
896
  * Order: every adapter's `onDispose` in registration order (stop taking new
815
- * work first), then instance hooks by ascending `priority`. Everything is
897
+ * work first), then the {@link runDetached} runs in flight are awaited, then
898
+ * instance hooks and {@link addDisposeHook} functions by ascending `priority`. Everything is
816
899
  * awaited, failures never stop the run — once all hooks have run, a failed
817
900
  * disposal rejects the returned promise with an `AggregateError` naming each
818
901
  * failing `Class.method`.
@@ -1139,8 +1222,19 @@ declare function setOvertake(fn: TOvertakeFn, ctx?: EventContext): void;
1139
1222
  declare function useOvertake(ctx?: EventContext): TOvertakeFn;
1140
1223
  /** Stores the interceptor result value in the current event context. */
1141
1224
  declare function setInterceptResult(value: unknown, ctx?: EventContext): void;
1142
- /** Retrieves the interceptor result value from the current event context. */
1225
+ /**
1226
+ * Retrieves the interceptor result value from the current event context: in an after/error
1227
+ * hook, the CURRENT value of the chain — the handler result or error as replaced by the
1228
+ * hooks that ran before this one.
1229
+ */
1143
1230
  declare function useInterceptResult<T = unknown>(ctx?: EventContext): T;
1231
+ /** Stores the value that entered the after/error chain in the current event context. */
1232
+ declare function setInterceptOriginal(value: unknown, ctx?: EventContext): void;
1233
+ /**
1234
+ * In an after/error hook: the value that entered the after/error chain — the handler result,
1235
+ * the error it raised, or a before hook's reply — before any hook replaced it.
1236
+ */
1237
+ declare function useInterceptOriginal<T = unknown>(ctx?: EventContext): T;
1144
1238
 
1145
1239
  type TInterceptorDefFactory = () => TInterceptorDef | Promise<TInterceptorDef>;
1146
1240
  /**
@@ -1168,16 +1262,21 @@ declare function Before(): MethodDecorator;
1168
1262
  * ## @After
1169
1263
  * ### Method Decorator
1170
1264
  * Marks a method as an after-phase interceptor hook.
1171
- * Runs after successful handler execution.
1172
- * Use @Response() param for the handler result, @Overtake() for the reply fn.
1265
+ * Runs when the current value of the after/error chain is not an Error.
1266
+ * Use @Response() param for the current value, @Overtake() for the reply fn,
1267
+ * `useInterceptOriginal()` for the value that entered the chain.
1268
+ * Throwing (or replying with an Error) hands the error to the error hooks
1269
+ * of the interceptors that run after this one.
1173
1270
  */
1174
1271
  declare function After(): MethodDecorator;
1175
1272
  /**
1176
1273
  * ## @OnError
1177
1274
  * ### Method Decorator
1178
1275
  * Marks a method as an error-phase interceptor hook.
1179
- * Runs when the handler throws or returns an Error.
1180
- * Use @Response() param for the error, @Overtake() for the reply fn.
1276
+ * Runs when the current value of the after/error chain is an Error — the handler
1277
+ * threw or returned one, or an earlier after/error hook raised or replied one.
1278
+ * Use @Response() param for the error, @Overtake() for the reply fn
1279
+ * (a non-Error reply recovers the chain).
1181
1280
  */
1182
1281
  declare function OnError(): MethodDecorator;
1183
1282
  /**
@@ -1190,9 +1289,9 @@ declare function Overtake(): ParameterDecorator;
1190
1289
  /**
1191
1290
  * ## @Response
1192
1291
  * ### Parameter Decorator
1193
- * Injects the handler result into an interceptor method parameter.
1194
- * In @After methods, this is the successful response.
1195
- * In @OnError methods, this is the Error that was thrown.
1292
+ * Injects the current value of the after/error chain into an interceptor method parameter:
1293
+ * the handler result (or error) as replaced by the hooks that ran before this one.
1294
+ * In @After methods, this is the response; in @OnError methods, the Error.
1196
1295
  */
1197
1296
  declare function Response(): ParameterDecorator;
1198
1297
 
@@ -1292,6 +1391,18 @@ declare function Param(name: string): MethodDecorator & ClassDecorator & Paramet
1292
1391
  * @paramType object
1293
1392
  */
1294
1393
  declare function Params(): MethodDecorator & ClassDecorator & ParameterDecorator & PropertyDecorator;
1394
+ /**
1395
+ * Get the locale of the current event (`useLocale().get()`), for any event kind.
1396
+ *
1397
+ * Moost only carries the locale: the app decides how it is chosen (for HTTP, the
1398
+ * `resolveLocale` option of `MoostHttp`, typically with `useAcceptLanguage().negotiate()`;
1399
+ * or `useLocale().set()` in an interceptor) and how text is translated (see `setTranslator`).
1400
+ * A workflow or other child event reads the locale of the event that started it.
1401
+ * Resolves `undefined` when no locale is set.
1402
+ * @decorator
1403
+ * @paramType string | undefined
1404
+ */
1405
+ declare function Locale(): ParameterDecorator & PropertyDecorator;
1295
1406
  /**
1296
1407
  * Provide Const Value
1297
1408
  * @decorator
@@ -1316,28 +1427,33 @@ interface TInterceptorEntry {
1316
1427
  /** Pre-computed span name for ci.with(). */
1317
1428
  spanName: string;
1318
1429
  }
1319
- /** A registered after/onError hook of one event. */
1320
- interface TRegisteredHook<F> {
1430
+ /** The after/error hooks of one interceptor def, registered for one event. */
1431
+ interface TRegisteredHooks {
1321
1432
  /** Pre-computed span name for ci.with(). */
1322
1433
  spanName: string;
1323
- fn: F;
1434
+ after?: TInterceptorAfterFn;
1435
+ error?: TInterceptorErrorFn;
1324
1436
  }
1325
1437
  /**
1326
1438
  * Manages the before/after/error interceptor lifecycle for a single event.
1327
1439
  * Optimised for the common sync path — only allocates promises when an interceptor goes async.
1328
1440
  */
1329
1441
  declare class InterceptorHandler {
1330
- protected after: TRegisteredHook<TInterceptorAfterFn>[] | undefined;
1331
- protected onError: TRegisteredHook<TInterceptorErrorFn>[] | undefined;
1442
+ protected hooks: TRegisteredHooks[] | undefined;
1332
1443
  response?: unknown;
1444
+ /**
1445
+ * After {@link fireAfter}: whether the final value is raised (thrown) rather than returned.
1446
+ * Set by a hook that throws; cleared by a hook replying a non-Error value.
1447
+ */
1448
+ raised: boolean;
1333
1449
  responseOverwritten: boolean;
1334
1450
  protected _boundReplyFn?: (reply: unknown) => void;
1335
1451
  protected handlers: TInterceptorEntry[];
1336
1452
  constructor(handlers: TInterceptorEntry[]);
1337
1453
  protected getReplyFn(): (reply: unknown) => void;
1338
1454
  get count(): number;
1339
- get countAfter(): number;
1340
- get countOnError(): number;
1455
+ /** Number of registered interceptors with an after and/or error hook. */
1456
+ get countHooks(): number;
1341
1457
  /**
1342
1458
  * Register hooks from a TInterceptorDef.
1343
1459
  * Returns a pending PromiseLike if `before` went async, or undefined.
@@ -1347,7 +1463,26 @@ declare class InterceptorHandler {
1347
1463
  private _beforeAsyncFactory;
1348
1464
  private _beforeAsyncPending;
1349
1465
  private _beforeFrom;
1350
- fireAfter(response: unknown): unknown;
1466
+ /**
1467
+ * Runs the after/error hooks as ONE chain over the current value, innermost interceptor
1468
+ * first. Each hook sees the current value (the previous hook's reply) and the `original`
1469
+ * value that entered the chain: a raised value or an `Error` runs the hook's `error` fn,
1470
+ * anything else its `after` fn. A hook that throws (anything), or replies with an `Error`,
1471
+ * switches the rest of the chain to error mode; a non-Error reply recovers it.
1472
+ *
1473
+ * `raised` tells whether `response` was thrown (by the handler or a before hook) rather than
1474
+ * returned; read {@link raised} afterwards for the final value's state.
1475
+ */
1476
+ fireAfter(response: unknown, raised?: boolean): unknown;
1477
+ /**
1478
+ * Runs one interceptor's hook for the current value: its `error` fn while the chain is in
1479
+ * error mode (a raised value — even a thrown non-Error — or an `Error`), else its `after` fn.
1480
+ * A sync throw switches the chain to error mode; an async hook's promise is returned for the
1481
+ * caller to await (its rejection switches to error mode there).
1482
+ */
1483
+ private runHook;
1484
+ /** A hook threw: the error becomes the current value, raised. */
1485
+ private raise;
1351
1486
  private _fireAfterAsync;
1352
1487
  }
1353
1488
 
@@ -1630,10 +1765,54 @@ declare function defineInterceptor(def: TInterceptorDef, priority?: TInterceptor
1630
1765
  */
1631
1766
  declare function definePipeFn<T extends TObject = TEmpty>(fn: TPipeFn<T>, priority?: TPipePriority): TPipeFn<T>;
1632
1767
 
1768
+ /** What a translator gets besides the text: the event's locale and the event itself. */
1769
+ interface TTranslateContext {
1770
+ /** `useLocale().get()` of the event the text is translated for. */
1771
+ locale: string | undefined;
1772
+ /** The event context — read anything else your policy needs from it. */
1773
+ event: EventContext;
1774
+ }
1775
+ /**
1776
+ * The app's translator. `text` is a source text (gettext-style message id) or an error/message
1777
+ * code (`WF_EXPIRED`, `NAME_TAKEN`, …); `params` are the values it refers to. Return the
1778
+ * translation, or `undefined` to keep `text` as is.
1779
+ *
1780
+ * Moost never translates on its own: plug in any i18n library (vue-i18n, i18next, FormatJS)
1781
+ * or a plain map here.
1782
+ */
1783
+ type TTranslate = (text: string, params: Record<string, unknown> | undefined, ctx: TTranslateContext) => string | undefined;
1784
+ /**
1785
+ * Registers the app's translator (process-wide), or removes it with `undefined`.
1786
+ * It takes effect where code calls {@link useTranslate} — your own code and packages that opt
1787
+ * in to translating the text they serialise; with no translator registered, text stays as
1788
+ * authored.
1789
+ *
1790
+ * @example
1791
+ * ```ts
1792
+ * const messages = { de: { 'Name taken': 'Name vergeben' } }
1793
+ * setTranslator((text, params, { locale }) => messages[locale ?? '']?.[text])
1794
+ * ```
1795
+ */
1796
+ declare function setTranslator(translate: TTranslate | undefined): void;
1797
+ /** The registered translator, or `undefined` (e.g. to skip a per-locale variant entirely). */
1798
+ declare function getTranslator(): TTranslate | undefined;
1799
+ /**
1800
+ * Returns `t(text, params?)` for the current event: the registered translator's result in the
1801
+ * event's locale (`useLocale().get()`), else `text` unchanged — never a missing-key marker.
1802
+ * With no translator registered, `t` returns `text` (params are not interpolated).
1803
+ *
1804
+ * @example
1805
+ * ```ts
1806
+ * const t = useTranslate()
1807
+ * throw new HttpError(409, { message: t('Name taken'), code: 'NAME_TAKEN' })
1808
+ * ```
1809
+ */
1810
+ declare function useTranslate(ctx?: EventContext): (text: string, params?: Record<string, unknown>) => string;
1811
+
1633
1812
  /** Creates a new `ProstoLogger` instance with sensible defaults (level 4, colored console). */
1634
1813
  declare function createLogger(opts?: Partial<TProstoLoggerOptions>): ProstoLogger;
1635
1814
  /** Default colored console transport used by Moost loggers. */
1636
1815
  declare const loggerConsoleTransport: _prostojs_logger.TProstoLoggerTransportFn<any>;
1637
1816
 
1638
- export { After, ApplyDecorators, Before, Circular, Const, ConstFactory, Controller, Description, HandlerPaths, Id, ImportController, Inherit, Inject, InjectEventLogger, InjectFromScope, InjectMoost, InjectMoostLogger, InjectScopeVars, Injectable, Intercept, Interceptor, InterceptorHandler, Label, LoggerTopic, MOOST_ADAPTER_BRAND, MOOST_MODULE_IDENTITY_KEY, Moost, MoostDispose, MoostInit, OnError, Optional, Overtake, Param, Params, Pipe, Provide, Replace, Required, Resolve, Response, TInterceptorPriority, TPipePriority, Value, createLogger, defineAfterInterceptor, defineBeforeInterceptor, defineErrorInterceptor, defineInfactScope, defineInterceptor, defineMoostEventHandler, definePipeFn, disposeInstances, forkEventContext, getHandlerPaths, getInfactScopeVars, getInfactSingletonInstances, getInstanceOwnMethods, getInstanceOwnProps, getMoostInfact, getMoostMate, getNewMoostInfact, globalKey, isThenable, loggerConsoleTransport, mergeSorted, registerEventScope, resolvePipe, setControllerContext, setInfactLoggingOptions, setInterceptResult, setOvertake, stampModuleIdentity, stampOnce, useControllerContext, useHandlerPaths, useInterceptResult, useOvertake, useScopeId, withControllerContext };
1639
- export type { TAny, TAnyFn, TClassConstructor, TContextInjectorHook, TControllerOverview, TControllerRegistration, TControllersGroup, TDisposeError, TDisposeOptions, TDisposeResult, TEmpty, TForkEventContextOptions, TFunction, TGetHandlerPathsOptions, TInjectableScope, TInterceptorAfterFn, TInterceptorBeforeFn, TInterceptorData, TInterceptorDef, TInterceptorDefFactory, TInterceptorEntry, TInterceptorErrorFn, TIsolatedSlot, TLogger, TMoostAdapter, TMoostAdapterOptions, TMoostEventHandlerHookOptions, TMoostEventHandlerOptions, TMoostHandler, TMoostMetadata, TMoostModuleIdentity, TMoostOptions, TMoostParamsMetadata, TObject, TOvertakeFn, TPipeData, TPipeFn, TPipeMetas, TPrimitives, TRegistryMergeOptions, TWithControllerContextOptions };
1817
+ export { After, ApplyDecorators, Before, Circular, Const, ConstFactory, Controller, Description, HandlerPaths, Id, ImportController, Inherit, Inject, InjectEventLogger, InjectFromScope, InjectMoost, InjectMoostLogger, InjectScopeVars, Injectable, Intercept, Interceptor, InterceptorHandler, Label, Locale, LoggerTopic, MOOST_ADAPTER_BRAND, MOOST_MODULE_IDENTITY_KEY, Moost, MoostDispose, MoostInit, OnError, Optional, Overtake, Param, Params, Pipe, Provide, Replace, Required, Resolve, Response, TInterceptorPriority, TPipePriority, Value, createLogger, defineAfterInterceptor, defineBeforeInterceptor, defineErrorInterceptor, defineInfactScope, defineInterceptor, defineMoostEventHandler, definePipeFn, detachedKind, disposeInstances, forkEventContext, getHandlerPaths, getInfactScopeVars, getInfactSingletonInstances, getInstanceOwnMethods, getInstanceOwnProps, getMoostInfact, getMoostMate, getNewMoostInfact, getTranslator, globalKey, isThenable, loggerConsoleTransport, mergeSorted, registerEventScope, resolvePipe, setControllerContext, setInfactLoggingOptions, setInterceptOriginal, setInterceptResult, setOvertake, setTranslator, stampModuleIdentity, stampOnce, useControllerContext, useHandlerPaths, useInterceptOriginal, useInterceptResult, useOvertake, useScopeId, useTranslate, withControllerContext };
1818
+ export type { TAny, TAnyFn, TClassConstructor, TContextInjectorHook, TControllerOverview, TControllerRegistration, TControllersGroup, TDisposeError, TDisposeOptions, TDisposeResult, TEmpty, TForkEventContextOptions, TFunction, TGetHandlerPathsOptions, TInjectableScope, TInterceptorAfterFn, TInterceptorBeforeFn, TInterceptorData, TInterceptorDef, TInterceptorDefFactory, TInterceptorEntry, TInterceptorErrorFn, TIsolatedSlot, TLogger, TMoostAdapter, TMoostAdapterOptions, TMoostEventHandlerHookOptions, TMoostEventHandlerOptions, TMoostHandler, TMoostMetadata, TMoostModuleIdentity, TMoostOptions, TMoostParamsMetadata, TObject, TOvertakeFn, TPipeData, TPipeFn, TPipeMetas, TPrimitives, TRegistryMergeOptions, TRunDetachedOptions, TTranslate, TTranslateContext, TWithControllerContextOptions };