imapflow 1.5.0 → 1.6.1

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 (36) hide show
  1. package/.release-please-manifest.json +1 -1
  2. package/CHANGELOG.md +22 -0
  3. package/CLAUDE.md +1 -1
  4. package/lib/commands/idle.js +197 -104
  5. package/lib/commands/list.js +15 -7
  6. package/lib/commands/quota.js +3 -0
  7. package/lib/commands/select.js +5 -0
  8. package/lib/commands/status.js +4 -0
  9. package/lib/connection-deadline.js +98 -0
  10. package/lib/handler/imap-compiler.js +8 -5
  11. package/lib/handler/imap-stream.js +141 -50
  12. package/lib/handler/limits.js +43 -0
  13. package/lib/handler/token-parser.js +31 -1
  14. package/lib/imap-flow.d.ts +33 -5
  15. package/lib/imap-flow.js +583 -281
  16. package/lib/proxy-connection.js +393 -98
  17. package/lib/special-use.js +660 -51
  18. package/lib/tools.js +17 -0
  19. package/package.json +2 -2
  20. package/test/commands-branches-test.js +17 -1
  21. package/test/commands-integration-test.js +24 -2
  22. package/test/fixtures/fake-timers.js +115 -0
  23. package/test/handler-branches-test.js +0 -25
  24. package/test/idle-polling-test.js +349 -0
  25. package/test/imap-flow-compress-test.js +12 -0
  26. package/test/imap-flow-coverage-test.js +3 -3
  27. package/test/imap-flow-internals-test.js +23 -0
  28. package/test/imap-flow-proxy-paths-test.js +151 -0
  29. package/test/imap-flow-secure-test.js +159 -0
  30. package/test/imap-flow-server-test.js +186 -0
  31. package/test/parser-limits-test.js +274 -0
  32. package/test/proxy-connection-test.js +553 -442
  33. package/test/reliability-improvements-test.js +87 -0
  34. package/test/special-use-test.js +337 -0
  35. package/test/tag-correlation-test.js +333 -0
  36. package/test/timer-policy-test.js +214 -0
@@ -1,5 +1,80 @@
1
1
  'use strict';
2
2
 
3
+ // Generic mail nouns that decorate a special-use folder name without changing what
4
+ // it means: "Sent Mail", "Gelöschte Elemente", "Отправленные письма". Stripping them
5
+ // lets a decorated name fall back to its core word. A name that still has more than
6
+ // one meaningful word left after stripping is treated as a user folder and rejected,
7
+ // which is what separates "Sent Mail" (a real Sent folder) from "Sent to clients".
8
+ // Words that name a special-use folder on their own (like "bin") do not belong here,
9
+ // and neither do hyphenated words, since TOKEN_SPLIT below splits on the hyphen and
10
+ // no such token could ever be produced.
11
+ // Folded by construction so the set matches the form tokens are compared against,
12
+ // the same invariant the name tables are tested for.
13
+ const GENERIC_TOKENS = new Set(
14
+ [
15
+ // English. "e" is here because TOKEN_SPLIT breaks on the hyphen, so the
16
+ // "e-mail" / "e-posta" / "e-kirjad" family arrives as a bare "e" token.
17
+ 'e',
18
+ 'mail',
19
+ 'mails',
20
+ 'email',
21
+ 'emails',
22
+ 'message',
23
+ 'messages',
24
+ 'item',
25
+ 'items',
26
+ 'folder',
27
+ 'my',
28
+ // German, Dutch, Nordic
29
+ 'objekt',
30
+ 'objekte',
31
+ 'objekten',
32
+ 'objekter',
33
+ 'elemente',
34
+ 'elementen',
35
+ 'elementer',
36
+ 'nachrichten',
37
+ 'berichten',
38
+ 'post',
39
+ 'poster',
40
+ 'viestit',
41
+ 'kirjad',
42
+ 'meldinger',
43
+ 'beskeder',
44
+ // Romance
45
+ 'correo',
46
+ 'posta',
47
+ 'courrier',
48
+ 'elementos',
49
+ 'elementi',
50
+ 'éléments',
51
+ 'mensajes',
52
+ 'messaggi',
53
+ 'mensagens',
54
+ 'itens',
55
+ // Slavic
56
+ 'poczta',
57
+ 'pošta',
58
+ 'elementy',
59
+ 'wiadomości',
60
+ 'položky',
61
+ 'správy',
62
+ 'письма',
63
+ 'письмо',
64
+ 'элементы',
65
+ 'сообщения',
66
+ 'пошта',
67
+ // Other
68
+ 'öğeler',
69
+ 'mesajlar',
70
+ 'μηνύματα',
71
+ 'στοιχεία'
72
+ ].map(token => token.toLowerCase().normalize('NFKC'))
73
+ );
74
+
75
+ // Word separators seen in folder names across clients.
76
+ const TOKEN_SPLIT = /[\s\-_/.,()[\]]+/;
77
+
3
78
  // Localized folder name mappings for detecting special-use mailboxes.
