@talex-touch/utils 1.0.42 → 1.0.44

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 (233) hide show
  1. package/.eslintcache +1 -0
  2. package/__tests__/cloud-sync-sdk.test.ts +442 -0
  3. package/__tests__/icons/icons.test.ts +84 -0
  4. package/__tests__/plugin-sdk-lifecycle.test.ts +130 -0
  5. package/__tests__/power-sdk.test.ts +143 -0
  6. package/__tests__/preset-export-types.test.ts +108 -0
  7. package/__tests__/search/fuzzy-match.test.ts +137 -0
  8. package/__tests__/transport/port-policy.test.ts +44 -0
  9. package/__tests__/transport-domain-sdks.test.ts +152 -0
  10. package/__tests__/types/update.test.ts +67 -0
  11. package/account/account-sdk.ts +915 -0
  12. package/account/index.ts +2 -0
  13. package/account/types.ts +321 -0
  14. package/analytics/client.ts +136 -0
  15. package/analytics/index.ts +2 -0
  16. package/analytics/types.ts +156 -0
  17. package/animation/auto-resize.ts +322 -0
  18. package/animation/window-node.ts +26 -19
  19. package/auth/clerk-types.ts +12 -30
  20. package/auth/index.ts +0 -2
  21. package/auth/useAuthState.ts +6 -14
  22. package/base/index.ts +2 -0
  23. package/base/log-level.ts +105 -0
  24. package/channel/index.ts +170 -69
  25. package/cloud-sync/cloud-sync-sdk.ts +450 -0
  26. package/cloud-sync/index.ts +1 -0
  27. package/common/file-scan-utils.ts +17 -9
  28. package/common/index.ts +4 -0
  29. package/common/logger/index.ts +46 -0
  30. package/common/logger/logger-manager.ts +303 -0
  31. package/common/logger/module-logger.ts +270 -0
  32. package/common/logger/transport-logger.ts +234 -0
  33. package/common/logger/types.ts +93 -0
  34. package/common/search/gather.ts +48 -6
  35. package/common/search/index.ts +8 -0
  36. package/common/storage/constants.ts +13 -0
  37. package/common/storage/entity/app-settings.ts +245 -0
  38. package/common/storage/entity/index.ts +3 -0
  39. package/common/storage/entity/layout-atom-types.ts +147 -0
  40. package/common/storage/entity/openers.ts +1 -0
  41. package/common/storage/entity/preset-cloud-api.ts +132 -0
  42. package/common/storage/entity/preset-export-types.ts +256 -0
  43. package/common/storage/entity/shortcut-settings.ts +1 -0
  44. package/common/storage/shortcut-storage.ts +11 -0
  45. package/common/utils/clone-diagnostics.ts +105 -0
  46. package/common/utils/file.ts +16 -8
  47. package/common/utils/index.ts +6 -2
  48. package/common/utils/payload-preview.ts +173 -0
  49. package/common/utils/polling.ts +167 -13
  50. package/common/utils/safe-path.ts +103 -0
  51. package/common/utils/safe-shell.ts +115 -0
  52. package/common/utils/task-queue.ts +4 -1
  53. package/core-box/builder/tuff-builder.ts +0 -1
  54. package/core-box/index.ts +1 -1
  55. package/core-box/recommendation.ts +38 -1
  56. package/core-box/tuff/tuff-dsl.ts +32 -0
  57. package/electron/download-manager.ts +10 -7
  58. package/electron/env-tool.ts +42 -40
  59. package/electron/index.ts +0 -1
  60. package/env/index.ts +156 -0
  61. package/eslint.config.js +55 -0
  62. package/i18n/index.ts +62 -0
  63. package/i18n/locales/en.json +226 -0
  64. package/i18n/locales/zh.json +226 -0
  65. package/i18n/message-keys.ts +236 -0
  66. package/i18n/resolver.ts +181 -0
  67. package/icons/index.ts +257 -0
  68. package/icons/svg.ts +69 -0
  69. package/index.ts +9 -1
  70. package/intelligence/client.ts +72 -42
  71. package/market/constants.ts +9 -5
  72. package/market/index.ts +1 -1
  73. package/market/types.ts +19 -4
  74. package/package.json +15 -5
  75. package/permission/index.ts +143 -46
  76. package/permission/legacy.ts +26 -0
  77. package/permission/registry.ts +304 -0
  78. package/permission/types.ts +164 -0
  79. package/plugin/channel.ts +68 -39
  80. package/plugin/index.ts +80 -7
  81. package/plugin/install.ts +3 -0
  82. package/plugin/log/types.ts +22 -5
  83. package/plugin/node/logger-manager.ts +11 -3
  84. package/plugin/node/logger.ts +24 -17
  85. package/plugin/preload.ts +25 -2
  86. package/plugin/providers/index.ts +4 -4
  87. package/plugin/providers/market-client.ts +6 -3
  88. package/plugin/providers/npm-provider.ts +22 -7
  89. package/plugin/providers/tpex-provider.ts +22 -8
  90. package/plugin/sdk/box-items.ts +14 -0
  91. package/plugin/sdk/box-sdk.ts +64 -0
  92. package/plugin/sdk/channel.ts +119 -4
  93. package/plugin/sdk/clipboard.ts +26 -12
  94. package/plugin/sdk/cloud-sync.ts +113 -0
  95. package/plugin/sdk/common.ts +19 -11
  96. package/plugin/sdk/core-box.ts +6 -15
  97. package/plugin/sdk/division-box.ts +160 -65
  98. package/plugin/sdk/examples/storage-onDidChange-example.js +5 -2
  99. package/plugin/sdk/feature-sdk.ts +111 -76
  100. package/plugin/sdk/flow.ts +146 -45
  101. package/plugin/sdk/hooks/bridge.ts +13 -6
  102. package/plugin/sdk/hooks/life-cycle.ts +35 -16
  103. package/plugin/sdk/index.ts +14 -3
  104. package/plugin/sdk/intelligence.ts +87 -0
  105. package/plugin/sdk/meta/README.md +179 -0
  106. package/plugin/sdk/meta-sdk.ts +244 -0
  107. package/plugin/sdk/notification.ts +9 -0
  108. package/plugin/sdk/plugin-info.ts +64 -0
  109. package/plugin/sdk/power.ts +155 -0
  110. package/plugin/sdk/recommend.ts +21 -0
  111. package/plugin/sdk/service/index.ts +12 -8
  112. package/plugin/sdk/sqlite.ts +141 -0
  113. package/plugin/sdk/storage.ts +2 -6
  114. package/plugin/sdk/system.ts +2 -9
  115. package/plugin/sdk/temp-files.ts +41 -0
  116. package/plugin/sdk/touch-sdk.ts +18 -0
  117. package/plugin/sdk/types.ts +44 -4
  118. package/plugin/sdk/window/index.ts +12 -9
  119. package/plugin/sdk-version.ts +231 -0
  120. package/preload/renderer.ts +3 -2
  121. package/renderer/hooks/arg-mapper.ts +16 -2
  122. package/renderer/hooks/index.ts +13 -0
  123. package/renderer/hooks/initialize.ts +2 -1
  124. package/renderer/hooks/use-agent-market-sdk.ts +7 -0
  125. package/renderer/hooks/use-agent-market.ts +106 -0
  126. package/renderer/hooks/use-agents-sdk.ts +7 -0
  127. package/renderer/hooks/use-app-sdk.ts +7 -0
  128. package/renderer/hooks/use-channel.ts +33 -4
  129. package/renderer/hooks/use-download-sdk.ts +21 -0
  130. package/renderer/hooks/use-intelligence-sdk.ts +7 -0
  131. package/renderer/hooks/use-intelligence-stats.ts +290 -0
  132. package/renderer/hooks/use-intelligence.ts +55 -214
  133. package/renderer/hooks/use-market-sdk.ts +16 -0
  134. package/renderer/hooks/use-notification-sdk.ts +7 -0
  135. package/renderer/hooks/use-permission-sdk.ts +7 -0
  136. package/renderer/hooks/use-permission.ts +325 -0
  137. package/renderer/hooks/use-platform-sdk.ts +7 -0
  138. package/renderer/hooks/use-plugin-sdk.ts +16 -0
  139. package/renderer/hooks/use-settings-sdk.ts +7 -0
  140. package/renderer/hooks/use-update-sdk.ts +21 -0
  141. package/renderer/index.ts +1 -0
  142. package/renderer/ref.ts +19 -10
  143. package/renderer/shared/components/SharedPluginDetailContent.vue +84 -0
  144. package/renderer/shared/components/SharedPluginDetailHeader.vue +116 -0
  145. package/renderer/shared/components/SharedPluginDetailMetaList.vue +39 -0
  146. package/renderer/shared/components/SharedPluginDetailReadme.vue +45 -0
  147. package/renderer/shared/components/SharedPluginDetailVersions.vue +98 -0
  148. package/renderer/shared/components/index.ts +5 -0
  149. package/renderer/shared/components/shims-vue.d.ts +5 -0
  150. package/renderer/shared/index.ts +2 -0
  151. package/renderer/shared/plugin-detail.ts +62 -0
  152. package/renderer/storage/app-settings.ts +3 -1
  153. package/renderer/storage/base-storage.ts +508 -82
  154. package/renderer/storage/intelligence-storage.ts +31 -40
  155. package/renderer/storage/openers.ts +3 -1
  156. package/renderer/storage/storage-subscription.ts +126 -42
  157. package/renderer/touch-sdk/env.ts +10 -10
  158. package/renderer/touch-sdk/index.ts +114 -18
  159. package/renderer/touch-sdk/terminal.ts +24 -13
  160. package/search/feature-matcher.ts +279 -0
  161. package/search/fuzzy-match.ts +64 -34
  162. package/search/index.ts +10 -0
  163. package/search/levenshtein-utils.ts +17 -11
  164. package/transport/errors.ts +310 -0
  165. package/transport/event/builder.ts +378 -0
  166. package/transport/event/index.ts +7 -0
  167. package/transport/event/types.ts +292 -0
  168. package/transport/events/index.ts +2670 -0
  169. package/transport/events/meta-overlay.ts +79 -0
  170. package/transport/events/types/agents.ts +177 -0
  171. package/transport/events/types/app-index.ts +9 -0
  172. package/transport/events/types/app.ts +475 -0
  173. package/transport/events/types/box-item.ts +222 -0
  174. package/transport/events/types/clipboard.ts +80 -0
  175. package/transport/events/types/core-box.ts +534 -0
  176. package/transport/events/types/device-idle.ts +7 -0
  177. package/transport/events/types/division-box.ts +99 -0
  178. package/transport/events/types/download.ts +115 -0
  179. package/transport/events/types/file-index.ts +73 -0
  180. package/transport/events/types/flow.ts +149 -0
  181. package/transport/events/types/index.ts +70 -0
  182. package/transport/events/types/market.ts +39 -0
  183. package/transport/events/types/meta-overlay.ts +184 -0
  184. package/transport/events/types/notification.ts +140 -0
  185. package/transport/events/types/permission.ts +90 -0
  186. package/transport/events/types/platform.ts +8 -0
  187. package/transport/events/types/plugin.ts +620 -0
  188. package/transport/events/types/sentry.ts +20 -0
  189. package/transport/events/types/storage.ts +208 -0
  190. package/transport/events/types/transport.ts +60 -0
  191. package/transport/events/types/tray.ts +16 -0
  192. package/transport/events/types/update.ts +78 -0
  193. package/transport/index.ts +139 -0
  194. package/transport/main.ts +2 -0
  195. package/transport/sdk/constants.ts +29 -0
  196. package/transport/sdk/domains/agents-market.ts +47 -0
  197. package/transport/sdk/domains/agents.ts +62 -0
  198. package/transport/sdk/domains/app.ts +48 -0
  199. package/transport/sdk/domains/disposable.ts +35 -0
  200. package/transport/sdk/domains/download.ts +139 -0
  201. package/transport/sdk/domains/index.ts +13 -0
  202. package/transport/sdk/domains/intelligence.ts +616 -0
  203. package/transport/sdk/domains/market.ts +35 -0
  204. package/transport/sdk/domains/notification.ts +62 -0
  205. package/transport/sdk/domains/permission.ts +85 -0
  206. package/transport/sdk/domains/platform.ts +19 -0
  207. package/transport/sdk/domains/plugin.ts +144 -0
  208. package/transport/sdk/domains/settings.ts +92 -0
  209. package/transport/sdk/domains/update.ts +64 -0
  210. package/transport/sdk/index.ts +60 -0
  211. package/transport/sdk/main-transport.ts +710 -0
  212. package/transport/sdk/main.ts +9 -0
  213. package/transport/sdk/plugin-transport.ts +654 -0
  214. package/transport/sdk/port-policy.ts +38 -0
  215. package/transport/sdk/renderer-transport.ts +1165 -0
  216. package/transport/types.ts +605 -0
  217. package/types/agent.ts +399 -0
  218. package/types/cloud-sync.ts +157 -0
  219. package/types/division-box.ts +31 -31
  220. package/types/download.ts +1 -0
  221. package/types/flow.ts +63 -12
  222. package/types/icon.ts +2 -1
  223. package/types/index.ts +5 -0
  224. package/types/intelligence.ts +166 -173
  225. package/types/modules/base.ts +2 -0
  226. package/types/path-browserify.d.ts +5 -0
  227. package/types/platform.ts +12 -0
  228. package/types/startup-info.ts +32 -0
  229. package/types/touch-app-core.ts +8 -8
  230. package/types/update.ts +94 -1
  231. package/vitest.config.ts +25 -0
  232. package/auth/useClerkConfig.ts +0 -40
  233. package/auth/useClerkProvider.ts +0 -52
