moost 0.7.4 → 0.7.5

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.cjs CHANGED
@@ -48,7 +48,7 @@ let stamped = false;
48
48
  if (stamped) return;
49
49
  stamped = true;
50
50
  stampModuleIdentity({
51
- version: "0.7.4",
51
+ version: "0.7.5",
52
52
  path: typeof __filename === "string" ? __filename : require("url").pathToFileURL(__filename).href
53
53
  });
54
54
  }
@@ -3182,6 +3182,38 @@ function _define_property(obj, key, value) {
3182
3182
  return getHandlerPaths(moost, getController(), method ?? getMethod() ?? "", opts);
3183
3183
  }
3184
3184
 
3185
+ //#endregion
3186
+ //#region packages/moost/src/wooks-owner.ts
3187
+ /** Per Wooks instance: the first claim of each adapter (by `name`) bound into it. */ const owners = /* @__PURE__ */ new WeakMap();
3188
+ /** Wooks instances already warned about: the warning is logged once per instance. */ const warned = /* @__PURE__ */ new WeakSet();
3189
+ /**
3190
+ * Records that `app` binds the handlers of its `adapter` (the adapter's `name`) into `wooks`
3191
+ * (the Wooks instance its routes go to: `wooksApp.getWooks()`). Adapters call it from `onInit`.
3192
+ *
3193
+ * Adapters created without a Wooks app of their own share the process-wide global Wooks, so two
3194
+ * apps in one process (typically in tests) route through one router. When another app already
3195
+ * bound handlers of the same adapter into `wooks`, and either adapter created its Wooks app
3196
+ * (`opts.created`), it warns — once per Wooks instance, with the app's logger. Apps that share
3197
+ * a Wooks app they pass in on purpose stay silent; re-initialising the same app does not warn.
3198
+ * A disposed app still counts: `dispose()` does not remove its routes.
3199
+ */ function claimWooks(app, wooks, adapter, opts) {
3200
+ const created = opts?.created === true;
3201
+ let byAdapter = owners.get(wooks);
3202
+ if (!byAdapter) {
3203
+ byAdapter = /* @__PURE__ */ new Map();
3204
+ owners.set(wooks, byAdapter);
3205
+ }
3206
+ const owner = byAdapter.get(adapter);
3207
+ if (owner === void 0) byAdapter.set(adapter, {
3208
+ app,
3209
+ created
3210
+ });
3211
+ else if (owner.app !== app && (created || owner.created) && !warned.has(wooks)) {
3212
+ warned.add(wooks);
3213
+ app.getLogger().warn(`[moost] A second Moost app binds "${adapter}" handlers into the Wooks instance another app already uses (adapters created without their own Wooks app share the process-wide global one). Both apps' routes now live in one router: an event on a route both register runs the first app's handler chain, and when that throws, its error is swallowed and the next app's chain runs instead. Call clearGlobalWooks() before creating each app (e.g. in a test's beforeEach): the next adapter then gets a fresh Wooks. (Logged once per Wooks instance.)`);
3214
+ }
3215
+ }
3216
+
3185
3217
  //#endregion
3186
3218
  //#region packages/moost/src/i18n.ts
3187
3219
  var _globalThis, _STATE, _state, _state1, _state2, _state3, _state4;
@@ -3221,6 +3253,7 @@ const state = (_globalThis = globalThis)[_STATE = STATE] ?? (_globalThis[_STATE]
3221
3253
  translate: state.translate,
3222
3254
  get: opts.scope
3223
3255
  };
3256
+ state.noPathPrefix = opts?.pathPrefix === false ? state.translate : void 0;
3224
3257
  const locales = opts?.locales ?? (isMap ? Object.keys(translate) : void 0);
3225
3258
  state.locales = translate && locales?.length ? Object.freeze([...locales]) : NO_LOCALES;
3226
3259
  state.failureLogged = false;
