mnfst 0.5.132 → 0.5.134

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.
@@ -11,6 +11,102 @@ const originalHtmlLang = (typeof document !== 'undefined' && document.documentEl
11
11
  ? (document.documentElement.lang || '')
12
12
  : '';
13
13
 
14
+ // RTL language codes — module scope so both the init plugin (sets <html dir>)
15
+ // and the $locale magic (sets $locale.list direction) share one table.
16
+ const rtlLanguages = new Set([
17
+ // Arabic script
18
+ 'ar', // Arabic
19
+ 'az-Arab',// Azerbaijani (Arabic script)
20
+ 'bal', // Balochi
21
+ 'ckb', // Central Kurdish (Sorani)
22
+ 'fa', // Persian (Farsi)
23
+ 'glk', // Gilaki
24
+ 'ks', // Kashmiri
25
+ 'ku-Arab',// Kurdish (Arabic script)
26
+ 'lrc', // Northern Luri
27
+ 'mzn', // Mazanderani
28
+ 'pnb', // Western Punjabi (Shahmukhi)
29
+ 'ps', // Pashto
30
+ 'sd', // Sindhi
31
+ 'ur', // Urdu
32
+
33
+ // Hebrew script
34
+ 'he', // Hebrew
35
+ 'yi', // Yiddish
36
+ 'jrb', // Judeo-Arabic
37
+ 'jpr', // Judeo-Persian
38
+ 'lad-Hebr',// Ladino (Hebrew script)
39
+
40
+ // Thaana script
41
+ 'dv', // Dhivehi (Maldivian)
42
+
43
+ // N’Ko script
44
+ 'nqo', // N’Ko (West Africa)
45
+
46
+ // Syriac script
47
+ 'syr', // Syriac
48
+ 'aii', // Assyrian Neo-Aramaic
49
+ 'arc', // Aramaic
50
+ 'sam', // Samaritan Aramaic
51
+
52
+ // Mandaic script
53
+ 'mid', // Mandaic
54
+
55
+ // Other RTL minority/obscure scripts
56
+ 'uga', // Ugaritic
57
+ 'phn', // Phoenician
58
+ 'xpr', // Parthian (ancient)
59
+ 'peo', // Old Persian (cuneiform, but RTL)
60
+ 'pal', // Middle Persian (Pahlavi)
61
+ 'avst', // Avestan
62
+ 'man', // Manding (N'Ko variants)
63
+ ]);
64
+
65
+ // Detect if a language is RTL
66
+ function isRTL(lang) {
67
+ return rtlLanguages.has(lang);
68
+ }
69
+
70
+ // Native language name (endonym) for a BCP-47 code, e.g. 'fr' → "français".
71
+ // Derived from the browser's Intl.DisplayNames — no data files, no fetches.
72
+ // Falls back to the raw code for custom/unsupported codes.
73
+ const localeNameCache = new Map();
74
+ function localeName(code) {
75
+ if (localeNameCache.has(code)) return localeNameCache.get(code);
76
+ let name = code;
77
+ try {
78
+ if (typeof Intl !== 'undefined' && typeof Intl.DisplayNames === 'function') {
79
+ name = new Intl.DisplayNames([code], { type: 'language' }).of(code) || code;
80
+ }
81
+ } catch { /* unsupported code — keep raw code */ }
82
+ localeNameCache.set(code, name);
83
+ return name;
84
+ }
85
+
86
+ // Rich list backing $locale.list: one object per available locale, with
87
+ // ordering views hung off the array as non-enumerable getters so x-for and
88
+ // spreads only ever see the locale items, never the view accessors.
89
+ // $locale.list → source (manifest.json) order
90
+ // $locale.list.alphabetical → by native name, locale-aware compare
91
+ // $locale.list.currentFirst → active locale first, rest in source order
92
+ function buildLocaleList(available, current) {
93
+ const list = available.map(code => ({
94
+ code,
95
+ name: localeName(code),
96
+ direction: isRTL(code) ? 'rtl' : 'ltr',
97
+ current: code === current
98
+ }));
99
+ Object.defineProperty(list, 'alphabetical', {
100
+ enumerable: false,
101
+ get() { return [...list].sort((a, b) => a.name.localeCompare(b.name)); }
102
+ });
103
+ Object.defineProperty(list, 'currentFirst', {
104
+ enumerable: false,
105
+ get() { return [...list].sort((a, b) => (b.current === true) - (a.current === true)); }
106
+ });
107
+ return list;
108
+ }
109
+
14
110
  // Global setLocale wrapper - will be replaced with real implementation
15
111
  let setLocaleImpl = null;
16
112
 
@@ -45,61 +141,6 @@ function initializeLocalizationPlugin() {
45
141
  // Debug logging disabled for production
46
142
  const debugLog = () => { };
47
143
 
48
- // RTL language codes - using Set for O(1) lookups
49
- const rtlLanguages = new Set([
50
- // Arabic script
51
- 'ar', // Arabic
52
- 'az-Arab',// Azerbaijani (Arabic script)
53
- 'bal', // Balochi
54
- 'ckb', // Central Kurdish (Sorani)
55
- 'fa', // Persian (Farsi)
56
- 'glk', // Gilaki
57
- 'ks', // Kashmiri
58
- 'ku-Arab',// Kurdish (Arabic script)
59
- 'lrc', // Northern Luri
60
- 'mzn', // Mazanderani
61
- 'pnb', // Western Punjabi (Shahmukhi)
62
- 'ps', // Pashto
63
- 'sd', // Sindhi
64
- 'ur', // Urdu
65
-
66
- // Hebrew script
67
- 'he', // Hebrew
68
- 'yi', // Yiddish
69
- 'jrb', // Judeo-Arabic
70
- 'jpr', // Judeo-Persian
71
- 'lad-Hebr',// Ladino (Hebrew script)
72
-
73
- // Thaana script
74
- 'dv', // Dhivehi (Maldivian)
75
-
76
- // N’Ko script
77
- 'nqo', // N’Ko (West Africa)
78
-
79
- // Syriac script
80
- 'syr', // Syriac
81
- 'aii', // Assyrian Neo-Aramaic
82
- 'arc', // Aramaic
83
- 'sam', // Samaritan Aramaic
84
-
85
- // Mandaic script
86
- 'mid', // Mandaic
87
-
88
- // Other RTL minority/obscure scripts
89
- 'uga', // Ugaritic
90
- 'phn', // Phoenician
91
- 'xpr', // Parthian (ancient)
92
- 'peo', // Old Persian (cuneiform, but RTL)
93
- 'pal', // Middle Persian (Pahlavi)
94
- 'avst', // Avestan
95
- 'man', // Manding (N'Ko variants)
96
- ]);
97
-
98
- // Detect if a language is RTL
99
- function isRTL(lang) {
100
- return rtlLanguages.has(lang);
101
- }
102
-
103
144
  function isPrerenderedStaticBuild() {
104
145
  return document.head?.querySelector('meta[name="manifest:prerendered"][content="1"]') !== null;
105
146
  }
@@ -712,6 +753,13 @@ function registerLocaleMagic() {
712
753
  if (prop === 'current') return currentStore?.current || document.documentElement.lang || 'en';
713
754
  if (prop === 'available') return currentStore?.available || [document.documentElement.lang || 'en'];
714
755
  if (prop === 'direction') return currentStore?.direction || 'ltr';
756
+ if (prop === 'list') {
757
+ const fallback = document.documentElement.lang || 'en';
758
+ return buildLocaleList(
759
+ currentStore?.available || [fallback],
760
+ currentStore?.current || fallback
761
+ );
762
+ }
715
763
  if (prop === 'set') {
716
764
  // Use the global setLocale function (wrapper or real implementation)
717
765
  return async (locale, updateUrl = false) => {
@@ -43,7 +43,13 @@ if (!window.ManifestDOMPurify) {
43
43
  if (this._promise) return this._promise;
44
44
  this._promise = new Promise((resolve, reject) => {
45
45
  const script = document.createElement('script');
46
- script.src = 'https://cdn.jsdelivr.net/npm/dompurify@latest/dist/purify.min.js';
46
+ // Pinned + Subresource Integrity: a moving `@latest` (or a
47
+ // tampered CDN file) would otherwise run arbitrary JS in the
48
+ // user's page. The browser rejects the script if the bytes don't
49
+ // match the hash. Bump version AND integrity together.
50
+ script.src = 'https://cdn.jsdelivr.net/npm/dompurify@3.4.10/dist/purify.min.js';
51
+ script.integrity = 'sha384-eguRoJERj8ghOpzO//Rl7+ScQsQIR1cH+ajll7+fG+IpbNPlkZsQn9h8ccr+wPXx';
52
+ script.crossOrigin = 'anonymous';
47
53
  script.onload = () => {
48
54
  if (typeof window.DOMPurify !== 'undefined') {
49
55
  resolve(window.DOMPurify);
@@ -96,7 +102,11 @@ async function loadMarkedJS() {
96
102
 
97
103
  markedPromise = new Promise((resolve, reject) => {
98
104
  const script = document.createElement('script');
99
- script.src = 'https://cdn.jsdelivr.net/npm/marked/marked.min.js';
105
+ // Pinned + Subresource Integrity (see DOMPurify loader above) — a
106
+ // floating version or tampered CDN file can't inject arbitrary JS.
107
+ script.src = 'https://cdn.jsdelivr.net/npm/marked@15.0.12/marked.min.js';
108
+ script.integrity = 'sha384-948ahk4ZmxYVYOc+rxN1H2gM1EJ2Duhp7uHtZ4WSLkV4Vtx5MUqnV+l7u9B+jFv+';
109
+ script.crossOrigin = 'anonymous';
100
110
  script.onload = () => {
101
111
  // Initialize marked.js
102
112
  if (typeof marked !== 'undefined') {