package/.eslintcache ADDED
@@ -0,0 +1 @@
1
+ [{"/Users/talexdreamsoul/Workspace/Projects/talex-touch/packages/utils/__tests__/transport-domain-sdks.test.ts":"1","/Users/talexdreamsoul/Workspace/Projects/talex-touch/packages/utils/cloud-sync/cloud-sync-sdk.ts":"2","/Users/talexdreamsoul/Workspace/Projects/talex-touch/packages/utils/common/utils/index.ts":"3","/Users/talexdreamsoul/Workspace/Projects/talex-touch/packages/utils/common/utils/safe-path.ts":"4","/Users/talexdreamsoul/Workspace/Projects/talex-touch/packages/utils/common/utils/safe-shell.ts":"5","/Users/talexdreamsoul/Workspace/Projects/talex-touch/packages/utils/electron/env-tool.ts":"6","/Users/talexdreamsoul/Workspace/Projects/talex-touch/packages/utils/i18n/message-keys.ts":"7","/Users/talexdreamsoul/Workspace/Projects/talex-touch/packages/utils/permission/index.ts":"8","/Users/talexdreamsoul/Workspace/Projects/talex-touch/packages/utils/permission/registry.ts":"9","/Users/talexdreamsoul/Workspace/Projects/talex-touch/packages/utils/permission/types.ts":"10","/Users/talexdreamsoul/Workspace/Projects/talex-touch/packages/utils/plugin/sdk/cloud-sync.ts":"11","/Users/talexdreamsoul/Workspace/Projects/talex-touch/packages/utils/renderer/storage/base-storage.ts":"12","/Users/talexdreamsoul/Workspace/Projects/talex-touch/packages/utils/renderer/storage/intelligence-storage.ts":"13","/Users/talexdreamsoul/Workspace/Projects/talex-touch/packages/utils/transport/sdk/domains/intelligence.ts":"14","/Users/talexdreamsoul/Workspace/Projects/talex-touch/packages/utils/types/cloud-sync.ts":"15","/Users/talexdreamsoul/Workspace/Projects/talex-touch/packages/utils/types/intelligence.ts":"16","/Users/talexdreamsoul/Workspace/Projects/talex-touch/packages/utils/types/update.ts":"17","/Users/talexdreamsoul/Workspace/Projects/talex-touch/packages/utils/transport/events/types/flow.ts":"18","/Users/talexdreamsoul/Workspace/Projects/talex-touch/packages/utils/core-box/recommendation.ts":"19","/Users/talexdreamsoul/Workspace/Projects/talex-touch/packages/utils/plugin/sdk/index.ts":"20","/Users/talexdreamsoul/Workspace/Projects/talex-touch/packages/utils/plugin/sdk/recommend.ts":"21","/Users/talexdreamsoul/Workspace/Projects/talex-touch/packages/utils/plugin/sdk/types.ts":"22","/Users/talexdreamsoul/Workspace/Projects/talex-touch/packages/utils/common/utils/polling.ts":"23","/Users/talexdreamsoul/Workspace/Projects/talex-touch/packages/utils/transport/events/index.ts":"24","/Users/talexdreamsoul/Workspace/Projects/talex-touch/packages/utils/transport/events/types/file-index.ts":"25","/Users/talexdreamsoul/Workspace/Projects/talex-touch/packages/utils/transport/sdk/domains/settings.ts":"26","/Users/talexdreamsoul/Workspace/Projects/talex-touch/packages/utils/intelligence/client.ts":"27","/Users/talexdreamsoul/Workspace/Projects/talex-touch/packages/utils/plugin/index.ts":"28","/Users/talexdreamsoul/Workspace/Projects/talex-touch/packages/utils/types/index.ts":"29","/Users/talexdreamsoul/Workspace/Projects/talex-touch/packages/utils/types/path-browserify.d.ts":"30","/Users/talexdreamsoul/Workspace/Projects/talex-touch/packages/utils/transport/events/types/core-box.ts":"31"},{"size":5306,"mtime":1771563102452,"results":"32","hashOfConfig":"33"},{"size":15379,"mtime":1771563102455,"results":"34","hashOfConfig":"33"},{"size":5659,"mtime":1771563102455,"results":"35","hashOfConfig":"33"},{"size":2691,"mtime":1771563102456,"results":"36","hashOfConfig":"33"},{"size":2879,"mtime":1771563102456,"results":"37","hashOfConfig":"33"},{"size":2472,"mtime":1771563102458,"results":"38","hashOfConfig":"33"},{"size":7760,"mtime":1771563102481,"results":"39","hashOfConfig":"33"},{"size":4206,"mtime":1771563102484,"results":"40","hashOfConfig":"33"},{"size":7775,"mtime":1771563102484,"results":"41","hashOfConfig":"33"},{"size":3423,"mtime":1771563102493,"results":"42","hashOfConfig":"33"},{"size":3750,"mtime":1771563102509,"results":"43","hashOfConfig":"33"},{"size":26935,"mtime":1771563102511,"results":"44","hashOfConfig":"33"},{"size":6457,"mtime":1771563102514,"results":"45","hashOfConfig":"33"},{"size":21154,"mtime":1771563102519,"results":"46","hashOfConfig":"33"},{"size":3091,"mtime":1771563102519,"results":"47","hashOfConfig":"33"},{"size":55314,"mtime":1771563102519,"results":"48","hashOfConfig":"33"},{"size":6327,"mtime":1771566199653,"results":"49","hashOfConfig":"50"},{"size":2888,"mtime":1771563689317,"results":"51","hashOfConfig":"33"},{"size":3003,"mtime":1771611450129,"results":"52","hashOfConfig":"50"},{"size":986,"mtime":1771611509397,"results":"53","hashOfConfig":"50"},{"size":683,"mtime":1771611477506,"results":"54","hashOfConfig":"50"},{"size":26106,"mtime":1771611536284,"results":"55","hashOfConfig":"50"},{"size":11083,"mtime":1771632074503,"results":"56","hashOfConfig":"50"},{"size":74191,"mtime":1771632883663,"results":"57","hashOfConfig":"50"},{"size":1560,"mtime":1771632863820,"results":"58","hashOfConfig":"50"},{"size":4093,"mtime":1771632913471,"results":"59","hashOfConfig":"50"},{"size":3327,"mtime":1771678966584,"results":"60","hashOfConfig":"50"},{"size":18271,"mtime":1771679568421,"results":"61","hashOfConfig":"50"},{"size":329,"mtime":1771678957747,"results":"62","hashOfConfig":"50"},{"size":135,"mtime":1771678957747,"results":"63","hashOfConfig":"64"},{"size":9317,"mtime":1771684543626,"results":"65","hashOfConfig":"50"},{"filePath":"66","messages":"67","suppressedMessages":"68","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},"tyugd7",{"filePath":"69","messages":"70","suppressedMessages":"71","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"72","messages":"73","suppressedMessages":"74","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"75","messages":"76","suppressedMessages":"77","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"78","messages":"79","suppressedMessages":"80","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"81","messages":"82","suppressedMessages":"83","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"84","messages":"85","suppressedMessages":"86","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"87","messages":"88","suppressedMessages":"89","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"90","messages":"91","suppressedMessages":"92","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"93","messages":"94","suppressedMessages":"95","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"96","messages":"97","suppressedMessages":"98","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"99","messages":"100","suppressedMessages":"101","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"102","messages":"103","suppressedMessages":"104","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"105","messages":"106","suppressedMessages":"107","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"108","messages":"109","suppressedMessages":"110","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"111","messages":"112","suppressedMessages":"113","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"114","messages":"115","suppressedMessages":"116","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},"m7p77f",{"filePath":"117","messages":"118","suppressedMessages":"119","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"120","messages":"121","suppressedMessages":"122","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"123","messages":"124","suppressedMessages":"125","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"126","messages":"127","suppressedMessages":"128","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"129","messages":"130","suppressedMessages":"131","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"132","messages":"133","suppressedMessages":"134","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"135","messages":"136","suppressedMessages":"137","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"138","messages":"139","suppressedMessages":"140","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"141","messages":"142","suppressedMessages":"143","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"144","messages":"145","suppressedMessages":"146","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"147","messages":"148","suppressedMessages":"149","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"150","messages":"151","suppressedMessages":"152","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"153","messages":"154","suppressedMessages":"155","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},"552fw",{"filePath":"156","messages":"157","suppressedMessages":"158","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},"/Users/talexdreamsoul/Workspace/Projects/talex-touch/packages/utils/__tests__/transport-domain-sdks.test.ts",[],[],"/Users/talexdreamsoul/Workspace/Projects/talex-touch/packages/utils/cloud-sync/cloud-sync-sdk.ts",[],[],"/Users/talexdreamsoul/Workspace/Projects/talex-touch/packages/utils/common/utils/index.ts",[],[],"/Users/talexdreamsoul/Workspace/Projects/talex-touch/packages/utils/common/utils/safe-path.ts",[],[],"/Users/talexdreamsoul/Workspace/Projects/talex-touch/packages/utils/common/utils/safe-shell.ts",[],[],"/Users/talexdreamsoul/Workspace/Projects/talex-touch/packages/utils/electron/env-tool.ts",[],[],"/Users/talexdreamsoul/Workspace/Projects/talex-touch/packages/utils/i18n/message-keys.ts",[],[],"/Users/talexdreamsoul/Workspace/Projects/talex-touch/packages/utils/permission/index.ts",[],[],"/Users/talexdreamsoul/Workspace/Projects/talex-touch/packages/utils/permission/registry.ts",[],[],"/Users/talexdreamsoul/Workspace/Projects/talex-touch/packages/utils/permission/types.ts",[],[],"/Users/talexdreamsoul/Workspace/Projects/talex-touch/packages/utils/plugin/sdk/cloud-sync.ts",[],[],"/Users/talexdreamsoul/Workspace/Projects/talex-touch/packages/utils/renderer/storage/base-storage.ts",[],[],"/Users/talexdreamsoul/Workspace/Projects/talex-touch/packages/utils/renderer/storage/intelligence-storage.ts",[],[],"/Users/talexdreamsoul/Workspace/Projects/talex-touch/packages/utils/transport/sdk/domains/intelligence.ts",[],[],"/Users/talexdreamsoul/Workspace/Projects/talex-touch/packages/utils/types/cloud-sync.ts",[],[],"/Users/talexdreamsoul/Workspace/Projects/talex-touch/packages/utils/types/intelligence.ts",[],[],"/Users/talexdreamsoul/Workspace/Projects/talex-touch/packages/utils/types/update.ts",[],[],"/Users/talexdreamsoul/Workspace/Projects/talex-touch/packages/utils/transport/events/types/flow.ts",[],[],"/Users/talexdreamsoul/Workspace/Projects/talex-touch/packages/utils/core-box/recommendation.ts",[],[],"/Users/talexdreamsoul/Workspace/Projects/talex-touch/packages/utils/plugin/sdk/index.ts",[],[],"/Users/talexdreamsoul/Workspace/Projects/talex-touch/packages/utils/plugin/sdk/recommend.ts",[],[],"/Users/talexdreamsoul/Workspace/Projects/talex-touch/packages/utils/plugin/sdk/types.ts",[],[],"/Users/talexdreamsoul/Workspace/Projects/talex-touch/packages/utils/common/utils/polling.ts",[],[],"/Users/talexdreamsoul/Workspace/Projects/talex-touch/packages/utils/transport/events/index.ts",[],[],"/Users/talexdreamsoul/Workspace/Projects/talex-touch/packages/utils/transport/events/types/file-index.ts",[],[],"/Users/talexdreamsoul/Workspace/Projects/talex-touch/packages/utils/transport/sdk/domains/settings.ts",[],[],"/Users/talexdreamsoul/Workspace/Projects/talex-touch/packages/utils/intelligence/client.ts",[],[],"/Users/talexdreamsoul/Workspace/Projects/talex-touch/packages/utils/plugin/index.ts",[],[],"/Users/talexdreamsoul/Workspace/Projects/talex-touch/packages/utils/types/index.ts",[],[],"/Users/talexdreamsoul/Workspace/Projects/talex-touch/packages/utils/types/path-browserify.d.ts",[],[],"/Users/talexdreamsoul/Workspace/Projects/talex-touch/packages/utils/transport/events/types/core-box.ts",[],[]]
@@ -0,0 +1,442 @@
1
+ import { describe, expect, it, vi } from 'vitest'
2
+ import { accountSDK } from '../account'
3
+ import { CloudSyncError, CloudSyncSDK } from '../cloud-sync/cloud-sync-sdk'
4
+ import { CloudSyncSDK as PluginCloudSyncSDK } from '../plugin/sdk/cloud-sync'
5
+ import type { SyncItemInput } from '../types/cloud-sync'
6
+
7
+ const createJsonResponse = (payload: unknown, status = 200) => new Response(
8
+ JSON.stringify(payload),
9
+ {
10
+ status,
11
+ headers: { 'content-type': 'application/json' },
12
+ },
13
+ )
14
+
15
+ const createBinaryResponse = (payload: Uint8Array, status = 200, headers?: Record<string, string>) => new Response(
16
+ new Uint8Array(payload).buffer,
17
+ {
18
+ status,
19
+ headers: { 'content-type': 'application/octet-stream', ...(headers ?? {}) },
20
+ },
21
+ )
22
+
23
+ describe('CloudSyncSDK', () => {
24
+ it('handshakes and attaches sync token for push', async () => {
25
+ const fetchMock = vi.fn(async (input: RequestInfo | URL, init?: RequestInit) => {
26
+ const url = String(input)
27
+ if (url.endsWith('/api/v1/sync/handshake')) {
28
+ return createJsonResponse({
29
+ sync_token: 'sync-1',
30
+ sync_token_expires_at: '2099-01-01T00:00:00.000Z',
31
+ server_cursor: 0,
32
+ device_id: 'device-1',
33
+ quotas: {
34
+ limits: { storage_limit_bytes: 100, object_limit: 10, item_limit: 5, device_limit: 3 },
35
+ usage: { used_storage_bytes: 0, used_objects: 0, used_devices: 1 },
36
+ },
37
+ })
38
+ }
39
+ if (url.endsWith('/api/v1/sync/push')) {
40
+ const headers = new Headers(init?.headers as HeadersInit)
41
+ expect(headers.get('x-device-id')).toBe('device-1')
42
+ expect(headers.get('x-sync-token')).toBe('sync-1')
43
+ return createJsonResponse({ ack_cursor: 1, conflicts: [] })
44
+ }
45
+ return createJsonResponse({}, 404)
46
+ })
47
+
48
+ const cache: { token?: string, expiresAt?: string } = {}
49
+ const sdk = new CloudSyncSDK({
50
+ baseUrl: 'https://example.com',
51
+ getAuthToken: () => 'auth-1',
52
+ getDeviceId: () => 'device-1',
53
+ fetch: fetchMock as any,
54
+ syncTokenCache: cache,
55
+ now: () => 0,
56
+ })
57
+
58
+ const item: SyncItemInput = {
59
+ item_id: 'note-1',
60
+ type: 'note',
61
+ schema_version: 1,
62
+ payload_enc: 'enc',
63
+ payload_ref: null,
64
+ meta_plain: { title: 'hello' },
65
+ payload_size: 10,
66
+ updated_at: '2026-02-04T00:00:00.000Z',
67
+ deleted_at: null,
68
+ op_seq: 1,
69
+ op_hash: 'hash-1',
70
+ op_type: 'upsert',
71
+ }
72
+
73
+ const result = await sdk.push([item])
74
+ expect(result.ack_cursor).toBe(1)
75
+ expect(cache.token).toBe('sync-1')
76
+ expect(fetchMock).toHaveBeenCalledTimes(2)
77
+ })
78
+
79
+ it('throws CloudSyncError when response has errorCode', async () => {
80
+ const fetchMock = vi.fn(async (input: RequestInfo | URL, _init?: RequestInit) => {
81
+ const url = String(input)
82
+ if (url.endsWith('/api/v1/sync/handshake')) {
83
+ return createJsonResponse({
84
+ sync_token: 'sync-2',
85
+ sync_token_expires_at: '2099-01-01T00:00:00.000Z',
86
+ server_cursor: 0,
87
+ device_id: 'device-2',
88
+ quotas: {
89
+ limits: { storage_limit_bytes: 100, object_limit: 10, item_limit: 5, device_limit: 3 },
90
+ usage: { used_storage_bytes: 0, used_objects: 0, used_devices: 1 },
91
+ },
92
+ })
93
+ }
94
+ if (url.endsWith('/api/v1/sync/push')) {
95
+ return createJsonResponse({ errorCode: 'SYNC_INVALID_PAYLOAD' }, 400)
96
+ }
97
+ return createJsonResponse({}, 404)
98
+ })
99
+
100
+ const sdk = new CloudSyncSDK({
101
+ serviceBaseUrl: 'https://example.com',
102
+ getAuthToken: () => 'auth-2',
103
+ getDeviceId: () => 'device-2',
104
+ fetch: fetchMock as any,
105
+ })
106
+
107
+ const item: SyncItemInput = {
108
+ item_id: 'note-2',
109
+ type: 'note',
110
+ schema_version: 1,
111
+ payload_enc: 'enc',
112
+ payload_ref: null,
113
+ meta_plain: null,
114
+ payload_size: 10,
115
+ updated_at: '2026-02-04T00:00:00.000Z',
116
+ deleted_at: null,
117
+ op_seq: 1,
118
+ op_hash: 'hash-2',
119
+ op_type: 'upsert',
120
+ }
121
+
122
+ await expect(sdk.push([item])).rejects.toBeInstanceOf(CloudSyncError)
123
+ })
124
+
125
+ it('plugin sdk resolves auth/device via accountSDK', async () => {
126
+ const fetchMock = vi.fn(async (input: RequestInfo | URL, init?: RequestInit) => {
127
+ const url = String(input)
128
+ if (url.endsWith('/api/v1/sync/handshake')) {
129
+ const headers = new Headers(init?.headers as HeadersInit)
130
+ expect(headers.get('authorization')).toBe('Bearer auth-3')
131
+ expect(headers.get('x-device-id')).toBe('device-3')
132
+ return createJsonResponse({
133
+ sync_token: 'sync-3',
134
+ sync_token_expires_at: '2099-01-01T00:00:00.000Z',
135
+ server_cursor: 0,
136
+ device_id: 'device-3',
137
+ quotas: {
138
+ limits: { storage_limit_bytes: 100, object_limit: 10, item_limit: 5, device_limit: 3 },
139
+ usage: { used_storage_bytes: 0, used_objects: 0, used_devices: 1 },
140
+ },
141
+ })
142
+ }
143
+ if (url.endsWith('/api/v1/sync/push')) {
144
+ const headers = new Headers(init?.headers as HeadersInit)
145
+ expect(headers.get('authorization')).toBe('Bearer auth-3')
146
+ expect(headers.get('x-device-id')).toBe('device-3')
147
+ expect(headers.get('x-sync-token')).toBe('sync-3')
148
+ return createJsonResponse({ ack_cursor: 1, conflicts: [] })
149
+ }
150
+ return createJsonResponse({}, 404)
151
+ })
152
+
153
+ const authSpy = vi.spyOn(accountSDK, 'getAuthToken').mockResolvedValue('auth-3')
154
+ const deviceSpy = vi.spyOn(accountSDK, 'getDeviceId').mockResolvedValue('device-3')
155
+ const recordSpy = vi.spyOn(accountSDK, 'recordSyncActivity').mockResolvedValue()
156
+
157
+ const sdk = new PluginCloudSyncSDK({
158
+ baseUrl: 'https://example.com',
159
+ fetch: fetchMock as any,
160
+ channelSend: vi.fn(async () => null),
161
+ now: () => 0,
162
+ })
163
+
164
+ const item: SyncItemInput = {
165
+ item_id: 'note-3',
166
+ type: 'note',
167
+ schema_version: 1,
168
+ payload_enc: 'enc',
169
+ payload_ref: null,
170
+ meta_plain: { title: 'plugin' },
171
+ payload_size: 10,
172
+ updated_at: '2026-02-04T00:00:00.000Z',
173
+ deleted_at: null,
174
+ op_seq: 1,
175
+ op_hash: 'hash-3',
176
+ op_type: 'upsert',
177
+ }
178
+
179
+ await sdk.push([item])
180
+ expect(authSpy).toHaveBeenCalled()
181
+ expect(deviceSpy).toHaveBeenCalled()
182
+ expect(recordSpy).toHaveBeenCalledWith('push')
183
+
184
+ authSpy.mockRestore()
185
+ deviceSpy.mockRestore()
186
+ recordSpy.mockRestore()
187
+ })
188
+
189
+ it('plugin sdk records pull activity', async () => {
190
+ const fetchMock = vi.fn(async (input: RequestInfo | URL, init?: RequestInit) => {
191
+ const url = String(input)
192
+ if (url.endsWith('/api/v1/sync/handshake')) {
193
+ return createJsonResponse({
194
+ sync_token: 'sync-3-pull',
195
+ sync_token_expires_at: '2099-01-01T00:00:00.000Z',
196
+ server_cursor: 0,
197
+ device_id: 'device-3',
198
+ quotas: {
199
+ limits: { storage_limit_bytes: 100, object_limit: 10, item_limit: 5, device_limit: 3 },
200
+ usage: { used_storage_bytes: 0, used_objects: 0, used_devices: 1 },
201
+ },
202
+ })
203
+ }
204
+ if (url.includes('/api/v1/sync/pull')) {
205
+ const headers = new Headers(init?.headers as HeadersInit)
206
+ expect(headers.get('x-sync-token')).toBe('sync-3-pull')
207
+ return createJsonResponse({ items: [], oplog: [], next_cursor: 0 })
208
+ }
209
+ return createJsonResponse({}, 404)
210
+ })
211
+
212
+ const authSpy = vi.spyOn(accountSDK, 'getAuthToken').mockResolvedValue('auth-3')
213
+ const deviceSpy = vi.spyOn(accountSDK, 'getDeviceId').mockResolvedValue('device-3')
214
+ const recordSpy = vi.spyOn(accountSDK, 'recordSyncActivity').mockResolvedValue()
215
+
216
+ const sdk = new PluginCloudSyncSDK({
217
+ baseUrl: 'https://example.com',
218
+ fetch: fetchMock as any,
219
+ channelSend: vi.fn(async () => null),
220
+ })
221
+
222
+ await sdk.pull({ cursor: 0, limit: 20 })
223
+ expect(authSpy).toHaveBeenCalled()
224
+ expect(deviceSpy).toHaveBeenCalled()
225
+ expect(recordSpy).toHaveBeenCalledWith('pull')
226
+
227
+ authSpy.mockRestore()
228
+ deviceSpy.mockRestore()
229
+ recordSpy.mockRestore()
230
+ })
231
+
232
+ it('plugin sdk blocks sync requests when user disabled sync preference', async () => {
233
+ const fetchMock = vi.fn(async () => createJsonResponse({}, 200))
234
+ const authSpy = vi.spyOn(accountSDK, 'getAuthToken').mockResolvedValue('auth-disabled')
235
+ const deviceSpy = vi.spyOn(accountSDK, 'getDeviceId').mockResolvedValue('device-disabled')
236
+ const syncEnabledSpy = vi.spyOn(accountSDK, 'getSyncEnabled').mockResolvedValue(false)
237
+
238
+ const sdk = new PluginCloudSyncSDK({
239
+ baseUrl: 'https://example.com',
240
+ fetch: fetchMock as any,
241
+ channelSend: vi.fn(async () => null),
242
+ now: () => 0,
243
+ })
244
+
245
+ const item: SyncItemInput = {
246
+ item_id: 'note-disabled',
247
+ type: 'note',
248
+ schema_version: 1,
249
+ payload_enc: 'enc',
250
+ payload_ref: null,
251
+ meta_plain: { title: 'disabled' },
252
+ payload_size: 10,
253
+ updated_at: '2026-02-12T00:00:00.000Z',
254
+ deleted_at: null,
255
+ op_seq: 1,
256
+ op_hash: 'hash-disabled',
257
+ op_type: 'upsert',
258
+ }
259
+
260
+ await expect(sdk.push([item])).rejects.toBeInstanceOf(CloudSyncError)
261
+ expect(fetchMock).not.toHaveBeenCalled()
262
+ expect(authSpy).not.toHaveBeenCalled()
263
+ expect(deviceSpy).not.toHaveBeenCalled()
264
+
265
+ syncEnabledSpy.mockRestore()
266
+ authSpy.mockRestore()
267
+ deviceSpy.mockRestore()
268
+ })
269
+
270
+ it('downloads blob as binary and keeps sync token header', async () => {
271
+ const fetchMock = vi.fn(async (input: RequestInfo | URL, init?: RequestInit) => {
272
+ const url = String(input)
273
+ if (url.endsWith('/api/v1/sync/handshake')) {
274
+ return createJsonResponse({
275
+ sync_token: 'sync-4',
276
+ sync_token_expires_at: '2099-01-01T00:00:00.000Z',
277
+ server_cursor: 0,
278
+ device_id: 'device-4',
279
+ quotas: {
280
+ limits: { storage_limit_bytes: 100, object_limit: 10, item_limit: 5, device_limit: 3 },
281
+ usage: { used_storage_bytes: 0, used_objects: 0, used_devices: 1 },
282
+ },
283
+ })
284
+ }
285
+ if (url.endsWith('/api/v1/sync/blobs/blob-1/download')) {
286
+ const headers = new Headers(init?.headers as HeadersInit)
287
+ expect(headers.get('x-sync-token')).toBe('sync-4')
288
+ return createBinaryResponse(
289
+ new TextEncoder().encode('hello'),
290
+ 200,
291
+ { 'x-content-sha256': 'sha-1', 'content-length': '5' },
292
+ )
293
+ }
294
+ return createJsonResponse({}, 404)
295
+ })
296
+
297
+ const sdk = new CloudSyncSDK({
298
+ baseUrl: 'https://example.com',
299
+ getAuthToken: () => 'auth-4',
300
+ getDeviceId: () => 'device-4',
301
+ fetch: fetchMock as any,
302
+ now: () => 0,
303
+ })
304
+
305
+ const blob = await sdk.downloadBlob('blob-1')
306
+ expect(blob.sha256).toBe('sha-1')
307
+ expect(blob.sizeBytes).toBe(5)
308
+ expect(new TextDecoder().decode(blob.data)).toBe('hello')
309
+ })
310
+
311
+ it('calls keys and device attest apis without sync token', async () => {
312
+ const fetchMock = vi.fn(async (input: RequestInfo | URL, init?: RequestInit) => {
313
+ const url = String(input)
314
+ if (url.endsWith('/api/v1/keys/list')) {
315
+ const headers = new Headers(init?.headers as HeadersInit)
316
+ expect(headers.get('x-sync-token')).toBeNull()
317
+ return createJsonResponse({
318
+ keyrings: [
319
+ {
320
+ keyring_id: 'k1',
321
+ device_id: 'd1',
322
+ key_type: 'uk',
323
+ rotated_at: null,
324
+ created_at: '2026-02-04T00:00:00.000Z',
325
+ has_recovery_code: true,
326
+ },
327
+ ],
328
+ })
329
+ }
330
+ if (url.endsWith('/api/v1/keys/issue-device')) {
331
+ return createJsonResponse({ keyring_id: 'k2' })
332
+ }
333
+ if (url.endsWith('/api/v1/keys/recover-device')) {
334
+ return createJsonResponse({
335
+ keyrings: [
336
+ {
337
+ keyring_id: 'k3',
338
+ device_id: 'd2',
339
+ key_type: 'uk',
340
+ encrypted_key: 'enc',
341
+ rotated_at: null,
342
+ created_at: '2026-02-04T00:00:00.000Z',
343
+ },
344
+ ],
345
+ })
346
+ }
347
+ if (url.endsWith('/api/v1/devices/attest')) {
348
+ return createJsonResponse({ ok: true, device_id: 'd1', updated_at: '2026-02-04T00:00:00.000Z' })
349
+ }
350
+ return createJsonResponse({}, 404)
351
+ })
352
+
353
+ const sdk = new CloudSyncSDK({
354
+ baseUrl: 'https://example.com',
355
+ getAuthToken: () => 'auth-5',
356
+ getDeviceId: () => 'device-5',
357
+ fetch: fetchMock as any,
358
+ })
359
+
360
+ const keyrings = await sdk.listKeyrings()
361
+ expect(keyrings).toHaveLength(1)
362
+
363
+ const issued = await sdk.issueDeviceKey({
364
+ target_device_id: 'd1',
365
+ key_type: 'uk',
366
+ encrypted_key: 'enc',
367
+ recovery_code_hash: 'rc',
368
+ })
369
+ expect(issued.keyring_id).toBe('k2')
370
+
371
+ const recovered = await sdk.recoverDevice({ recovery_code: 'rc' })
372
+ expect(recovered).toHaveLength(1)
373
+
374
+ const attested = await sdk.attestDevice({ machine_code_hash: 'mc' })
375
+ expect(attested.ok).toBe(true)
376
+ })
377
+
378
+ it('retries sensitive keys api with step-up callback token', async () => {
379
+ let issueCalls = 0
380
+ let recoverCalls = 0
381
+ const fetchMock = vi.fn(async (input: RequestInfo | URL, init?: RequestInit) => {
382
+ const url = String(input)
383
+ if (url.endsWith('/api/v1/keys/issue-device')) {
384
+ issueCalls += 1
385
+ const headers = new Headers(init?.headers as HeadersInit)
386
+ const loginToken = headers.get('x-login-token')
387
+ if (!loginToken) {
388
+ return createJsonResponse({ errorCode: 'DEVICE_NOT_AUTHORIZED', message: 'MF2A required' }, 403)
389
+ }
390
+ expect(loginToken).toBe('step-up-token')
391
+ return createJsonResponse({ keyring_id: 'k4' })
392
+ }
393
+
394
+ if (url.endsWith('/api/v1/keys/recover-device')) {
395
+ recoverCalls += 1
396
+ const headers = new Headers(init?.headers as HeadersInit)
397
+ const loginToken = headers.get('x-login-token')
398
+ if (!loginToken) {
399
+ return createJsonResponse({ errorCode: 'DEVICE_NOT_AUTHORIZED', message: 'MF2A required' }, 403)
400
+ }
401
+ expect(loginToken).toBe('step-up-token')
402
+ return createJsonResponse({
403
+ keyrings: [
404
+ {
405
+ keyring_id: 'k5',
406
+ device_id: 'd5',
407
+ key_type: 'uk',
408
+ encrypted_key: 'enc',
409
+ rotated_at: null,
410
+ created_at: '2026-02-04T00:00:00.000Z',
411
+ },
412
+ ],
413
+ })
414
+ }
415
+
416
+ return createJsonResponse({}, 404)
417
+ })
418
+
419
+ const onStepUpRequired = vi.fn(async () => 'step-up-token')
420
+ const sdk = new CloudSyncSDK({
421
+ baseUrl: 'https://example.com',
422
+ getAuthToken: () => 'auth-6',
423
+ getDeviceId: () => 'device-6',
424
+ fetch: fetchMock as any,
425
+ onStepUpRequired,
426
+ })
427
+
428
+ const issued = await sdk.issueDeviceKey({
429
+ target_device_id: 'd5',
430
+ key_type: 'uk',
431
+ encrypted_key: 'enc',
432
+ })
433
+ expect(issued.keyring_id).toBe('k4')
434
+
435
+ const recovered = await sdk.recoverDevice({ recovery_code: 'rc-1' })
436
+ expect(recovered).toHaveLength(1)
437
+
438
+ expect(onStepUpRequired).toHaveBeenCalledTimes(2)
439
+ expect(issueCalls).toBe(2)
440
+ expect(recoverCalls).toBe(2)
441
+ })
442
+ })
@@ -0,0 +1,84 @@
1
+ import { describe, expect, it } from 'vitest'
2
+
3
+ import { AppIcons, classIcon, getIcon, TuffIcons } from '../../icons'
4
+
5
+ describe('tuffIcons', () => {
6
+ it('should have common icons defined', () => {
7
+ expect(TuffIcons.Search).toBe('i-ri-search-line')
8
+ expect(TuffIcons.Settings).toBe('i-ri-settings-3-line')
9
+ expect(TuffIcons.Home).toBe('i-ri-home-line')
10
+ expect(TuffIcons.User).toBe('i-ri-user-line')
11
+ })
12
+
13
+ it('should have navigation icons', () => {
14
+ expect(TuffIcons.Back).toBe('i-ri-arrow-left-line')
15
+ expect(TuffIcons.Forward).toBe('i-ri-arrow-right-line')
16
+ expect(TuffIcons.Menu).toBe('i-ri-menu-line')
17
+ })
18
+
19
+ it('should have action icons', () => {
20
+ expect(TuffIcons.Add).toBe('i-ri-add-line')
21
+ expect(TuffIcons.Delete).toBe('i-ri-delete-bin-line')
22
+ expect(TuffIcons.Edit).toBe('i-ri-edit-line')
23
+ expect(TuffIcons.Copy).toBe('i-ri-file-copy-line')
24
+ })
25
+
26
+ it('should have status icons', () => {
27
+ expect(TuffIcons.Check).toBe('i-ri-check-line')
28
+ expect(TuffIcons.Close).toBe('i-ri-close-line')
29
+ expect(TuffIcons.Warning).toBe('i-ri-error-warning-line')
30
+ expect(TuffIcons.Info).toBe('i-ri-information-line')
31
+ })
32
+
33
+ it('should have file icons', () => {
34
+ expect(TuffIcons.File).toBe('i-ri-file-line')
35
+ expect(TuffIcons.Folder).toBe('i-ri-folder-line')
36
+ expect(TuffIcons.FileCode).toBe('i-ri-file-code-line')
37
+ })
38
+
39
+ it('should follow UnoCSS icon naming convention', () => {
40
+ Object.values(TuffIcons).forEach((iconClass) => {
41
+ expect(iconClass).toMatch(/^i-[a-z]+-[a-z0-9-]+$/)
42
+ })
43
+ })
44
+ })
45
+
46
+ describe('classIcon', () => {
47
+ it('should create ITuffIcon from class name', () => {
48
+ const icon = classIcon('i-ri-star-line')
49
+ expect(icon.type).toBe('class')
50
+ expect(icon.value).toBe('i-ri-star-line')
51
+ })
52
+ })
53
+
54
+ describe('getIcon', () => {
55
+ it('should get ITuffIcon from TuffIcons key', () => {
56
+ const icon = getIcon('Search')
57
+ expect(icon.type).toBe('class')
58
+ expect(icon.value).toBe('i-ri-search-line')
59
+ })
60
+
61
+ it('should work with all defined icon keys', () => {
62
+ const keys = ['Home', 'Settings', 'User', 'File', 'Folder'] as const
63
+ keys.forEach((key) => {
64
+ const icon = getIcon(key)
65
+ expect(icon.type).toBe('class')
66
+ expect(icon.value).toBeTruthy()
67
+ })
68
+ })
69
+ })
70
+
71
+ describe('appIcons', () => {
72
+ it('should have app brand icons', () => {
73
+ expect(AppIcons.VSCode).toBe('i-simple-icons-visualstudiocode')
74
+ expect(AppIcons.Chrome).toBe('i-simple-icons-googlechrome')
75
+ expect(AppIcons.GitHub).toBe('i-simple-icons-github')
76
+ expect(AppIcons.Slack).toBe('i-simple-icons-slack')
77
+ })
78
+
79
+ it('should follow UnoCSS icon naming convention', () => {
80
+ Object.values(AppIcons).forEach((iconClass) => {
81
+ expect(iconClass).toMatch(/^i-[a-z]+-[a-z0-9-]+$/)
82
+ })
83
+ })
84
+ })