diff --git a/AGENTS.md b/AGENTS.md index 2efc8d73..ad04a134 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -48,7 +48,7 @@ This is the monorepo for an extensible Obsidian syncing plugin to sync vault fil - For mobile compatibility, Node.js API prohibited. - Sentence case for UI text. - Module-specific behavior should not pollute plugin core. -- All Obsidian API mocks go `packages/shared/src/obsidian-mock.ts`. +- All Obsidian API mocks go `packages/shared/test/obsidian-mock.ts`. - Use inline Tailwind CSS for common styling, only use semantic CSS for animations and complex compositions. (Documentation website doesn't use TailwindCSS, you need to edit `docs/.vitepress/theme/styles.css`) - When any function or class needs to use `Context` as argument, prefer structural typing instead of direct `Context`. - Excluding main plugin, shared utils and documentation site, all packages are Sync Engine modules, they use the SDK and follow unified module structure. diff --git a/bun.lock b/bun.lock index 257cc31e..749562f5 100644 --- a/bun.lock +++ b/bun.lock @@ -14,7 +14,7 @@ "oxlint-tsgolint": "^7.0.2001", "tsdown": "^0.22.14", "turbo": "^2.10.11", - "typescript": "^7.0.2", + "typescript": "^6.0.3", }, }, "docs": { @@ -27,7 +27,6 @@ "vue": "^3.5.41", }, "devDependencies": { - "typescript": "^6.0.0", "vite-plugin-json-canvas": "^1.0.1", "vue-tsc": "^3.3.9", }, @@ -49,7 +48,6 @@ "devDependencies": { "@hesprs/sync-engine-sdk": "workspace:*", "@repo/shared": "workspace:*", - "hash-wasm": "^4.12.0", "uni-kv": "../../uni-kv.tgz", }, }, @@ -62,7 +60,7 @@ }, "packages/plugin": { "name": "@hesprs/sync-engine-sdk", - "version": "3.1.0", + "version": "3.1.1", "dependencies": { "obsidian": "^1.13.1", }, @@ -429,46 +427,6 @@ "@types/web-bluetooth": ["@types/web-bluetooth@0.0.21", "", {}, "sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA=="], - "@typescript/typescript-aix-ppc64": ["@typescript/typescript-aix-ppc64@7.0.2", "", { "os": "aix", "cpu": "ppc64" }, "sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ=="], - - "@typescript/typescript-darwin-arm64": ["@typescript/typescript-darwin-arm64@7.0.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-gowzar9MwS/aRWp6f3a4KUqzRjAZjOsmGNCM6LcTgXum+dBfgsBVMN+AgvOCCbguXyick6LJhpBszxMebJ8syA=="], - - "@typescript/typescript-darwin-x64": ["@typescript/typescript-darwin-x64@7.0.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-SZ9xZInqApNlNGc9s0W1VSsktYSOe9cFqNOIqmN1Gs8SmkjKZYFt017G4VwPxASInODuAdbTW7sXiFUf893RgA=="], - - "@typescript/typescript-freebsd-arm64": ["@typescript/typescript-freebsd-arm64@7.0.2", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-W5NH4y/J0plIIS5b2xvTEkU7JFxyqdMAOgf+Ilhl0vHQXKO5dZoxd+C/jEtq56c4F3wk71RB4BMRQ2XdI+bwYQ=="], - - "@typescript/typescript-freebsd-x64": ["@typescript/typescript-freebsd-x64@7.0.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-UMGDx5sTpzNw3WiPebH7l90IWfJggEd+egHt/q6p7/Cm3zqoV7VxkGXt+3DxPIw8CcmvAB0j3sVVfbhX+M4Tpw=="], - - "@typescript/typescript-linux-arm": ["@typescript/typescript-linux-arm@7.0.2", "", { "os": "linux", "cpu": "arm" }, "sha512-gffT3xPz9sR7j/YJExkyPntrI0P2EP9XbOyWzth2/Gs0RstK+90RBcO0ncXoXy/beYll1SXw846Nf2zdnEz0QQ=="], - - "@typescript/typescript-linux-arm64": ["@typescript/typescript-linux-arm64@7.0.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-Qh4eU4/y3yDjnfjjyPYihMj5/ODIlmt+Bzu17OI+fiSRDW57QmU5SiN63exPRNJPKUzcc1INa1NXdrJ+MqHjUQ=="], - - "@typescript/typescript-linux-loong64": ["@typescript/typescript-linux-loong64@7.0.2", "", { "os": "linux", "cpu": "none" }, "sha512-uEHck9i8hoAzXPiYRib1O7miOnz23SxIeVl6F4LXox+qov1K35jHcEW6VHKvZI+pyvl7fZEP4MCU5LYvIq1GuQ=="], - - "@typescript/typescript-linux-mips64el": ["@typescript/typescript-linux-mips64el@7.0.2", "", { "os": "linux", "cpu": "none" }, "sha512-R4KvAMnE43W5Qeqb0Ly56O3mWMWIAgsMyz36DCaycd5nbg/9kzm0liw3JocfRqyJY0KPmzFjbswozXyW0DnIYA=="], - - "@typescript/typescript-linux-ppc64": ["@typescript/typescript-linux-ppc64@7.0.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-DORx5b3sd/4S7eayxm4FQv+A7CrkUIGRaHiwI8oiHTAI1fAPWhF4J0vAlkC8biAlHSVVwxMQ3tjZ2/DVbnQiiA=="], - - "@typescript/typescript-linux-riscv64": ["@typescript/typescript-linux-riscv64@7.0.2", "", { "os": "linux", "cpu": "none" }, "sha512-wf0jqEDOjrPRnKwYRyyJDRo11KMbvMFrU+q4zqKyChODBzvlkbhNQfKvLxQCcwTpdDaXSHZTVuh0JoCrKCUMHQ=="], - - "@typescript/typescript-linux-s390x": ["@typescript/typescript-linux-s390x@7.0.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-IkwJc3L7yhytWd/ewjyxNDfOmswCm9GWMJT/ue/dU4aZNbwZeYAetq42VyLmsmSjvoX7z74X6ZaYCtzAr0EuGw=="], - - "@typescript/typescript-linux-x64": ["@typescript/typescript-linux-x64@7.0.2", "", { "os": "linux", "cpu": "x64" }, "sha512-EYdf2cNg7rgCWJnxCdJ+F3V39O8ihb37eHAu1LK8oAFizgTQbPOK7zHHXbPt8rX24COqODXeI3sIf0fCXG7H/A=="], - - "@typescript/typescript-netbsd-arm64": ["@typescript/typescript-netbsd-arm64@7.0.2", "", { "os": "none", "cpu": "arm64" }, "sha512-+polYF4MF04aPpO5FTkHran9yUQDSXqy5GiSDKpsll5jy3l3+g9QLhpf39T+ePtefhXLOGrLl0QIjkQP6VnelA=="], - - "@typescript/typescript-netbsd-x64": ["@typescript/typescript-netbsd-x64@7.0.2", "", { "os": "none", "cpu": "x64" }, "sha512-8YIT0EHM/3dq10ZOVF/A7pc/YSMtbcecct4rWtexrnSCHOPcpC2KTLXfTCR6vDpnSiY12heNb1GiN/wu+T/FyA=="], - - "@typescript/typescript-openbsd-arm64": ["@typescript/typescript-openbsd-arm64@7.0.2", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-APT8+ClYnuYm1u9+kgGXoMj2VzWzcymwh2gNSQVySHfkRDGOTVkoWLjCmOQSaO+PoqQ57B0flRp9SA+7GnnkzQ=="], - - "@typescript/typescript-openbsd-x64": ["@typescript/typescript-openbsd-x64@7.0.2", "", { "os": "openbsd", "cpu": "x64" }, "sha512-yX7s+Q0Dln0Dt9tEzZsAjXXR/+ytBM7AlglaqyeMPxQszJ1JhlJdZ6jLA+IzldHtflX81em7lDao1xXu+aRRkg=="], - - "@typescript/typescript-sunos-x64": ["@typescript/typescript-sunos-x64@7.0.2", "", { "os": "sunos", "cpu": "x64" }, "sha512-dLJDGaLZ1D4HPQn62u1n8mBDkJREwMsAkCdkwd4Ieqw+x3TUyTsqY0YiBCtE6H6OzzgGk3iuZ3vFWRS+E8/d1g=="], - - "@typescript/typescript-win32-arm64": ["@typescript/typescript-win32-arm64@7.0.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-Gyl1Vy6OsWesLzmq+EP0Fb7b4Nid5232AvcA2SFcdYreldpNtYFFofPjnt62y9hQy7VTaZp65ICJjuAQRaVcIQ=="], - - "@typescript/typescript-win32-x64": ["@typescript/typescript-win32-x64@7.0.2", "", { "os": "win32", "cpu": "x64" }, "sha512-0BQ3HkAHHlKLSp1qRvf3SUhGpGsDuhB/jgFw75guyqbxJqEaS0Cw/VFO8i2nHglJUzQCRtMMR/IBAKE3ETMC4g=="], - "@ungap/structured-clone": ["@ungap/structured-clone@1.3.3", "", {}, "sha512-60YRaenCQcVjYEKOcG824+DRGGIQ3VKErcBoAEDJZz5bKIs2ZG+X/H9Nk+Q6EVkwJk5QNApxbrc5QtBSwtrXAg=="], "@unocss/cli": ["@unocss/cli@66.7.5", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "@unocss/config": "66.7.5", "@unocss/core": "66.7.5", "@unocss/preset-wind3": "66.7.5", "@unocss/preset-wind4": "66.7.5", "@unocss/transformer-directives": "66.7.5", "cac": "^7.0.0", "chokidar": "^5.0.0", "colorette": "^2.0.20", "consola": "^3.4.2", "magic-string": "^0.30.21", "pathe": "^2.0.3", "perfect-debounce": "^2.1.0", "tinyglobby": "^0.2.17", "unplugin-utils": "^0.3.1" }, "bin": { "unocss": "./bin/unocss.mjs" } }, "sha512-fgWkECRn2LGVo9sEpmjk/KJ3NSKUDitaZCNrJcEYM93DG+ELriTHcL+VWBlF4rcZf9fdGlq1nKbbRHUZirFCHQ=="], @@ -923,7 +881,7 @@ "type-level-regexp": ["type-level-regexp@0.1.17", "", {}, "sha512-wTk4DH3cxwk196uGLK/E9pE45aLfeKJacKmcEgEOA/q5dnPGNxXt0cfYdFxb57L+sEpf1oJH4Dnx/pnRcku9jg=="], - "typescript": ["typescript@7.0.2", "", { "optionalDependencies": { "@typescript/typescript-aix-ppc64": "7.0.2", "@typescript/typescript-darwin-arm64": "7.0.2", "@typescript/typescript-darwin-x64": "7.0.2", "@typescript/typescript-freebsd-arm64": "7.0.2", "@typescript/typescript-freebsd-x64": "7.0.2", "@typescript/typescript-linux-arm": "7.0.2", "@typescript/typescript-linux-arm64": "7.0.2", "@typescript/typescript-linux-loong64": "7.0.2", "@typescript/typescript-linux-mips64el": "7.0.2", "@typescript/typescript-linux-ppc64": "7.0.2", "@typescript/typescript-linux-riscv64": "7.0.2", "@typescript/typescript-linux-s390x": "7.0.2", "@typescript/typescript-linux-x64": "7.0.2", "@typescript/typescript-netbsd-arm64": "7.0.2", "@typescript/typescript-netbsd-x64": "7.0.2", "@typescript/typescript-openbsd-arm64": "7.0.2", "@typescript/typescript-openbsd-x64": "7.0.2", "@typescript/typescript-sunos-x64": "7.0.2", "@typescript/typescript-win32-arm64": "7.0.2", "@typescript/typescript-win32-x64": "7.0.2" }, "bin": { "tsc": "bin/tsc" } }, "sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA=="], + "typescript": ["typescript@6.0.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw=="], "ufo": ["ufo@1.6.4", "", {}, "sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA=="], @@ -1003,8 +961,6 @@ "caniuse-api/browserslist": ["browserslist@4.28.7", "", { "dependencies": { "baseline-browser-mapping": "^2.10.44", "caniuse-lite": "^1.0.30001806", "electron-to-chromium": "^1.5.393", "node-releases": "^2.0.51", "update-browserslist-db": "^1.2.3" }, "bin": { "browserslist": "cli.js" } }, "sha512-JxV13hNrFxqjOc8alRbq9dK1MM79NEXYpma2B2J4wAtpWS5zIEIKqWPGCl7N4o7Uc7B7itylh7SuDujATRyyTw=="], - "docs/typescript": ["typescript@6.0.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw=="], - "json-canvas-viewer/marked": ["marked@18.0.9", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-/Sa4qiiHZxf0/FQdBBowr9q4r10krCwMvpK48FUBdXdUXScDxiQGR9zCPrFgRVR5LU3iySOiIjy09ZQvADir1w=="], "magic-regexp/estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="], @@ -1015,8 +971,6 @@ "parse5/entities": ["entities@6.0.1", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="], - "synthkernel/typescript": ["typescript@6.0.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw=="], - "vitepress-theme-trito/vitepress": ["vitepress@2.0.0-alpha.18", "", { "dependencies": { "@docsearch/css": "^4.6.3", "@docsearch/js": "^4.6.3", "@docsearch/sidepanel-js": "^4.6.3", "@iconify-json/simple-icons": "^1.2.87", "@shikijs/core": "^4.3.0", "@shikijs/transformers": "^4.3.0", "@shikijs/types": "^4.3.0", "@types/markdown-it": "^14.1.2", "@vitejs/plugin-vue": "^6.0.7", "@vue/devtools-api": "^8.1.4", "@vue/shared": "^3.5.39", "@vueuse/core": "^14.3.0", "@vueuse/integrations": "^14.3.0", "focus-trap": "^8.2.2", "mark.js": "8.11.1", "minisearch": "^7.2.0", "shiki": "^4.3.0", "vite": "^8.1.3", "vue": "^3.5.39" }, "peerDependencies": { "markdown-it-mathjax3": "^4", "postcss": "^8" }, "optionalPeers": ["markdown-it-mathjax3", "postcss"], "bin": { "vitepress": "bin/vitepress.js" } }, "sha512-Lk1G2/QqSf+MwNLICl9fmzWOtoCEwjZoWgaQy41QvWTfcGpLE9XwJDbcCyES/9rj6R2g1zFqFvLVkYKLLDALFw=="], } } diff --git a/docs/.vitepress/i18n.ts b/docs/.vitepress/i18n.ts index c174214d..59129d84 100644 --- a/docs/.vitepress/i18n.ts +++ b/docs/.vitepress/i18n.ts @@ -39,7 +39,7 @@ const en = { s3: 'S3', security: 'Security', settings: 'Settings', - settingsAndUI: 'Settings And UI', + settingsAndUI: 'Settings and UI', sideDescription: 'Next-generation syncing plugin for Obsidian.', smartMerge: 'Smart Merge', storage: 'Storage', diff --git a/docs/package.json b/docs/package.json index ecbe168e..64b17eec 100644 --- a/docs/package.json +++ b/docs/package.json @@ -17,7 +17,6 @@ "vue": "^3.5.41" }, "devDependencies": { - "typescript": "^6.0.0", "vite-plugin-json-canvas": "^1.0.1", "vue-tsc": "^3.3.9" } diff --git a/docs/src/pages/en/deep-dive/architecture.md b/docs/src/pages/en/deep-dive/architecture.md index f465f1e3..60dad926 100644 --- a/docs/src/pages/en/deep-dive/architecture.md +++ b/docs/src/pages/en/deep-dive/architecture.md @@ -89,7 +89,7 @@ Registration entries are consumed according to their role: - **Factories and ID maps:** the selected remote backend, decider, and conflict resolver are looked up by the ID in settings. Missing IDs fail with an explicit error. - **Wrappers:** request and file-system wrappers are grouped by numeric priority and applied in ascending priority order. Within one priority, the first wrapper that returns a replacement wins; returning `undefined` declines the current value. - **First-match pipelines:** listers and optimizers are also priority ordered. The first entry that returns a result supplies the implementation for that operation. -- **Settings:** setting-definition trees are ordered by priority and merged into the native plugin settings tab. See [Settings And UI](../development/settings-and-ui). +- **Settings:** setting-definition trees are ordered by priority and merged into the native plugin settings tab. See [Settings and UI](../development/settings-and-ui). Bootstrap uses these same APIs to build the production pipeline. For example, cancellation, rate limiting, retry, custom headers, memory control, optimization, context caching, and asymmetric storage are independent registrations layered around the base request and file-system implementations. Their behavior is specified in [Request Middleware](./request-middleware) and [File System Wrappers](./file-system-wrappers). diff --git a/docs/src/pages/en/development/miscellaneous.md b/docs/src/pages/en/development/miscellaneous.md index c6dbce16..31ab3f9a 100644 --- a/docs/src/pages/en/development/miscellaneous.md +++ b/docs/src/pages/en/development/miscellaneous.md @@ -8,11 +8,11 @@ | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------- | | `digOriginal` | Unwraps nested wrappers to root filesystem. See [file system](./file-system#digoriginal). | | `prefixWrapper` | Exposes a prefixed directory as an `Fs` root. See [file system](./file-system#prefixwrapper). | -| `setNeedMigration` | Adds migration confirmation behavior to a setting toggle; see [Settings And UI](./settings-and-ui#migration-aware-toggles). | +| `setNeedMigration` | Adds migration confirmation behavior to a setting toggle; see [Settings and UI](./settings-and-ui#migration-aware-toggles). | | `pipe` | Transfer a file between two filesystems with auto-streaming. See [file system](./file-system#pipe). | | `readWithSize` | Read a file with auto-streaming by size threshold. See [file system](./file-system#readwithsize). | | `writeWithValue` | Write a value with auto-streaming by input type. See [file system](./file-system#writewithvalue). | -| `s` | Combines a parent setting definition with nested setting definitions. See [Settings And UI](./settings-and-ui#nested-setting-registration). | +| `s` | Combines a parent setting definition with nested setting definitions. See [Settings and UI](./settings-and-ui#nested-setting-registration). | ### `/dev` runtime exports @@ -145,7 +145,7 @@ These Context members are not commonly used by modules. Explore source code to o | `on(key, cb)` | Subscribes to typed SDK event. See [events](./events). | | `dispatch(key, payload?)` | Dispatches typed SDK event. See [events](./events). | | `isIdle` | SynthKernel `Ref` for sync-idle state. | -| `translate` | Translates a key from merged resources. See [Settings And UI](./settings-and-ui#internationalization). | +| `translate` | Translates a key from merged resources. See [Settings and UI](./settings-and-ui#internationalization). | | `rerenderSettingTab()` | Renders contributed settings again. | | `refreshSettingTab()` | Cheap setting page rerender that makes Obsidian reevaluate the visibility of each setting entry. | diff --git a/docs/src/pages/en/development/registration.md b/docs/src/pages/en/development/registration.md index 9658995c..c3b48c44 100644 --- a/docs/src/pages/en/development/registration.md +++ b/docs/src/pages/en/development/registration.md @@ -126,9 +126,9 @@ type ConflictResolverEntry = { prettyName: () => string; resolver: ConflictResol ctx.registerConflictResolver(id: string, entry: ConflictResolverEntry): () => boolean; ``` -## Settings And UI +## Settings and UI -Register nested setting definitions, translations, and migration-aware controls as described in [Settings And UI](./settings-and-ui). +Register nested setting definitions, translations, and migration-aware controls as described in [Settings and UI](./settings-and-ui). ## CSS diff --git a/docs/src/pages/en/development/settings-and-ui.md b/docs/src/pages/en/development/settings-and-ui.md index 2d159e5e..20a9d51b 100644 --- a/docs/src/pages/en/development/settings-and-ui.md +++ b/docs/src/pages/en/development/settings-and-ui.md @@ -1,4 +1,4 @@ -# Settings And UI +# Settings and UI Sync Engine settings use Obsidian's setting definitions. Modules can contribute nested groups, pages, lists, controls, and custom rendered settings without owning the plugin settings tab. This page also covers the translation and migration helpers used by module UI. diff --git a/package.json b/package.json index e34a364e..98e8edbd 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "oxlint-tsgolint": "^7.0.2001", "tsdown": "^0.22.14", "turbo": "^2.10.11", - "typescript": "^7.0.2" + "typescript": "^6.0.3" }, "packageManager": "bun@1.3.13" } diff --git a/packages/gdrive/package.json b/packages/gdrive/package.json index c3e4a1bf..2dbd9de0 100644 --- a/packages/gdrive/package.json +++ b/packages/gdrive/package.json @@ -25,7 +25,6 @@ "devDependencies": { "@hesprs/sync-engine-sdk": "workspace:*", "@repo/shared": "workspace:*", - "hash-wasm": "^4.12.0", "uni-kv": "../../uni-kv.tgz" } } diff --git a/packages/gdrive/test/auth.test.ts b/packages/gdrive/test/auth.test.ts index dc9fb052..1ea050cc 100644 --- a/packages/gdrive/test/auth.test.ts +++ b/packages/gdrive/test/auth.test.ts @@ -1,6 +1,6 @@ import type { Request, RequestParam, RequestResponse } from '@hesprs/sync-engine-sdk'; import type { SecretStorage } from 'obsidian'; -import * as ObsidianMock from '@repo/shared/mocks'; +import ObsidianMock from '@repo/shared/obsidian-mock'; import { expect, mock, test } from 'bun:test'; type HttpResponse = { json: unknown; status?: number }; diff --git a/packages/gdrive/test/mocks.ts b/packages/gdrive/test/mocks.ts index 45f6b609..7e8474f7 100644 --- a/packages/gdrive/test/mocks.ts +++ b/packages/gdrive/test/mocks.ts @@ -1,5 +1,4 @@ -// oxlint-disable-next-line import/no-namespace -import * as ObsidianMock from '@repo/shared/mocks'; +import ObsidianMock from '@repo/shared/obsidian-mock'; import { mock } from 'bun:test'; process.env.CLIENT_ID = btoa(process.env.GDRIVE_CLIENT_ID ?? ''); diff --git a/packages/plugin/dist/dev.spec.d.ts b/packages/plugin/dist/dev.spec.d.ts index 59aad76c..068ff8e0 100644 --- a/packages/plugin/dist/dev.spec.d.ts +++ b/packages/plugin/dist/dev.spec.d.ts @@ -1,4 +1,4 @@ -import { $ as RootFs, G as Fs, P as TaskNames, ct as RecordStat, dt as StatsMap, et as WrappedFs, h as RequestParam, it as FolderStat, lt as RecordStatsMap, m as Request, nt as Binary, rt as FileStat, ut as Stat, v as Decider } from "./index-LOl_cerr.spec.js"; +import { $ as RootFs, G as Fs, P as TaskNames, ct as RecordStatsMap, et as WrappedFs, ft as Binary, h as RequestParam, lt as Stat, m as Request, nt as FileStat, rt as FolderStat, st as RecordStat, ut as StatsMap, v as Decider } from "./index-g3mTMY1u.spec.js"; //#region src/sdk/debug-wrapper.d.ts declare function debugWrapper(original: Fs, log: (content: string) => void): WrappedFs; //#endregion diff --git a/packages/plugin/dist/index-LOl_cerr.spec.d.ts b/packages/plugin/dist/index-g3mTMY1u.spec.d.ts similarity index 95% rename from packages/plugin/dist/index-LOl_cerr.spec.d.ts rename to packages/plugin/dist/index-g3mTMY1u.spec.d.ts index a8064016..f8dc8ee8 100644 --- a/packages/plugin/dist/index-LOl_cerr.spec.d.ts +++ b/packages/plugin/dist/index-g3mTMY1u.spec.d.ts @@ -1,7 +1,10 @@ import { App, ListedFiles, RequestUrlParam, Stat } from "obsidian"; -//#region test/e2e-utils.d.ts +//#region ../shared/src/e2e-utils.spec.d.ts type General = any; //#endregion +//#region ../shared/src/binary.d.ts +type Binary = Uint8Array; +//#endregion //#region src/types.d.ts type MaybePromise = Promise | T; type TogglableValue = { @@ -38,7 +41,6 @@ type Progress = { completed: number; current?: T; }; -type Binary = Uint8Array; //#endregion //#region src/fs/interface.d.ts /** @@ -459,7 +461,7 @@ declare class Registrar { root: { conflictResolverRegistry: Map; createLocalFs: () => Fs; - createRemoteFs: (remoteFs?: string) => RootFs; + createRemoteFs: (remoteFs?: string) => Fs; deciderRegistry: Map; getCheckConnection: (remoteFs?: string) => () => MaybePromise; getConflictResolver: () => ConflictResolver; @@ -555,4 +557,4 @@ type VaultRequestResponseMap = { }; type VaultRequest = (params: T) => Promise; //#endregion -export { RootFs as $, AddRecord as A, StoreOperations as B, RemoveRemote as C, MoveLocal as D, MoveRemote as E, RecordStore as F, Fs as G, BatchOptimizer as H, Storage as I, MkdirAtom as J, InputAtom as K, DatabaseAsync as L, ConflictResolver as M, ConflictResolverPayload as N, Download as O, TaskNames as P, OutputAtom as Q, DatabaseSync as R, ResolveConflict as S, RemoveLocal as T, CustomAtom as U, StoreSync as V, DeleteAtom as W, OptimizerInput as X, MoveAtom as Y, OptimizerOutput as Z, CreateLocalDir as _, FsWrapperEntry as a, GlobMatchRule as at, TaskFactory as b, OptimizerEntry as c, RecordStat as ct, RemoteLister as d, StatsMap as dt, WrappedFs as et, RemoteListerEntry as f, TogglableValue as ft, RequestResponse as g, RequestParam as h, DeciderEntry as i, FolderStat as it, BaseTask as j, CreateRemoteDir as k, Registrar as l, RecordStatsMap as lt, Request as m, CheckConnectionResult as n, Binary as nt, Infras as o, MaybePromise as ot, RemoteRequestMiddlewareEntry as p, General as pt, ListReporter as q, ConflictResolverEntry as r, FileStat as rt, LocalRequestMiddlewareEntry as s, Progress as st, VaultRequest as t, WriteAtom as tt, RemoteFsEntry as u, Stat$1 as ut, Decider as v, RemoveRecord as w, Upload as x, DeciderInput as y, StoreAsync as z }; \ No newline at end of file +export { RootFs as $, AddRecord as A, StoreOperations as B, RemoveRemote as C, MoveLocal as D, MoveRemote as E, RecordStore as F, Fs as G, BatchOptimizer as H, Storage as I, MkdirAtom as J, InputAtom as K, DatabaseAsync as L, ConflictResolver as M, ConflictResolverPayload as N, Download as O, TaskNames as P, OutputAtom as Q, DatabaseSync as R, ResolveConflict as S, RemoveLocal as T, CustomAtom as U, StoreSync as V, DeleteAtom as W, OptimizerInput as X, MoveAtom as Y, OptimizerOutput as Z, CreateLocalDir as _, FsWrapperEntry as a, MaybePromise as at, TaskFactory as b, OptimizerEntry as c, RecordStatsMap as ct, RemoteLister as d, TogglableValue as dt, WrappedFs as et, RemoteListerEntry as f, Binary as ft, RequestResponse as g, RequestParam as h, DeciderEntry as i, GlobMatchRule as it, BaseTask as j, CreateRemoteDir as k, Registrar as l, Stat$1 as lt, Request as m, CheckConnectionResult as n, FileStat as nt, Infras as o, Progress as ot, RemoteRequestMiddlewareEntry as p, General as pt, ListReporter as q, ConflictResolverEntry as r, FolderStat as rt, LocalRequestMiddlewareEntry as s, RecordStat as st, VaultRequest as t, WriteAtom as tt, RemoteFsEntry as u, StatsMap as ut, Decider as v, RemoveRecord as w, Upload as x, DeciderInput as y, StoreAsync as z }; \ No newline at end of file diff --git a/packages/plugin/dist/index.spec.d.ts b/packages/plugin/dist/index.spec.d.ts index 5a60195c..3ed5a981 100644 --- a/packages/plugin/dist/index.spec.d.ts +++ b/packages/plugin/dist/index.spec.d.ts @@ -1,4 +1,4 @@ -import { $ as RootFs, A as AddRecord, B as StoreOperations, C as RemoveRemote, D as MoveLocal, E as MoveRemote, F as RecordStore, G as Fs, H as BatchOptimizer, I as Storage, J as MkdirAtom, K as InputAtom, L as DatabaseAsync, M as ConflictResolver, N as ConflictResolverPayload, O as Download, P as TaskNames, Q as OutputAtom, R as DatabaseSync, S as ResolveConflict, T as RemoveLocal, U as CustomAtom, V as StoreSync, W as DeleteAtom, X as OptimizerInput, Y as MoveAtom, Z as OptimizerOutput, _ as CreateLocalDir, a as FsWrapperEntry, at as GlobMatchRule, b as TaskFactory, c as OptimizerEntry, ct as RecordStat, d as RemoteLister, dt as StatsMap, et as WrappedFs, f as RemoteListerEntry, ft as TogglableValue, g as RequestResponse, h as RequestParam, i as DeciderEntry, it as FolderStat, j as BaseTask, k as CreateRemoteDir, l as Registrar, lt as RecordStatsMap, m as Request, n as CheckConnectionResult, nt as Binary, o as Infras, ot as MaybePromise, p as RemoteRequestMiddlewareEntry, pt as General$1, q as ListReporter, r as ConflictResolverEntry, rt as FileStat, s as LocalRequestMiddlewareEntry, st as Progress, t as VaultRequest, tt as WriteAtom, u as RemoteFsEntry, ut as Stat, v as Decider, w as RemoveRecord, x as Upload, y as DeciderInput, z as StoreAsync } from "./index-LOl_cerr.spec.js"; +import { $ as RootFs, A as AddRecord, B as StoreOperations, C as RemoveRemote, D as MoveLocal, E as MoveRemote, F as RecordStore, G as Fs, H as BatchOptimizer, I as Storage, J as MkdirAtom, K as InputAtom, L as DatabaseAsync, M as ConflictResolver, N as ConflictResolverPayload, O as Download, P as TaskNames, Q as OutputAtom, R as DatabaseSync, S as ResolveConflict, T as RemoveLocal, U as CustomAtom, V as StoreSync, W as DeleteAtom, X as OptimizerInput, Y as MoveAtom, Z as OptimizerOutput, _ as CreateLocalDir, a as FsWrapperEntry, at as MaybePromise, b as TaskFactory, c as OptimizerEntry, ct as RecordStatsMap, d as RemoteLister, dt as TogglableValue, et as WrappedFs, f as RemoteListerEntry, ft as Binary, g as RequestResponse, h as RequestParam, i as DeciderEntry, it as GlobMatchRule, j as BaseTask, k as CreateRemoteDir, l as Registrar, lt as Stat, m as Request, n as CheckConnectionResult, nt as FileStat, o as Infras, ot as Progress, p as RemoteRequestMiddlewareEntry, pt as General$1, q as ListReporter, r as ConflictResolverEntry, rt as FolderStat, s as LocalRequestMiddlewareEntry, st as RecordStat, t as VaultRequest, tt as WriteAtom, u as RemoteFsEntry, ut as StatsMap, v as Decider, w as RemoveRecord, x as Upload, y as DeciderInput, z as StoreAsync } from "./index-g3mTMY1u.spec.js"; import { App, Command, EventRef, IconName, Modal, Plugin, Setting, SettingDefinition, SettingDefinitionGroup, SettingDefinitionItem, SettingDefinitionList, SettingDefinitionPage, ToggleComponent } from "obsidian"; //#region ../../node_modules/.bun/synthkernel@.+synthkernel.tgz/node_modules/synthkernel/dist/context.d.ts //#region src/context.d.ts @@ -152,14 +152,14 @@ declare class Sync { private readonly convertDeleteToUpload; root: { executeSync: (trigger: string) => Promise<{ - result: 'cancelled'; + result: "cancelled"; } | { - result: 'completed'; + result: "completed"; } | { - result: 'failed'; + result: "failed"; error: string; } | { - result: 'noop'; + result: "noop"; }>; }; } diff --git a/packages/plugin/src/fs/vault/request.ts b/packages/plugin/src/fs/vault/request.ts index 0eb2879f..bfa26bf5 100644 --- a/packages/plugin/src/fs/vault/request.ts +++ b/packages/plugin/src/fs/vault/request.ts @@ -1,5 +1,6 @@ import type { Vault, Stat, ListedFiles, App } from 'obsidian'; import { toArrayBuffer, toUint8Array } from '@repo/shared/binary'; +import { requestNative } from '@repo/shared/e2e-utils.spec'; import { basename, isFolder, stripEndSlash } from '@repo/shared/path'; import { Platform, TFile, TFolder } from 'obsidian'; import type { Binary, MaybePromise } from '@/types'; @@ -68,7 +69,7 @@ export default function createVaultRequest(app: App): VaultRequest { if (method === 'GET') return adapter.readBinary(path).then((buffer) => toUint8Array(buffer)) as never; if (method === 'GET_STREAM') { - const response = await fetch(adapter.getResourcePath(path)); + const response = await requestNative(adapter.getResourcePath(path)); if (!response.body) throw new Error('Streaming vault file is not supported!'); return response.body as never; } diff --git a/packages/plugin/src/global.css b/packages/plugin/src/global.css index b4b94d5c..445755b3 100644 --- a/packages/plugin/src/global.css +++ b/packages/plugin/src/global.css @@ -91,11 +91,8 @@ input[type='text'].sync-engine-invalid-input { .setting-item-info { display: none; } - .setting-item-control { - width: 100% !important; - input[type='text'] { - flex: 1; - } + .setting-item-control input[type='text'] { + flex: 1; } } diff --git a/packages/plugin/src/modules/Extensibility.ts b/packages/plugin/src/modules/Extensibility.ts index fb0d64da..2e38201a 100644 --- a/packages/plugin/src/modules/Extensibility.ts +++ b/packages/plugin/src/modules/Extensibility.ts @@ -1,13 +1,14 @@ import type { Context, Events, Translations } from '@'; -import type { App } from 'obsidian'; +import type { App, DataAdapter } from 'obsidian'; import type { Ref } from 'synthkernel'; import type { DatabaseAsync, StoreAsync, StoreOperations } from 'uni-kv'; -import loadModule from '$/e2e-utils'; import hash from '@repo/shared/crypto'; +import { importCode } from '@repo/shared/e2e-utils.spec'; import obsidian, { Notice, requestUrl } from 'obsidian'; import { compare } from 'verkit'; import type { General } from '@/types'; import UnknownModuleModal from '@/components/UnknownModuleModal'; +import sha256 from '@/utils/sha-256'; import toErrorMessage from '@/utils/to-error-message'; import untilTrue from '@/utils/until-true'; import type { Dispatch } from './EventBus'; @@ -22,6 +23,7 @@ export type ModuleInstance = { start?: () => void; }; export type ModuleCtor = new (ctx: object) => ModuleInstance; +type GeneralCtor = new (...args: ReadonlyArray) => General; export type ModuleMeta = { id: string; @@ -382,3 +384,18 @@ function isValidMeta(meta: unknown): meta is ModuleMeta { /^[0-9a-f]*$/u.test(meta.integrity) ); } + +export async function loadModule( + options: + | { path: string; integrity: string; adapter: DataAdapter } + | { module: string; integrity: string; adapter: DataAdapter }, +) { + const { adapter, integrity } = options; + const file = 'module' in options ? options.module : await adapter.read(options.path); + if (integrity && (await sha256(file)) !== integrity) + throw new Error('Module has been maliciously modified!'); + const module = await importCode<{ default?: GeneralCtor }>(file); + const ctor = module.default; + if (typeof ctor !== 'function') throw new Error(`Invalid module!`); + return ctor; +} diff --git a/packages/plugin/src/settings/development.ts b/packages/plugin/src/settings/development.ts index 6034fe7c..4960778e 100644 --- a/packages/plugin/src/settings/development.ts +++ b/packages/plugin/src/settings/development.ts @@ -5,7 +5,7 @@ import { normalizeBaseDir, normalizeUrl } from '@repo/shared/path'; import { Notice } from 'obsidian'; import type { Translate } from '@/modules/I18n'; import type { CallableOrObjectTree } from '@/modules/Setting'; -import type { MaybePromise } from '@/types'; +import type { General, MaybePromise } from '@/types'; import { generateEditableList, reactivelyValidate, s } from './utils'; export type DevelopmentSettingTranslations = { @@ -41,7 +41,7 @@ export default function developmentSettings({ exportLogs: () => Promise; settings: Settings; saveSettings: () => Promise; - memoryDB: DatabaseSync; + memoryDB: DatabaseSync; rerenderSettingTab: () => void; }): CallableOrObjectTree { return { diff --git a/packages/plugin/src/settings/filter.ts b/packages/plugin/src/settings/filter.ts index 28ad2397..fcdfd428 100644 --- a/packages/plugin/src/settings/filter.ts +++ b/packages/plugin/src/settings/filter.ts @@ -3,7 +3,7 @@ import type { SettingGroupItem } from 'obsidian'; import type { DatabaseSync } from 'uni-kv'; import type { Fragment, Translate } from '@/modules/I18n'; import type { CallableOrObjectTree } from '@/modules/Setting'; -import type { GlobMatchRule } from '@/types'; +import type { General, GlobMatchRule } from '@/types'; import { normalizeGlob } from '@/utils/glob-match'; import type { LabelDefinition } from './utils'; import { generateEditableList, reactivelyValidate, s } from './utils'; @@ -33,7 +33,7 @@ export default function filterSettings({ translate: Translate; saveSettings: () => Promise; settings: Settings; - memoryDB: DatabaseSync; + memoryDB: DatabaseSync; rerenderSettingTab: () => void; speedLabel: () => LabelDefinition; }): CallableOrObjectTree { diff --git a/packages/plugin/src/settings/miscellaneous.ts b/packages/plugin/src/settings/miscellaneous.ts index 6b0c2f90..924c00d5 100644 --- a/packages/plugin/src/settings/miscellaneous.ts +++ b/packages/plugin/src/settings/miscellaneous.ts @@ -4,6 +4,7 @@ import type { DatabaseSync } from 'uni-kv'; import { SecretComponent } from 'obsidian'; import type { Translate } from '@/modules/I18n'; import type { CallableOrObjectTree } from '@/modules/Setting'; +import type { General } from '@/types'; import { generateEditableList, reactivelyValidate, s } from './utils'; export type MiscellaneousSettingTranslations = { @@ -40,7 +41,7 @@ export default function miscellaneousSettings({ translate: Translate; saveSettings: () => Promise; settings: Settings; - memoryDB: DatabaseSync; + memoryDB: DatabaseSync; rerenderSettingTab: () => void; app: App; }): CallableOrObjectTree { diff --git a/packages/plugin/src/settings/utils.ts b/packages/plugin/src/settings/utils.ts index d3190b98..2cec8005 100644 --- a/packages/plugin/src/settings/utils.ts +++ b/packages/plugin/src/settings/utils.ts @@ -205,6 +205,7 @@ export function generateEditableList({ name: '', render: (setting) => { setting.settingEl.addClass('sync-engine-editable-list'); + setting.settingEl.querySelector('.setting-item-control')?.addClass('w-100%!'); return render(setting, item, saveEdit); }, searchable: false, diff --git a/packages/plugin/src/types.ts b/packages/plugin/src/types.ts index c7c5c53c..cd204be4 100644 --- a/packages/plugin/src/types.ts +++ b/packages/plugin/src/types.ts @@ -1,4 +1,5 @@ -export type { General } from '$/e2e-utils'; +export type { General } from '@repo/shared/e2e-utils.spec'; +export type { Binary } from '@repo/shared/binary'; export type MaybePromise = Promise | T; export type TogglableValue = { enabled: boolean; value: T }; @@ -30,5 +31,3 @@ export type Progress = { completed: number; current?: T; }; - -export type Binary = Uint8Array; diff --git a/packages/plugin/test/e2e-utils.ts b/packages/plugin/test/e2e-utils.ts deleted file mode 100644 index 558f7fa4..00000000 --- a/packages/plugin/test/e2e-utils.ts +++ /dev/null @@ -1,22 +0,0 @@ -import type { DataAdapter } from 'obsidian'; -import sha256 from '@/utils/sha-256'; - -// oxlint-disable-next-line typescript/no-explicit-any -export type General = any; -type GeneralCtor = new (...args: ReadonlyArray) => General; - -export default async function loadModule( - options: - | { path: string; integrity: string; adapter: DataAdapter } - | { module: string; integrity: string; adapter: DataAdapter }, -) { - const { adapter, integrity } = options; - const file = 'module' in options ? options.module : await adapter.read(options.path); - if (integrity && (await sha256(file)) !== integrity) - throw new Error('Module has been maliciously modified!'); - const blob = new Blob([file], { type: 'application/javascript' }); - const module = (await import(URL.createObjectURL(blob))) as { default?: GeneralCtor }; - const ctor = module.default; - if (typeof ctor !== 'function') throw new Error(`Invalid module!`); - return ctor; -} diff --git a/packages/plugin/test/mocks.ts b/packages/plugin/test/mocks.ts index bf6976b0..9fbd27fb 100644 --- a/packages/plugin/test/mocks.ts +++ b/packages/plugin/test/mocks.ts @@ -1,5 +1,4 @@ -// oxlint-disable-next-line import/no-namespace -import * as ObsidianMock from '@repo/shared/mocks'; +import ObsidianMock from '@repo/shared/obsidian-mock'; import { mock } from 'bun:test'; Object.assign(globalThis, { window: globalThis }); diff --git a/packages/plugin/tsdown.config.ts b/packages/plugin/tsdown.config.ts index 067f7a03..eda0814d 100644 --- a/packages/plugin/tsdown.config.ts +++ b/packages/plugin/tsdown.config.ts @@ -46,7 +46,7 @@ const pluginConfig = defineConfig({ const sdkConfig = defineConfig({ ...sharedConfig, clean: dtsPass, - dts: dtsPass, + dts: dtsPass ? { eager: true } : false, entry: { dev: 'src/sdk/dev.ts', index: 'src/sdk/index.ts' }, unbundle: !dtsPass, }); diff --git a/packages/s3/test/mocks.ts b/packages/s3/test/mocks.ts index d612befb..9a27a793 100644 --- a/packages/s3/test/mocks.ts +++ b/packages/s3/test/mocks.ts @@ -1,5 +1,4 @@ -// oxlint-disable-next-line import/no-namespace -import * as ObsidianMock from '@repo/shared/mocks'; +import ObsidianMock from '@repo/shared/obsidian-mock'; import { mock } from 'bun:test'; void mock.module('obsidian', () => ObsidianMock); diff --git a/packages/shared/package.json b/packages/shared/package.json index df73d2ef..ea332275 100644 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -13,6 +13,6 @@ "scripts": { "fix": "oxlint --silent --fix && oxfmt", "check": "tsc && oxlint && oxfmt --check", - "tests": "bun test" + "tests": "bun test ./test" } } diff --git a/packages/shared/src/binary.ts b/packages/shared/src/binary.ts index 7188a096..b78d984a 100644 --- a/packages/shared/src/binary.ts +++ b/packages/shared/src/binary.ts @@ -1,7 +1,7 @@ const encoder = new TextEncoder(); const decoder = new TextDecoder(); -type Binary = Uint8Array; +export type Binary = Uint8Array; export function toUint8Array(buffer: ArrayBuffer) { return new Uint8Array(buffer); diff --git a/packages/shared/src/e2e-utils.spec.ts b/packages/shared/src/e2e-utils.spec.ts new file mode 100644 index 00000000..7fadc9bb --- /dev/null +++ b/packages/shared/src/e2e-utils.spec.ts @@ -0,0 +1,11 @@ +// oxlint-disable-next-line typescript/no-explicit-any +export type General = any; + +// oxlint-disable-next-line typescript/no-unnecessary-type-parameters +export function importCode(code: string): Promise { + return import( + URL.createObjectURL(new Blob([code], { type: 'application/javascript' })) + ) as Promise; +} + +export const requestNative: typeof fetch = fetch; diff --git a/packages/shared/src/mocks.ts b/packages/shared/src/obsidian-mock.spec.ts similarity index 82% rename from packages/shared/src/mocks.ts rename to packages/shared/src/obsidian-mock.spec.ts index bf7b34cd..01584b83 100644 --- a/packages/shared/src/mocks.ts +++ b/packages/shared/src/obsidian-mock.spec.ts @@ -1,9 +1,4 @@ -// oxlint-disable no-useless-constructor -// oxlint-disable typescript/no-extraneous-class -// oxlint-disable eslint/no-empty-function -// oxlint-disable eslint/require-await -// oxlint-disable typescript/require-await - +// oxlint-disable typescript/require-await typescript/no-extraneous-class no-useless-constructor no-empty-function export async function requestUrl() { return { headers: {}, diff --git a/packages/shared/src/obsidian-mock.ts b/packages/shared/src/obsidian-mock.ts new file mode 100644 index 00000000..a08dbebb --- /dev/null +++ b/packages/shared/src/obsidian-mock.ts @@ -0,0 +1,4 @@ +// oxlint-disable-next-line import/no-namespace +import * as ObsidianMock from './obsidian-mock.spec'; + +export default ObsidianMock; diff --git a/packages/smart-merge/test/mocks.ts b/packages/smart-merge/test/mocks.ts index d612befb..9a27a793 100644 --- a/packages/smart-merge/test/mocks.ts +++ b/packages/smart-merge/test/mocks.ts @@ -1,5 +1,4 @@ -// oxlint-disable-next-line import/no-namespace -import * as ObsidianMock from '@repo/shared/mocks'; +import ObsidianMock from '@repo/shared/obsidian-mock'; import { mock } from 'bun:test'; void mock.module('obsidian', () => ObsidianMock); diff --git a/packages/webdav/test/mocks.ts b/packages/webdav/test/mocks.ts index d612befb..89ff2a4d 100644 --- a/packages/webdav/test/mocks.ts +++ b/packages/webdav/test/mocks.ts @@ -1,5 +1,5 @@ // oxlint-disable-next-line import/no-namespace -import * as ObsidianMock from '@repo/shared/mocks'; +import ObsidianMock from '@repo/shared/obsidian-mock'; import { mock } from 'bun:test'; void mock.module('obsidian', () => ObsidianMock);