4
79
  // When the server does not advertise the SPECIAL-USE extension (RFC 6154),
5
80
  // we fall back to matching folder names against these lists of known
@@ -9,306 +84,840 @@ module.exports = {
9
84
  names: {
10
85
  '\\Sent': [
11
86
  'aika',
87
+ 'air a chur',
88
+ 'am post cuirte',
89
+ 'anfonwyd',
90
+ 'bidalia',
12
91
  'bidaliak',
13
92
  'bidalita',
93
+ 'bidalitakoak',
94
+ 'currieru mandatu',
95
+ 'danfonwyd',
14
96
  'dihantar',
15
97
  'e rometsweng',
16
98
  'e tindami',
99
+ 'elementos enviados',
17
100
  'elküldött',
101
+ 'elküldött elemek',
102
+ 'elküldött üzenetek',
18
103
  'elküldöttek',
19
- 'elementos enviados',
20
- 'éléments envoyés',
21
- 'enviadas',
22
104
  'enviadas',
23
105
  'enviados',
106
+ 'enviat',
24
107
  'enviats',
25
108
  'envoyés',
26
109
  'ethunyelweyo',
27
110
  'expediate',
28
111
  'ezipuru',
112
+ 'ferstjoerd',
113
+ 'gesendet',
29
114
  'gesendete',
30
115
  'gesendete elemente',
31
116
  'gestuur',
117
+ 'gönderilmiş',
32
118
  'gönderilmiş öğeler',
33
119
  'göndərilənlər',
120
+ 'hantar',
34
121
  'iberilen',
122
+ 'inviata',
123
+ 'inviate',
35
124
  'inviati',
125
+ 'išsiųsti',
126
+ 'išsiųsti laiškai',
36
127
  'išsiųstieji',
128
+ 'jo`natilgan xatlar',
129
+ 'jo‘natilgan',
130
+ 'kaset',
37
131
  'kuthunyelwe',
132
+ 'küldöttek',
38
133
  'lasa',
39
134
  'lähetetyt',
135
+ 'mesaje trimise',
40
136
  'messages envoyés',
41
137
  'naipadala',
42
138
  'nalefa',
43
139
  'napadala',
140
+ 'nosūtīts',
141
+ 'nosūtītās',
44
142
  'nosūtītās ziņas',
45
- 'odeslané',
46
143
  'odeslaná pošta',
144
+ 'odeslané',
145
+ 'odoslaná',
146
+ 'odoslaná pošta',
147
+ 'odoslané',
47
148
  'padala',
149
+ 'poslana pošta',
48
150
  'poslane',
49
151
  'poslano',
50
- 'poslano',
51
152
  'poslané',
52
153
  'poslato',
154
+ 'posta inviata',
155
+ 'pósłane',
156
+ 'pósłany',
53
157
  'saadetud',
54
158
  'saadetud kirjad',
55
159
  'saadetud üksused',
160
+ 'senditujo',
56
161
  'sendt',
57
- 'sendt',
162
+ 'sendt post',
163
+ 'sendte',
164
+ 'sendte beskeder',
58
165
  'sent',
59
166
  'sent items',
60
167
  'sent messages',
168
+ 'seolta',
169
+ 'siūsti',
170
+ 'skickat',
61
171
  'sända poster',
62
172
  'sänt',
173
+ 'sūtīt',
63
174
  'terkirim',
175
+ 'thư đã gửi',
64
176
  'ti fi ranṣẹ',
177
+ 'titaq',
178
+ 'tramess',
179
+ 'trimis',
180
+ 'trimise',
181
+ 'ttwaznen',
182
+ 'të dërguar',
65
183
  'të dërguara',
184
+ 'unviaos',
185
+ 'unvios fechos',
186
+ 'verschéckt',
66
187
  'verzonden',
188
+ 'verzonden berichten',
67
189
  'vilivyotumwa',
68
190
  'wysłane',
191
+ 'éléments envoyés',
69
192
  'đã gửi',
193
+ 'şandî',
194
+ 'απεσταλμένα',
70
195
  'σταλθέντα',
196
+ 'адпраўлена',
71
197
  'жиберилген',
198
+ 'жіберілген',
199
+ 'жіберілген хаттар',
72
200
  'жіберілгендер',
73
201
  'изпратени',
202
+ 'изпратени писма',
74
203
  'илгээсэн',
75
204
  'ирсол шуд',
205
+ 'испратени',
76
206
  'испратено',
77
207
  'надіслані',
78
208
  'отправленные',
79
209
  'пасланыя',
210
+ 'послате',
211
+ 'послато',
212
+ 'пратени',
80
213
  'юборилган',
214
+ 'ուղարկուած',
81
215
  'ուղարկված',
216
+ 'נשלח',
82
217
  'נשלחו',
83
218
  'פריטים שנשלחו',
219
+ 'ئەۋەتىلگەن',
220
+ 'ارسال شده',
221
+ 'ارسالی',
222
+ 'البريد المرسل',
223
+ 'المرسل',
84
224
  'المرسلة',
225
+ 'المُرسَل',
226
+ 'بھیجا ہوا میل',
85
227
  'بھیجے گئے',
86
228
  'سوزمژہ',
229
+ 'لېږل شوي ليکونه',
87
230
  'لېګل شوی',
88
231
  'موارد ارسال شده',
232
+ 'مُرسَل',
233
+ 'نێردراو',
234
+ 'पठईएका मेलहरु',
235
+ 'पाठवलेले',
89
236
  'पाठविले',
90
237
  'पाठविलेले',
91
238
  'प्रेषित',
92
239
  'भेजा गया',
240
+ 'भेजे गए',
241
+ 'পাঠানো হয়েছে',
93
242
  'প্রেরিত',
94
- 'প্রেরিত',
243
+ 'প্রেরিত(পাঠানো মেইল)',
95
244
  'প্ৰেৰিত',
96
245
  'ਭੇਜੇ',
97
246
  'મોકલેલા',
247
+ 'મોકલેલ્યા',
98
248
  'ପଠାଗଲା',
249
+ 'அனுப்பிய அஞ்சல்',
99
250
  'அனுப்பியவை',
100
251
  'పంపించబడింది',
101
252
  'ಕಳುಹಿಸಲಾದ',
253
+ 'അയച്ചവ',
102
254
  'അയച്ചു',
103
255
  'යැවු පණිවුඩ',
256
+ 'යැවූ',
257
+ 'ที่ส่งแล้ว',
258
+ 'ส่ง',
104
259
  'ส่งแล้ว',
105
260
  'გაგზავნილი',
261
+ 'ዝተለኣኸ',
106
262
  'የተላኩ',
107
263
  'បាន​ផ្ញើ',
108
- '寄件備份',
264
+ 'សំបុត្រដែលបានបញ្ជូន',
109
265
  '寄件備份',
110
266
  '已发信息',
111
- '送信済みメール',
267
+ '已发送',
268
+ '已发送消息',
269
+ '已发送邮件',
270
+ '送信済み',
271
+ '送信済みアイテム',
272
+ '送信済みトレイ',
273
+ '送信済みメール',
112
274
  '발신 메시지',
113
- '보낸 편지함'
275
+ '보낸 편지함',
276
+ '보냄'
114
277
  ],
115
278
  '\\Trash': [
279
+ 'an sgudal',
116
280
  'articole șterse',
281
+ 'atkritne',
117
282
  'bin',
283
+ 'borttaget',
118
284
  'borttagna objekt',
285
+ 'bosca bruscair',
286
+ 'bruscar',
287
+ 'cestino',
288
+ 'chanaster da palpiri',
289
+ 'chiqitdon',
290
+ 'corbe a papiro',
291
+ 'corbeille',
292
+ 'coș de gunoi',
293
+ 'curbella',
119
294
  'deleted',
120
295
  'deleted items',
121
296
  'deleted messages',
297
+ 'dzēstie vienumi',
122
298
  'elementi eliminati',
123
299
  'elementos borrados',
124
300
  'elementos eliminados',
125
- 'gelöschte objekte',
301
+ 'elements suprimits',
302
+ 'eliminata',
303
+ 'gelöscht',
126
304
  'gelöschte elemente',
305
+ 'gelöschte objekte',
306
+ 'gunoi',
307
+ 'hedhurina',
127
308
  'item dipadam',
128
309
  'itens apagados',
310
+ 'itens eliminados',
129
311
  'itens excluídos',
312
+ 'ištrinti',
313
+ 'iḍuman',
314
+ 'jiskefet',
315
+ 'jêbirdank',
316
+ 'kanta',
317
+ 'kest',
318
+ 'kosz',
319
+ 'koš',
320
+ 'kuka',
321
+ 'kustutatud',
130
322
  'kustutatud üksused',
323
+ 'kôš',
324
+ 'lixeira',
325
+ 'lixo',
326
+ 'lomtár',
327
+ 'molwuj',
131
328
  'mục đã xóa',
132
- 'odstraněné položky',
329
+ 'obrisane stavke',
330
+ 'odpadkový kôš',
133
331
  'odstraněná pošta',
332
+ 'odstraněné položky',
333
+ 'papeleira',
334
+ 'papelera',
335
+ 'paperera',
336
+ 'papierkorb',
337
+ 'papirkorg',
338
+ 'papirkurv',
339
+ 'papjernik',
340
+ 'papperskorg',
341
+ 'papperskorgen',
342
+ 'papīrgrozs',
343
+ 'papīrkurvis',
134
344
  'pesan terhapus',
345
+ 'pod-lastez',
135
346
  'poistetut',
347
+ 'poubelle',
136
348
  'praht',
349
+ 'prullenbak',
137
350
  'prügikast',
351
+ 'reciclagem',
352
+ 'roskakori',
353
+ 'rubujo',
354
+ 'rusl',
355
+ 'savat',
356
+ 'sbwriel',
357
+ 'sgudal',
138
358
  'silinmiş öğeler',
359
+ 'skrell',
360
+ 'sletta',
139
361
  'slettede beskeder',
140
362
  'slettede elementer',
363
+ 'slettet',
364
+ 'smeti',
365
+ 'smeće',
366
+ 'snippermandjie',
367
+ 'surat terhapus',
368
+ 'səbət',
369
+ 'thùng rác',
370
+ 'tong sampah',
141
371
  'trash',
142
- 'törölt elemek',
143
372
  'törölt',
373
+ 'törölt elemek',
374
+ 'usunięte',
144
375
  'usunięte wiadomości',
145
376
  'verwijderde items',
146
377
  'vymazané správy',
378
+ 'zakarrontzia',
379
+ 'çöp',
380
+ 'çöp kutusu',
147
381
  'éléments supprimés',
382
+ 'šiukšliadėžė',
383
+ 'šiukšlinė',
384
+ 'šiukšlių dėžė',
385
+ 'șterse',
386
+ 'απορρίμματα',
387
+ 'διαγραμμένα',
388
+ 'κάδος απορριμάτων',
389
+ 'κάδος απορριμμάτων',
148
390
  'видалені',
391
+ 'выдаленыя',
149
392
  'жойылғандар',
393
+ 'избришани',
394
+ 'изтрити',
395
+ 'канта',
396
+ 'корзина',
397
+ 'корпа',
398
+ 'кошик',
399
+ 'кошче',
400
+ 'обрисане ставке',
401
+ 'себет',
402
+ 'сметніца',
403
+ 'смеће',
404
+ 'смітник',
150
405
  'удаленные',
406
+ 'удалённые',
407
+ 'хогийн сав',
408
+ 'қоқыс шелегі',
409
+ 'աղբարկղ',
410
+ 'אשפה',
151
411
  'פריטים שנמחקו',
412
+ 'اشغال دانی',
152
413
  'العناصر المحذوفة',
414
+ 'المهملات',
415
+ 'ردی کی ٹوکری',
416
+ 'زباله‌دان',
417
+ 'زبڵدان',
153
418
  'موارد حذف شده',
419
+ 'مُهملات',
420
+ 'کثافت دانۍ',
421
+ 'कचरा',
422
+ 'कचरा पेटी',
423
+ 'रद्दी',
424
+ 'रद्दी टोकरी',
425
+ 'ঝুড়ি',
426
+ 'ডাস্টবিন',
427
+ 'કચરો',
428
+ 'குப்பை',
429
+ 'ചവറ്റുകുട്ട',
430
+ 'ඉවතලන බඳුන',
431
+ 'ถังขยะ',
154
432
  'รายการที่ลบ',
433
+ 'ნაგავი',
434
+ 'ურნა',
435
+ 'წაშლილი',
436
+ 'እንዳጉሓፍ',
437
+ 'ធុងសំរាម',
438
+ 'ごみ箱',
439
+ 'ゴミ箱',
440
+ '削除済みアイテム',
441
+ '回收站',
442
+ '垃圾桶',
155
443
  '已删除邮件',
156
444
  '已刪除項目',
157
- '已刪除項目'
445
+ '废件箱',
446
+ '지운 편지함',
447
+ '휴지통'
158
448
  ],
159
449
  '\\Junk': [
450
+ 'aspam',
451
+ 'basura',
452
+ 'brukalas',
160
453
  'bulk mail',
454
+ 'cajk',
455
+ 'correo basura',
456
+ 'correo lixo',
161
457
  'correo no deseado',
458
+ 'correu brossa',
459
+ 'corréu puxarra',
162
460
  'courrier indésirable',
461
+ 'dramhaíl',
462
+ 'dramhphost',
463
+ 'gemors',
464
+ 'gemorspos',
465
+ 'gereksiz',
466
+ 'indesiderata',
467
+ 'indesirate',
468
+ 'indésirables',
163
469
  'istenmeyen',
164
470
  'istenmeyen e-posta',
471
+ 'i̇stenmeyen',
165
472
  'junk',
166
473
  'junk e-mail',
167
474
  'junk email',
475
+ 'junk mail',
168
476
  'junk-e-mail',
477
+ 'kéretlen',
478
+ 'lastez',
169
479
  'levélszemét',
480
+ 'lixo electrónico',
481
+ 'lixo eletrónico',
482
+ 'lixo eletrônico',
483
+ 'mel remeh',
484
+ 'mesaje nedorite',
485
+ 'møsn',
486
+ 'mēstule',
487
+ 'mēstules',
488
+ 'nepoželjne',
489
+ 'nesolicitate',
490
+ 'net-winske',
170
491
  'nevyžiadaná pošta',
492
+ 'nevyžádaná',
171
493
  'nevyžádaná pošta',
494
+ 'nevēlams',
495
+ 'nezaželeno',
496
+ 'neželena pošta',
497
+ 'neželena sporočila',
498
+ 'neželjena pošta',
499
+ 'niechciane',
172
500
  'no deseado',
501
+ 'nungiavischà',
502
+ 'ongewenst',
503
+ 'ongewenste e-mail',
173
504
  'posta indesiderata',
505
+ 'posta indesirate',
174
506
  'pourriel',
507
+ 'pourriels',
508
+ 'puxarra',
509
+ 'roskaa',
175
510
  'roskaposti',
511
+ 'roskapostit',
512
+ 'ruslpóstur',
513
+ 'rämps',
176
514
  'rämpspost',
515
+ 'sbam',
516
+ "seq'",
517
+ 'skräp',
177
518
  'skräppost',
178
- 'spam',
519
+ 'sothach',
179
520
  'spam',
180
521
  'spamowanie',
522
+ 'spamujo',
523
+ 'stroboù',
524
+ 'szemét',
181
525
  'søppelpost',
182
526
  'thư rác',
527
+ 'truilleis',
528
+ 'të pavlera',
529
+ 'të pavlerë',
530
+ 'uønsket',
531
+ 'uønsket e-mail',
532
+ 'uønsket e-post',
533
+ 'uønsket post',
534
+ 'uønskt',
183
535
  'wiadomości-śmieci',
536
+ 'zabor-posta',
537
+ 'zaborra',
538
+ 'önemsiz',
539
+ 'čapor',
540
+ 'šiukšlės',
541
+ 'šlamštas',
542
+ 'ανεπιθύμητα',
543
+ 'ανεπιθύμητη αλληλογραφία',
544
+ 'небажана пошта',
545
+ 'нежелана поща',
546
+ 'нежелательная почта',
547
+ 'непажаданая пошта',
548
+ 'непожељна пошта',
549
+ 'непожељне',
550
+ 'непожељно',
551
+ 'непосакувана пошта',
184
552
  'спам',
553
+ 'қалаусыз пошта',
554
+ 'անպիտան',
555
+ 'թափոն',
556
+ 'սպամ',
185
557
  'דואר זבל',
558
+ 'זבל',
186
559
  'الرسائل العشوائية',
560
+ 'الرسائل غير المرغوب فيها',
561
+ 'بنجل',
562
+ 'بیکاره',
563
+ 'بێکه‌ڵک',
564
+ 'جنک',
565
+ 'جنک میل',
566
+ 'سُخام',
567
+ 'غير المرغوب',
187
568
  'هرزنامه',
569
+ 'नको असलेले कचरा संदेश',
570
+ 'स्पाम',
571
+ 'स्पैम',
572
+ 'আজেবাজে মেইল',
573
+ 'எரிதம்',
574
+ 'ആവശ്യമില്ലാത്തവ',
575
+ 'සුන්බුන්',
576
+ 'กล่องจดหมายขยะ',
577
+ 'ขยะ',
188
578
  'สแปม',
189
- '垃圾郵件',
579
+ 'อีเมลขยะ',
580
+ 'სპამი',
581
+ 'ჯართი',
582
+ 'እንዳቅንጠመንጢ',
583
+ 'សំបុត្រមិនល្អ',
584
+ 'ឥត​បាន​ការ',
585
+ '垃圾',
586
+ '垃圾信件',
190
587
  '垃圾邮件',
191
- '垃圾電郵'
588
+ '垃圾郵件',
589
+ '垃圾電郵',
590
+ '迷惑メール',
591
+ '스팸',
592
+ '스팸 편지함',
593
+ '정크',
594
+ '정크 메일'
192
595
  ],
193
596
  '\\Drafts': [
597
+ 'arewway',
194
598
  'ba brouillon',
195
599
  'borrador',
196
- 'borrador',
197
600
  'borradores',
198
601
  'bozze',
602
+ 'brouilhedoù',
603
+ 'brouillonen',
199
604
  'brouillons',
605
+ 'bruttacopie',
200
606
  'bản thảo',
201
607
  'ciorne',
202
608
  'concepten',
203
609
  'draf',
610
+ 'drafftiau',
204
611
  'draft',
205
612
  'drafts',
613
+ 'dreachdan',
614
+ 'dréachtaí',
206
615
  'drög',
207
616
  'entwürfe',
208
617
  'esborranys',
618
+ 'esbossos',
209
619
  'garalamalar',
210
620
  'ihe edeturu',
211
621
  'iidrafti',
212
622
  'izinhlaka',
213
623
  'juodraščiai',
624
+ 'jusamaj',
214
625
  'kladd',
626
+ 'kladdar',
215
627
  'kladder',
216
628
  'koncepty',
217
- 'koncepty',
218
629
  'konsep',
219
630
  'konsepte',
631
+ 'konsepten',
220
632
  'kopie robocze',
221
633
  'layihələr',
222
634
  'luonnokset',
635
+ 'malnetujo',
223
636
  'melnraksti',
224
637
  'meralo',
638
+ 'mesaje nefinalizate',
225
639
  'mesazhe të padërguara',
226
640
  'mga draft',
227
641
  'mustandid',
642
+ 'nacerjenja',
228
643
  'nacrti',
229
- 'nacrti',
644
+ 'naćiski',
645
+ 'nedovršene',
646
+ 'nedovršeno',
647
+ 'onvoltooid',
230
648
  'osnutki',
231
649
  'piszkozatok',
650
+ 'qaralamalar',
651
+ 'qoralama xatlar',
652
+ 'qoralamalar',
232
653
  'rascunhos',
233
654
  'rasimu',
655
+ 'reşnivîs',
656
+ 'rozepsané',
657
+ 'sbozs',
658
+ 'skica',
234
659
  'skice',
660
+ 'skitsur',
661
+ 'szkice',
662
+ 'taslak',
235
663
  'taslaklar',
664
+ 'thư nháp',
236
665
  'tsararrun saƙonni',
237
666
  'utkast',
667
+ 'uzmetumi',
238
668
  'vakiraoka',
669
+ 'versiones provisori',
239
670
  'vázlatok',
671
+ 'wersje robocze',
240
672
  'zirriborroak',
241
673
  'àwọn àkọpamọ́',
674
+ 'προσχέδια',
242
675
  'πρόχειρα',
676
+ 'драфтови',
677
+ 'жоба жазбалар',
243
678
  'жобалар',
244
679
  'нацрти',
680
+ 'недовршене',
681
+ 'недовршено',
682
+ 'непратени',
245
683
  'нооргууд',
684
+ 'ноорог',
246
685
  'сиёҳнавис',
686
+ 'скици',
247
687
  'хомаки хатлар',
248
688
  'чарнавікі',
249
689
  'чернетки',
250
690
  'чернови',
251
691
  'черновики',
252
692
  'черновиктер',
253
- 'սևագրեր',
693
+ 'шимай қағаз',
694
+ 'սեւագրեր',
254
695
  'טיוטות',
696
+ 'المسودات',
697
+ 'بارليک',
698
+ 'قوليازمىلار',
255
699
  'مسودات',
256
- 'مسودات',
700
+ 'مسوّدات',
257
701
  'موسودې',
702
+ 'نامه های ناتکمیل',
258
703
  'پیش نویسها',
704
+ 'پیش‌نویس‌ها',
705
+ 'ڈرافٹ',
259
706
  'ڈرافٹ/',
260
- 'ड्राफ़्ट',
707
+ 'ڕه‌شنووسەکان',
708
+ 'ड्राफ़ट',
709
+ 'ड्राफ़्ट',
710
+ 'ड्राफ्ट',
711
+ 'ड्राफ्टहरू',
261
712
  'प्रारूप',
262
- 'খসড়া',
263
- 'খসড়া',
713
+ 'मसुदा',
714
+ 'খসড়া',
264
715
  'ড্ৰাফ্ট',
265
716
  'ਡ੍ਰਾਫਟ',
717
+ 'ડ્રાફ્ટ',
266
718
  'ડ્રાફ્ટસ',
267
719
  'ଡ୍ରାଫ୍ଟ',
268
720
  'வரைவுகள்',
269
721
  'చిత్తు ప్రతులు',
270
722
  'ಕರಡುಗಳು',
271
723
  'കരടുകള്‍',
724
+ 'ഡ്രാഫ്റ്റുകൾ',
725
+ 'പൂര്‍ത്തിയാകാത്തവ',
726
+ 'කටු සටහන්',
272
727
  'කෙටුම් පත්',
728
+ 'กล่องจดหมายร่าง',
273
729
  'ฉบับร่าง',
730
+ 'ร่าง',
731
+ 'လိက်အပြောံ',
732
+ 'დროებითი',
274
733
  'მონახაზები',
734
+ 'წინასწარი',
275
735
  'ረቂቆች',
736
+ 'ወጡን ጽሑፍ',
276
737
  'សារព្រាង',
738
+ 'សេចក្ដី​ព្រាង​',
739
+ 'សំបុត្រពង្រៀង',
277
740
  '下書き',
278
741
  '草稿',
279
- '草稿',
280
- '草稿',
281
- '임시 보관함'
742
+ '草稿匣',
743
+ '草稿箱',
744
+ '임시 보관함',
745
+ '초안'
282
746
  ],
283
747
 
284
- '\\Archive': ['archive']
285
- },
748
+ '\\Archive': [
749
+ 'an chartlann',
750
+ 'archief',
751
+ 'archieven',
752
+ 'archif',
753
+ 'archifau',
754
+ 'archiv',
755
+ 'archivados',
756
+ 'archivar',
757
+ 'archive',
758
+ 'archives',
759
+ 'archivi',
760
+ 'archivio',
761
+ 'archivo',
762
+ 'archivos',
763
+ 'archivovať',
764
+ 'archivs',
765
+ 'archivu',
766
+ 'archiválás',
767
+ 'archiwum',
768
+ 'archiwy',
769
+ 'archyvas',
770
+ 'archyvuoti',
771
+ 'archív',
772
+ 'archívum',
773
+ 'archívy',
774
+ 'argief',
775
+ 'argiven',
776
+ 'argyf',
777
+ 'arhiiv',
778
+ 'arhiv',
779
+ 'arhiva',
780
+ 'arhive',
781
+ 'arhivi',
782
+ 'arhivă',
783
+ 'arhīvi',
784
+ 'arhīvs',
785
+ 'arkib',
786
+ 'arkisto',
787
+ 'arkiv',
788
+ 'arkiva',
789
+ 'arkiver',
790
+ 'arkivo',
791
+ 'arkivoje',
792
+ 'arquivamento',
793
+ 'arquivo',
794
+ 'arquivo morto',
795
+ 'arquivos',
796
+ 'arsip',
797
+ 'artxibatu',
798
+ 'artxiboa',
799
+ 'artxiboak',
800
+ 'arxiu',
801
+ 'arxiv',
802
+ 'arxivlar',
803
+ 'arşiv',
804
+ 'arşivler',
805
+ 'aḥraz',
806
+ 'cartlanna',
807
+ 'diell',
808
+ 'dielloù',
809
+ 'erşîv',
810
+ 'geymsla',
811
+ 'goym í skjalasavni',
812
+ 'iɣbaren',
813
+ 'lưu trữ',
814
+ 'skjalageymsla',
815
+ "taq yakb'äl",
816
+ 'tasg-lannan',
817
+ 'αρχειοθέτηση',
818
+ 'αρχειοθήκη',
819
+ 'архив',
820
+ 'архива',
821
+ 'архиве',
822
+ 'архиви',
823
+ 'архивирай',
824
+ 'архивтер',
825
+ 'архивы',
826
+ 'архів',
827
+ 'архіви',
828
+ 'архівы',
829
+ 'архіў',
830
+ 'мұрағат',
831
+ 'արխիվ',
832
+ 'արխիւներ',
833
+ 'ארכיון',
834
+ 'أرشفة',
835
+ 'أرشيف',
836
+ 'ئارخىپ',
837
+ 'ئەرشیف',
838
+ 'ارشیو',
839
+ 'الأرشيف',
840
+ 'بایگانی',
841
+ 'जतन केलेला',
842
+ 'संग्रह',
843
+ 'ശേഖരം',
844
+ 'සංරක්‍ෂණය',
845
+ 'การเก็บถาวร',
846
+ 'ที่เก็บถาวร',
847
+ 'არქივები',
848
+ 'არქივი',
849
+ 'ប័ណ្ណសារ',
850
+ 'ប័ណ្ណសារ​',
851
+ 'アーカイブ',
852
+ '備存',
853
+ '存档',
854
+ '存檔',
855
+ '封存',
856
+ '归档',
857
+ '보관 편지함',
858
+ '보관함',
859
+ '저장 편지함'
860
+ ]
861
+ }
862
+ };
286
863
 
