icloud-utils.js 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. (function icloudUtilsModule(root, factory) {
  2. if (typeof module !== 'undefined' && module.exports) {
  3. module.exports = factory();
  4. return;
  5. }
  6. root.IcloudUtils = factory();
  7. })(typeof self !== 'undefined' ? self : globalThis, function createIcloudUtils() {
  8. function normalizeIcloudHost(rawHost) {
  9. const host = String(rawHost || '').trim().toLowerCase();
  10. if (!host) return '';
  11. if (host === 'icloud.com' || host === 'www.icloud.com' || host === 'setup.icloud.com') return 'icloud.com';
  12. if (host === 'icloud.com.cn' || host === 'www.icloud.com.cn' || host === 'setup.icloud.com.cn') return 'icloud.com.cn';
  13. return '';
  14. }
  15. function getConfiguredIcloudHostPreference(stateOrValue = '') {
  16. const preference = typeof stateOrValue === 'object'
  17. ? String(stateOrValue?.icloudHostPreference || '').trim().toLowerCase()
  18. : String(stateOrValue || '').trim().toLowerCase();
  19. if (!preference || preference === 'auto') return '';
  20. return normalizeIcloudHost(preference);
  21. }
  22. function getIcloudLoginUrlForHost(host) {
  23. const normalizedHost = normalizeIcloudHost(host);
  24. if (normalizedHost === 'icloud.com') return 'https://www.icloud.com/';
  25. if (normalizedHost === 'icloud.com.cn') return 'https://www.icloud.com.cn/';
  26. return '';
  27. }
  28. function getIcloudSetupUrlForHost(host) {
  29. const normalizedHost = normalizeIcloudHost(host);
  30. if (normalizedHost === 'icloud.com') return 'https://setup.icloud.com/setup/ws/1';
  31. if (normalizedHost === 'icloud.com.cn') return 'https://setup.icloud.com.cn/setup/ws/1';
  32. return '';
  33. }
  34. function getIcloudHostHintFromMessage(message) {
  35. const lower = String(message || '').toLowerCase();
  36. if (lower.includes('setup.icloud.com.cn') || lower.includes('www.icloud.com.cn') || lower.includes('icloud.com.cn')) {
  37. return 'icloud.com.cn';
  38. }
  39. if (lower.includes('setup.icloud.com') || lower.includes('www.icloud.com') || lower.includes('icloud.com')) {
  40. return 'icloud.com';
  41. }
  42. return '';
  43. }
  44. function normalizeBooleanMap(rawValue = {}) {
  45. if (!rawValue || typeof rawValue !== 'object' || Array.isArray(rawValue)) {
  46. return {};
  47. }
  48. return Object.entries(rawValue).reduce((result, [key, value]) => {
  49. const normalizedKey = String(key || '').trim().toLowerCase();
  50. if (!normalizedKey) {
  51. return result;
  52. }
  53. result[normalizedKey] = Boolean(value);
  54. return result;
  55. }, {});
  56. }
  57. function toNormalizedEmailSet(values = []) {
  58. if (values instanceof Set) {
  59. return new Set(Array.from(values, (item) => String(item || '').trim().toLowerCase()).filter(Boolean));
  60. }
  61. if (Array.isArray(values)) {
  62. return new Set(values.map((item) => String(item || '').trim().toLowerCase()).filter(Boolean));
  63. }
  64. if (values && typeof values === 'object') {
  65. const normalizedMap = normalizeBooleanMap(values);
  66. return new Set(Object.entries(normalizedMap)
  67. .filter(([, value]) => value)
  68. .map(([email]) => email));
  69. }
  70. return new Set();
  71. }
  72. function findIcloudAliasArray(node, depth = 0) {
  73. if (!node || depth > 4) return null;
  74. if (Array.isArray(node)) {
  75. return node.some((item) => item && typeof item === 'object') ? node : null;
  76. }
  77. if (typeof node !== 'object') return null;
  78. const priorityKeys = ['hmeEmails', 'hmeEmailList', 'hmeList', 'hmes', 'aliases', 'items'];
  79. for (const key of priorityKeys) {
  80. if (Array.isArray(node[key])) return node[key];
  81. }
  82. for (const value of Object.values(node)) {
  83. const nested = findIcloudAliasArray(value, depth + 1);
  84. if (nested) return nested;
  85. }
  86. return null;
  87. }
  88. function normalizeIcloudAliasRecord(raw, options = {}) {
  89. const usedEmails = toNormalizedEmailSet(options.usedEmails);
  90. const preservedEmails = toNormalizedEmailSet(options.preservedEmails);
  91. const anonymousId = String(raw?.anonymousId || raw?.id || '').trim();
  92. const email = String(
  93. raw?.hme
  94. || raw?.email
  95. || raw?.alias
  96. || raw?.address
  97. || raw?.metaData?.hme
  98. || ''
  99. ).trim().toLowerCase();
  100. if (!email || !email.includes('@')) return null;
  101. const label = String(raw?.label || raw?.metaData?.label || '').trim();
  102. const note = String(raw?.note || raw?.metaData?.note || '').trim();
  103. const state = String(raw?.state || raw?.status || '').trim().toLowerCase();
  104. const createdAt = raw?.createTimestamp
  105. || raw?.createTime
  106. || raw?.createdAt
  107. || raw?.createdDate
  108. || null;
  109. return {
  110. anonymousId,
  111. email,
  112. label,
  113. note,
  114. state,
  115. active: raw?.active !== false && raw?.isActive !== false && state !== 'inactive' && state !== 'deleted',
  116. used: usedEmails.has(email),
  117. preserved: preservedEmails.has(email),
  118. createdAt,
  119. };
  120. }
  121. function normalizeIcloudAliasList(response, options = {}) {
  122. const aliases = findIcloudAliasArray(response);
  123. if (!aliases) return [];
  124. return aliases
  125. .map((alias) => normalizeIcloudAliasRecord(alias, options))
  126. .filter(Boolean)
  127. .sort((left, right) => {
  128. if (left.active !== right.active) return left.active ? -1 : 1;
  129. if (left.used !== right.used) return left.used ? 1 : -1;
  130. return String(left.email).localeCompare(String(right.email));
  131. });
  132. }
  133. function pickReusableIcloudAlias(aliases = []) {
  134. return (Array.isArray(aliases) ? aliases : []).find((alias) => alias?.active && !alias?.used) || null;
  135. }
  136. function findIcloudAliasByEmail(aliases = [], email = '') {
  137. const normalizedEmail = String(email || '').trim().toLowerCase();
  138. if (!normalizedEmail) return null;
  139. return (Array.isArray(aliases) ? aliases : [])
  140. .find((alias) => String(alias?.email || '').trim().toLowerCase() === normalizedEmail) || null;
  141. }
  142. return {
  143. findIcloudAliasArray,
  144. findIcloudAliasByEmail,
  145. getConfiguredIcloudHostPreference,
  146. getIcloudHostHintFromMessage,
  147. getIcloudLoginUrlForHost,
  148. getIcloudSetupUrlForHost,
  149. normalizeBooleanMap,
  150. normalizeIcloudAliasList,
  151. normalizeIcloudAliasRecord,
  152. normalizeIcloudHost,
  153. pickReusableIcloudAlias,
  154. toNormalizedEmailSet,
  155. };
  156. });