moost 0.7.3 → 0.7.4

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.3",
51
+ version: "0.7.4",
52
52
  path: typeof __filename === "string" ? __filename : require("url").pathToFileURL(__filename).href
53
53
  });
54
54
  }
@@ -3187,6 +3187,7 @@ function _define_property(obj, key, value) {
3187
3187
  var _globalThis, _STATE, _state, _state1, _state2, _state3, _state4;
3188
3188
  const STATE = Symbol.for("moost.i18n");
3189
3189
  const NO_LOCALES = Object.freeze([]);
3190
+ /** Distinct scopes given memo keys per generation: keeps every memo keyed by `key` bounded. */ const MAX_SCOPES = 32;
3190
3191
  const state = (_globalThis = globalThis)[_STATE = STATE] ?? (_globalThis[_STATE] = {});
3191
3192
  (_state = state).locales ?? (_state.locales = NO_LOCALES);
3192
3193
  (_state1 = state).generation ?? (_state1.generation = 0);
@@ -3216,9 +3217,14 @@ const state = (_globalThis = globalThis)[_STATE = STATE] ?? (_globalThis[_STATE]
3216
3217
  */ function setTranslator(translate, opts) {
3217
3218
  const isMap = typeof translate === "object";
3218
3219
  state.translate = isMap ? mapTranslator(translate) : translate;
3220
+ state.scoping = opts?.scope && {
3221
+ translate: state.translate,
3222
+ get: opts.scope
3223
+ };
3219
3224
  const locales = opts?.locales ?? (isMap ? Object.keys(translate) : void 0);
3220
3225
  state.locales = translate && locales?.length ? Object.freeze([...locales]) : NO_LOCALES;
3221
3226
  state.failureLogged = false;
3227
+ state.scopeFailureLogged = false;
3222
3228
  newGeneration();
3223
3229
  }
3224
3230
  /** The registered translator (a map wrapped as a function), or `undefined`. */ function getTranslator() {
@@ -3244,7 +3250,7 @@ const state = (_globalThis = globalThis)[_STATE = STATE] ?? (_globalThis[_STATE]
3244
3250
  }
3245
3251
  if (changed) newGeneration();
3246
3252
  }
3247
- /** The only invalidation: drops everything derived from the translator and the templates. */ function newGeneration() {
3253
+ /** Drops everything derived from the translator and the templates. */ function newGeneration() {
3248
3254
  state.generation++;
3249
3255
  state.memo.clear();
3250
3256
  }
@@ -3279,10 +3285,7 @@ const PLACEHOLDER = /\{(\w+)\}/g;
3279
3285
  let translateCtx;
3280
3286
  return (id, params, fallback) => {
3281
3287
  const translate = state.translate;
3282
- return (translate ? callTranslator(translate, id, params, translateCtx ?? (translateCtx = {
3283
- locale: (0, _wooksjs_event_core.useLocale)(event).get(),
3284
- event
3285
- })) : void 0) ?? untranslated(id, params, fallback);
3288
+ return (translate ? callTranslator(translate, id, params, translateCtx ?? (translateCtx = translateContext(event, (0, _wooksjs_event_core.useLocale)(event).get(), readScope(event)))) : void 0) ?? untranslated(id, params, fallback);
3286
3289
  };
3287
3290
  }
3288
3291
  /** `t`'s text without a translation: `fallback` as is, else the filled template (or id). */ function untranslated(id, params, fallback) {
@@ -3298,12 +3301,11 @@ const PLACEHOLDER = /\{(\w+)\}/g;
3298
3301
  const event = ctx ?? (0, _wooksjs_event_core.current)();
3299
3302
  const locale = (0, _wooksjs_event_core.useLocale)(event).get();
3300
3303
  if (locale === void 0) return;
3301
- const translateCtx = {
3302
- locale,
3303
- event
3304
- };
3304
+ const scope = readScope(event);
3305
+ const translateCtx = translateContext(event, locale, scope);
3305
3306
  const generation = state.generation;
3306
- const key = state.locales.includes(locale) ? `${generation}:${locale}` : void 0;
3307
+ const partition = typeof scope === "string" ? `${locale}:${scope}` : locale;
3308
+ const key = state.locales.includes(locale) && (scope === void 0 || scope !== null && admitted(scope)) ? `${generation}:${partition}` : void 0;
3307
3309
  const t = (id, params, fallback) => callTranslator(translate, id, params, translateCtx) ?? untranslated(id, params, fallback);
3308
3310
  const messages = () => {
3309
3311
  if (key === void 0 || state.generation !== generation) return translateTemplates(translate, translateCtx);
@@ -3316,11 +3318,52 @@ const PLACEHOLDER = /\{(\w+)\}/g;
3316
3318
  };
3317
3319
  return {
3318
3320
  locale,
3321
+ partition,
3319
3322
  key,
3320
3323
  t,
3321
3324
  messages
3322
3325
  };
3323
3326
  }
3327
+ function translateContext(event, locale, scope) {
3328
+ return typeof scope === "string" ? {
3329
+ locale,
3330
+ scope,
3331
+ event
3332
+ } : {
3333
+ locale,
3334
+ event
3335
+ };
3336
+ }
3337
+ /**
3338
+ * The event's scope (`opts.scope`): `undefined` without one, `null` when the scope function
3339
+ * threw (logged once) — translated unscoped then, and not memoized.
3340
+ */ function readScope(event) {
3341
+ const scoping = state.scoping;
3342
+ if (!scoping || scoping.translate !== state.translate) return;
3343
+ try {
3344
+ const { get } = scoping;
3345
+ const scope = (0, _wooksjs_event_core.tryGetCurrent)() === event ? get() : (0, _wooksjs_event_core.run)(event, get);
3346
+ return typeof scope === "string" ? scope : typeof scope === "number" && Number.isFinite(scope) ? String(scope) : void 0;
3347
+ } catch (error) {
3348
+ logOnce("scopeFailureLogged", event, "The translation scope threw — translating unscoped, without memoizing (logged once)", error);
3349
+ return null;
3350
+ }
3351
+ }
3352
+ /** Whether `scope` gets memo keys: one of the first {@link MAX_SCOPES} of the generation. */ function admitted(scope) {
3353
+ if (state.admittedScopes?.generation !== state.generation) state.admittedScopes = {
3354
+ generation: state.generation,
3355
+ names: /* @__PURE__ */ new Set()
3356
+ };
3357
+ const { names } = state.admittedScopes;
3358
+ if (names.size < MAX_SCOPES) names.add(scope);
3359
+ return names.has(scope);
3360
+ }
3361
+ /** Logs a failure of the translator or the scope function once per `setTranslator`. */ function logOnce(flag, event, message, error) {
3362
+ if (!state[flag]) {
3363
+ state[flag] = true;
3364
+ (0, _wooksjs_event_core.useLogger)("i18n", event).error(message, error);
3365
+ }
3366
+ }
3324
3367
  /**
3325
3368
  * Returns a translated copy of an error body (`{ message, code?, params?, errors? }`) in the
3326
3369
  * event's locale — the body itself when there is no translation ({@link useTranslation}).
@@ -3332,9 +3375,9 @@ const PLACEHOLDER = /\{(\w+)\}/g;
3332
3375
  *
3333
3376
  * `MoostHttp` applies it to every error body when it creates the HTTP app.
3334
3377
  */ function translateErrorBody(body, ctx) {
3335
- const translation = useTranslation(ctx);
3336
- if (!translation) return body;
3337
- const { t } = translation;
3378
+ 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);
3338
3381
  const source = Array.isArray(body.errors) ? body.errors : void 0;
3339
3382
  const errors = source && translateEntries(source, t);
3340
3383
  let message;
@@ -3361,10 +3404,7 @@ function mapTranslator(map) {
3361
3404
  const out = translate(id, params, ctx);
3362
3405
  return typeof out === "string" ? out : void 0;
3363
3406
  } catch (error) {
3364
- if (!state.failureLogged) {
3365
- state.failureLogged = true;
3366
- (0, _wooksjs_event_core.useLogger)("i18n", ctx.event).error(`The translator threw for "${id}" — using the untranslated text (logged once)`, error);
3367
- }
3407
+ logOnce("failureLogged", ctx.event, `The translator threw for "${id}" — using the untranslated text (logged once)`, error);
3368
3408
  return;
3369
3409
  }
3370
3410
  }
package/dist/index.d.ts CHANGED
@@ -1771,10 +1771,15 @@ declare function defineInterceptor(def: TInterceptorDef, priority?: TInterceptor
1771
1771
  */
1772
1772
  declare function definePipeFn<T extends TObject = TEmpty>(fn: TPipeFn<T>, priority?: TPipePriority): TPipeFn<T>;
1773
1773
 
1774
- /** What a translator gets besides the text: the event's locale and the event itself. */
1774
+ /** What a translator gets besides the text: the event's locale, its scope and the event itself. */
1775
1775
  interface TTranslateContext {
1776
1776
  /** `useLocale().get()` of the event the text is translated for. */
1777
1777
  locale: string | undefined;
1778
+ /**
1779
+ * The event's translation scope ({@link TTranslatorOptions.scope}) — e.g. a tenant whose
1780
+ * wording differs. `undefined`: the shared wording.
1781
+ */
1782
+ scope?: string;
1778
1783
  /** The event context — read anything else your policy needs from it. */
1779
1784
  event: EventContext;
1780
1785
  }
@@ -1792,6 +1797,23 @@ type TTranslate = (text: string, params: Record<string, unknown> | undefined, ct
1792
1797
  * placeholders, filled with {@link formatMessage}.
1793
1798
  */
1794
1799
  type TTranslations = Record<string, Record<string, string>>;
1800
+ /** The options of {@link setTranslator}. */
1801
+ interface TTranslatorOptions {
1802
+ /** The locales the app serves, the default first. For a map: its keys by default. */
1803
+ locales?: readonly string[];
1804
+ /**
1805
+ * The event's translation scope, for wording that differs by more than the locale (per
1806
+ * tenant, brand, user group, …). Called in the event's context each time a translation is
1807
+ * taken — keep it cheap. Its result is passed to the translator ({@link TTranslateContext.scope})
1808
+ * and partitions everything memoized per translation ({@link TTranslation.key}); a finite
1809
+ * number counts as its string, and `undefined` (or any other value) is the shared, unscoped
1810
+ * wording. The translator must return the same text for the same (id, params, locale, scope).
1811
+ *
1812
+ * Derive it from a validated value (the authenticated principal's tenant), never straight from
1813
+ * request input: only the first 32 scopes seen per generation get a {@link TTranslation.key}.
1814
+ */
1815
+ scope?: () => string | number | undefined;
1816
+ }
1795
1817
  /** `t(id, params?, fallback?)` — see {@link useTranslate}. */
1796
1818
  type TTranslateFn = (id: string, params?: Record<string, unknown>, fallback?: string) => string;
1797
1819
  /** The translation of one event's locale — see {@link useTranslation}. */
@@ -1799,9 +1821,17 @@ interface TTranslation {
1799
1821
  /** The event's locale. */
1800
1822
  locale: string;
1801
1823
  /**
1802
- * A memo key for anything derived from this translation: changes with every
1803
- * {@link setTranslator} call and every {@link registerMessages} call that adds or changes a
1804
- * template. `undefined` for a locale outside the translator's `locales` — don't memoize then.
1824
+ * What to index memoized results by: the locale, plus the event's scope when it has one
1825
+ * ({@link TTranslatorOptions.scope}). Keep one entry per partition, replaced when
1826
+ * {@link TTranslation.key} changes.
1827
+ */
1828
+ partition: string;
1829
+ /**
1830
+ * A memo key for anything derived from this translation: the locale, the scope (if any) and
1831
+ * the generation — changes with every {@link setTranslator} call and every
1832
+ * {@link registerMessages} call that adds or changes a template. `undefined` for a locale
1833
+ * outside the translator's `locales`, or a scope past the cap ({@link TTranslatorOptions.scope})
1834
+ * — don't memoize then.
1805
1835
  */
1806
1836
  key: string | undefined;
1807
1837
  /** {@link useTranslate}'s `t`, bound to this translation. */
@@ -1834,9 +1864,7 @@ interface TTranslation {
1834
1864
  * )
1835
1865
  * ```
1836
1866
  */
1837
- declare function setTranslator(translate: TTranslate | TTranslations | undefined, opts?: {
1838
- locales?: readonly string[];
1839
- }): void;
1867
+ declare function setTranslator(translate: TTranslate | TTranslations | undefined, opts?: TTranslatorOptions): void;
1840
1868
  /** The registered translator (a map wrapped as a function), or `undefined`. */
1841
1869
  declare function getTranslator(): TTranslate | undefined;
1842
1870
  /** The locales of the registered translator, the default first (empty without a translator). */
@@ -1901,4 +1929,4 @@ declare function createLogger(opts?: Partial<TProstoLoggerOptions>): ProstoLogge
1901
1929
  declare const loggerConsoleTransport: _prostojs_logger.TProstoLoggerTransportFn<any>;
1902
1930
 
1903
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 };
1904
- 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, TWithControllerContextOptions };
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 };
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.3",
50
+ version: "0.7.4",
51
51
  path: typeof __filename === "string" ? __filename : import.meta.url
52
52
  });
53
53
  }
@@ -3186,6 +3186,7 @@ function _define_property(obj, key, value) {
3186
3186
  var _globalThis, _STATE, _state, _state1, _state2, _state3, _state4;
3187
3187
  const STATE = Symbol.for("moost.i18n");
3188
3188
  const NO_LOCALES = Object.freeze([]);
3189
+ /** Distinct scopes given memo keys per generation: keeps every memo keyed by `key` bounded. */ const MAX_SCOPES = 32;
3189
3190
  const state = (_globalThis = globalThis)[_STATE = STATE] ?? (_globalThis[_STATE] = {});
3190
3191
  (_state = state).locales ?? (_state.locales = NO_LOCALES);
3191
3192
  (_state1 = state).generation ?? (_state1.generation = 0);
@@ -3215,9 +3216,14 @@ const state = (_globalThis = globalThis)[_STATE = STATE] ?? (_globalThis[_STATE]
3215
3216
  */ function setTranslator(translate, opts) {
3216
3217
  const isMap = typeof translate === "object";
3217
3218
  state.translate = isMap ? mapTranslator(translate) : translate;
3219
+ state.scoping = opts?.scope && {
3220
+ translate: state.translate,
3221
+ get: opts.scope
3222
+ };
3218
3223
  const locales = opts?.locales ?? (isMap ? Object.keys(translate) : void 0);
3219
3224
  state.locales = translate && locales?.length ? Object.freeze([...locales]) : NO_LOCALES;
3220
3225
  state.failureLogged = false;
3226
+ state.scopeFailureLogged = false;
3221
3227
  newGeneration();
3222
3228
  }
3223
3229
  /** The registered translator (a map wrapped as a function), or `undefined`. */ function getTranslator() {
@@ -3243,7 +3249,7 @@ const state = (_globalThis = globalThis)[_STATE = STATE] ?? (_globalThis[_STATE]
3243
3249
  }
3244
3250
  if (changed) newGeneration();
3245
3251
  }
3246
- /** The only invalidation: drops everything derived from the translator and the templates. */ function newGeneration() {
3252
+ /** Drops everything derived from the translator and the templates. */ function newGeneration() {
3247
3253
  state.generation++;
3248
3254
  state.memo.clear();
3249
3255
  }
@@ -3278,10 +3284,7 @@ const PLACEHOLDER = /\{(\w+)\}/g;
3278
3284
  let translateCtx;
3279
3285
  return (id, params, fallback) => {
3280
3286
  const translate = state.translate;
3281
- return (translate ? callTranslator(translate, id, params, translateCtx ?? (translateCtx = {
3282
- locale: useLocale$1(event).get(),
3283
- event
3284
- })) : void 0) ?? untranslated(id, params, fallback);
3287
+ return (translate ? callTranslator(translate, id, params, translateCtx ?? (translateCtx = translateContext(event, useLocale$1(event).get(), readScope(event)))) : void 0) ?? untranslated(id, params, fallback);
3285
3288
  };
3286
3289
  }
3287
3290
  /** `t`'s text without a translation: `fallback` as is, else the filled template (or id). */ function untranslated(id, params, fallback) {
@@ -3297,12 +3300,11 @@ const PLACEHOLDER = /\{(\w+)\}/g;
3297
3300
  const event = ctx ?? current$1();
3298
3301
  const locale = useLocale$1(event).get();
3299
3302
  if (locale === void 0) return;
3300
- const translateCtx = {
3301
- locale,
3302
- event
3303
- };
3303
+ const scope = readScope(event);
3304
+ const translateCtx = translateContext(event, locale, scope);
3304
3305
  const generation = state.generation;
3305
- const key = state.locales.includes(locale) ? `${generation}:${locale}` : void 0;
3306
+ const partition = typeof scope === "string" ? `${locale}:${scope}` : locale;
3307
+ const key = state.locales.includes(locale) && (scope === void 0 || scope !== null && admitted(scope)) ? `${generation}:${partition}` : void 0;
3306
3308
  const t = (id, params, fallback) => callTranslator(translate, id, params, translateCtx) ?? untranslated(id, params, fallback);
3307
3309
  const messages = () => {
3308
3310
  if (key === void 0 || state.generation !== generation) return translateTemplates(translate, translateCtx);
@@ -3315,11 +3317,52 @@ const PLACEHOLDER = /\{(\w+)\}/g;
3315
3317
  };
3316
3318
  return {
3317
3319
  locale,
3320
+ partition,
3318
3321
  key,
3319
3322
  t,
3320
3323
  messages
3321
3324
  };
3322
3325
  }
3326
+ function translateContext(event, locale, scope) {
3327
+ return typeof scope === "string" ? {
3328
+ locale,
3329
+ scope,
3330
+ event
3331
+ } : {
3332
+ locale,
3333
+ event
3334
+ };
3335
+ }
3336
+ /**
3337
+ * The event's scope (`opts.scope`): `undefined` without one, `null` when the scope function
3338
+ * threw (logged once) — translated unscoped then, and not memoized.
3339
+ */ function readScope(event) {
3340
+ const scoping = state.scoping;
3341
+ if (!scoping || scoping.translate !== state.translate) return;
3342
+ try {
3343
+ const { get } = scoping;
3344
+ const scope = tryGetCurrent() === event ? get() : run$1(event, get);
3345
+ return typeof scope === "string" ? scope : typeof scope === "number" && Number.isFinite(scope) ? String(scope) : void 0;
3346
+ } catch (error) {
3347
+ logOnce("scopeFailureLogged", event, "The translation scope threw — translating unscoped, without memoizing (logged once)", error);
3348
+ return null;
3349
+ }
3350
+ }
3351
+ /** Whether `scope` gets memo keys: one of the first {@link MAX_SCOPES} of the generation. */ function admitted(scope) {
3352
+ if (state.admittedScopes?.generation !== state.generation) state.admittedScopes = {
3353
+ generation: state.generation,
3354
+ names: /* @__PURE__ */ new Set()
3355
+ };
3356
+ const { names } = state.admittedScopes;
3357
+ if (names.size < MAX_SCOPES) names.add(scope);
3358
+ return names.has(scope);
3359
+ }
3360
+ /** Logs a failure of the translator or the scope function once per `setTranslator`. */ function logOnce(flag, event, message, error) {
3361
+ if (!state[flag]) {
3362
+ state[flag] = true;
3363
+ useLogger$1("i18n", event).error(message, error);
3364
+ }
3365
+ }
3323
3366
  /**
3324
3367
  * Returns a translated copy of an error body (`{ message, code?, params?, errors? }`) in the
3325
3368
  * event's locale — the body itself when there is no translation ({@link useTranslation}).
@@ -3331,9 +3374,9 @@ const PLACEHOLDER = /\{(\w+)\}/g;
3331
3374
  *
3332
3375
  * `MoostHttp` applies it to every error body when it creates the HTTP app.
3333
3376
  */ function translateErrorBody(body, ctx) {
3334
- const translation = useTranslation(ctx);
3335
- if (!translation) return body;
3336
- const { t } = translation;
3377
+ const event = ctx ?? current$1();
3378
+ if (!state.translate || useLocale$1(event).get() === void 0) return body;
3379
+ const t = useTranslate(event);
3337
3380
  const source = Array.isArray(body.errors) ? body.errors : void 0;
3338
3381
  const errors = source && translateEntries(source, t);
3339
3382
  let message;
@@ -3360,10 +3403,7 @@ function mapTranslator(map) {
3360
3403
  const out = translate(id, params, ctx);
3361
3404
  return typeof out === "string" ? out : void 0;
3362
3405
  } catch (error) {
3363
- if (!state.failureLogged) {
3364
- state.failureLogged = true;
3365
- useLogger$1("i18n", ctx.event).error(`The translator threw for "${id}" — using the untranslated text (logged once)`, error);
3366
- }
3406
+ logOnce("failureLogged", ctx.event, `The translator threw for "${id}" — using the untranslated text (logged once)`, error);
3367
3407
  return;
3368
3408
  }
3369
3409
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "moost",
3
- "version": "0.7.3",
3
+ "version": "0.7.4",
4
4
  "description": "moost",
5
5
  "keywords": [
6
6
  "composables",
@@ -49,7 +49,7 @@
49
49
  "@wooksjs/event-http": "^0.8.1",
50
50
  "@wooksjs/http-body": "^0.8.1",
51
51
  "vitest": "4.1.12",
52
- "@moostjs/event-http": "^0.7.3"
52
+ "@moostjs/event-http": "^0.7.4"
53
53
  },
54
54
  "scripts": {
55
55
  "pub": "pnpm publish --access public",