@messagevisor/sdk 0.0.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1000 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Messagevisor = void 0;
4
+ exports.createMessagevisorCache = createMessagevisorCache;
5
+ exports.createMessagevisor = createMessagevisor;
6
+ const conditions_1 = require("./conditions");
7
+ const DEFAULT_CURRENCY = "USD";
8
+ const LOG_PREFIX = "[Messagevisor]";
9
+ class MessagevisorCloseError extends Error {
10
+ constructor(message, errors) {
11
+ super(message);
12
+ this.name = "MessagevisorCloseError";
13
+ this.errors = errors;
14
+ }
15
+ }
16
+ function createEmptyRecord() {
17
+ return {};
18
+ }
19
+ function createMessagevisorCache() {
20
+ return {
21
+ numberFormat: createEmptyRecord(),
22
+ dateTimeFormat: createEmptyRecord(),
23
+ relativeTimeFormat: createEmptyRecord(),
24
+ pluralRules: createEmptyRecord(),
25
+ listFormat: createEmptyRecord(),
26
+ displayNames: createEmptyRecord(),
27
+ };
28
+ }
29
+ function isPlainObject(value) {
30
+ return typeof value === "object" && value !== null && !Array.isArray(value);
31
+ }
32
+ function deepMerge(parent, child) {
33
+ if (typeof parent === "undefined") {
34
+ return child;
35
+ }
36
+ if (typeof child === "undefined") {
37
+ return parent;
38
+ }
39
+ if (!isPlainObject(parent) || !isPlainObject(child)) {
40
+ return child;
41
+ }
42
+ const result = { ...parent };
43
+ for (const key of Object.keys(child)) {
44
+ result[key] = deepMerge(result[key], child[key]);
45
+ }
46
+ return result;
47
+ }
48
+ function getDefaultTimeZone() {
49
+ return Intl.DateTimeFormat().resolvedOptions().timeZone;
50
+ }
51
+ function resolveCurrency(optionCurrency, presetCurrency, instanceCurrency) {
52
+ return optionCurrency || presetCurrency || instanceCurrency || DEFAULT_CURRENCY;
53
+ }
54
+ function resolveTimeZone(optionTimeZone, formatTimeZone, instanceTimeZone) {
55
+ return optionTimeZone || formatTimeZone || instanceTimeZone || getDefaultTimeZone();
56
+ }
57
+ function resolveDateTimeOptions(formatOptions, options, instanceTimeZone) {
58
+ return {
59
+ ...(formatOptions || {}),
60
+ timeZone: resolveTimeZone(options.timeZone, formatOptions === null || formatOptions === void 0 ? void 0 : formatOptions.timeZone, instanceTimeZone),
61
+ };
62
+ }
63
+ function shouldLog(currentLevel, level) {
64
+ const order = ["fatal", "error", "warn", "info", "debug"];
65
+ return order.indexOf(currentLevel) >= order.indexOf(level);
66
+ }
67
+ function mergeStoredDatafile(existing, incoming) {
68
+ var _a;
69
+ if (!existing) {
70
+ return incoming;
71
+ }
72
+ return {
73
+ schemaVersion: incoming.schemaVersion,
74
+ messagevisorVersion: incoming.messagevisorVersion,
75
+ revision: incoming.revision,
76
+ target: incoming.target,
77
+ locale: incoming.locale,
78
+ direction: (_a = incoming.direction) !== null && _a !== void 0 ? _a : existing.direction,
79
+ formats: incoming.formats,
80
+ segments: {
81
+ ...(existing.segments || {}),
82
+ ...(incoming.segments || {}),
83
+ },
84
+ messages: {
85
+ ...(existing.messages || {}),
86
+ ...(incoming.messages || {}),
87
+ },
88
+ translations: {
89
+ ...(existing.translations || {}),
90
+ ...(incoming.translations || {}),
91
+ },
92
+ };
93
+ }
94
+ class Messagevisor {
95
+ constructor(options = {}) {
96
+ this.datafiles = {};
97
+ this.defaultTranslationsByLocale = {};
98
+ this.defaultFormatsByLocale = {};
99
+ this.locale = null;
100
+ this.context = {};
101
+ this.logLevel = "info";
102
+ this.modules = [];
103
+ this.moduleDiagnosticSubscriptions = [];
104
+ this.moduleApis = {};
105
+ this.moduleApiId = 0;
106
+ this.closed = false;
107
+ this.version = 0;
108
+ this.listeners = {
109
+ change: [],
110
+ error: [],
111
+ datafile_set: [],
112
+ locale_set: [],
113
+ context_set: [],
114
+ currency_set: [],
115
+ timeZone_set: [],
116
+ };
117
+ this.currency = options.currency;
118
+ this.timeZone = options.timeZone;
119
+ this.context = options.context || {};
120
+ this.resolveFlag = options.resolveFlag;
121
+ this.resolveVariation = options.resolveVariation;
122
+ this.logLevel = options.logLevel || "info";
123
+ this.onDiagnostic = options.onDiagnostic;
124
+ this.cache = options.cache || createMessagevisorCache();
125
+ this.setFlagResolver(options.resolveFlag);
126
+ this.setVariationResolver(options.resolveVariation);
127
+ (options.modules || []).forEach((module) => {
128
+ this.addModule(module);
129
+ });
130
+ if (options.defaultTranslations) {
131
+ this.defaultTranslationsByLocale = { ...options.defaultTranslations };
132
+ }
133
+ if (options.defaultFormats) {
134
+ this.defaultFormatsByLocale = { ...options.defaultFormats };
135
+ }
136
+ if (options.datafile) {
137
+ this.setDatafile(options.datafile);
138
+ }
139
+ else {
140
+ this.locale = options.locale || null;
141
+ }
142
+ this.reportDiagnostic({
143
+ level: "info",
144
+ code: "sdk_initialized",
145
+ message: "SDK initialized",
146
+ });
147
+ }
148
+ subscribe(callback) {
149
+ if (this.closed) {
150
+ return () => { };
151
+ }
152
+ return this.on("change", callback);
153
+ }
154
+ on(eventName, callback) {
155
+ if (this.closed) {
156
+ return () => { };
157
+ }
158
+ if (this.listeners[eventName].indexOf(callback) === -1) {
159
+ this.listeners[eventName].push(callback);
160
+ }
161
+ return () => {
162
+ this.listeners[eventName] = this.listeners[eventName].filter((listener) => listener !== callback);
163
+ };
164
+ }
165
+ getSnapshot() {
166
+ var _a;
167
+ const datafileRevisionsByLocale = {};
168
+ Object.keys(this.datafiles).forEach((locale) => {
169
+ datafileRevisionsByLocale[locale] = this.datafiles[locale].revision;
170
+ });
171
+ return {
172
+ version: this.version,
173
+ locale: this.locale,
174
+ direction: this.locale ? (_a = this.datafiles[this.locale]) === null || _a === void 0 ? void 0 : _a.direction : undefined,
175
+ context: { ...this.context },
176
+ currency: this.currency,
177
+ timeZone: this.timeZone,
178
+ datafileLocales: Object.keys(this.datafiles),
179
+ datafileRevisionsByLocale,
180
+ };
181
+ }
182
+ addModule(module) {
183
+ if (this.closed) {
184
+ return;
185
+ }
186
+ if (module.name &&
187
+ this.modules.some((registeredModule) => registeredModule.name === module.name)) {
188
+ this.reportDiagnostic({
189
+ level: "error",
190
+ code: "duplicate_module",
191
+ message: "Duplicate module name",
192
+ moduleName: module.name,
193
+ });
194
+ return;
195
+ }
196
+ this.runModuleSetup(module);
197
+ this.modules.push(module);
198
+ }
199
+ removeModule(name) {
200
+ if (this.closed) {
201
+ return;
202
+ }
203
+ const removedModules = this.modules.filter((module) => module.name === name);
204
+ this.modules = this.modules.filter((module) => module.name !== name);
205
+ removedModules.forEach((module) => this.clearModuleDiagnosticSubscriptions(module));
206
+ }
207
+ setFlagResolver(resolver) {
208
+ this.resolveFlag = resolver;
209
+ }
210
+ setVariationResolver(resolver) {
211
+ this.resolveVariation = resolver;
212
+ }
213
+ setCurrency(currency) {
214
+ const previousSnapshot = this.getSnapshot();
215
+ const previousCurrency = this.currency;
216
+ this.currency = currency;
217
+ this.emit("currency_set", previousSnapshot, {
218
+ currency,
219
+ previousCurrency,
220
+ });
221
+ }
222
+ getCurrency() {
223
+ return this.currency;
224
+ }
225
+ setTimeZone(timeZone) {
226
+ const previousSnapshot = this.getSnapshot();
227
+ const previousTimeZone = this.timeZone;
228
+ this.timeZone = timeZone;
229
+ this.emit("timeZone_set", previousSnapshot, {
230
+ timeZone,
231
+ previousTimeZone,
232
+ });
233
+ }
234
+ getTimeZone() {
235
+ return this.timeZone;
236
+ }
237
+ setDatafile(datafile, replace = false) {
238
+ const resolvedDatafile = this.resolveDatafileInput(datafile);
239
+ if (!resolvedDatafile) {
240
+ return;
241
+ }
242
+ const previousSnapshot = this.getSnapshot();
243
+ const previousLocale = this.locale;
244
+ const storedDatafile = !replace && this.datafiles[resolvedDatafile.locale]
245
+ ? mergeStoredDatafile(this.datafiles[resolvedDatafile.locale], resolvedDatafile)
246
+ : resolvedDatafile;
247
+ this.datafiles[storedDatafile.locale] = storedDatafile;
248
+ if (!this.locale) {
249
+ this.locale = storedDatafile.locale;
250
+ }
251
+ this.emit("datafile_set", previousSnapshot, {
252
+ datafile: storedDatafile,
253
+ locale: this.locale,
254
+ previousLocale,
255
+ });
256
+ }
257
+ setContext(context, replace = false) {
258
+ const previousSnapshot = this.getSnapshot();
259
+ const previousContext = this.context;
260
+ this.context = replace ? context : { ...this.context, ...context };
261
+ this.emit("context_set", previousSnapshot, {
262
+ context: { ...this.context },
263
+ previousContext: { ...previousContext },
264
+ });
265
+ }
266
+ getContext() {
267
+ return { ...this.context };
268
+ }
269
+ setLocale(locale) {
270
+ if (!this.datafiles[locale]) {
271
+ throw new Error(`Datafile not found for locale: ${locale}`);
272
+ }
273
+ const previousSnapshot = this.getSnapshot();
274
+ const previousLocale = this.locale;
275
+ this.locale = locale;
276
+ this.emit("locale_set", previousSnapshot, {
277
+ locale,
278
+ previousLocale,
279
+ });
280
+ }
281
+ getLocale() {
282
+ return this.locale;
283
+ }
284
+ getDirection(locale = this.locale) {
285
+ if (!locale) {
286
+ return undefined;
287
+ }
288
+ return this.getDatafile(locale).direction;
289
+ }
290
+ getDatafile(locale = this.locale) {
291
+ if (!locale) {
292
+ this.reportDiagnostic({
293
+ level: "error",
294
+ code: "missing_locale",
295
+ message: "Datafile not found: no locale is set",
296
+ locale: this.locale,
297
+ });
298
+ throw new Error("Datafile not found: no locale is set");
299
+ }
300
+ const datafile = this.datafiles[locale];
301
+ if (!datafile) {
302
+ this.reportDiagnostic({
303
+ level: "error",
304
+ code: "missing_datafile",
305
+ message: "Datafile not found for locale",
306
+ locale,
307
+ });
308
+ throw new Error(`Datafile not found for locale: ${locale}`);
309
+ }
310
+ return datafile;
311
+ }
312
+ getRevision(locale) {
313
+ return this.getDatafile(locale || this.locale).revision;
314
+ }
315
+ getCurrentLocale(options = {}) {
316
+ const locale = options.locale || this.locale;
317
+ if (!locale) {
318
+ this.reportDiagnostic({
319
+ level: "error",
320
+ code: "missing_locale",
321
+ message: "Locale not set",
322
+ locale: this.locale,
323
+ });
324
+ throw new Error("Locale not set");
325
+ }
326
+ return locale;
327
+ }
328
+ emitError(diagnostic) {
329
+ if (this.closed) {
330
+ return;
331
+ }
332
+ const snapshot = this.getSnapshot();
333
+ const event = {
334
+ type: "error",
335
+ version: this.version,
336
+ snapshot,
337
+ previousSnapshot: snapshot,
338
+ diagnostic,
339
+ };
340
+ this.listeners.error.slice().forEach((callback) => callback(event));
341
+ }
342
+ reportDiagnostic(diagnostic, sourceModuleKey) {
343
+ this.moduleDiagnosticSubscriptions.slice().forEach((subscription) => {
344
+ if (subscription.moduleKey === sourceModuleKey) {
345
+ return;
346
+ }
347
+ if (!shouldLog(subscription.logLevel, diagnostic.level)) {
348
+ return;
349
+ }
350
+ subscription.handler(diagnostic);
351
+ });
352
+ if (shouldLog(this.logLevel, diagnostic.level)) {
353
+ if (this.onDiagnostic) {
354
+ this.onDiagnostic(diagnostic);
355
+ }
356
+ else {
357
+ const method = diagnostic.level === "fatal" || diagnostic.level === "error"
358
+ ? "error"
359
+ : diagnostic.level === "warn"
360
+ ? "warn"
361
+ : diagnostic.level === "debug"
362
+ ? "debug"
363
+ : "info";
364
+ console[method](LOG_PREFIX, diagnostic.message, diagnostic);
365
+ }
366
+ }
367
+ if (diagnostic.level === "error") {
368
+ this.emitError(diagnostic);
369
+ }
370
+ }
371
+ resolveDatafileInput(datafile) {
372
+ try {
373
+ const parsedDatafile = typeof datafile === "string" ? JSON.parse(datafile) : datafile;
374
+ if (!isPlainObject(parsedDatafile) || typeof parsedDatafile.locale !== "string") {
375
+ throw new Error("Datafile must be an object with a string locale.");
376
+ }
377
+ return parsedDatafile;
378
+ }
379
+ catch (error) {
380
+ this.reportDiagnostic({
381
+ level: "error",
382
+ code: "invalid_datafile",
383
+ message: "could not parse datafile",
384
+ originalError: error,
385
+ });
386
+ return undefined;
387
+ }
388
+ }
389
+ getDefaultTranslations(locale = this.locale) {
390
+ if (!locale) {
391
+ return undefined;
392
+ }
393
+ return this.defaultTranslationsByLocale[locale];
394
+ }
395
+ getDefaultFormats(locale = this.locale) {
396
+ if (!locale) {
397
+ return undefined;
398
+ }
399
+ return this.defaultFormatsByLocale[locale];
400
+ }
401
+ getMessageFromDatafile(messageKey, options = {}, locale = this.getCurrentLocale(options)) {
402
+ const datafile = this.datafiles[locale];
403
+ if (!datafile) {
404
+ return undefined;
405
+ }
406
+ const evaluationContext = {
407
+ ...this.context,
408
+ ...(options.context || {}),
409
+ };
410
+ const message = datafile.messages[messageKey];
411
+ const overrides = (message === null || message === void 0 ? void 0 : message.overrides) || [];
412
+ for (let index = 0; index < overrides.length; index++) {
413
+ const override = overrides[index];
414
+ const matchesConditions = (0, conditions_1.evaluateCondition)(override.conditions, {
415
+ context: evaluationContext,
416
+ segments: datafile.segments,
417
+ resolveFlag: this.resolveFlag,
418
+ resolveVariation: this.resolveVariation,
419
+ });
420
+ const matchesSegments = (0, conditions_1.evaluateGroupSegment)(override.segments, {
421
+ context: evaluationContext,
422
+ segments: datafile.segments,
423
+ resolveFlag: this.resolveFlag,
424
+ resolveVariation: this.resolveVariation,
425
+ });
426
+ const matched = matchesConditions && matchesSegments;
427
+ if (matched) {
428
+ const diagnostic = {
429
+ level: "debug",
430
+ code: "message_override_matched",
431
+ message: "Message override matched",
432
+ locale,
433
+ messageKey,
434
+ overrideKey: override.key,
435
+ };
436
+ this.reportDiagnostic(diagnostic);
437
+ return override.translation;
438
+ }
439
+ }
440
+ return datafile.translations[messageKey];
441
+ }
442
+ getMessageMeta(messageKey, locale = this.getCurrentLocale()) {
443
+ var _a, _b;
444
+ const datafile = this.datafiles[locale];
445
+ return (_b = (_a = datafile === null || datafile === void 0 ? void 0 : datafile.messages) === null || _a === void 0 ? void 0 : _a[messageKey]) === null || _b === void 0 ? void 0 : _b.meta;
446
+ }
447
+ getMessageDefinition(messageKey, locale = this.getCurrentLocale()) {
448
+ var _a;
449
+ const datafile = this.datafiles[locale];
450
+ return (_a = datafile === null || datafile === void 0 ? void 0 : datafile.messages) === null || _a === void 0 ? void 0 : _a[messageKey];
451
+ }
452
+ reportDeprecatedMessage(messageKey, message, locale = this.getCurrentLocale()) {
453
+ if (!message.deprecated) {
454
+ return;
455
+ }
456
+ this.reportDiagnostic({
457
+ level: "warn",
458
+ code: "deprecated_message",
459
+ message: "Deprecated message evaluated",
460
+ locale,
461
+ messageKey,
462
+ deprecationWarning: message.deprecationWarning,
463
+ source: "translation",
464
+ });
465
+ }
466
+ resolveMessage(messageKey, defaultTranslation, options = {}) {
467
+ const locale = this.getCurrentLocale(options);
468
+ const isMissing = (value) => typeof value === "undefined";
469
+ let translated;
470
+ if (messageKey) {
471
+ translated = this.getMessageFromDatafile(messageKey, options, locale);
472
+ if (isMissing(translated)) {
473
+ const translations = this.getDefaultTranslations(locale);
474
+ translated = translations ? translations[messageKey] : undefined;
475
+ }
476
+ }
477
+ if (!messageKey && typeof defaultTranslation === "string") {
478
+ return {
479
+ locale,
480
+ source: defaultTranslation,
481
+ formatted: defaultTranslation,
482
+ messageKey: undefined,
483
+ };
484
+ }
485
+ if (!isMissing(translated)) {
486
+ const message = messageKey ? this.getMessageDefinition(messageKey, locale) : undefined;
487
+ if (messageKey && message) {
488
+ this.reportDeprecatedMessage(messageKey, message, locale);
489
+ }
490
+ return {
491
+ locale,
492
+ source: translated,
493
+ formatted: translated,
494
+ messageKey,
495
+ };
496
+ }
497
+ if (messageKey) {
498
+ this.reportDiagnostic({
499
+ level: "error",
500
+ code: "missing_translation",
501
+ message: "Missing translation",
502
+ locale,
503
+ messageKey,
504
+ source: "translation",
505
+ });
506
+ }
507
+ if (typeof defaultTranslation === "string") {
508
+ return {
509
+ locale,
510
+ source: defaultTranslation,
511
+ formatted: defaultTranslation,
512
+ messageKey,
513
+ };
514
+ }
515
+ return {
516
+ locale,
517
+ source: messageKey || "",
518
+ formatted: messageKey || "",
519
+ messageKey,
520
+ };
521
+ }
522
+ emit(type, previousSnapshot, details = {}) {
523
+ if (this.closed) {
524
+ return;
525
+ }
526
+ this.version += 1;
527
+ const event = {
528
+ ...details,
529
+ type,
530
+ version: this.version,
531
+ snapshot: this.getSnapshot(),
532
+ previousSnapshot,
533
+ };
534
+ this.listeners[type].slice().forEach((callback) => callback(event));
535
+ const changeEvent = {
536
+ ...event,
537
+ type: "change",
538
+ };
539
+ this.listeners.change.slice().forEach((callback) => callback(changeEvent));
540
+ }
541
+ getEvaluationFormats(options = {}, locale = this.getCurrentLocale(options)) {
542
+ const formats = deepMerge(deepMerge(this.getDefaultFormats(locale), this.datafiles[locale] ? this.datafiles[locale].formats : undefined), options.formats) || {};
543
+ const numberFormats = {};
544
+ const dateFormats = {};
545
+ const timeFormats = {};
546
+ const dateTimeRangeFormats = {};
547
+ Object.keys(formats.number || {}).forEach((key) => {
548
+ var _a;
549
+ const formatOptions = (_a = formats.number) === null || _a === void 0 ? void 0 : _a[key];
550
+ if (!formatOptions) {
551
+ return;
552
+ }
553
+ if (formatOptions.style !== "currency") {
554
+ numberFormats[key] = formatOptions;
555
+ return;
556
+ }
557
+ numberFormats[key] = {
558
+ ...formatOptions,
559
+ currency: resolveCurrency(options.currency, formatOptions.currency, this.currency),
560
+ };
561
+ });
562
+ Object.keys(formats.date || {}).forEach((key) => {
563
+ var _a;
564
+ const formatOptions = (_a = formats.date) === null || _a === void 0 ? void 0 : _a[key];
565
+ if (!formatOptions) {
566
+ return;
567
+ }
568
+ dateFormats[key] = {
569
+ ...formatOptions,
570
+ timeZone: resolveTimeZone(options.timeZone, formatOptions.timeZone, this.timeZone),
571
+ };
572
+ });
573
+ Object.keys(formats.time || {}).forEach((key) => {
574
+ var _a;
575
+ const formatOptions = (_a = formats.time) === null || _a === void 0 ? void 0 : _a[key];
576
+ if (!formatOptions) {
577
+ return;
578
+ }
579
+ timeFormats[key] = {
580
+ ...formatOptions,
581
+ timeZone: resolveTimeZone(options.timeZone, formatOptions.timeZone, this.timeZone),
582
+ };
583
+ });
584
+ Object.keys(formats.dateTimeRange || {}).forEach((key) => {
585
+ var _a;
586
+ const formatOptions = (_a = formats.dateTimeRange) === null || _a === void 0 ? void 0 : _a[key];
587
+ if (!formatOptions) {
588
+ return;
589
+ }
590
+ dateTimeRangeFormats[key] = {
591
+ ...formatOptions,
592
+ timeZone: resolveTimeZone(options.timeZone, formatOptions.timeZone, this.timeZone),
593
+ };
594
+ });
595
+ return {
596
+ ...formats,
597
+ number: numberFormats,
598
+ date: dateFormats,
599
+ time: timeFormats,
600
+ dateTimeRange: dateTimeRangeFormats,
601
+ };
602
+ }
603
+ getCachedNumberFormat(locale, options) {
604
+ var cacheKey = JSON.stringify({
605
+ locale,
606
+ options,
607
+ });
608
+ if (!this.cache.numberFormat[cacheKey]) {
609
+ this.cache.numberFormat[cacheKey] = new Intl.NumberFormat(locale, options);
610
+ }
611
+ return this.cache.numberFormat[cacheKey];
612
+ }
613
+ getCachedDateTimeFormat(locale, options) {
614
+ var cacheKey = JSON.stringify({
615
+ locale,
616
+ options,
617
+ });
618
+ if (!this.cache.dateTimeFormat[cacheKey]) {
619
+ this.cache.dateTimeFormat[cacheKey] = new Intl.DateTimeFormat(locale, options);
620
+ }
621
+ return this.cache.dateTimeFormat[cacheKey];
622
+ }
623
+ getCachedRelativeTimeFormat(locale, options = {}) {
624
+ var cacheKey = JSON.stringify({
625
+ locale,
626
+ options,
627
+ });
628
+ if (!this.cache.relativeTimeFormat[cacheKey]) {
629
+ this.cache.relativeTimeFormat[cacheKey] = new Intl.RelativeTimeFormat(locale, options);
630
+ }
631
+ return this.cache.relativeTimeFormat[cacheKey];
632
+ }
633
+ createModuleApi(module) {
634
+ const moduleKey = this.getModuleApiKey(module);
635
+ const setFlagResolver = (resolver) => {
636
+ this.setFlagResolver(resolver);
637
+ };
638
+ const setVariationResolver = (resolver) => {
639
+ this.setVariationResolver(resolver);
640
+ };
641
+ const getRevision = (locale) => {
642
+ return this.getRevision(locale);
643
+ };
644
+ const onDiagnostic = (handler, options = {}) => {
645
+ const subscription = {
646
+ moduleKey,
647
+ handler,
648
+ logLevel: options.logLevel || "info",
649
+ };
650
+ this.moduleDiagnosticSubscriptions.push(subscription);
651
+ return () => {
652
+ this.moduleDiagnosticSubscriptions = this.moduleDiagnosticSubscriptions.filter((currentSubscription) => currentSubscription !== subscription);
653
+ };
654
+ };
655
+ const reportDiagnostic = (diagnostic) => {
656
+ const moduleDiagnostic = { ...diagnostic };
657
+ if (module.name) {
658
+ moduleDiagnostic.module = module.name;
659
+ }
660
+ this.reportDiagnostic(moduleDiagnostic, moduleKey);
661
+ };
662
+ return {
663
+ setFlagResolver,
664
+ setVariationResolver,
665
+ getRevision,
666
+ onDiagnostic,
667
+ reportDiagnostic,
668
+ };
669
+ }
670
+ getModuleApiKey(module) {
671
+ if (module.name) {
672
+ return `name:${module.name}`;
673
+ }
674
+ const moduleWithApiKey = module;
675
+ if (!moduleWithApiKey.__messagevisorModuleApiKey) {
676
+ moduleWithApiKey.__messagevisorModuleApiKey = `anonymous:${++this.moduleApiId}`;
677
+ }
678
+ return moduleWithApiKey.__messagevisorModuleApiKey;
679
+ }
680
+ getModuleApi(module) {
681
+ const key = this.getModuleApiKey(module);
682
+ const existingApi = this.moduleApis[key];
683
+ if (existingApi) {
684
+ return existingApi;
685
+ }
686
+ const api = this.createModuleApi(module);
687
+ this.moduleApis[key] = api;
688
+ return api;
689
+ }
690
+ runModuleSetup(module) {
691
+ if (!module.setup) {
692
+ return;
693
+ }
694
+ module.setup(this.getModuleApi(module));
695
+ }
696
+ clearModuleDiagnosticSubscriptions(module) {
697
+ const moduleKey = this.getModuleApiKey(module);
698
+ this.moduleDiagnosticSubscriptions = this.moduleDiagnosticSubscriptions.filter((subscription) => subscription.moduleKey !== moduleKey);
699
+ delete this.moduleApis[moduleKey];
700
+ }
701
+ runTransforms(translation, payload) {
702
+ var _a;
703
+ let currentTranslation = translation;
704
+ for (const module of this.modules) {
705
+ const nextTranslation = (_a = module.transform) === null || _a === void 0 ? void 0 : _a.call(module, {
706
+ ...payload,
707
+ translation: currentTranslation,
708
+ }, this.getModuleApi(module));
709
+ if (typeof nextTranslation !== "undefined") {
710
+ currentTranslation = nextTranslation;
711
+ }
712
+ }
713
+ return currentTranslation;
714
+ }
715
+ runFormats(translation, values, payload) {
716
+ let currentTranslation = translation;
717
+ for (const module of this.modules) {
718
+ if (!module.format) {
719
+ continue;
720
+ }
721
+ try {
722
+ const nextTranslation = module.format({
723
+ ...payload,
724
+ translation: currentTranslation,
725
+ values,
726
+ }, this.getModuleApi(module));
727
+ if (typeof nextTranslation !== "undefined") {
728
+ currentTranslation = nextTranslation;
729
+ }
730
+ }
731
+ catch (error) {
732
+ this.reportDiagnostic({
733
+ level: "error",
734
+ code: "invalid_message",
735
+ message: "Unable to format message",
736
+ locale: payload.locale,
737
+ messageKey: payload.messageKey,
738
+ source: payload.source,
739
+ originalError: error,
740
+ });
741
+ throw error;
742
+ }
743
+ }
744
+ return currentTranslation;
745
+ }
746
+ formatMessageInternal(message, values, options = {}) {
747
+ const locale = this.getCurrentLocale(options);
748
+ const formats = this.getEvaluationFormats(options, locale);
749
+ const translation = this.runFormats(message, values, {
750
+ locale,
751
+ source: "formatMessage",
752
+ meta: undefined,
753
+ formats,
754
+ moduleOptions: options.moduleOptions,
755
+ });
756
+ return this.runTransforms(translation, {
757
+ locale,
758
+ source: "formatMessage",
759
+ meta: undefined,
760
+ });
761
+ }
762
+ translate(messageKey, values, options = {}) {
763
+ const rawMessage = this.getRawTranslation(messageKey, options);
764
+ const locale = this.getCurrentLocale(options);
765
+ const formats = this.getEvaluationFormats(options, locale);
766
+ const meta = this.getMessageMeta(messageKey, locale);
767
+ const translation = this.runFormats(rawMessage, values, {
768
+ locale,
769
+ source: "translation",
770
+ messageKey,
771
+ meta,
772
+ formats,
773
+ moduleOptions: options.moduleOptions,
774
+ });
775
+ return this.runTransforms(translation, {
776
+ locale,
777
+ source: "translation",
778
+ messageKey,
779
+ meta,
780
+ });
781
+ }
782
+ t(messageKey, values, options = {}) {
783
+ if (values === undefined) {
784
+ return this.translate(messageKey, undefined, options);
785
+ }
786
+ return this.translate(messageKey, values, options);
787
+ }
788
+ getRawTranslation(messageKey, options = {}) {
789
+ return this.resolveMessage(messageKey, options.defaultTranslation, options).formatted;
790
+ }
791
+ formatMessage(message, values, options = {}) {
792
+ return this.formatMessageInternal(message, values, options);
793
+ }
794
+ async close() {
795
+ if (this.closePromise) {
796
+ return this.closePromise;
797
+ }
798
+ if (this.closed) {
799
+ return;
800
+ }
801
+ this.closed = true;
802
+ Object.keys(this.listeners).forEach((eventName) => {
803
+ this.listeners[eventName] = [];
804
+ });
805
+ this.moduleDiagnosticSubscriptions = [];
806
+ this.moduleApis = {};
807
+ this.closePromise = this.closeModules();
808
+ return this.closePromise;
809
+ }
810
+ async closeModules() {
811
+ const errors = [];
812
+ const modulesToClose = [...this.modules].reverse();
813
+ for (const module of modulesToClose) {
814
+ if (!module.close) {
815
+ continue;
816
+ }
817
+ try {
818
+ await module.close();
819
+ }
820
+ catch (error) {
821
+ errors.push(error);
822
+ }
823
+ }
824
+ this.modules = [];
825
+ if (errors.length > 0) {
826
+ throw new MessagevisorCloseError("One or more Messagevisor modules failed to close.", errors);
827
+ }
828
+ }
829
+ formatNumber(value, presetOrOptions, options = {}) {
830
+ var _a;
831
+ const locale = this.getCurrentLocale(options);
832
+ const evaluationFormats = this.getEvaluationFormats(options, locale);
833
+ const formatOptions = typeof presetOrOptions === "string"
834
+ ? (_a = evaluationFormats.number) === null || _a === void 0 ? void 0 : _a[presetOrOptions]
835
+ : presetOrOptions;
836
+ const finalOptions = { ...(formatOptions || {}) };
837
+ if (finalOptions.style === "currency") {
838
+ finalOptions.currency = resolveCurrency(options.currency, finalOptions.currency, this.currency);
839
+ }
840
+ return this.getCachedNumberFormat(locale, finalOptions).format(value);
841
+ }
842
+ formatNumberToParts(value, presetOrOptions, options = {}) {
843
+ var _a;
844
+ const locale = this.getCurrentLocale(options);
845
+ const evaluationFormats = this.getEvaluationFormats(options, locale);
846
+ const formatOptions = typeof presetOrOptions === "string"
847
+ ? (_a = evaluationFormats.number) === null || _a === void 0 ? void 0 : _a[presetOrOptions]
848
+ : presetOrOptions;
849
+ const finalOptions = { ...(formatOptions || {}) };
850
+ if (finalOptions.style === "currency") {
851
+ finalOptions.currency = resolveCurrency(options.currency, finalOptions.currency, this.currency);
852
+ }
853
+ return this.getCachedNumberFormat(locale, finalOptions).formatToParts(value);
854
+ }
855
+ formatDate(value, presetOrOptions, options = {}) {
856
+ var _a;
857
+ const locale = this.getCurrentLocale(options);
858
+ const evaluationFormats = this.getEvaluationFormats(options, locale);
859
+ const formatOptions = typeof presetOrOptions === "string"
860
+ ? (_a = evaluationFormats.date) === null || _a === void 0 ? void 0 : _a[presetOrOptions]
861
+ : presetOrOptions;
862
+ return this.getCachedDateTimeFormat(locale, resolveDateTimeOptions(formatOptions, options, this.timeZone)).format(new Date(value));
863
+ }
864
+ formatDateToParts(value, presetOrOptions, options = {}) {
865
+ var _a;
866
+ const locale = this.getCurrentLocale(options);
867
+ const evaluationFormats = this.getEvaluationFormats(options, locale);
868
+ const formatOptions = typeof presetOrOptions === "string"
869
+ ? (_a = evaluationFormats.date) === null || _a === void 0 ? void 0 : _a[presetOrOptions]
870
+ : presetOrOptions;
871
+ return this.getCachedDateTimeFormat(locale, resolveDateTimeOptions(formatOptions, options, this.timeZone)).formatToParts(new Date(value));
872
+ }
873
+ formatTime(value, presetOrOptions, options = {}) {
874
+ var _a;
875
+ const locale = this.getCurrentLocale(options);
876
+ const evaluationFormats = this.getEvaluationFormats(options, locale);
877
+ const formatOptions = typeof presetOrOptions === "string"
878
+ ? (_a = evaluationFormats.time) === null || _a === void 0 ? void 0 : _a[presetOrOptions]
879
+ : presetOrOptions;
880
+ return this.getCachedDateTimeFormat(locale, resolveDateTimeOptions(formatOptions, options, this.timeZone)).format(new Date(value));
881
+ }
882
+ formatTimeToParts(value, presetOrOptions, options = {}) {
883
+ var _a;
884
+ const locale = this.getCurrentLocale(options);
885
+ const evaluationFormats = this.getEvaluationFormats(options, locale);
886
+ const formatOptions = typeof presetOrOptions === "string"
887
+ ? (_a = evaluationFormats.time) === null || _a === void 0 ? void 0 : _a[presetOrOptions]
888
+ : presetOrOptions;
889
+ return this.getCachedDateTimeFormat(locale, resolveDateTimeOptions(formatOptions, options, this.timeZone)).formatToParts(new Date(value));
890
+ }
891
+ formatDateTimeRange(start, end, presetOrOptions, options = {}) {
892
+ var _a;
893
+ const locale = this.getCurrentLocale(options);
894
+ const evaluationFormats = this.getEvaluationFormats(options, locale);
895
+ const formatOptions = typeof presetOrOptions === "string"
896
+ ? (_a = evaluationFormats.dateTimeRange) === null || _a === void 0 ? void 0 : _a[presetOrOptions]
897
+ : presetOrOptions;
898
+ const formatter = this.getCachedDateTimeFormat(locale, resolveDateTimeOptions(formatOptions, options, this.timeZone));
899
+ const rangeFormatter = formatter;
900
+ if (rangeFormatter.formatRange) {
901
+ return rangeFormatter.formatRange(new Date(start), new Date(end));
902
+ }
903
+ return `${formatter.format(new Date(start))} - ${formatter.format(new Date(end))}`;
904
+ }
905
+ formatRelativeTime(value, unit, presetOrOptions, options = {}) {
906
+ var _a;
907
+ const locale = this.getCurrentLocale(options);
908
+ const evaluationFormats = this.getEvaluationFormats(options, locale);
909
+ const formatOptions = typeof presetOrOptions === "string"
910
+ ? (_a = evaluationFormats.relative) === null || _a === void 0 ? void 0 : _a[presetOrOptions]
911
+ : presetOrOptions;
912
+ return this.getCachedRelativeTimeFormat(locale, formatOptions).format(value, unit);
913
+ }
914
+ formatPlural(value, options = {}) {
915
+ const { locale: optionLocale, ...pluralOptions } = options;
916
+ const locale = this.getCurrentLocale({ locale: optionLocale });
917
+ var cacheKey = JSON.stringify({
918
+ locale,
919
+ options: pluralOptions,
920
+ });
921
+ if (!this.cache.pluralRules[cacheKey]) {
922
+ this.cache.pluralRules[cacheKey] = new Intl.PluralRules(locale, pluralOptions);
923
+ }
924
+ return this.cache.pluralRules[cacheKey].select(value);
925
+ }
926
+ formatList(values, options = {}) {
927
+ const { locale: optionLocale, ...listOptions } = options || {};
928
+ const locale = this.getCurrentLocale({ locale: optionLocale });
929
+ var cacheKey = JSON.stringify({
930
+ locale,
931
+ options: listOptions,
932
+ });
933
+ var ListFormat = Intl.ListFormat;
934
+ if (!ListFormat) {
935
+ this.reportDiagnostic({
936
+ level: "warn",
937
+ code: "unsupported_formatter",
938
+ message: "Intl.ListFormat is not available in this environment.",
939
+ locale,
940
+ });
941
+ return values.join(", ");
942
+ }
943
+ if (!this.cache.listFormat[cacheKey]) {
944
+ this.cache.listFormat[cacheKey] = new ListFormat(locale, listOptions);
945
+ }
946
+ return this.cache.listFormat[cacheKey].format(values);
947
+ }
948
+ formatListToParts(values, options = {}) {
949
+ const { locale: optionLocale, ...listOptions } = options || {};
950
+ const locale = this.getCurrentLocale({ locale: optionLocale });
951
+ var cacheKey = JSON.stringify({
952
+ locale,
953
+ options: listOptions,
954
+ });
955
+ var ListFormat = Intl.ListFormat;
956
+ if (!ListFormat) {
957
+ this.reportDiagnostic({
958
+ level: "warn",
959
+ code: "unsupported_formatter",
960
+ message: "Intl.ListFormat is not available in this environment.",
961
+ locale,
962
+ });
963
+ return values;
964
+ }
965
+ if (!this.cache.listFormat[cacheKey]) {
966
+ this.cache.listFormat[cacheKey] = new ListFormat(locale, listOptions);
967
+ }
968
+ if (typeof this.cache.listFormat[cacheKey].formatToParts !== "function") {
969
+ return values;
970
+ }
971
+ return this.cache.listFormat[cacheKey].formatToParts(values);
972
+ }
973
+ formatDisplayName(value, options = {}) {
974
+ const { locale: optionLocale, ...displayNameOptions } = options || {};
975
+ const locale = this.getCurrentLocale({ locale: optionLocale });
976
+ var cacheKey = JSON.stringify({
977
+ locale,
978
+ options: displayNameOptions,
979
+ });
980
+ var DisplayNames = Intl.DisplayNames;
981
+ if (!DisplayNames) {
982
+ this.reportDiagnostic({
983
+ level: "warn",
984
+ code: "unsupported_formatter",
985
+ message: "Intl.DisplayNames is not available in this environment.",
986
+ locale,
987
+ });
988
+ return displayNameOptions && displayNameOptions.fallback === "none" ? undefined : value;
989
+ }
990
+ if (!this.cache.displayNames[cacheKey]) {
991
+ this.cache.displayNames[cacheKey] = new DisplayNames(locale, displayNameOptions);
992
+ }
993
+ return this.cache.displayNames[cacheKey].of(value);
994
+ }
995
+ }
996
+ exports.Messagevisor = Messagevisor;
997
+ function createMessagevisor(options = {}) {
998
+ return new Messagevisor(options);
999
+ }
1000
+ //# sourceMappingURL=instance.js.map