stream-chat 10.0.0-rc.5 → 10.0.0-rc.6

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.
Files changed (73) hide show
  1. package/dist/cjs/i18n.browser.js +28 -37
  2. package/dist/cjs/i18n.browser.js.map +2 -2
  3. package/dist/cjs/i18n.node.js +28 -37
  4. package/dist/cjs/i18n.node.js.map +2 -2
  5. package/dist/cjs/index.browser.js +200 -520
  6. package/dist/cjs/index.browser.js.map +4 -4
  7. package/dist/cjs/index.node.js +200 -533
  8. package/dist/cjs/index.node.js.map +4 -4
  9. package/dist/esm/i18n.mjs +28 -37
  10. package/dist/esm/i18n.mjs.map +2 -2
  11. package/dist/esm/index.mjs +200 -520
  12. package/dist/esm/index.mjs.map +4 -4
  13. package/dist/types/LiveLocationManager.d.ts +5 -4
  14. package/dist/types/channel.d.ts +7 -80
  15. package/dist/types/client.d.ts +5 -108
  16. package/dist/types/gen/chat/ChannelApi.d.ts +17 -1
  17. package/dist/types/gen/chat/ChatApi.d.ts +19 -1
  18. package/dist/types/gen/models/index.d.ts +14 -4
  19. package/dist/types/i18n/Streami18n.d.ts +4 -14
  20. package/dist/types/index.d.ts +0 -1
  21. package/dist/types/messageComposer/LocationComposer.d.ts +2 -2
  22. package/dist/types/messageComposer/middleware/messageComposer/types.d.ts +2 -2
  23. package/dist/types/messageComposer/middleware/textComposer/mentions.d.ts +7 -7
  24. package/dist/types/messageComposer/pollComposer.d.ts +1 -2
  25. package/dist/types/moderation.d.ts +14 -4
  26. package/dist/types/offline-support/types.d.ts +4 -4
  27. package/dist/types/pagination/cursorDerivation/linearPaginationFlags.d.ts +19 -2
  28. package/dist/types/pagination/paginators/ChannelPaginator.d.ts +8 -8
  29. package/dist/types/pagination/paginators/MessageIntervalPaginator.d.ts +3 -3
  30. package/dist/types/pagination/paginators/ReminderPaginator.d.ts +4 -4
  31. package/dist/types/poll.d.ts +9 -9
  32. package/dist/types/poll_manager.d.ts +2 -2
  33. package/dist/types/search/ChannelMemberSearchSource.d.ts +3 -3
  34. package/dist/types/search/ChannelSearchSource.d.ts +3 -3
  35. package/dist/types/search/MessageSearchSource.d.ts +5 -5
  36. package/dist/types/search/UserSearchSource.d.ts +3 -3
  37. package/dist/types/types.d.ts +62 -423
  38. package/dist/types/utils.d.ts +2 -2
  39. package/package.json +1 -1
  40. package/src/LiveLocationManager.ts +5 -6
  41. package/src/channel.ts +21 -159
  42. package/src/client.ts +7 -214
  43. package/src/gen/chat/ChannelApi.ts +33 -0
  44. package/src/gen/chat/ChatApi.ts +60 -0
  45. package/src/gen/model-decoders/decoders.ts +7 -0
  46. package/src/gen/models/index.ts +16 -4
  47. package/src/i18n/Streami18n.ts +35 -43
  48. package/src/index.ts +0 -1
  49. package/src/messageComposer/LocationComposer.ts +2 -7
  50. package/src/messageComposer/middleware/messageComposer/types.ts +2 -7
  51. package/src/messageComposer/middleware/messageComposer/userDataInjection.ts +11 -6
  52. package/src/messageComposer/middleware/textComposer/mentions.ts +7 -8
  53. package/src/messageComposer/pollComposer.ts +1 -2
  54. package/src/moderation.ts +15 -16
  55. package/src/offline-support/offline_support_api.ts +15 -7
  56. package/src/offline-support/types.ts +5 -6
  57. package/src/pagination/cursorDerivation/linearPaginationFlags.ts +24 -6
  58. package/src/pagination/paginators/ChannelPaginator.ts +10 -10
  59. package/src/pagination/paginators/MessageIntervalPaginator.ts +9 -8
  60. package/src/pagination/paginators/MessagePaginator.ts +1 -1
  61. package/src/pagination/paginators/PinnedMessagePaginator.ts +4 -4
  62. package/src/pagination/paginators/ReminderPaginator.ts +5 -5
  63. package/src/pagination/utility.queryChannel.ts +1 -1
  64. package/src/poll.ts +15 -12
  65. package/src/poll_manager.ts +5 -5
  66. package/src/search/ChannelMemberSearchSource.ts +3 -3
  67. package/src/search/ChannelSearchSource.ts +3 -3
  68. package/src/search/MessageSearchSource.ts +5 -6
  69. package/src/search/UserSearchSource.ts +4 -4
  70. package/src/types.ts +73 -532
  71. package/src/utils.ts +7 -8
  72. package/dist/types/permissions.d.ts +0 -55
  73. package/src/permissions.ts +0 -102
@@ -1017,46 +1017,39 @@ var Streami18n = class {
1017
1017
  return this.state?.getLatestValue().language ?? DEFAULT_LANGUAGE;
1018
1018
  }
1019
1019
  /**
1020
- * Initializes i18next. Idempotent and safe to call concurrently.
1020
+ * Initializes i18next. Memoized, so concurrent callers share one initialization.
1021
1021
  *
1022
- * Memoized, so two independent consumers — a UI SDK's chat root and its overlay host — share one
1023
- * initialization.
1024
- *
1025
- * An i18next failure does **not** reject: neither UI SDK awaits this, so a rejection would surface
1026
- * as an unhandled rejection and the memo would latch it for the process lifetime. It is logged
1027
- * instead, leaving the instance *degraded but safe*: `state.initialized` stays false, which is what
1028
- * keeps the methods below off a dead i18next instance, and `t` remains the default translator, so
1029
- * every call site still renders its inline English. There is no retry — construct a new instance.
1030
- *
1031
- * One path does escape: an integrator `logger` that throws is called from the `catch` itself, so it
1032
- * rejects out of here. Rare enough not to guard, but it is why this is not an absolute guarantee.
1022
+ * **Rejects on failure**, so handle it. The instance stays usable either way: `initialized` stays
1023
+ * false, keeping {@link Streami18n.registerTranslation} and {@link Streami18n.setLanguage} off a
1024
+ * dead i18next instance, and `t` keeps returning each call site's inline English.
1033
1025
  */
1034
1026
  init() {
1035
- this.initPromise ?? (this.initPromise = this.runInit());
1036
- return this.initPromise;
1027
+ if (this.initPromise) return this.initPromise;
1028
+ const pending = this.runInit();
1029
+ this.initPromise = pending;
1030
+ pending.catch(() => {
1031
+ if (this.initPromise === pending) this.initPromise = void 0;
1032
+ });
1033
+ return pending;
1037
1034
  }
1038
1035
  async runInit() {
1039
- try {
1040
- this.validateCurrentLanguage();
1041
- this.assertPluralRulesCoverage(this.currentLanguage);
1042
- const dayjsLocale = this.dayjsLocales[this.currentLanguage];
1043
- if (dayjsLocale) this.addOrUpdateLocale(this.currentLanguage, dayjsLocale);
1044
- const t = await this.i18nInstance.init({
1045
- ...this.i18nextConfig,
1046
- lng: this.currentLanguage,
1047
- resources: this.i18nextResources()
1048
- });
1049
- this.registerFormatters();
1050
- Object.entries(this.translationBuilderTopics).forEach(([topic, Topic]) => {
1051
- this.translationBuilder.registerTopic(topic, Topic);
1052
- });
1053
- this.state.partialNext({
1054
- initialized: true,
1055
- ...this.tOverridden ? {} : { t }
1056
- });
1057
- } catch (error) {
1058
- this.logger(`Streami18n: initialization failed: ${describeError(error)}`);
1059
- }
1036
+ this.validateCurrentLanguage();
1037
+ this.assertPluralRulesCoverage(this.currentLanguage);
1038
+ const dayjsLocale = this.dayjsLocales[this.currentLanguage];
1039
+ if (dayjsLocale) this.addOrUpdateLocale(this.currentLanguage, dayjsLocale);
1040
+ const t = await this.i18nInstance.init({
1041
+ ...this.i18nextConfig,
1042
+ lng: this.currentLanguage,
1043
+ resources: this.i18nextResources()
1044
+ });
1045
+ this.registerFormatters();
1046
+ Object.entries(this.translationBuilderTopics).forEach(([topic, Topic]) => {
1047
+ this.translationBuilder.registerTopic(topic, Topic);
1048
+ });
1049
+ this.state.partialNext({
1050
+ initialized: true,
1051
+ ...this.tOverridden ? {} : { t }
1052
+ });
1060
1053
  return this.state.getLatestValue();
1061
1054
  }
1062
1055
  registerTranslation(language, translation, dayjsLocaleConfig) {
@@ -1134,7 +1127,5 @@ var Streami18n = class {
1134
1127
  addOrUpdateDayjsLocale(language, config, this.DateTimeParser);
1135
1128
  }
1136
1129
  };
1137
- /** Marks instances across bundle copies, where `instanceof` silently fails. */
1138
- Streami18n.brand = /* @__PURE__ */ Symbol.for("stream-chat.Streami18n");
1139
1130
  var describeError = (error) => error instanceof Error ? error.message : String(error);
1140
1131
  //# sourceMappingURL=i18n.browser.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/i18n/index.ts", "../../src/i18n/dayjs.ts", "../../src/i18n/translator.ts", "../../src/i18n/formatters.ts", "../../src/i18n/languageNames.ts", "../../src/i18n/Streami18n.ts", "../../src/store.ts", "../../src/i18n/TranslationBuilder.ts", "../../src/i18n/TranslationStore.ts"],