@@ -3369,21 +3402,31 @@ function translateContext(event, locale, scope) {
3369
3402
  * event's locale — the body itself when there is no translation ({@link useTranslation}).
3370
3403
  *
3371
3404
  * - each entry of `errors` (and of its entries' `details`): by its `code` (unless `custom`),
3372
- * else by its message text, its English `message` being the fallback;
3373
- * - the top `message`: by `code` when the body has one; else, when it is the first entry's
3374
- * message (`path: message`), the first entry's translation; else by its text.
3405
+ * else by its message text, its English `message` being the fallback; the translator gets the
3406
+ * entry's `path`, `code` and `custom` ({@link TTranslateContext});
3407
+ * - the top `message`: by `code` when the body has one (the translator gets that `code`); else,
3408
+ * when it is the first entry's message (`path: message`), the first entry's translation
3409
+ * (`path: ` dropped with `setTranslator(t, { pathPrefix: false })`); else by its text.
3375
3410
  *
3376
3411
  * `MoostHttp` applies it to every error body when it creates the HTTP app.
3377
3412
  */ function translateErrorBody(body, ctx) {
3378
3413
  const event = ctx ?? (0, _wooksjs_event_core.current)();
3379
- if (!state.translate || (0, _wooksjs_event_core.useLocale)(event).get() === void 0) return body;
3380
- const t = useTranslate(event);
3414
+ const translate = state.translate;
3415
+ const locale = (0, _wooksjs_event_core.useLocale)(event).get();
3416
+ if (!translate || locale === void 0) return body;
3417
+ const base = translateContext(event, locale, readScope(event));
3418
+ const t = (id, params, fallback, entry) => callTranslator(translate, id, params, entry ? entryContext(base, entry) : base) ?? untranslated(id, params, fallback);
3381
3419
  const source = Array.isArray(body.errors) ? body.errors : void 0;
3382
3420
  const errors = source && translateEntries(source, t);
3383
3421
  let message;
3384
- if (typeof body.code === "string" && body.code) message = t(body.code, body.params, body.message);
3385
- else if (source && errors && isEntry(source[0]) && body.message === topMessage(source[0])) message = topMessage(errors[0]);
3386
- else message = t(body.message, body.params, body.message);
3422
+ if (typeof body.code === "string" && body.code) message = callTranslator(translate, body.code, body.params, {
3423
+ ...base,
3424
+ code: body.code
3425
+ }) ?? untranslated(body.code, body.params, body.message);
3426
+ else if (source && errors && isEntry(source[0]) && body.message === topMessage(source[0])) {
3427
+ const first = errors[0];
3428
+ message = state.noPathPrefix === translate ? first.message : topMessage(first);
3429
+ } else message = t(body.message, body.params, body.message);
3387
3430
  return errors ? {
3388
3431
  ...body,
3389
3432
  message,
@@ -3421,13 +3464,22 @@ function mapTranslator(map) {
3421
3464
  function isEntry(value) {
3422
3465
  return typeof value === "object" && value !== null && typeof value.message === "string";
3423
3466
  }
3467
+ /** `base` with the entry's `path` and `code` (each only when a string) and `custom: true`. */ function entryContext(base, { path, code, custom }) {
3468
+ const hasCode = typeof code === "string" && code !== "";
3469
+ if (typeof path !== "string" && !hasCode && custom !== true) return base;
3470
+ const out = { ...base };
3471
+ if (typeof path === "string") out.path = path;
3472
+ if (hasCode) out.code = code;
3473
+ if (custom === true) out.custom = true;
3474
+ return out;
3475
+ }
3424
3476
  function translateEntries(entries, t) {
3425
3477
  return entries.map((entry) => {
3426
3478
  if (!isEntry(entry)) return entry;
3427
3479
  const id = typeof entry.code === "string" && entry.code && entry.custom !== true ? entry.code : entry.message;
3428
3480
  const out = {
3429
3481
  ...entry,
3430
- message: t(id, entry.params, entry.message)
3482
+ message: t(id, entry.params, entry.message, entry)
3431
3483
  };
3432
3484
  if (Array.isArray(entry.details)) out.details = translateEntries(entry.details, t);
3433
3485
  return out;
@@ -3500,6 +3552,7 @@ Object.defineProperty(exports, 'cached', {
3500
3552
  return _wooksjs_event_core.cached;
3501
3553
  }
3502
3554
  });
3555
+ exports.claimWooks = claimWooks;
3503
3556
  Object.defineProperty(exports, 'clearGlobalWooks', {
3504
3557
  enumerable: true,
3505
3558
  get: function () {
package/dist/index.d.ts CHANGED
@@ -1649,6 +1649,27 @@ declare function mergeSorted<T extends {
1649
1649
  priority: number;
1650
1650
  }>(...lists: (T[] | readonly T[] | undefined)[]): T[];
1651
1651
 
1652
+ /** The options of {@link claimWooks}. */
1653
+ interface TClaimWooksOptions {
1654
+ /**
1655
+ * The adapter created its Wooks app itself (none was passed in), so it routes through the
1656
+ * process-wide global Wooks. Default `false`: an app passed in — sharing it is deliberate.
1657
+ */
1658
+ created?: boolean;
1659
+ }
1660
+ /**
1661
+ * Records that `app` binds the handlers of its `adapter` (the adapter's `name`) into `wooks`
1662
+ * (the Wooks instance its routes go to: `wooksApp.getWooks()`). Adapters call it from `onInit`.
1663
+ *
1664
+ * Adapters created without a Wooks app of their own share the process-wide global Wooks, so two
1665
+ * apps in one process (typically in tests) route through one router. When another app already
1666
+ * bound handlers of the same adapter into `wooks`, and either adapter created its Wooks app
1667
+ * (`opts.created`), it warns — once per Wooks instance, with the app's logger. Apps that share
1668
+ * a Wooks app they pass in on purpose stay silent; re-initialising the same app does not warn.
1669
+ * A disposed app still counts: `dispose()` does not remove its routes.
1670
+ */
1671
+ declare function claimWooks(app: Moost, wooks: object, adapter: string, opts?: TClaimWooksOptions): void;
1672
+
1652
1673
  /**
1653
1674
  * Sets the controller context for the current event scope.
1654
1675
  * Called internally by adapters when dispatching events to handlers.
@@ -1771,7 +1792,11 @@ declare function defineInterceptor(def: TInterceptorDef, priority?: TInterceptor
1771
1792
  */
1772
1793
  declare function definePipeFn<T extends TObject = TEmpty>(fn: TPipeFn<T>, priority?: TPipePriority): TPipeFn<T>;
1773
1794
 
1774
- /** What a translator gets besides the text: the event's locale, its scope and the event itself. */
1795
+ /**
1796
+ * What a translator gets besides the text: the event's locale, its scope and the event itself —
1797
+ * plus, when an error body is translated ({@link translateErrorBody}), the entry's `path` and
1798
+ * `code`.
1799
+ */
1775
1800
  interface TTranslateContext {
1776
1801
  /** `useLocale().get()` of the event the text is translated for. */
1777
1802
  locale: string | undefined;
@@ -1782,6 +1807,25 @@ interface TTranslateContext {
1782
1807
  scope?: string;
1783
1808
  /** The event context — read anything else your policy needs from it. */
1784
1809
  event: EventContext;
1810
+ /**
1811
+ * The field path (`'items.0.name'`, `''` for the root) of the error entry being
1812
+ * translated — word a message per field. Only set by {@link translateErrorBody}, for its
1813
+ * `errors` entries (and their `details`); error bodies are never memoized, while the memoized
1814
+ * {@link TTranslation.messages} never carry a path.
1815
+ */
1816
+ path?: string;
1817
+ /**
1818
+ * The code of the error entry (or error body) being translated — also when `text` is the
1819
+ * entry's own message (an entry marked `custom`, or one translated by its text). Only set by
1820
+ * {@link translateErrorBody}.
1821
+ */
1822
+ code?: string;
1823
+ /**
1824
+ * `true` when the error entry carries the app's own message (`custom: true`, e.g. from
1825
+ * `@expect.message`): `text` is that message — keep it unless you translate it as text. Only
1826
+ * set by {@link translateErrorBody}.
1827
+ */
1828
+ custom?: boolean;
1785
1829
  }
1786
1830
  /**
1787
1831
  * The app's translator. `text` is a message id: an error/message code (`WF_EXPIRED`,
@@ -1813,6 +1857,13 @@ interface TTranslatorOptions {
1813
1857
  * request input: only the first 32 scopes seen per generation get a {@link TTranslation.key}.
1814
1858
  */
1815
1859
  scope?: () => string | number | undefined;
1860
+ /**
1861
+ * `false`: a translated error body's top `message` that repeats its first entry is just that
1862
+ * entry's text, without the `path: ` prefix ({@link translateErrorBody}) — also when the entry
1863
+ * has no translation (its English text). Bodies of events without a locale are not touched.
1864
+ * Default `true`: `path: <translated>`, like the untranslated message.
1865
+ */
1866
+ pathPrefix?: boolean;
1816
1867
  }
1817
1868
  /** `t(id, params?, fallback?)` — see {@link useTranslate}. */
1818
1869
  type TTranslateFn = (id: string, params?: Record<string, unknown>, fallback?: string) => string;
@@ -1910,9 +1961,11 @@ declare function useTranslation(ctx?: EventContext): TTranslation | undefined;
1910
1961
  * event's locale — the body itself when there is no translation ({@link useTranslation}).
1911
1962
  *
1912
1963
  * - each entry of `errors` (and of its entries' `details`): by its `code` (unless `custom`),
1913
- * else by its message text, its English `message` being the fallback;
1914
- * - the top `message`: by `code` when the body has one; else, when it is the first entry's
1915
- * message (`path: message`), the first entry's translation; else by its text.
1964
+ * else by its message text, its English `message` being the fallback; the translator gets the
1965
+ * entry's `path`, `code` and `custom` ({@link TTranslateContext});
1966
+ * - the top `message`: by `code` when the body has one (the translator gets that `code`); else,
1967
+ * when it is the first entry's message (`path: message`), the first entry's translation
1968
+ * (`path: ` dropped with `setTranslator(t, { pathPrefix: false })`); else by its text.
1916
1969
  *
1917
1970
  * `MoostHttp` applies it to every error body when it creates the HTTP app.
1918
1971
  */
@@ -1928,5 +1981,5 @@ declare function createLogger(opts?: Partial<TProstoLoggerOptions>): ProstoLogge
1928
1981
  /** Default colored console transport used by Moost loggers. */
1929
1982
  declare const loggerConsoleTransport: _prostojs_logger.TProstoLoggerTransportFn<any>;
1930
1983
 
1931
- 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, formatMessage, getHandlerPaths, getInfactScopeVars, getInfactSingletonInstances, getInstanceOwnMethods, getInstanceOwnProps, getMoostInfact, getMoostMate, getNewMoostInfact, getTranslator, getTranslatorLocales, globalKey, isThenable, loggerConsoleTransport, mergeSorted, registerEventScope, registerMessages, resolvePipe, setControllerContext, setInfactLoggingOptions, setInterceptOriginal, setInterceptResult, setOvertake, setTranslator, stampModuleIdentity, stampOnce, translateErrorBody, useControllerContext, useHandlerPaths, useInterceptOriginal, useInterceptResult, useOvertake, useScopeId, useTranslate, useTranslation, withControllerContext };
1932
- 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, TTranslation, TTranslations, TTranslatorOptions, TWithControllerContextOptions };
1984
+ 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, claimWooks, createLogger, defineAfterInterceptor, defineBeforeInterceptor, defineErrorInterceptor, defineInfactScope, defineInterceptor, defineMoostEventHandler, definePipeFn, detachedKind, disposeInstances, forkEventContext, formatMessage, getHandlerPaths, getInfactScopeVars, getInfactSingletonInstances, getInstanceOwnMethods, getInstanceOwnProps, getMoostInfact, getMoostMate, getNewMoostInfact, getTranslator, getTranslatorLocales, globalKey, isThenable, loggerConsoleTransport, mergeSorted, registerEventScope, registerMessages, resolvePipe, setControllerContext, setInfactLoggingOptions, setInterceptOriginal, setInterceptResult, setOvertake, setTranslator, stampModuleIdentity, stampOnce, translateErrorBody, useControllerContext, useHandlerPaths, useInterceptOriginal, useInterceptResult, useOvertake, useScopeId, useTranslate, useTranslation, withControllerContext };
1985
+ export type { TAny, TAnyFn, TClaimWooksOptions, 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, TTranslation, TTranslations, TTranslatorOptions, TWithControllerContextOptions };
package/dist/index.mjs CHANGED
@@ -47,7 +47,7 @@ let stamped = false;
47
47
  if (stamped) return;
48
48
  stamped = true;
49
49
  stampModuleIdentity({
50
- version: "0.7.4",
50
+ version: "0.7.5",
51
51
  path: typeof __filename === "string" ? __filename : import.meta.url
52
52
  });
53
53
  }
@@ -3181,6 +3181,38 @@ function _define_property(obj, key, value) {
3181
3181
  return getHandlerPaths(moost, getController(), method ?? getMethod() ?? "", opts);
3182
3182
  }
3183
3183
 
3184
+ //#endregion
3185
+ //#region packages/moost/src/wooks-owner.ts
3186
+ /** Per Wooks instance: the first claim of each adapter (by `name`) bound into it. */ const owners = /* @__PURE__ */ new WeakMap();
3187
+ /** Wooks instances already warned about: the warning is logged once per instance. */ const warned = /* @__PURE__ */ new WeakSet();
3188
+ /**
3189
+ * Records that `app` binds the handlers of its `adapter` (the adapter's `name`) into `wooks`
3190
+ * (the Wooks instance its routes go to: `wooksApp.getWooks()`). Adapters call it from `onInit`.
3191
+ *
3192
+ * Adapters created without a Wooks app of their own share the process-wide global Wooks, so two
3193
+ * apps in one process (typically in tests) route through one router. When another app already
3194
+ * bound handlers of the same adapter into `wooks`, and either adapter created its Wooks app
3195
+ * (`opts.created`), it warns — once per Wooks instance, with the app's logger. Apps that share
3196
+ * a Wooks app they pass in on purpose stay silent; re-initialising the same app does not warn.
3197
+ * A disposed app still counts: `dispose()` does not remove its routes.
3198
+ */ function claimWooks(app, wooks, adapter, opts) {
3199
+ const created = opts?.created === true;
3200
+ let byAdapter = owners.get(wooks);
3201
+ if (!byAdapter) {
3202
+ byAdapter = /* @__PURE__ */ new Map();
3203
+ owners.set(wooks, byAdapter);
3204
+ }
3205
+ const owner = byAdapter.get(adapter);
3206
+ if (owner === void 0) byAdapter.set(adapter, {
3207
+ app,
3208
+ created
3209
+ });
3210
+ else if (owner.app !== app && (created || owner.created) && !warned.has(wooks)) {
3211
+ warned.add(wooks);
3212
+ app.getLogger().warn(`[moost] A second Moost app binds "${adapter}" handlers into the Wooks instance another app already uses (adapters created without their own Wooks app share the process-wide global one). Both apps' routes now live in one router: an event on a route both register runs the first app's handler chain, and when that throws, its error is swallowed and the next app's chain runs instead. Call clearGlobalWooks() before creating each app (e.g. in a test's beforeEach): the next adapter then gets a fresh Wooks. (Logged once per Wooks instance.)`);
3213
+ }
3214
+ }
3215
+
3184
3216
  //#endregion
3185
3217
  //#region packages/moost/src/i18n.ts
3186
3218
  var _globalThis, _STATE, _state, _state1, _state2, _state3, _state4;
@@ -3220,6 +3252,7 @@ const state = (_globalThis = globalThis)[_STATE = STATE] ?? (_globalThis[_STATE]
3220
3252
  translate: state.translate,
3221
3253
  get: opts.scope
3222
3254
  };
3255
+ state.noPathPrefix = opts?.pathPrefix === false ? state.translate : void 0;
3223
3256
  const locales = opts?.locales ?? (isMap ? Object.keys(translate) : void 0);
3224
3257
  state.locales = translate && locales?.length ? Object.freeze([...locales]) : NO_LOCALES;
3225
3258
  state.failureLogged = false;
@@ -3368,21 +3401,31 @@ function translateContext(event, locale, scope) {
3368
3401
  * event's locale — the body itself when there is no translation ({@link useTranslation}).
3369
3402
  *
3370
3403
  * - each entry of `errors` (and of its entries' `details`): by its `code` (unless `custom`),
3371
- * else by its message text, its English `message` being the fallback;
3372
- * - the top `message`: by `code` when the body has one; else, when it is the first entry's
3373
- * message (`path: message`), the first entry's translation; else by its text.
3404
+ * else by its message text, its English `message` being the fallback; the translator gets the
3405
+ * entry's `path`, `code` and `custom` ({@link TTranslateContext});
3406
+ * - the top `message`: by `code` when the body has one (the translator gets that `code`); else,
3407
+ * when it is the first entry's message (`path: message`), the first entry's translation
3408
+ * (`path: ` dropped with `setTranslator(t, { pathPrefix: false })`); else by its text.
3374
3409
  *
3375
3410
  * `MoostHttp` applies it to every error body when it creates the HTTP app.
3376
3411
  */ function translateErrorBody(body, ctx) {
3377
3412
  const event = ctx ?? current$1();
3378
- if (!state.translate || useLocale$1(event).get() === void 0) return body;
3379
- const t = useTranslate(event);
3413
+ const translate = state.translate;
3414
+ const locale = useLocale$1(event).get();
3415
+ if (!translate || locale === void 0) return body;
3416
+ const base = translateContext(event, locale, readScope(event));
3417
+ const t = (id, params, fallback, entry) => callTranslator(translate, id, params, entry ? entryContext(base, entry) : base) ?? untranslated(id, params, fallback);
3380
3418
  const source = Array.isArray(body.errors) ? body.errors : void 0;
3381
3419
  const errors = source && translateEntries(source, t);
3382
3420
  let message;
3383
- if (typeof body.code === "string" && body.code) message = t(body.code, body.params, body.message);
3384
- else if (source && errors && isEntry(source[0]) && body.message === topMessage(source[0])) message = topMessage(errors[0]);
3385
- else message = t(body.message, body.params, body.message);
3421
+ if (typeof body.code === "string" && body.code) message = callTranslator(translate, body.code, body.params, {
3422
+ ...base,
3423
+ code: body.code
3424
+ }) ?? untranslated(body.code, body.params, body.message);
3425
+ else if (source && errors && isEntry(source[0]) && body.message === topMessage(source[0])) {
3426
+ const first = errors[0];
3427
+ message = state.noPathPrefix === translate ? first.message : topMessage(first);
3428
+ } else message = t(body.message, body.params, body.message);
3386
3429
  return errors ? {
3387
3430
  ...body,
3388
3431
  message,
@@ -3420,13 +3463,22 @@ function mapTranslator(map) {
3420
3463
  function isEntry(value) {
3421
3464
  return typeof value === "object" && value !== null && typeof value.message === "string";
3422
3465
  }
3466
+ /** `base` with the entry's `path` and `code` (each only when a string) and `custom: true`. */ function entryContext(base, { path, code, custom }) {
3467
+ const hasCode = typeof code === "string" && code !== "";
3468
+ if (typeof path !== "string" && !hasCode && custom !== true) return base;
3469
+ const out = { ...base };
3470
+ if (typeof path === "string") out.path = path;
3471
+ if (hasCode) out.code = code;
3472
+ if (custom === true) out.custom = true;
3473
+ return out;
3474
+ }
3423
3475
  function translateEntries(entries, t) {
3424
3476
  return entries.map((entry) => {
3425
3477
  if (!isEntry(entry)) return entry;
3426
3478
  const id = typeof entry.code === "string" && entry.code && entry.custom !== true ? entry.code : entry.message;
3427
3479
  const out = {
3428
3480
  ...entry,
3429
- message: t(id, entry.params, entry.message)
3481
+ message: t(id, entry.params, entry.message, entry)
3430
3482
  };
3431
3483
  if (Array.isArray(entry.details)) out.details = translateEntries(entry.details, t);
3432
3484
  return out;
@@ -3437,4 +3489,4 @@ function topMessage(entry) {
3437
3489
  }
3438
3490
 
3439
3491
  //#endregion
3440
- export { After, ApplyDecorators, Before, Circular, Const, ConstFactory, ContextInjector, 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, ProstoLogger, Provide, Replace, Required, Resolve, Response, TInterceptorPriority, TPipePriority, Value, cached, clearGlobalWooks, createEventContext, createLogger, createProvideRegistry, createReplaceRegistry, current, defineAfterInterceptor, defineBeforeInterceptor, defineErrorInterceptor, defineInfactScope, defineInterceptor, defineMoostEventHandler, definePipeFn, detachedKind, disposeInstances, eventTypeKey, forkEventContext, formatMessage, getClassKey, getConstructor, getContextInjector, getGlobalWooks, getHandlerPaths, getInfactScopeVars, getInfactSingletonInstances, getInstanceOwnMethods, getInstanceOwnProps, getMoostInfact, getMoostMate, getNewMoostInfact, getTranslator, getTranslatorLocales, globalKey, isConstructor, isThenable, key, loggerConsoleTransport, mergeSorted, registerEventScope, registerMessages, replaceContextInjector, resetContextInjector, resolvePipe, run, setControllerContext, setInfactLoggingOptions, setInterceptOriginal, setInterceptResult, setOvertake, setTranslator, stampModuleIdentity, stampOnce, translateErrorBody, useControllerContext, useHandlerPaths, useInterceptOriginal, useInterceptResult, useLocale, useLogger, useOvertake, useScopeId, useTranslate, useTranslation, withControllerContext };
3492
+ export { After, ApplyDecorators, Before, Circular, Const, ConstFactory, ContextInjector, 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, ProstoLogger, Provide, Replace, Required, Resolve, Response, TInterceptorPriority, TPipePriority, Value, cached, claimWooks, clearGlobalWooks, createEventContext, createLogger, createProvideRegistry, createReplaceRegistry, current, defineAfterInterceptor, defineBeforeInterceptor, defineErrorInterceptor, defineInfactScope, defineInterceptor, defineMoostEventHandler, definePipeFn, detachedKind, disposeInstances, eventTypeKey, forkEventContext, formatMessage, getClassKey, getConstructor, getContextInjector, getGlobalWooks, getHandlerPaths, getInfactScopeVars, getInfactSingletonInstances, getInstanceOwnMethods, getInstanceOwnProps, getMoostInfact, getMoostMate, getNewMoostInfact, getTranslator, getTranslatorLocales, globalKey, isConstructor, isThenable, key, loggerConsoleTransport, mergeSorted, registerEventScope, registerMessages, replaceContextInjector, resetContextInjector, resolvePipe, run, setControllerContext, setInfactLoggingOptions, setInterceptOriginal, setInterceptResult, setOvertake, setTranslator, stampModuleIdentity, stampOnce, translateErrorBody, useControllerContext, useHandlerPaths, useInterceptOriginal, useInterceptResult, useLocale, useLogger, useOvertake, useScopeId, useTranslate, useTranslation, withControllerContext };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "moost",
3
- "version": "0.7.4",
3
+ "version": "0.7.5",
4
4
  "description": "moost",
5
5
  "keywords": [
6
6
  "composables",
@@ -41,15 +41,15 @@
41
41
  "@prostojs/infact": "^0.6.1",
42
42
  "@prostojs/logger": "^0.4.3",
43
43
  "@prostojs/mate": "^0.5.1",
44
- "@wooksjs/event-core": "^0.8.1",
44
+ "@wooksjs/event-core": "^0.8.2",
45
45
  "hookable": "^5.5.3",
46
- "wooks": "^0.8.1"
46
+ "wooks": "^0.8.2"
47
47
  },
48
48
  "devDependencies": {
49
- "@wooksjs/event-http": "^0.8.1",
50
- "@wooksjs/http-body": "^0.8.1",
49
+ "@wooksjs/event-http": "^0.8.2",
50
+ "@wooksjs/http-body": "^0.8.2",
51
51
  "vitest": "4.1.12",
52
- "@moostjs/event-http": "^0.7.4"
52
+ "@moostjs/event-http": "^0.7.5"
53
53
  },
54
54
  "scripts": {
55
55
  "pub": "pnpm publish --access public",