@talex-touch/utils 1.0.42 → 1.0.45
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.
- package/.eslintcache +1 -0
- package/__tests__/cloud-sync-sdk.test.ts +442 -0
- package/__tests__/icons/icons.test.ts +84 -0
- package/__tests__/plugin-sdk-lifecycle.test.ts +130 -0
- package/__tests__/power-sdk.test.ts +143 -0
- package/__tests__/preset-export-types.test.ts +108 -0
- package/__tests__/search/fuzzy-match.test.ts +137 -0
- package/__tests__/transport/port-policy.test.ts +44 -0
- package/__tests__/transport-domain-sdks.test.ts +152 -0
- package/__tests__/types/update.test.ts +67 -0
- package/account/account-sdk.ts +915 -0
- package/account/index.ts +2 -0
- package/account/types.ts +321 -0
- package/analytics/client.ts +136 -0
- package/analytics/index.ts +2 -0
- package/analytics/types.ts +156 -0
- package/animation/auto-resize.ts +322 -0
- package/animation/window-node.ts +26 -19
- package/auth/clerk-types.ts +12 -30
- package/auth/index.ts +0 -2
- package/auth/useAuthState.ts +6 -14
- package/base/index.ts +2 -0
- package/base/log-level.ts +105 -0
- package/channel/index.ts +170 -69
- package/cloud-sync/cloud-sync-sdk.ts +450 -0
- package/cloud-sync/index.ts +1 -0
- package/common/file-scan-utils.ts +17 -9
- package/common/index.ts +4 -0
- package/common/logger/index.ts +46 -0
- package/common/logger/logger-manager.ts +303 -0
- package/common/logger/module-logger.ts +270 -0
- package/common/logger/transport-logger.ts +234 -0
- package/common/logger/types.ts +93 -0
- package/common/search/gather.ts +48 -6
- package/common/search/index.ts +8 -0
- package/common/storage/constants.ts +13 -0
- package/common/storage/entity/app-settings.ts +245 -0
- package/common/storage/entity/index.ts +3 -0
- package/common/storage/entity/layout-atom-types.ts +147 -0
- package/common/storage/entity/openers.ts +1 -0
- package/common/storage/entity/preset-cloud-api.ts +132 -0
- package/common/storage/entity/preset-export-types.ts +256 -0
- package/common/storage/entity/shortcut-settings.ts +1 -0
- package/common/storage/shortcut-storage.ts +11 -0
- package/common/utils/clone-diagnostics.ts +105 -0
- package/common/utils/file.ts +16 -8
- package/common/utils/index.ts +6 -2
- package/common/utils/payload-preview.ts +173 -0
- package/common/utils/polling.ts +167 -13
- package/common/utils/safe-path.ts +103 -0
- package/common/utils/safe-shell.ts +115 -0
- package/common/utils/task-queue.ts +4 -1
- package/core-box/builder/tuff-builder.ts +0 -1
- package/core-box/index.ts +1 -1
- package/core-box/recommendation.ts +38 -1
- package/core-box/tuff/tuff-dsl.ts +32 -0
- package/electron/download-manager.ts +10 -7
- package/electron/env-tool.ts +42 -40
- package/electron/index.ts +0 -1
- package/env/index.ts +156 -0
- package/eslint.config.js +55 -0
- package/i18n/index.ts +62 -0
- package/i18n/locales/en.json +226 -0
- package/i18n/locales/zh.json +226 -0
- package/i18n/message-keys.ts +236 -0
- package/i18n/resolver.ts +181 -0
- package/icons/index.ts +257 -0
- package/icons/svg.ts +69 -0
- package/index.ts +9 -1
- package/intelligence/client.ts +72 -42
- package/market/constants.ts +9 -5
- package/market/index.ts +1 -1
- package/market/types.ts +19 -4
- package/package.json +15 -5
- package/permission/index.ts +143 -46
- package/permission/legacy.ts +26 -0
- package/permission/registry.ts +304 -0
- package/permission/types.ts +164 -0
- package/plugin/channel.ts +68 -39
- package/plugin/index.ts +80 -7
- package/plugin/install.ts +3 -0
- package/plugin/log/types.ts +22 -5
- package/plugin/node/logger-manager.ts +11 -3
- package/plugin/node/logger.ts +24 -17
- package/plugin/preload.ts +25 -2
- package/plugin/providers/index.ts +4 -4
- package/plugin/providers/market-client.ts +6 -3
- package/plugin/providers/npm-provider.ts +22 -7
- package/plugin/providers/tpex-provider.ts +22 -8
- package/plugin/sdk/box-items.ts +14 -0
- package/plugin/sdk/box-sdk.ts +64 -0
- package/plugin/sdk/channel.ts +119 -4
- package/plugin/sdk/clipboard.ts +26 -12
- package/plugin/sdk/cloud-sync.ts +113 -0
- package/plugin/sdk/common.ts +19 -11
- package/plugin/sdk/core-box.ts +6 -15
- package/plugin/sdk/division-box.ts +160 -65
- package/plugin/sdk/examples/storage-onDidChange-example.js +5 -2
- package/plugin/sdk/feature-sdk.ts +111 -76
- package/plugin/sdk/flow.ts +146 -45
- package/plugin/sdk/hooks/bridge.ts +13 -6
- package/plugin/sdk/hooks/life-cycle.ts +35 -16
- package/plugin/sdk/index.ts +14 -3
- package/plugin/sdk/intelligence.ts +87 -0
- package/plugin/sdk/meta/README.md +179 -0
- package/plugin/sdk/meta-sdk.ts +244 -0
- package/plugin/sdk/notification.ts +9 -0
- package/plugin/sdk/plugin-info.ts +64 -0
- package/plugin/sdk/power.ts +155 -0
- package/plugin/sdk/recommend.ts +21 -0
- package/plugin/sdk/service/index.ts +12 -8
- package/plugin/sdk/sqlite.ts +141 -0
- package/plugin/sdk/storage.ts +2 -6
- package/plugin/sdk/system.ts +2 -9
- package/plugin/sdk/temp-files.ts +41 -0
- package/plugin/sdk/touch-sdk.ts +18 -0
- package/plugin/sdk/types.ts +44 -4
- package/plugin/sdk/window/index.ts +12 -9
- package/plugin/sdk-version.ts +231 -0
- package/preload/renderer.ts +3 -2
- package/renderer/hooks/arg-mapper.ts +16 -2
- package/renderer/hooks/index.ts +13 -0
- package/renderer/hooks/initialize.ts +2 -1
- package/renderer/hooks/use-agent-market-sdk.ts +7 -0
- package/renderer/hooks/use-agent-market.ts +106 -0
- package/renderer/hooks/use-agents-sdk.ts +7 -0
- package/renderer/hooks/use-app-sdk.ts +7 -0
- package/renderer/hooks/use-channel.ts +33 -4
- package/renderer/hooks/use-download-sdk.ts +21 -0
- package/renderer/hooks/use-intelligence-sdk.ts +7 -0
- package/renderer/hooks/use-intelligence-stats.ts +290 -0
- package/renderer/hooks/use-intelligence.ts +55 -214
- package/renderer/hooks/use-market-sdk.ts +16 -0
- package/renderer/hooks/use-notification-sdk.ts +7 -0
- package/renderer/hooks/use-permission-sdk.ts +7 -0
- package/renderer/hooks/use-permission.ts +325 -0
- package/renderer/hooks/use-platform-sdk.ts +7 -0
- package/renderer/hooks/use-plugin-sdk.ts +16 -0
- package/renderer/hooks/use-settings-sdk.ts +7 -0
- package/renderer/hooks/use-update-sdk.ts +21 -0
- package/renderer/index.ts +1 -0
- package/renderer/ref.ts +19 -10
- package/renderer/shared/components/SharedPluginDetailContent.vue +84 -0
- package/renderer/shared/components/SharedPluginDetailHeader.vue +116 -0
- package/renderer/shared/components/SharedPluginDetailMetaList.vue +39 -0
- package/renderer/shared/components/SharedPluginDetailReadme.vue +45 -0
- package/renderer/shared/components/SharedPluginDetailVersions.vue +98 -0
- package/renderer/shared/components/index.ts +5 -0
- package/renderer/shared/components/shims-vue.d.ts +5 -0
- package/renderer/shared/index.ts +2 -0
- package/renderer/shared/plugin-detail.ts +62 -0
- package/renderer/storage/app-settings.ts +3 -1
- package/renderer/storage/base-storage.ts +508 -82
- package/renderer/storage/intelligence-storage.ts +31 -40
- package/renderer/storage/openers.ts +3 -1
- package/renderer/storage/storage-subscription.ts +126 -42
- package/renderer/touch-sdk/env.ts +10 -10
- package/renderer/touch-sdk/index.ts +114 -18
- package/renderer/touch-sdk/terminal.ts +24 -13
- package/search/feature-matcher.ts +279 -0
- package/search/fuzzy-match.ts +64 -34
- package/search/index.ts +10 -0
- package/search/levenshtein-utils.ts +17 -11
- package/transport/errors.ts +310 -0
- package/transport/event/builder.ts +378 -0
- package/transport/event/index.ts +7 -0
- package/transport/event/types.ts +292 -0
- package/transport/events/index.ts +2690 -0
- package/transport/events/meta-overlay.ts +79 -0
- package/transport/events/types/agents.ts +177 -0
- package/transport/events/types/app-index.ts +20 -0
- package/transport/events/types/app.ts +475 -0
- package/transport/events/types/box-item.ts +222 -0
- package/transport/events/types/clipboard.ts +80 -0
- package/transport/events/types/core-box.ts +534 -0
- package/transport/events/types/device-idle.ts +7 -0
- package/transport/events/types/division-box.ts +99 -0
- package/transport/events/types/download.ts +115 -0
- package/transport/events/types/file-index.ts +84 -0
- package/transport/events/types/flow.ts +149 -0
- package/transport/events/types/index.ts +70 -0
- package/transport/events/types/market.ts +39 -0
- package/transport/events/types/meta-overlay.ts +184 -0
- package/transport/events/types/notification.ts +140 -0
- package/transport/events/types/permission.ts +90 -0
- package/transport/events/types/platform.ts +8 -0
- package/transport/events/types/plugin.ts +631 -0
- package/transport/events/types/sentry.ts +20 -0
- package/transport/events/types/storage.ts +208 -0
- package/transport/events/types/transport.ts +60 -0
- package/transport/events/types/tray.ts +16 -0
- package/transport/events/types/update.ts +78 -0
- package/transport/index.ts +141 -0
- package/transport/main.ts +2 -0
- package/transport/prelude.ts +208 -0
- package/transport/sdk/constants.ts +29 -0
- package/transport/sdk/domains/agents-market.ts +47 -0
- package/transport/sdk/domains/agents.ts +62 -0
- package/transport/sdk/domains/app.ts +48 -0
- package/transport/sdk/domains/disposable.ts +35 -0
- package/transport/sdk/domains/download.ts +139 -0
- package/transport/sdk/domains/index.ts +13 -0
- package/transport/sdk/domains/intelligence.ts +616 -0
- package/transport/sdk/domains/market.ts +35 -0
- package/transport/sdk/domains/notification.ts +62 -0
- package/transport/sdk/domains/permission.ts +85 -0
- package/transport/sdk/domains/platform.ts +19 -0
- package/transport/sdk/domains/plugin.ts +144 -0
- package/transport/sdk/domains/settings.ts +102 -0
- package/transport/sdk/domains/update.ts +64 -0
- package/transport/sdk/index.ts +60 -0
- package/transport/sdk/main-transport.ts +710 -0
- package/transport/sdk/main.ts +9 -0
- package/transport/sdk/plugin-transport.ts +654 -0
- package/transport/sdk/port-policy.ts +38 -0
- package/transport/sdk/renderer-transport.ts +1165 -0
- package/transport/types.ts +605 -0
- package/types/agent.ts +399 -0
- package/types/cloud-sync.ts +157 -0
- package/types/division-box.ts +31 -31
- package/types/download.ts +1 -0
- package/types/flow.ts +63 -12
- package/types/icon.ts +2 -1
- package/types/index.ts +5 -0
- package/types/intelligence.ts +166 -173
- package/types/modules/base.ts +2 -0
- package/types/path-browserify.d.ts +5 -0
- package/types/platform.ts +12 -0
- package/types/startup-info.ts +32 -0
- package/types/touch-app-core.ts +8 -8
- package/types/update.ts +94 -1
- package/vitest.config.ts +25 -0
- package/auth/useClerkConfig.ts +0 -40
- 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","/Users/talexdreamsoul/Workspace/Projects/talex-touch/packages/utils/transport/events/types/app-index.ts":"32","/Users/talexdreamsoul/Workspace/Projects/talex-touch/packages/utils/transport/index.ts":"33","/Users/talexdreamsoul/Workspace/Projects/talex-touch/packages/utils/transport/prelude.ts":"34","/Users/talexdreamsoul/Workspace/Projects/talex-touch/packages/utils/transport/events/types/plugin.ts":"35"},{"size":5306,"mtime":1771563102452,"results":"36","hashOfConfig":"37"},{"size":15379,"mtime":1771563102455,"results":"38","hashOfConfig":"37"},{"size":5659,"mtime":1771563102455,"results":"39","hashOfConfig":"37"},{"size":2691,"mtime":1771563102456,"results":"40","hashOfConfig":"37"},{"size":2710,"mtime":1771755191828,"results":"41","hashOfConfig":"42"},{"size":2472,"mtime":1771563102458,"results":"43","hashOfConfig":"37"},{"size":7760,"mtime":1771563102481,"results":"44","hashOfConfig":"37"},{"size":4206,"mtime":1771563102484,"results":"45","hashOfConfig":"37"},{"size":7775,"mtime":1771563102484,"results":"46","hashOfConfig":"37"},{"size":3423,"mtime":1771563102493,"results":"47","hashOfConfig":"37"},{"size":3750,"mtime":1771563102509,"results":"48","hashOfConfig":"37"},{"size":26935,"mtime":1771563102511,"results":"49","hashOfConfig":"37"},{"size":6457,"mtime":1771563102514,"results":"50","hashOfConfig":"37"},{"size":21154,"mtime":1771563102519,"results":"51","hashOfConfig":"37"},{"size":3091,"mtime":1771563102519,"results":"52","hashOfConfig":"37"},{"size":55314,"mtime":1771563102519,"results":"53","hashOfConfig":"37"},{"size":6327,"mtime":1771566199653,"results":"54","hashOfConfig":"42"},{"size":2888,"mtime":1771563689317,"results":"55","hashOfConfig":"37"},{"size":3003,"mtime":1771611450129,"results":"56","hashOfConfig":"42"},{"size":986,"mtime":1771611509397,"results":"57","hashOfConfig":"42"},{"size":683,"mtime":1771611477506,"results":"58","hashOfConfig":"42"},{"size":26106,"mtime":1771611536284,"results":"59","hashOfConfig":"42"},{"size":11083,"mtime":1771632074503,"results":"60","hashOfConfig":"42"},{"size":74847,"mtime":1771749468590,"results":"61","hashOfConfig":"42"},{"size":1770,"mtime":1771710444200,"results":"62","hashOfConfig":"42"},{"size":4523,"mtime":1771710495855,"results":"63","hashOfConfig":"42"},{"size":3327,"mtime":1771678966584,"results":"64","hashOfConfig":"42"},{"size":18271,"mtime":1771679568421,"results":"65","hashOfConfig":"42"},{"size":329,"mtime":1771678957747,"results":"66","hashOfConfig":"42"},{"size":135,"mtime":1771678957747,"results":"67","hashOfConfig":"68"},{"size":9317,"mtime":1771684543626,"results":"69","hashOfConfig":"42"},{"size":478,"mtime":1771710451259,"results":"70","hashOfConfig":"42"},{"size":3533,"mtime":1771739652939,"results":"71","hashOfConfig":"42"},{"size":6426,"mtime":1771739449023,"results":"72","hashOfConfig":"42"},{"size":11904,"mtime":1771750097131,"results":"73","hashOfConfig":"42"},{"filePath":"74","messages":"75","suppressedMessages":"76","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},"tyugd7",{"filePath":"77","messages":"78","suppressedMessages":"79","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"80","messages":"81","suppressedMessages":"82","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"83","messages":"84","suppressedMessages":"85","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"86","messages":"87","suppressedMessages":"88","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},"m7p77f",{"filePath":"89","messages":"90","suppressedMessages":"91","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"92","messages":"93","suppressedMessages":"94","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"95","messages":"96","suppressedMessages":"97","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"98","messages":"99","suppressedMessages":"100","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"101","messages":"102","suppressedMessages":"103","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"104","messages":"105","suppressedMessages":"106","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"107","messages":"108","suppressedMessages":"109","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"110","messages":"111","suppressedMessages":"112","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"113","messages":"114","suppressedMessages":"115","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"116","messages":"117","suppressedMessages":"118","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"119","messages":"120","suppressedMessages":"121","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"122","messages":"123","suppressedMessages":"124","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"125","messages":"126","suppressedMessages":"127","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"128","messages":"129","suppressedMessages":"130","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"131","messages":"132","suppressedMessages":"133","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"134","messages":"135","suppressedMessages":"136","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"137","messages":"138","suppressedMessages":"139","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"140","messages":"141","suppressedMessages":"142","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"143","messages":"144","suppressedMessages":"145","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"146","messages":"147","suppressedMessages":"148","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"149","messages":"150","suppressedMessages":"151","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"152","messages":"153","suppressedMessages":"154","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"155","messages":"156","suppressedMessages":"157","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"158","messages":"159","suppressedMessages":"160","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"161","messages":"162","suppressedMessages":"163","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},"552fw",{"filePath":"164","messages":"165","suppressedMessages":"166","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"167","messages":"168","suppressedMessages":"169","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"170","messages":"171","suppressedMessages":"172","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"173","messages":"174","suppressedMessages":"175","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"176","messages":"177","suppressedMessages":"178","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",[],[],"/Users/talexdreamsoul/Workspace/Projects/talex-touch/packages/utils/transport/events/types/app-index.ts",[],[],"/Users/talexdreamsoul/Workspace/Projects/talex-touch/packages/utils/transport/index.ts",[],[],"/Users/talexdreamsoul/Workspace/Projects/talex-touch/packages/utils/transport/prelude.ts",[],[],"/Users/talexdreamsoul/Workspace/Projects/talex-touch/packages/utils/transport/events/types/plugin.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
|
+
})
|