4
- "sourcesContent": ["/**\n * The shared i18n layer, published as `stream-chat/i18n`.\n *\n * Deliberately **not** re-exported from `stream-chat`'s root barrel: this module pulls in `i18next` and\n * `dayjs`, and keeping them out of the root bundle is the entire reason it is a separate entry point.\n * `scripts/bundle.mts` asserts that boundary at build time.\n */\nexport * from './dayjs';\nexport * from './formatters';\nexport * from './languageNames';\nexport * from './Streami18n';\nexport * from './TranslationBuilder';\nexport * from './TranslationStore';\nexport * from './translator';\nexport * from './types';\n", "import Dayjs from 'dayjs';\nimport calendar from 'dayjs/plugin/calendar.js';\nimport duration from 'dayjs/plugin/duration.js';\nimport localeData from 'dayjs/plugin/localeData.js';\nimport localizedFormat from 'dayjs/plugin/localizedFormat.js';\nimport relativeTime from 'dayjs/plugin/relativeTime.js';\nimport timezone from 'dayjs/plugin/timezone.js';\nimport updateLocale from 'dayjs/plugin/updateLocale.js';\nimport utc from 'dayjs/plugin/utc.js';\n\nimport type {\n DateTimeLike,\n DateTimeParserModule,\n TDateTimeParserInput,\n TDateTimeParserOutput,\n} from './types';\n\n/**\n * The calendar-plugin config shape. Not part of dayjs's own `ILocale`, so it has to be declared here.\n *\n * Supplying it is how relative wording (\"heute um\", \"ieri alle\") gets localized \u2014 no dayjs locale file\n * defines `calendar`, which is the single most common surprise when adding a language.\n */\nexport type CalendarFormats = {\n lastDay: string;\n lastWeek: string;\n nextDay: string;\n nextWeek: string;\n sameDay: string;\n sameElse: string;\n};\n\n/**\n * A dayjs locale config, as accepted by `dayjsLocaleConfigForLanguage` and by\n * `registerTranslation`'s third argument.\n *\n * Typing this as a bare `Partial<ILocale>` makes passing a calendar config a TS2345 \"no properties in\n * common\" error, which is exactly the wording an integrator hits first \u2014 hence the explicit\n * `calendar`.\n */\nexport type DayjsLocaleConfig = Partial<ILocale> & { calendar?: CalendarFormats };\n\n/**\n * The dayjs module surface this file drives: our own `dayjs`, or a module an integrator supplied\n * through `DateTimeParser`. Method shorthand, so a real `typeof dayjs` satisfies it -- as function\n * properties these would be checked contravariantly and rejected.\n *\n * `Ls`, `locale` and `updateLocale` are optional because a module only has them once the plugins are\n * registered, and because a non-dayjs parser (a Moment) has a different shape entirely.\n */\ntype DayjsExtendable = object & {\n extend?(plugin: unknown, option?: unknown): unknown;\n Ls?: Record<string, unknown>;\n locale?(preset: unknown, object?: unknown, isLocal?: boolean): unknown;\n updateLocale?(name: string, config: unknown): unknown;\n};\n\n/** The module locale helpers default to, when a caller does not name one. */\nconst ownDayjs = () => Dayjs as unknown as DayjsExtendable;\n\n/**\n * The English locale skeleton a custom locale is merged over, so a partial config still has month and\n * weekday names to fall back on.\n */\nconst EN_LOCALE_FALLBACK = {\n /**\n * `formats` and `relativeTime` are empty on purpose, and are not removable: `Dayjs.locale()` takes an\n * `ILocale`, which declares both as required, so omitting them is a type error. Empty means \"inherit\n * dayjs's own defaults\", which is the intent.\n */\n formats: {},\n relativeTime: {},\n months: [\n 'January',\n 'February',\n 'March',\n 'April',\n 'May',\n 'June',\n 'July',\n 'August',\n 'September',\n 'October',\n 'November',\n 'December',\n ],\n weekdays: [\n 'Sunday',\n 'Monday',\n 'Tuesday',\n 'Wednesday',\n 'Thursday',\n 'Friday',\n 'Saturday',\n ],\n};\n\n/**\n * The plugins the formatters depend on, in dependency order: `timezone` builds on `utc`.\n */\nconst REQUIRED_PLUGINS = [\n updateLocale,\n utc,\n timezone,\n localizedFormat,\n calendar,\n localeData,\n relativeTime,\n duration,\n];\n\n/**\n * Modules already extended. A `WeakSet` rather than a boolean because the module to extend is not\n * always ours -- see {@link ensureDayjsPlugins}.\n */\nconst extendedModules = new WeakSet<object>();\n\n/**\n * Registers the dayjs plugins the formatters need, once per module.\n *\n * Takes the module to extend, defaulting to our own `dayjs`. Passing it matters: an integrator\n * supplying `DateTimeParser` may hand over a *different physical copy* of dayjs, and extending ours\n * leaves theirs without the plugins. That failure is silent and total -- `.calendar()` is simply\n * absent, and `format('LT')` returns the literal string `\"LT\"` because `localizedFormat` never\n * registered the token.\n *\n * Deliberately **not** done at module scope. Module-scope `Dayjs.extend(...)` is a side effect, which\n * would force `stream-chat` to declare `sideEffects` and would make importing this module do work\n * whether or not anything uses it. Calling it from both the constructor and `defaultDateTimeParser`\n * covers the two ways the formatters can be reached, including a standalone `getDateString()` call\n * with no `Streami18n` instance in play.\n *\n * Idempotent twice over: tracked here, and dayjs itself no-ops a repeated `extend` via the plugin's\n * `$i` marker. The module is recorded *after* the extends run, so a throw does not leave it marked as\n * done.\n *\n * `timezone` is included because it depends on `utc` and callers can set `timezone` at any point;\n * registering it lazily on first use would leave the plugin missing for an instance that only sets\n * `timezone` later.\n */\nexport const ensureDayjsPlugins = (\n module: DayjsExtendable = Dayjs as unknown as DayjsExtendable,\n) => {\n if (extendedModules.has(module)) return;\n if (typeof module.extend !== 'function') return;\n\n for (const plugin of REQUIRED_PLUGINS) module.extend(plugin);\n extendedModules.add(module);\n};\n\n/**\n * The parser used when none is supplied, and by `getDateString()` called outside an instance.\n *\n * Note there is no `import 'dayjs/locale/en'` anywhere: dayjs bundles `en` and has it registered\n * before any import runs (`Object.keys(Dayjs.Ls)` is already `['en']`), so that import \u2014 which both UI\n * SDKs carried \u2014 was a no-op.\n */\nexport const defaultDateTimeParser = (input?: TDateTimeParserInput) => {\n ensureDayjsPlugins();\n return Dayjs(input);\n};\n\n/**\n * The dayjs module itself, with plugins registered.\n *\n * `Streami18n.DateTimeParser` has to be the *module*, not a parse function, because\n * `durationFormatter` calls `.duration()` \u2014 which lives on the module, not on a parsed instance.\n */\nexport const getDefaultDateTimeParserModule = (): DateTimeParserModule => {\n ensureDayjsPlugins();\n return Dayjs as unknown as DateTimeParserModule;\n};\n\n/**\n * Registers or updates a dayjs locale without changing the global locale.\n *\n * Takes the module to register on, defaulting to ours. Passing it matters for the same reason\n * {@link ensureDayjsPlugins} takes one: an integrator supplying `DateTimeParser` may hand over a second\n * physical copy of dayjs, and registering on ours would leave the locale absent from the module that\n * actually formats the dates -- so a `calendar` config or a `dayjsLocaleConfigForLanguage` would be\n * silently ignored.\n */\nexport const addOrUpdateDayjsLocale = (\n language: string,\n config: DayjsLocaleConfig,\n module: DayjsExtendable = ownDayjs(),\n) => {\n ensureDayjsPlugins(module);\n\n if (dayjsLocaleExists(language, module)) {\n module.updateLocale?.(language, { ...config });\n return;\n }\n // Merged over the English skeleton so missing keys still resolve.\n module.locale?.({ name: language, ...EN_LOCALE_FALLBACK, ...config }, undefined, true);\n};\n\nexport const dayjsLocaleExists = (\n language: string,\n module: DayjsExtendable = ownDayjs(),\n) => Object.keys(module.Ls ?? {}).includes(language);\n\n/**\n * Whether a parser is dayjs, as opposed to a Moment the integrator brought.\n *\n * A property check rather than the `.extend !== undefined` both UI SDKs used, which throws on `null`.\n */\nexport const isDayjsLike = (parser: unknown): parser is DateTimeParserModule =>\n typeof parser === 'function' &&\n typeof (parser as DateTimeParserModule).extend === 'function';\n\n/** Whether a parser supports `.tz()`, i.e. dayjs with the timezone plugin, or moment-timezone. */\nexport const supportsTimezone = (parser: unknown): boolean =>\n typeof parser === 'function' && typeof (parser as { tz?: unknown }).tz === 'function';\n\nexport const isDate = (value: TDateTimeParserOutput): value is Date =>\n value instanceof Date;\n\nexport const isNumberOrString = (\n value: TDateTimeParserOutput,\n): value is number | string => typeof value === 'number' || typeof value === 'string';\n\n/** Whether a parser output is a dayjs or Moment object rather than a raw Date/string/number. */\nexport const isDayOrMoment = (value: TDateTimeParserOutput): value is DateTimeLike =>\n typeof value === 'object' &&\n value !== null &&\n !(value instanceof Date) &&\n typeof (value as DateTimeLike).format === 'function';\n", "import type {\n AnyTranslationCatalog,\n DynamicTranslationKey,\n StreamTFunctionFor,\n} from './types';\n\n/**\n * Brands a runtime-resolved string as a translation key.\n *\n * The brand on {@link DynamicTranslationKey} is required, so this is the only way to pass a key the\n * compiler cannot see \u2014 which keeps every such escape deliberate and greppable.\n */\nexport const asDynamicKey = (key: string): DynamicTranslationKey =>\n key as DynamicTranslationKey;\n\n/** Matches `{{ name }}` / `{{name}}`, allowing dots so `{{ user.name }}` interpolates too. */\nconst INTERPOLATION_PATTERN = /\\{\\{\\s*([\\w.]+)\\s*\\}\\}/g;\n\nconst interpolate = (copy: string, values: Record<string, unknown>) =>\n copy.replace(INTERPOLATION_PATTERN, (whole, name: string) =>\n values[name] === undefined ? whole : String(values[name]),\n );\n\n/**\n * The `t` in force before i18next has initialized, and the default for a UI SDK's translation context.\n *\n * It has to honour the inline `defaultValue`: every prose call site passes its English copy as the\n * second argument, so echoing the key back would flash raw dotted paths on the first frame \u2014 and would\n * render them permanently anywhere a context default is in play (a component used outside the SDK's\n * provider).\n *\n * A ~30-line stand-in for i18next, deliberately: pulling i18next in just to render the first frame\n * would defeat keeping it out of the default path.\n */\nexport const createDefaultTranslatorFunction = <\n C extends AnyTranslationCatalog = AnyTranslationCatalog,\n Bundled extends string = never,\n>(): StreamTFunctionFor<C, Bundled> =>\n ((\n key: string,\n defaultValueOrOptions?: string | Record<string, unknown>,\n maybeOptions?: Record<string, unknown>,\n ) => {\n // Prose: the copy arrives positionally.\n if (typeof defaultValueOrOptions === 'string') {\n return maybeOptions\n ? interpolate(defaultValueOrOptions, maybeOptions)\n : defaultValueOrOptions;\n }\n\n const options = defaultValueOrOptions ?? maybeOptions;\n if (!options) return key;\n\n // Plural call sites pass their copy as `defaultValue_one` / `defaultValue_other` inside the\n // options object, so a bare `defaultValue` check would still leak the raw key for them. English\n // only distinguishes one from other; a registered language's own categories are irrelevant here,\n // since this function is only ever in play before i18next has initialized.\n const resolved =\n (options.count === 1 ? options.defaultValue_one : options.defaultValue_other) ??\n options.defaultValue;\n\n return typeof resolved === 'string' ? interpolate(resolved, options) : key;\n }) as StreamTFunctionFor<C, Bundled>;\n\n/**\n * Wraps an integrator's `parseMissingKeyHandler` so it only sees genuinely missing translations.\n *\n * i18next counts every prose key as missing \u2014 they render from the inline `defaultValue`, not from a\n * resource bundle \u2014 and lets the handler's return value replace the rendered string. An unguarded\n * handler therefore blanks out most of the UI. A resolved default arrives as the second argument,\n * which is how the two cases are told apart.\n */\nexport const guardMissingKeyHandler =\n (handler: (key: string, defaultValue?: string) => string) =>\n (key: string, defaultValue?: string) =>\n typeof defaultValue === 'string' ? defaultValue : handler(key, defaultValue);\n", "import { isDate, isDayOrMoment, isNumberOrString } from './dayjs';\nimport type { CalendarFormats } from './dayjs';\nimport { asDynamicKey } from './translator';\nimport type {\n DurationFormatterOptions,\n FormatterContext,\n FormatterFactory,\n LooseTranslateFunction,\n PredefinedFormatters,\n TDateTimeParser,\n TimestampFormatterOptions,\n} from './types';\n\n/**\n * The `relativeTime.*` keys this module renders, with their English copy.\n *\n * Exported as a catalog fragment because the *call sites* are here, in core, while the *catalog* is\n * generated from each UI SDK's own source. Without this an SDK's codegen cannot see these keys, so they\n * would drop out of its `TranslationCatalog` and an integrator could no longer type them in a\n * dictionary \u2014 i.e. could no longer translate relative dates at all. A UI SDK intersects this into its\n * catalog type.\n *\n * Plurals appear as `_one` / `_other` because that is how a dictionary supplies them; English needs no\n * distinction, but a language with different forms does.\n */\nexport const RELATIVE_TIME_CATALOG = {\n 'relativeTime.daysAgo_one': '{{ count }}d ago',\n 'relativeTime.daysAgo_other': '{{ count }}d ago',\n 'relativeTime.today': 'Today',\n 'relativeTime.weeksAgo_one': '{{ count }}w ago',\n 'relativeTime.weeksAgo_other': '{{ count }}w ago',\n 'relativeTime.yesterday': 'Yesterday',\n} as const;\n\nexport type RelativeTimeCatalog = typeof RELATIVE_TIME_CATALOG;\n\n/** Defaults for the relative-compact window, matching what both UI SDKs shipped. */\nconst DEFAULT_RELATIVE_COMPACT_MAX_DAYS = 6;\nconst DEFAULT_RELATIVE_COMPACT_MAX_WEEKS = 3;\n\n/**\n * Whether a string is not a date this module can render.\n *\n * `!Date.parse(value)` would be the obvious spelling and is wrong: `Date.parse` returns `0` for the\n * Unix epoch, which is falsy, so a perfectly valid `'1970-01-01T00:00:00.000Z'` was classified as junk\n * and dropped -- rendered as `''` by the formatter and as `null` by `getDateString`.\n */\nconst isUnparseableDateString = (value: string) => Number.isNaN(Date.parse(value));\n\n/**\n * Coerces the week-rounding option, which arrives as text from an i18next format expression.\n *\n * Anything unrecognised falls back to `floor`, so a typo degrades to the default rather than throwing\n * inside a formatter, where the only visible symptom would be a blank timestamp.\n */\nconst asWeekRounding = (value: unknown): 'ceil' | 'floor' =>\n value === 'ceil' || value === true ? 'ceil' : 'floor';\n\n/**\n * Coerces a numeric formatter option.\n *\n * These arrive as strings, not numbers: they are written inside an i18next format expression\n * (`{{ timestamp | timestampFormatter(relativeCompactMaxDays: 10) }}`), and i18next hands every\n * argument over as text. The declared type says `number` because that is what a programmatic caller\n * passes, so both have to be accepted.\n */\nconst asNumber = (value: unknown, fallback: number) => {\n const parsed =\n typeof value === 'number' ? value : Number.parseInt(String(value ?? ''), 10);\n return Number.isFinite(parsed) ? parsed : fallback;\n};\n\n/**\n * Per-key calendar config may arrive as an object or as a JSON string.\n *\n * The string case is not a quirk to clean up: bundled defaults embed the config inside the i18next\n * expression itself, so by the time it reaches a formatter it is text.\n */\nconst parseCalendarFormats = (\n value: TimestampFormatterOptions['calendarFormats'],\n logger: (message?: string) => void,\n): Record<string, string> | undefined => {\n if (!value) return undefined;\n if (typeof value !== 'string') return value;\n try {\n return JSON.parse(value) as Record<string, string>;\n } catch (error) {\n // Reported through the instance's logger, not through `translate` -- a diagnostic is not copy.\n logger(\n `Streami18n: calendarFormats is not valid JSON, ignoring it: ${value} (${\n error instanceof Error ? error.message : String(error)\n })`,\n );\n return undefined;\n }\n};\n\n/**\n * \"Today\" / \"Yesterday\" / \"3d ago\" / \"2w ago\", falling back to a short date.\n *\n * Every word goes through `t()`. The React Native SDK shipped this as a standalone formatter with the\n * English baked in, which no dictionary could translate \u2014 and because the wording lived in a formatter\n * body rather than a catalog value, the codegen's English-prose guard never saw it either.\n */\nconst relativeCompactDateString = ({\n maxDays,\n maxWeeks,\n tDateTimeParser,\n timestamp,\n translate,\n weekRounding,\n}: {\n maxDays: number;\n maxWeeks: number;\n tDateTimeParser: TDateTimeParser;\n timestamp: string | Date;\n translate: LooseTranslateFunction;\n weekRounding: 'ceil' | 'floor';\n}): string | null => {\n const parsed = tDateTimeParser(timestamp as string);\n if (!isDayOrMoment(parsed)) return null;\n\n const now = tDateTimeParser(new Date());\n if (!isDayOrMoment(now)) return null;\n\n const daysAgo = now.startOf('day').diff(parsed.startOf('day'), 'day');\n\n // A future timestamp is not \"Today\" \u2014 fall straight through to a date.\n if (daysAgo < 0) return parsed.format('DD/MM/YY');\n\n if (daysAgo === 0)\n return translate('relativeTime.today', RELATIVE_TIME_CATALOG['relativeTime.today']);\n if (daysAgo === 1)\n return translate(\n 'relativeTime.yesterday',\n RELATIVE_TIME_CATALOG['relativeTime.yesterday'],\n );\n // Plural defaults rather than one `defaultValue`: English needs no distinction here, but a language\n // whose plural categories differ has to be able to supply `_one` / `_other` and have i18next select.\n if (daysAgo <= maxDays) {\n return translate('relativeTime.daysAgo', {\n count: daysAgo,\n defaultValue_one: RELATIVE_TIME_CATALOG['relativeTime.daysAgo_one'],\n defaultValue_other: RELATIVE_TIME_CATALOG['relativeTime.daysAgo_other'],\n });\n }\n\n // `maxWeeks > 0` and a full week elapsed, both required: with `maxWeeks: 0` a 3-day-old timestamp\n // has `Math.floor(3 / 7) === 0`, which would otherwise match and render \"0w ago\".\n //\n // The two roundings bound the window differently, which is the whole reason both exist: `floor`\n // stops on the week *count*, `ceil` on the day count. See `relativeCompactWeekRounding`.\n const weeksAgo =\n weekRounding === 'ceil' ? Math.ceil(daysAgo / 7) : Math.floor(daysAgo / 7);\n const withinWindow =\n weekRounding === 'ceil' ? daysAgo <= maxWeeks * 7 : weeksAgo <= maxWeeks;\n\n if (maxWeeks > 0 && daysAgo >= 7 && withinWindow) {\n return translate('relativeTime.weeksAgo', {\n count: weeksAgo,\n defaultValue_one: RELATIVE_TIME_CATALOG['relativeTime.weeksAgo_one'],\n defaultValue_other: RELATIVE_TIME_CATALOG['relativeTime.weeksAgo_other'],\n });\n }\n\n return parsed.format('DD/MM/YY');\n};\n\nconst timestampFormatter: FormatterFactory<string | Date> =\n ({ logger, tDateTimeParser, translate }: FormatterContext) =>\n (value, _lng, options) => {\n const {\n calendar,\n calendarFormats,\n format,\n relativeCompact,\n relativeCompactMaxDays,\n relativeCompactMaxWeeks,\n relativeCompactWeekRounding,\n } = options as TimestampFormatterOptions;\n\n // Nothing renderable: empty rather than the stringified value. `null` used to come out as the\n // literal text \"null\" and an unparseable string as \"Invalid Date\", both of which are junk a user\n // can see. `getDateString` has always guarded this; the formatter is a separate path and did not.\n if (value === null || value === undefined) return '';\n if (typeof value === 'string' && isUnparseableDateString(value)) return '';\n\n if (relativeCompact) {\n const relative = relativeCompactDateString({\n maxDays: asNumber(relativeCompactMaxDays, DEFAULT_RELATIVE_COMPACT_MAX_DAYS),\n maxWeeks: asNumber(relativeCompactMaxWeeks, DEFAULT_RELATIVE_COMPACT_MAX_WEEKS),\n tDateTimeParser,\n timestamp: value,\n translate,\n weekRounding: asWeekRounding(relativeCompactWeekRounding),\n });\n if (relative !== null) return relative;\n }\n\n const parsed = tDateTimeParser(value as string);\n\n if (isDayOrMoment(parsed)) {\n if (calendar && typeof parsed.calendar === 'function') {\n return parsed.calendar(undefined, parseCalendarFormats(calendarFormats, logger));\n }\n return parsed.format(format);\n }\n if (isDate(parsed)) return parsed.toDateString();\n if (isNumberOrString(parsed)) return String(parsed);\n return '';\n };\n\n/**\n * Renders a length of time, e.g. `600000` -> \"10 minutes\".\n *\n * Goes through the date library's `.duration()` rather than parsing the number as a timestamp \u2014 which\n * would read 600000 as \"10 minutes past the epoch\" and render \"57 years ago\".\n */\nconst durationFormatter: FormatterFactory<number | string> =\n ({ dateTimeParser }: FormatterContext) =>\n (value, _lng, options) => {\n const { format, withSuffix } = options as DurationFormatterOptions;\n if (typeof dateTimeParser.duration !== 'function') return String(value);\n\n const duration = dateTimeParser.duration(value as number);\n // Only dayjs's duration plugin has `.format`; moment durations humanize only.\n if (format && typeof duration.format === 'function') return duration.format(format);\n return duration.humanize(Boolean(withSuffix));\n };\n\nconst fromNowFormatter: FormatterFactory<string | Date> =\n ({ tDateTimeParser }: FormatterContext) =>\n (value, _lng, options) => {\n if (value === null || value === undefined) return '';\n const parsed = tDateTimeParser(value as string);\n if (!isDayOrMoment(parsed) || typeof parsed.fromNow !== 'function') return '';\n return parsed.fromNow(\n Boolean((options as { withoutSuffix?: boolean }).withoutSuffix),\n );\n };\n\n/** The formatters registered with i18next by default. */\nexport const predefinedFormatters: PredefinedFormatters = {\n durationFormatter,\n fromNowFormatter,\n timestampFormatter,\n};\n\n/* ------------------------------------------------------------------------------------------------\n * getDateString\n * ---------------------------------------------------------------------------------------------- */\n\nexport type GetDateStringParams = TimestampFormatterOptions & {\n /** The timestamp to render. */\n messageCreatedAt?: string | Date;\n /** An integrator-supplied override, which wins over everything else. */\n formatDate?: (date: Date) => string;\n /** The key carrying a formatter expression for this timestamp, if there is one. */\n timestampTranslationKey?: string;\n t?: LooseTranslateFunction;\n tDateTimeParser?: TDateTimeParser;\n};\n\n/**\n * Resolves a timestamp to a display string.\n *\n * Resolution order, and why: an integrator's `formatDate` wins outright; then the translation key, so\n * a language can restyle the timestamp without touching component props; then the parser. Returns\n * `null` rather than a placeholder when there is nothing sensible to render, so callers can omit the\n * element entirely.\n */\nexport const getDateString = ({\n calendar,\n calendarFormats,\n format,\n formatDate,\n messageCreatedAt,\n relativeCompact,\n relativeCompactMaxDays,\n relativeCompactMaxWeeks,\n relativeCompactWeekRounding,\n t,\n tDateTimeParser,\n timestampTranslationKey,\n}: GetDateStringParams): string | number | null => {\n if (\n !messageCreatedAt ||\n (typeof messageCreatedAt === 'string' && isUnparseableDateString(messageCreatedAt))\n ) {\n return null;\n }\n\n if (formatDate) return formatDate(new Date(messageCreatedAt));\n\n // Before the translation-key path, so a caller can ask for relative-compact rendering directly\n // rather than only through a key whose expression sets it. Falls through when it declines (a future\n // date, or no dayjs-like parser), so the normal formatting still applies.\n if (relativeCompact && t && tDateTimeParser) {\n const relative = relativeCompactDateString({\n maxDays: asNumber(relativeCompactMaxDays, DEFAULT_RELATIVE_COMPACT_MAX_DAYS),\n maxWeeks: asNumber(relativeCompactMaxWeeks, DEFAULT_RELATIVE_COMPACT_MAX_WEEKS),\n tDateTimeParser,\n timestamp: messageCreatedAt,\n translate: t,\n weekRounding: asWeekRounding(relativeCompactWeekRounding),\n });\n if (relative) return relative;\n }\n\n if (t && timestampTranslationKey) {\n // Only forward options that were actually supplied.\n //\n // These reach i18next as interpolation values and are merged over the arguments the key's own\n // formatter expression declares \u2014 so passing `format: undefined` explicitly *overrides*\n // `timestampFormatter(format: HH:mm)` with nothing, and the timestamp renders as a raw ISO string.\n // The caller is usually a component forwarding optional props, so most of these are undefined most\n // of the time.\n const overrides: Record<string, unknown> = {};\n const supplied = {\n calendar,\n calendarFormats,\n format,\n relativeCompact,\n relativeCompactMaxDays,\n relativeCompactMaxWeeks,\n relativeCompactWeekRounding,\n };\n for (const [key, value] of Object.entries(supplied)) {\n if (value !== undefined) overrides[key] = value;\n }\n\n const translated = t(asDynamicKey(timestampTranslationKey), {\n ...overrides,\n // A `Date`, not the raw value. Integrators override a `timestamp.*` key with their own\n // formatter, and those read `options.timestamp` expecting a Date \u2014 passing the string through\n // breaks them with `timestamp.toISOString is not a function`.\n timestamp: new Date(messageCreatedAt),\n });\n // i18next echoes the key back when nothing resolved it, which is how a miss is detected.\n if (translated !== timestampTranslationKey) return translated;\n }\n\n if (!tDateTimeParser) return null;\n\n const parsed = tDateTimeParser(messageCreatedAt);\n\n if (isDayOrMoment(parsed)) {\n if (calendar && typeof parsed.calendar === 'function') {\n return parsed.calendar(\n undefined,\n typeof calendarFormats === 'string' ? undefined : calendarFormats,\n );\n }\n return parsed.format(format);\n }\n if (isDate(parsed)) return parsed.toDateString();\n if (isNumberOrString(parsed)) return parsed;\n return null;\n};\n\n/**\n * The same resolution as {@link getDateString}, but always spelling the date out in full.\n *\n * A screen reader announcing \"14:32\" with no date is ambiguous, so the a11y string ignores the compact\n * and calendar options a visual timestamp uses.\n */\nexport const getDateStringForA11y = ({\n messageCreatedAt,\n t,\n tDateTimeParser,\n timestampTranslationKey,\n}: Pick<\n GetDateStringParams,\n 'messageCreatedAt' | 't' | 'tDateTimeParser' | 'timestampTranslationKey'\n>): string | number | null =>\n getDateString({\n calendar: false,\n format: 'LLLL',\n messageCreatedAt,\n t,\n tDateTimeParser,\n timestampTranslationKey,\n });\n\n/**\n * Calendar wording used by {@link getCalendarDateStringForA11y}, for the one bundled locale.\n *\n * Only English ships. A language an integrator registers supplies its own wording through\n * `dayjsLocaleConfigForLanguage` (or `registerTranslation`'s third argument) \u2014 a per-locale block here\n * is useless on its own without the matching `dayjs/locale/xx` beside it.\n */\nexport const A11Y_CALENDAR_FORMATS: Record<string, CalendarFormats> = {\n en: {\n lastDay: '[Yesterday]',\n lastWeek: 'dddd',\n nextDay: '[Tomorrow]',\n nextWeek: 'dddd [at] LT',\n sameDay: '[Today]',\n sameElse: 'L',\n },\n};\n\nexport type GetCalendarDateStringForA11yParams = {\n /**\n * Calendar-format overrides applied over the locale defaults and the `sameElse: 'LL'` substitution.\n * Use it where the visible date deliberately diverges \u2014 a channel preview shows `sameDay: 'LT'`, the\n * time rather than \"Today\".\n */\n calendarFormatOverrides?: Partial<CalendarFormats>;\n /** Calendar wording per language. Defaults to {@link A11Y_CALENDAR_FORMATS}. */\n calendarFormats?: Record<string, CalendarFormats>;\n messageCreatedAt?: string | Date;\n tDateTimeParser?: TDateTimeParser;\n /**\n * The UI language, used to pick calendar wording. Plain `string`: it indexes `calendarFormats`, which\n * an integrator extends for whatever language they registered \u2014 not `stream-chat`'s\n * auto-translation `TranslationLanguage` union.\n */\n userLanguage?: string;\n};\n\n/**\n * A TTS-friendly calendar string, preserving relative wording.\n *\n * Distinct from {@link getDateStringForA11y}, which spells the date out in full via `LLLL`. Both exist\n * because the two UI SDKs arrived at different answers and both are defensible: this one keeps\n * \"Today\"/\"Yesterday\"/weekday names from the locale's calendar and substitutes `LL` (\"April 8, 2026\")\n * only into the `sameElse` slot, because iOS VoiceOver reads a numeric date like \"04/08/2026\"\n * character by character. Do not collapse them into one \u2014 that would silently change one SDK's\n * announced labels.\n *\n * Returns `undefined` when there is nothing to announce, including when the parser has no calendar\n * plugin, so the caller omits the label rather than announcing a malformed date.\n */\nexport const getCalendarDateStringForA11y = ({\n calendarFormatOverrides,\n calendarFormats = A11Y_CALENDAR_FORMATS,\n messageCreatedAt,\n tDateTimeParser,\n userLanguage,\n}: GetCalendarDateStringForA11yParams): string | undefined => {\n if (\n !messageCreatedAt ||\n (typeof messageCreatedAt === 'string' && isUnparseableDateString(messageCreatedAt)) ||\n !tDateTimeParser\n ) {\n return undefined;\n }\n\n const parsed = tDateTimeParser(messageCreatedAt);\n if (!isDayOrMoment(parsed) || !parsed.calendar) return undefined;\n\n const localeFormats =\n (userLanguage && calendarFormats[userLanguage]) || calendarFormats.en;\n\n return parsed.calendar(undefined, {\n ...localeFormats,\n sameElse: 'LL',\n ...calendarFormatOverrides,\n });\n};\n", "import type { TranslationLanguage } from '../types';\n\n/**\n * The human-readable name of each language the API can auto-translate a message into.\n *\n * These are display copy for a **core-owned** set: `message.i18n.language` is typed\n * {@link TranslationLanguage}, so core defines which languages exist and therefore owns their names\n * too. A UI SDK uses them to say \"Translated from German\" rather than \"Translated from de\".\n *\n * `satisfies Record<TranslationLanguage, string>` is the drift gate, and it works in both\n * directions: adding a language to the API union fails to compile until a name is supplied here, and a\n * name for a language the union does not contain is rejected as an excess property. Before this lived\n * in core, each UI SDK hand-maintained its own copy with nothing tying it to the union \u2014 so a miss\n * could only be detected at runtime, by comparing the rendered string against the key.\n *\n * Names are in English on purpose. A language picker conventionally shows each language endonymously\n * (\"Deutsch\", not \"German\"), but this is the *source* language of an auto-translated message rendered\n * inside a sentence in the reader's own language, so it has to agree with the surrounding copy. An\n * integrator wanting endonyms overrides the `language.*` keys.\n */\nexport const LANGUAGE_NAMES = {\n af: 'Afrikaans',\n am: 'Amharic',\n ar: 'Arabic',\n az: 'Azerbaijani',\n bg: 'Bulgarian',\n bn: 'Bengali',\n bs: 'Bosnian',\n cs: 'Czech',\n da: 'Danish',\n de: 'German',\n el: 'Greek',\n en: 'English',\n es: 'Spanish',\n 'es-MX': 'Spanish (Mexico)',\n et: 'Estonian',\n fa: 'Persian',\n 'fa-AF': 'Dari',\n fi: 'Finnish',\n fr: 'French',\n 'fr-CA': 'French (Canada)',\n ha: 'Hausa',\n he: 'Hebrew',\n hi: 'Hindi',\n hr: 'Croatian',\n ht: 'Haitian Creole',\n hu: 'Hungarian',\n id: 'Indonesian',\n it: 'Italian',\n ja: 'Japanese',\n ka: 'Georgian',\n ko: 'Korean',\n lt: 'Lithuanian',\n lv: 'Latvian',\n ms: 'Malay',\n nl: 'Dutch',\n no: 'Norwegian',\n pl: 'Polish',\n ps: 'Pashto',\n pt: 'Portuguese',\n ro: 'Romanian',\n ru: 'Russian',\n sk: 'Slovak',\n sl: 'Slovenian',\n so: 'Somali',\n sq: 'Albanian',\n sr: 'Serbian',\n sv: 'Swedish',\n sw: 'Swahili',\n ta: 'Tamil',\n th: 'Thai',\n tl: 'Tagalog',\n tr: 'Turkish',\n uk: 'Ukrainian',\n ur: 'Urdu',\n vi: 'Vietnamese',\n zh: 'Chinese (Simplified)',\n 'zh-TW': 'Chinese (Traditional)',\n} as const satisfies Record<TranslationLanguage, string>;\n\n/**\n * The `language.*` slice of a translation catalog.\n *\n * A UI SDK intersects this into its own generated catalog, which makes `t('language.de')` a checked\n * key rather than something that has to go through `asDynamicKey()`:\n *\n * ```ts\n * type TranslationCatalog = GeneratedCatalog & LanguageNameCatalog;\n * ```\n */\nexport type LanguageNameCatalog = {\n [K in keyof typeof LANGUAGE_NAMES as `language.${K & string}`]: (typeof LANGUAGE_NAMES)[K];\n};\n\n/**\n * {@link LANGUAGE_NAMES} keyed the way a catalog is, ready to merge into an SDK's bundled defaults.\n *\n * These keys are resolved from a runtime value (the message's source language), so there is no call\n * site to carry an inline default \u2014 which is exactly why they have to ship as data.\n */\nexport const languageNameDefaults: Record<string, string> = Object.fromEntries(\n Object.entries(LANGUAGE_NAMES).map(([code, name]) => [`language.${code}`, name]),\n);\n", "import i18next from 'i18next';\nimport type { i18n as I18nInstance, InitOptions } from 'i18next';\n\nimport { StateStore } from '../store';\nimport {\n addOrUpdateDayjsLocale,\n dayjsLocaleExists,\n ensureDayjsPlugins,\n getDefaultDateTimeParserModule,\n isDayjsLike,\n supportsTimezone,\n} from './dayjs';\nimport type { DayjsLocaleConfig } from './dayjs';\nimport { predefinedFormatters } from './formatters';\nimport { TranslationBuilder } from './TranslationBuilder';\nimport type { TranslationTopicConstructor } from './TranslationBuilder';\nimport { DEFAULT_LANGUAGE, TranslationStore } from './TranslationStore';\nimport {\n asDynamicKey,\n createDefaultTranslatorFunction,\n guardMissingKeyHandler,\n} from './translator';\nimport type {\n AnyTranslationCatalog,\n CustomFormatters,\n DateTimeParserModule,\n FormatterContext,\n LooseTranslateFunction,\n PredefinedFormatters,\n StreamTFunctionFor,\n TDateTimeParser,\n TranslationDictionaryOf,\n} from './types';\n\nconst DEFAULT_NAMESPACE = 'translation';\n\nexport type Streami18nOptions<C extends AnyTranslationCatalog = AnyTranslationCatalog> = {\n /** A dayjs or moment module. Defaults to dayjs with the required plugins registered. */\n DateTimeParser?: DateTimeParserModule;\n dayjsLocaleConfigForLanguage?: DayjsLocaleConfig;\n debug?: boolean;\n /** Keep dates in English regardless of the active language. */\n disableDateTimeTranslations?: boolean;\n formatters?: Partial<PredefinedFormatters> & CustomFormatters;\n /**\n * Any i18next `InitOptions`. Applied over the SDK's defaults, so it can reach settings the SDK does\n * not surface. `parseMissingKeyHandler` supplied here is guarded the same way as the top-level\n * option.\n */\n i18nextConfigOverrides?: Partial<InitOptions>;\n language?: string;\n logger?: (message?: string) => void;\n /**\n * Called only for keys that are genuinely missing \u2014 one with no inline default and no bundled\n * value. See {@link guardMissingKeyHandler} for why it cannot be passed straight to i18next.\n */\n parseMissingKeyHandler?: (key: string, defaultValue?: string) => string;\n /**\n * The SDK's bundled translation data: the keys that cannot carry an inline `defaultValue` at their\n * call site.\n *\n * Injected rather than imported because the catalog belongs to the UI SDK, not to core. Layered\n * under **every** language, which is what stops a partial dictionary from knocking out formatter\n * keys.\n */\n runtimeDefaults?: Record<string, string>;\n /** A valid TZ identifier, e.g. `Europe/Prague`. */\n timezone?: string;\n /**\n * Post-processor topics for copy that cannot be resolved from a key alone \u2014 see\n * {@link TranslationBuilder}. The key here must match the post-processor name in the translation\n * value, i.e. `{{ value, topicName }}`.\n */\n translationBuilderTopics?: Record<string, TranslationTopicConstructor>;\n translationsForLanguage?: TranslationDictionaryOf<C>;\n};\n\nexport type Streami18nState<\n C extends AnyTranslationCatalog = AnyTranslationCatalog,\n Bundled extends string = never,\n> = {\n initialized: boolean;\n language: string;\n t: StreamTFunctionFor<C, Bundled>;\n tDateTimeParser: TDateTimeParser;\n};\n\n/**\n * Wrapper around [i18next](https://www.i18next.com/) for Stream's translations. A UI SDK passes an\n * instance to its `<Chat>` component to control language and copy. Only English ships; every other\n * language comes from the integrator, and a partial dictionary is safe.\n *\n * ```ts\n * import 'dayjs/locale/de';\n *\n * const i18n = new Streami18n({ language: 'de' });\n * i18n.registerTranslation('de', de, { calendar: { sameDay: '[heute um] LT', ... } });\n * ```\n *\n * No dayjs locale file defines `calendar` \u2014 that field belongs to the calendar plugin \u2014 so a new\n * language needs both the locale import and a `calendar` config, or relative dates render English\n * scaffolding around translated day names.\n */\nexport class Streami18n<\n C extends AnyTranslationCatalog = AnyTranslationCatalog,\n Bundled extends string = never,\n> {\n /** Marks instances across bundle copies, where `instanceof` silently fails. */\n static readonly brand = Symbol.for('stream-chat.Streami18n');\n\n readonly i18nInstance: I18nInstance = i18next.createInstance();\n\n readonly state: StateStore<Streami18nState<C, Bundled>>;\n\n readonly translationBuilder: TranslationBuilder;\n\n readonly logger: (message?: string) => void;\n readonly DateTimeParser: DateTimeParserModule;\n readonly formatters: PredefinedFormatters & CustomFormatters;\n readonly timezone?: string;\n\n private readonly translations: TranslationStore;\n\n /** Applied when the language becomes active, not on registration: `Dayjs.locale()` is global. */\n private readonly dayjsLocales: Record<string, DayjsLocaleConfig> = {};\n\n private readonly translationBuilderTopics: Record<string, TranslationTopicConstructor>;\n private readonly disableDateTimeTranslations: boolean;\n private readonly i18nextConfig: InitOptions;\n private initPromise?: Promise<Streami18nState<C, Bundled>>;\n /** Set by {@link overrideTFunction}, so `init()` does not clobber a swapped-in implementation. */\n private tOverridden = false;\n\n constructor(options: Streami18nOptions<C> = {}) {\n this.logger = options.logger ?? ((message?: string) => console.warn(message));\n this.translations = new TranslationStore(options.runtimeDefaults);\n this.disableDateTimeTranslations = options.disableDateTimeTranslations ?? false;\n this.timezone = options.timezone;\n this.formatters = { ...predefinedFormatters, ...options.formatters };\n this.translationBuilder = new TranslationBuilder(this.i18nInstance);\n this.translationBuilderTopics = options.translationBuilderTopics ?? {};\n\n const language = options.language ?? DEFAULT_LANGUAGE;\n\n if (options.DateTimeParser) {\n this.DateTimeParser = options.DateTimeParser;\n // The supplied module, not ours -- it may be a second copy of dayjs, and extending ours would\n // leave theirs plugin-less, rendering every `LT` / `LLLL` token literally.\n if (isDayjsLike(this.DateTimeParser)) ensureDayjsPlugins(this.DateTimeParser);\n } else {\n this.DateTimeParser = getDefaultDateTimeParserModule();\n }\n\n const tDateTimeParser: TDateTimeParser = (timestamp) => {\n const locale =\n this.disableDateTimeTranslations || !this.localeExists(this.currentLanguageValue)\n ? DEFAULT_LANGUAGE\n : this.currentLanguageValue;\n\n const parsed = this.DateTimeParser(timestamp);\n const withZone =\n this.timezone && supportsTimezone(this.DateTimeParser)\n ? (parsed as unknown as { tz: (tz: string) => typeof parsed }).tz(this.timezone)\n : parsed;\n\n return (withZone as unknown as { locale: (l: string) => typeof parsed }).locale(\n locale,\n );\n };\n\n this.state = new StateStore<Streami18nState<C, Bundled>>({\n initialized: false,\n language,\n t: createDefaultTranslatorFunction<C, Bundled>(),\n tDateTimeParser,\n });\n\n // Both always exist, so an unregistered language still renders English copy rather than dotted keys.\n this.translations.ensure(DEFAULT_LANGUAGE);\n this.translations.ensure(language);\n\n if (options.translationsForLanguage) {\n this.translations.register(\n language,\n options.translationsForLanguage as Record<string, string>,\n );\n }\n\n const missingKeyHandler =\n options.parseMissingKeyHandler ??\n options.i18nextConfigOverrides?.parseMissingKeyHandler;\n\n this.i18nextConfig = {\n debug: options.debug ?? false,\n fallbackLng: false,\n interpolation: { escapeValue: false, formatSeparator: '|' },\n // Must stay false: keys are flat strings containing dots, and some copy contains `...`.\n keySeparator: false,\n lng: language,\n nsSeparator: false,\n // i18next only runs post-processors it was told about at init time.\n ...(Object.keys(this.translationBuilderTopics).length > 0\n ? { postProcess: Object.keys(this.translationBuilderTopics) }\n : {}),\n ...options.i18nextConfigOverrides,\n // Guarded even when integrator-supplied: an unguarded handler silently blanks every prose key.\n parseMissingKeyHandler: missingKeyHandler\n ? guardMissingKeyHandler(missingKeyHandler)\n : (key: string, defaultValue?: string) => {\n if (typeof defaultValue === 'string') return defaultValue;\n this.logger(`Streami18n: missing translation for key: ${key}`);\n return key;\n },\n };\n\n // No dictionary check here -- `registerTranslation()` legitimately runs after construction, so\n // `init()` is the first moment the registered set is final.\n if (options.dayjsLocaleConfigForLanguage) {\n this.addOrUpdateLocale(language, options.dayjsLocaleConfigForLanguage);\n } else if (!this.localeExists(language)) {\n this.logger(\n `Streami18n: no dayjs locale is registered for '${language}', so dates render with the ` +\n `English locale. Import it with \"import 'dayjs/locale/${language}';\" in your app, or pass ` +\n `a config via registerTranslation('${language}', translation, dayjsLocaleConfig).`,\n );\n }\n }\n\n get t(): StreamTFunctionFor<C, Bundled> {\n return this.state.getLatestValue().t;\n }\n\n get tDateTimeParser(): TDateTimeParser {\n return this.state.getLatestValue().tDateTimeParser;\n }\n\n get currentLanguage(): string {\n return this.state.getLatestValue().language;\n }\n\n get initialized(): boolean {\n return this.state.getLatestValue().initialized;\n }\n\n /** Read inside the constructor, before `state` getters are safe to rely on externally. */\n private get currentLanguageValue(): string {\n return this.state?.getLatestValue().language ?? DEFAULT_LANGUAGE;\n }\n\n /**\n * Initializes i18next. Idempotent and safe to call concurrently.\n *\n * Memoized, so two independent consumers \u2014 a UI SDK's chat root and its overlay host \u2014 share one\n * initialization.\n *\n * An i18next failure does **not** reject: neither UI SDK awaits this, so a rejection would surface\n * as an unhandled rejection and the memo would latch it for the process lifetime. It is logged\n * instead, leaving the instance *degraded but safe*: `state.initialized` stays false, which is what\n * keeps the methods below off a dead i18next instance, and `t` remains the default translator, so\n * every call site still renders its inline English. There is no retry \u2014 construct a new instance.\n *\n * One path does escape: an integrator `logger` that throws is called from the `catch` itself, so it\n * rejects out of here. Rare enough not to guard, but it is why this is not an absolute guarantee.\n */\n init(): Promise<Streami18nState<C, Bundled>> {\n this.initPromise ??= this.runInit();\n return this.initPromise;\n }\n\n private async runInit(): Promise<Streami18nState<C, Bundled>> {\n // Everything is inside the `try` -- see `init()` for why an i18next failure must not reject.\n try {\n this.validateCurrentLanguage();\n this.assertPluralRulesCoverage(this.currentLanguage);\n\n const dayjsLocale = this.dayjsLocales[this.currentLanguage];\n if (dayjsLocale) this.addOrUpdateLocale(this.currentLanguage, dayjsLocale);\n\n const t = await this.i18nInstance.init({\n ...this.i18nextConfig,\n lng: this.currentLanguage,\n resources: this.i18nextResources(),\n });\n\n this.registerFormatters();\n\n // After init, so post-processors attach to a live instance and buffered translators flush.\n Object.entries(this.translationBuilderTopics).forEach(([topic, Topic]) => {\n this.translationBuilder.registerTopic(topic, Topic);\n });\n\n this.state.partialNext({\n initialized: true,\n ...(this.tOverridden\n ? {}\n : { t: t as unknown as StreamTFunctionFor<C, Bundled> }),\n });\n } catch (error) {\n this.logger(`Streami18n: initialization failed: ${describeError(error)}`);\n }\n\n return this.state.getLatestValue();\n }\n\n /**\n * Re-run on every language change, not just at `init()`: factories destructure the language out of\n * their context, so one built once keeps formatting in the initial language forever. `formatter.add`\n * replaces by name, which is what makes re-registering sufficient.\n */\n private registerFormatters = () => {\n const context = this.createFormatterContext();\n\n Object.entries(this.formatters).forEach(([name, factory]) => {\n if (!factory) return;\n const formatter = factory(context);\n // Widened here rather than in the public type: a custom formatter's value is `never` so any\n // implementation is assignable (parameters are contravariant), while i18next's takes `any`.\n this.i18nInstance.services.formatter?.add(\n name,\n formatter as (value: any, lng: string | undefined, options: any) => string,\n );\n });\n };\n\n /**\n * Accessors rather than snapshots, for a formatter that holds the context and reads per call; one\n * that destructures is covered by {@link registerFormatters} re-running instead.\n *\n * The nested arrows are load-bearing: a getter in an object literal binds `this` to the literal.\n */\n private createFormatterContext = (): FormatterContext => {\n const readLanguage = () => this.currentLanguage;\n const readDateTimeParser = () => this.tDateTimeParser;\n\n return {\n get currentLanguage() {\n return readLanguage();\n },\n dateTimeParser: this.DateTimeParser,\n logger: this.logger,\n get tDateTimeParser() {\n return readDateTimeParser();\n },\n timezone: this.timezone,\n translate: this.translate,\n };\n };\n\n /** The store's flat dictionaries in i18next's nested `resources` shape. */\n private i18nextResources = (): Record<string, Record<string, Record<string, string>>> =>\n Object.fromEntries(\n this.translations\n .entries()\n .map(([language, dictionary]) => [language, { [DEFAULT_NAMESPACE]: dictionary }]),\n );\n\n /** The only route for a language added after `init()`. */\n private ensureLanguage = (language: string) => {\n this.addResources(language, this.translations.ensure(language));\n };\n\n private addResources = (language: string, dictionary: Record<string, string>) => {\n if (!this.initialized) return;\n this.i18nInstance.addResources(language, DEFAULT_NAMESPACE, dictionary);\n };\n\n registerTranslation(\n language: string,\n translation: TranslationDictionaryOf<C>,\n dayjsLocaleConfig?: DayjsLocaleConfig,\n ) {\n if (!translation) {\n this.logger(\n 'Streami18n: registerTranslation called without a translation dictionary',\n );\n return;\n }\n\n const merged = this.translations.register(\n language,\n translation as Record<string, string>,\n );\n\n if (dayjsLocaleConfig) {\n this.dayjsLocales[language] = { ...dayjsLocaleConfig };\n } else if (!this.localeExists(language)) {\n this.logger(\n `Streami18n: no dayjs locale is registered for '${language}'. Import it with ` +\n `\"import 'dayjs/locale/${language}';\" in your app, or pass a config as the third ` +\n `argument to registerTranslation.`,\n );\n }\n\n // `merged`, not `translation`: for a post-init language this is the only write into i18next's\n // store, so the partial would leave the bundled defaults absent there.\n this.addResources(language, merged);\n }\n\n /**\n * Returns nothing: the new `t` is published to {@link Streami18n.state}. Handing one back would offer\n * a value that goes stale on the next language change.\n */\n async setLanguage(language: string): Promise<void> {\n const previousLanguage = this.state.getLatestValue().language;\n\n // Published up front so the warnings below name the language being adopted, and rolled back in the\n // `catch` -- otherwise the store advertises one i18next never switched to.\n this.state.partialNext({ language });\n this.ensureLanguage(language);\n\n if (!this.initialized) return;\n\n this.validateCurrentLanguage();\n this.assertPluralRulesCoverage(language);\n\n try {\n const t = await this.i18nInstance.changeLanguage(language);\n const dayjsLocale = this.dayjsLocales[language];\n if (dayjsLocale) this.addOrUpdateLocale(language, dayjsLocale);\n this.registerFormatters();\n if (!this.tOverridden) {\n this.state.partialNext({ t: t as unknown as StreamTFunctionFor<C, Bundled> });\n }\n } catch (error) {\n this.state.partialNext({ language: previousLanguage });\n this.logger(`Streami18n: failed to set language: ${describeError(error)}`);\n }\n }\n\n /**\n * Swaps in a different translation implementation, for an app that already has an i18n layer. Safe\n * before `init()`, which will not overwrite it.\n */\n overrideTFunction(t: StreamTFunctionFor<C, Bundled>) {\n this.tOverridden = true;\n this.state.partialNext({ t });\n }\n\n /**\n * Languages an integrator supplied a dictionary for. Read-only because adding to it would claim a\n * language is registered with no dictionary behind it; use `registerTranslation`.\n */\n get registeredLanguages(): ReadonlySet<string> {\n return this.translations.registeredLanguages;\n }\n\n /**\n * Warns rather than falling back to `en`: the language renders English copy from the inline defaults\n * while keeping its own date formats, and resetting it would discard the integrator's choice.\n */\n private validateCurrentLanguage = () => {\n const language = this.currentLanguageValue;\n if (this.translations.isRegistered(language)) return;\n\n this.logger(\n `Streami18n: no translation dictionary is registered for '${language}', so the SDK's copy ` +\n `renders in English. Call registerTranslation('${language}', {...}) to translate it. ` +\n `Registered: ${[...this.translations.registeredLanguages].join(', ')}`,\n );\n };\n\n /**\n * Checked against the module that actually formats the dates.\n *\n * A supplied dayjs copy has its own locale registry, so consulting ours would answer for the wrong\n * one. True for a non-dayjs parser, whose registry we cannot inspect.\n */\n private localeExists = (language: string) => {\n if (!isDayjsLike(this.DateTimeParser)) return true;\n return dayjsLocaleExists(language, this.DateTimeParser);\n };\n\n /**\n * Registers a locale on the module that formats the dates, not on ours.\n *\n * Only dayjs has a registry we can write to. For a Moment the config cannot be applied at all, so it\n * is reported -- previously it was written to core's own dayjs, where nothing would ever read it.\n */\n private addOrUpdateLocale(language: string, config: DayjsLocaleConfig) {\n if (!isDayjsLike(this.DateTimeParser)) {\n this.logger(\n `Streami18n: a dayjs locale config was supplied for '${language}', but DateTimeParser is ` +\n `not dayjs, so it cannot be applied. Configure the locale on your own date library instead.`,\n );\n return;\n }\n addOrUpdateDayjsLocale(language, config, this.DateTimeParser);\n }\n\n /**\n * For formatters, which resolve keys handed to them at runtime -- including their own\n * `relativeTime.*` copy, which no catalog declares.\n */\n private translate: LooseTranslateFunction = (key, defaultValueOrOptions, options) =>\n (this.t as LooseTranslateFunction)(\n asDynamicKey(key),\n defaultValueOrOptions,\n options,\n ) as string;\n\n /**\n * Hermes ships a partial ICU: `Intl.PluralRules` silently falls back to root rules (`other` only) for\n * locales it lacks, so a dictionary's `_few` / `_many` never render and nothing errors.\n *\n * Called from `init()` because i18next caches a resolver per language there \u2014 the last moment\n * `intl-pluralrules` could still have been loaded in time.\n */\n private assertPluralRulesCoverage = (language: string) => {\n try {\n const resolved = new Intl.PluralRules(language).resolvedOptions().locale;\n if (resolved.split('-')[0] === language.split('-')[0]) return;\n this.logger(\n `Streami18n: Intl.PluralRules has no data for '${language}' (it resolved to ` +\n `'${resolved}'), so every count selects the '_other' form. On React Native, import ` +\n `'intl-pluralrules' before anything else in your entry file.`,\n );\n } catch {\n this.logger(\n `Streami18n: Intl.PluralRules is unavailable, so plural selection will not work. On React ` +\n `Native, import 'intl-pluralrules' before anything else in your entry file.`,\n );\n }\n };\n}\n\n/** `JSON.stringify(error)` renders an `Error` as `{}`. */\nconst describeError = (error: unknown) =>\n error instanceof Error ? error.message : String(error);\n", "export type Patch<T> = (value: T) => T;\nexport type ValueOrPatch<T> = T | Patch<T>;\nexport type Handler<T> = (nextValue: T, previousValue: T | undefined) => void;\nexport type Unsubscribe = () => void;\n// aliases\nexport type RemovePreprocessor = Unsubscribe;\nexport type Preprocessor<T> = Handler<T>;\n\nexport const isPatch = <T>(value: ValueOrPatch<T>): value is Patch<T> =>\n typeof value === 'function';\n\n// eslint-disable-next-line @typescript-eslint/no-empty-function\nconst noop = () => {};\n\nexport class StateStore<T extends Record<string, unknown>> {\n protected handlers = new Set<Handler<T>>();\n protected preprocessors = new Set<Preprocessor<T>>();\n\n constructor(protected value: T) {}\n\n /**\n * Allows merging two stores only if their keys differ otherwise there's no way to ensure the data type stability.\n *\n * @experimental\n * This method is experimental and may change in future versions.\n */\n\n public merge<Q extends StateStore<any>>(\n stateStore: Q extends StateStore<infer L>\n ? Extract<keyof T, keyof L> extends never\n ? Q\n : never\n : never,\n ) {\n return new MergedStateStore<T, Q extends StateStore<infer L> ? L : never>({\n original: this,\n merged: stateStore,\n });\n }\n\n public next(newValueOrPatch: ValueOrPatch<T>): void {\n // newValue (or patch output) should never be a mutated previous value\n const newValue = isPatch(newValueOrPatch)\n ? newValueOrPatch(this.value)\n : newValueOrPatch;\n\n // do not notify subscribers if the value hasn't changed\n if (newValue === this.value) return;\n\n this.preprocessors.forEach((preprocessor) => preprocessor(newValue, this.value));\n\n const oldValue = this.value;\n this.value = newValue;\n\n this.handlers.forEach((handler) => handler(this.value, oldValue));\n }\n\n public partialNext = (partial: Partial<T>): void =>\n this.next((current) => ({ ...current, ...partial }));\n\n public getLatestValue(): T {\n return this.value;\n }\n\n public subscribe(handler: Handler<T>): Unsubscribe {\n handler(this.value, undefined);\n this.handlers.add(handler);\n return () => {\n this.handlers.delete(handler);\n };\n }\n\n public subscribeWithSelector = <\n O extends Readonly<Record<string, unknown>> | Readonly<unknown[]>,\n >(\n selector: (nextValue: T) => O,\n handler: Handler<O>,\n ) => {\n // begin with undefined to reduce amount of selector calls\n let previouslySelectedValues: O | undefined;\n\n const wrappedHandler: Handler<T> = (nextValue) => {\n const newlySelectedValues = selector(nextValue);\n\n let hasUpdatedValues = typeof previouslySelectedValues === 'undefined';\n\n for (const key in previouslySelectedValues) {\n if (previouslySelectedValues[key] === newlySelectedValues[key]) continue;\n hasUpdatedValues = true;\n break;\n }\n\n if (!hasUpdatedValues) return;\n\n // save a copy of previouslySelectedValues before running\n // handler - if previouslySelectedValues are set to\n // newlySelectedValues after the handler call, there's a chance\n // that it'll never get set as handler can throw and flow might\n // go out of sync\n const previouslySelectedValuesCopy = previouslySelectedValues;\n previouslySelectedValues = newlySelectedValues;\n\n handler(newlySelectedValues, previouslySelectedValuesCopy);\n };\n\n return this.subscribe(wrappedHandler);\n };\n\n /**\n * Registers a preprocessor function that will be called before the state is updated.\n *\n * Preprocessors are invoked with the new and previous values whenever `next` or `partialNext` methods\n * are called, allowing you to mutate or react to the new value before it is set. Preprocessors run in the\n * order they were registered.\n *\n * @example\n * ```ts\n * const store = new StateStore<{ count: number; isMaxValue: bool; }>({ count: 0, isMaxValue: false });\n *\n * store.addPreprocessor((nextValue, prevValue) => {\n * if (nextValue.count > 10) {\n * nextValue.count = 10; // Clamp the value to a maximum of 10\n * }\n *\n * if (nextValue.count === 10) {\n * nextValue.isMaxValue = true; // Set isMaxValue to true if count is 10\n * } else {\n * nextValue.isMaxValue = false; // Reset isMaxValue otherwise\n * }\n * });\n *\n * store.partialNext({ count: 15 });\n *\n * store.getLatestValue(); // { count: 10, isMaxValue: true }\n *\n * store.partialNext({ count: 5 });\n *\n * store.getLatestValue(); // { count: 5, isMaxValue: false }\n * ```\n *\n * @param preprocessor - The function to be called with the next and previous values before the state is updated.\n * @returns A `RemovePreprocessor` function that removes the preprocessor when called.\n */\n public addPreprocessor(preprocessor: Preprocessor<T>): RemovePreprocessor {\n this.preprocessors.add(preprocessor);\n\n return () => {\n this.preprocessors.delete(preprocessor);\n };\n }\n}\n\n/**\n * Represents a merged state store that combines two separate state stores into one.\n *\n * The MergedStateStore allows combining two stores with non-overlapping keys.\n * It extends StateStore with the combined type of both source stores.\n * Changes to either the original or merged store will propagate to the combined store.\n *\n * Note: Direct mutations (next, partialNext, addPreprocessor) are disabled on the merged store.\n * You should instead call these methods on the original or merged stores.\n *\n * @template O The type of the original state store\n * @template M The type of the merged state store\n *\n * @experimental\n * This class is experimental and may change in future versions.\n */\nexport class MergedStateStore<\n O extends Record<string, unknown>,\n M extends Record<string, unknown>,\n> extends StateStore<O & M> {\n public readonly original: StateStore<O>;\n public readonly merged: StateStore<M>;\n private cachedOriginalValue: O;\n private cachedMergedValue: M;\n\n constructor({ original, merged }: { original: StateStore<O>; merged: StateStore<M> }) {\n const originalValue = original.getLatestValue();\n const mergedValue = merged.getLatestValue();\n\n super({\n ...originalValue,\n ...mergedValue,\n });\n\n this.cachedOriginalValue = originalValue;\n this.cachedMergedValue = mergedValue;\n\n this.original = original;\n this.merged = merged;\n }\n\n /**\n * Subscribes to changes in the merged state store.\n *\n * This method extends the base subscribe functionality to handle the merged nature of this store:\n * 1. The first subscriber triggers registration of helper subscribers that listen to both source stores\n * 2. Changes from either source store are propagated to this merged store\n * 3. Source store values are cached to prevent unnecessary updates\n *\n * When the first subscriber is added, the method sets up listeners on both original and merged stores.\n * These listeners update the combined store value whenever either source store changes.\n * All subscriptions (helpers and the actual handler) are tracked so they can be properly cleaned up.\n *\n * @param handler - The callback function that will be executed when the state changes\n * @returns An unsubscribe function that, when called, removes the subscription and any helper subscriptions\n */\n public subscribe(handler: Handler<O & M>) {\n const unsubscribeFunctions: Unsubscribe[] = [];\n\n // first subscriber will also register helpers which listen to changes of the\n // \"original\" and \"merged\" stores, combined outputs will be emitted through super.next\n // whenever cached values do not equal (always apart from the initial subscription)\n // since the actual handler subscription is registered after helpers, the actual\n // handler will run only once\n if (!this.handlers.size) {\n const base = (nextValue: O | M) => {\n super.next((currentValue) => ({\n ...currentValue,\n ...nextValue,\n }));\n };\n\n unsubscribeFunctions.push(\n this.original.subscribe((nextValue) => {\n if (nextValue === this.cachedOriginalValue) return;\n this.cachedOriginalValue = nextValue;\n base(nextValue);\n }),\n this.merged.subscribe((nextValue) => {\n if (nextValue === this.cachedMergedValue) return;\n this.cachedMergedValue = nextValue;\n base(nextValue);\n }),\n );\n }\n\n unsubscribeFunctions.push(super.subscribe(handler));\n\n return () => {\n unsubscribeFunctions.forEach((unsubscribe) => unsubscribe());\n };\n }\n\n /**\n * Retrieves the latest combined state from both original and merged stores.\n *\n * This method extends the base getLatestValue functionality to ensure the merged store\n * remains in sync with its source stores even when there are no active subscribers.\n *\n * When there are no handlers registered, the method:\n * 1. Fetches the latest values from both source stores\n * 2. Compares them with the cached values to detect changes\n * 3. If changes are detected, updates the internal value and caches\n * the new source values to maintain consistency\n *\n * This approach ensures that calling getLatestValue() always returns the most\n * up-to-date combined state, even if the merged store hasn't been actively\n * receiving updates through subscriptions.\n *\n * @returns The latest combined state from both original and merged stores\n */\n public getLatestValue() {\n // if there are no handlers registered to MergedStore then the local value might be out-of-sync\n // pull latest and compare against cached - if they differ, cache latest and produce new combined\n if (!this.handlers.size) {\n const originalValue = this.original.getLatestValue();\n const mergedValue = this.merged.getLatestValue();\n\n if (\n originalValue !== this.cachedOriginalValue ||\n mergedValue !== this.cachedMergedValue\n ) {\n this.value = {\n ...originalValue,\n ...mergedValue,\n };\n this.cachedMergedValue = mergedValue;\n this.cachedOriginalValue = originalValue;\n }\n }\n\n return super.getLatestValue();\n }\n\n // override original methods and \"disable\" them\n public next = () => {\n console.warn(\n `${MergedStateStore.name}.next is disabled, call original.next or merged.next instead`,\n );\n };\n public partialNext = () => {\n console.warn(\n `${MergedStateStore.name}.partialNext is disabled, call original.partialNext or merged.partialNext instead`,\n );\n };\n public addPreprocessor() {\n console.warn(\n `${MergedStateStore.name}.addPreprocessor is disabled, call original.addPreprocessor or merged.addPreprocessor instead`,\n );\n return noop;\n }\n}\n", "import type { i18n as I18nInstance } from 'i18next';\n\n/**\n * An i18next instance, as accepted by {@link TranslationTopic} and exposed as\n * `Streami18n.i18nInstance`.\n *\n * Re-exported because it is part of this module's public surface: a consumer implementing a topic, or\n * mocking one in a test, has to be able to name the type. Without this they would reach past\n * `stream-chat` into `i18next` directly and have to declare it themselves \u2014 the same mistake the\n * `moment-timezone` type leak was.\n */\nexport type { I18nInstance };\n\nimport type { LooseTranslateFunction } from './types';\n\n/**\n * i18next post-processor plumbing, for copy that cannot be resolved from a key alone.\n *\n * The motivating case is a notification: what to render depends on a runtime object, not just the key,\n * so `t('translationBuilderTopic.notification', { notification })` dispatches through a *topic* which\n * picks a *translator* based on that object. This is only the mechanism \u2014 the topics and their\n * translators are SDK-specific and stay in the UI SDKs, since they reference SDK key names.\n *\n * A UI SDK may not need this at all: dispatching on the object at the render site instead is perfectly\n * valid, and the React Native SDK does exactly that. The plumbing lives here so either approach is\n * available without a core change.\n */\ntype TopicName = string;\ntype TranslatorName = string;\n\n/**\n * Resolves one case within a topic. Returning `null` means \"not mine\" and lets the next candidate try.\n *\n * `t` is loose rather than catalog-typed: a translator is handed keys by the post-processor at runtime,\n * so it cannot be checked against a specific catalog.\n */\nexport type Translator<O extends Record<string, unknown> = Record<string, unknown>> =\n (params: {\n key: string;\n options: O;\n t: LooseTranslateFunction;\n value: string;\n }) => string | null;\n\nexport type TranslationTopicOptions<\n O extends Record<string, unknown> = Record<string, unknown>,\n> = {\n i18next: I18nInstance;\n translators?: Record<string, Translator<O>>;\n};\n\nexport abstract class TranslationTopic<\n O extends Record<string, unknown> = Record<string, unknown>,\n> {\n protected translators: Map<string, Translator<O>> = new Map();\n protected i18next: I18nInstance;\n\n constructor(protected options: TranslationTopicOptions<O>) {\n this.i18next = options.i18next;\n if (options.translators) {\n Object.entries(options.translators).forEach(([name, translator]) => {\n this.setTranslator(name, translator);\n });\n }\n }\n\n abstract translate(value: string, key: string, options: O): string;\n\n setTranslator = (name: string, translator: Translator<O>) => {\n this.translators.set(name, translator);\n };\n\n removeTranslator = (name: string) => {\n this.translators.delete(name);\n };\n}\n\nexport type TranslationTopicConstructor = new (\n options: TranslationTopicOptions,\n) => TranslationTopic;\n\nconst forwardTranslation: Translator = ({ value }) => value;\n\nexport class TranslationBuilder {\n private topics = new Map<string, TranslationTopic>();\n\n /**\n * Translators registered before their topic exists.\n *\n * Topics are only created during `Streami18n.init()`, but an integrator registers translators against\n * the constructed instance \u2014 so registrations that arrive first are buffered and flushed when the\n * topic appears, rather than silently dropped.\n */\n private translatorRegistrationsBuffer: Record<\n TopicName,\n Record<TranslatorName, Translator>\n > = {};\n\n constructor(private i18next: I18nInstance) {}\n\n registerTopic = (name: TopicName, Topic: TranslationTopicConstructor) => {\n let topic = this.topics.get(name);\n\n if (!topic) {\n topic = new Topic({ i18next: this.i18next });\n this.topics.set(name, topic);\n this.i18next.use({\n name,\n process: (value: string, key: string, options: Record<string, unknown>) => {\n // Re-read from the map rather than closing over `topic`, so `disableTopic` takes effect.\n const registered = this.topics.get(name);\n if (!registered) return value;\n return registered.translate(value, key, options);\n },\n type: 'postProcessor' as const,\n });\n }\n\n const buffered = this.translatorRegistrationsBuffer[name];\n if (buffered) {\n Object.entries(buffered).forEach(([translatorName, translator]) => {\n topic.setTranslator(translatorName, translator);\n });\n delete this.translatorRegistrationsBuffer[name];\n }\n\n return topic;\n };\n\n disableTopic = (topicName: TopicName) => {\n const topic = this.topics.get(topicName);\n if (!topic) return;\n // i18next has no way to remove a post-processor, so it is replaced with a pass-through.\n this.i18next.use({\n name: topicName,\n process: forwardTranslation,\n type: 'postProcessor',\n });\n this.topics.delete(topicName);\n };\n\n getTopic = (topicName: TopicName) => this.topics.get(topicName);\n\n registerTranslators(\n topicName: TopicName,\n translators: Record<TranslatorName, Translator>,\n ) {\n const topic = this.getTopic(topicName);\n\n if (!topic) {\n this.translatorRegistrationsBuffer[topicName] ??= {};\n Object.entries(translators).forEach(([translatorName, translator]) => {\n this.translatorRegistrationsBuffer[topicName][translatorName] = translator;\n });\n return;\n }\n\n Object.entries(translators).forEach(([name, translator]) => {\n topic.setTranslator(name, translator);\n });\n }\n\n removeTranslators(topicName: TopicName, translators: TranslatorName[]) {\n if (this.translatorRegistrationsBuffer[topicName]) {\n translators.forEach((translatorName) => {\n delete this.translatorRegistrationsBuffer[topicName][translatorName];\n });\n }\n\n const topic = this.getTopic(topicName);\n if (!topic) return;\n translators.forEach((name) => {\n topic.removeTranslator(name);\n });\n }\n}\n", "/**\n * The translation dictionaries an instance holds, and the one rule that governs them.\n *\n * Extracted from `Streami18n` because it is a self-contained concern with no dependency on i18next or\n * dayjs: given the SDK's bundled defaults and whatever dictionaries an integrator supplies, produce the\n * dictionary for a language. That makes the layering rule testable on its own rather than only through\n * a fully initialized instance.\n *\n * Deliberately free of i18next concepts \u2014 no namespaces, no resource nesting. `Streami18n` adapts these\n * flat dictionaries to i18next's shape, so the rule below stays readable without knowing that library.\n */\n\n/** The one language whose dictionary always exists, because the bundled copy is English. */\nexport const DEFAULT_LANGUAGE = 'en';\n\nexport class TranslationStore {\n /**\n * The SDK's bundled translation data: the keys that cannot carry an inline `defaultValue` at their\n * call site \u2014 formatter expressions, and prose reaching `t()` as a runtime value.\n */\n private readonly runtimeDefaults: Record<string, string>;\n\n private readonly dictionaries = new Map<string, Record<string, string>>();\n\n private readonly registered = new Set<string>([DEFAULT_LANGUAGE]);\n\n constructor(runtimeDefaults: Record<string, string> = {}) {\n this.runtimeDefaults = runtimeDefaults;\n }\n\n /**\n * Languages an integrator actually supplied a dictionary for.\n *\n * Deliberately narrower than {@link TranslationStore.languages}, which also counts every language\n * created just to carry the bundled defaults. Without the distinction there would be no way to warn\n * that the active language has no translations \u2014 every language would look registered.\n */\n get registeredLanguages(): ReadonlySet<string> {\n return this.registered;\n }\n\n /** Every language with a dictionary, including those carrying only the bundled defaults. */\n get languages(): string[] {\n return [...this.dictionaries.keys()];\n }\n\n /** `language -> dictionary`, for a caller that has to hand them all over at once. */\n entries(): Array<[string, Record<string, string>]> {\n return [...this.dictionaries];\n }\n\n isRegistered(language: string) {\n return this.registered.has(language);\n }\n\n /**\n * Guarantees `language` has a dictionary, and returns it.\n *\n * Called for a language nobody registered, so that it still formats dates and renders the SDK's copy\n * in English rather than raw dotted keys.\n */\n ensure(language: string): Record<string, string> {\n return this.merge(language);\n }\n\n /**\n * Layers a dictionary over what `language` already has, and marks it registered.\n *\n * **Merged, not replaced.** Repeated calls for one language accumulate, and \u2014 the reason this class\n * exists \u2014 the bundled defaults survive a partial dictionary. A bundled key has no inline\n * `defaultValue` at its call site and `fallbackLng` is false, so a language that loses them renders\n * raw dotted keys and unformatted ISO timestamps. That is guarantee G1 of the i18n suite.\n */\n register(language: string, dictionary: Record<string, string>): Record<string, string> {\n const merged = this.merge(language, dictionary);\n this.registered.add(language);\n return merged;\n }\n\n /**\n * Bundled defaults first, then whatever the language already had, then the incoming dictionary \u2014 so\n * an integrator can override a bundled key, and a later registration wins over an earlier one.\n */\n private merge(\n language: string,\n dictionary?: Record<string, string>,\n ): Record<string, string> {\n const merged = {\n ...this.runtimeDefaults,\n ...this.dictionaries.get(language),\n ...dictionary,\n };\n this.dictionaries.set(language, merged);\n return merged;\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * The shared i18n layer, published as `stream-chat/i18n`.\n *\n * Deliberately **not** re-exported from `stream-chat`'s root barrel: this module pulls in `i18next` and\n * `dayjs`, and keeping them out of the root bundle is the entire reason it is a separate entry point.\n * `scripts/bundle.mts` asserts that boundary at build time.\n */\nexport * from './dayjs';\nexport * from './formatters';\nexport * from './languageNames';\nexport * from './Streami18n';\nexport * from './TranslationBuilder';\nexport * from './TranslationStore';\nexport * from './translator';\nexport * from './types';\n", "import Dayjs from 'dayjs';\nimport calendar from 'dayjs/plugin/calendar.js';\nimport duration from 'dayjs/plugin/duration.js';\nimport localeData from 'dayjs/plugin/localeData.js';\nimport localizedFormat from 'dayjs/plugin/localizedFormat.js';\nimport relativeTime from 'dayjs/plugin/relativeTime.js';\nimport timezone from 'dayjs/plugin/timezone.js';\nimport updateLocale from 'dayjs/plugin/updateLocale.js';\nimport utc from 'dayjs/plugin/utc.js';\n\nimport type {\n DateTimeLike,\n DateTimeParserModule,\n TDateTimeParserInput,\n TDateTimeParserOutput,\n} from './types';\n\n/**\n * The calendar-plugin config shape. Not part of dayjs's own `ILocale`, so it has to be declared here.\n *\n * Supplying it is how relative wording (\"heute um\", \"ieri alle\") gets localized \u2014 no dayjs locale file\n * defines `calendar`, which is the single most common surprise when adding a language.\n */\nexport type CalendarFormats = {\n lastDay: string;\n lastWeek: string;\n nextDay: string;\n nextWeek: string;\n sameDay: string;\n sameElse: string;\n};\n\n/**\n * A dayjs locale config, as accepted by `dayjsLocaleConfigForLanguage` and by\n * `registerTranslation`'s third argument.\n *\n * Typing this as a bare `Partial<ILocale>` makes passing a calendar config a TS2345 \"no properties in\n * common\" error, which is exactly the wording an integrator hits first \u2014 hence the explicit\n * `calendar`.\n */\nexport type DayjsLocaleConfig = Partial<ILocale> & { calendar?: CalendarFormats };\n\n/**\n * The dayjs module surface this file drives: our own `dayjs`, or a module an integrator supplied\n * through `DateTimeParser`. Method shorthand, so a real `typeof dayjs` satisfies it -- as function\n * properties these would be checked contravariantly and rejected.\n *\n * `Ls`, `locale` and `updateLocale` are optional because a module only has them once the plugins are\n * registered, and because a non-dayjs parser (a Moment) has a different shape entirely.\n */\ntype DayjsExtendable = object & {\n extend?(plugin: unknown, option?: unknown): unknown;\n Ls?: Record<string, unknown>;\n locale?(preset: unknown, object?: unknown, isLocal?: boolean): unknown;\n updateLocale?(name: string, config: unknown): unknown;\n};\n\n/** The module locale helpers default to, when a caller does not name one. */\nconst ownDayjs = () => Dayjs as unknown as DayjsExtendable;\n\n/**\n * The English locale skeleton a custom locale is merged over, so a partial config still has month and\n * weekday names to fall back on.\n */\nconst EN_LOCALE_FALLBACK = {\n /**\n * `formats` and `relativeTime` are empty on purpose, and are not removable: `Dayjs.locale()` takes an\n * `ILocale`, which declares both as required, so omitting them is a type error. Empty means \"inherit\n * dayjs's own defaults\", which is the intent.\n */\n formats: {},\n relativeTime: {},\n months: [\n 'January',\n 'February',\n 'March',\n 'April',\n 'May',\n 'June',\n 'July',\n 'August',\n 'September',\n 'October',\n 'November',\n 'December',\n ],\n weekdays: [\n 'Sunday',\n 'Monday',\n 'Tuesday',\n 'Wednesday',\n 'Thursday',\n 'Friday',\n 'Saturday',\n ],\n};\n\n/**\n * The plugins the formatters depend on, in dependency order: `timezone` builds on `utc`.\n */\nconst REQUIRED_PLUGINS = [\n updateLocale,\n utc,\n timezone,\n localizedFormat,\n calendar,\n localeData,\n relativeTime,\n duration,\n];\n\n/**\n * Modules already extended. A `WeakSet` rather than a boolean because the module to extend is not\n * always ours -- see {@link ensureDayjsPlugins}.\n */\nconst extendedModules = new WeakSet<object>();\n\n/**\n * Registers the dayjs plugins the formatters need, once per module.\n *\n * Takes the module to extend, defaulting to our own `dayjs`. Passing it matters: an integrator\n * supplying `DateTimeParser` may hand over a *different physical copy* of dayjs, and extending ours\n * leaves theirs without the plugins. That failure is silent and total -- `.calendar()` is simply\n * absent, and `format('LT')` returns the literal string `\"LT\"` because `localizedFormat` never\n * registered the token.\n *\n * Deliberately **not** done at module scope. Module-scope `Dayjs.extend(...)` is a side effect, which\n * would force `stream-chat` to declare `sideEffects` and would make importing this module do work\n * whether or not anything uses it. Calling it from both the constructor and `defaultDateTimeParser`\n * covers the two ways the formatters can be reached, including a standalone `getDateString()` call\n * with no `Streami18n` instance in play.\n *\n * Idempotent twice over: tracked here, and dayjs itself no-ops a repeated `extend` via the plugin's\n * `$i` marker. The module is recorded *after* the extends run, so a throw does not leave it marked as\n * done.\n *\n * `timezone` is included because it depends on `utc` and callers can set `timezone` at any point;\n * registering it lazily on first use would leave the plugin missing for an instance that only sets\n * `timezone` later.\n */\nexport const ensureDayjsPlugins = (\n module: DayjsExtendable = Dayjs as unknown as DayjsExtendable,\n) => {\n if (extendedModules.has(module)) return;\n if (typeof module.extend !== 'function') return;\n\n for (const plugin of REQUIRED_PLUGINS) module.extend(plugin);\n extendedModules.add(module);\n};\n\n/**\n * The parser used when none is supplied, and by `getDateString()` called outside an instance.\n *\n * Note there is no `import 'dayjs/locale/en'` anywhere: dayjs bundles `en` and has it registered\n * before any import runs (`Object.keys(Dayjs.Ls)` is already `['en']`), so that import \u2014 which both UI\n * SDKs carried \u2014 was a no-op.\n */\nexport const defaultDateTimeParser = (input?: TDateTimeParserInput) => {\n ensureDayjsPlugins();\n return Dayjs(input);\n};\n\n/**\n * The dayjs module itself, with plugins registered.\n *\n * `Streami18n.DateTimeParser` has to be the *module*, not a parse function, because\n * `durationFormatter` calls `.duration()` \u2014 which lives on the module, not on a parsed instance.\n */\nexport const getDefaultDateTimeParserModule = (): DateTimeParserModule => {\n ensureDayjsPlugins();\n return Dayjs as unknown as DateTimeParserModule;\n};\n\n/**\n * Registers or updates a dayjs locale without changing the global locale.\n *\n * Takes the module to register on, defaulting to ours. Passing it matters for the same reason\n * {@link ensureDayjsPlugins} takes one: an integrator supplying `DateTimeParser` may hand over a second\n * physical copy of dayjs, and registering on ours would leave the locale absent from the module that\n * actually formats the dates -- so a `calendar` config or a `dayjsLocaleConfigForLanguage` would be\n * silently ignored.\n */\nexport const addOrUpdateDayjsLocale = (\n language: string,\n config: DayjsLocaleConfig,\n module: DayjsExtendable = ownDayjs(),\n) => {\n ensureDayjsPlugins(module);\n\n if (dayjsLocaleExists(language, module)) {\n module.updateLocale?.(language, { ...config });\n return;\n }\n // Merged over the English skeleton so missing keys still resolve.\n module.locale?.({ name: language, ...EN_LOCALE_FALLBACK, ...config }, undefined, true);\n};\n\nexport const dayjsLocaleExists = (\n language: string,\n module: DayjsExtendable = ownDayjs(),\n) => Object.keys(module.Ls ?? {}).includes(language);\n\n/**\n * Whether a parser is dayjs, as opposed to a Moment the integrator brought.\n *\n * A property check rather than the `.extend !== undefined` both UI SDKs used, which throws on `null`.\n */\nexport const isDayjsLike = (parser: unknown): parser is DateTimeParserModule =>\n typeof parser === 'function' &&\n typeof (parser as DateTimeParserModule).extend === 'function';\n\n/** Whether a parser supports `.tz()`, i.e. dayjs with the timezone plugin, or moment-timezone. */\nexport const supportsTimezone = (parser: unknown): boolean =>\n typeof parser === 'function' && typeof (parser as { tz?: unknown }).tz === 'function';\n\nexport const isDate = (value: TDateTimeParserOutput): value is Date =>\n value instanceof Date;\n\nexport const isNumberOrString = (\n value: TDateTimeParserOutput,\n): value is number | string => typeof value === 'number' || typeof value === 'string';\n\n/** Whether a parser output is a dayjs or Moment object rather than a raw Date/string/number. */\nexport const isDayOrMoment = (value: TDateTimeParserOutput): value is DateTimeLike =>\n typeof value === 'object' &&\n value !== null &&\n !(value instanceof Date) &&\n typeof (value as DateTimeLike).format === 'function';\n", "import type {\n AnyTranslationCatalog,\n DynamicTranslationKey,\n StreamTFunctionFor,\n} from './types';\n\n/**\n * Brands a runtime-resolved string as a translation key.\n *\n * The brand on {@link DynamicTranslationKey} is required, so this is the only way to pass a key the\n * compiler cannot see \u2014 which keeps every such escape deliberate and greppable.\n */\nexport const asDynamicKey = (key: string): DynamicTranslationKey =>\n key as DynamicTranslationKey;\n\n/** Matches `{{ name }}` / `{{name}}`, allowing dots so `{{ user.name }}` interpolates too. */\nconst INTERPOLATION_PATTERN = /\\{\\{\\s*([\\w.]+)\\s*\\}\\}/g;\n\nconst interpolate = (copy: string, values: Record<string, unknown>) =>\n copy.replace(INTERPOLATION_PATTERN, (whole, name: string) =>\n values[name] === undefined ? whole : String(values[name]),\n );\n\n/**\n * The `t` in force before i18next has initialized, and the default for a UI SDK's translation context.\n *\n * It has to honour the inline `defaultValue`: every prose call site passes its English copy as the\n * second argument, so echoing the key back would flash raw dotted paths on the first frame \u2014 and would\n * render them permanently anywhere a context default is in play (a component used outside the SDK's\n * provider).\n *\n * A ~30-line stand-in for i18next, deliberately: pulling i18next in just to render the first frame\n * would defeat keeping it out of the default path.\n */\nexport const createDefaultTranslatorFunction = <\n C extends AnyTranslationCatalog = AnyTranslationCatalog,\n Bundled extends string = never,\n>(): StreamTFunctionFor<C, Bundled> =>\n ((\n key: string,\n defaultValueOrOptions?: string | Record<string, unknown>,\n maybeOptions?: Record<string, unknown>,\n ) => {\n // Prose: the copy arrives positionally.\n if (typeof defaultValueOrOptions === 'string') {\n return maybeOptions\n ? interpolate(defaultValueOrOptions, maybeOptions)\n : defaultValueOrOptions;\n }\n\n const options = defaultValueOrOptions ?? maybeOptions;\n if (!options) return key;\n\n // Plural call sites pass their copy as `defaultValue_one` / `defaultValue_other` inside the\n // options object, so a bare `defaultValue` check would still leak the raw key for them. English\n // only distinguishes one from other; a registered language's own categories are irrelevant here,\n // since this function is only ever in play before i18next has initialized.\n const resolved =\n (options.count === 1 ? options.defaultValue_one : options.defaultValue_other) ??\n options.defaultValue;\n\n return typeof resolved === 'string' ? interpolate(resolved, options) : key;\n }) as StreamTFunctionFor<C, Bundled>;\n\n/**\n * Wraps an integrator's `parseMissingKeyHandler` so it only sees genuinely missing translations.\n *\n * i18next counts every prose key as missing \u2014 they render from the inline `defaultValue`, not from a\n * resource bundle \u2014 and lets the handler's return value replace the rendered string. An unguarded\n * handler therefore blanks out most of the UI. A resolved default arrives as the second argument,\n * which is how the two cases are told apart.\n */\nexport const guardMissingKeyHandler =\n (handler: (key: string, defaultValue?: string) => string) =>\n (key: string, defaultValue?: string) =>\n typeof defaultValue === 'string' ? defaultValue : handler(key, defaultValue);\n", "import { isDate, isDayOrMoment, isNumberOrString } from './dayjs';\nimport type { CalendarFormats } from './dayjs';\nimport { asDynamicKey } from './translator';\nimport type {\n DurationFormatterOptions,\n FormatterContext,\n FormatterFactory,\n LooseTranslateFunction,\n PredefinedFormatters,\n TDateTimeParser,\n TimestampFormatterOptions,\n} from './types';\n\n/**\n * The `relativeTime.*` keys this module renders, with their English copy.\n *\n * Exported as a catalog fragment because the *call sites* are here, in core, while the *catalog* is\n * generated from each UI SDK's own source. Without this an SDK's codegen cannot see these keys, so they\n * would drop out of its `TranslationCatalog` and an integrator could no longer type them in a\n * dictionary \u2014 i.e. could no longer translate relative dates at all. A UI SDK intersects this into its\n * catalog type.\n *\n * Plurals appear as `_one` / `_other` because that is how a dictionary supplies them; English needs no\n * distinction, but a language with different forms does.\n */\nexport const RELATIVE_TIME_CATALOG = {\n 'relativeTime.daysAgo_one': '{{ count }}d ago',\n 'relativeTime.daysAgo_other': '{{ count }}d ago',\n 'relativeTime.today': 'Today',\n 'relativeTime.weeksAgo_one': '{{ count }}w ago',\n 'relativeTime.weeksAgo_other': '{{ count }}w ago',\n 'relativeTime.yesterday': 'Yesterday',\n} as const;\n\nexport type RelativeTimeCatalog = typeof RELATIVE_TIME_CATALOG;\n\n/** Defaults for the relative-compact window, matching what both UI SDKs shipped. */\nconst DEFAULT_RELATIVE_COMPACT_MAX_DAYS = 6;\nconst DEFAULT_RELATIVE_COMPACT_MAX_WEEKS = 3;\n\n/**\n * Whether a string is not a date this module can render.\n *\n * `!Date.parse(value)` would be the obvious spelling and is wrong: `Date.parse` returns `0` for the\n * Unix epoch, which is falsy, so a perfectly valid `'1970-01-01T00:00:00.000Z'` was classified as junk\n * and dropped -- rendered as `''` by the formatter and as `null` by `getDateString`.\n */\nconst isUnparseableDateString = (value: string) => Number.isNaN(Date.parse(value));\n\n/**\n * Coerces the week-rounding option, which arrives as text from an i18next format expression.\n *\n * Anything unrecognised falls back to `floor`, so a typo degrades to the default rather than throwing\n * inside a formatter, where the only visible symptom would be a blank timestamp.\n */\nconst asWeekRounding = (value: unknown): 'ceil' | 'floor' =>\n value === 'ceil' || value === true ? 'ceil' : 'floor';\n\n/**\n * Coerces a numeric formatter option.\n *\n * These arrive as strings, not numbers: they are written inside an i18next format expression\n * (`{{ timestamp | timestampFormatter(relativeCompactMaxDays: 10) }}`), and i18next hands every\n * argument over as text. The declared type says `number` because that is what a programmatic caller\n * passes, so both have to be accepted.\n */\nconst asNumber = (value: unknown, fallback: number) => {\n const parsed =\n typeof value === 'number' ? value : Number.parseInt(String(value ?? ''), 10);\n return Number.isFinite(parsed) ? parsed : fallback;\n};\n\n/**\n * Per-key calendar config may arrive as an object or as a JSON string.\n *\n * The string case is not a quirk to clean up: bundled defaults embed the config inside the i18next\n * expression itself, so by the time it reaches a formatter it is text.\n */\nconst parseCalendarFormats = (\n value: TimestampFormatterOptions['calendarFormats'],\n logger: (message?: string) => void,\n): Record<string, string> | undefined => {\n if (!value) return undefined;\n if (typeof value !== 'string') return value;\n try {\n return JSON.parse(value) as Record<string, string>;\n } catch (error) {\n // Reported through the instance's logger, not through `translate` -- a diagnostic is not copy.\n logger(\n `Streami18n: calendarFormats is not valid JSON, ignoring it: ${value} (${\n error instanceof Error ? error.message : String(error)\n })`,\n );\n return undefined;\n }\n};\n\n/**\n * \"Today\" / \"Yesterday\" / \"3d ago\" / \"2w ago\", falling back to a short date.\n *\n * Every word goes through `t()`. The React Native SDK shipped this as a standalone formatter with the\n * English baked in, which no dictionary could translate \u2014 and because the wording lived in a formatter\n * body rather than a catalog value, the codegen's English-prose guard never saw it either.\n */\nconst relativeCompactDateString = ({\n maxDays,\n maxWeeks,\n tDateTimeParser,\n timestamp,\n translate,\n weekRounding,\n}: {\n maxDays: number;\n maxWeeks: number;\n tDateTimeParser: TDateTimeParser;\n timestamp: string | Date;\n translate: LooseTranslateFunction;\n weekRounding: 'ceil' | 'floor';\n}): string | null => {\n const parsed = tDateTimeParser(timestamp as string);\n if (!isDayOrMoment(parsed)) return null;\n\n const now = tDateTimeParser(new Date());\n if (!isDayOrMoment(now)) return null;\n\n const daysAgo = now.startOf('day').diff(parsed.startOf('day'), 'day');\n\n // A future timestamp is not \"Today\" \u2014 fall straight through to a date.\n if (daysAgo < 0) return parsed.format('DD/MM/YY');\n\n if (daysAgo === 0)\n return translate('relativeTime.today', RELATIVE_TIME_CATALOG['relativeTime.today']);\n if (daysAgo === 1)\n return translate(\n 'relativeTime.yesterday',\n RELATIVE_TIME_CATALOG['relativeTime.yesterday'],\n );\n // Plural defaults rather than one `defaultValue`: English needs no distinction here, but a language\n // whose plural categories differ has to be able to supply `_one` / `_other` and have i18next select.\n if (daysAgo <= maxDays) {\n return translate('relativeTime.daysAgo', {\n count: daysAgo,\n defaultValue_one: RELATIVE_TIME_CATALOG['relativeTime.daysAgo_one'],\n defaultValue_other: RELATIVE_TIME_CATALOG['relativeTime.daysAgo_other'],\n });\n }\n\n // `maxWeeks > 0` and a full week elapsed, both required: with `maxWeeks: 0` a 3-day-old timestamp\n // has `Math.floor(3 / 7) === 0`, which would otherwise match and render \"0w ago\".\n //\n // The two roundings bound the window differently, which is the whole reason both exist: `floor`\n // stops on the week *count*, `ceil` on the day count. See `relativeCompactWeekRounding`.\n const weeksAgo =\n weekRounding === 'ceil' ? Math.ceil(daysAgo / 7) : Math.floor(daysAgo / 7);\n const withinWindow =\n weekRounding === 'ceil' ? daysAgo <= maxWeeks * 7 : weeksAgo <= maxWeeks;\n\n if (maxWeeks > 0 && daysAgo >= 7 && withinWindow) {\n return translate('relativeTime.weeksAgo', {\n count: weeksAgo,\n defaultValue_one: RELATIVE_TIME_CATALOG['relativeTime.weeksAgo_one'],\n defaultValue_other: RELATIVE_TIME_CATALOG['relativeTime.weeksAgo_other'],\n });\n }\n\n return parsed.format('DD/MM/YY');\n};\n\nconst timestampFormatter: FormatterFactory<string | Date> =\n ({ logger, tDateTimeParser, translate }: FormatterContext) =>\n (value, _lng, options) => {\n const {\n calendar,\n calendarFormats,\n format,\n relativeCompact,\n relativeCompactMaxDays,\n relativeCompactMaxWeeks,\n relativeCompactWeekRounding,\n } = options as TimestampFormatterOptions;\n\n // Nothing renderable: empty rather than the stringified value. `null` used to come out as the\n // literal text \"null\" and an unparseable string as \"Invalid Date\", both of which are junk a user\n // can see. `getDateString` has always guarded this; the formatter is a separate path and did not.\n if (value === null || value === undefined) return '';\n if (typeof value === 'string' && isUnparseableDateString(value)) return '';\n\n if (relativeCompact) {\n const relative = relativeCompactDateString({\n maxDays: asNumber(relativeCompactMaxDays, DEFAULT_RELATIVE_COMPACT_MAX_DAYS),\n maxWeeks: asNumber(relativeCompactMaxWeeks, DEFAULT_RELATIVE_COMPACT_MAX_WEEKS),\n tDateTimeParser,\n timestamp: value,\n translate,\n weekRounding: asWeekRounding(relativeCompactWeekRounding),\n });\n if (relative !== null) return relative;\n }\n\n const parsed = tDateTimeParser(value as string);\n\n if (isDayOrMoment(parsed)) {\n if (calendar && typeof parsed.calendar === 'function') {\n return parsed.calendar(undefined, parseCalendarFormats(calendarFormats, logger));\n }\n return parsed.format(format);\n }\n if (isDate(parsed)) return parsed.toDateString();\n if (isNumberOrString(parsed)) return String(parsed);\n return '';\n };\n\n/**\n * Renders a length of time, e.g. `600000` -> \"10 minutes\".\n *\n * Goes through the date library's `.duration()` rather than parsing the number as a timestamp \u2014 which\n * would read 600000 as \"10 minutes past the epoch\" and render \"57 years ago\".\n */\nconst durationFormatter: FormatterFactory<number | string> =\n ({ dateTimeParser }: FormatterContext) =>\n (value, _lng, options) => {\n const { format, withSuffix } = options as DurationFormatterOptions;\n if (typeof dateTimeParser.duration !== 'function') return String(value);\n\n const duration = dateTimeParser.duration(value as number);\n // Only dayjs's duration plugin has `.format`; moment durations humanize only.\n if (format && typeof duration.format === 'function') return duration.format(format);\n return duration.humanize(Boolean(withSuffix));\n };\n\nconst fromNowFormatter: FormatterFactory<string | Date> =\n ({ tDateTimeParser }: FormatterContext) =>\n (value, _lng, options) => {\n if (value === null || value === undefined) return '';\n const parsed = tDateTimeParser(value as string);\n if (!isDayOrMoment(parsed) || typeof parsed.fromNow !== 'function') return '';\n return parsed.fromNow(\n Boolean((options as { withoutSuffix?: boolean }).withoutSuffix),\n );\n };\n\n/** The formatters registered with i18next by default. */\nexport const predefinedFormatters: PredefinedFormatters = {\n durationFormatter,\n fromNowFormatter,\n timestampFormatter,\n};\n\n/* ------------------------------------------------------------------------------------------------\n * getDateString\n * ---------------------------------------------------------------------------------------------- */\n\nexport type GetDateStringParams = TimestampFormatterOptions & {\n /** The timestamp to render. */\n messageCreatedAt?: string | Date;\n /** An integrator-supplied override, which wins over everything else. */\n formatDate?: (date: Date) => string;\n /** The key carrying a formatter expression for this timestamp, if there is one. */\n timestampTranslationKey?: string;\n t?: LooseTranslateFunction;\n tDateTimeParser?: TDateTimeParser;\n};\n\n/**\n * Resolves a timestamp to a display string.\n *\n * Resolution order, and why: an integrator's `formatDate` wins outright; then the translation key, so\n * a language can restyle the timestamp without touching component props; then the parser. Returns\n * `null` rather than a placeholder when there is nothing sensible to render, so callers can omit the\n * element entirely.\n */\nexport const getDateString = ({\n calendar,\n calendarFormats,\n format,\n formatDate,\n messageCreatedAt,\n relativeCompact,\n relativeCompactMaxDays,\n relativeCompactMaxWeeks,\n relativeCompactWeekRounding,\n t,\n tDateTimeParser,\n timestampTranslationKey,\n}: GetDateStringParams): string | number | null => {\n if (\n !messageCreatedAt ||\n (typeof messageCreatedAt === 'string' && isUnparseableDateString(messageCreatedAt))\n ) {\n return null;\n }\n\n if (formatDate) return formatDate(new Date(messageCreatedAt));\n\n // Before the translation-key path, so a caller can ask for relative-compact rendering directly\n // rather than only through a key whose expression sets it. Falls through when it declines (a future\n // date, or no dayjs-like parser), so the normal formatting still applies.\n if (relativeCompact && t && tDateTimeParser) {\n const relative = relativeCompactDateString({\n maxDays: asNumber(relativeCompactMaxDays, DEFAULT_RELATIVE_COMPACT_MAX_DAYS),\n maxWeeks: asNumber(relativeCompactMaxWeeks, DEFAULT_RELATIVE_COMPACT_MAX_WEEKS),\n tDateTimeParser,\n timestamp: messageCreatedAt,\n translate: t,\n weekRounding: asWeekRounding(relativeCompactWeekRounding),\n });\n if (relative) return relative;\n }\n\n if (t && timestampTranslationKey) {\n // Only forward options that were actually supplied.\n //\n // These reach i18next as interpolation values and are merged over the arguments the key's own\n // formatter expression declares \u2014 so passing `format: undefined` explicitly *overrides*\n // `timestampFormatter(format: HH:mm)` with nothing, and the timestamp renders as a raw ISO string.\n // The caller is usually a component forwarding optional props, so most of these are undefined most\n // of the time.\n const overrides: Record<string, unknown> = {};\n const supplied = {\n calendar,\n calendarFormats,\n format,\n relativeCompact,\n relativeCompactMaxDays,\n relativeCompactMaxWeeks,\n relativeCompactWeekRounding,\n };\n for (const [key, value] of Object.entries(supplied)) {\n if (value !== undefined) overrides[key] = value;\n }\n\n const translated = t(asDynamicKey(timestampTranslationKey), {\n ...overrides,\n // A `Date`, not the raw value. Integrators override a `timestamp.*` key with their own\n // formatter, and those read `options.timestamp` expecting a Date \u2014 passing the string through\n // breaks them with `timestamp.toISOString is not a function`.\n timestamp: new Date(messageCreatedAt),\n });\n // i18next echoes the key back when nothing resolved it, which is how a miss is detected.\n if (translated !== timestampTranslationKey) return translated;\n }\n\n if (!tDateTimeParser) return null;\n\n const parsed = tDateTimeParser(messageCreatedAt);\n\n if (isDayOrMoment(parsed)) {\n if (calendar && typeof parsed.calendar === 'function') {\n return parsed.calendar(\n undefined,\n typeof calendarFormats === 'string' ? undefined : calendarFormats,\n );\n }\n return parsed.format(format);\n }\n if (isDate(parsed)) return parsed.toDateString();\n if (isNumberOrString(parsed)) return parsed;\n return null;\n};\n\n/**\n * The same resolution as {@link getDateString}, but always spelling the date out in full.\n *\n * A screen reader announcing \"14:32\" with no date is ambiguous, so the a11y string ignores the compact\n * and calendar options a visual timestamp uses.\n */\nexport const getDateStringForA11y = ({\n messageCreatedAt,\n t,\n tDateTimeParser,\n timestampTranslationKey,\n}: Pick<\n GetDateStringParams,\n 'messageCreatedAt' | 't' | 'tDateTimeParser' | 'timestampTranslationKey'\n>): string | number | null =>\n getDateString({\n calendar: false,\n format: 'LLLL',\n messageCreatedAt,\n t,\n tDateTimeParser,\n timestampTranslationKey,\n });\n\n/**\n * Calendar wording used by {@link getCalendarDateStringForA11y}, for the one bundled locale.\n *\n * Only English ships. A language an integrator registers supplies its own wording through\n * `dayjsLocaleConfigForLanguage` (or `registerTranslation`'s third argument) \u2014 a per-locale block here\n * is useless on its own without the matching `dayjs/locale/xx` beside it.\n */\nexport const A11Y_CALENDAR_FORMATS: Record<string, CalendarFormats> = {\n en: {\n lastDay: '[Yesterday]',\n lastWeek: 'dddd',\n nextDay: '[Tomorrow]',\n nextWeek: 'dddd [at] LT',\n sameDay: '[Today]',\n sameElse: 'L',\n },\n};\n\nexport type GetCalendarDateStringForA11yParams = {\n /**\n * Calendar-format overrides applied over the locale defaults and the `sameElse: 'LL'` substitution.\n * Use it where the visible date deliberately diverges \u2014 a channel preview shows `sameDay: 'LT'`, the\n * time rather than \"Today\".\n */\n calendarFormatOverrides?: Partial<CalendarFormats>;\n /** Calendar wording per language. Defaults to {@link A11Y_CALENDAR_FORMATS}. */\n calendarFormats?: Record<string, CalendarFormats>;\n messageCreatedAt?: string | Date;\n tDateTimeParser?: TDateTimeParser;\n /**\n * The UI language, used to pick calendar wording. Plain `string`: it indexes `calendarFormats`, which\n * an integrator extends for whatever language they registered \u2014 not `stream-chat`'s\n * auto-translation `TranslationLanguage` union.\n */\n userLanguage?: string;\n};\n\n/**\n * A TTS-friendly calendar string, preserving relative wording.\n *\n * Distinct from {@link getDateStringForA11y}, which spells the date out in full via `LLLL`. Both exist\n * because the two UI SDKs arrived at different answers and both are defensible: this one keeps\n * \"Today\"/\"Yesterday\"/weekday names from the locale's calendar and substitutes `LL` (\"April 8, 2026\")\n * only into the `sameElse` slot, because iOS VoiceOver reads a numeric date like \"04/08/2026\"\n * character by character. Do not collapse them into one \u2014 that would silently change one SDK's\n * announced labels.\n *\n * Returns `undefined` when there is nothing to announce, including when the parser has no calendar\n * plugin, so the caller omits the label rather than announcing a malformed date.\n */\nexport const getCalendarDateStringForA11y = ({\n calendarFormatOverrides,\n calendarFormats = A11Y_CALENDAR_FORMATS,\n messageCreatedAt,\n tDateTimeParser,\n userLanguage,\n}: GetCalendarDateStringForA11yParams): string | undefined => {\n if (\n !messageCreatedAt ||\n (typeof messageCreatedAt === 'string' && isUnparseableDateString(messageCreatedAt)) ||\n !tDateTimeParser\n ) {\n return undefined;\n }\n\n const parsed = tDateTimeParser(messageCreatedAt);\n if (!isDayOrMoment(parsed) || !parsed.calendar) return undefined;\n\n const localeFormats =\n (userLanguage && calendarFormats[userLanguage]) || calendarFormats.en;\n\n return parsed.calendar(undefined, {\n ...localeFormats,\n sameElse: 'LL',\n ...calendarFormatOverrides,\n });\n};\n", "import type { TranslationLanguage } from '../types';\n\n/**\n * The human-readable name of each language the API can auto-translate a message into.\n *\n * These are display copy for a **core-owned** set: `message.i18n.language` is typed\n * {@link TranslationLanguage}, so core defines which languages exist and therefore owns their names\n * too. A UI SDK uses them to say \"Translated from German\" rather than \"Translated from de\".\n *\n * `satisfies Record<TranslationLanguage, string>` is the drift gate, and it works in both\n * directions: adding a language to the API union fails to compile until a name is supplied here, and a\n * name for a language the union does not contain is rejected as an excess property. Before this lived\n * in core, each UI SDK hand-maintained its own copy with nothing tying it to the union \u2014 so a miss\n * could only be detected at runtime, by comparing the rendered string against the key.\n *\n * Names are in English on purpose. A language picker conventionally shows each language endonymously\n * (\"Deutsch\", not \"German\"), but this is the *source* language of an auto-translated message rendered\n * inside a sentence in the reader's own language, so it has to agree with the surrounding copy. An\n * integrator wanting endonyms overrides the `language.*` keys.\n */\nexport const LANGUAGE_NAMES = {\n af: 'Afrikaans',\n am: 'Amharic',\n ar: 'Arabic',\n az: 'Azerbaijani',\n bg: 'Bulgarian',\n bn: 'Bengali',\n bs: 'Bosnian',\n cs: 'Czech',\n da: 'Danish',\n de: 'German',\n el: 'Greek',\n en: 'English',\n es: 'Spanish',\n 'es-MX': 'Spanish (Mexico)',\n et: 'Estonian',\n fa: 'Persian',\n 'fa-AF': 'Dari',\n fi: 'Finnish',\n fr: 'French',\n 'fr-CA': 'French (Canada)',\n ha: 'Hausa',\n he: 'Hebrew',\n hi: 'Hindi',\n hr: 'Croatian',\n ht: 'Haitian Creole',\n hu: 'Hungarian',\n id: 'Indonesian',\n it: 'Italian',\n ja: 'Japanese',\n ka: 'Georgian',\n ko: 'Korean',\n lt: 'Lithuanian',\n lv: 'Latvian',\n ms: 'Malay',\n nl: 'Dutch',\n no: 'Norwegian',\n pl: 'Polish',\n ps: 'Pashto',\n pt: 'Portuguese',\n ro: 'Romanian',\n ru: 'Russian',\n sk: 'Slovak',\n sl: 'Slovenian',\n so: 'Somali',\n sq: 'Albanian',\n sr: 'Serbian',\n sv: 'Swedish',\n sw: 'Swahili',\n ta: 'Tamil',\n th: 'Thai',\n tl: 'Tagalog',\n tr: 'Turkish',\n uk: 'Ukrainian',\n ur: 'Urdu',\n vi: 'Vietnamese',\n zh: 'Chinese (Simplified)',\n 'zh-TW': 'Chinese (Traditional)',\n} as const satisfies Record<TranslationLanguage, string>;\n\n/**\n * The `language.*` slice of a translation catalog.\n *\n * A UI SDK intersects this into its own generated catalog, which makes `t('language.de')` a checked\n * key rather than something that has to go through `asDynamicKey()`:\n *\n * ```ts\n * type TranslationCatalog = GeneratedCatalog & LanguageNameCatalog;\n * ```\n */\nexport type LanguageNameCatalog = {\n [K in keyof typeof LANGUAGE_NAMES as `language.${K & string}`]: (typeof LANGUAGE_NAMES)[K];\n};\n\n/**\n * {@link LANGUAGE_NAMES} keyed the way a catalog is, ready to merge into an SDK's bundled defaults.\n *\n * These keys are resolved from a runtime value (the message's source language), so there is no call\n * site to carry an inline default \u2014 which is exactly why they have to ship as data.\n */\nexport const languageNameDefaults: Record<string, string> = Object.fromEntries(\n Object.entries(LANGUAGE_NAMES).map(([code, name]) => [`language.${code}`, name]),\n);\n", "import i18next from 'i18next';\nimport type { i18n as I18nInstance, InitOptions } from 'i18next';\n\nimport { StateStore } from '../store';\nimport {\n addOrUpdateDayjsLocale,\n dayjsLocaleExists,\n ensureDayjsPlugins,\n getDefaultDateTimeParserModule,\n isDayjsLike,\n supportsTimezone,\n} from './dayjs';\nimport type { DayjsLocaleConfig } from './dayjs';\nimport { predefinedFormatters } from './formatters';\nimport { TranslationBuilder } from './TranslationBuilder';\nimport type { TranslationTopicConstructor } from './TranslationBuilder';\nimport { DEFAULT_LANGUAGE, TranslationStore } from './TranslationStore';\nimport {\n asDynamicKey,\n createDefaultTranslatorFunction,\n guardMissingKeyHandler,\n} from './translator';\nimport type {\n AnyTranslationCatalog,\n CustomFormatters,\n DateTimeParserModule,\n FormatterContext,\n LooseTranslateFunction,\n PredefinedFormatters,\n StreamTFunctionFor,\n TDateTimeParser,\n TranslationDictionaryOf,\n} from './types';\n\nconst DEFAULT_NAMESPACE = 'translation';\n\nexport type Streami18nOptions<C extends AnyTranslationCatalog = AnyTranslationCatalog> = {\n /** A dayjs or moment module. Defaults to dayjs with the required plugins registered. */\n DateTimeParser?: DateTimeParserModule;\n dayjsLocaleConfigForLanguage?: DayjsLocaleConfig;\n debug?: boolean;\n /** Keep dates in English regardless of the active language. */\n disableDateTimeTranslations?: boolean;\n formatters?: Partial<PredefinedFormatters> & CustomFormatters;\n /**\n * Any i18next `InitOptions`. Applied over the SDK's defaults, so it can reach settings the SDK does\n * not surface. `parseMissingKeyHandler` supplied here is guarded the same way as the top-level\n * option.\n */\n i18nextConfigOverrides?: Partial<InitOptions>;\n language?: string;\n logger?: (message?: string) => void;\n /**\n * Called only for keys that are genuinely missing \u2014 one with no inline default and no bundled\n * value. See {@link guardMissingKeyHandler} for why it cannot be passed straight to i18next.\n */\n parseMissingKeyHandler?: (key: string, defaultValue?: string) => string;\n /**\n * The SDK's bundled translation data: the keys that cannot carry an inline `defaultValue` at their\n * call site.\n *\n * Injected rather than imported because the catalog belongs to the UI SDK, not to core. Layered\n * under **every** language, which is what stops a partial dictionary from knocking out formatter\n * keys.\n */\n runtimeDefaults?: Record<string, string>;\n /** A valid TZ identifier, e.g. `Europe/Prague`. */\n timezone?: string;\n /**\n * Post-processor topics for copy that cannot be resolved from a key alone \u2014 see\n * {@link TranslationBuilder}. The key here must match the post-processor name in the translation\n * value, i.e. `{{ value, topicName }}`.\n */\n translationBuilderTopics?: Record<string, TranslationTopicConstructor>;\n translationsForLanguage?: TranslationDictionaryOf<C>;\n};\n\nexport type Streami18nState<\n C extends AnyTranslationCatalog = AnyTranslationCatalog,\n Bundled extends string = never,\n> = {\n initialized: boolean;\n language: string;\n t: StreamTFunctionFor<C, Bundled>;\n tDateTimeParser: TDateTimeParser;\n};\n\n/**\n * Wrapper around [i18next](https://www.i18next.com/) for Stream's translations. A UI SDK passes an\n * instance to its `<Chat>` component to control language and copy. Only English ships; every other\n * language comes from the integrator, and a partial dictionary is safe.\n *\n * ```ts\n * import 'dayjs/locale/de';\n *\n * const i18n = new Streami18n({ language: 'de' });\n * i18n.registerTranslation('de', de, { calendar: { sameDay: '[heute um] LT', ... } });\n * ```\n *\n * No dayjs locale file defines `calendar` \u2014 that field belongs to the calendar plugin \u2014 so a new\n * language needs both the locale import and a `calendar` config, or relative dates render English\n * scaffolding around translated day names.\n */\nexport class Streami18n<\n C extends AnyTranslationCatalog = AnyTranslationCatalog,\n Bundled extends string = never,\n> {\n readonly i18nInstance: I18nInstance = i18next.createInstance();\n\n readonly state: StateStore<Streami18nState<C, Bundled>>;\n\n readonly translationBuilder: TranslationBuilder;\n\n readonly logger: (message?: string) => void;\n readonly DateTimeParser: DateTimeParserModule;\n readonly formatters: PredefinedFormatters & CustomFormatters;\n readonly timezone?: string;\n\n private readonly translations: TranslationStore;\n\n /** Applied when the language becomes active, not on registration: `Dayjs.locale()` is global. */\n private readonly dayjsLocales: Record<string, DayjsLocaleConfig> = {};\n\n private readonly translationBuilderTopics: Record<string, TranslationTopicConstructor>;\n private readonly disableDateTimeTranslations: boolean;\n private readonly i18nextConfig: InitOptions;\n private initPromise?: Promise<Streami18nState<C, Bundled>>;\n /** Set by {@link overrideTFunction}, so `init()` does not clobber a swapped-in implementation. */\n private tOverridden = false;\n\n constructor(options: Streami18nOptions<C> = {}) {\n this.logger = options.logger ?? ((message?: string) => console.warn(message));\n this.translations = new TranslationStore(options.runtimeDefaults);\n this.disableDateTimeTranslations = options.disableDateTimeTranslations ?? false;\n this.timezone = options.timezone;\n this.formatters = { ...predefinedFormatters, ...options.formatters };\n this.translationBuilder = new TranslationBuilder(this.i18nInstance);\n this.translationBuilderTopics = options.translationBuilderTopics ?? {};\n\n const language = options.language ?? DEFAULT_LANGUAGE;\n\n if (options.DateTimeParser) {\n this.DateTimeParser = options.DateTimeParser;\n // The supplied module, not ours -- it may be a second copy of dayjs, and extending ours would\n // leave theirs plugin-less, rendering every `LT` / `LLLL` token literally.\n if (isDayjsLike(this.DateTimeParser)) ensureDayjsPlugins(this.DateTimeParser);\n } else {\n this.DateTimeParser = getDefaultDateTimeParserModule();\n }\n\n const tDateTimeParser: TDateTimeParser = (timestamp) => {\n const locale =\n this.disableDateTimeTranslations || !this.localeExists(this.currentLanguageValue)\n ? DEFAULT_LANGUAGE\n : this.currentLanguageValue;\n\n const parsed = this.DateTimeParser(timestamp);\n const withZone =\n this.timezone && supportsTimezone(this.DateTimeParser)\n ? (parsed as unknown as { tz: (tz: string) => typeof parsed }).tz(this.timezone)\n : parsed;\n\n return (withZone as unknown as { locale: (l: string) => typeof parsed }).locale(\n locale,\n );\n };\n\n this.state = new StateStore<Streami18nState<C, Bundled>>({\n initialized: false,\n language,\n t: createDefaultTranslatorFunction<C, Bundled>(),\n tDateTimeParser,\n });\n\n // Both always exist, so an unregistered language still renders English copy rather than dotted keys.\n this.translations.ensure(DEFAULT_LANGUAGE);\n this.translations.ensure(language);\n\n if (options.translationsForLanguage) {\n this.translations.register(\n language,\n options.translationsForLanguage as Record<string, string>,\n );\n }\n\n const missingKeyHandler =\n options.parseMissingKeyHandler ??\n options.i18nextConfigOverrides?.parseMissingKeyHandler;\n\n this.i18nextConfig = {\n debug: options.debug ?? false,\n fallbackLng: false,\n interpolation: { escapeValue: false, formatSeparator: '|' },\n // Must stay false: keys are flat strings containing dots, and some copy contains `...`.\n keySeparator: false,\n lng: language,\n nsSeparator: false,\n // i18next only runs post-processors it was told about at init time.\n ...(Object.keys(this.translationBuilderTopics).length > 0\n ? { postProcess: Object.keys(this.translationBuilderTopics) }\n : {}),\n ...options.i18nextConfigOverrides,\n // Guarded even when integrator-supplied: an unguarded handler silently blanks every prose key.\n parseMissingKeyHandler: missingKeyHandler\n ? guardMissingKeyHandler(missingKeyHandler)\n : (key: string, defaultValue?: string) => {\n if (typeof defaultValue === 'string') return defaultValue;\n this.logger(`Streami18n: missing translation for key: ${key}`);\n return key;\n },\n };\n\n // No dictionary check here -- `registerTranslation()` legitimately runs after construction, so\n // `init()` is the first moment the registered set is final.\n if (options.dayjsLocaleConfigForLanguage) {\n this.addOrUpdateLocale(language, options.dayjsLocaleConfigForLanguage);\n } else if (!this.localeExists(language)) {\n this.logger(\n `Streami18n: no dayjs locale is registered for '${language}', so dates render with the ` +\n `English locale. Import it with \"import 'dayjs/locale/${language}';\" in your app, or pass ` +\n `a config via registerTranslation('${language}', translation, dayjsLocaleConfig).`,\n );\n }\n }\n\n get t(): StreamTFunctionFor<C, Bundled> {\n return this.state.getLatestValue().t;\n }\n\n get tDateTimeParser(): TDateTimeParser {\n return this.state.getLatestValue().tDateTimeParser;\n }\n\n get currentLanguage(): string {\n return this.state.getLatestValue().language;\n }\n\n get initialized(): boolean {\n return this.state.getLatestValue().initialized;\n }\n\n /** Read inside the constructor, before `state` getters are safe to rely on externally. */\n private get currentLanguageValue(): string {\n return this.state?.getLatestValue().language ?? DEFAULT_LANGUAGE;\n }\n\n /**\n * Initializes i18next. Memoized, so concurrent callers share one initialization.\n *\n * **Rejects on failure**, so handle it. The instance stays usable either way: `initialized` stays\n * false, keeping {@link Streami18n.registerTranslation} and {@link Streami18n.setLanguage} off a\n * dead i18next instance, and `t` keeps returning each call site's inline English.\n */\n init(): Promise<Streami18nState<C, Bundled>> {\n if (this.initPromise) return this.initPromise;\n\n const pending = this.runInit();\n this.initPromise = pending;\n\n // Clear the memo so a later call retries. Attached to a derived promise, so `pending` stays\n // unhandled and still reaches the caller's `catch`.\n pending.catch(() => {\n if (this.initPromise === pending) this.initPromise = undefined;\n });\n\n return pending;\n }\n\n private async runInit(): Promise<Streami18nState<C, Bundled>> {\n this.validateCurrentLanguage();\n this.assertPluralRulesCoverage(this.currentLanguage);\n\n const dayjsLocale = this.dayjsLocales[this.currentLanguage];\n if (dayjsLocale) this.addOrUpdateLocale(this.currentLanguage, dayjsLocale);\n\n const t = await this.i18nInstance.init({\n ...this.i18nextConfig,\n lng: this.currentLanguage,\n resources: this.i18nextResources(),\n });\n\n this.registerFormatters();\n\n // After init, so post-processors attach to a live instance and buffered translators flush.\n Object.entries(this.translationBuilderTopics).forEach(([topic, Topic]) => {\n this.translationBuilder.registerTopic(topic, Topic);\n });\n\n this.state.partialNext({\n initialized: true,\n ...(this.tOverridden ? {} : { t: t as unknown as StreamTFunctionFor<C, Bundled> }),\n });\n\n return this.state.getLatestValue();\n }\n\n /**\n * Re-run on every language change, not just at `init()`: factories destructure the language out of\n * their context, so one built once keeps formatting in the initial language forever. `formatter.add`\n * replaces by name, which is what makes re-registering sufficient.\n */\n private registerFormatters = () => {\n const context = this.createFormatterContext();\n\n Object.entries(this.formatters).forEach(([name, factory]) => {\n if (!factory) return;\n const formatter = factory(context);\n // Widened here rather than in the public type: a custom formatter's value is `never` so any\n // implementation is assignable (parameters are contravariant), while i18next's takes `any`.\n this.i18nInstance.services.formatter?.add(\n name,\n formatter as (value: any, lng: string | undefined, options: any) => string,\n );\n });\n };\n\n /**\n * Accessors rather than snapshots, for a formatter that holds the context and reads per call; one\n * that destructures is covered by {@link registerFormatters} re-running instead.\n *\n * The nested arrows are load-bearing: a getter in an object literal binds `this` to the literal.\n */\n private createFormatterContext = (): FormatterContext => {\n const readLanguage = () => this.currentLanguage;\n const readDateTimeParser = () => this.tDateTimeParser;\n\n return {\n get currentLanguage() {\n return readLanguage();\n },\n dateTimeParser: this.DateTimeParser,\n logger: this.logger,\n get tDateTimeParser() {\n return readDateTimeParser();\n },\n timezone: this.timezone,\n translate: this.translate,\n };\n };\n\n /** The store's flat dictionaries in i18next's nested `resources` shape. */\n private i18nextResources = (): Record<string, Record<string, Record<string, string>>> =>\n Object.fromEntries(\n this.translations\n .entries()\n .map(([language, dictionary]) => [language, { [DEFAULT_NAMESPACE]: dictionary }]),\n );\n\n /** The only route for a language added after `init()`. */\n private ensureLanguage = (language: string) => {\n this.addResources(language, this.translations.ensure(language));\n };\n\n private addResources = (language: string, dictionary: Record<string, string>) => {\n if (!this.initialized) return;\n this.i18nInstance.addResources(language, DEFAULT_NAMESPACE, dictionary);\n };\n\n registerTranslation(\n language: string,\n translation: TranslationDictionaryOf<C>,\n dayjsLocaleConfig?: DayjsLocaleConfig,\n ) {\n if (!translation) {\n this.logger(\n 'Streami18n: registerTranslation called without a translation dictionary',\n );\n return;\n }\n\n const merged = this.translations.register(\n language,\n translation as Record<string, string>,\n );\n\n if (dayjsLocaleConfig) {\n this.dayjsLocales[language] = { ...dayjsLocaleConfig };\n } else if (!this.localeExists(language)) {\n this.logger(\n `Streami18n: no dayjs locale is registered for '${language}'. Import it with ` +\n `\"import 'dayjs/locale/${language}';\" in your app, or pass a config as the third ` +\n `argument to registerTranslation.`,\n );\n }\n\n // `merged`, not `translation`: for a post-init language this is the only write into i18next's\n // store, so the partial would leave the bundled defaults absent there.\n this.addResources(language, merged);\n }\n\n /**\n * Returns nothing: the new `t` is published to {@link Streami18n.state}. Handing one back would offer\n * a value that goes stale on the next language change.\n */\n async setLanguage(language: string): Promise<void> {\n const previousLanguage = this.state.getLatestValue().language;\n\n // Published up front so the warnings below name the language being adopted, and rolled back in the\n // `catch` -- otherwise the store advertises one i18next never switched to.\n this.state.partialNext({ language });\n this.ensureLanguage(language);\n\n if (!this.initialized) return;\n\n this.validateCurrentLanguage();\n this.assertPluralRulesCoverage(language);\n\n try {\n const t = await this.i18nInstance.changeLanguage(language);\n const dayjsLocale = this.dayjsLocales[language];\n if (dayjsLocale) this.addOrUpdateLocale(language, dayjsLocale);\n this.registerFormatters();\n if (!this.tOverridden) {\n this.state.partialNext({ t: t as unknown as StreamTFunctionFor<C, Bundled> });\n }\n } catch (error) {\n this.state.partialNext({ language: previousLanguage });\n this.logger(`Streami18n: failed to set language: ${describeError(error)}`);\n }\n }\n\n /**\n * Swaps in a different translation implementation, for an app that already has an i18n layer. Safe\n * before `init()`, which will not overwrite it.\n */\n overrideTFunction(t: StreamTFunctionFor<C, Bundled>) {\n this.tOverridden = true;\n this.state.partialNext({ t });\n }\n\n /**\n * Languages an integrator supplied a dictionary for. Read-only because adding to it would claim a\n * language is registered with no dictionary behind it; use `registerTranslation`.\n */\n get registeredLanguages(): ReadonlySet<string> {\n return this.translations.registeredLanguages;\n }\n\n /**\n * Warns rather than falling back to `en`: the language renders English copy from the inline defaults\n * while keeping its own date formats, and resetting it would discard the integrator's choice.\n */\n private validateCurrentLanguage = () => {\n const language = this.currentLanguageValue;\n if (this.translations.isRegistered(language)) return;\n\n this.logger(\n `Streami18n: no translation dictionary is registered for '${language}', so the SDK's copy ` +\n `renders in English. Call registerTranslation('${language}', {...}) to translate it. ` +\n `Registered: ${[...this.translations.registeredLanguages].join(', ')}`,\n );\n };\n\n /**\n * Checked against the module that actually formats the dates.\n *\n * A supplied dayjs copy has its own locale registry, so consulting ours would answer for the wrong\n * one. True for a non-dayjs parser, whose registry we cannot inspect.\n */\n private localeExists = (language: string) => {\n if (!isDayjsLike(this.DateTimeParser)) return true;\n return dayjsLocaleExists(language, this.DateTimeParser);\n };\n\n /**\n * Registers a locale on the module that formats the dates, not on ours.\n *\n * Only dayjs has a registry we can write to. For a Moment the config cannot be applied at all, so it\n * is reported -- previously it was written to core's own dayjs, where nothing would ever read it.\n */\n private addOrUpdateLocale(language: string, config: DayjsLocaleConfig) {\n if (!isDayjsLike(this.DateTimeParser)) {\n this.logger(\n `Streami18n: a dayjs locale config was supplied for '${language}', but DateTimeParser is ` +\n `not dayjs, so it cannot be applied. Configure the locale on your own date library instead.`,\n );\n return;\n }\n addOrUpdateDayjsLocale(language, config, this.DateTimeParser);\n }\n\n /**\n * For formatters, which resolve keys handed to them at runtime -- including their own\n * `relativeTime.*` copy, which no catalog declares.\n */\n private translate: LooseTranslateFunction = (key, defaultValueOrOptions, options) =>\n (this.t as LooseTranslateFunction)(\n asDynamicKey(key),\n defaultValueOrOptions,\n options,\n ) as string;\n\n /**\n * Hermes ships a partial ICU: `Intl.PluralRules` silently falls back to root rules (`other` only) for\n * locales it lacks, so a dictionary's `_few` / `_many` never render and nothing errors.\n *\n * Called from `init()` because i18next caches a resolver per language there \u2014 the last moment\n * `intl-pluralrules` could still have been loaded in time.\n */\n private assertPluralRulesCoverage = (language: string) => {\n try {\n const resolved = new Intl.PluralRules(language).resolvedOptions().locale;\n if (resolved.split('-')[0] === language.split('-')[0]) return;\n this.logger(\n `Streami18n: Intl.PluralRules has no data for '${language}' (it resolved to ` +\n `'${resolved}'), so every count selects the '_other' form. On React Native, import ` +\n `'intl-pluralrules' before anything else in your entry file.`,\n );\n } catch {\n this.logger(\n `Streami18n: Intl.PluralRules is unavailable, so plural selection will not work. On React ` +\n `Native, import 'intl-pluralrules' before anything else in your entry file.`,\n );\n }\n };\n}\n\n/** `JSON.stringify(error)` renders an `Error` as `{}`. */\nconst describeError = (error: unknown) =>\n error instanceof Error ? error.message : String(error);\n", "export type Patch<T> = (value: T) => T;\nexport type ValueOrPatch<T> = T | Patch<T>;\nexport type Handler<T> = (nextValue: T, previousValue: T | undefined) => void;\nexport type Unsubscribe = () => void;\n// aliases\nexport type RemovePreprocessor = Unsubscribe;\nexport type Preprocessor<T> = Handler<T>;\n\nexport const isPatch = <T>(value: ValueOrPatch<T>): value is Patch<T> =>\n typeof value === 'function';\n\n// eslint-disable-next-line @typescript-eslint/no-empty-function\nconst noop = () => {};\n\nexport class StateStore<T extends Record<string, unknown>> {\n protected handlers = new Set<Handler<T>>();\n protected preprocessors = new Set<Preprocessor<T>>();\n\n constructor(protected value: T) {}\n\n /**\n * Allows merging two stores only if their keys differ otherwise there's no way to ensure the data type stability.\n *\n * @experimental\n * This method is experimental and may change in future versions.\n */\n\n public merge<Q extends StateStore<any>>(\n stateStore: Q extends StateStore<infer L>\n ? Extract<keyof T, keyof L> extends never\n ? Q\n : never\n : never,\n ) {\n return new MergedStateStore<T, Q extends StateStore<infer L> ? L : never>({\n original: this,\n merged: stateStore,\n });\n }\n\n public next(newValueOrPatch: ValueOrPatch<T>): void {\n // newValue (or patch output) should never be a mutated previous value\n const newValue = isPatch(newValueOrPatch)\n ? newValueOrPatch(this.value)\n : newValueOrPatch;\n\n // do not notify subscribers if the value hasn't changed\n if (newValue === this.value) return;\n\n this.preprocessors.forEach((preprocessor) => preprocessor(newValue, this.value));\n\n const oldValue = this.value;\n this.value = newValue;\n\n this.handlers.forEach((handler) => handler(this.value, oldValue));\n }\n\n public partialNext = (partial: Partial<T>): void =>\n this.next((current) => ({ ...current, ...partial }));\n\n public getLatestValue(): T {\n return this.value;\n }\n\n public subscribe(handler: Handler<T>): Unsubscribe {\n handler(this.value, undefined);\n this.handlers.add(handler);\n return () => {\n this.handlers.delete(handler);\n };\n }\n\n public subscribeWithSelector = <\n O extends Readonly<Record<string, unknown>> | Readonly<unknown[]>,\n >(\n selector: (nextValue: T) => O,\n handler: Handler<O>,\n ) => {\n // begin with undefined to reduce amount of selector calls\n let previouslySelectedValues: O | undefined;\n\n const wrappedHandler: Handler<T> = (nextValue) => {\n const newlySelectedValues = selector(nextValue);\n\n let hasUpdatedValues = typeof previouslySelectedValues === 'undefined';\n\n for (const key in previouslySelectedValues) {\n if (previouslySelectedValues[key] === newlySelectedValues[key]) continue;\n hasUpdatedValues = true;\n break;\n }\n\n if (!hasUpdatedValues) return;\n\n // save a copy of previouslySelectedValues before running\n // handler - if previouslySelectedValues are set to\n // newlySelectedValues after the handler call, there's a chance\n // that it'll never get set as handler can throw and flow might\n // go out of sync\n const previouslySelectedValuesCopy = previouslySelectedValues;\n previouslySelectedValues = newlySelectedValues;\n\n handler(newlySelectedValues, previouslySelectedValuesCopy);\n };\n\n return this.subscribe(wrappedHandler);\n };\n\n /**\n * Registers a preprocessor function that will be called before the state is updated.\n *\n * Preprocessors are invoked with the new and previous values whenever `next` or `partialNext` methods\n * are called, allowing you to mutate or react to the new value before it is set. Preprocessors run in the\n * order they were registered.\n *\n * @example\n * ```ts\n * const store = new StateStore<{ count: number; isMaxValue: bool; }>({ count: 0, isMaxValue: false });\n *\n * store.addPreprocessor((nextValue, prevValue) => {\n * if (nextValue.count > 10) {\n * nextValue.count = 10; // Clamp the value to a maximum of 10\n * }\n *\n * if (nextValue.count === 10) {\n * nextValue.isMaxValue = true; // Set isMaxValue to true if count is 10\n * } else {\n * nextValue.isMaxValue = false; // Reset isMaxValue otherwise\n * }\n * });\n *\n * store.partialNext({ count: 15 });\n *\n * store.getLatestValue(); // { count: 10, isMaxValue: true }\n *\n * store.partialNext({ count: 5 });\n *\n * store.getLatestValue(); // { count: 5, isMaxValue: false }\n * ```\n *\n * @param preprocessor - The function to be called with the next and previous values before the state is updated.\n * @returns A `RemovePreprocessor` function that removes the preprocessor when called.\n */\n public addPreprocessor(preprocessor: Preprocessor<T>): RemovePreprocessor {\n this.preprocessors.add(preprocessor);\n\n return () => {\n this.preprocessors.delete(preprocessor);\n };\n }\n}\n\n/**\n * Represents a merged state store that combines two separate state stores into one.\n *\n * The MergedStateStore allows combining two stores with non-overlapping keys.\n * It extends StateStore with the combined type of both source stores.\n * Changes to either the original or merged store will propagate to the combined store.\n *\n * Note: Direct mutations (next, partialNext, addPreprocessor) are disabled on the merged store.\n * You should instead call these methods on the original or merged stores.\n *\n * @template O The type of the original state store\n * @template M The type of the merged state store\n *\n * @experimental\n * This class is experimental and may change in future versions.\n */\nexport class MergedStateStore<\n O extends Record<string, unknown>,\n M extends Record<string, unknown>,\n> extends StateStore<O & M> {\n public readonly original: StateStore<O>;\n public readonly merged: StateStore<M>;\n private cachedOriginalValue: O;\n private cachedMergedValue: M;\n\n constructor({ original, merged }: { original: StateStore<O>; merged: StateStore<M> }) {\n const originalValue = original.getLatestValue();\n const mergedValue = merged.getLatestValue();\n\n super({\n ...originalValue,\n ...mergedValue,\n });\n\n this.cachedOriginalValue = originalValue;\n this.cachedMergedValue = mergedValue;\n\n this.original = original;\n this.merged = merged;\n }\n\n /**\n * Subscribes to changes in the merged state store.\n *\n * This method extends the base subscribe functionality to handle the merged nature of this store:\n * 1. The first subscriber triggers registration of helper subscribers that listen to both source stores\n * 2. Changes from either source store are propagated to this merged store\n * 3. Source store values are cached to prevent unnecessary updates\n *\n * When the first subscriber is added, the method sets up listeners on both original and merged stores.\n * These listeners update the combined store value whenever either source store changes.\n * All subscriptions (helpers and the actual handler) are tracked so they can be properly cleaned up.\n *\n * @param handler - The callback function that will be executed when the state changes\n * @returns An unsubscribe function that, when called, removes the subscription and any helper subscriptions\n */\n public subscribe(handler: Handler<O & M>) {\n const unsubscribeFunctions: Unsubscribe[] = [];\n\n // first subscriber will also register helpers which listen to changes of the\n // \"original\" and \"merged\" stores, combined outputs will be emitted through super.next\n // whenever cached values do not equal (always apart from the initial subscription)\n // since the actual handler subscription is registered after helpers, the actual\n // handler will run only once\n if (!this.handlers.size) {\n const base = (nextValue: O | M) => {\n super.next((currentValue) => ({\n ...currentValue,\n ...nextValue,\n }));\n };\n\n unsubscribeFunctions.push(\n this.original.subscribe((nextValue) => {\n if (nextValue === this.cachedOriginalValue) return;\n this.cachedOriginalValue = nextValue;\n base(nextValue);\n }),\n this.merged.subscribe((nextValue) => {\n if (nextValue === this.cachedMergedValue) return;\n this.cachedMergedValue = nextValue;\n base(nextValue);\n }),\n );\n }\n\n unsubscribeFunctions.push(super.subscribe(handler));\n\n return () => {\n unsubscribeFunctions.forEach((unsubscribe) => unsubscribe());\n };\n }\n\n /**\n * Retrieves the latest combined state from both original and merged stores.\n *\n * This method extends the base getLatestValue functionality to ensure the merged store\n * remains in sync with its source stores even when there are no active subscribers.\n *\n * When there are no handlers registered, the method:\n * 1. Fetches the latest values from both source stores\n * 2. Compares them with the cached values to detect changes\n * 3. If changes are detected, updates the internal value and caches\n * the new source values to maintain consistency\n *\n * This approach ensures that calling getLatestValue() always returns the most\n * up-to-date combined state, even if the merged store hasn't been actively\n * receiving updates through subscriptions.\n *\n * @returns The latest combined state from both original and merged stores\n */\n public getLatestValue() {\n // if there are no handlers registered to MergedStore then the local value might be out-of-sync\n // pull latest and compare against cached - if they differ, cache latest and produce new combined\n if (!this.handlers.size) {\n const originalValue = this.original.getLatestValue();\n const mergedValue = this.merged.getLatestValue();\n\n if (\n originalValue !== this.cachedOriginalValue ||\n mergedValue !== this.cachedMergedValue\n ) {\n this.value = {\n ...originalValue,\n ...mergedValue,\n };\n this.cachedMergedValue = mergedValue;\n this.cachedOriginalValue = originalValue;\n }\n }\n\n return super.getLatestValue();\n }\n\n // override original methods and \"disable\" them\n public next = () => {\n console.warn(\n `${MergedStateStore.name}.next is disabled, call original.next or merged.next instead`,\n );\n };\n public partialNext = () => {\n console.warn(\n `${MergedStateStore.name}.partialNext is disabled, call original.partialNext or merged.partialNext instead`,\n );\n };\n public addPreprocessor() {\n console.warn(\n `${MergedStateStore.name}.addPreprocessor is disabled, call original.addPreprocessor or merged.addPreprocessor instead`,\n );\n return noop;\n }\n}\n", "import type { i18n as I18nInstance } from 'i18next';\n\n/**\n * An i18next instance, as accepted by {@link TranslationTopic} and exposed as\n * `Streami18n.i18nInstance`.\n *\n * Re-exported because it is part of this module's public surface: a consumer implementing a topic, or\n * mocking one in a test, has to be able to name the type. Without this they would reach past\n * `stream-chat` into `i18next` directly and have to declare it themselves \u2014 the same mistake the\n * `moment-timezone` type leak was.\n */\nexport type { I18nInstance };\n\nimport type { LooseTranslateFunction } from './types';\n\n/**\n * i18next post-processor plumbing, for copy that cannot be resolved from a key alone.\n *\n * The motivating case is a notification: what to render depends on a runtime object, not just the key,\n * so `t('translationBuilderTopic.notification', { notification })` dispatches through a *topic* which\n * picks a *translator* based on that object. This is only the mechanism \u2014 the topics and their\n * translators are SDK-specific and stay in the UI SDKs, since they reference SDK key names.\n *\n * A UI SDK may not need this at all: dispatching on the object at the render site instead is perfectly\n * valid, and the React Native SDK does exactly that. The plumbing lives here so either approach is\n * available without a core change.\n */\ntype TopicName = string;\ntype TranslatorName = string;\n\n/**\n * Resolves one case within a topic. Returning `null` means \"not mine\" and lets the next candidate try.\n *\n * `t` is loose rather than catalog-typed: a translator is handed keys by the post-processor at runtime,\n * so it cannot be checked against a specific catalog.\n */\nexport type Translator<O extends Record<string, unknown> = Record<string, unknown>> =\n (params: {\n key: string;\n options: O;\n t: LooseTranslateFunction;\n value: string;\n }) => string | null;\n\nexport type TranslationTopicOptions<\n O extends Record<string, unknown> = Record<string, unknown>,\n> = {\n i18next: I18nInstance;\n translators?: Record<string, Translator<O>>;\n};\n\nexport abstract class TranslationTopic<\n O extends Record<string, unknown> = Record<string, unknown>,\n> {\n protected translators: Map<string, Translator<O>> = new Map();\n protected i18next: I18nInstance;\n\n constructor(protected options: TranslationTopicOptions<O>) {\n this.i18next = options.i18next;\n if (options.translators) {\n Object.entries(options.translators).forEach(([name, translator]) => {\n this.setTranslator(name, translator);\n });\n }\n }\n\n abstract translate(value: string, key: string, options: O): string;\n\n setTranslator = (name: string, translator: Translator<O>) => {\n this.translators.set(name, translator);\n };\n\n removeTranslator = (name: string) => {\n this.translators.delete(name);\n };\n}\n\nexport type TranslationTopicConstructor = new (\n options: TranslationTopicOptions,\n) => TranslationTopic;\n\nconst forwardTranslation: Translator = ({ value }) => value;\n\nexport class TranslationBuilder {\n private topics = new Map<string, TranslationTopic>();\n\n /**\n * Translators registered before their topic exists.\n *\n * Topics are only created during `Streami18n.init()`, but an integrator registers translators against\n * the constructed instance \u2014 so registrations that arrive first are buffered and flushed when the\n * topic appears, rather than silently dropped.\n */\n private translatorRegistrationsBuffer: Record<\n TopicName,\n Record<TranslatorName, Translator>\n > = {};\n\n constructor(private i18next: I18nInstance) {}\n\n registerTopic = (name: TopicName, Topic: TranslationTopicConstructor) => {\n let topic = this.topics.get(name);\n\n if (!topic) {\n topic = new Topic({ i18next: this.i18next });\n this.topics.set(name, topic);\n this.i18next.use({\n name,\n process: (value: string, key: string, options: Record<string, unknown>) => {\n // Re-read from the map rather than closing over `topic`, so `disableTopic` takes effect.\n const registered = this.topics.get(name);\n if (!registered) return value;\n return registered.translate(value, key, options);\n },\n type: 'postProcessor' as const,\n });\n }\n\n const buffered = this.translatorRegistrationsBuffer[name];\n if (buffered) {\n Object.entries(buffered).forEach(([translatorName, translator]) => {\n topic.setTranslator(translatorName, translator);\n });\n delete this.translatorRegistrationsBuffer[name];\n }\n\n return topic;\n };\n\n disableTopic = (topicName: TopicName) => {\n const topic = this.topics.get(topicName);\n if (!topic) return;\n // i18next has no way to remove a post-processor, so it is replaced with a pass-through.\n this.i18next.use({\n name: topicName,\n process: forwardTranslation,\n type: 'postProcessor',\n });\n this.topics.delete(topicName);\n };\n\n getTopic = (topicName: TopicName) => this.topics.get(topicName);\n\n registerTranslators(\n topicName: TopicName,\n translators: Record<TranslatorName, Translator>,\n ) {\n const topic = this.getTopic(topicName);\n\n if (!topic) {\n this.translatorRegistrationsBuffer[topicName] ??= {};\n Object.entries(translators).forEach(([translatorName, translator]) => {\n this.translatorRegistrationsBuffer[topicName][translatorName] = translator;\n });\n return;\n }\n\n Object.entries(translators).forEach(([name, translator]) => {\n topic.setTranslator(name, translator);\n });\n }\n\n removeTranslators(topicName: TopicName, translators: TranslatorName[]) {\n if (this.translatorRegistrationsBuffer[topicName]) {\n translators.forEach((translatorName) => {\n delete this.translatorRegistrationsBuffer[topicName][translatorName];\n });\n }\n\n const topic = this.getTopic(topicName);\n if (!topic) return;\n translators.forEach((name) => {\n topic.removeTranslator(name);\n });\n }\n}\n", "/**\n * The translation dictionaries an instance holds, and the one rule that governs them.\n *\n * Extracted from `Streami18n` because it is a self-contained concern with no dependency on i18next or\n * dayjs: given the SDK's bundled defaults and whatever dictionaries an integrator supplies, produce the\n * dictionary for a language. That makes the layering rule testable on its own rather than only through\n * a fully initialized instance.\n *\n * Deliberately free of i18next concepts \u2014 no namespaces, no resource nesting. `Streami18n` adapts these\n * flat dictionaries to i18next's shape, so the rule below stays readable without knowing that library.\n */\n\n/** The one language whose dictionary always exists, because the bundled copy is English. */\nexport const DEFAULT_LANGUAGE = 'en';\n\nexport class TranslationStore {\n /**\n * The SDK's bundled translation data: the keys that cannot carry an inline `defaultValue` at their\n * call site \u2014 formatter expressions, and prose reaching `t()` as a runtime value.\n */\n private readonly runtimeDefaults: Record<string, string>;\n\n private readonly dictionaries = new Map<string, Record<string, string>>();\n\n private readonly registered = new Set<string>([DEFAULT_LANGUAGE]);\n\n constructor(runtimeDefaults: Record<string, string> = {}) {\n this.runtimeDefaults = runtimeDefaults;\n }\n\n /**\n * Languages an integrator actually supplied a dictionary for.\n *\n * Deliberately narrower than {@link TranslationStore.languages}, which also counts every language\n * created just to carry the bundled defaults. Without the distinction there would be no way to warn\n * that the active language has no translations \u2014 every language would look registered.\n */\n get registeredLanguages(): ReadonlySet<string> {\n return this.registered;\n }\n\n /** Every language with a dictionary, including those carrying only the bundled defaults. */\n get languages(): string[] {\n return [...this.dictionaries.keys()];\n }\n\n /** `language -> dictionary`, for a caller that has to hand them all over at once. */\n entries(): Array<[string, Record<string, string>]> {\n return [...this.dictionaries];\n }\n\n isRegistered(language: string) {\n return this.registered.has(language);\n }\n\n /**\n * Guarantees `language` has a dictionary, and returns it.\n *\n * Called for a language nobody registered, so that it still formats dates and renders the SDK's copy\n * in English rather than raw dotted keys.\n */\n ensure(language: string): Record<string, string> {\n return this.merge(language);\n }\n\n /**\n * Layers a dictionary over what `language` already has, and marks it registered.\n *\n * **Merged, not replaced.** Repeated calls for one language accumulate, and \u2014 the reason this class\n * exists \u2014 the bundled defaults survive a partial dictionary. A bundled key has no inline\n * `defaultValue` at its call site and `fallbackLng` is false, so a language that loses them renders\n * raw dotted keys and unformatted ISO timestamps. That is guarantee G1 of the i18n suite.\n */\n register(language: string, dictionary: Record<string, string>): Record<string, string> {\n const merged = this.merge(language, dictionary);\n this.registered.add(language);\n return merged;\n }\n\n /**\n * Bundled defaults first, then whatever the language already had, then the incoming dictionary \u2014 so\n * an integrator can override a bundled key, and a later registration wins over an earlier one.\n */\n private merge(\n language: string,\n dictionary?: Record<string, string>,\n ): Record<string, string> {\n const merged = {\n ...this.runtimeDefaults,\n ...this.dictionaries.get(language),\n ...dictionary,\n };\n this.dictionaries.set(language, merged);\n return merged;\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["Dayjs", "updateLocale", "utc", "timezone", "localizedFormat", "calendar", "localeData", "relativeTime", "duration", "module", "calendar", "duration", "i18next", "i18next"]
7
7
  }