287
- specialUse(hasSpecialUseExtension, folder) {
288
- // If the server supports SPECIAL-USE (RFC 6154), check for special-use flags first.
289
- // Extension-provided flags take precedence over name-based detection because they
290
- // are authoritative -- the server explicitly marks the folder's role.
291
- if (hasSpecialUseExtension) {
292
- const flag = module.exports.flags.find(flag => folder.flags.has(flag));
293
- if (flag) {
294
- return { flag, source: 'extension' };
295
- }
296
- }
864
+ // Exact lookup index over the tables above, built once at module load. `names` is
865
+ // treated as read-only from here on; nothing in the library mutates it after this point.
866
+ const NAME_INDEX = new Map();
867
+ for (let flag of Object.keys(module.exports.names)) {
868
+ for (let entry of module.exports.names[flag]) {
869
+ NAME_INDEX.set(entry, flag);
870
+ }
871
+ }
297
872
 
298
- // Fallback: match folder name against known localized names.
299
- // Remove U+200E (LEFT-TO-RIGHT MARK) which some mail clients (especially for
300
- // RTL languages like Arabic, Hebrew) insert into folder names for display purposes.
301
- // These invisible marks would otherwise prevent exact string matching.
302
- let name = folder.name
303
- .toLowerCase()
304
- .replace(/\u200e/g, '')
305
- .trim();
873
+ // Fold a folder name into the form the name tables are stored in.
874
+ // Remove U+200E (LEFT-TO-RIGHT MARK) which some mail clients (especially for
875
+ // RTL languages like Arabic, Hebrew) insert into folder names for display purposes.
876
+ // These invisible marks would otherwise prevent exact string matching.
877
+ // Normalize with NFKC last: the same folder name can arrive in different but
878
+ // equivalent forms depending on the client that created it, and matching is exact.
879
+ // NFKC rather than NFC because the compatibility folding is what maps halfwidth
880
+ // katakana and fullwidth Latin onto the regular forms that servers actually send.
881
+ // Every entry in the tables above is stored in NFKC form, which a test asserts.
882
+ function normalizeName(name) {
883
+ return name
884
+ .toLowerCase()
885
+ .replace(/\u200e/g, '')
886
+ .trim()
887
+ .normalize('NFKC');
888
+ }
306
889
 
307
- const flag = Object.keys(module.exports.names).find(flag => module.exports.names[flag].includes(name));
890
+ module.exports.specialUse = (hasSpecialUseExtension, folder) => {
891
+ // If the server supports SPECIAL-USE (RFC 6154), check for special-use flags first.
892
+ // Extension-provided flags take precedence over name-based detection because they
893
+ // are authoritative -- the server explicitly marks the folder's role.
894
+ if (hasSpecialUseExtension) {
895
+ const flag = module.exports.flags.find(flag => folder.flags.has(flag));
308
896
  if (flag) {
309
- return { flag, source: 'name' };
897
+ return { flag, source: 'extension' };
310
898
  }
899
+ }
311
900
 
312
- return { flag: null };
901
+ let name = normalizeName(folder.name);
902
+
903
+ // Fallback 1: the name is exactly a known localized folder name.
904
+ let flag = NAME_INDEX.get(name);
905
+ if (flag) {
906
+ return { flag, source: 'name' };
313
907
  }
908
+
909
+ // Fallback 2: the name is a known folder name decorated with generic mail nouns
910
+ // ("Sent Mail", "Otpravlennye pisma"). Stripping them has to leave exactly one word,
911
+ // and that word still has to be a known name - this tier never matches approximately.
912
+ // Reported as a separate, lower priority source because the decoration makes it a
913
+ // weaker signal than an exactly named folder.
914
+ let core = name.split(TOKEN_SPLIT).filter(token => token && !GENERIC_TOKENS.has(token));
915
+ if (core.length === 1 && core[0] !== name) {
916
+ flag = NAME_INDEX.get(core[0]);
917
+ if (flag) {
918
+ return { flag, source: 'name-guess' };
919
+ }
920
+ }
921
+
922
+ return { flag: null };
314
923
  };