diff --git a/.eslintrc.js b/.eslintrc.js index 31e36813f7..5e5566f176 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -126,6 +126,8 @@ const config = { 'no-cond-assign': 'off', 'no-constant-binary-expression': 'error', 'no-constructor-return': 'error', + 'no-self-compare': 'error', + eqeqeq: ['error', 'always', { null: 'ignore' }], 'react/prop-types': 'off', 'react/jsx-no-useless-fragment': 'error', '@typescript-eslint/consistent-type-imports': 'error', @@ -133,6 +135,9 @@ const config = { '@typescript-eslint/no-explicit-any': 'off', '@typescript-eslint/no-empty-function': 'off', '@typescript-eslint/await-thenable': 'error', + '@typescript-eslint/require-array-sort-compare': 'error', + '@typescript-eslint/unified-signatures': 'error', + '@typescript-eslint/prefer-for-of': 'error', '@typescript-eslint/no-unused-vars': [ 'error', { @@ -204,6 +209,17 @@ const config = { }, ], 'unicorn/no-unnecessary-await': 'error', + 'unicorn/no-useless-fallback-in-spread': 'error', + 'unicorn/prefer-dom-node-dataset': 'error', + 'unicorn/prefer-dom-node-append': 'error', + 'unicorn/prefer-dom-node-remove': 'error', + 'unicorn/prefer-array-some': 'error', + 'unicorn/prefer-date-now': 'error', + 'unicorn/prefer-blob-reading-methods': 'error', + 'unicorn/no-typeof-undefined': 'error', + 'unicorn/no-useless-promise-resolve-reject': 'error', + 'unicorn/no-new-array': 'error', + 'unicorn/new-for-builtins': 'error', 'sonarjs/no-all-duplicated-branches': 'error', 'sonarjs/no-element-overwrite': 'error', 'sonarjs/no-empty-collection': 'error', @@ -254,6 +270,7 @@ const config = { }, ], '@typescript-eslint/no-misused-promises': ['error'], + '@typescript-eslint/prefer-readonly': 'error', 'i/no-extraneous-dependencies': ['error'], 'react-hooks/exhaustive-deps': [ 'warn', diff --git a/.github/actions/build-rust/action.yml b/.github/actions/build-rust/action.yml index 3ef2b87907..f187641f85 100644 --- a/.github/actions/build-rust/action.yml +++ b/.github/actions/build-rust/action.yml @@ -14,10 +14,17 @@ inputs: runs: using: 'composite' steps: + - name: Print rustup toolchain version + shell: bash + id: rustup-version + run: | + export RUST_TOOLCHAIN_VERSION="$(grep 'channel' rust-toolchain.toml | head -1 | awk -F '"' '{print $2}')" + echo "Rust toolchain version: $RUST_TOOLCHAIN_VERSION" + echo "RUST_TOOLCHAIN_VERSION=$RUST_TOOLCHAIN_VERSION" >> "$GITHUB_OUTPUT" - name: Setup Rust uses: dtolnay/rust-toolchain@stable with: - toolchain: stable + toolchain: '${{ steps.rustup-version.outputs.RUST_TOOLCHAIN_VERSION }}' targets: ${{ inputs.target }} env: CARGO_INCREMENTAL: '1' @@ -49,6 +56,8 @@ runs: run: | export CC=x86_64-unknown-linux-gnu-gcc export CC_x86_64_unknown_linux_gnu=x86_64-unknown-linux-gnu-gcc + rm -rf /usr/local/rustup/downloads/* + rustup target add x86_64-unknown-linux-gnu export RUSTFLAGS="-C debuginfo=1" yarn workspace ${{ inputs.package }} nx build ${{ inputs.package }} --target ${{ inputs.target }} if [ -d "node_modules/.cache" ]; then @@ -66,6 +75,8 @@ runs: options: --user 0:0 -v ${{ github.workspace }}/.cargo-cache/git/db:/usr/local/cargo/git/db -v ${{ github.workspace }}/.cargo/registry/cache:/usr/local/cargo/registry/cache -v ${{ github.workspace }}/.cargo/registry/index:/usr/local/cargo/registry/index -v ${{ github.workspace }}:/build -w /build -e NX_CLOUD_ACCESS_TOKEN=${{ inputs.nx_token }} run: | export RUSTFLAGS="-C debuginfo=1" + rm -rf /usr/local/rustup/downloads/* + rustup target add aarch64-unknown-linux-gnu yarn workspace ${{ inputs.package }} nx build ${{ inputs.package }} --target ${{ inputs.target }} if [ -d "node_modules/.cache" ]; then chmod -R 777 node_modules/.cache diff --git a/.github/actions/setup-node/action.yml b/.github/actions/setup-node/action.yml index f00e6b47ef..c9dbcdba32 100644 --- a/.github/actions/setup-node/action.yml +++ b/.github/actions/setup-node/action.yml @@ -41,7 +41,7 @@ runs: using: 'composite' steps: - name: Setup Node.js - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version-file: '.nvmrc' registry-url: https://npm.pkg.github.com diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9bd2266a4f..ee01a2e891 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -44,7 +44,7 @@ jobs: - uses: actions/checkout@v4 - name: Run oxlint # oxlint is fast, so wrong code will fail quickly - run: yarn dlx oxlint@latest . + run: yarn dlx $(node -e "console.log(require('./package.json').scripts['lint:ox'])") - name: Setup Node.js uses: ./.github/actions/setup-node with: @@ -58,8 +58,6 @@ jobs: run: | git checkout .yarnrc.yml yarn lint:prettier - - name: Run circular - run: yarn circular - name: Run Type Check run: yarn typecheck diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index d2f47e61d3..31ec20581b 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -9,7 +9,6 @@ on: default: canary env: - BUILD_TYPE: canary APP_NAME: affine NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }} @@ -17,7 +16,6 @@ jobs: build-server: name: Build Server runs-on: ubuntu-latest - environment: ${{ github.event.inputs.flavor }} steps: - uses: actions/checkout@v4 - name: Setup Node.js @@ -43,12 +41,12 @@ jobs: - name: Build Plugins run: yarn run build:plugins - name: Build Core - run: yarn nx build @affine/core + run: yarn nx build @affine/core --skip-nx-cache env: R2_ACCOUNT_ID: ${{ secrets.R2_ACCOUNT_ID }} R2_ACCESS_KEY_ID: ${{ secrets.R2_ACCESS_KEY_ID }} R2_SECRET_ACCESS_KEY: ${{ secrets.R2_SECRET_ACCESS_KEY }} - BUILD_TYPE_OVERRIDE: ${{ github.event.inputs.flavor }} + BUILD_TYPE: ${{ github.event.inputs.flavor }} SHOULD_REPORT_TRACE: true TRACE_REPORT_ENDPOINT: ${{ secrets.TRACE_REPORT_ENDPOINT }} CAPTCHA_SITE_KEY: ${{ secrets.CAPTCHA_SITE_KEY }} @@ -209,7 +207,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - name: Deploy to dev + - name: Deploy to ${{ github.event.inputs.flavor }} uses: ./.github/actions/deploy with: build-type: ${{ github.event.inputs.flavor }} diff --git a/.github/workflows/nightly-build.yml b/.github/workflows/nightly-build.yml index 374b1ae369..a8930db8c0 100644 --- a/.github/workflows/nightly-build.yml +++ b/.github/workflows/nightly-build.yml @@ -65,14 +65,15 @@ jobs: - name: Replace Version run: ./scripts/set-version.sh ${{ needs.set-build-version.outputs.version }} - name: generate-assets - working-directory: packages/frontend/electron - run: yarn generate-assets + run: yarn workspace @affine/electron generate-assets env: SENTRY_ORG: ${{ secrets.SENTRY_ORG }} SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }} SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} SENTRY_DSN: ${{ secrets.SENTRY_DSN }} RELEASE_VERSION: ${{ needs.set-build-version.outputs.version }} + SKIP_PLUGIN_BUILD: 'true' + SKIP_NX_CACHE: 'true' - name: Upload core artifact uses: actions/upload-artifact@v3 diff --git a/.github/workflows/pr-title-lint.yml b/.github/workflows/pr-title-lint.yml index 75ecb019bf..5cab44c6d6 100644 --- a/.github/workflows/pr-title-lint.yml +++ b/.github/workflows/pr-title-lint.yml @@ -19,7 +19,10 @@ jobs: steps: - uses: actions/checkout@v4 - name: Setup Node.js - uses: ./.github/actions/setup-node + uses: actions/setup-node@v4 with: - electron-install: false - - run: echo "${{ github.event.pull_request.title }}" | yarn dlx commitlint -g ./.commitlintrc.json + cache: 'yarn' + node-version-file: '.nvmrc' + - name: Install dependencies + run: yarn workspaces focus @affine/commitlint-config + - run: echo "${{ github.event.pull_request.title }}" | yarn workspace @affine/commitlint-config commitlint -g ./.commitlintrc.json diff --git a/.github/workflows/release-desktop-app.yml b/.github/workflows/release-desktop-app.yml index 764f5ca772..41347783d6 100644 --- a/.github/workflows/release-desktop-app.yml +++ b/.github/workflows/release-desktop-app.yml @@ -69,6 +69,7 @@ jobs: SENTRY_DSN: ${{ secrets.SENTRY_DSN }} RELEASE_VERSION: ${{ github.event.inputs.version || steps.get-canary-version.outputs.RELEASE_VERSION }} SKIP_PLUGIN_BUILD: 'true' + SKIP_NX_CACHE: 'true' - name: Upload core artifact uses: actions/upload-artifact@v3 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index bbaac8305c..0000000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,165 +0,0 @@ -name: Release - -on: - push: - branches: - - canary - -env: - BUILD_TYPE: stable - APP_NAME: affine - COVERAGE: false - DISTRIBUTION: browser - NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }} - -jobs: - release: - name: Try publishing npm@latest release - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Setup Node.js - uses: ./.github/actions/setup-node - - name: Try publishing to NPM - run: ./scripts/publish.sh - env: - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - - build-core: - name: Build @affine/core - runs-on: ubuntu-latest - environment: development - - steps: - - uses: actions/checkout@v4 - - name: Setup Node.js - uses: ./.github/actions/setup-node - - name: Build Plugins - run: yarn run build:plugins - - name: Build Core - run: yarn nx build @affine/core - - name: Upload core artifact - uses: actions/upload-artifact@v3 - with: - name: core - path: ./packages/frontend/core/dist - if-no-files-found: error - - build-server: - name: Build Server - runs-on: ubuntu-latest - environment: development - steps: - - uses: actions/checkout@v4 - - name: Setup Node.js - uses: ./.github/actions/setup-node - with: - electron-install: false - - name: Build Server - run: yarn nx build @affine/server - - name: Upload server dist - uses: actions/upload-artifact@v3 - with: - name: server-dist - path: ./packages/backend/server/dist - if-no-files-found: error - - build-storage: - name: Build Storage - runs-on: ubuntu-latest - env: - RUSTFLAGS: '-C debuginfo=1' - environment: development - - steps: - - uses: actions/checkout@v4 - - name: Setup Node.js - uses: ./.github/actions/setup-node - - name: Setup Rust - uses: ./.github/actions/build-rust - with: - target: 'x86_64-unknown-linux-gnu' - package: '@affine/storage' - nx_token: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }} - - name: Upload storage.node - uses: actions/upload-artifact@v3 - with: - name: storage.node - path: ./packages/backend/storage/storage.node - if-no-files-found: error - - build-docker: - if: github.ref == 'refs/heads/canary' - name: Build Docker - runs-on: ubuntu-latest - needs: - - build-server - - build-core - - build-storage - steps: - - uses: actions/checkout@v4 - - name: Download core artifact - uses: actions/download-artifact@v3 - with: - name: core - path: ./packages/frontend/core/dist - - name: Download server dist - uses: actions/download-artifact@v3 - with: - name: server-dist - path: ./packages/backend/server/dist - - name: Download storage.node - uses: actions/download-artifact@v3 - with: - name: storage.node - path: ./packages/backend/server - - name: Setup Git short hash - run: | - echo "GIT_SHORT_HASH=$(git rev-parse --short HEAD)" >> "$GITHUB_ENV" - - name: Login to GitHub Container Registry - uses: docker/login-action@v3 - with: - registry: ghcr.io - logout: false - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - name: Build front Dockerfile - uses: docker/build-push-action@v5 - with: - context: . - push: true - pull: true - platforms: linux/amd64,linux/arm64 - provenance: true - file: .github/deployment/front/Dockerfile - tags: ghcr.io/toeverything/affine-front:${{ env.GIT_SHORT_HASH }},ghcr.io/toeverything/affine-front:latest - - # setup node without cache configuration - # Prisma cache is not compatible with docker build cache - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version-file: '.nvmrc' - registry-url: https://npm.pkg.github.com - scope: '@toeverything' - - - name: Install Node.js dependencies - run: yarn workspaces focus @affine/server --production - - - name: Generate Prisma client - run: yarn workspace @affine/server prisma generate - - - name: Build graphql Dockerfile - uses: docker/build-push-action@v5 - with: - context: . - push: true - pull: true - platforms: linux/amd64,linux/arm64 - provenance: true - file: .github/deployment/node/Dockerfile - tags: ghcr.io/toeverything/affine-graphql:${{ env.GIT_SHORT_HASH }},ghcr.io/toeverything/affine-graphql:latest diff --git a/.husky/pre-commit b/.husky/pre-commit index 0a0d459b28..2f1bef1ce9 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,23 +1,4 @@ #!/usr/bin/env sh . "$(dirname -- "$0")/_/husky.sh" -# check lockfile is up to date -yarn install --mode=skip-build --inline-builds --immutable - -# build infra code -yarn -T run build:infra - -# generate prisma client type -yarn workspace @affine/server prisma generate - -# generate i18n -yarn i18n-codegen gen - -# lint staged files -yarn exec lint-staged - -# type check -yarn typecheck - -# circular dependency check -yarn circular +yarn lint-staged && yarn lint:ox && cargo fmt --all && git add . diff --git a/README.md b/README.md index 10562298e3..513a0ff604 100644 --- a/README.md +++ b/README.md @@ -228,7 +228,7 @@ See [LICENSE] for details. [jobs available]: ./docs/jobs.md [latest packages]: https://github.com/toeverything/AFFiNE/pkgs/container/affine-self-hosted [contributor license agreement]: https://github.com/toeverything/affine/edit/canary/.github/CLA.md -[rust-version-icon]: https://img.shields.io/badge/Rust-1.71.0-dea584 +[rust-version-icon]: https://img.shields.io/badge/Rust-1.74.0-dea584 [stars-icon]: https://img.shields.io/github/stars/toeverything/AFFiNE.svg?style=flat&logo=github&colorB=red&label=stars [codecov]: https://codecov.io/gh/toeverything/affine/branch/canary/graphs/badge.svg?branch=canary [node-version-icon]: https://img.shields.io/badge/node-%3E=18.16.1-success diff --git a/package.json b/package.json index 6464b0c7c5..e1be4c6888 100644 --- a/package.json +++ b/package.json @@ -33,12 +33,12 @@ "lint:eslint:fix": "yarn lint:eslint --fix", "lint:prettier": "prettier --ignore-unknown --cache --check .", "lint:prettier:fix": "prettier --ignore-unknown --cache --write .", + "lint:ox": "oxlint --deny-warnings --import-plugin -D correctness -D nursery -D prefer-array-some -D no-useless-promise-resolve-reject -A no-undef -A consistent-type-exports -A default -A named -A ban-ts-comment", "lint": "yarn lint:eslint && yarn lint:prettier", "lint:fix": "yarn lint:eslint:fix && yarn lint:prettier:fix", "test": "vitest --run", "test:ui": "vitest --ui", "test:coverage": "vitest run --coverage", - "circular": "madge --circular --ts-config ./tsconfig.json ./packages/frontend/core/src/pages/**/*.tsx ./packages/frontend/core/src/index.tsx ./packages/frontend/electron/src/*/index.ts", "typecheck": "tsc -b tsconfig.json --diagnostics", "postinstall": "node ./scripts/check-version.mjs && yarn i18n-codegen gen && yarn husky install" }, @@ -49,7 +49,6 @@ "eslint --cache --fix" ], "*.toml": [ - "prettier --ignore-unknown --write", "taplo format" ] }, @@ -71,8 +70,8 @@ "@types/affine__env": "workspace:*", "@types/eslint": "^8.44.7", "@types/node": "^20.9.3", - "@typescript-eslint/eslint-plugin": "^6.12.0", - "@typescript-eslint/parser": "^6.12.0", + "@typescript-eslint/eslint-plugin": "^6.13.1", + "@typescript-eslint/parser": "^6.13.1", "@vanilla-extract/vite-plugin": "^3.9.2", "@vanilla-extract/webpack-plugin": "^2.3.1", "@vitejs/plugin-react-swc": "^3.5.0", @@ -82,7 +81,6 @@ "eslint": "^8.54.0", "eslint-config-prettier": "^9.0.0", "eslint-plugin-i": "^2.29.0", - "eslint-plugin-prettier": "^5.0.1", "eslint-plugin-react": "^7.33.2", "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-simple-import-sort": "^10.0.0", @@ -94,12 +92,12 @@ "happy-dom": "^12.10.3", "husky": "^8.0.3", "lint-staged": "^15.1.0", - "madge": "^6.1.0", "msw": "^2.0.8", "nanoid": "^5.0.3", "nx": "^17.1.3", "nx-cloud": "^16.5.2", "nyc": "^15.1.0", + "oxlint": "^0.0.18", "prettier": "^3.1.0", "semver": "^7.5.4", "serve": "^14.2.1", diff --git a/packages/backend/server/package.json b/packages/backend/server/package.json index 77607feb76..406ce7596a 100644 --- a/packages/backend/server/package.json +++ b/packages/backend/server/package.json @@ -48,6 +48,7 @@ "@opentelemetry/instrumentation-ioredis": "^0.35.3", "@opentelemetry/instrumentation-nestjs-core": "^0.33.3", "@opentelemetry/instrumentation-socket.io": "^0.34.3", + "@opentelemetry/resources": "^1.18.1", "@opentelemetry/sdk-metrics": "^1.18.1", "@opentelemetry/sdk-node": "^0.45.1", "@opentelemetry/sdk-trace-node": "^1.18.1", diff --git a/packages/backend/server/src/config/def.ts b/packages/backend/server/src/config/def.ts index 895df5f08d..1b9a065d9a 100644 --- a/packages/backend/server/src/config/def.ts +++ b/packages/backend/server/src/config/def.ts @@ -50,7 +50,7 @@ function boolean(value: string) { } export function parseEnvValue(value: string | undefined, type?: EnvConfigType) { - if (typeof value === 'undefined') { + if (value === undefined) { return; } diff --git a/packages/backend/server/src/config/env.ts b/packages/backend/server/src/config/env.ts index f68dfedd5f..8d5b950fff 100644 --- a/packages/backend/server/src/config/env.ts +++ b/packages/backend/server/src/config/env.ts @@ -10,7 +10,7 @@ export function applyEnvToConfig(rawConfig: AFFiNEConfig) { ? [config, process.env[env]] : [config[0], parseEnvValue(process.env[env], config[1])]; - if (typeof value !== 'undefined') { + if (value !== undefined) { set(rawConfig, path, value); } } diff --git a/packages/backend/server/src/graphql/logger-plugin.ts b/packages/backend/server/src/graphql/logger-plugin.ts index 67c399d667..9028ce722a 100644 --- a/packages/backend/server/src/graphql/logger-plugin.ts +++ b/packages/backend/server/src/graphql/logger-plugin.ts @@ -20,7 +20,7 @@ export class GQLLoggerPlugin implements ApolloServerPlugin { const res = reqContext.contextValue.req.res as Response; const operation = reqContext.request.operationName; - metrics().gqlRequest.add(1, { operation }); + metrics.gql.counter('query_counter').add(1, { operation }); const start = Date.now(); return Promise.resolve({ @@ -30,7 +30,9 @@ export class GQLLoggerPlugin implements ApolloServerPlugin { 'Server-Timing', `gql;dur=${costInMilliseconds};desc="GraphQL"` ); - metrics().gqlTimer.record(costInMilliseconds, { operation }); + metrics.gql + .histogram('query_duration') + .record(costInMilliseconds, { operation }); return Promise.resolve(); }, didEncounterErrors: () => { @@ -39,7 +41,9 @@ export class GQLLoggerPlugin implements ApolloServerPlugin { 'Server-Timing', `gql;dur=${costInMilliseconds};desc="GraphQL ${operation}"` ); - metrics().gqlTimer.record(costInMilliseconds, { operation }); + metrics.gql + .histogram('query_duration') + .record(costInMilliseconds, { operation }); return Promise.resolve(); }, }); diff --git a/packages/backend/server/src/metrics/metrics.ts b/packages/backend/server/src/metrics/metrics.ts index b91223cf97..ccbb7be606 100644 --- a/packages/backend/server/src/metrics/metrics.ts +++ b/packages/backend/server/src/metrics/metrics.ts @@ -1,76 +1,129 @@ -import opentelemetry, { Attributes, Observable } from '@opentelemetry/api'; +import { + Attributes, + Counter, + Histogram, + Meter, + MetricOptions, +} from '@opentelemetry/api'; -interface AsyncMetric { - ob: Observable; - get value(): any; - get attrs(): Attributes | undefined; -} +import { getMeter } from './opentelemetry'; -let _metrics: ReturnType | undefined = undefined; +type MetricType = 'counter' | 'gauge' | 'histogram'; +type Metric = T extends 'counter' + ? Counter + : T extends 'gauge' + ? Histogram + : T extends 'histogram' + ? Histogram + : never; -export function getMeter(name = 'business') { - return opentelemetry.metrics.getMeter(name); -} +export type ScopedMetrics = { + [T in MetricType]: (name: string, opts?: MetricOptions) => Metric; +}; +type MetricCreators = { + [T in MetricType]: ( + meter: Meter, + name: string, + opts?: MetricOptions + ) => Metric; +}; -function createBusinessMetrics() { - const meter = getMeter(); - const asyncMetrics: AsyncMetric[] = []; +export type KnownMetricScopes = + | 'socketio' + | 'gql' + | 'jwst' + | 'auth' + | 'controllers' + | 'doc'; - function createGauge(name: string) { +const metricCreators: MetricCreators = { + counter(meter: Meter, name: string, opts?: MetricOptions) { + return meter.createCounter(name, opts); + }, + gauge(meter: Meter, name: string, opts?: MetricOptions) { let value: any; let attrs: Attributes | undefined; - const ob = meter.createObservableGauge(name); - asyncMetrics.push({ - ob, - get value() { - return value; - }, - get attrs() { - return attrs; - }, + const ob = meter.createObservableGauge(name, opts); + + ob.addCallback(result => { + result.observe(value, attrs); }); - return (newValue: any, newAttrs?: Attributes) => { - value = newValue; - attrs = newAttrs; - }; + return { + record: (newValue, newAttrs) => { + value = newValue; + attrs = newAttrs; + }, + } satisfies Histogram; + }, + histogram(meter: Meter, name: string, opts?: MetricOptions) { + return meter.createHistogram(name, opts); + }, +}; + +const scopes = new Map(); + +function make(scope: string) { + const meter = getMeter(); + const metrics = new Map(); + const prefix = scope + '/'; + + function getOrCreate( + type: T, + name: string, + opts?: MetricOptions + ): Metric { + name = prefix + name; + const metric = metrics.get(name); + if (metric) { + if (type !== metric.type) { + throw new Error( + `Metric ${name} has already been registered as ${metric.type} mode, but get as ${type} again.` + ); + } + + return metric.metric; + } else { + const metric = metricCreators[type](meter, name, opts); + metrics.set(name, { type, metric }); + return metric; + } } - const metrics = { - socketIOConnectionGauge: createGauge('socket_io_connection'), - - gqlRequest: meter.createCounter('gql_request'), - gqlError: meter.createCounter('gql_error'), - gqlTimer: meter.createHistogram('gql_timer'), - - jwstCodecMerge: meter.createCounter('jwst_codec_merge'), - jwstCodecDidnotMatch: meter.createCounter('jwst_codec_didnot_match'), - jwstCodecFail: meter.createCounter('jwst_codec_fail'), - - authCounter: meter.createCounter('auth'), - authFailCounter: meter.createCounter('auth_fail'), - - docHistoryCounter: meter.createCounter('doc_history_created'), - docRecoverCounter: meter.createCounter('doc_history_recovered'), - }; - - meter.addBatchObservableCallback( - result => { - asyncMetrics.forEach(metric => { - result.observe(metric.ob, metric.value, metric.attrs); - }); + return { + counter(name, opts) { + return getOrCreate('counter', name, opts); }, - asyncMetrics.map(({ ob }) => ob) - ); - - return metrics; + gauge(name, opts) { + return getOrCreate('gauge', name, opts); + }, + histogram(name, opts) { + return getOrCreate('histogram', name, opts); + }, + } satisfies ScopedMetrics; } -export function registerBusinessMetrics() { - if (!_metrics) { - _metrics = createBusinessMetrics(); +/** + * @example + * + * ``` + * metrics.scope.counter('example_count').add(1, { + * attr1: 'example-event' + * }) + * ``` + */ +export const metrics = new Proxy>( + // @ts-expect-error proxied + {}, + { + get(_, scopeName: string) { + let scope = scopes.get(scopeName); + if (!scope) { + scope = make(scopeName); + scopes.set(scopeName, scope); + } + + return scope; + }, } - - return _metrics; -} -export const metrics = registerBusinessMetrics; +); diff --git a/packages/backend/server/src/metrics/opentelemetry.ts b/packages/backend/server/src/metrics/opentelemetry.ts index cd5fcfa358..5b084c8cfc 100644 --- a/packages/backend/server/src/metrics/opentelemetry.ts +++ b/packages/backend/server/src/metrics/opentelemetry.ts @@ -1,5 +1,6 @@ import { MetricExporter } from '@google-cloud/opentelemetry-cloud-monitoring-exporter'; import { TraceExporter } from '@google-cloud/opentelemetry-cloud-trace-exporter'; +import { metrics } from '@opentelemetry/api'; import { CompositePropagator, W3CBaggagePropagator, @@ -16,6 +17,8 @@ import { NestInstrumentation } from '@opentelemetry/instrumentation-nestjs-core' import { SocketIoInstrumentation } from '@opentelemetry/instrumentation-socket.io'; import { ConsoleMetricExporter, + type MeterProvider, + MetricProducer, MetricReader, PeriodicExportingMetricReader, } from '@opentelemetry/sdk-metrics'; @@ -24,10 +27,11 @@ import { BatchSpanProcessor, ConsoleSpanExporter, SpanExporter, + TraceIdRatioBasedSampler, } from '@opentelemetry/sdk-trace-node'; import { PrismaInstrumentation } from '@prisma/instrumentation'; -import { registerBusinessMetrics } from './metrics'; +import { PrismaMetricProducer } from './prisma'; abstract class OpentelemetryFactor { abstract getMetricReader(): MetricReader; @@ -44,9 +48,14 @@ abstract class OpentelemetryFactor { ]; } + getMetricsProducers(): MetricProducer[] { + return [new PrismaMetricProducer()]; + } + create() { const traceExporter = this.getSpanExporter(); return new NodeSDK({ + sampler: new TraceIdRatioBasedSampler(0.1), traceExporter, metricReader: this.getMetricReader(), spanProcessor: new BatchSpanProcessor(traceExporter), @@ -67,7 +76,10 @@ class GCloudOpentelemetryFactor extends OpentelemetryFactor { return new PeriodicExportingMetricReader({ exportIntervalMillis: 30000, exportTimeoutMillis: 10000, - exporter: new MetricExporter(), + exporter: new MetricExporter({ + prefix: 'custom.googleapis.com', + }), + metricProducers: this.getMetricsProducers(), }); } @@ -78,7 +90,9 @@ class GCloudOpentelemetryFactor extends OpentelemetryFactor { class LocalOpentelemetryFactor extends OpentelemetryFactor { override getMetricReader(): MetricReader { - return new PrometheusExporter(); + return new PrometheusExporter({ + metricProducers: this.getMetricsProducers(), + }); } override getSpanExporter(): SpanExporter { @@ -90,6 +104,7 @@ class DebugOpentelemetryFactor extends OpentelemetryFactor { override getMetricReader(): MetricReader { return new PeriodicExportingMetricReader({ exporter: new ConsoleMetricExporter(), + metricProducers: this.getMetricsProducers(), }); } @@ -111,9 +126,30 @@ function createSDK() { return factor?.create(); } +let OPENTELEMETRY_STARTED = false; + +function ensureStarted() { + if (!OPENTELEMETRY_STARTED) { + OPENTELEMETRY_STARTED = true; + start(); + } +} + +function getMeterProvider() { + ensureStarted(); + return metrics.getMeterProvider(); +} + function registerCustomMetrics() { - const host = new HostMetrics({ name: 'instance-host-metrics' }); - host.start(); + const hostMetricsMonitoring = new HostMetrics({ + name: 'instance-host-metrics', + meterProvider: getMeterProvider() as MeterProvider, + }); + hostMetricsMonitoring.start(); +} + +export function getMeter(name = 'business') { + return getMeterProvider().getMeter(name); } export function start() { @@ -122,6 +158,5 @@ export function start() { if (sdk) { sdk.start(); registerCustomMetrics(); - registerBusinessMetrics(); } } diff --git a/packages/backend/server/src/metrics/prisma.ts b/packages/backend/server/src/metrics/prisma.ts new file mode 100644 index 0000000000..6c024c98ff --- /dev/null +++ b/packages/backend/server/src/metrics/prisma.ts @@ -0,0 +1,132 @@ +import { HrTime, ValueType } from '@opentelemetry/api'; +import { hrTime } from '@opentelemetry/core'; +import { Resource } from '@opentelemetry/resources'; +import { + AggregationTemporality, + CollectionResult, + DataPointType, + InstrumentType, + MetricProducer, + ScopeMetrics, +} from '@opentelemetry/sdk-metrics'; + +import { PrismaService } from '../prisma'; + +function transformPrismaKey(key: string) { + // replace first '_' to '/' as a scope prefix + // example: prisma_client_query_duration_seconds_sum -> prisma/client_query_duration_seconds_sum + return key.replace(/_/, '/'); +} + +export class PrismaMetricProducer implements MetricProducer { + private readonly startTime: HrTime = hrTime(); + + async collect(): Promise { + const result: CollectionResult = { + resourceMetrics: { + resource: Resource.EMPTY, + scopeMetrics: [], + }, + errors: [], + }; + + if (!PrismaService.INSTANCE) { + return result; + } + + const prisma = PrismaService.INSTANCE; + + const endTime = hrTime(); + + const metrics = await prisma.$metrics.json(); + const scopeMetrics: ScopeMetrics = { + scope: { + name: '', + }, + metrics: [], + }; + for (const counter of metrics.counters) { + scopeMetrics.metrics.push({ + descriptor: { + name: transformPrismaKey(counter.key), + description: counter.description, + unit: '1', + type: InstrumentType.COUNTER, + valueType: ValueType.INT, + }, + dataPointType: DataPointType.SUM, + aggregationTemporality: AggregationTemporality.CUMULATIVE, + dataPoints: [ + { + startTime: this.startTime, + endTime: endTime, + value: counter.value, + attributes: counter.labels, + }, + ], + isMonotonic: true, + }); + } + + for (const gauge of metrics.gauges) { + scopeMetrics.metrics.push({ + descriptor: { + name: transformPrismaKey(gauge.key), + description: gauge.description, + unit: '1', + type: InstrumentType.UP_DOWN_COUNTER, + valueType: ValueType.INT, + }, + dataPointType: DataPointType.GAUGE, + aggregationTemporality: AggregationTemporality.CUMULATIVE, + dataPoints: [ + { + startTime: this.startTime, + endTime: endTime, + value: gauge.value, + attributes: gauge.labels, + }, + ], + }); + } + + for (const histogram of metrics.histograms) { + const boundaries = []; + const counts = []; + for (const [boundary, count] of histogram.value.buckets) { + boundaries.push(boundary); + counts.push(count); + } + scopeMetrics.metrics.push({ + descriptor: { + name: transformPrismaKey(histogram.key), + description: histogram.description, + unit: 'ms', + type: InstrumentType.HISTOGRAM, + valueType: ValueType.DOUBLE, + }, + dataPointType: DataPointType.HISTOGRAM, + aggregationTemporality: AggregationTemporality.CUMULATIVE, + dataPoints: [ + { + startTime: this.startTime, + endTime: endTime, + value: { + buckets: { + boundaries, + counts, + }, + count: histogram.value.count, + sum: histogram.value.sum, + }, + attributes: histogram.labels, + }, + ], + }); + } + + result.resourceMetrics.scopeMetrics.push(scopeMetrics); + + return result; + } +} diff --git a/packages/backend/server/src/metrics/utils.ts b/packages/backend/server/src/metrics/utils.ts index 1c4fa313ff..e4d16a3492 100644 --- a/packages/backend/server/src/metrics/utils.ts +++ b/packages/backend/server/src/metrics/utils.ts @@ -1,8 +1,9 @@ import { Attributes } from '@opentelemetry/api'; -import { getMeter } from './metrics'; +import { KnownMetricScopes, metrics } from './metrics'; export const CallTimer = ( + scope: KnownMetricScopes, name: string, attrs?: Attributes ): MethodDecorator => { @@ -18,9 +19,11 @@ export const CallTimer = ( } desc.value = function (...args: any[]) { - const timer = getMeter().createHistogram(name, { + const timer = metrics[scope].histogram(name, { description: `function call time costs of ${name}`, + unit: 'ms', }); + const start = Date.now(); const end = () => { @@ -48,6 +51,7 @@ export const CallTimer = ( }; export const CallCounter = ( + scope: KnownMetricScopes, name: string, attrs?: Attributes ): MethodDecorator => { @@ -63,7 +67,7 @@ export const CallCounter = ( } desc.value = function (...args: any[]) { - const count = getMeter().createCounter(name, { + const count = metrics[scope].counter(name, { description: `function call counter of ${name}`, }); diff --git a/packages/backend/server/src/middleware/exception-logger.ts b/packages/backend/server/src/middleware/exception-logger.ts index 89abbd02e0..4e4c97d3c8 100644 --- a/packages/backend/server/src/middleware/exception-logger.ts +++ b/packages/backend/server/src/middleware/exception-logger.ts @@ -14,7 +14,7 @@ const TrivialExceptions = [NotFoundException]; @Catch() export class ExceptionLogger implements ExceptionFilter { - private logger = new Logger('ExceptionLogger'); + private readonly logger = new Logger('ExceptionLogger'); catch(exception: Error, host: ArgumentsHost) { // with useGlobalFilters, the context is always HTTP diff --git a/packages/backend/server/src/modules/auth/guard.ts b/packages/backend/server/src/modules/auth/guard.ts index 6b4532354d..7e311159d2 100644 --- a/packages/backend/server/src/modules/auth/guard.ts +++ b/packages/backend/server/src/modules/auth/guard.ts @@ -53,8 +53,8 @@ class AuthGuard implements CanActivate { constructor( @Inject(NextAuthOptionsProvide) private readonly nextAuthOptions: NextAuthOptions, - private auth: AuthService, - private prisma: PrismaService, + private readonly auth: AuthService, + private readonly prisma: PrismaService, private readonly reflector: Reflector ) {} diff --git a/packages/backend/server/src/modules/auth/next-auth.controller.ts b/packages/backend/server/src/modules/auth/next-auth.controller.ts index f8d8faa043..9d717f0121 100644 --- a/packages/backend/server/src/modules/auth/next-auth.controller.ts +++ b/packages/backend/server/src/modules/auth/next-auth.controller.ts @@ -89,12 +89,13 @@ export class NextAuthController { res.redirect(`/signin${query}`); return; } - metrics().authCounter.add(1); const [action, providerId] = req.url // start with request url .slice(BASE_URL.length) // make relative to baseUrl .replace(/\?.*/, '') // remove query part, use only path part .split('/') as [AuthAction, string]; // as array of strings; + metrics.auth.counter('call_counter').add(1, { action, providerId }); + const credentialsSignIn = req.method === 'POST' && providerId === 'credentials'; let userId: string | undefined; @@ -126,7 +127,9 @@ export class NextAuthController { const options = this.nextAuthOptions; if (req.method === 'POST' && action === 'session') { if (typeof req.body !== 'object' || typeof req.body.data !== 'object') { - metrics().authFailCounter.add(1, { reason: 'invalid_session_data' }); + metrics.auth + .counter('call_fails_counter') + .add(1, { reason: 'invalid_session_data' }); throw new BadRequestException(`Invalid new session data`); } const user = await this.updateSession(req, req.body.data); @@ -209,9 +212,10 @@ export class NextAuthController { if (redirect?.endsWith('api/auth/error?error=AccessDenied')) { this.logger.log(`Early access redirect headers: ${req.headers}`); - metrics().authFailCounter.add(1, { - reason: 'no_early_access_permission', - }); + metrics.auth + .counter('call_fails_counter') + .add(1, { reason: 'no_early_access_permission' }); + if ( !req.headers?.referer || checkUrlOrigin(req.headers.referer, 'https://accounts.google.com') diff --git a/packages/backend/server/src/modules/auth/service.ts b/packages/backend/server/src/modules/auth/service.ts index 91652c34dc..b6150a65d1 100644 --- a/packages/backend/server/src/modules/auth/service.ts +++ b/packages/backend/server/src/modules/auth/service.ts @@ -23,14 +23,14 @@ export type UserClaim = Pick< hasPassword?: boolean; }; -export const getUtcTimestamp = () => Math.floor(new Date().getTime() / 1000); +export const getUtcTimestamp = () => Math.floor(Date.now() / 1000); @Injectable() export class AuthService { constructor( - private config: Config, - private prisma: PrismaService, - private mailer: MailService + private readonly config: Config, + private readonly prisma: PrismaService, + private readonly mailer: MailService ) {} sign(user: UserClaim) { diff --git a/packages/backend/server/src/modules/config.ts b/packages/backend/server/src/modules/config.ts index d0fb9e69d4..be9261f2a1 100644 --- a/packages/backend/server/src/modules/config.ts +++ b/packages/backend/server/src/modules/config.ts @@ -1,7 +1,7 @@ import { Module } from '@nestjs/common'; import { Field, ObjectType, Query } from '@nestjs/graphql'; -import { SERVER_FLAVOR } from '../modules'; +export const { SERVER_FLAVOR } = process.env; @ObjectType() export class ServerConfigType { diff --git a/packages/backend/server/src/modules/doc/history.ts b/packages/backend/server/src/modules/doc/history.ts index 2a6dc47554..7502355950 100644 --- a/packages/backend/server/src/modules/doc/history.ts +++ b/packages/backend/server/src/modules/doc/history.ts @@ -68,7 +68,11 @@ export class DocHistoryManager { // safe to ignore // only happens when duplicated history record created in multi processes }); - metrics().docHistoryCounter.add(1, {}); + metrics.doc + .counter('history_created_counter', { + description: 'How many times the snapshot history created', + }) + .add(1); this.logger.log( `History created for ${snapshot.id} in workspace ${snapshot.workspaceId}.` ); @@ -182,7 +186,11 @@ export class DocHistoryManager { // which is not the solution in CRDT. // let user revert in client and update the data in sync system // `await this.db.snapshot.update();` - metrics().docRecoverCounter.add(1, {}); + metrics.doc + .counter('history_recovered_counter', { + description: 'How many times history recovered request happened', + }) + .add(1); return history.timestamp; } diff --git a/packages/backend/server/src/modules/doc/manager.ts b/packages/backend/server/src/modules/doc/manager.ts index e531f6057e..326902b35f 100644 --- a/packages/backend/server/src/modules/doc/manager.ts +++ b/packages/backend/server/src/modules/doc/manager.ts @@ -42,7 +42,7 @@ function compare(yBinary: Buffer, jwstBinary: Buffer, strict = false): boolean { function isEmptyBuffer(buf: Buffer): boolean { return ( - buf.length == 0 || + buf.length === 0 || // 0x0000 (buf.length === 2 && buf[0] === 0 && buf[1] === 0) ); @@ -60,9 +60,9 @@ const MAX_SEQ_NUM = 0x3fffffff; // u31 */ @Injectable() export class DocManager implements OnModuleInit, OnModuleDestroy { - private logger = new Logger(DocManager.name); + private readonly logger = new Logger(DocManager.name); private job: NodeJS.Timeout | null = null; - private seqMap = new Map(); + private readonly seqMap = new Map(); private busy = false; constructor( @@ -125,13 +125,13 @@ export class DocManager implements OnModuleInit, OnModuleDestroy { this.config.doc.manager.experimentalMergeWithJwstCodec && updates.length < 100 /* avoid overloading */ ) { - metrics().jwstCodecMerge.add(1); + metrics.jwst.counter('codec_merge_counter').add(1); const yjsResult = Buffer.from(encodeStateAsUpdate(doc)); let log = false; try { const jwstResult = jwstMergeUpdates(updates); if (!compare(yjsResult, jwstResult)) { - metrics().jwstCodecDidnotMatch.add(1); + metrics.jwst.counter('codec_not_match').add(1); this.logger.warn( `jwst codec result doesn't match yjs codec result for: ${guid}` ); @@ -142,7 +142,7 @@ export class DocManager implements OnModuleInit, OnModuleDestroy { } } } catch (e) { - metrics().jwstCodecFail.add(1); + metrics.jwst.counter('codec_fails_counter').add(1); this.logger.warn(`jwst apply update failed for ${guid}: ${e}`); log = true; } finally { diff --git a/packages/backend/server/src/modules/index.ts b/packages/backend/server/src/modules/index.ts index a16215c68e..d021dd8bfa 100644 --- a/packages/backend/server/src/modules/index.ts +++ b/packages/backend/server/src/modules/index.ts @@ -3,7 +3,7 @@ import { EventEmitterModule } from '@nestjs/event-emitter'; import { ScheduleModule } from '@nestjs/schedule'; import { GqlModule } from '../graphql.module'; -import { ServerConfigModule } from './config'; +import { SERVER_FLAVOR, ServerConfigModule } from './config'; import { DocModule } from './doc'; import { PaymentModule } from './payment'; import { SelfHostedModule } from './self-hosted'; @@ -11,8 +11,6 @@ import { SyncModule } from './sync'; import { UsersModule } from './users'; import { WorkspaceModule } from './workspaces'; -const { SERVER_FLAVOR } = process.env; - const BusinessModules: (Type | DynamicModule)[] = [ EventEmitterModule.forRoot({ global: true, diff --git a/packages/backend/server/src/modules/sync/events/events.gateway.ts b/packages/backend/server/src/modules/sync/events/events.gateway.ts index 2921411860..f847842c1c 100644 --- a/packages/backend/server/src/modules/sync/events/events.gateway.ts +++ b/packages/backend/server/src/modules/sync/events/events.gateway.ts @@ -45,6 +45,7 @@ export const GatewayErrorWrapper = (): MethodDecorator => { try { result = originalMethod.apply(this, args); } catch (e) { + metrics.socketio.counter('unhandled_errors').add(1); return { error: new InternalError(e as Error), }; @@ -52,6 +53,7 @@ export const GatewayErrorWrapper = (): MethodDecorator => { if (result instanceof Promise) { return result.catch(e => { + metrics.socketio.counter('unhandled_errors').add(1); return { error: new InternalError(e), }; @@ -68,7 +70,7 @@ export const GatewayErrorWrapper = (): MethodDecorator => { const SubscribeMessage = (event: string) => applyDecorators( GatewayErrorWrapper(), - CallTimer('socket_io_event_duration', { event }), + CallTimer('socketio', 'event_duration', { event }), RawSubscribeMessage(event) ); @@ -104,12 +106,12 @@ export class EventsGateway implements OnGatewayConnection, OnGatewayDisconnect { handleConnection() { this.connectionCount++; - metrics().socketIOConnectionGauge(this.connectionCount); + metrics.socketio.gauge('realtime_connections').record(this.connectionCount); } handleDisconnect() { this.connectionCount--; - metrics().socketIOConnectionGauge(this.connectionCount); + metrics.socketio.gauge('realtime_connections').record(this.connectionCount); } @Auth() diff --git a/packages/backend/server/src/modules/workspaces/controller.ts b/packages/backend/server/src/modules/workspaces/controller.ts index 2d20272f94..47b64cf4aa 100644 --- a/packages/backend/server/src/modules/workspaces/controller.ts +++ b/packages/backend/server/src/modules/workspaces/controller.ts @@ -34,7 +34,7 @@ export class WorkspacesController { // // NOTE: because graphql can't represent a File, so we have to use REST API to get blob @Get('/:id/blobs/:name') - @CallTimer('doc_controller', { method: 'get_blob' }) + @CallTimer('controllers', 'workspace_get_blob') async blob( @Param('id') workspaceId: string, @Param('name') name: string, @@ -59,7 +59,7 @@ export class WorkspacesController { @Get('/:id/docs/:guid') @Auth() @Publicable() - @CallTimer('doc_controller', { method: 'get_doc' }) + @CallTimer('controllers', 'workspace_get_doc') async doc( @CurrentUser() user: UserType | undefined, @Param('id') ws: string, @@ -106,7 +106,7 @@ export class WorkspacesController { @Get('/:id/docs/:guid/histories/:timestamp') @Auth() - @CallTimer('doc_controller', { method: 'get_history' }) + @CallTimer('controllers', 'workspace_get_history') async history( @CurrentUser() user: UserType, @Param('id') ws: string, diff --git a/packages/backend/server/src/prisma/service.ts b/packages/backend/server/src/prisma/service.ts index 25ac6b92f4..76fb92b392 100644 --- a/packages/backend/server/src/prisma/service.ts +++ b/packages/backend/server/src/prisma/service.ts @@ -7,6 +7,13 @@ export class PrismaService extends PrismaClient implements OnModuleInit, OnModuleDestroy { + static INSTANCE: PrismaService | null = null; + + constructor() { + super(); + PrismaService.INSTANCE = this; + } + async onModuleInit() { await this.$connect(); } diff --git a/packages/backend/server/src/utils/doc.ts b/packages/backend/server/src/utils/doc.ts index 7230b05a18..52c59204c4 100644 --- a/packages/backend/server/src/utils/doc.ts +++ b/packages/backend/server/src/utils/doc.ts @@ -16,7 +16,7 @@ export class DocID { raw: string; workspace: string; variant: DocVariant; - private sub: string | null; + private readonly sub: string | null; static parse(raw: string): DocID | null { try { diff --git a/packages/backend/server/tests/exception-logger.e2e.ts b/packages/backend/server/tests/exception-logger.e2e.ts index 982225fdeb..12f00ee9eb 100644 --- a/packages/backend/server/tests/exception-logger.e2e.ts +++ b/packages/backend/server/tests/exception-logger.e2e.ts @@ -17,7 +17,7 @@ class FakePrisma { get workspace() { return { async findUnique() { - throw Error('exception from graphql'); + throw new Error('exception from graphql'); }, }; } diff --git a/packages/backend/server/tests/history.spec.ts b/packages/backend/server/tests/history.spec.ts index 103a7d231a..c2ed148574 100644 --- a/packages/backend/server/tests/history.spec.ts +++ b/packages/backend/server/tests/history.spec.ts @@ -183,26 +183,30 @@ test('should correctly list all history records', async t => { // insert expired data await db.snapshotHistory.createMany({ - data: new Array(10).fill(0).map((_, i) => ({ - workspaceId: snapshot.workspaceId, - id: snapshot.id, - blob: snapshot.blob, - state: snapshot.state, - timestamp: new Date(timestamp - 10 - i), - expiredAt: new Date(timestamp - 1), - })), + data: Array.from({ length: 10 }) + .fill(0) + .map((_, i) => ({ + workspaceId: snapshot.workspaceId, + id: snapshot.id, + blob: snapshot.blob, + state: snapshot.state, + timestamp: new Date(timestamp - 10 - i), + expiredAt: new Date(timestamp - 1), + })), }); // insert available data await db.snapshotHistory.createMany({ - data: new Array(10).fill(0).map((_, i) => ({ - workspaceId: snapshot.workspaceId, - id: snapshot.id, - blob: snapshot.blob, - state: snapshot.state, - timestamp: new Date(timestamp + i), - expiredAt: new Date(timestamp + 1000), - })), + data: Array.from({ length: 10 }) + .fill(0) + .map((_, i) => ({ + workspaceId: snapshot.workspaceId, + id: snapshot.id, + blob: snapshot.blob, + state: snapshot.state, + timestamp: new Date(timestamp + i), + expiredAt: new Date(timestamp + 1000), + })), }); const list = await manager.list( @@ -243,14 +247,16 @@ test('should be able to get last history record', async t => { // insert available data await db.snapshotHistory.createMany({ - data: new Array(10).fill(0).map((_, i) => ({ - workspaceId: snapshot.workspaceId, - id: snapshot.id, - blob: snapshot.blob, - state: snapshot.state, - timestamp: new Date(timestamp + i), - expiredAt: new Date(timestamp + 1000), - })), + data: Array.from({ length: 10 }) + .fill(0) + .map((_, i) => ({ + workspaceId: snapshot.workspaceId, + id: snapshot.id, + blob: snapshot.blob, + state: snapshot.state, + timestamp: new Date(timestamp + i), + expiredAt: new Date(timestamp + 1000), + })), }); const history = await manager.last(snapshot.workspaceId, snapshot.id); @@ -299,26 +305,30 @@ test('should be able to cleanup expired history', async t => { // insert expired data await db.snapshotHistory.createMany({ - data: new Array(10).fill(0).map((_, i) => ({ - workspaceId: snapshot.workspaceId, - id: snapshot.id, - blob: snapshot.blob, - state: snapshot.state, - timestamp: new Date(timestamp - 10 - i), - expiredAt: new Date(timestamp - 1), - })), + data: Array.from({ length: 10 }) + .fill(0) + .map((_, i) => ({ + workspaceId: snapshot.workspaceId, + id: snapshot.id, + blob: snapshot.blob, + state: snapshot.state, + timestamp: new Date(timestamp - 10 - i), + expiredAt: new Date(timestamp - 1), + })), }); // insert available data await db.snapshotHistory.createMany({ - data: new Array(10).fill(0).map((_, i) => ({ - workspaceId: snapshot.workspaceId, - id: snapshot.id, - blob: snapshot.blob, - state: snapshot.state, - timestamp: new Date(timestamp + i), - expiredAt: new Date(timestamp + 1000), - })), + data: Array.from({ length: 10 }) + .fill(0) + .map((_, i) => ({ + workspaceId: snapshot.workspaceId, + id: snapshot.id, + blob: snapshot.blob, + state: snapshot.state, + timestamp: new Date(timestamp + i), + expiredAt: new Date(timestamp + 1000), + })), }); let count = await db.snapshotHistory.count(); diff --git a/packages/backend/server/tests/workspace-usage.spec.ts b/packages/backend/server/tests/workspace-usage.spec.ts index 6cda0e2ad2..a7051b1eb0 100644 --- a/packages/backend/server/tests/workspace-usage.spec.ts +++ b/packages/backend/server/tests/workspace-usage.spec.ts @@ -65,9 +65,7 @@ test.afterEach.always(async t => { test('should get blob size limit', async t => { const { resolver } = t.context; - fakeUserService.getStorageQuotaById.returns( - Promise.resolve(100 * 1024 * 1024 * 1024) - ); + fakeUserService.getStorageQuotaById.resolves(100 * 1024 * 1024 * 1024); const res = await resolver.checkBlobSize(new FakePrisma().fakeUser, '', 100); t.not(res, false); // @ts-expect-error diff --git a/packages/common/debug/src/index.ts b/packages/common/debug/src/index.ts index d943e27fd5..b805541cfb 100644 --- a/packages/common/debug/src/index.ts +++ b/packages/common/debug/src/index.ts @@ -24,7 +24,7 @@ if (typeof window !== 'undefined') { } export class DebugLogger { - private _debug: debug.Debugger; + private readonly _debug: debug.Debugger; constructor(namespace: string) { this._debug = debug(namespace); diff --git a/packages/common/env/src/global.ts b/packages/common/env/src/global.ts index 7e2125a38e..88c86f24ad 100644 --- a/packages/common/env/src/global.ts +++ b/packages/common/env/src/global.ts @@ -39,6 +39,12 @@ export const runtimeFlagsSchema = z.object({ editorFlags: blockSuiteFeatureFlags, appVersion: z.string(), editorVersion: z.string(), + appBuildType: z.union([ + z.literal('stable'), + z.literal('beta'), + z.literal('internal'), + z.literal('canary'), + ]), }); export type BlockSuiteFeatureFlags = z.infer; diff --git a/packages/common/env/src/ua-helper.ts b/packages/common/env/src/ua-helper.ts index 66a685dce3..fc372b6d9a 100644 --- a/packages/common/env/src/ua-helper.ts +++ b/packages/common/env/src/ua-helper.ts @@ -1,7 +1,7 @@ import { assertExists } from '@blocksuite/global/utils'; export class UaHelper { - private uaMap; + private readonly uaMap; public isLinux = false; public isMacOs = false; public isSafari = false; diff --git a/packages/common/infra/src/blocksuite/initialization/index.ts b/packages/common/infra/src/blocksuite/initialization/index.ts index ea1e70f82f..ca06c51e5b 100644 --- a/packages/common/infra/src/blocksuite/initialization/index.ts +++ b/packages/common/infra/src/blocksuite/initialization/index.ts @@ -3,8 +3,11 @@ import type { Page, PageMeta, Workspace } from '@blocksuite/store'; import type { createStore, WritableAtom } from 'jotai/vanilla'; import { nanoid } from 'nanoid'; -import { checkWorkspaceCompatibility, MigrationPoint } from '..'; import { migratePages } from '../migration/blocksuite'; +import { + checkWorkspaceCompatibility, + MigrationPoint, +} from '../migration/workspace'; export async function initEmptyPage(page: Page, title?: string) { await page.load(() => { diff --git a/packages/common/infra/src/blocksuite/migration/subdoc.ts b/packages/common/infra/src/blocksuite/migration/subdoc.ts index c8acf278cb..da77756cc2 100644 --- a/packages/common/infra/src/blocksuite/migration/subdoc.ts +++ b/packages/common/infra/src/blocksuite/migration/subdoc.ts @@ -50,7 +50,7 @@ function migrateDatabase(data: YMap) { update: (cell: { id: string; value: unknown }) => void ) => { Object.values(cells).forEach(row => { - if (row[id] != null) { + if (row[id] !== null && row[id] !== undefined) { update(row[id]); } }); diff --git a/packages/common/infra/src/type.ts b/packages/common/infra/src/type.ts index e998db425b..4467e27734 100644 --- a/packages/common/infra/src/type.ts +++ b/packages/common/infra/src/type.ts @@ -45,8 +45,8 @@ export abstract class HandlerManager< Handlers extends Record, > { static instance: HandlerManager>; - private _app: App; - private _namespace: Namespace; + private readonly _app: App; + private readonly _namespace: Namespace; private _handlers: Handlers; constructor() { @@ -190,9 +190,17 @@ export interface UpdateMeta { allowAutoUpdate: boolean; } +export type UpdaterConfig = { + autoCheckUpdate: boolean; + autoDownloadUpdate: boolean; +}; + export type UpdaterHandlers = { currentVersion: () => Promise; quitAndInstall: () => Promise; + downloadUpdate: () => Promise; + getConfig: () => Promise; + setConfig: (newConfig: Partial) => Promise; checkForUpdatesAndNotify: () => Promise<{ version: string } | null>; }; diff --git a/packages/frontend/component/src/components/app-sidebar/app-updater-button/index.jotai.ts b/packages/frontend/component/src/components/app-sidebar/app-updater-button/index.jotai.ts deleted file mode 100644 index c438a2ce9d..0000000000 --- a/packages/frontend/component/src/components/app-sidebar/app-updater-button/index.jotai.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { isBrowser } from '@affine/env/constant'; -import type { UpdateMeta } from '@toeverything/infra/type'; -import { atomWithObservable, atomWithStorage } from 'jotai/utils'; -import { Observable } from 'rxjs'; - -// todo: move to utils? -function rpcToObservable< - T, - H extends () => Promise, - E extends (callback: (t: T) => void) => () => void, ->( - initialValue: T | null, - { - event, - handler, - onSubscribe, - }: { - event?: E; - handler?: H; - onSubscribe?: () => void; - } -): Observable { - return new Observable(subscriber => { - subscriber.next(initialValue); - onSubscribe?.(); - if (!isBrowser || !environment.isDesktop || !event) { - subscriber.complete(); - return; - } - handler?.() - .then(t => { - subscriber.next(t); - }) - .catch(err => { - subscriber.error(err); - }); - return event(t => { - subscriber.next(t); - }); - }); -} - -export const updateReadyAtom = atomWithObservable(() => { - return rpcToObservable(null as UpdateMeta | null, { - event: window.events?.updater.onUpdateReady, - }); -}); - -export const updateAvailableAtom = atomWithObservable(() => { - return rpcToObservable(null as UpdateMeta | null, { - event: window.events?.updater.onUpdateAvailable, - onSubscribe: () => { - window.apis?.updater.checkForUpdatesAndNotify().catch(err => { - console.error(err); - }); - }, - }); -}); - -export const downloadProgressAtom = atomWithObservable(() => { - return rpcToObservable(null as number | null, { - event: window.events?.updater.onDownloadProgress, - }); -}); - -export const changelogCheckedAtom = atomWithStorage>( - 'affine:client-changelog-checked', - {} -); diff --git a/packages/frontend/component/src/components/app-sidebar/app-updater-button/index.tsx b/packages/frontend/component/src/components/app-sidebar/app-updater-button/index.tsx index a3afe7a00d..4d7a442c81 100644 --- a/packages/frontend/component/src/components/app-sidebar/app-updater-button/index.tsx +++ b/packages/frontend/component/src/components/app-sidebar/app-updater-button/index.tsx @@ -1,18 +1,21 @@ -import { isBrowser, Unreachable } from '@affine/env/constant'; +import { Unreachable } from '@affine/env/constant'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { CloseIcon, NewIcon, ResetIcon } from '@blocksuite/icons'; import { Tooltip } from '@toeverything/components/tooltip'; -import clsx from 'clsx'; -import { atom, useAtomValue, useSetAtom } from 'jotai'; -import { startTransition, useCallback, useState } from 'react'; - -import * as styles from './index.css'; import { changelogCheckedAtom, + currentChangelogUnreadAtom, + currentVersionAtom, downloadProgressAtom, updateAvailableAtom, updateReadyAtom, -} from './index.jotai'; + useAppUpdater, +} from '@toeverything/hooks/use-app-updater'; +import clsx from 'clsx'; +import { useAtomValue, useSetAtom } from 'jotai'; +import { startTransition, useCallback } from 'react'; + +import * as styles from './index.css'; export interface AddPageButtonPureProps { onClickUpdate: () => void; @@ -29,26 +32,6 @@ export interface AddPageButtonPureProps { style?: React.CSSProperties; } -const currentVersionAtom = atom(async () => { - if (!isBrowser) { - return null; - } - const currentVersion = await window.apis?.updater.currentVersion(); - return currentVersion; -}); - -const currentChangelogUnreadAtom = atom(async get => { - if (!isBrowser) { - return false; - } - const mapping = get(changelogCheckedAtom); - const currentVersion = await get(currentVersionAtom); - if (currentVersion) { - return !mapping[currentVersion]; - } - return false; -}); - export function AppUpdaterButtonPure({ updateReady, onClickUpdate, @@ -198,12 +181,12 @@ export function AppUpdaterButton({ const currentChangelogUnread = useAtomValue(currentChangelogUnreadAtom); const updateReady = useAtomValue(updateReadyAtom); const updateAvailable = useAtomValue(updateAvailableAtom); - const currentVersion = useAtomValue(currentVersionAtom); const downloadProgress = useAtomValue(downloadProgressAtom); + const currentVersion = useAtomValue(currentVersionAtom); + const { quitAndInstall, appQuitting } = useAppUpdater(); const setChangelogCheckAtom = useSetAtom(changelogCheckedAtom); - const [appQuitting, setAppQuitting] = useState(false); - const onDismissCurrentChangelog = useCallback(() => { + const dismissCurrentChangelog = useCallback(() => { if (!currentVersion) { return; } @@ -216,13 +199,10 @@ export function AppUpdaterButton({ }) ); }, [currentVersion, setChangelogCheckAtom]); - const onClickUpdate = useCallback(() => { + + const handleClickUpdate = useCallback(() => { if (updateReady) { - setAppQuitting(true); - window.apis?.updater.quitAndInstall().catch(err => { - // TODO: add error toast here - console.error(err); - }); + quitAndInstall(); } else if (updateAvailable) { if (updateAvailable.allowAutoUpdate) { // wait for download to finish @@ -234,23 +214,25 @@ export function AppUpdaterButton({ } } else if (currentChangelogUnread) { window.open(runtimeConfig.changelogUrl, '_blank'); - onDismissCurrentChangelog(); + dismissCurrentChangelog(); } else { throw new Unreachable(); } }, [ - currentChangelogUnread, - currentVersion, - onDismissCurrentChangelog, - updateAvailable, updateReady, + quitAndInstall, + updateAvailable, + currentChangelogUnread, + dismissCurrentChangelog, + currentVersion, ]); + return ( ); } - -export * from './index.jotai'; diff --git a/packages/frontend/component/src/components/app-sidebar/index.jotai.ts b/packages/frontend/component/src/components/app-sidebar/index.jotai.ts index 696e8620f4..f645cc33f4 100644 --- a/packages/frontend/component/src/components/app-sidebar/index.jotai.ts +++ b/packages/frontend/component/src/components/app-sidebar/index.jotai.ts @@ -2,8 +2,10 @@ import { atom } from 'jotai'; import { atomWithStorage } from 'jotai/utils'; export const APP_SIDEBAR_OPEN = 'app-sidebar-open'; -export const appSidebarOpenAtom = atomWithStorage(APP_SIDEBAR_OPEN, true); -export const appSidebarFloatingAtom = atom(false); +export const isMobile = window.innerWidth < 768; + +export const appSidebarOpenAtom = atomWithStorage(APP_SIDEBAR_OPEN, !isMobile); +export const appSidebarFloatingAtom = atom(isMobile); export const appSidebarResizingAtom = atom(false); export const appSidebarWidthAtom = atomWithStorage( diff --git a/packages/frontend/component/src/components/block-hub/index.tsx b/packages/frontend/component/src/components/block-hub/index.tsx index b6157b0521..a6f27b9937 100644 --- a/packages/frontend/component/src/components/block-hub/index.tsx +++ b/packages/frontend/component/src/components/block-hub/index.tsx @@ -10,9 +10,9 @@ export const RootBlockHub = () => { const div = ref.current; if (blockHub) { if (div.hasChildNodes()) { - div.removeChild(div.firstChild as ChildNode); + (div.firstChild as ChildNode).remove(); } - div.appendChild(blockHub); + div.append(blockHub); } } }, [blockHub]); diff --git a/packages/frontend/component/src/components/block-suite-editor/index.tsx b/packages/frontend/component/src/components/block-suite-editor/index.tsx index 07d3e7b200..41a4b26c51 100644 --- a/packages/frontend/component/src/components/block-suite-editor/index.tsx +++ b/packages/frontend/component/src/components/block-suite-editor/index.tsx @@ -192,9 +192,9 @@ const BlockSuiteEditorImpl = ({ if (!container) { return; } - container.appendChild(editor); + container.append(editor); return () => { - container.removeChild(editor); + editor.remove(); }; }, [editor]); diff --git a/packages/frontend/component/src/components/page-list/filter/logical/matcher.ts b/packages/frontend/component/src/components/page-list/filter/logical/matcher.ts index d7ffc5763c..fe3574378e 100644 --- a/packages/frontend/component/src/components/page-list/filter/logical/matcher.ts +++ b/packages/frontend/component/src/components/page-list/filter/logical/matcher.ts @@ -4,9 +4,11 @@ import { typesystem } from './typesystem'; type MatcherData = { type: Type; data: Data }; export class Matcher { - private list: MatcherData[] = []; + private readonly list: MatcherData[] = []; - constructor(private _match?: (type: Type, target: TType) => boolean) {} + constructor( + private readonly _match?: (type: Type, target: TType) => boolean + ) {} register(type: Type, data: Data) { this.list.push({ type, data }); diff --git a/packages/frontend/component/src/components/page-list/filter/logical/typesystem.ts b/packages/frontend/component/src/components/page-list/filter/logical/typesystem.ts index 4b0e0a8533..28345c3727 100644 --- a/packages/frontend/component/src/components/page-list/filter/logical/typesystem.ts +++ b/packages/frontend/component/src/components/page-list/filter/logical/typesystem.ts @@ -92,8 +92,8 @@ export type ValueOfData = T extends DataDefine export class DataDefine> { constructor( - private config: DataDefineConfig, - private dataMap: Map + private readonly config: DataDefineConfig, + private readonly dataMap: Map ) {} create(data?: Data): TDataType { @@ -195,7 +195,7 @@ export class Typesystem { ): boolean { if (superType.type === 'typeRef') { // TODO both are ref - if (context && sub.type != 'typeRef') { + if (context && sub.type !== 'typeRef') { context[superType.name] = sub; } // TODO bound diff --git a/packages/frontend/component/src/components/page-list/filter/vars.tsx b/packages/frontend/component/src/components/page-list/filter/vars.tsx index 9b3c5fc2e9..bf4af661af 100644 --- a/packages/frontend/component/src/components/page-list/filter/vars.tsx +++ b/packages/frontend/component/src/components/page-list/filter/vars.tsx @@ -141,7 +141,7 @@ filterMatcher.register( name: 'is', defaultArgs: () => [true], impl: (value, target) => { - return value == target; + return value === target; }, } ); @@ -209,7 +209,7 @@ filterMatcher.register( defaultArgs: () => [], impl: tags => { const safeTags = safeArray(tags); - return safeTags.length == 0; + return safeTags.length === 0; }, } ); diff --git a/packages/frontend/component/src/components/page-list/page-group.tsx b/packages/frontend/component/src/components/page-list/page-group.tsx index 87963a9216..77dc4ba191 100644 --- a/packages/frontend/component/src/components/page-list/page-group.tsx +++ b/packages/frontend/component/src/components/page-list/page-group.tsx @@ -1,5 +1,4 @@ import type { Tag } from '@affine/env/filter'; -import { Trans } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { assertExists } from '@blocksuite/global/utils'; import { EdgelessIcon, PageIcon, ToggleCollapseIcon } from '@blocksuite/icons'; @@ -19,93 +18,8 @@ import { useAtom, useAtomValue, } from './scoped-atoms'; -import type { - PageGroupDefinition, - PageGroupProps, - PageListItemProps, - PageListProps, -} from './types'; -import { type DateKey } from './types'; -import { betweenDaysAgo, shallowEqual, withinDaysAgo } from './utils'; - -// todo: optimize date matchers -const getDateGroupDefinitions = (key: DateKey): PageGroupDefinition[] => [ - { - id: 'today', - label: , - match: item => withinDaysAgo(new Date(item[key] ?? item.createDate), 1), - }, - { - id: 'yesterday', - label: , - match: item => betweenDaysAgo(new Date(item[key] ?? item.createDate), 1, 2), - }, - { - id: 'last7Days', - label: , - match: item => betweenDaysAgo(new Date(item[key] ?? item.createDate), 2, 7), - }, - { - id: 'last30Days', - label: , - match: item => - betweenDaysAgo(new Date(item[key] ?? item.createDate), 7, 30), - }, - { - id: 'moreThan30Days', - label: , - match: item => !withinDaysAgo(new Date(item[key] ?? item.createDate), 30), - }, -]; - -const pageGroupDefinitions = { - createDate: getDateGroupDefinitions('createDate'), - updatedDate: getDateGroupDefinitions('updatedDate'), - // add more here later - // todo: some page group definitions maybe dynamic -}; - -export function pagesToPageGroups( - pages: PageMeta[], - key?: DateKey -): PageGroupProps[] { - if (!key) { - return [ - { - id: 'all', - items: pages, - allItems: pages, - }, - ]; - } - - // assume pages are already sorted, we will use the page order to determine the group order - const groupDefs = pageGroupDefinitions[key]; - const groups: PageGroupProps[] = []; - - for (const page of pages) { - // for a single page, there could be multiple groups that it belongs to - const matchedGroups = groupDefs.filter(def => def.match(page)); - for (const groupDef of matchedGroups) { - const group = groups.find(g => g.id === groupDef.id); - if (group) { - group.items.push(page); - } else { - const label = - typeof groupDef.label === 'function' - ? groupDef.label() - : groupDef.label; - groups.push({ - id: groupDef.id, - label: label, - items: [page], - allItems: pages, - }); - } - } - } - return groups; -} +import type { PageGroupProps, PageListItemProps, PageListProps } from './types'; +import { shallowEqual } from './utils'; export const PageGroupHeader = ({ id, items, label }: PageGroupProps) => { const [collapseState, setCollapseState] = useAtom(pageGroupCollapseStateAtom); diff --git a/packages/frontend/component/src/components/page-list/pages-to-page-group.tsx b/packages/frontend/component/src/components/page-list/pages-to-page-group.tsx new file mode 100644 index 0000000000..cb272368c5 --- /dev/null +++ b/packages/frontend/component/src/components/page-list/pages-to-page-group.tsx @@ -0,0 +1,85 @@ +import { Trans } from '@affine/i18n'; +import type { PageMeta } from '@blocksuite/store'; + +import type { PageGroupDefinition, PageGroupProps } from './types'; +import { type DateKey } from './types'; +import { betweenDaysAgo, withinDaysAgo } from './utils'; + +// todo: optimize date matchers +const getDateGroupDefinitions = (key: DateKey): PageGroupDefinition[] => [ + { + id: 'today', + label: , + match: item => withinDaysAgo(new Date(item[key] ?? item.createDate), 1), + }, + { + id: 'yesterday', + label: , + match: item => betweenDaysAgo(new Date(item[key] ?? item.createDate), 1, 2), + }, + { + id: 'last7Days', + label: , + match: item => betweenDaysAgo(new Date(item[key] ?? item.createDate), 2, 7), + }, + { + id: 'last30Days', + label: , + match: item => + betweenDaysAgo(new Date(item[key] ?? item.createDate), 7, 30), + }, + { + id: 'moreThan30Days', + label: , + match: item => !withinDaysAgo(new Date(item[key] ?? item.createDate), 30), + }, +]; + +const pageGroupDefinitions = { + createDate: getDateGroupDefinitions('createDate'), + updatedDate: getDateGroupDefinitions('updatedDate'), + // add more here later + // todo: some page group definitions maybe dynamic +}; + +export function pagesToPageGroups( + pages: PageMeta[], + key?: DateKey +): PageGroupProps[] { + if (!key) { + return [ + { + id: 'all', + items: pages, + allItems: pages, + }, + ]; + } + + // assume pages are already sorted, we will use the page order to determine the group order + const groupDefs = pageGroupDefinitions[key]; + const groups: PageGroupProps[] = []; + + for (const page of pages) { + // for a single page, there could be multiple groups that it belongs to + const matchedGroups = groupDefs.filter(def => def.match(page)); + for (const groupDef of matchedGroups) { + const group = groups.find(g => g.id === groupDef.id); + if (group) { + group.items.push(page); + } else { + const label = + typeof groupDef.label === 'function' + ? groupDef.label() + : groupDef.label; + groups.push({ + id: groupDef.id, + label: label, + items: [page], + allItems: pages, + }); + } + } + } + return groups; +} diff --git a/packages/frontend/component/src/components/page-list/scoped-atoms.tsx b/packages/frontend/component/src/components/page-list/scoped-atoms.tsx index 9ad94f539e..bed647ba8c 100644 --- a/packages/frontend/component/src/components/page-list/scoped-atoms.tsx +++ b/packages/frontend/component/src/components/page-list/scoped-atoms.tsx @@ -4,7 +4,7 @@ import { atom } from 'jotai'; import { selectAtom } from 'jotai/utils'; import { createIsolation } from 'jotai-scope'; -import { pagesToPageGroups } from './page-group'; +import { pagesToPageGroups } from './pages-to-page-group'; import type { PageListProps, PageMetaRecord, diff --git a/packages/frontend/component/src/components/page-list/utils.tsx b/packages/frontend/component/src/components/page-list/utils.tsx index 8acf0002eb..c0f29a9e10 100644 --- a/packages/frontend/component/src/components/page-list/utils.tsx +++ b/packages/frontend/component/src/components/page-list/utils.tsx @@ -10,9 +10,9 @@ import * as styles from './page-list.css'; export function isToday(date: Date): boolean { const today = new Date(); return ( - date.getDate() == today.getDate() && - date.getMonth() == today.getMonth() && - date.getFullYear() == today.getFullYear() + date.getDate() === today.getDate() && + date.getMonth() === today.getMonth() && + date.getFullYear() === today.getFullYear() ); } @@ -162,10 +162,10 @@ export function shallowEqual(objA: any, objB: any) { } // Test for A's keys different from B. - for (let i = 0; i < keysA.length; i++) { + for (const key of keysA) { if ( - !Object.prototype.hasOwnProperty.call(objB, keysA[i]) || - !Object.is(objA[keysA[i]], objB[keysA[i]]) + !Object.prototype.hasOwnProperty.call(objB, key) || + !Object.is(objA[key], objB[key]) ) { return false; } diff --git a/packages/frontend/component/src/components/page-list/view/collection-list.tsx b/packages/frontend/component/src/components/page-list/view/collection-list.tsx index 0c6f949aba..563bef0414 100644 --- a/packages/frontend/component/src/components/page-list/view/collection-list.tsx +++ b/packages/frontend/component/src/components/page-list/view/collection-list.tsx @@ -5,7 +5,7 @@ import type { } from '@affine/env/filter'; import type { PropertiesMeta } from '@affine/env/filter'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; -import { FilteredIcon } from '@blocksuite/icons'; +import { FilterIcon } from '@blocksuite/icons'; import { Button } from '@toeverything/components/button'; import { Menu } from '@toeverything/components/menu'; import { useCallback, useState } from 'react'; @@ -75,7 +75,7 @@ export const CollectionList = ({ - {node} - - ); -}; diff --git a/packages/frontend/component/src/components/page-list/view/edit-collection/edit-collection.tsx b/packages/frontend/component/src/components/page-list/view/edit-collection/edit-collection.tsx index 4cf17f9b32..ad91403861 100644 --- a/packages/frontend/component/src/components/page-list/view/edit-collection/edit-collection.tsx +++ b/packages/frontend/component/src/components/page-list/view/edit-collection/edit-collection.tsx @@ -6,7 +6,7 @@ import { Button } from '@toeverything/components/button'; import { Modal } from '@toeverything/components/modal'; import { type ReactNode, useCallback, useMemo, useState } from 'react'; -import { RadioButton, RadioButtonGroup } from '../../../../index'; +import { RadioButton, RadioButtonGroup } from '../../../../ui/button'; import * as styles from './edit-collection.css'; import { PagesMode } from './pages-mode'; import { RulesMode } from './rules-mode'; diff --git a/packages/frontend/component/src/components/page-list/view/edit-collection/hooks.tsx b/packages/frontend/component/src/components/page-list/view/edit-collection/hooks.tsx index 55ba854682..58fde6a7ed 100644 --- a/packages/frontend/component/src/components/page-list/view/edit-collection/hooks.tsx +++ b/packages/frontend/component/src/components/page-list/view/edit-collection/hooks.tsx @@ -1,12 +1,7 @@ -import { - type AllPageListConfig, - filterPageByRules, -} from '@affine/component/page-list'; -import type { Filter } from '@affine/env/filter'; -import type { PageMeta } from '@blocksuite/store'; import { Modal } from '@toeverything/components/modal'; -import { type MouseEvent, useCallback, useState } from 'react'; +import { useCallback, useState } from 'react'; +import type { AllPageListConfig } from './edit-collection'; import { SelectPage } from './select-page'; export const useSelectPage = ({ allPageListConfig, @@ -60,47 +55,3 @@ export const useSelectPage = ({ }), }; }; -export const useFilter = (list: PageMeta[]) => { - const [filters, changeFilters] = useState([]); - const [showFilter, setShowFilter] = useState(false); - const clickFilter = useCallback( - (e: MouseEvent) => { - if (showFilter || filters.length !== 0) { - e.stopPropagation(); - e.preventDefault(); - setShowFilter(!showFilter); - } - }, - [filters.length, showFilter] - ); - const onCreateFilter = useCallback( - (filter: Filter) => { - changeFilters([...filters, filter]); - setShowFilter(true); - }, - [filters] - ); - return { - showFilter, - filters, - updateFilters: changeFilters, - clickFilter, - createFilter: onCreateFilter, - filteredList: list.filter(v => { - if (v.trash) { - return false; - } - return filterPageByRules(filters, [], v); - }), - }; -}; -export const useSearch = (list: PageMeta[]) => { - const [value, onChange] = useState(''); - return { - searchText: value, - updateSearchText: onChange, - searchedList: value - ? list.filter(v => v.title.toLowerCase().includes(value.toLowerCase())) - : list, - }; -}; diff --git a/packages/frontend/component/src/components/page-list/view/edit-collection/pages-mode.tsx b/packages/frontend/component/src/components/page-list/view/edit-collection/pages-mode.tsx index 358f5b460b..4f25b105e7 100644 --- a/packages/frontend/component/src/components/page-list/view/edit-collection/pages-mode.tsx +++ b/packages/frontend/component/src/components/page-list/view/edit-collection/pages-mode.tsx @@ -1,8 +1,3 @@ -import { - type AllPageListConfig, - FilterList, - VirtualizedPageList, -} from '@affine/component/page-list'; import type { Collection } from '@affine/env/filter'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { FilterIcon } from '@blocksuite/icons'; @@ -11,10 +6,14 @@ import { Menu } from '@toeverything/components/menu'; import clsx from 'clsx'; import { type ReactNode, useCallback } from 'react'; +import { FilterList } from '../../filter/filter-list'; import { VariableSelect } from '../../filter/vars'; +import { VirtualizedPageList } from '../../virtualized-page-list'; +import type { AllPageListConfig } from './edit-collection'; import * as styles from './edit-collection.css'; -import { useFilter, useSearch } from './hooks'; import { EmptyList } from './select-page'; +import { useFilter } from './use-filter'; +import { useSearch } from './use-search'; export const PagesMode = ({ switchMode, diff --git a/packages/frontend/component/src/components/page-list/view/edit-collection/select-page.tsx b/packages/frontend/component/src/components/page-list/view/edit-collection/select-page.tsx index 86a5f839f1..f4a4e7ffcf 100644 --- a/packages/frontend/component/src/components/page-list/view/edit-collection/select-page.tsx +++ b/packages/frontend/component/src/components/page-list/view/edit-collection/select-page.tsx @@ -6,13 +6,15 @@ import { Menu } from '@toeverything/components/menu'; import clsx from 'clsx'; import { useCallback, useState } from 'react'; -import { VirtualizedPageList } from '../..'; import { FilterList } from '../../filter'; import { VariableSelect } from '../../filter/vars'; +import { VirtualizedPageList } from '../../virtualized-page-list'; import { AffineShapeIcon } from '../affine-shape'; import type { AllPageListConfig } from './edit-collection'; import * as styles from './edit-collection.css'; -import { useFilter, useSearch } from './hooks'; +import { useFilter } from './use-filter'; +import { useSearch } from './use-search'; + export const SelectPage = ({ allPageListConfig, init, diff --git a/packages/frontend/component/src/components/page-list/view/edit-collection/use-filter.tsx b/packages/frontend/component/src/components/page-list/view/edit-collection/use-filter.tsx new file mode 100644 index 0000000000..8775c5a064 --- /dev/null +++ b/packages/frontend/component/src/components/page-list/view/edit-collection/use-filter.tsx @@ -0,0 +1,40 @@ +import type { Filter } from '@affine/env/filter'; +import type { PageMeta } from '@blocksuite/store'; +import { type MouseEvent, useCallback, useState } from 'react'; + +import { filterPageByRules } from '../../use-collection-manager'; + +export const useFilter = (list: PageMeta[]) => { + const [filters, changeFilters] = useState([]); + const [showFilter, setShowFilter] = useState(false); + const clickFilter = useCallback( + (e: MouseEvent) => { + if (showFilter || filters.length !== 0) { + e.stopPropagation(); + e.preventDefault(); + setShowFilter(!showFilter); + } + }, + [filters.length, showFilter] + ); + const onCreateFilter = useCallback( + (filter: Filter) => { + changeFilters([...filters, filter]); + setShowFilter(true); + }, + [filters] + ); + return { + showFilter, + filters, + updateFilters: changeFilters, + clickFilter, + createFilter: onCreateFilter, + filteredList: list.filter(v => { + if (v.trash) { + return false; + } + return filterPageByRules(filters, [], v); + }), + }; +}; diff --git a/packages/frontend/component/src/components/page-list/view/edit-collection/use-search.tsx b/packages/frontend/component/src/components/page-list/view/edit-collection/use-search.tsx new file mode 100644 index 0000000000..55e63e8ec1 --- /dev/null +++ b/packages/frontend/component/src/components/page-list/view/edit-collection/use-search.tsx @@ -0,0 +1,13 @@ +import type { PageMeta } from '@blocksuite/store'; +import { useState } from 'react'; + +export const useSearch = (list: PageMeta[]) => { + const [value, onChange] = useState(''); + return { + searchText: value, + updateSearchText: onChange, + searchedList: value + ? list.filter(v => v.title.toLowerCase().includes(value.toLowerCase())) + : list, + }; +}; diff --git a/packages/frontend/component/src/components/page-list/view/index.ts b/packages/frontend/component/src/components/page-list/view/index.ts index 8c130dca83..35f615cd90 100644 --- a/packages/frontend/component/src/components/page-list/view/index.ts +++ b/packages/frontend/component/src/components/page-list/view/index.ts @@ -4,4 +4,5 @@ export * from './collection-list'; export * from './collection-operations'; export * from './create-collection'; export * from './edit-collection/edit-collection'; +export * from './save-as-collection-button'; export * from './use-edit-collection'; diff --git a/packages/frontend/component/src/components/page-list/view/save-as-collection-button.tsx b/packages/frontend/component/src/components/page-list/view/save-as-collection-button.tsx new file mode 100644 index 0000000000..bcdd35c44f --- /dev/null +++ b/packages/frontend/component/src/components/page-list/view/save-as-collection-button.tsx @@ -0,0 +1,46 @@ +import type { Collection } from '@affine/env/filter'; +import { useAFFiNEI18N } from '@affine/i18n/hooks'; +import { SaveIcon } from '@blocksuite/icons'; +import { Button } from '@toeverything/components/button'; +import { nanoid } from 'nanoid'; +import { useCallback } from 'react'; + +import { createEmptyCollection } from '../use-collection-manager'; +import { useEditCollectionName } from './use-edit-collection'; + +interface SaveAsCollectionButtonProps { + onConfirm: (collection: Collection) => Promise; +} + +export const SaveAsCollectionButton = ({ + onConfirm, +}: SaveAsCollectionButtonProps) => { + const t = useAFFiNEI18N(); + const { open, node } = useEditCollectionName({ + title: t['com.affine.editCollection.saveCollection'](), + showTips: true, + }); + const handleClick = useCallback(() => { + open('') + .then(name => { + return onConfirm(createEmptyCollection(nanoid(), { name })); + }) + .catch(err => { + console.error(err); + }); + }, [open, onConfirm]); + return ( + <> + + {node} + + ); +}; diff --git a/packages/frontend/component/src/components/page-list/view/use-edit-collection.tsx b/packages/frontend/component/src/components/page-list/view/use-edit-collection.tsx index 901ec58beb..063e5cf19a 100644 --- a/packages/frontend/component/src/components/page-list/view/use-edit-collection.tsx +++ b/packages/frontend/component/src/components/page-list/view/use-edit-collection.tsx @@ -1,12 +1,13 @@ -import { - type AllPageListConfig, - CreateCollectionModal, - EditCollectionModal, - type EditCollectionMode, -} from '@affine/component/page-list'; import type { Collection } from '@affine/env/filter'; import { useCallback, useState } from 'react'; +import { CreateCollectionModal } from './create-collection'; +import { + type AllPageListConfig, + EditCollectionModal, + type EditCollectionMode, +} from './edit-collection/edit-collection'; + export const useEditCollection = (config: AllPageListConfig) => { const [data, setData] = useState<{ collection: Collection; diff --git a/packages/frontend/component/src/components/setting-components/workspace-detail-skeleton.tsx b/packages/frontend/component/src/components/setting-components/workspace-detail-skeleton.tsx index 6755c200d6..dde4dfd578 100644 --- a/packages/frontend/component/src/components/setting-components/workspace-detail-skeleton.tsx +++ b/packages/frontend/component/src/components/setting-components/workspace-detail-skeleton.tsx @@ -8,17 +8,19 @@ export const WorkspaceDetailSkeleton = () => { <> } subtitle={} /> - {new Array(3).fill(0).map((_, index) => { - return ( - } key={index}> - } - desc={} - spreadCol={false} - > - - ); - })} + {Array.from({ length: 3 }) + .fill(0) + .map((_, index) => { + return ( + } key={index}> + } + desc={} + spreadCol={false} + > + + ); + })} ); }; diff --git a/packages/frontend/component/src/components/setting-components/workspace-list-skeleton.tsx b/packages/frontend/component/src/components/setting-components/workspace-list-skeleton.tsx index 170798d4a4..8783ab82b0 100644 --- a/packages/frontend/component/src/components/setting-components/workspace-list-skeleton.tsx +++ b/packages/frontend/component/src/components/setting-components/workspace-list-skeleton.tsx @@ -26,9 +26,11 @@ export const WorkspaceListItemSkeleton = () => { export const WorkspaceListSkeleton = () => { return ( <> - {new Array(5).fill(0).map((_, index) => { - return ; - })} + {Array.from({ length: 5 }) + .fill(0) + .map((_, index) => { + return ; + })} ); }; diff --git a/packages/frontend/component/src/ui/button/styles.css.ts b/packages/frontend/component/src/ui/button/styles.css.ts index 4d9643b999..20e732ade5 100644 --- a/packages/frontend/component/src/ui/button/styles.css.ts +++ b/packages/frontend/component/src/ui/button/styles.css.ts @@ -61,6 +61,7 @@ export const dropdownIcon = style({ export const radioButton = style({ flexGrow: 1, + flex: 1, selectors: { '&:not(:last-of-type)': { marginRight: '4px', @@ -72,7 +73,8 @@ export const radioButtonContent = style({ display: 'flex', justifyContent: 'center', alignItems: 'center', - height: '24px', + height: '28px', + padding: '4px 8px', borderRadius: '8px', filter: 'drop-shadow(0px 0px 4px rgba(0, 0, 0, 0.1))', whiteSpace: 'nowrap', diff --git a/packages/frontend/component/src/ui/checkbox/checkbox.tsx b/packages/frontend/component/src/ui/checkbox/checkbox.tsx index 3acb39bf89..b54c245006 100644 --- a/packages/frontend/component/src/ui/checkbox/checkbox.tsx +++ b/packages/frontend/component/src/ui/checkbox/checkbox.tsx @@ -84,7 +84,7 @@ export const playCheckAnimation = async (refElement: Element) => { border-radius: 50%; font-size: inherit; `; - refElement.appendChild(sparkingEl); + refElement.append(sparkingEl); await sparkingEl.animate( [ diff --git a/packages/frontend/component/src/ui/skeleton/skeleton.tsx b/packages/frontend/component/src/ui/skeleton/skeleton.tsx index fef3285905..404e9817a7 100644 --- a/packages/frontend/component/src/ui/skeleton/skeleton.tsx +++ b/packages/frontend/component/src/ui/skeleton/skeleton.tsx @@ -29,7 +29,7 @@ export const Skeleton = ({ const style = { width, height, - ...(_style || {}), + ..._style, }; return ( diff --git a/packages/frontend/component/src/ui/toast/toast.ts b/packages/frontend/component/src/ui/toast/toast.ts index 794ae40491..56f86d9712 100644 --- a/packages/frontend/component/src/ui/toast/toast.ts +++ b/packages/frontend/component/src/ui/toast/toast.ts @@ -46,7 +46,7 @@ const createToastContainer = (portal?: HTMLElement) => { data-testid="affine-toast-container" >`; const element = htmlToElement(template); - portal.appendChild(element); + portal.append(element); return element; }; @@ -98,7 +98,7 @@ const createAndShowNewToast = ( const toastElement = htmlToElement(toastTemplate); // message is not trusted toastElement.textContent = message; - ToastContainer.appendChild(toastElement); + ToastContainer.append(toastElement); logger.debug(`toast with message: "${message}"`); window.dispatchEvent( new CustomEvent('affine-toast:emit', { detail: message }) diff --git a/packages/frontend/core/.webpack/runtime-config.ts b/packages/frontend/core/.webpack/runtime-config.ts index e54ac2410d..d613fa11aa 100644 --- a/packages/frontend/core/.webpack/runtime-config.ts +++ b/packages/frontend/core/.webpack/runtime-config.ts @@ -38,18 +38,21 @@ export function getRuntimeConfig(buildFlags: BuildFlags): RuntimeConfig { editorFlags, appVersion: packageJson.version, editorVersion: packageJson.dependencies['@blocksuite/editor'], + appBuildType: 'stable', }, get beta() { return { ...this.stable, enablePageHistory: false, serverUrlPrefix: 'https://insider.affine.pro', + appBuildType: 'beta' as const, }; }, get internal() { return { ...this.stable, serverUrlPrefix: 'https://insider.affine.pro', + appBuildType: 'internal' as const, }; }, // canary will be aggressive and enable all features @@ -82,6 +85,7 @@ export function getRuntimeConfig(buildFlags: BuildFlags): RuntimeConfig { editorFlags, appVersion: packageJson.version, editorVersion: packageJson.dependencies['@blocksuite/editor'], + appBuildType: 'canary', }, }; diff --git a/packages/frontend/core/project.json b/packages/frontend/core/project.json index 2d3dfa2787..a3d1d51dda 100644 --- a/packages/frontend/core/project.json +++ b/packages/frontend/core/project.json @@ -25,6 +25,9 @@ { "env": "BUILD_TYPE" }, + { + "env": "BUILD_TYPE_OVERRIDE" + }, { "env": "PERFSEE_TOKEN" }, @@ -48,6 +51,24 @@ }, { "env": "DISABLE_DEV_OVERLAY" + }, + { + "env": "CAPTCHA_SITE_KEY" + }, + { + "env": "SHOULD_REPORT_TRACE" + }, + { + "env": "TRACE_REPORT_ENDPOINT" + }, + { + "env": "R2_ACCOUNT_ID" + }, + { + "env": "R2_ACCESS_KEY_ID" + }, + { + "env": "R2_SECRET_ACCESS_KEY" } ], "options": { diff --git a/packages/frontend/core/src/bootstrap/register-plugins.ts b/packages/frontend/core/src/bootstrap/register-plugins.ts index 1af4e775cc..348208f60b 100644 --- a/packages/frontend/core/src/bootstrap/register-plugins.ts +++ b/packages/frontend/core/src/bootstrap/register-plugins.ts @@ -81,7 +81,7 @@ export async function bootstrapPluginSystem( logger.debug(`registering plugin ${pluginName}`); logger.debug(`package.json: ${packageJson}`); if (!release && !runtimeConfig.enablePlugin) { - return Promise.resolve(); + return; } const baseURL = url; const entryURL = `${baseURL}/${core}`; @@ -95,7 +95,7 @@ export async function bootstrapPluginSystem( const loadedAssetName = `${pluginName}_${asset}`; // todo(himself65): add assets into shadow dom if (loadedAssets.has(loadedAssetName)) { - return Promise.resolve(); + return; } if (asset.endsWith('.css')) { loadedAssets.add(loadedAssetName); @@ -106,12 +106,12 @@ export async function bootstrapPluginSystem( const style = document.createElement('style'); style.setAttribute('plugin-id', pluginName); style.textContent = text; - document.head.appendChild(style); + document.head.append(style); }); } return null; } else { - return Promise.resolve(); + return; } }) ); diff --git a/packages/frontend/core/src/commands/affine-updates.tsx b/packages/frontend/core/src/commands/affine-updates.tsx index 4ccc0a4af8..da4e167916 100644 --- a/packages/frontend/core/src/commands/affine-updates.tsx +++ b/packages/frontend/core/src/commands/affine-updates.tsx @@ -1,6 +1,6 @@ -import { updateReadyAtom } from '@affine/component/app-sidebar/app-updater-button'; import type { useAFFiNEI18N } from '@affine/i18n/hooks'; import { ResetIcon } from '@blocksuite/icons'; +import { updateReadyAtom } from '@toeverything/hooks/use-app-updater'; import { registerAffineCommand } from '@toeverything/infra/command'; import type { createStore } from 'jotai'; diff --git a/packages/frontend/core/src/components/affine/language-menu/index.tsx b/packages/frontend/core/src/components/affine/language-menu/index.tsx index f27e7fcdc0..8856057dcc 100644 --- a/packages/frontend/core/src/components/affine/language-menu/index.tsx +++ b/packages/frontend/core/src/components/affine/language-menu/index.tsx @@ -33,13 +33,14 @@ export const LanguageMenu = () => { contentOptions={{ style: { background: 'var(--affine-white)', + width: '250px', }, align: 'end', }} > {currentLanguage?.originalName || ''} diff --git a/packages/frontend/core/src/components/affine/new-workspace-setting-detail/profile.tsx b/packages/frontend/core/src/components/affine/new-workspace-setting-detail/profile.tsx index 80eacda929..ef9722ab58 100644 --- a/packages/frontend/core/src/components/affine/new-workspace-setting-detail/profile.tsx +++ b/packages/frontend/core/src/components/affine/new-workspace-setting-detail/profile.tsx @@ -96,6 +96,8 @@ export const ProfilePanel = ({ workspace, isOwner }: ProfilePanelProps) => { [pushNotification, update] ); + const canAdjustAvatar = workspaceAvatar && isOwner; + return (
{ name={name} colorfulFallback hoverIcon={isOwner ? : undefined} - onRemove={ - workspaceAvatar && isOwner ? handleRemoveUserAvatar : undefined + onRemove={canAdjustAvatar ? handleRemoveUserAvatar : undefined} + avatarTooltipOptions={ + canAdjustAvatar + ? { content: t['Click to replace photo']() } + : undefined + } + removeTooltipOptions={ + canAdjustAvatar ? { content: t['Remove photo']() } : undefined } - avatarTooltipOptions={{ content: t['Click to replace photo']() }} - removeTooltipOptions={{ content: t['Remove photo']() }} data-testid="workspace-setting-avatar" removeButtonProps={{ ['data-testid' as string]: 'workspace-setting-remove-avatar-button', diff --git a/packages/frontend/core/src/components/affine/page-history-modal/history-modal.tsx b/packages/frontend/core/src/components/affine/page-history-modal/history-modal.tsx index 220fe3ef2f..8eb7150e82 100644 --- a/packages/frontend/core/src/components/affine/page-history-modal/history-modal.tsx +++ b/packages/frontend/core/src/components/affine/page-history-modal/history-modal.tsx @@ -1,7 +1,7 @@ import { Scrollable } from '@affine/component'; import { BlockSuiteEditor, - BlockSuiteFallback, + EditorLoading, } from '@affine/component/block-suite-editor'; import type { PageMode } from '@affine/core/atoms'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; @@ -147,7 +147,7 @@ const HistoryEditorPreview = ({ onModeChange={onModeChange} /> ) : ( - + )}
); @@ -410,7 +410,7 @@ export const PageHistoryModal = ({ return ( - }> + }> { const t = useAFFiNEI18N(); const { appSettings, updateSettings } = useAppSettingHelper(); + const { toggleAutoCheck, toggleAutoDownload } = useAppUpdater(); + const channel = runtimeConfig.appBuildType; + const appIcon = appIconMap[channel]; + const appName = appNames[channel]; + + const onSwitchAutoCheck = useCallback( + (checked: boolean) => { + toggleAutoCheck(checked); + updateSettings('autoCheckUpdate', checked); + }, + [toggleAutoCheck, updateSettings] + ); + + const onSwitchAutoDownload = useCallback( + (checked: boolean) => { + toggleAutoDownload(checked); + updateSettings('autoDownloadUpdate', checked); + }, + [toggleAutoDownload, updateSettings] + ); + return ( <> { /> + className={styles.appImageRow} + > + {appName} + - {runtimeConfig.enableNewSettingUnstableApi && environment.isDesktop ? ( + {environment.isDesktop ? ( <> - + updateSettings('autoCheckUpdate', checked)} + onChange={onSwitchAutoCheck} /> { ]()} > updateSettings('autoCheckUpdate', checked)} + checked={appSettings.autoDownloadUpdate} + onChange={onSwitchAutoDownload} /> { { { -
+
{relatedLinks.map(({ icon, title, link }) => { return (
{ window.open(link, '_blank'); }} @@ -107,7 +132,7 @@ export const AboutAffine = () => { { { + const t = useAFFiNEI18N(); + const isCheckingForUpdates = useAtomValue(isCheckingForUpdatesAtom); + const updateAvailable = useAtomValue(updateAvailableAtom); + const updateReady = useAtomValue(updateReadyAtom); + const downloadProgress = useAtomValue(downloadProgressAtom); + + const buttonLabel = useMemo(() => { + if (updateAvailable && downloadProgress === null) { + return t['com.affine.aboutAFFiNE.checkUpdate.button.download'](); + } + if (updateReady) { + return t['com.affine.aboutAFFiNE.checkUpdate.button.restart'](); + } + if ( + checkUpdateStatus === CheckUpdateStatus.LATEST || + checkUpdateStatus === CheckUpdateStatus.ERROR + ) { + return t['com.affine.aboutAFFiNE.checkUpdate.button.retry'](); + } + return t['com.affine.aboutAFFiNE.checkUpdate.button.check'](); + }, [checkUpdateStatus, downloadProgress, t, updateAvailable, updateReady]); + + const subtitleLabel = useMemo(() => { + if (updateAvailable && downloadProgress === null) { + return t['com.affine.aboutAFFiNE.checkUpdate.subtitle.update-available']({ + version: updateAvailable.version, + }); + } else if (isCheckingForUpdates) { + return t['com.affine.aboutAFFiNE.checkUpdate.subtitle.checking'](); + } else if (updateAvailable && downloadProgress !== null) { + return t['com.affine.aboutAFFiNE.checkUpdate.subtitle.downloading'](); + } else if (updateReady) { + return t['com.affine.aboutAFFiNE.checkUpdate.subtitle.restart'](); + } else if (checkUpdateStatus === CheckUpdateStatus.ERROR) { + return t['com.affine.aboutAFFiNE.checkUpdate.subtitle.error'](); + } else if (checkUpdateStatus === CheckUpdateStatus.LATEST) { + return t['com.affine.aboutAFFiNE.checkUpdate.subtitle.latest'](); + } + return t['com.affine.aboutAFFiNE.checkUpdate.subtitle.check'](); + }, [ + checkUpdateStatus, + downloadProgress, + isCheckingForUpdates, + t, + updateAvailable, + updateReady, + ]); + + const subtitle = useMemo(() => { + return ( + + {isCheckingForUpdates ? : null} + {subtitleLabel} + + ); + }, [ + checkUpdateStatus, + downloadProgress, + isCheckingForUpdates, + subtitleLabel, + updateAvailable, + updateReady, + ]); + + return { subtitle, buttonLabel }; +}; + +export const UpdateCheckSection = () => { + const t = useAFFiNEI18N(); + const { checkForUpdates, downloadUpdate, quitAndInstall } = useAppUpdater(); + const updateAvailable = useAtomValue(updateAvailableAtom); + const updateReady = useAtomValue(updateReadyAtom); + const downloadProgress = useAtomValue(downloadProgressAtom); + const [checkUpdateStatus, setCheckUpdateStatus] = useState( + CheckUpdateStatus.UNCHECK + ); + + const { buttonLabel, subtitle } = useUpdateStatusLabels(checkUpdateStatus); + + const asyncCheckForUpdates = useAsyncCallback(async () => { + let statusCheck = CheckUpdateStatus.UNCHECK; + try { + const status = await checkForUpdates(); + + if (status === null) { + statusCheck = CheckUpdateStatus.ERROR; + } else if (status === false) { + statusCheck = CheckUpdateStatus.LATEST; + } else if (typeof status === 'string') { + statusCheck = CheckUpdateStatus.UPDATE_AVAILABLE; + } + } catch (e) { + console.error(e); + statusCheck = CheckUpdateStatus.ERROR; + } finally { + setCheckUpdateStatus(statusCheck); + } + }, [checkForUpdates]); + + const handleClick = useCallback(() => { + if (updateAvailable && downloadProgress === null) { + return downloadUpdate(); + } + if (updateReady) { + return quitAndInstall(); + } + asyncCheckForUpdates(); + }, [ + asyncCheckForUpdates, + downloadProgress, + downloadUpdate, + quitAndInstall, + updateAvailable, + updateReady, + ]); + + return ( + + + + ); +}; diff --git a/packages/frontend/core/src/components/affine/setting-modal/general-setting/plans/index.tsx b/packages/frontend/core/src/components/affine/setting-modal/general-setting/plans/index.tsx index c7cda5e661..ff6f8555ba 100644 --- a/packages/frontend/core/src/components/affine/setting-modal/general-setting/plans/index.tsx +++ b/packages/frontend/core/src/components/affine/setting-modal/general-setting/plans/index.tsx @@ -84,8 +84,7 @@ const Settings = () => { scrollWrapper.current.getBoundingClientRect().left - parseInt(wrapperComputedStyle.paddingLeft) : 0; - const appeared = - scrollWrapper.current.getAttribute('data-appeared') === 'true'; + const appeared = scrollWrapper.current.dataset.appeared === 'true'; const animationFrameId = requestAnimationFrame(() => { scrollWrapper.current?.scrollTo({ behavior: appeared ? 'smooth' : 'instant', diff --git a/packages/frontend/core/src/components/affine/setting-modal/general-setting/plans/plan-card.tsx b/packages/frontend/core/src/components/affine/setting-modal/general-setting/plans/plan-card.tsx index 16074b71dc..f8f415ee69 100644 --- a/packages/frontend/core/src/components/affine/setting-modal/general-setting/plans/plan-card.tsx +++ b/packages/frontend/core/src/components/affine/setting-modal/general-setting/plans/plan-card.tsx @@ -179,7 +179,7 @@ export const PlanCard = (props: PlanCardProps) => { {detail.benefits.map((content, i) => (
- {detail.type == 'dynamic' ? ( + {detail.type === 'dynamic' ? ( ) : ( ) : null} - {generalSettingList.find(v => v.key === activeTab) ? ( + {generalSettingList.some(v => v.key === activeTab) ? ( ) : null} {activeTab === 'account' && loginStatus === 'authenticated' ? ( diff --git a/packages/frontend/core/src/components/affine/setting-modal/setting-sidebar/index.tsx b/packages/frontend/core/src/components/affine/setting-modal/setting-sidebar/index.tsx index 337439b457..030b08fcc4 100644 --- a/packages/frontend/core/src/components/affine/setting-modal/setting-sidebar/index.tsx +++ b/packages/frontend/core/src/components/affine/setting-modal/setting-sidebar/index.tsx @@ -38,16 +38,20 @@ import { export type UserInfoProps = { onAccountSettingClick: () => void; + active?: boolean; }; export const UserInfo = ({ onAccountSettingClick, + active, }: UserInfoProps): ReactElement => { const user = useCurrentUser(); return (
@@ -163,7 +167,10 @@ export const SettingSidebar = ({ {runtimeConfig.enableCloud && loginStatus === 'authenticated' ? ( - + ) : null}
diff --git a/packages/frontend/core/src/components/affine/setting-modal/setting-sidebar/style.css.ts b/packages/frontend/core/src/components/affine/setting-modal/setting-sidebar/style.css.ts index 7b812e8f4f..08abd856ad 100644 --- a/packages/frontend/core/src/components/affine/setting-modal/setting-sidebar/style.css.ts +++ b/packages/frontend/core/src/components/affine/setting-modal/setting-sidebar/style.css.ts @@ -105,6 +105,11 @@ export const accountButton = style({ ':hover': { background: 'var(--affine-hover-color)', }, + selectors: { + '&.active': { + background: 'var(--affine-hover-color)', + }, + }, }); globalStyle(`${accountButton} .avatar`, { diff --git a/packages/frontend/core/src/components/affine/share-page-modal/share-menu/index.css.ts b/packages/frontend/core/src/components/affine/share-page-modal/share-menu/index.css.ts index 4cf8ffb4f8..e8d56153eb 100644 --- a/packages/frontend/core/src/components/affine/share-page-modal/share-menu/index.css.ts +++ b/packages/frontend/core/src/components/affine/share-page-modal/share-menu/index.css.ts @@ -112,9 +112,6 @@ export const radioButton = style({ }, }, }); -export const spanStyle = style({ - padding: '4px 20px', -}); export const disableSharePage = style({ color: 'var(--affine-error-color)', diff --git a/packages/frontend/core/src/components/affine/share-page-modal/share-menu/share-page.tsx b/packages/frontend/core/src/components/affine/share-page-modal/share-menu/share-page.tsx index d68479460b..250bd718cd 100644 --- a/packages/frontend/core/src/components/affine/share-page-modal/share-menu/share-page.tsx +++ b/packages/frontend/core/src/components/affine/share-page-modal/share-menu/share-page.tsx @@ -174,18 +174,10 @@ export const AffineSharePage = (props: ShareMenuProps) => { value={mode} onValueChange={onShareModeChange} > - + {t['com.affine.pageMode.page']()} - + {t['com.affine.pageMode.edgeless']()} diff --git a/packages/frontend/core/src/components/blocksuite/block-suite-header-title/operation-menu.tsx b/packages/frontend/core/src/components/blocksuite/block-suite-header-title/operation-menu.tsx index 5d19c820da..2fa9a63ad9 100644 --- a/packages/frontend/core/src/components/blocksuite/block-suite-header-title/operation-menu.tsx +++ b/packages/frontend/core/src/components/blocksuite/block-suite-header-title/operation-menu.tsx @@ -20,23 +20,15 @@ import { MenuItem, MenuSeparator, } from '@toeverything/components/menu'; -import { useAsyncCallback } from '@toeverything/hooks/affine-async-hooks'; -import { - useBlockSuitePageMeta, - usePageMetaHelper, -} from '@toeverything/hooks/use-block-suite-page-meta'; -import { useBlockSuiteWorkspaceHelper } from '@toeverything/hooks/use-block-suite-workspace-helper'; -import { useAtomValue, useSetAtom } from 'jotai'; +import { useBlockSuitePageMeta } from '@toeverything/hooks/use-block-suite-page-meta'; +import { useAtomValue } from 'jotai'; import { useCallback, useRef, useState } from 'react'; -import { applyUpdate, encodeStateAsUpdate } from 'yjs'; -import { setPageModeAtom } from '../../../atoms'; import { currentModeAtom } from '../../../atoms/mode'; import { useBlockSuiteMetaHelper } from '../../../hooks/affine/use-block-suite-meta-helper'; import { useExportPage } from '../../../hooks/affine/use-export-page'; import { useTrashModalHelper } from '../../../hooks/affine/use-trash-modal-helper'; import { useCurrentWorkspace } from '../../../hooks/current/use-current-workspace'; -import { useNavigateHelper } from '../../../hooks/use-navigate-helper'; import { toast } from '../../../utils'; import { PageHistoryModal } from '../../affine/page-history-modal/history-modal'; import { HeaderDropDownButton } from '../../pure/header-drop-down-button'; @@ -50,7 +42,6 @@ type PageMenuProps = { export const PageMenu = ({ rename, pageId }: PageMenuProps) => { const t = useAFFiNEI18N(); const ref = useRef(null); - const { openPage } = useNavigateHelper(); // fixme(himself65): remove these hooks ASAP const [workspace] = useCurrentWorkspace(); @@ -64,11 +55,9 @@ export const PageMenu = ({ rename, pageId }: PageMenuProps) => { const currentMode = useAtomValue(currentModeAtom); const favorite = pageMeta.favorite ?? false; - const { setPageMeta, setPageTitle } = usePageMetaHelper(blockSuiteWorkspace); - const { togglePageMode, toggleFavorite } = + const { togglePageMode, toggleFavorite, duplicate } = useBlockSuiteMetaHelper(blockSuiteWorkspace); const { importFile } = usePageHelper(blockSuiteWorkspace); - const { createPage } = useBlockSuiteWorkspaceHelper(blockSuiteWorkspace); const { setTrashModal } = useTrashModalHelper(blockSuiteWorkspace); const [historyModalOpen, setHistoryModalOpen] = useState(false); @@ -107,34 +96,11 @@ export const PageMenu = ({ rename, pageId }: PageMenuProps) => { }; const exportHandler = useExportPage(currentPage); - const setPageMode = useSetAtom(setPageModeAtom); - const duplicate = useAsyncCallback(async () => { - const currentPageMeta = currentPage.meta; - const newPage = createPage(); - await newPage.waitForLoaded(); + const handleDuplicate = useCallback(() => { + duplicate(pageId); + }, [duplicate, pageId]); - const update = encodeStateAsUpdate(currentPage.spaceDoc); - applyUpdate(newPage.spaceDoc, update); - - setPageMeta(newPage.id, { - tags: currentPageMeta.tags, - favorite: currentPageMeta.favorite, - }); - setPageMode(newPage.id, currentMode); - setPageTitle(newPage.id, `${currentPageMeta.title}(1)`); - openPage(blockSuiteWorkspace.id, newPage.id); - }, [ - blockSuiteWorkspace.id, - createPage, - currentMode, - currentPage.meta, - currentPage.spaceDoc, - openPage, - setPageMeta, - setPageMode, - setPageTitle, - ]); const EditMenu = ( <> { } data-testid="editor-option-menu-duplicate" - onSelect={duplicate} + onSelect={handleDuplicate} style={menuItemStyle} > {t['com.affine.header.option.duplicate']()} diff --git a/packages/frontend/core/src/components/blocksuite/block-suite-header-title/styles.css.ts b/packages/frontend/core/src/components/blocksuite/block-suite-header-title/styles.css.ts index f59ac8413b..d92135036c 100644 --- a/packages/frontend/core/src/components/blocksuite/block-suite-header-title/styles.css.ts +++ b/packages/frontend/core/src/components/blocksuite/block-suite-header-title/styles.css.ts @@ -32,6 +32,8 @@ export const titleInput = style({ borderRadius: '8px', height: '32px', padding: '6px 8px', + borderColor: 'var(--affine-primary-color)', + boxShadow: 'var(--affine-active-shadow)', }, }, }); diff --git a/packages/frontend/core/src/components/blocksuite/block-suite-page-list/utils.tsx b/packages/frontend/core/src/components/blocksuite/block-suite-page-list/utils.tsx index ea0287e157..d0f3f7b467 100644 --- a/packages/frontend/core/src/components/blocksuite/block-suite-page-list/utils.tsx +++ b/packages/frontend/core/src/components/blocksuite/block-suite-page-list/utils.tsx @@ -14,11 +14,14 @@ export const usePageHelper = (blockSuiteWorkspace: BlockSuiteWorkspace) => { const { openPage, jumpToSubPath } = useNavigateHelper(); const { createPage } = useBlockSuiteWorkspaceHelper(blockSuiteWorkspace); const pageSettings = useAtomValue(pageSettingsAtom); + const isPreferredEdgeless = useCallback( (pageId: string) => pageSettings[pageId]?.mode === 'edgeless', [pageSettings] ); + const setPageMode = useSetAtom(setPageModeAtom); + const createPageAndOpen = useCallback( (mode?: 'page' | 'edgeless') => { const page = createPage(); @@ -31,9 +34,11 @@ export const usePageHelper = (blockSuiteWorkspace: BlockSuiteWorkspace) => { }, [blockSuiteWorkspace.id, createPage, openPage, setPageMode] ); + const createEdgelessAndOpen = useCallback(() => { return createPageAndOpen('edgeless'); }, [createPageAndOpen]); + const importFileAndOpen = useAsyncCallback(async () => { const { showImportModal } = await import('@blocksuite/blocks'); const onSuccess = (pageIds: string[], isWorkspaceFile: boolean) => { @@ -55,6 +60,7 @@ export const usePageHelper = (blockSuiteWorkspace: BlockSuiteWorkspace) => { }; showImportModal({ workspace: blockSuiteWorkspace, onSuccess }); }, [blockSuiteWorkspace, openPage, jumpToSubPath]); + return useMemo(() => { return { createPage: createPageAndOpen, diff --git a/packages/frontend/core/src/components/page-detail-editor.tsx b/packages/frontend/core/src/components/page-detail-editor.tsx index 8447ca0d73..ca3dc916d9 100644 --- a/packages/frontend/core/src/components/page-detail-editor.tsx +++ b/packages/frontend/core/src/components/page-detail-editor.tsx @@ -174,10 +174,10 @@ const EditorWrapper = memo(function EditorWrapper({ div.setAttribute('plugin-id', id); const cleanup = editorItem(div, editor); assertExists(parent); - document.body.appendChild(div); + document.body.append(div); return () => { cleanup(); - document.body.removeChild(div); + div.remove(); }; }); }); @@ -235,14 +235,14 @@ const PluginContentAdapter = memo( } const div = document.createElement('div'); const cleanup = windowItem(div); - root.appendChild(div); + root.append(div); if (abortController.signal.aborted) { cleanup(); - root.removeChild(div); + div.remove(); } else { const cl = () => { cleanup(); - root.removeChild(div); + div.remove(); }; const dispose = addCleanup(pluginName, cl); abortController.signal.addEventListener('abort', () => { diff --git a/packages/frontend/core/src/components/pure/cmdk/main.css.ts b/packages/frontend/core/src/components/pure/cmdk/main.css.ts index fc23684cb1..a0d9264b37 100644 --- a/packages/frontend/core/src/components/pure/cmdk/main.css.ts +++ b/packages/frontend/core/src/components/pure/cmdk/main.css.ts @@ -12,6 +12,7 @@ export const searchInput = style({ color: 'var(--affine-text-primary-color)', fontSize: 'var(--affine-font-h-5)', padding: '21px 24px', + marginBottom: '8px', width: '100%', borderBottom: '1px solid var(--affine-border-color)', flexShrink: 0, @@ -114,13 +115,6 @@ globalStyle(`${root} [cmdk-group][hidden]`, { display: 'none', }); -globalStyle( - `${root} [cmdk-group]:not([hidden]):first-of-type [cmdk-group-heading]`, - { - paddingTop: 16, - } -); - globalStyle(`${root} [cmdk-list]`, { maxHeight: 400, minHeight: 120, @@ -187,3 +181,11 @@ globalStyle( color: 'var(--affine-error-color)', } ); + +export const resultGroupHeader = style({ + padding: '8px', + color: 'var(--affine-text-secondary-color)', + fontSize: 'var(--affine-font-xs)', + fontWeight: 600, + lineHeight: '1.67', +}); diff --git a/packages/frontend/core/src/components/pure/cmdk/main.tsx b/packages/frontend/core/src/components/pure/cmdk/main.tsx index 4252265658..918b78e539 100644 --- a/packages/frontend/core/src/components/pure/cmdk/main.tsx +++ b/packages/frontend/core/src/components/pure/cmdk/main.tsx @@ -1,11 +1,12 @@ import { Command } from '@affine/cmdk'; +import { useCommandState } from '@affine/cmdk'; import { formatDate } from '@affine/component/page-list'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; import type { PageMeta } from '@blocksuite/store'; import { useAsyncCallback } from '@toeverything/hooks/affine-async-hooks'; import type { CommandCategory } from '@toeverything/infra/command'; import clsx from 'clsx'; -import { useAtom } from 'jotai'; +import { useAtom, useAtomValue } from 'jotai'; import { Suspense, useLayoutEffect, useMemo, useState } from 'react'; import { @@ -71,7 +72,7 @@ const QuickSearchGroup = ({ ); return ( - + {commands.map(command => { const label = typeof command.label === 'string' @@ -129,18 +130,43 @@ const QuickSearchCommands = ({ }: { onOpenChange?: (open: boolean) => void; }) => { + const t = useAFFiNEI18N(); const groups = useCMDKCommandGroups(); - return groups.map(([category, commands]) => { - return ( - - ); - }); + const query = useAtomValue(cmdkQueryAtom); + const resultCount = useCommandState(state => state.filtered.count); + const resultGroupHeader = useMemo(() => { + if (query) { + return ( +
+ { + // hack: use resultCount to determine if it is creation or results + // because the creation(as 2 results) is always shown at the top when there is no result + resultCount === 2 + ? t['com.affine.cmdk.affine.category.affine.creation']() + : t['com.affine.cmdk.affine.category.results']() + } +
+ ); + } + return null; + }, [query, resultCount, t]); + + return ( + <> + {resultGroupHeader} + {groups.map(([category, commands]) => { + return ( + + ); + })} + + ); }; export const CMDKContainer = ({ diff --git a/packages/frontend/core/src/components/pure/plugin-header/index.tsx b/packages/frontend/core/src/components/pure/plugin-header/index.tsx index 3484f3b735..6156691546 100644 --- a/packages/frontend/core/src/components/pure/plugin-header/index.tsx +++ b/packages/frontend/core/src/components/pure/plugin-header/index.tsx @@ -25,12 +25,12 @@ export const PluginHeader = () => { div.setAttribute('plugin-id', pluginName); startTransition(() => { const cleanup = create(div); - root.appendChild(div); + root.append(div); addCleanup(pluginName, () => { pluginsRef.current = pluginsRef.current.filter( name => name !== pluginName ); - root.removeChild(div); + div.remove(); cleanup(); }); }); diff --git a/packages/frontend/core/src/components/pure/workspace-mode-filter-tab/index.css.ts b/packages/frontend/core/src/components/pure/workspace-mode-filter-tab/index.css.ts new file mode 100644 index 0000000000..9a6505aa34 --- /dev/null +++ b/packages/frontend/core/src/components/pure/workspace-mode-filter-tab/index.css.ts @@ -0,0 +1,8 @@ +import { style } from '@vanilla-extract/css'; + +export const filterTab = style({ + fontSize: 'var(--affine-font-xs)', + fontWeight: 600, + textTransform: 'capitalize', + minWidth: '91px', +}); diff --git a/packages/frontend/core/src/components/pure/workspace-mode-filter-tab/index.tsx b/packages/frontend/core/src/components/pure/workspace-mode-filter-tab/index.tsx index c546fc270f..61b8078c61 100644 --- a/packages/frontend/core/src/components/pure/workspace-mode-filter-tab/index.tsx +++ b/packages/frontend/core/src/components/pure/workspace-mode-filter-tab/index.tsx @@ -3,6 +3,7 @@ import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { useAtom } from 'jotai'; import { allPageModeSelectAtom } from '../../../atoms'; +import * as styles from './index.css'; export const WorkspaceModeFilterTab = () => { const t = useAFFiNEI18N(); @@ -15,16 +16,14 @@ export const WorkspaceModeFilterTab = () => { }; return ( - - + + {t['com.affine.pageMode.all']()} - {t['com.affine.pageMode.page']()} - + + {t['com.affine.pageMode.page']()} + + {t['com.affine.pageMode.edgeless']()} diff --git a/packages/frontend/core/src/components/pure/workspace-slider-bar/collections/collections-list.tsx b/packages/frontend/core/src/components/pure/workspace-slider-bar/collections/collections-list.tsx index c369cdd551..1dfa9edf4b 100644 --- a/packages/frontend/core/src/components/pure/workspace-slider-bar/collections/collections-list.tsx +++ b/packages/frontend/core/src/components/pure/workspace-slider-bar/collections/collections-list.tsx @@ -85,7 +85,7 @@ const CollectionRenderer = ({ async (id: string) => { await setting.updateCollection({ ...collection, - allowList: collection.allowList?.filter(v => v != id), + allowList: collection.allowList?.filter(v => v !== id), }); }, [collection, setting] diff --git a/packages/frontend/core/src/components/root-app-sidebar/index.tsx b/packages/frontend/core/src/components/root-app-sidebar/index.tsx index 3b82e54e26..e08826e098 100644 --- a/packages/frontend/core/src/components/root-app-sidebar/index.tsx +++ b/packages/frontend/core/src/components/root-app-sidebar/index.tsx @@ -20,6 +20,11 @@ import type { Page } from '@blocksuite/store'; import { useDroppable } from '@dnd-kit/core'; import { Menu } from '@toeverything/components/menu'; import { useAsyncCallback } from '@toeverything/hooks/affine-async-hooks'; +import { + isAutoCheckUpdateAtom, + isAutoDownloadUpdateAtom, + useAppUpdater, +} from '@toeverything/hooks/use-app-updater'; import { useAtom, useAtomValue } from 'jotai'; import type { HTMLAttributes, ReactElement } from 'react'; import { forwardRef, useCallback, useEffect, useMemo } from 'react'; @@ -102,6 +107,10 @@ export const RootAppSidebar = ({ }: RootAppSidebarProps): ReactElement => { const currentWorkspaceId = currentWorkspace.id; const { appSettings } = useAppSettingHelper(); + const { toggleAutoCheck, toggleAutoDownload } = useAppUpdater(); + const { autoCheckUpdate, autoDownloadUpdate } = appSettings; + const isAutoDownload = useAtomValue(isAutoDownloadUpdateAtom); + const isAutoCheck = useAtomValue(isAutoCheckUpdateAtom); const blockSuiteWorkspace = currentWorkspace.blockSuiteWorkspace; const t = useAFFiNEI18N(); const [openUserWorkspaceList, setOpenUserWorkspaceList] = useAtom( @@ -150,6 +159,26 @@ export const RootAppSidebar = ({ } }, [sidebarOpen]); + useEffect(() => { + if (!environment.isDesktop) { + return; + } + + if (isAutoCheck !== autoCheckUpdate) { + toggleAutoCheck(autoCheckUpdate); + } + if (isAutoDownload !== autoDownloadUpdate) { + toggleAutoDownload(autoDownloadUpdate); + } + }, [ + autoCheckUpdate, + autoDownloadUpdate, + isAutoCheck, + isAutoDownload, + toggleAutoCheck, + toggleAutoDownload, + ]); + const [history, setHistory] = useHistoryAtom(); const router = useMemo(() => { return { diff --git a/packages/frontend/core/src/hooks/affine/use-block-suite-meta-helper.ts b/packages/frontend/core/src/hooks/affine/use-block-suite-meta-helper.ts index 53dd028296..2898844a4e 100644 --- a/packages/frontend/core/src/hooks/affine/use-block-suite-meta-helper.ts +++ b/packages/frontend/core/src/hooks/affine/use-block-suite-meta-helper.ts @@ -1,25 +1,31 @@ +import { useAsyncCallback } from '@toeverything/hooks/affine-async-hooks'; import { useBlockSuitePageMeta, usePageMetaHelper, } from '@toeverything/hooks/use-block-suite-page-meta'; +import { useBlockSuiteWorkspaceHelper } from '@toeverything/hooks/use-block-suite-workspace-helper'; import { useAtomValue, useSetAtom } from 'jotai'; import { useCallback } from 'react'; +import { applyUpdate, encodeStateAsUpdate } from 'yjs'; import { setPageModeAtom } from '../../atoms'; import { currentModeAtom } from '../../atoms/mode'; import type { BlockSuiteWorkspace } from '../../shared'; import { getWorkspaceSetting } from '../../utils/workspace-setting'; +import { useNavigateHelper } from '../use-navigate-helper'; import { useReferenceLinkHelper } from './use-reference-link-helper'; export function useBlockSuiteMetaHelper( blockSuiteWorkspace: BlockSuiteWorkspace ) { - const { setPageMeta, getPageMeta, setPageReadonly } = + const { setPageMeta, getPageMeta, setPageReadonly, setPageTitle } = usePageMetaHelper(blockSuiteWorkspace); const { addReferenceLink } = useReferenceLinkHelper(blockSuiteWorkspace); const metas = useBlockSuitePageMeta(blockSuiteWorkspace); const setPageMode = useSetAtom(setPageModeAtom); const currentMode = useAtomValue(currentModeAtom); + const { createPage } = useBlockSuiteWorkspaceHelper(blockSuiteWorkspace); + const { openPage } = useNavigateHelper(); const switchToPageMode = useCallback( (pageId: string) => { @@ -79,7 +85,7 @@ export function useBlockSuiteMetaHelper( setPageMeta(pageId, { trash: true, - trashDate: +new Date(), + trashDate: Date.now(), trashRelate: isRoot ? parentMeta?.id : undefined, }); setPageReadonly(pageId, true); @@ -140,6 +146,40 @@ export function useBlockSuiteMetaHelper( [setPageMeta] ); + const duplicate = useAsyncCallback( + async (pageId: string) => { + const currentPageMeta = getPageMeta(pageId); + const newPage = createPage(); + const currentPage = blockSuiteWorkspace.getPage(pageId); + + await newPage.waitForLoaded(); + if (!currentPageMeta || !currentPage) { + return; + } + + const update = encodeStateAsUpdate(currentPage.spaceDoc); + applyUpdate(newPage.spaceDoc, update); + + setPageMeta(newPage.id, { + tags: currentPageMeta.tags, + favorite: currentPageMeta.favorite, + }); + setPageMode(newPage.id, currentMode); + setPageTitle(newPage.id, `${currentPageMeta.title}(1)`); + openPage(blockSuiteWorkspace.id, newPage.id); + }, + [ + blockSuiteWorkspace, + createPage, + currentMode, + getPageMeta, + openPage, + setPageMeta, + setPageMode, + setPageTitle, + ] + ); + return { switchToPageMode, switchToEdgelessMode, @@ -155,5 +195,7 @@ export function useBlockSuiteMetaHelper( removeToTrash, restoreFromTrash, permanentlyDeletePage, + + duplicate, }; } diff --git a/packages/frontend/core/src/hooks/affine/use-register-blocksuite-editor-commands.tsx b/packages/frontend/core/src/hooks/affine/use-register-blocksuite-editor-commands.tsx index 62ddb8b48b..73a8b53e62 100644 --- a/packages/frontend/core/src/hooks/affine/use-register-blocksuite-editor-commands.tsx +++ b/packages/frontend/core/src/hooks/affine/use-register-blocksuite-editor-commands.tsx @@ -41,7 +41,7 @@ export function useRegisterBlocksuiteEditorCommands( })); }, [pageId, setPageHistoryModalState]); - const { togglePageMode, toggleFavorite, restoreFromTrash } = + const { togglePageMode, toggleFavorite, restoreFromTrash, duplicate } = useBlockSuiteMetaHelper(blockSuiteWorkspace); const exportHandler = useExportPage(currentPage); const { setTrashModal } = useTrashModalHelper(blockSuiteWorkspace); @@ -125,6 +125,19 @@ export function useRegisterBlocksuiteEditorCommands( }) ); + unsubs.push( + registerAffineCommand({ + id: `editor:${mode}-duplicate`, + preconditionStrategy, + category: `editor:${mode}`, + icon: mode === 'page' ? : , + label: t['com.affine.header.option.duplicate'](), + run() { + duplicate(pageId); + }, + }) + ); + unsubs.push( registerAffineCommand({ id: `editor:${mode}-export-to-pdf`, @@ -234,5 +247,6 @@ export function useRegisterBlocksuiteEditorCommands( trash, isCloudWorkspace, openHistoryModal, + duplicate, ]); } diff --git a/packages/frontend/core/src/layouts/workspace-layout.tsx b/packages/frontend/core/src/layouts/workspace-layout.tsx index 12bbf066a6..3c4a8d3498 100644 --- a/packages/frontend/core/src/layouts/workspace-layout.tsx +++ b/packages/frontend/core/src/layouts/workspace-layout.tsx @@ -194,7 +194,8 @@ export const WorkspaceLayoutInner = ({ const blockVersions = meta.get('blockVersions'); if ( !(blockVersions instanceof YMap) && - blockVersions != null && + blockVersions !== null && + blockVersions !== undefined && typeof blockVersions === 'object' ) { meta.set( diff --git a/packages/frontend/core/src/pages/open-app.tsx b/packages/frontend/core/src/pages/open-app.tsx index 7e6df768e6..b26da450ff 100644 --- a/packages/frontend/core/src/pages/open-app.tsx +++ b/packages/frontend/core/src/pages/open-app.tsx @@ -37,14 +37,14 @@ const schemaToChanel = { 'affine-dev': 'canary', // dev does not have a dedicated app. use canary as the placeholder. } as Record; -const appIconMap = { +export const appIconMap = { stable: '/imgs/app-icon-stable.ico', canary: '/imgs/app-icon-canary.ico', beta: '/imgs/app-icon-beta.ico', internal: '/imgs/app-icon-internal.ico', } satisfies Record; -const appNames = { +export const appNames = { stable: 'AFFiNE', canary: 'AFFiNE Canary', beta: 'AFFiNE Beta', diff --git a/packages/frontend/core/src/pages/workspace/collection.tsx b/packages/frontend/core/src/pages/workspace/collection.tsx index ef2f330d6f..f66bb5fe75 100644 --- a/packages/frontend/core/src/pages/workspace/collection.tsx +++ b/packages/frontend/core/src/pages/workspace/collection.tsx @@ -118,6 +118,7 @@ const Placeholder = ({ collection }: { collection: Collection }) => { alignItems: 'center', gap: 8, padding: '12px 24px', + fontSize: 'var(--affine-font-xs)', }} >
{ color: 'var(--affine-text-secondary-color)', }} > - + {t['com.affine.collection.allCollections']()}
/
diff --git a/packages/frontend/core/src/utils/user-setting.ts b/packages/frontend/core/src/utils/user-setting.ts index 6493047649..3a668b6459 100644 --- a/packages/frontend/core/src/utils/user-setting.ts +++ b/packages/frontend/core/src/utils/user-setting.ts @@ -5,8 +5,8 @@ import type { Map as YMap } from 'yjs'; import { Doc as YDoc } from 'yjs'; export class UserSetting { constructor( - private workspace: Workspace, - private userId: string + private readonly workspace: Workspace, + private readonly userId: string ) {} get setting(): YDoc { diff --git a/packages/frontend/core/src/utils/workspace-setting.ts b/packages/frontend/core/src/utils/workspace-setting.ts index 18d0344588..dacdc92ce9 100644 --- a/packages/frontend/core/src/utils/workspace-setting.ts +++ b/packages/frontend/core/src/utils/workspace-setting.ts @@ -13,7 +13,7 @@ const COLLECTIONS_TRASH_KEY = 'collections_trash'; const SETTING_KEY = 'setting'; export class WorkspaceSetting { - constructor(private workspace: Workspace) {} + constructor(private readonly workspace: Workspace) {} get doc() { return this.workspace.doc; diff --git a/packages/frontend/electron/scripts/generate-assets.ts b/packages/frontend/electron/scripts/generate-assets.ts index 20c9d38f5c..0284274d21 100755 --- a/packages/frontend/electron/scripts/generate-assets.ts +++ b/packages/frontend/electron/scripts/generate-assets.ts @@ -39,6 +39,9 @@ process.env.DISTRIBUTION = 'desktop'; const cwd = repoRootDir; +const { SKIP_NX_CACHE } = process.env; +const nxFlag = SKIP_NX_CACHE ? '--skip-nx-cache' : ''; + if (!process.env.SKIP_PLUGIN_BUILD) { spawnSync('yarn', ['build:plugins'], { stdio: 'inherit', @@ -49,7 +52,7 @@ if (!process.env.SKIP_PLUGIN_BUILD) { // step 1: build web dist if (!process.env.SKIP_WEB_BUILD) { - spawnSync('yarn', ['nx', 'build', '@affine/core'], { + spawnSync('yarn', ['nx', 'build', '@affine/core', nxFlag], { stdio: 'inherit', env: process.env, cwd, diff --git a/packages/frontend/electron/src/main/helper-process.ts b/packages/frontend/electron/src/main/helper-process.ts index ed73b096d5..23b6da1653 100644 --- a/packages/frontend/electron/src/main/helper-process.ts +++ b/packages/frontend/electron/src/main/helper-process.ts @@ -33,7 +33,7 @@ function pickAndBind( class HelperProcessManager { ready: Promise; - #process: UtilityProcess; + readonly #process: UtilityProcess; // a rpc server for the main process -> helper process rpc?: _AsyncVersionOf; diff --git a/packages/frontend/electron/src/main/updater/custom-github-provider.ts b/packages/frontend/electron/src/main/updater/custom-github-provider.ts index 55bf60102f..75a9861f6a 100644 --- a/packages/frontend/electron/src/main/updater/custom-github-provider.ts +++ b/packages/frontend/electron/src/main/updater/custom-github-provider.ts @@ -29,7 +29,7 @@ const hrefRegExp = /\/tag\/([^/]+)$/; export class CustomGitHubProvider extends BaseGitHubProvider { constructor( options: CustomPublishOptions, - private updater: AppUpdater, + private readonly updater: AppUpdater, runtimeOptions: ProviderRuntimeOptions ) { super(options as unknown as GithubOptions, 'github.com', runtimeOptions); @@ -105,7 +105,7 @@ export class CustomGitHubProvider extends BaseGitHubProvider { ); } - if (tag == null) { + if (tag === null || tag === undefined) { throw newError( `No published versions on GitHub`, 'ERR_UPDATER_NO_PUBLISHED_VERSIONS' @@ -154,7 +154,7 @@ export class CustomGitHubProvider extends BaseGitHubProvider { } const result = parseUpdateInfo(rawData, channelFile, channelFileUrl); - if (result.releaseName == null) { + if (result.releaseName === null) { result.releaseName = latestRelease.elementValueOrEmpty('title'); } diff --git a/packages/frontend/electron/src/main/updater/electron-updater.ts b/packages/frontend/electron/src/main/updater/electron-updater.ts index 74559e1a8d..7594664107 100644 --- a/packages/frontend/electron/src/main/updater/electron-updater.ts +++ b/packages/frontend/electron/src/main/updater/electron-updater.ts @@ -18,13 +18,53 @@ export const quitAndInstall = async () => { }; let lastCheckTime = 0; -export const checkForUpdates = async (force = true) => { - // check every 30 minutes (1800 seconds) at most - if (!disabled && (force || lastCheckTime + 1000 * 1800 < Date.now())) { + +let downloading = false; + +export type UpdaterConfig = { + autoCheckUpdate: boolean; + autoDownloadUpdate: boolean; +}; + +const config: UpdaterConfig = { + autoCheckUpdate: true, + autoDownloadUpdate: true, +}; + +export const getConfig = (): UpdaterConfig => { + return { ...config }; +}; + +export const setConfig = (newConfig: Partial = {}): void => { + Object.assign(config, newConfig); +}; + +export const checkForUpdates = async (force = false) => { + if (disabled) { + return; + } + + if ( + force || + (config.autoCheckUpdate && lastCheckTime + 1000 * 1800 < Date.now()) + ) { lastCheckTime = Date.now(); return await autoUpdater.checkForUpdates(); } - return void 0; + return; +}; + +export const downloadUpdate = async () => { + if (disabled) { + return; + } + downloading = true; + autoUpdater.downloadUpdate().catch(e => { + downloading = false; + logger.error('Failed to download update', e); + }); + logger.info('Update available, downloading...'); + return; }; export const registerUpdater = async () => { @@ -60,10 +100,9 @@ export const registerUpdater = async () => { autoUpdater.on('checking-for-update', () => { logger.info('Checking for update'); }); - let downloading = false; autoUpdater.on('update-available', info => { logger.info('Update available', info); - if (allowAutoUpdate && !downloading) { + if (config.autoDownloadUpdate && allowAutoUpdate && !downloading) { downloading = true; autoUpdater?.downloadUpdate().catch(e => { downloading = false; diff --git a/packages/frontend/electron/src/main/updater/index.ts b/packages/frontend/electron/src/main/updater/index.ts index 9a7d4378bc..19d24aaaf3 100644 --- a/packages/frontend/electron/src/main/updater/index.ts +++ b/packages/frontend/electron/src/main/updater/index.ts @@ -1,7 +1,14 @@ -import { app } from 'electron'; +import { app, type IpcMainInvokeEvent } from 'electron'; import type { NamespaceHandlers } from '../type'; -import { checkForUpdates, quitAndInstall } from './electron-updater'; +import { + checkForUpdates, + downloadUpdate, + getConfig, + quitAndInstall, + setConfig, + type UpdaterConfig, +} from './electron-updater'; export const updaterHandlers = { currentVersion: async () => { @@ -10,6 +17,18 @@ export const updaterHandlers = { quitAndInstall: async () => { return quitAndInstall(); }, + downloadUpdate: async () => { + return downloadUpdate(); + }, + getConfig: async (): Promise => { + return getConfig(); + }, + setConfig: async ( + _e: IpcMainInvokeEvent, + newConfig: Partial + ): Promise => { + return setConfig(newConfig); + }, checkForUpdatesAndNotify: async () => { const res = await checkForUpdates(true); if (res) { diff --git a/packages/frontend/electron/src/shared/utils.ts b/packages/frontend/electron/src/shared/utils.ts index 80d05b78da..b7e7438b1f 100644 --- a/packages/frontend/electron/src/shared/utils.ts +++ b/packages/frontend/electron/src/shared/utils.ts @@ -1,7 +1,7 @@ import type { EventBasedChannel } from 'async-call-rpc'; export function getTime() { - return new Date().getTime(); + return Date.now(); } export const isMacOS = () => { @@ -22,7 +22,7 @@ interface MessagePortLike { } export class MessageEventChannel implements EventBasedChannel { - constructor(private worker: MessagePortLike) {} + constructor(private readonly worker: MessagePortLike) {} on(listener: (data: unknown) => void) { const f = (data: unknown) => { diff --git a/packages/frontend/graphql/src/fetcher.ts b/packages/frontend/graphql/src/fetcher.ts index 4015f96e74..2c2e6f0198 100644 --- a/packages/frontend/graphql/src/fetcher.ts +++ b/packages/frontend/graphql/src/fetcher.ts @@ -255,6 +255,6 @@ export const fetchWithTraceReport = async ( requestId, ...(event ? { event } : {}), }); - return await Promise.reject(err); + throw err; } }; diff --git a/packages/frontend/graphql/src/utils.ts b/packages/frontend/graphql/src/utils.ts index fb9804c504..9c537d7521 100644 --- a/packages/frontend/graphql/src/utils.ts +++ b/packages/frontend/graphql/src/utils.ts @@ -3,7 +3,7 @@ export const TRACE_ID_BYTES = 16; export const TRACE_VERSION = '00'; export const TRACE_FLAG = '01'; -const BytesBuffer = Array(32); +const BytesBuffer = Array.from({ length: 32 }); type TraceSpan = { name: string; @@ -54,7 +54,7 @@ export class TraceReporter { private spansCache = new Array(); private reportIntervalId: number | undefined | NodeJS.Timeout; - private reportInterval = 60_000; + private readonly reportInterval = 60_000; private static instance: TraceReporter; @@ -175,7 +175,7 @@ export class TraceReporter { }; } - private initTraceReport = () => { + private readonly initTraceReport = () => { if (!this.reportIntervalId && TraceReporter.shouldReportTrace) { if (typeof window !== 'undefined') { this.reportIntervalId = window.setInterval( @@ -191,7 +191,7 @@ export class TraceReporter { } }; - private reportHandler = () => { + private readonly reportHandler = () => { if (this.spansCache.length <= 0) { clearInterval(this.reportIntervalId); this.reportIntervalId = undefined; diff --git a/packages/frontend/hooks/package.json b/packages/frontend/hooks/package.json index 84efd99933..c38eda2dd3 100644 --- a/packages/frontend/hooks/package.json +++ b/packages/frontend/hooks/package.json @@ -12,6 +12,7 @@ "lodash.debounce": "^4.0.8", "p-queue": "^7.4.1", "react": "18.2.0", + "rxjs": "^7.8.1", "swr": "2.2.4", "uuid": "^9.0.1" }, diff --git a/packages/frontend/hooks/src/use-app-updater.ts b/packages/frontend/hooks/src/use-app-updater.ts new file mode 100644 index 0000000000..adf0c8e7f0 --- /dev/null +++ b/packages/frontend/hooks/src/use-app-updater.ts @@ -0,0 +1,186 @@ +import { isBrowser } from '@affine/env/constant'; +import type { UpdateMeta } from '@toeverything/infra/type'; +import { atom, useAtomValue, useSetAtom } from 'jotai'; +import { atomWithObservable, atomWithStorage } from 'jotai/utils'; +import { useCallback, useState } from 'react'; +import { Observable } from 'rxjs'; + +function rpcToObservable< + T, + H extends () => Promise, + E extends (callback: (t: T) => void) => () => void, +>( + initialValue: T | null, + { + event, + handler, + onSubscribe, + }: { + event?: E; + handler?: H; + onSubscribe?: () => void; + } +): Observable { + return new Observable(subscriber => { + subscriber.next(initialValue); + onSubscribe?.(); + if (!isBrowser || !environment.isDesktop || !event) { + subscriber.complete(); + return; + } + handler?.() + .then(t => { + subscriber.next(t); + }) + .catch(err => { + subscriber.error(err); + }); + return event(t => { + subscriber.next(t); + }); + }); +} + +export const updateReadyAtom = atomWithObservable(() => { + return rpcToObservable(null as UpdateMeta | null, { + event: window.events?.updater.onUpdateReady, + }); +}); + +export const updateAvailableStateAtom = atom(null); + +export const updateAvailableAtom = atomWithObservable(get => { + return rpcToObservable(get(updateAvailableStateAtom), { + event: window.events?.updater.onUpdateAvailable, + onSubscribe: () => { + window.apis?.updater.checkForUpdatesAndNotify().catch(err => { + console.error(err); + }); + }, + }); +}); + +export const downloadProgressAtom = atomWithObservable(() => { + return rpcToObservable(null as number | null, { + event: window.events?.updater.onDownloadProgress, + }); +}); + +export const changelogCheckedAtom = atomWithStorage>( + 'affine:client-changelog-checked', + {} +); + +export const currentVersionAtom = atom(async () => { + if (!isBrowser) { + return null; + } + const currentVersion = await window.apis?.updater.currentVersion(); + return currentVersion; +}); + +export const currentChangelogUnreadAtom = atom(async get => { + if (!isBrowser) { + return false; + } + const mapping = get(changelogCheckedAtom); + const currentVersion = await get(currentVersionAtom); + if (currentVersion) { + return !mapping[currentVersion]; + } + return false; +}); + +export const isCheckingForUpdatesAtom = atom(false); +export const isAutoDownloadUpdateAtom = atom(true); +export const isAutoCheckUpdateAtom = atom(true); + +export const useAppUpdater = () => { + const [appQuitting, setAppQuitting] = useState(false); + const updateReady = useAtomValue(updateReadyAtom); + const setUpdateAvailableState = useSetAtom(updateAvailableStateAtom); + const setIsCheckingForUpdates = useSetAtom(isCheckingForUpdatesAtom); + const setIsAutoCheckUpdate = useSetAtom(isAutoCheckUpdateAtom); + const setIsAutoDownloadUpdate = useSetAtom(isAutoDownloadUpdateAtom); + + const quitAndInstall = useCallback(() => { + if (updateReady) { + setAppQuitting(true); + window.apis?.updater.quitAndInstall().catch(err => { + // TODO: add error toast here + console.error(err); + }); + } + }, [updateReady]); + + const checkForUpdates = useCallback(async () => { + setIsCheckingForUpdates(true); + try { + const updateInfo = await window.apis?.updater.checkForUpdatesAndNotify(); + setIsCheckingForUpdates(false); + if (updateInfo) { + const updateMeta: UpdateMeta = { + version: updateInfo.version, + allowAutoUpdate: false, + }; + setUpdateAvailableState(updateMeta); + return updateInfo.version; + } + return false; + } catch (err) { + setIsCheckingForUpdates(false); + console.error('Error checking for updates:', err); + return null; + } + }, [setIsCheckingForUpdates, setUpdateAvailableState]); + + const downloadUpdate = useCallback(() => { + window.apis?.updater + .downloadUpdate() + .then(() => {}) + .catch(err => { + console.error('Error downloading update:', err); + }); + }, []); + + const toggleAutoDownload = useCallback( + (enable: boolean) => { + window.apis?.updater + .setConfig({ + autoDownloadUpdate: enable, + }) + .then(() => { + setIsAutoDownloadUpdate(enable); + }) + .catch(err => { + console.error('Error setting auto download:', err); + }); + }, + [setIsAutoDownloadUpdate] + ); + + const toggleAutoCheck = useCallback( + (enable: boolean) => { + window.apis?.updater + .setConfig({ + autoCheckUpdate: enable, + }) + .then(() => { + setIsAutoCheckUpdate(enable); + }) + .catch(err => { + console.error('Error setting auto check:', err); + }); + }, + [setIsAutoCheckUpdate] + ); + + return { + quitAndInstall, + appQuitting, + checkForUpdates, + downloadUpdate, + toggleAutoDownload, + toggleAutoCheck, + }; +}; diff --git a/packages/frontend/hooks/src/use-block-suite-workspace-avatar-url.ts b/packages/frontend/hooks/src/use-block-suite-workspace-avatar-url.ts index efbff4e7b4..6da97d7ec5 100644 --- a/packages/frontend/hooks/src/use-block-suite-workspace-avatar-url.ts +++ b/packages/frontend/hooks/src/use-block-suite-workspace-avatar-url.ts @@ -2,7 +2,7 @@ import { assertExists } from '@blocksuite/global/utils'; import type { Workspace } from '@blocksuite/store'; import reduce from 'image-blob-reduce'; import { useCallback, useEffect, useState } from 'react'; -import useSWR from 'swr'; +import useSWRImmutable from 'swr/immutable'; // validate and reduce image size and return as file export const validateAndReduceImage = async (file: File): Promise => { @@ -52,7 +52,7 @@ export function useBlockSuiteWorkspaceAvatarUrl( if (url !== blockSuiteWorkspace.meta.avatar) { set(blockSuiteWorkspace.meta.avatar); } - const { data: avatar, mutate } = useSWR(url, { + const { data: avatar, mutate } = useSWRImmutable(url, { fetcher: async avatar => { assertExists(blockSuiteWorkspace); const blobs = blockSuiteWorkspace.blob; @@ -62,8 +62,7 @@ export function useBlockSuiteWorkspaceAvatarUrl( } return null; }, - suspense: true, - fallbackData: null, + suspense: false, }); const setAvatar = useCallback( diff --git a/packages/frontend/i18n/src/index.ts b/packages/frontend/i18n/src/index.ts index c6988d6098..8987941ef5 100644 --- a/packages/frontend/i18n/src/index.ts +++ b/packages/frontend/i18n/src/index.ts @@ -57,9 +57,9 @@ const standardizeLocale = (language: string) => { } else if (language.slice(0, 2).toLowerCase() === 'zh') { language = 'zh-Hant'; } - if (LOCALES.find(locale => locale.tag === language)) return language; + if (LOCALES.some(locale => locale.tag === language)) return language; if ( - LOCALES.find(locale => locale.tag === language.slice(0, 2).toLowerCase()) + LOCALES.some(locale => locale.tag === language.slice(0, 2).toLowerCase()) ) { return language.slice(0, 2).toLowerCase(); } diff --git a/packages/frontend/i18n/src/resources/en.json b/packages/frontend/i18n/src/resources/en.json index fa30483378..22f5d6072f 100644 --- a/packages/frontend/i18n/src/resources/en.json +++ b/packages/frontend/i18n/src/resources/en.json @@ -346,6 +346,17 @@ "com.affine.aboutAFFiNE.changelog.title": "Discover what's new", "com.affine.aboutAFFiNE.checkUpdate.description": "New version is ready", "com.affine.aboutAFFiNE.checkUpdate.title": "Check for updates", + "com.affine.aboutAFFiNE.checkUpdate.button.check": "Check for Update", + "com.affine.aboutAFFiNE.checkUpdate.button.download": "Download Update", + "com.affine.aboutAFFiNE.checkUpdate.button.restart": "Restart to Update", + "com.affine.aboutAFFiNE.checkUpdate.button.retry": "Retry", + "com.affine.aboutAFFiNE.checkUpdate.subtitle.check": "Manually check for updates.", + "com.affine.aboutAFFiNE.checkUpdate.subtitle.checking": "Checking for updates...", + "com.affine.aboutAFFiNE.checkUpdate.subtitle.update-available": "New update available ({{version}})", + "com.affine.aboutAFFiNE.checkUpdate.subtitle.downloading": "Downloading the latest version...", + "com.affine.aboutAFFiNE.checkUpdate.subtitle.restart": "Restart tot apply update.", + "com.affine.aboutAFFiNE.checkUpdate.subtitle.latest": "You’ve got the latest version of AFFiNE.", + "com.affine.aboutAFFiNE.checkUpdate.subtitle.error": "Unable to connect to the update server.", "com.affine.aboutAFFiNE.community.title": "Communities", "com.affine.aboutAFFiNE.contact.community": "AFFiNE Community", "com.affine.aboutAFFiNE.contact.title": "Contact Us", @@ -486,6 +497,7 @@ "com.affine.cmdk.affine.category.editor.edgeless": "Edgeless Commands", "com.affine.cmdk.affine.category.editor.insert-object": "Insert Object", "com.affine.cmdk.affine.category.editor.page": "Page Commands", + "com.affine.cmdk.affine.category.results": "Results", "com.affine.cmdk.affine.client-border-style.to": "Change Client Border Style to", "com.affine.cmdk.affine.color-mode.to": "Change Colour Mode to", "com.affine.cmdk.affine.color-scheme.to": "Change Colour Scheme to", diff --git a/packages/frontend/i18n/src/resources/index.ts b/packages/frontend/i18n/src/resources/index.ts index 331d74ea97..d3410e9060 100644 --- a/packages/frontend/i18n/src/resources/index.ts +++ b/packages/frontend/i18n/src/resources/index.ts @@ -27,7 +27,7 @@ export const LOCALES = [ id: 1000040021, name: 'Portuguese (Brazil)', tag: 'pt-BR', - originalName: 'português (Brasil)', + originalName: 'Português (Brasil)', flagEmoji: '🇧🇷', base: false, completeRate: 0.414, @@ -77,7 +77,7 @@ export const LOCALES = [ id: 1000040008, name: 'Spanish', tag: 'es', - originalName: 'español', + originalName: 'Español', flagEmoji: '🇪🇸', base: false, completeRate: 0.357, @@ -97,7 +97,7 @@ export const LOCALES = [ id: 1000040011, name: 'Russian', tag: 'ru', - originalName: 'русский', + originalName: 'Pусский', flagEmoji: '🇷🇺', base: false, completeRate: 0.445, diff --git a/packages/frontend/native/src/lib.rs b/packages/frontend/native/src/lib.rs index ef92a6727e..fa8de0f14a 100644 --- a/packages/frontend/native/src/lib.rs +++ b/packages/frontend/native/src/lib.rs @@ -1,2 +1,2 @@ +pub mod hashcash; pub mod sqlite; -pub mod hashcash; \ No newline at end of file diff --git a/packages/frontend/native/src/sqlite/mod.rs b/packages/frontend/native/src/sqlite/mod.rs index ed1fb1c931..7738721167 100644 --- a/packages/frontend/native/src/sqlite/mod.rs +++ b/packages/frontend/native/src/sqlite/mod.rs @@ -1,5 +1,5 @@ use chrono::NaiveDateTime; -use napi::bindgen_prelude::{Buffer, FromNapiValue, ToNapiValue, Uint8Array}; +use napi::bindgen_prelude::{Buffer, Uint8Array}; use napi_derive::napi; use sqlx::{ migrate::MigrateDatabase, diff --git a/packages/frontend/workspace/src/affine/gql.ts b/packages/frontend/workspace/src/affine/gql.ts index b7988042a0..36f02c4eca 100644 --- a/packages/frontend/workspace/src/affine/gql.ts +++ b/packages/frontend/workspace/src/affine/gql.ts @@ -44,18 +44,9 @@ export const fetcher = gqlFetcherFactory( * }) * ``` */ -export function useQuery( - options: QueryOptions -): SWRResponse< - QueryResponse, - GraphQLError | GraphQLError[], - { - suspense: true; - } ->; export function useQuery( options: QueryOptions, - config: Omit< + config?: Omit< SWRConfiguration< QueryResponse, GraphQLError | GraphQLError[], @@ -160,17 +151,9 @@ export function useQueryInfinite( * * trigger({ name: 'John Doe' }) */ -export function useMutation( - options: Omit, 'variables'> -): SWRMutationResponse< - QueryResponse, - GraphQLError | GraphQLError[], - K, - QueryVariables ->; export function useMutation( options: Omit, 'variables'>, - config: Omit< + config?: Omit< SWRMutationConfiguration< QueryResponse, GraphQLError | GraphQLError[], diff --git a/packages/frontend/workspace/src/blob/engine.ts b/packages/frontend/workspace/src/blob/engine.ts index 16f993bdaa..0c5adb241d 100644 --- a/packages/frontend/workspace/src/blob/engine.ts +++ b/packages/frontend/workspace/src/blob/engine.ts @@ -5,8 +5,8 @@ const logger = new DebugLogger('affine:blob-engine'); export class BlobEngine { constructor( - private local: BlobStorage, - private remotes: BlobStorage[] + private readonly local: BlobStorage, + private readonly remotes: BlobStorage[] ) {} get storages() { diff --git a/packages/frontend/workspace/src/providers/awareness/affine/index.ts b/packages/frontend/workspace/src/providers/awareness/affine/index.ts index 9b6682b08f..d6d0b9d393 100644 --- a/packages/frontend/workspace/src/providers/awareness/affine/index.ts +++ b/packages/frontend/workspace/src/providers/awareness/affine/index.ts @@ -24,13 +24,13 @@ export function createAffineAwarenessProvider( const socket = getIoManager().socket('/'); const awarenessBroadcast = ({ - workspaceId: remoteWorkspaceId, + workspaceId: wsId, awarenessUpdate, }: { workspaceId: string; awarenessUpdate: string; }) => { - if (remoteWorkspaceId !== workspaceId) { + if (wsId !== workspaceId) { return; } applyAwarenessUpdate( diff --git a/packages/frontend/workspace/src/providers/storage/affine/batch-sync-sender.ts b/packages/frontend/workspace/src/providers/storage/affine/batch-sync-sender.ts index daebb9bc53..764eea09f7 100644 --- a/packages/frontend/workspace/src/providers/storage/affine/batch-sync-sender.ts +++ b/packages/frontend/workspace/src/providers/storage/affine/batch-sync-sender.ts @@ -15,12 +15,12 @@ interface SyncUpdateSender { * - retryable, allow retry when previous sync request failed but with retry flag been set to true */ export class BatchSyncSender { - private buffered: Uint8Array[] = []; + private readonly buffered: Uint8Array[] = []; private job: Promise | null = null; private started = true; constructor( - private guid: string, + private readonly guid: string, private readonly rawSender: SyncUpdateSender ) {} diff --git a/packages/frontend/workspace/src/providers/sync/consts.ts b/packages/frontend/workspace/src/providers/sync/consts.ts new file mode 100644 index 0000000000..4ddda3db06 --- /dev/null +++ b/packages/frontend/workspace/src/providers/sync/consts.ts @@ -0,0 +1,7 @@ +export const MANUALLY_STOP = 'manually-stop'; + +export enum SyncEngineStep { + Stopped = 0, + Syncing = 1, + Synced = 2, +} diff --git a/packages/frontend/workspace/src/providers/sync/engine.ts b/packages/frontend/workspace/src/providers/sync/engine.ts index ae723708c6..fca3e2c74a 100644 --- a/packages/frontend/workspace/src/providers/sync/engine.ts +++ b/packages/frontend/workspace/src/providers/sync/engine.ts @@ -3,16 +3,9 @@ import { Slot } from '@blocksuite/global/utils'; import type { Doc } from 'yjs'; import type { Storage } from '../storage'; +import { MANUALLY_STOP, SyncEngineStep } from './consts'; import { SyncPeer, type SyncPeerStatus, SyncPeerStep } from './peer'; -export const MANUALLY_STOP = 'manually-stop'; - -export enum SyncEngineStep { - Stopped = 0, - Syncing = 1, - Synced = 2, -} - export interface SyncEngineStatus { step: SyncEngineStep; local: SyncPeerStatus | null; @@ -70,9 +63,9 @@ export class SyncEngine { private abort = new AbortController(); constructor( - private rootDoc: Doc, - private local: Storage, - private remotes: Storage[] + private readonly rootDoc: Doc, + private readonly local: Storage, + private readonly remotes: Storage[] ) { this._status = { step: SyncEngineStep.Stopped, @@ -193,13 +186,13 @@ export class SyncEngine { } async waitForSynced(abort?: AbortSignal) { - if (this.status.step == SyncEngineStep.Synced) { + if (this.status.step === SyncEngineStep.Synced) { return; } else { return Promise.race([ new Promise(resolve => { this.onStatusChange.on(status => { - if (status.step == SyncEngineStep.Synced) { + if (status.step === SyncEngineStep.Synced) { resolve(); } }); diff --git a/packages/frontend/workspace/src/providers/sync/index.ts b/packages/frontend/workspace/src/providers/sync/index.ts index 882fbe154a..b741931973 100644 --- a/packages/frontend/workspace/src/providers/sync/index.ts +++ b/packages/frontend/workspace/src/providers/sync/index.ts @@ -14,5 +14,6 @@ * */ +export * from './consts'; export * from './engine'; export * from './peer'; diff --git a/packages/frontend/workspace/src/providers/sync/peer.ts b/packages/frontend/workspace/src/providers/sync/peer.ts index ecca8106a7..5597bb8469 100644 --- a/packages/frontend/workspace/src/providers/sync/peer.ts +++ b/packages/frontend/workspace/src/providers/sync/peer.ts @@ -7,7 +7,7 @@ import { applyUpdate, encodeStateAsUpdate, encodeStateVector } from 'yjs'; import { mergeUpdates, type Storage } from '../storage'; import { AsyncQueue } from '../utils/async-queue'; import { throwIfAborted } from '../utils/throw-if-aborted'; -import { MANUALLY_STOP } from './engine'; +import { MANUALLY_STOP } from './consts'; export enum SyncPeerStep { Stopped = 0, @@ -69,8 +69,8 @@ export class SyncPeer { logger = new DebugLogger('affine:sync-peer:' + this.name); constructor( - private rootDoc: Doc, - private storage: Storage + private readonly rootDoc: Doc, + private readonly storage: Storage ) { this.logger.debug('peer start'); @@ -150,7 +150,7 @@ export class SyncPeer { } } - private state: { + private readonly state: { connectedDocs: Map; pushUpdatesQueue: AsyncQueue<{ docId: string; diff --git a/packages/plugins/image-preview/src/component/index.jotai.ts b/packages/plugins/image-preview/src/component/index.jotai.ts index 67aa51d95d..0322bc78b6 100644 --- a/packages/plugins/image-preview/src/component/index.jotai.ts +++ b/packages/plugins/image-preview/src/component/index.jotai.ts @@ -9,7 +9,7 @@ previewBlockIdAtom.onMount = set => { if (target?.tagName === 'IMG') { const imageBlock = target.closest('affine-image'); if (imageBlock) { - const blockId = imageBlock.getAttribute('data-block-id'); + const blockId = imageBlock.dataset.blockId; if (!blockId) return; set(blockId); } diff --git a/packages/plugins/image-preview/src/component/index.tsx b/packages/plugins/image-preview/src/component/index.tsx index c66f1746b0..853ddd52a2 100644 --- a/packages/plugins/image-preview/src/component/index.tsx +++ b/packages/plugins/image-preview/src/component/index.tsx @@ -143,7 +143,7 @@ const ImagePreviewModalImpl = ( if ( page .getPreviousSiblings(block) - .findLast( + .some( (block): block is ImageBlockModel => block.flavour === 'affine:image' ) @@ -160,7 +160,7 @@ const ImagePreviewModalImpl = ( } else if ( page .getNextSiblings(block) - .find( + .some( (block): block is ImageBlockModel => block.flavour === 'affine:image' ) @@ -231,9 +231,9 @@ const ImagePreviewModalImpl = ( const a = document.createElement('a'); a.href = downloadUrl; a.download = block.id ?? 'image'; - document.body.appendChild(a); + document.body.append(a); a.click(); - document.body.removeChild(a); + a.remove(); } }, [props.pageId, props.workspace] diff --git a/packages/plugins/outline/src/app.tsx b/packages/plugins/outline/src/app.tsx index 1f54ad30be..caeb0eaa1a 100644 --- a/packages/plugins/outline/src/app.tsx +++ b/packages/plugins/outline/src/app.tsx @@ -40,7 +40,7 @@ const Outline = () => { ref={useCallback((container: HTMLDivElement | null) => { if (container) { assertExists(tocPanelRef.current); - container.appendChild(tocPanelRef.current); + container.append(tocPanelRef.current); } }, [])} /> diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000000..e7f52bc821 --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,3 @@ +[toolchain] +channel = "1.74.0" +profile = "default" diff --git a/scripts/publish.sh b/scripts/publish.sh deleted file mode 100755 index 70c257d32d..0000000000 --- a/scripts/publish.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash - -packages=( - "y-indexeddb" - "y-provider" - "infra" - "sdk" -) - -for package in "${packages[@]}"; do - yarn nx build $package - cd "packages/common/$package" - - if [ "$NIGHTLY" = "true" ]; then - yarn npm publish --no-git-checks --tag nightly - else - yarn npm publish - fi - - cd ../../../ -done diff --git a/tests/affine-cloud/e2e/collaboration.spec.ts b/tests/affine-cloud/e2e/collaboration.spec.ts index 2be5db6571..a592bc494c 100644 --- a/tests/affine-cloud/e2e/collaboration.spec.ts +++ b/tests/affine-cloud/e2e/collaboration.spec.ts @@ -265,7 +265,9 @@ test.describe('collaboration members', () => { await addUserToWorkspace(workspaceId, userB.id, 1 /* READ */); }; await Promise.all( - new Array(10).fill(1).map(() => createUserAndAddToWorkspace()) + Array.from({ length: 10 }) + .fill(1) + .map(() => createUserAndAddToWorkspace()) ); await openSettingModal(page); @@ -366,18 +368,10 @@ test('can sync svg between different browsers', async ({ page, browser }) => { expect(src).not.toBeNull(); // fetch the src resource in the browser - const svg2 = await page2.evaluate(async src => { - async function blobToString(blob: Blob) { - return new Promise((resolve, reject) => { - const reader = new FileReader(); - reader.onloadend = () => resolve(reader.result); - reader.onerror = reject; - reader.readAsText(blob); - }); - } - - const blob = fetch(src!).then(res => res.blob()); - return blobToString(await blob); + const svg2 = await page2.evaluate(src => { + return fetch(src!) + .then(res => res.blob()) + .then(blob => blob.text()); }, src); // turn the blob into string and check if it contains the svg diff --git a/tests/affine-desktop/e2e/basic.spec.ts b/tests/affine-desktop/e2e/basic.spec.ts index c77223a573..5ff31caf87 100644 --- a/tests/affine-desktop/e2e/basic.spec.ts +++ b/tests/affine-desktop/e2e/basic.spec.ts @@ -114,9 +114,7 @@ test('clientBorder value should disable by default on window', async ({ test('app theme', async ({ page, electronApp }) => { const root = page.locator('html'); { - const themeMode = await root.evaluate(element => - element.getAttribute('data-theme') - ); + const themeMode = await root.evaluate(element => element.dataset.theme); expect(themeMode).toBe('light'); const theme = await electronApp.evaluate(({ nativeTheme }) => { @@ -131,9 +129,7 @@ test('app theme', async ({ page, electronApp }) => { await page.getByTestId('appearance-panel-trigger').click(); await page.waitForTimeout(50); await page.getByTestId('dark-theme-trigger').click(); - const themeMode = await root.evaluate(element => - element.getAttribute('data-theme') - ); + const themeMode = await root.evaluate(element => element.dataset.theme); expect(themeMode).toBe('dark'); const theme = await electronApp.evaluate(({ nativeTheme }) => { return nativeTheme.shouldUseDarkColors ? 'dark' : 'light'; diff --git a/tests/affine-local/e2e/all-page.spec.ts b/tests/affine-local/e2e/all-page.spec.ts index 488abb2657..5ed1c78770 100644 --- a/tests/affine-local/e2e/all-page.spec.ts +++ b/tests/affine-local/e2e/all-page.spec.ts @@ -1,3 +1,4 @@ +/* eslint-disable unicorn/prefer-dom-node-dataset */ import { test } from '@affine-test/kit/playwright'; import { changeFilter, @@ -307,7 +308,6 @@ test('select a group of items by clicking "Select All" in group header', async ( const selectedGroupItemTotalCount = await page .locator('[data-testid="page-list-group-header"]') .getAttribute('data-group-items-count'); - expect(selectedItemCount).toBe(selectedGroupItemTotalCount); // check the selected count is equal to the one displayed in the floating toolbar diff --git a/tests/affine-local/e2e/image-preview.spec.ts b/tests/affine-local/e2e/image-preview.spec.ts index a0a62666b8..118952323a 100644 --- a/tests/affine-local/e2e/image-preview.spec.ts +++ b/tests/affine-local/e2e/image-preview.spec.ts @@ -1,3 +1,4 @@ +/* eslint-disable unicorn/prefer-dom-node-dataset */ import { openHomePage } from '@affine-test/kit/utils/load-page'; import { clickNewPageButton, diff --git a/tests/affine-local/e2e/quick-search.spec.ts b/tests/affine-local/e2e/quick-search.spec.ts index cb5ceb836b..bc53c0cd9e 100644 --- a/tests/affine-local/e2e/quick-search.spec.ts +++ b/tests/affine-local/e2e/quick-search.spec.ts @@ -65,7 +65,7 @@ async function waitForScrollToFinish(page: Page) { let lastScrollTop: number; const interval = setInterval(() => { const { scrollTop } = document.documentElement; - if (scrollTop != lastScrollTop) { + if (scrollTop !== lastScrollTop) { lastScrollTop = scrollTop; } else { clearInterval(interval); diff --git a/tests/affine-local/e2e/settings.spec.ts b/tests/affine-local/e2e/settings.spec.ts index 2b3c65d059..ba5e866aa2 100644 --- a/tests/affine-local/e2e/settings.spec.ts +++ b/tests/affine-local/e2e/settings.spec.ts @@ -49,15 +49,11 @@ test('Change theme', async ({ page }) => { const root = page.locator('html'); await page.getByTestId('light-theme-trigger').click(); - const lightMode = await root.evaluate(element => - element.getAttribute('data-theme') - ); + const lightMode = await root.evaluate(element => element.dataset.theme); expect(lightMode).toBe('light'); await page.getByTestId('dark-theme-trigger').click(); - const darkMode = await root.evaluate(element => - element.getAttribute('data-theme') - ); + const darkMode = await root.evaluate(element => element.dataset.theme); expect(darkMode).toBe('dark'); }); diff --git a/tests/affine-local/e2e/theme.spec.ts b/tests/affine-local/e2e/theme.spec.ts index 2e4d3c39b2..ae383963f5 100644 --- a/tests/affine-local/e2e/theme.spec.ts +++ b/tests/affine-local/e2e/theme.spec.ts @@ -14,9 +14,7 @@ test('default white', async ({ browser }) => { await openHomePage(page); await waitForEditorLoad(page); const root = page.locator('html'); - const themeMode = await root.evaluate(element => - element.getAttribute('data-theme') - ); + const themeMode = await root.evaluate(element => element.dataset.theme); expect(themeMode).toBe('light'); await page.screenshot({ path: resolve(testResultDir, 'affine-light-theme.png'), @@ -25,9 +23,7 @@ test('default white', async ({ browser }) => { await page.getByTestId('appearance-panel-trigger').click(); await page.waitForTimeout(50); await page.getByTestId('dark-theme-trigger').click(); - const darkMode = await root.evaluate(element => - element.getAttribute('data-theme') - ); + const darkMode = await root.evaluate(element => element.dataset.theme); expect(darkMode).toBe('dark'); await page.screenshot({ path: resolve(testResultDir, 'affine-dark-theme.png'), diff --git a/tests/kit/utils/filter.ts b/tests/kit/utils/filter.ts index dc1edaf81b..e4809fd622 100644 --- a/tests/kit/utils/filter.ts +++ b/tests/kit/utils/filter.ts @@ -48,6 +48,8 @@ export const getPagesCount = async (page: Page) => { return 0; } + // locator is not a HTMLElement, so we can't use dataset + // eslint-disable-next-line unicorn/prefer-dom-node-dataset const count = await locator.getAttribute('data-total-count'); return count ? parseInt(count) : 0; }; diff --git a/.commitlintrc.json b/tools/commitlint/.commitlintrc.json similarity index 100% rename from .commitlintrc.json rename to tools/commitlint/.commitlintrc.json diff --git a/tools/commitlint/README.md b/tools/commitlint/README.md new file mode 100644 index 0000000000..34ee016ab5 --- /dev/null +++ b/tools/commitlint/README.md @@ -0,0 +1,3 @@ +# commitlint + +This package defines commitlint and it's dependencies, so that it can be used in a CI pipeline to minimize the installation time. diff --git a/tools/commitlint/package.json b/tools/commitlint/package.json new file mode 100644 index 0000000000..6a47516f4b --- /dev/null +++ b/tools/commitlint/package.json @@ -0,0 +1,10 @@ +{ + "name": "@affine/commitlint-config", + "version": "0.0.0", + "private": true, + "devDependencies": { + "@commitlint/cli": "^18.4.3", + "@commitlint/config-conventional": "^18.4.3", + "commitlint": "^18.4.3" + } +} diff --git a/tools/workers/src/index.ts b/tools/workers/src/index.ts index 364d631abb..d49e2c433e 100644 --- a/tools/workers/src/index.ts +++ b/tools/workers/src/index.ts @@ -14,8 +14,8 @@ function isOriginAllowed( allowedOrigin: string | RegExp | Array ): boolean { if (Array.isArray(allowedOrigin)) { - for (let i = 0; i < allowedOrigin.length; ++i) { - if (isOriginAllowed(origin, allowedOrigin[i])) { + for (const allowed of allowedOrigin) { + if (isOriginAllowed(origin, allowed)) { return true; } } diff --git a/yarn.lock b/yarn.lock index 95fac082af..2bc8715170 100644 --- a/yarn.lock +++ b/yarn.lock @@ -204,6 +204,16 @@ __metadata: languageName: unknown linkType: soft +"@affine/commitlint-config@workspace:tools/commitlint": + version: 0.0.0-use.local + resolution: "@affine/commitlint-config@workspace:tools/commitlint" + dependencies: + "@commitlint/cli": "npm:^18.4.3" + "@commitlint/config-conventional": "npm:^18.4.3" + commitlint: "npm:^18.4.3" + languageName: unknown + linkType: soft + "@affine/component@workspace:*, @affine/component@workspace:packages/frontend/component": version: 0.0.0-use.local resolution: "@affine/component@workspace:packages/frontend/component" @@ -586,8 +596,8 @@ __metadata: "@types/affine__env": "workspace:*" "@types/eslint": "npm:^8.44.7" "@types/node": "npm:^20.9.3" - "@typescript-eslint/eslint-plugin": "npm:^6.12.0" - "@typescript-eslint/parser": "npm:^6.12.0" + "@typescript-eslint/eslint-plugin": "npm:^6.13.1" + "@typescript-eslint/parser": "npm:^6.13.1" "@vanilla-extract/vite-plugin": "npm:^3.9.2" "@vanilla-extract/webpack-plugin": "npm:^2.3.1" "@vitejs/plugin-react-swc": "npm:^3.5.0" @@ -597,7 +607,6 @@ __metadata: eslint: "npm:^8.54.0" eslint-config-prettier: "npm:^9.0.0" eslint-plugin-i: "npm:^2.29.0" - eslint-plugin-prettier: "npm:^5.0.1" eslint-plugin-react: "npm:^7.33.2" eslint-plugin-react-hooks: "npm:^4.6.0" eslint-plugin-simple-import-sort: "npm:^10.0.0" @@ -609,12 +618,12 @@ __metadata: happy-dom: "npm:^12.10.3" husky: "npm:^8.0.3" lint-staged: "npm:^15.1.0" - madge: "npm:^6.1.0" msw: "npm:^2.0.8" nanoid: "npm:^5.0.3" nx: "npm:^17.1.3" nx-cloud: "npm:^16.5.2" nyc: "npm:^15.1.0" + oxlint: "npm:^0.0.18" prettier: "npm:^3.1.0" semver: "npm:^7.5.4" serve: "npm:^14.2.1" @@ -737,6 +746,7 @@ __metadata: "@opentelemetry/instrumentation-ioredis": "npm:^0.35.3" "@opentelemetry/instrumentation-nestjs-core": "npm:^0.33.3" "@opentelemetry/instrumentation-socket.io": "npm:^0.34.3" + "@opentelemetry/resources": "npm:^1.18.1" "@opentelemetry/sdk-metrics": "npm:^1.18.1" "@opentelemetry/sdk-node": "npm:^0.45.1" "@opentelemetry/sdk-trace-node": "npm:^1.18.1" @@ -2751,7 +2761,7 @@ __metadata: languageName: node linkType: hard -"@babel/parser@npm:^7.0.0, @babel/parser@npm:^7.1.0, @babel/parser@npm:^7.13.16, @babel/parser@npm:^7.14.0, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.16.8, @babel/parser@npm:^7.17.3, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.21.4, @babel/parser@npm:^7.22.15, @babel/parser@npm:^7.22.7, @babel/parser@npm:^7.23.0, @babel/parser@npm:^7.23.3, @babel/parser@npm:^7.23.4": +"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.13.16, @babel/parser@npm:^7.14.0, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.16.8, @babel/parser@npm:^7.17.3, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.22.15, @babel/parser@npm:^7.22.7, @babel/parser@npm:^7.23.0, @babel/parser@npm:^7.23.3, @babel/parser@npm:^7.23.4": version: 7.23.4 resolution: "@babel/parser@npm:7.23.4" bin: @@ -4504,16 +4514,6 @@ __metadata: languageName: node linkType: hard -"@dependents/detective-less@npm:^3.0.1": - version: 3.0.2 - resolution: "@dependents/detective-less@npm:3.0.2" - dependencies: - gonzales-pe: "npm:^4.3.0" - node-source-walk: "npm:^5.0.1" - checksum: 2c263ab64fcd1f76117bc35f2b29a150c64bd2b105c96a909a63ce2f2baf07efd93d9ae80e612161d003fb71fbe46598292375f5cc3f447a1b83cfb545dc8f8f - languageName: node - linkType: hard - "@discoveryjs/json-ext@npm:^0.5.0, @discoveryjs/json-ext@npm:^0.5.3": version: 0.5.7 resolution: "@discoveryjs/json-ext@npm:0.5.7" @@ -9030,7 +9030,7 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/resources@npm:1.18.1": +"@opentelemetry/resources@npm:1.18.1, @opentelemetry/resources@npm:^1.18.1": version: 1.18.1 resolution: "@opentelemetry/resources@npm:1.18.1" dependencies: @@ -9147,6 +9147,48 @@ __metadata: languageName: node linkType: hard +"@oxlint/darwin-arm64@npm:0.0.18": + version: 0.0.18 + resolution: "@oxlint/darwin-arm64@npm:0.0.18" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@oxlint/darwin-x64@npm:0.0.18": + version: 0.0.18 + resolution: "@oxlint/darwin-x64@npm:0.0.18" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@oxlint/linux-arm64@npm:0.0.18": + version: 0.0.18 + resolution: "@oxlint/linux-arm64@npm:0.0.18" + conditions: os=linux & cpu=arm64 + languageName: node + linkType: hard + +"@oxlint/linux-x64@npm:0.0.18": + version: 0.0.18 + resolution: "@oxlint/linux-x64@npm:0.0.18" + conditions: os=linux & cpu=x64 + languageName: node + linkType: hard + +"@oxlint/win32-arm64@npm:0.0.18": + version: 0.0.18 + resolution: "@oxlint/win32-arm64@npm:0.0.18" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@oxlint/win32-x64@npm:0.0.18": + version: 0.0.18 + resolution: "@oxlint/win32-x64@npm:0.0.18" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + "@panva/hkdf@npm:^1.0.2, @panva/hkdf@npm:^1.1.1": version: 1.1.1 resolution: "@panva/hkdf@npm:1.1.1" @@ -9313,20 +9355,6 @@ __metadata: languageName: node linkType: hard -"@pkgr/utils@npm:^2.3.1": - version: 2.4.2 - resolution: "@pkgr/utils@npm:2.4.2" - dependencies: - cross-spawn: "npm:^7.0.3" - fast-glob: "npm:^3.3.0" - is-glob: "npm:^4.0.3" - open: "npm:^9.1.0" - picocolors: "npm:^1.0.0" - tslib: "npm:^2.6.0" - checksum: f0b0b305a83bd65fac5637d28ad3e33f19194043e03ceef6b4e13d260bfa2678b73df76dc56ed906469ffe0494d4bd214e6b92ca80684f38547982edf982dd15 - languageName: node - linkType: hard - "@playwright/test@npm:^1.39.0, @playwright/test@npm:^1.40.0": version: 1.40.0 resolution: "@playwright/test@npm:1.40.0" @@ -13127,6 +13155,7 @@ __metadata: lodash.debounce: "npm:^4.0.8" p-queue: "npm:^7.4.1" react: "npm:18.2.0" + rxjs: "npm:^7.8.1" swr: "npm:2.2.4" uuid: "npm:^9.0.1" vitest: "npm:0.34.6" @@ -13823,13 +13852,6 @@ __metadata: languageName: node linkType: hard -"@types/json5@npm:^0.0.29": - version: 0.0.29 - resolution: "@types/json5@npm:0.0.29" - checksum: 4e5aed58cabb2bbf6f725da13421aa50a49abb6bc17bfab6c31b8774b073fa7b50d557c61f961a09a85f6056151190f8ac95f13f5b48136ba5841f7d4484ec56 - languageName: node - linkType: hard - "@types/jsonfile@npm:*": version: 6.1.4 resolution: "@types/jsonfile@npm:6.1.4" @@ -14396,15 +14418,15 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/eslint-plugin@npm:^6.12.0": - version: 6.12.0 - resolution: "@typescript-eslint/eslint-plugin@npm:6.12.0" +"@typescript-eslint/eslint-plugin@npm:^6.13.1": + version: 6.13.1 + resolution: "@typescript-eslint/eslint-plugin@npm:6.13.1" dependencies: "@eslint-community/regexpp": "npm:^4.5.1" - "@typescript-eslint/scope-manager": "npm:6.12.0" - "@typescript-eslint/type-utils": "npm:6.12.0" - "@typescript-eslint/utils": "npm:6.12.0" - "@typescript-eslint/visitor-keys": "npm:6.12.0" + "@typescript-eslint/scope-manager": "npm:6.13.1" + "@typescript-eslint/type-utils": "npm:6.13.1" + "@typescript-eslint/utils": "npm:6.13.1" + "@typescript-eslint/visitor-keys": "npm:6.13.1" debug: "npm:^4.3.4" graphemer: "npm:^1.4.0" ignore: "npm:^5.2.4" @@ -14417,44 +14439,44 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 1b9d2bb88f3e793067d7ec1e24e11b9d22891314bd5ebdef80a11a0ddde19f5c052b341e2f2c8a466b3af48e492f1028023566feaeb10a826d3928380c3d3d88 + checksum: cae42c77404d8e6c149d68aca75bb3ce83cff5de8713d82e87e93bafae2839f29d261bc75b68f315b6b23858a85ea2f22ed8468cf5c7331e8330f7cee2129522 languageName: node linkType: hard -"@typescript-eslint/parser@npm:^6.12.0": - version: 6.12.0 - resolution: "@typescript-eslint/parser@npm:6.12.0" +"@typescript-eslint/parser@npm:^6.13.1": + version: 6.13.1 + resolution: "@typescript-eslint/parser@npm:6.13.1" dependencies: - "@typescript-eslint/scope-manager": "npm:6.12.0" - "@typescript-eslint/types": "npm:6.12.0" - "@typescript-eslint/typescript-estree": "npm:6.12.0" - "@typescript-eslint/visitor-keys": "npm:6.12.0" + "@typescript-eslint/scope-manager": "npm:6.13.1" + "@typescript-eslint/types": "npm:6.13.1" + "@typescript-eslint/typescript-estree": "npm:6.13.1" + "@typescript-eslint/visitor-keys": "npm:6.13.1" debug: "npm:^4.3.4" peerDependencies: eslint: ^7.0.0 || ^8.0.0 peerDependenciesMeta: typescript: optional: true - checksum: 2e33b581bcf882336bd4734e90a90dc3618960f8c07f5f7d16e4f3a0f00af97d3b3c8adc366170e1d9c8afd922068b3cfc5e9e997fd4ca6ebcb7c46a9e5b30a1 + checksum: cdc328d157a8b8a6babad88360451c177ea41666e2150f3822a474ed287a12336517dccf9f475f75a007d4aa622cb74f1442f17d17b87e19cc2c839784742351 languageName: node linkType: hard -"@typescript-eslint/scope-manager@npm:6.12.0": - version: 6.12.0 - resolution: "@typescript-eslint/scope-manager@npm:6.12.0" +"@typescript-eslint/scope-manager@npm:6.13.1": + version: 6.13.1 + resolution: "@typescript-eslint/scope-manager@npm:6.13.1" dependencies: - "@typescript-eslint/types": "npm:6.12.0" - "@typescript-eslint/visitor-keys": "npm:6.12.0" - checksum: 46c4a5575fbbb70a800934c93e89795cceef268a140b786a8d22615a0577a5356e42e316dfb23dbb43cec7271b480e712e3127ba33642040bd292fbb6a5de278 + "@typescript-eslint/types": "npm:6.13.1" + "@typescript-eslint/visitor-keys": "npm:6.13.1" + checksum: f728dbd995c58fadfc390411fe31b1b8a729b1c85ecf0ae2fe70f97f613298feab78c05bc180e03612f595b24cf0090476a0b8234c617b3edf1dae568342a7cf languageName: node linkType: hard -"@typescript-eslint/type-utils@npm:6.12.0": - version: 6.12.0 - resolution: "@typescript-eslint/type-utils@npm:6.12.0" +"@typescript-eslint/type-utils@npm:6.13.1": + version: 6.13.1 + resolution: "@typescript-eslint/type-utils@npm:6.13.1" dependencies: - "@typescript-eslint/typescript-estree": "npm:6.12.0" - "@typescript-eslint/utils": "npm:6.12.0" + "@typescript-eslint/typescript-estree": "npm:6.13.1" + "@typescript-eslint/utils": "npm:6.13.1" debug: "npm:^4.3.4" ts-api-utils: "npm:^1.0.1" peerDependencies: @@ -14462,37 +14484,23 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: e92709a0ea5d5aee86def3da40fe4190235d3560f41e77a73d4bc10f6f59e0df367d5a1263d4e05aa44af4deb158ca6f37b09e483248e341a38fd5e2e8b70f72 + checksum: 484e5f74fc604b24687fe6426e650f40f679d62216ee5e45bf6d1f91edd60cd8deef747ca43f7dc3c22b2b76f030477603c82559e44c3f2fb5c8877a0c65aefa languageName: node linkType: hard -"@typescript-eslint/types@npm:4.33.0": - version: 4.33.0 - resolution: "@typescript-eslint/types@npm:4.33.0" - checksum: c7ec55e6aef47ac49f712da52968c825552a23c59717f338f08c480e5336d592a689a324d21e2d370f48713a1302d7c78c14e6250bcea9b91f0b62ab6b2db3e1 +"@typescript-eslint/types@npm:6.13.1": + version: 6.13.1 + resolution: "@typescript-eslint/types@npm:6.13.1" + checksum: 350c7f847052f7c24629d41645c02be152c512f3e5c21a79f53c04821b1fff3019416b18d9b72e5ca5c3c5f62f210301f2bb69080b84e67fe83af27751a7af18 languageName: node linkType: hard -"@typescript-eslint/types@npm:5.62.0": - version: 5.62.0 - resolution: "@typescript-eslint/types@npm:5.62.0" - checksum: 24e8443177be84823242d6729d56af2c4b47bfc664dd411a1d730506abf2150d6c31bdefbbc6d97c8f91043e3a50e0c698239dcb145b79bb6b0c34469aaf6c45 - languageName: node - linkType: hard - -"@typescript-eslint/types@npm:6.12.0": - version: 6.12.0 - resolution: "@typescript-eslint/types@npm:6.12.0" - checksum: e52f12d01e2f543927fde985d709616dec1ef310da3a00e3d239874752ba7635e04d325e2a7cf6403d19977282f15fed7629d2477aeeb57df9140fa424f530fe - languageName: node - linkType: hard - -"@typescript-eslint/typescript-estree@npm:6.12.0": - version: 6.12.0 - resolution: "@typescript-eslint/typescript-estree@npm:6.12.0" +"@typescript-eslint/typescript-estree@npm:6.13.1": + version: 6.13.1 + resolution: "@typescript-eslint/typescript-estree@npm:6.13.1" dependencies: - "@typescript-eslint/types": "npm:6.12.0" - "@typescript-eslint/visitor-keys": "npm:6.12.0" + "@typescript-eslint/types": "npm:6.13.1" + "@typescript-eslint/visitor-keys": "npm:6.13.1" debug: "npm:^4.3.4" globby: "npm:^11.1.0" is-glob: "npm:^4.0.3" @@ -14501,90 +14509,34 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 16f327faf736becb145894380e059a68a993b14fdf6dab50c5b79ff3c027a1e1a61274742f44f6ecd9ebbfadfc55559f94fad52e1596e1ed2656a3053367de85 + checksum: 1df965c5b202664da1a4a1ffc51bda3d85e581d8c206cd4be63653e2558775104258f6071e1f35a269619ebfb81bd18ee74e3fcb724fed15d3a2577d0ee5a34f languageName: node linkType: hard -"@typescript-eslint/typescript-estree@npm:^4.33.0": - version: 4.33.0 - resolution: "@typescript-eslint/typescript-estree@npm:4.33.0" - dependencies: - "@typescript-eslint/types": "npm:4.33.0" - "@typescript-eslint/visitor-keys": "npm:4.33.0" - debug: "npm:^4.3.1" - globby: "npm:^11.0.3" - is-glob: "npm:^4.0.1" - semver: "npm:^7.3.5" - tsutils: "npm:^3.21.0" - peerDependenciesMeta: - typescript: - optional: true - checksum: e9409ec992d9d3c44efd53106bf63a6d5f6041e9dc359d54ea4b5f6787404cf44057cbd3090e9cc45ed4157dc631e398079f954490e50a4608cea1fbf9e69226 - languageName: node - linkType: hard - -"@typescript-eslint/typescript-estree@npm:^5.55.0": - version: 5.62.0 - resolution: "@typescript-eslint/typescript-estree@npm:5.62.0" - dependencies: - "@typescript-eslint/types": "npm:5.62.0" - "@typescript-eslint/visitor-keys": "npm:5.62.0" - debug: "npm:^4.3.4" - globby: "npm:^11.1.0" - is-glob: "npm:^4.0.3" - semver: "npm:^7.3.7" - tsutils: "npm:^3.21.0" - peerDependenciesMeta: - typescript: - optional: true - checksum: 06c975eb5f44b43bd19fadc2e1023c50cf87038fe4c0dd989d4331c67b3ff509b17fa60a3251896668ab4d7322bdc56162a9926971218d2e1a1874d2bef9a52e - languageName: node - linkType: hard - -"@typescript-eslint/utils@npm:6.12.0": - version: 6.12.0 - resolution: "@typescript-eslint/utils@npm:6.12.0" +"@typescript-eslint/utils@npm:6.13.1": + version: 6.13.1 + resolution: "@typescript-eslint/utils@npm:6.13.1" dependencies: "@eslint-community/eslint-utils": "npm:^4.4.0" "@types/json-schema": "npm:^7.0.12" "@types/semver": "npm:^7.5.0" - "@typescript-eslint/scope-manager": "npm:6.12.0" - "@typescript-eslint/types": "npm:6.12.0" - "@typescript-eslint/typescript-estree": "npm:6.12.0" + "@typescript-eslint/scope-manager": "npm:6.13.1" + "@typescript-eslint/types": "npm:6.13.1" + "@typescript-eslint/typescript-estree": "npm:6.13.1" semver: "npm:^7.5.4" peerDependencies: eslint: ^7.0.0 || ^8.0.0 - checksum: 84091ddc0f0cceb5d0a2e366139d65413867cf648f805355ab4a42ee273cdd691b9083084d1c1feb9cb3c1934c1ed338fbf92146c738a96b84de3d2ec2dfdec5 + checksum: 6fab1122071c7a2da959dcf16cdd723a65bd8ba8e55af9cea11bb1707c4d047e94c3daaed2ab504cdbd2ca0d052f2a33de5290b28de0277ba00210569673ac9b languageName: node linkType: hard -"@typescript-eslint/visitor-keys@npm:4.33.0": - version: 4.33.0 - resolution: "@typescript-eslint/visitor-keys@npm:4.33.0" +"@typescript-eslint/visitor-keys@npm:6.13.1": + version: 6.13.1 + resolution: "@typescript-eslint/visitor-keys@npm:6.13.1" dependencies: - "@typescript-eslint/types": "npm:4.33.0" - eslint-visitor-keys: "npm:^2.0.0" - checksum: b28cd3f2c5cd919587d576e0cec8a95ea6c12a2e22dfec59ce70714efad4a7af4d467f9f964dd4ba1a4ad82d4a8c680a6d397e621b9be9d1df89b3a95380813f - languageName: node - linkType: hard - -"@typescript-eslint/visitor-keys@npm:5.62.0": - version: 5.62.0 - resolution: "@typescript-eslint/visitor-keys@npm:5.62.0" - dependencies: - "@typescript-eslint/types": "npm:5.62.0" - eslint-visitor-keys: "npm:^3.3.0" - checksum: dc613ab7569df9bbe0b2ca677635eb91839dfb2ca2c6fa47870a5da4f160db0b436f7ec0764362e756d4164e9445d49d5eb1ff0b87f4c058946ae9d8c92eb388 - languageName: node - linkType: hard - -"@typescript-eslint/visitor-keys@npm:6.12.0": - version: 6.12.0 - resolution: "@typescript-eslint/visitor-keys@npm:6.12.0" - dependencies: - "@typescript-eslint/types": "npm:6.12.0" + "@typescript-eslint/types": "npm:6.13.1" eslint-visitor-keys: "npm:^3.4.1" - checksum: edf3537c8176059e8fdea680c10f85a635e427fb5caa6f88473077f50edbec7b011b0dc1e4499543519085559268d30a166b1cb160d30a1315ef818fc181a6a4 + checksum: 27ccc4bfe940e50b0977838356b7feda95b68754fa544a988588a159a2619eb04d07c67e55d16bfea1b0dc6184a7fb5daff1366b266c9f5fd19d72831dea6163 languageName: node linkType: hard @@ -15698,13 +15650,6 @@ __metadata: languageName: node linkType: hard -"any-promise@npm:^1.1.0": - version: 1.3.0 - resolution: "any-promise@npm:1.3.0" - checksum: 6737469ba353b5becf29e4dc3680736b9caa06d300bda6548812a8fee63ae7d336d756f88572fa6b5219aed36698d808fa55f62af3e7e6845c7a1dc77d240edb - languageName: node - linkType: hard - "anymatch@npm:^3.0.3, anymatch@npm:~3.1.2": version: 3.1.3 resolution: "anymatch@npm:3.1.3" @@ -15715,13 +15660,6 @@ __metadata: languageName: node linkType: hard -"app-module-path@npm:^2.2.0": - version: 2.2.0 - resolution: "app-module-path@npm:2.2.0" - checksum: 9ed8c6ce6247a6b5d556039f29b4610869237bbb5b8f3d905b22bd2d314c30efcc0fb70c2626d7461ecc52ec7edec9908f660d0938d2bea5b8cfc6868a28806f - languageName: node - linkType: hard - "app-root-dir@npm:^1.0.2": version: 1.0.2 resolution: "app-root-dir@npm:1.0.2" @@ -16018,27 +15956,6 @@ __metadata: languageName: node linkType: hard -"ast-module-types@npm:^2.7.1": - version: 2.7.1 - resolution: "ast-module-types@npm:2.7.1" - checksum: f5ff6a8aec1ac93533bb5af6ab3921b7eb9f91032f6fdd7cb08e6917005649dc8d5dcdfc98016b7125450db6028da22ae6990275e0ecd6d1fcfe7f1cf5c83b8a - languageName: node - linkType: hard - -"ast-module-types@npm:^3.0.0": - version: 3.0.0 - resolution: "ast-module-types@npm:3.0.0" - checksum: 9cf2e1c566ff88347762ba177dbe8a8bd3e59f366052d45c0a3b1afde2db2228a1c56f05a6e4d7fbfa219de26ae64831a138ff7533269310b13bb9e3a0e84c9f - languageName: node - linkType: hard - -"ast-module-types@npm:^4.0.0": - version: 4.0.0 - resolution: "ast-module-types@npm:4.0.0" - checksum: 06cf93519ef353bb580d90d87f0a9501f39ef99f79784e5291e59e30764c07b383018c153ff4020e9d1409030e9affd438dfca76c28d38400ba729addba8a54a - languageName: node - linkType: hard - "ast-types@npm:0.15.2": version: 0.15.2 resolution: "ast-types@npm:0.15.2" @@ -16844,15 +16761,6 @@ __metadata: languageName: node linkType: hard -"bundle-name@npm:^3.0.0": - version: 3.0.0 - resolution: "bundle-name@npm:3.0.0" - dependencies: - run-applescript: "npm:^5.0.0" - checksum: edf2b1fbe6096ed32e7566947ace2ea937ee427391744d7510a2880c4b9a5b3543d3f6c551236a29e5c87d3195f8e2912516290e638c15bcbede7b37cc375615 - languageName: node - linkType: hard - "busboy@npm:^1.0.0, busboy@npm:^1.6.0": version: 1.6.0 resolution: "busboy@npm:1.6.0" @@ -17704,7 +17612,7 @@ __metadata: languageName: node linkType: hard -"color-name@npm:^1.0.0, color-name@npm:^1.1.4, color-name@npm:~1.1.4": +"color-name@npm:^1.0.0, color-name@npm:~1.1.4": version: 1.1.4 resolution: "color-name@npm:1.1.4" checksum: b0445859521eb4021cd0fb0cc1a75cecf67fceecae89b63f62b201cca8d345baf8b952c966862a9d9a2632987d4f6581f0ec8d957dfacece86f0a7919316f610 @@ -17808,7 +17716,7 @@ __metadata: languageName: node linkType: hard -"commander@npm:^2.16.0, commander@npm:^2.20.0, commander@npm:^2.20.3, commander@npm:^2.8.1": +"commander@npm:^2.20.0, commander@npm:^2.20.3": version: 2.20.3 resolution: "commander@npm:2.20.3" checksum: 90c5b6898610cd075984c58c4f88418a4fb44af08c1b1415e9854c03171bec31b336b7f3e4cefe33de994b3f12b03c5e2d638da4316df83593b9e82554e7e95b @@ -17857,13 +17765,25 @@ __metadata: languageName: node linkType: hard -"commander@npm:^9.0.0, commander@npm:^9.4.1, commander@npm:^9.5.0": +"commander@npm:^9.0.0, commander@npm:^9.4.1": version: 9.5.0 resolution: "commander@npm:9.5.0" checksum: 41c49b3d0f94a1fbeb0463c85b13f15aa15a9e0b4d5e10a49c0a1d58d4489b549d62262b052ae0aa6cfda53299bee487bfe337825df15e342114dde543f82906 languageName: node linkType: hard +"commitlint@npm:^18.4.3": + version: 18.4.3 + resolution: "commitlint@npm:18.4.3" + dependencies: + "@commitlint/cli": "npm:^18.4.3" + "@commitlint/types": "npm:^18.4.3" + bin: + commitlint: cli.js + checksum: e37da4f1cd218381a7810b2e74a553bd05b01776d76dc3038b6e805ea6640308dfa0372c2a976ab5d8c34577128fe6df945e4718c27732dd0d79b517f066fee5 + languageName: node + linkType: hard + "common-path-prefix@npm:^3.0.0": version: 3.0.0 resolution: "common-path-prefix@npm:3.0.0" @@ -18809,7 +18729,7 @@ __metadata: languageName: node linkType: hard -"default-browser-id@npm:3.0.0, default-browser-id@npm:^3.0.0": +"default-browser-id@npm:3.0.0": version: 3.0.0 resolution: "default-browser-id@npm:3.0.0" dependencies: @@ -18819,18 +18739,6 @@ __metadata: languageName: node linkType: hard -"default-browser@npm:^4.0.0": - version: 4.0.0 - resolution: "default-browser@npm:4.0.0" - dependencies: - bundle-name: "npm:^3.0.0" - default-browser-id: "npm:^3.0.0" - execa: "npm:^7.1.1" - titleize: "npm:^3.0.0" - checksum: 40c5af984799042b140300be5639c9742599bda76dc9eba5ac9ad5943c83dd36cebc4471eafcfddf8e0ec817166d5ba89d56f08e66a126c7c7908a179cead1a7 - languageName: node - linkType: hard - "default-gateway@npm:^6.0.3": version: 6.0.3 resolution: "default-gateway@npm:6.0.3" @@ -18883,13 +18791,6 @@ __metadata: languageName: node linkType: hard -"define-lazy-prop@npm:^3.0.0": - version: 3.0.0 - resolution: "define-lazy-prop@npm:3.0.0" - checksum: f28421cf9ee86eecaf5f3b8fe875f13d7009c2625e97645bfff7a2a49aca678270b86c39f9c32939e5ca7ab96b551377ed4139558c795e076774287ad3af1aa4 - languageName: node - linkType: hard - "define-properties@npm:@nolyfill/define-properties@latest": version: 1.0.24 resolution: "@nolyfill/define-properties@npm:1.0.24" @@ -18964,21 +18865,6 @@ __metadata: languageName: node linkType: hard -"dependency-tree@npm:^9.0.0": - version: 9.0.0 - resolution: "dependency-tree@npm:9.0.0" - dependencies: - commander: "npm:^2.20.3" - debug: "npm:^4.3.1" - filing-cabinet: "npm:^3.0.1" - precinct: "npm:^9.0.0" - typescript: "npm:^4.0.0" - bin: - dependency-tree: bin/cli.js - checksum: 7ffc6009bf6eeea712cead1c18d57f87e2d9c0ca5a3327b12daf368de5350f85c613558ae4274e7b49b23010533746917d84ad75dd4fd4d731d61a2eaaf9e310 - languageName: node - linkType: hard - "dequal@npm:2.0.3, dequal@npm:^2.0.0, dequal@npm:^2.0.2, dequal@npm:^2.0.3": version: 2.0.3 resolution: "dequal@npm:2.0.3" @@ -19059,191 +18945,6 @@ __metadata: languageName: node linkType: hard -"detective-amd@npm:^3.1.0": - version: 3.1.2 - resolution: "detective-amd@npm:3.1.2" - dependencies: - ast-module-types: "npm:^3.0.0" - escodegen: "npm:^2.0.0" - get-amd-module-type: "npm:^3.0.0" - node-source-walk: "npm:^4.2.0" - bin: - detective-amd: bin/cli.js - checksum: 0b71555edad8e85c9a2ae85e2799d5faf2bdfe0de969587c9288ca76e717494678e34f444dffe32ffdd432e85ce50ca7017a5d0441a4855677a45a40c4590c74 - languageName: node - linkType: hard - -"detective-amd@npm:^4.0.1, detective-amd@npm:^4.1.0": - version: 4.2.0 - resolution: "detective-amd@npm:4.2.0" - dependencies: - ast-module-types: "npm:^4.0.0" - escodegen: "npm:^2.0.0" - get-amd-module-type: "npm:^4.1.0" - node-source-walk: "npm:^5.0.1" - bin: - detective-amd: bin/cli.js - checksum: c1e829a3202045796105680c9fe90ac61f63b0ccecc12cc30c7204c9e7ec22a4e2c3e2357719b9346a4e3579eba778cdce9a050e642938e2a4c8b57b091278e4 - languageName: node - linkType: hard - -"detective-cjs@npm:^3.1.1": - version: 3.1.3 - resolution: "detective-cjs@npm:3.1.3" - dependencies: - ast-module-types: "npm:^3.0.0" - node-source-walk: "npm:^4.0.0" - checksum: a691cb4afbbfea59d9aae0ee00752ec1a825a7ef18fc9178b53664975f162f3b537268590def009d9ce1cccfc5bc4f38cf775df08d0872aaacc05d96c72de85a - languageName: node - linkType: hard - -"detective-cjs@npm:^4.0.0, detective-cjs@npm:^4.1.0": - version: 4.1.0 - resolution: "detective-cjs@npm:4.1.0" - dependencies: - ast-module-types: "npm:^4.0.0" - node-source-walk: "npm:^5.0.1" - checksum: 17e40183959e9f377333a9fd03dcf4cbabf1b7a9f588882311066ecaaad68ad16765a7b63ffc096fc91d2a3c14ac044ed1823257c76105c9cb96dfc141a806e2 - languageName: node - linkType: hard - -"detective-es6@npm:^2.2.1": - version: 2.2.2 - resolution: "detective-es6@npm:2.2.2" - dependencies: - node-source-walk: "npm:^4.0.0" - checksum: 9ee9909c089f5dcd1f89eccd347d509197996280ba24e2e08742bbc5ca3eef655ff07b4edfd76b52d6b4376ba03b8ec17d621c9f9c4382a6ba233dc1b1d00d33 - languageName: node - linkType: hard - -"detective-es6@npm:^3.0.0, detective-es6@npm:^3.0.1": - version: 3.0.1 - resolution: "detective-es6@npm:3.0.1" - dependencies: - node-source-walk: "npm:^5.0.0" - checksum: 881a0c16b49504c212e61a521231ebbb4299a6102b178230959c74d2ca22d5f7538dfaf9518d01fb568ff93eadcf61d865d4428c9fed893dd4c91a7f29d515c5 - languageName: node - linkType: hard - -"detective-less@npm:^1.0.2": - version: 1.0.2 - resolution: "detective-less@npm:1.0.2" - dependencies: - debug: "npm:^4.0.0" - gonzales-pe: "npm:^4.2.3" - node-source-walk: "npm:^4.0.0" - checksum: 858936fbad87423bd5d7502ff5fafca023e7c99e4006ed01b31c12c4b5ff8697edce91419798479d857efec68ee8f022fcac64de5530db6a64012be600a2249e - languageName: node - linkType: hard - -"detective-postcss@npm:^4.0.0": - version: 4.0.0 - resolution: "detective-postcss@npm:4.0.0" - dependencies: - debug: "npm:^4.1.1" - is-url: "npm:^1.2.4" - postcss: "npm:^8.1.7" - postcss-values-parser: "npm:^2.0.1" - checksum: 9bd5d35554bf51f255db2640f14745f83c10352ba4c4f499096ee4f21547fd7fab501f3dc4017aa7d3bee3b1b455f1162a8067d1810c62d4b1806c96bd6c238e - languageName: node - linkType: hard - -"detective-postcss@npm:^6.1.0, detective-postcss@npm:^6.1.1": - version: 6.1.3 - resolution: "detective-postcss@npm:6.1.3" - dependencies: - is-url: "npm:^1.2.4" - postcss: "npm:^8.4.23" - postcss-values-parser: "npm:^6.0.2" - checksum: ee6e07fed20ac93a6ba84736b9c586a942a4a6b2df173f963f95ea753380c99e4a606da22b8d9e8407c50e356f3d893a127eb68cf84c97233a209e9fbbadb026 - languageName: node - linkType: hard - -"detective-sass@npm:^3.0.1": - version: 3.0.2 - resolution: "detective-sass@npm:3.0.2" - dependencies: - gonzales-pe: "npm:^4.3.0" - node-source-walk: "npm:^4.0.0" - checksum: 7489e5ae7dbed2eba89855cea21ad32321e8e92bd9f2d3b925e7feec0dd9aa8b4b865296525275938e573a3be9759715490038103cbc970570a1c48c4f2fd23d - languageName: node - linkType: hard - -"detective-sass@npm:^4.0.1, detective-sass@npm:^4.1.1": - version: 4.1.3 - resolution: "detective-sass@npm:4.1.3" - dependencies: - gonzales-pe: "npm:^4.3.0" - node-source-walk: "npm:^5.0.1" - checksum: 91681e90037cc935f38b2867fab2aa5585848491b3a269dfb44b37721146ff83f57a540d964b15db22dc1f232623568bedfd13470ec7363e6111991d4d3fe573 - languageName: node - linkType: hard - -"detective-scss@npm:^2.0.1": - version: 2.0.2 - resolution: "detective-scss@npm:2.0.2" - dependencies: - gonzales-pe: "npm:^4.3.0" - node-source-walk: "npm:^4.0.0" - checksum: 515ff1b8946ec92baead48ef435efe1ea0f33ee1d98a7537dd700f1d06dd192f9ea0971c10343adcb08b561ab296d01c18a1f62d0b63163a8f4c09885a956e1a - languageName: node - linkType: hard - -"detective-scss@npm:^3.0.0, detective-scss@npm:^3.0.1": - version: 3.1.1 - resolution: "detective-scss@npm:3.1.1" - dependencies: - gonzales-pe: "npm:^4.3.0" - node-source-walk: "npm:^5.0.1" - checksum: 3d9c0468216c822c25572e700b9aba1e2e2797d336b6b84fd455d83ce849263324855008d1e58d6ccdf9c7a4f099e31277b99e885407cd19674e0bb10fc458cd - languageName: node - linkType: hard - -"detective-stylus@npm:^1.0.0": - version: 1.0.3 - resolution: "detective-stylus@npm:1.0.3" - checksum: 2723da93545f3a55a2a7eaa76b50712457af3c93c2b003e95d02f4c240d5e5206a5df99209a4f5b54128c11fc4270c2de1d7316b4f7d02b359483ae74f5a6637 - languageName: node - linkType: hard - -"detective-stylus@npm:^2.0.1": - version: 2.0.1 - resolution: "detective-stylus@npm:2.0.1" - checksum: c701ba6df3e6b5346aa5dd37b8329a9069a20fd7d075933e2e3b819a75922a2adab809143591151e7337183d59c980e6bc64ad6e51ce96de864575221c1b9506 - languageName: node - linkType: hard - -"detective-stylus@npm:^3.0.0": - version: 3.0.0 - resolution: "detective-stylus@npm:3.0.0" - checksum: e82eda490406d289f7b22050423ad69eb1c0f0d88414adaa292de4ab533be3c50d4cf512a9fefba426f3ad20789f0c0db3b0d32f70162112ca89034bbc5ca9d3 - languageName: node - linkType: hard - -"detective-typescript@npm:^7.0.0": - version: 7.0.2 - resolution: "detective-typescript@npm:7.0.2" - dependencies: - "@typescript-eslint/typescript-estree": "npm:^4.33.0" - ast-module-types: "npm:^2.7.1" - node-source-walk: "npm:^4.2.0" - typescript: "npm:^3.9.10" - checksum: 77703410baa242029dc5e7d02cca7a26278dea498ec1c3320f92efa08a85263affc3b102fc2b09952ece1d2c851a3808733d7bfa9ed11944a7c0f39920e33ec9 - languageName: node - linkType: hard - -"detective-typescript@npm:^9.0.0, detective-typescript@npm:^9.1.1": - version: 9.1.1 - resolution: "detective-typescript@npm:9.1.1" - dependencies: - "@typescript-eslint/typescript-estree": "npm:^5.55.0" - ast-module-types: "npm:^4.0.0" - node-source-walk: "npm:^5.0.1" - typescript: "npm:^4.9.5" - checksum: 5f50801f622740d4e9d724ce04518ceb81591215bf18c18c5d22f6f3948df49dfb0a8bbe3596dac47220a37028bc2879ccd7a968f265217c9855817bda4622f5 - languageName: node - linkType: hard - "devlop@npm:^1.0.0, devlop@npm:^1.1.0": version: 1.1.0 resolution: "devlop@npm:1.1.0" @@ -19895,7 +19596,7 @@ __metadata: languageName: node linkType: hard -"enhanced-resolve@npm:^5.15.0, enhanced-resolve@npm:^5.8.3": +"enhanced-resolve@npm:^5.15.0": version: 5.15.0 resolution: "enhanced-resolve@npm:5.15.0" dependencies: @@ -20385,7 +20086,7 @@ __metadata: languageName: node linkType: hard -"escodegen@npm:^2.0.0, escodegen@npm:^2.1.0": +"escodegen@npm:^2.1.0": version: 2.1.0 resolution: "escodegen@npm:2.1.0" dependencies: @@ -20456,25 +20157,6 @@ __metadata: languageName: node linkType: hard -"eslint-plugin-prettier@npm:^5.0.1": - version: 5.0.1 - resolution: "eslint-plugin-prettier@npm:5.0.1" - dependencies: - prettier-linter-helpers: "npm:^1.0.0" - synckit: "npm:^0.8.5" - peerDependencies: - "@types/eslint": ">=8.0.0" - eslint: ">=8.0.0" - prettier: ">=3.0.0" - peerDependenciesMeta: - "@types/eslint": - optional: true - eslint-config-prettier: - optional: true - checksum: 1a43dcca90f61fde0167347e0e870b579835ba6b9d697a862c29c76097a7bb0e8f07a7cf88be33517ca11203d9d4aa335d794c377640c2fe5acd06871db67d34 - languageName: node - linkType: hard - "eslint-plugin-react-hooks@npm:^4.6.0": version: 4.6.0 resolution: "eslint-plugin-react-hooks@npm:4.6.0" @@ -20611,13 +20293,6 @@ __metadata: languageName: node linkType: hard -"eslint-visitor-keys@npm:^2.0.0": - version: 2.1.0 - resolution: "eslint-visitor-keys@npm:2.1.0" - checksum: db4547eef5039122d518fa307e938ceb8589da5f6e8f5222efaf14dd62f748ce82e2d2becd3ff9412a50350b726bda95dbea8515a471074547daefa58aee8735 - languageName: node - linkType: hard - "eslint-visitor-keys@npm:^3.3.0, eslint-visitor-keys@npm:^3.4.1, eslint-visitor-keys@npm:^3.4.3": version: 3.4.3 resolution: "eslint-visitor-keys@npm:3.4.3" @@ -20855,23 +20530,6 @@ __metadata: languageName: node linkType: hard -"execa@npm:^7.1.1": - version: 7.2.0 - resolution: "execa@npm:7.2.0" - dependencies: - cross-spawn: "npm:^7.0.3" - get-stream: "npm:^6.0.1" - human-signals: "npm:^4.3.0" - is-stream: "npm:^3.0.0" - merge-stream: "npm:^2.0.0" - npm-run-path: "npm:^5.1.0" - onetime: "npm:^6.0.0" - signal-exit: "npm:^3.0.7" - strip-final-newline: "npm:^3.0.0" - checksum: 473feff60f9d4dbe799225948de48b5158c1723021d19c4b982afe37bcd111ae84e1b4c9dfe967fae5101b0894b1a62e4dd564a286dfa3e46d7b0cfdbf7fe62b - languageName: node - linkType: hard - "exit-hook@npm:^2.2.1": version: 2.2.1 resolution: "exit-hook@npm:2.2.1" @@ -21054,7 +20712,7 @@ __metadata: languageName: node linkType: hard -"fast-diff@npm:^1.1.2, fast-diff@npm:^1.2.0": +"fast-diff@npm:^1.2.0": version: 1.3.0 resolution: "fast-diff@npm:1.3.0" checksum: 9e57415bc69cd6efcc720b3b8fe9fdaf42dcfc06f86f0f45378b1fa512598a8aac48aa3928c8751d58e2f01bb4ba4f07e4f3d9bc0d57586d45f1bd1e872c6cde @@ -21340,29 +20998,6 @@ __metadata: languageName: node linkType: hard -"filing-cabinet@npm:^3.0.1": - version: 3.3.1 - resolution: "filing-cabinet@npm:3.3.1" - dependencies: - app-module-path: "npm:^2.2.0" - commander: "npm:^2.20.3" - debug: "npm:^4.3.3" - enhanced-resolve: "npm:^5.8.3" - is-relative-path: "npm:^1.0.2" - module-definition: "npm:^3.3.1" - module-lookup-amd: "npm:^7.0.1" - resolve: "npm:^1.21.0" - resolve-dependency-path: "npm:^2.0.0" - sass-lookup: "npm:^3.0.0" - stylus-lookup: "npm:^3.0.1" - tsconfig-paths: "npm:^3.10.1" - typescript: "npm:^3.9.7" - bin: - filing-cabinet: bin/cli.js - checksum: c4e71efffa1714e2fe862fa7741446a6307ce03a27f01faed3cc4969c9fc395dddd4d1e095569ce0bf6b6c95826f2665883465971f3346ba179f815b888e8151 - languageName: node - linkType: hard - "fill-range@npm:^7.0.1": version: 7.0.1 resolution: "fill-range@npm:7.0.1" @@ -21530,13 +21165,6 @@ __metadata: languageName: node linkType: hard -"flatten@npm:^1.0.2": - version: 1.0.3 - resolution: "flatten@npm:1.0.3" - checksum: 5c57379816f1692aaa79fbc6390e0a0644e5e8442c5783ed57c6d315468eddbc53a659eaa03c9bb1e771b0f4a9bd8dd8a2620286bf21fd6538a7857321fdfb20 - languageName: node - linkType: hard - "flexsearch@npm:0.7.21": version: 0.7.21 resolution: "flexsearch@npm:0.7.21" @@ -22005,26 +21633,6 @@ __metadata: languageName: node linkType: hard -"get-amd-module-type@npm:^3.0.0": - version: 3.0.2 - resolution: "get-amd-module-type@npm:3.0.2" - dependencies: - ast-module-types: "npm:^3.0.0" - node-source-walk: "npm:^4.2.2" - checksum: dd41d3d966677ee03b2f51ffe0e9cd1e37df4757ec490c31d67840042d6dc3173c019ec82d03ed12282338239589b3380dac6c59508b4f4ab802f28f1c9c8a8d - languageName: node - linkType: hard - -"get-amd-module-type@npm:^4.1.0": - version: 4.1.0 - resolution: "get-amd-module-type@npm:4.1.0" - dependencies: - ast-module-types: "npm:^4.0.0" - node-source-walk: "npm:^5.0.1" - checksum: b12fe94eeee65df44010d7046f5435661d220b1f8132fbc11e525f2eab4ef2d9e6114c439e9905621f71449e5e43f588f21d29243a53bef36bcfcf5252481b6d - languageName: node - linkType: hard - "get-caller-file@npm:^2.0.1, get-caller-file@npm:^2.0.5": version: 2.0.5 resolution: "get-caller-file@npm:2.0.5" @@ -22093,13 +21701,6 @@ __metadata: languageName: node linkType: hard -"get-own-enumerable-property-symbols@npm:^3.0.0": - version: 3.0.2 - resolution: "get-own-enumerable-property-symbols@npm:3.0.2" - checksum: 8f0331f14159f939830884799f937343c8c0a2c330506094bc12cbee3665d88337fe97a4ea35c002cc2bdba0f5d9975ad7ec3abb925015cdf2a93e76d4759ede - languageName: node - linkType: hard - "get-package-info@npm:^1.0.0": version: 1.0.0 resolution: "get-package-info@npm:1.0.0" @@ -22154,7 +21755,7 @@ __metadata: languageName: node linkType: hard -"get-stream@npm:^6.0.0, get-stream@npm:^6.0.1": +"get-stream@npm:^6.0.0": version: 6.0.1 resolution: "get-stream@npm:6.0.1" checksum: 781266d29725f35c59f1d214aedc92b0ae855800a980800e2923b3fbc4e56b3cb6e462c42e09a1cf1a00c64e056a78fa407cbe06c7c92b7e5cd49b4b85c2a497 @@ -22462,17 +22063,6 @@ __metadata: languageName: node linkType: hard -"gonzales-pe@npm:^4.2.3, gonzales-pe@npm:^4.3.0": - version: 4.3.0 - resolution: "gonzales-pe@npm:4.3.0" - dependencies: - minimist: "npm:^1.2.5" - bin: - gonzales: bin/gonzales.js - checksum: d1676546bcaa4cb1c6c1fc5de5d62e85960665a13a4c489b02baeb58a10c53a249beef05ceaf21ea801813a559ff17d7b61158aa417211c135bcb8bdcb1701ca - languageName: node - linkType: hard - "google-auth-library@npm:^7.0.0, google-auth-library@npm:^7.0.2, google-auth-library@npm:^7.14.0": version: 7.14.1 resolution: "google-auth-library@npm:7.14.1" @@ -23212,13 +22802,6 @@ __metadata: languageName: node linkType: hard -"human-signals@npm:^4.3.0": - version: 4.3.1 - resolution: "human-signals@npm:4.3.1" - checksum: fa59894c358fe9f2b5549be2fb083661d5e1dff618d3ac70a49ca73495a72e873fbf6c0878561478e521e17d498292746ee391791db95ffe5747bfb5aef8765b - languageName: node - linkType: hard - "human-signals@npm:^5.0.0": version: 5.0.0 resolution: "human-signals@npm:5.0.0" @@ -23446,13 +23029,6 @@ __metadata: languageName: node linkType: hard -"indexes-of@npm:^1.0.1": - version: 1.0.1 - resolution: "indexes-of@npm:1.0.1" - checksum: 4f9799b1739a62f3e02d09f6f4162cf9673025282af7fa36e790146e7f4e216dad3e776a25b08536c093209c9fcb5ea7bd04b082d42686a45f58ff401d6da32e - languageName: node - linkType: hard - "infer-owner@npm:^1.0.4": version: 1.0.4 resolution: "infer-owner@npm:1.0.4" @@ -23701,15 +23277,6 @@ __metadata: languageName: node linkType: hard -"is-docker@npm:^3.0.0": - version: 3.0.0 - resolution: "is-docker@npm:3.0.0" - bin: - is-docker: cli.js - checksum: b698118f04feb7eaf3338922bd79cba064ea54a1c3db6ec8c0c8d8ee7613e7e5854d802d3ef646812a8a3ace81182a085dfa0a71cc68b06f3fa794b9783b3c90 - languageName: node - linkType: hard - "is-error@npm:^2.2.2": version: 2.2.2 resolution: "is-error@npm:2.2.2" @@ -23775,17 +23342,6 @@ __metadata: languageName: node linkType: hard -"is-inside-container@npm:^1.0.0": - version: 1.0.0 - resolution: "is-inside-container@npm:1.0.0" - dependencies: - is-docker: "npm:^3.0.0" - bin: - is-inside-container: cli.js - checksum: c50b75a2ab66ab3e8b92b3bc534e1ea72ca25766832c0623ac22d134116a98bcf012197d1caabe1d1c4bd5f84363d4aa5c36bb4b585fbcaf57be172cd10a1a03 - languageName: node - linkType: hard - "is-interactive@npm:^1.0.0": version: 1.0.0 resolution: "is-interactive@npm:1.0.0" @@ -23853,13 +23409,6 @@ __metadata: languageName: node linkType: hard -"is-obj@npm:^1.0.1": - version: 1.0.1 - resolution: "is-obj@npm:1.0.1" - checksum: 3ccf0efdea12951e0b9c784e2b00e77e87b2f8bd30b42a498548a8afcc11b3287342a2030c308e473e93a7a19c9ea7854c99a8832a476591c727df2a9c79796c - languageName: node - linkType: hard - "is-obj@npm:^2.0.0": version: 2.0.0 resolution: "is-obj@npm:2.0.0" @@ -23939,20 +23488,6 @@ __metadata: languageName: node linkType: hard -"is-regexp@npm:^1.0.0": - version: 1.0.0 - resolution: "is-regexp@npm:1.0.0" - checksum: be692828e24cba479ec33644326fa98959ec68ba77965e0291088c1a741feaea4919d79f8031708f85fd25e39de002b4520622b55460660b9c369e6f7187faef - languageName: node - linkType: hard - -"is-relative-path@npm:^1.0.2": - version: 1.0.2 - resolution: "is-relative-path@npm:1.0.2" - checksum: 6c2ccffd3c0a3e3990535d0571370883d558b825b51940085f3446ec338857f4552f44521dfec3e83b7e067de08c0b0369de290208a91200bcae2c569533e340 - languageName: node - linkType: hard - "is-relative@npm:^1.0.0": version: 1.0.0 resolution: "is-relative@npm:1.0.0" @@ -24056,20 +23591,6 @@ __metadata: languageName: node linkType: hard -"is-url-superb@npm:^4.0.0": - version: 4.0.0 - resolution: "is-url-superb@npm:4.0.0" - checksum: fd55e91c96349acb0d688f95fcb1ac67450e5db934976e3a8ff13ef446841e779a6f4d18b15f02331f05a3429c8fdaba2382ac1ab444059e86e9ffcde1ec8db0 - languageName: node - linkType: hard - -"is-url@npm:^1.2.4": - version: 1.2.4 - resolution: "is-url@npm:1.2.4" - checksum: 100e74b3b1feab87a43ef7653736e88d997eb7bd32e71fd3ebc413e58c1cbe56269699c776aaea84244b0567f2a7d68dfaa512a062293ed2f9fdecb394148432 - languageName: node - linkType: hard - "is-windows@npm:^0.2.0": version: 0.2.0 resolution: "is-windows@npm:0.2.0" @@ -25174,17 +24695,6 @@ __metadata: languageName: node linkType: hard -"json5@npm:^1.0.2": - version: 1.0.2 - resolution: "json5@npm:1.0.2" - dependencies: - minimist: "npm:^1.2.0" - bin: - json5: lib/cli.js - checksum: a78d812dbbd5642c4f637dd130954acfd231b074965871c3e28a5bbd571f099d623ecf9161f1960c4ddf68e0cc98dee8bebfdb94a71ad4551f85a1afc94b63f6 - languageName: node - linkType: hard - "json5@npm:^2.1.2, json5@npm:^2.2.2, json5@npm:^2.2.3": version: 2.2.3 resolution: "json5@npm:2.2.3" @@ -26398,43 +25908,6 @@ __metadata: languageName: node linkType: hard -"madge@npm:^6.1.0": - version: 6.1.0 - resolution: "madge@npm:6.1.0" - dependencies: - chalk: "npm:^4.1.1" - commander: "npm:^7.2.0" - commondir: "npm:^1.0.1" - debug: "npm:^4.3.1" - dependency-tree: "npm:^9.0.0" - detective-amd: "npm:^4.0.1" - detective-cjs: "npm:^4.0.0" - detective-es6: "npm:^3.0.0" - detective-less: "npm:^1.0.2" - detective-postcss: "npm:^6.1.0" - detective-sass: "npm:^4.0.1" - detective-scss: "npm:^3.0.0" - detective-stylus: "npm:^2.0.1" - detective-typescript: "npm:^9.0.0" - ora: "npm:^5.4.1" - pluralize: "npm:^8.0.0" - precinct: "npm:^8.1.0" - pretty-ms: "npm:^7.0.1" - rc: "npm:^1.2.7" - stream-to-array: "npm:^2.3.0" - ts-graphviz: "npm:^1.5.0" - walkdir: "npm:^0.4.1" - peerDependencies: - typescript: ^3.9.5 || ^4.9.5 || ^5 - peerDependenciesMeta: - typescript: - optional: true - bin: - madge: bin/cli.js - checksum: 80d3d549135e302109a5b98ab6cb088b44579779067365008368a042578bca0dfb1a76c697a1e9f63d5d2037724628e66b9dbd3efb0126555dd9d626a865d6d0 - languageName: node - linkType: hard - "magic-string@npm:0.27.0, magic-string@npm:^0.27.0": version: 0.27.0 resolution: "magic-string@npm:0.27.0" @@ -27739,30 +27212,6 @@ __metadata: languageName: node linkType: hard -"module-definition@npm:^3.3.1": - version: 3.4.0 - resolution: "module-definition@npm:3.4.0" - dependencies: - ast-module-types: "npm:^3.0.0" - node-source-walk: "npm:^4.0.0" - bin: - module-definition: bin/cli.js - checksum: 5cbfd38aab1a9169b5c31924e208e430a87a1b1512ab9736a9a368d950e3cc8e2f5cf642e37fe74123e25402cae50bfb8fdf1f5f0fd3d4d9270df705a2360bfa - languageName: node - linkType: hard - -"module-definition@npm:^4.1.0": - version: 4.1.0 - resolution: "module-definition@npm:4.1.0" - dependencies: - ast-module-types: "npm:^4.0.0" - node-source-walk: "npm:^5.0.1" - bin: - module-definition: bin/cli.js - checksum: d9b6397c9ba04b08bc035fd87a3652900530b9a5d6e5263f8a1e05c927dfc103fdffcecd7071a9fd6cd7813fc9feafbbe828f5277e5b706e5de82831153ef0fb - languageName: node - linkType: hard - "module-details-from-path@npm:^1.0.3": version: 1.0.3 resolution: "module-details-from-path@npm:1.0.3" @@ -27770,21 +27219,6 @@ __metadata: languageName: node linkType: hard -"module-lookup-amd@npm:^7.0.1": - version: 7.0.1 - resolution: "module-lookup-amd@npm:7.0.1" - dependencies: - commander: "npm:^2.8.1" - debug: "npm:^4.1.0" - glob: "npm:^7.1.6" - requirejs: "npm:^2.3.5" - requirejs-config-file: "npm:^4.0.0" - bin: - lookup-amd: bin/cli.js - checksum: dadf8c602dd970e211136b22271231a853f76ec0b857a3bfa765e52a1f45f313a8e8bc7e1961f96bb1eced14e557cdbc798b37f5c17f0235cf29155617f4e7a9 - languageName: node - linkType: hard - "mri@npm:^1.2.0": version: 1.2.0 resolution: "mri@npm:1.2.0" @@ -28262,24 +27696,6 @@ __metadata: languageName: node linkType: hard -"node-source-walk@npm:^4.0.0, node-source-walk@npm:^4.2.0, node-source-walk@npm:^4.2.2": - version: 4.3.0 - resolution: "node-source-walk@npm:4.3.0" - dependencies: - "@babel/parser": "npm:^7.0.0" - checksum: 3145f4b907624b42bf7847d19937e33a1c3946b960ebcf5ac7eded9ba187ae00edec143946ef4a8f04835c31688f06e8341e0a7b4962a41d4d56bd5ab0a83ed5 - languageName: node - linkType: hard - -"node-source-walk@npm:^5.0.0, node-source-walk@npm:^5.0.1": - version: 5.0.2 - resolution: "node-source-walk@npm:5.0.2" - dependencies: - "@babel/parser": "npm:^7.21.4" - checksum: cbb474a80a7c4576ed94bf4c999be889a85ad1177a0a80019714adece1f5ca70e6a5f728b6828afb4ea00ca524c3ea65fc24d7405b921b754bfb596553f78e7b - languageName: node - linkType: hard - "nodemailer@npm:^6.9.7": version: 6.9.7 resolution: "nodemailer@npm:6.9.7" @@ -28797,18 +28213,6 @@ __metadata: languageName: node linkType: hard -"open@npm:^9.1.0": - version: 9.1.0 - resolution: "open@npm:9.1.0" - dependencies: - default-browser: "npm:^4.0.0" - define-lazy-prop: "npm:^3.0.0" - is-inside-container: "npm:^1.0.0" - is-wsl: "npm:^2.2.0" - checksum: b45bcc7a6795804a2f560f0ca9f5e5344114bc40754d10c28a811c0c8f7027356979192931a6a7df2ab9e5bab3058988c99ae55f4fb71db2ce9fc77c40f619aa - languageName: node - linkType: hard - "openai@npm:~4.4.0": version: 4.4.0 resolution: "openai@npm:4.4.0" @@ -28921,6 +28325,35 @@ __metadata: languageName: node linkType: hard +"oxlint@npm:^0.0.18": + version: 0.0.18 + resolution: "oxlint@npm:0.0.18" + dependencies: + "@oxlint/darwin-arm64": "npm:0.0.18" + "@oxlint/darwin-x64": "npm:0.0.18" + "@oxlint/linux-arm64": "npm:0.0.18" + "@oxlint/linux-x64": "npm:0.0.18" + "@oxlint/win32-arm64": "npm:0.0.18" + "@oxlint/win32-x64": "npm:0.0.18" + dependenciesMeta: + "@oxlint/darwin-arm64": + optional: true + "@oxlint/darwin-x64": + optional: true + "@oxlint/linux-arm64": + optional: true + "@oxlint/linux-x64": + optional: true + "@oxlint/win32-arm64": + optional: true + "@oxlint/win32-x64": + optional: true + bin: + oxlint: bin/oxlint + checksum: c372394c27856e58f2a622dbcef45e72a9df10f6ba2a1b882732356b8982a0bcdf875019b28211d480ac07e4e77bb2870621c23c595addf74e82ee603e00acc0 + languageName: node + linkType: hard + "p-cancelable@npm:^2.0.0": version: 2.1.1 resolution: "p-cancelable@npm:2.1.1" @@ -29242,13 +28675,6 @@ __metadata: languageName: node linkType: hard -"parse-ms@npm:^2.1.0": - version: 2.1.0 - resolution: "parse-ms@npm:2.1.0" - checksum: 517eab80cdb9df6ae22a8fad944bfb4289482699bcde5211a1c127091dfea33c3dcb217246b188865fc32e998bcee815bfa4a863f41e3b2d0bcc69f34ef1a543 - languageName: node - linkType: hard - "parse-ms@npm:^3.0.0": version: 3.0.0 resolution: "parse-ms@npm:3.0.0" @@ -30077,31 +29503,7 @@ __metadata: languageName: node linkType: hard -"postcss-values-parser@npm:^2.0.1": - version: 2.0.1 - resolution: "postcss-values-parser@npm:2.0.1" - dependencies: - flatten: "npm:^1.0.2" - indexes-of: "npm:^1.0.1" - uniq: "npm:^1.0.1" - checksum: f69211656b90a9d2f5efb1ec4a9e5f076f968b0c1169bfddc16aa0dce2e3a2853d7875e4d8058652e9b65d084fab93bfee8da389f6a2145ac547ee9ab0a03456 - languageName: node - linkType: hard - -"postcss-values-parser@npm:^6.0.2": - version: 6.0.2 - resolution: "postcss-values-parser@npm:6.0.2" - dependencies: - color-name: "npm:^1.1.4" - is-url-superb: "npm:^4.0.0" - quote-unquote: "npm:^1.0.0" - peerDependencies: - postcss: ^8.2.9 - checksum: ff2fa096896f1c33f7531e814b8d01e785bd99d672c1597d5c5d8c2409b30b8146be6565f6269c952d1f03d626f00ae3f1afb8308cc772c08b323abee23c9a42 - languageName: node - linkType: hard - -"postcss@npm:^8.1.7, postcss@npm:^8.3.6, postcss@npm:^8.4.21, postcss@npm:^8.4.23, postcss@npm:^8.4.27, postcss@npm:^8.4.31": +"postcss@npm:^8.3.6, postcss@npm:^8.4.21, postcss@npm:^8.4.27, postcss@npm:^8.4.31": version: 8.4.31 resolution: "postcss@npm:8.4.31" dependencies: @@ -30148,51 +29550,6 @@ __metadata: languageName: node linkType: hard -"precinct@npm:^8.1.0": - version: 8.3.1 - resolution: "precinct@npm:8.3.1" - dependencies: - commander: "npm:^2.20.3" - debug: "npm:^4.3.3" - detective-amd: "npm:^3.1.0" - detective-cjs: "npm:^3.1.1" - detective-es6: "npm:^2.2.1" - detective-less: "npm:^1.0.2" - detective-postcss: "npm:^4.0.0" - detective-sass: "npm:^3.0.1" - detective-scss: "npm:^2.0.1" - detective-stylus: "npm:^1.0.0" - detective-typescript: "npm:^7.0.0" - module-definition: "npm:^3.3.1" - node-source-walk: "npm:^4.2.0" - bin: - precinct: bin/cli.js - checksum: 46ec2cd2e8af5386b1b0bf47fb264b288be39895dc6e33b9537d795d57c7b0a97f349c217edfa0bf584fef1031b4b0a5b30d1ad74fbe9833644adad6e325bc9e - languageName: node - linkType: hard - -"precinct@npm:^9.0.0": - version: 9.2.1 - resolution: "precinct@npm:9.2.1" - dependencies: - "@dependents/detective-less": "npm:^3.0.1" - commander: "npm:^9.5.0" - detective-amd: "npm:^4.1.0" - detective-cjs: "npm:^4.1.0" - detective-es6: "npm:^3.0.1" - detective-postcss: "npm:^6.1.1" - detective-sass: "npm:^4.1.1" - detective-scss: "npm:^3.0.1" - detective-stylus: "npm:^3.0.0" - detective-typescript: "npm:^9.1.1" - module-definition: "npm:^4.1.0" - node-source-walk: "npm:^5.0.1" - bin: - precinct: bin/cli.js - checksum: f208d2e3c5d7c7b3834f70f761d0255680f76268d3835cd498006bc5dafa602aaecda0b2e68868ed679ec330fddaee0b662ba862d6eacd7282760dc0bc5f1b35 - languageName: node - linkType: hard - "prelude-ls@npm:^1.2.1": version: 1.2.1 resolution: "prelude-ls@npm:1.2.1" @@ -30200,15 +29557,6 @@ __metadata: languageName: node linkType: hard -"prettier-linter-helpers@npm:^1.0.0": - version: 1.0.0 - resolution: "prettier-linter-helpers@npm:1.0.0" - dependencies: - fast-diff: "npm:^1.1.2" - checksum: 00ce8011cf6430158d27f9c92cfea0a7699405633f7f1d4a45f07e21bf78e99895911cbcdc3853db3a824201a7c745bd49bfea8abd5fb9883e765a90f74f8392 - languageName: node - linkType: hard - "prettier@npm:*, prettier@npm:^3.0.3, prettier@npm:^3.1.0": version: 3.1.0 resolution: "prettier@npm:3.1.0" @@ -30285,15 +29633,6 @@ __metadata: languageName: node linkType: hard -"pretty-ms@npm:^7.0.1": - version: 7.0.1 - resolution: "pretty-ms@npm:7.0.1" - dependencies: - parse-ms: "npm:^2.1.0" - checksum: a39aac23cc7dae7a94c70518ab8b6c6db0894a7b84c81ee7abc8778c5ec8bae2d1e71ba991ff641732b38433724bfbdbb37bd3a00418637f797c072e06fe8b4c - languageName: node - linkType: hard - "pretty-ms@npm:^8.0.0": version: 8.0.0 resolution: "pretty-ms@npm:8.0.0" @@ -30656,13 +29995,6 @@ __metadata: languageName: node linkType: hard -"quote-unquote@npm:^1.0.0": - version: 1.0.0 - resolution: "quote-unquote@npm:1.0.0" - checksum: 955a2ead534f5b6a3f8d4dc5a4b95ac6468213d3fb11f8c1592a0a56345c45a3d14d5ca04d3de2bc9891493fcac38c03dfa91c48a6159aef50124e9c5afcea49 - languageName: node - linkType: hard - "ramda@npm:0.29.0": version: 0.29.0 resolution: "ramda@npm:0.29.0" @@ -30739,7 +30071,7 @@ __metadata: languageName: node linkType: hard -"rc@npm:^1.0.1, rc@npm:^1.1.6, rc@npm:^1.2.7": +"rc@npm:^1.0.1, rc@npm:^1.1.6": version: 1.2.8 resolution: "rc@npm:1.2.8" dependencies: @@ -31624,26 +30956,6 @@ __metadata: languageName: node linkType: hard -"requirejs-config-file@npm:^4.0.0": - version: 4.0.0 - resolution: "requirejs-config-file@npm:4.0.0" - dependencies: - esprima: "npm:^4.0.0" - stringify-object: "npm:^3.2.1" - checksum: 9fc55813ca8627cc4d3704622702b6b119a222c97825fc61459727d5f9ef9435e4fd34ee03335dc119c66e834d7963fdfe43d9a4df99448d43219587c624ba4e - languageName: node - linkType: hard - -"requirejs@npm:^2.3.5": - version: 2.3.6 - resolution: "requirejs@npm:2.3.6" - bin: - r.js: ./bin/r.js - r_js: ./bin/r.js - checksum: 808540b0a2374cf19bf00d13036a90f94aac92984a9be0f1fa642266d0ee467db0b1aa9d85d567e0da71b54294f5feb92e13ac62bae7f85a0b3ac3ab393b05d4 - languageName: node - linkType: hard - "requires-port@npm:^1.0.0": version: 1.0.0 resolution: "requires-port@npm:1.0.0" @@ -31667,13 +30979,6 @@ __metadata: languageName: node linkType: hard -"resolve-dependency-path@npm:^2.0.0": - version: 2.0.0 - resolution: "resolve-dependency-path@npm:2.0.0" - checksum: f7511412546276473bbe0182730baf9bda08a437425f817b1d944ecac4c0a137129c544bf60ca5928b1b89f7124855408c257a12e1b604ebbffeb997bbb81a65 - languageName: node - linkType: hard - "resolve-dir@npm:^0.1.0": version: 0.1.1 resolution: "resolve-dir@npm:0.1.1" @@ -31740,7 +31045,7 @@ __metadata: languageName: node linkType: hard -"resolve@npm:^1.1.6, resolve@npm:^1.10.0, resolve@npm:^1.12.0, resolve@npm:^1.14.2, resolve@npm:^1.19.0, resolve@npm:^1.20.0, resolve@npm:^1.21.0, resolve@npm:^1.22.1, resolve@npm:^1.22.3, resolve@npm:^1.22.4, resolve@npm:~1.22.1": +"resolve@npm:^1.1.6, resolve@npm:^1.10.0, resolve@npm:^1.12.0, resolve@npm:^1.14.2, resolve@npm:^1.19.0, resolve@npm:^1.20.0, resolve@npm:^1.22.1, resolve@npm:^1.22.3, resolve@npm:^1.22.4, resolve@npm:~1.22.1": version: 1.22.8 resolution: "resolve@npm:1.22.8" dependencies: @@ -31776,7 +31081,7 @@ __metadata: languageName: node linkType: hard -"resolve@patch:resolve@npm%3A^1.1.6#optional!builtin, resolve@patch:resolve@npm%3A^1.10.0#optional!builtin, resolve@patch:resolve@npm%3A^1.12.0#optional!builtin, resolve@patch:resolve@npm%3A^1.14.2#optional!builtin, resolve@patch:resolve@npm%3A^1.19.0#optional!builtin, resolve@patch:resolve@npm%3A^1.20.0#optional!builtin, resolve@patch:resolve@npm%3A^1.21.0#optional!builtin, resolve@patch:resolve@npm%3A^1.22.1#optional!builtin, resolve@patch:resolve@npm%3A^1.22.3#optional!builtin, resolve@patch:resolve@npm%3A^1.22.4#optional!builtin, resolve@patch:resolve@npm%3A~1.22.1#optional!builtin": +"resolve@patch:resolve@npm%3A^1.1.6#optional!builtin, resolve@patch:resolve@npm%3A^1.10.0#optional!builtin, resolve@patch:resolve@npm%3A^1.12.0#optional!builtin, resolve@patch:resolve@npm%3A^1.14.2#optional!builtin, resolve@patch:resolve@npm%3A^1.19.0#optional!builtin, resolve@patch:resolve@npm%3A^1.20.0#optional!builtin, resolve@patch:resolve@npm%3A^1.22.1#optional!builtin, resolve@patch:resolve@npm%3A^1.22.3#optional!builtin, resolve@patch:resolve@npm%3A^1.22.4#optional!builtin, resolve@patch:resolve@npm%3A~1.22.1#optional!builtin": version: 1.22.8 resolution: "resolve@patch:resolve@npm%3A1.22.8#optional!builtin::version=1.22.8&hash=c3c19d" dependencies: @@ -31986,15 +31291,6 @@ __metadata: languageName: node linkType: hard -"run-applescript@npm:^5.0.0": - version: 5.0.0 - resolution: "run-applescript@npm:5.0.0" - dependencies: - execa: "npm:^5.0.0" - checksum: d00c2dbfa5b2d774de7451194b8b125f40f65fc183de7d9dcae97f57f59433586d3c39b9001e111c38bfa24c3436c99df1bb4066a2a0c90d39a8c4cd6889af77 - languageName: node - linkType: hard - "run-async@npm:^2.4.0": version: 2.4.1 resolution: "run-async@npm:2.4.1" @@ -32050,17 +31346,6 @@ __metadata: languageName: node linkType: hard -"sass-lookup@npm:^3.0.0": - version: 3.0.0 - resolution: "sass-lookup@npm:3.0.0" - dependencies: - commander: "npm:^2.16.0" - bin: - sass-lookup: bin/cli.js - checksum: 010b6ae2ee42554ef567a433a1febb9979e91ea99bfacc514e886cf966adba76cd075126d4eac3d557377c30ce2477825bd3d9b59a326571f295c99373a342da - languageName: node - linkType: hard - "sax@npm:^1.2.4": version: 1.3.0 resolution: "sax@npm:1.3.0" @@ -33076,15 +32361,6 @@ __metadata: languageName: node linkType: hard -"stream-to-array@npm:^2.3.0": - version: 2.3.0 - resolution: "stream-to-array@npm:2.3.0" - dependencies: - any-promise: "npm:^1.1.0" - checksum: 7feaf63b38399b850615e6ffcaa951e96e4c8f46745dbce4b553a94c5dc43966933813747014935a3ff97793e7f30a65270bde19f82b2932871a1879229a77cf - languageName: node - linkType: hard - "streamsearch@npm:^1.1.0": version: 1.1.0 resolution: "streamsearch@npm:1.1.0" @@ -33207,17 +32483,6 @@ __metadata: languageName: node linkType: hard -"stringify-object@npm:^3.2.1": - version: 3.3.0 - resolution: "stringify-object@npm:3.3.0" - dependencies: - get-own-enumerable-property-symbols: "npm:^3.0.0" - is-obj: "npm:^1.0.1" - is-regexp: "npm:^1.0.0" - checksum: 973782f09a3df3f39a2cf07dbf43fb9ba6cb32976f3616cd0f6c10e0a5c5415dd72b7b700e72920e8da2bf57c3001b8e37b5af7174bab9a748ce0416989e19b1 - languageName: node - linkType: hard - "strip-ansi-cjs@npm:strip-ansi@^6.0.1, strip-ansi@npm:^6.0.0, strip-ansi@npm:^6.0.1": version: 6.0.1 resolution: "strip-ansi@npm:6.0.1" @@ -33399,18 +32664,6 @@ __metadata: languageName: node linkType: hard -"stylus-lookup@npm:^3.0.1": - version: 3.0.2 - resolution: "stylus-lookup@npm:3.0.2" - dependencies: - commander: "npm:^2.8.1" - debug: "npm:^4.1.0" - bin: - stylus-lookup: bin/cli.js - checksum: 94da8b81ef16f73eae0dc734b18165e6f84d41960bc85fb39d9b028f4b6d8c4c2983ce52492751a3eb2e8e65e3e1507adc6739cc3c7893754672546c6af22f62 - languageName: node - linkType: hard - "subscriptions-transport-ws@npm:0.11.0": version: 0.11.0 resolution: "subscriptions-transport-ws@npm:0.11.0" @@ -33592,16 +32845,6 @@ __metadata: languageName: node linkType: hard -"synckit@npm:^0.8.5": - version: 0.8.5 - resolution: "synckit@npm:0.8.5" - dependencies: - "@pkgr/utils": "npm:^2.3.1" - tslib: "npm:^2.5.0" - checksum: fb6798a2db2650ca3a2435ad32d4fc14842da807993a1a350b64d267e0e770aa7f26492b119aa7500892d3d07a5af1eec7bfbd6e23a619451558be0f226a6094 - languageName: node - linkType: hard - "systeminformation@npm:^5.0.0": version: 5.21.17 resolution: "systeminformation@npm:5.21.17" @@ -33948,13 +33191,6 @@ __metadata: languageName: node linkType: hard -"titleize@npm:^3.0.0": - version: 3.0.0 - resolution: "titleize@npm:3.0.0" - checksum: 71fbbeabbfb36ccd840559f67f21e356e1d03da2915b32d2ae1a60ddcc13a124be2739f696d2feb884983441d159a18649e8d956648d591bdad35c430a6b6d28 - languageName: node - linkType: hard - "tmp-promise@npm:^3.0.2": version: 3.0.3 resolution: "tmp-promise@npm:3.0.3" @@ -34140,13 +33376,6 @@ __metadata: languageName: node linkType: hard -"ts-graphviz@npm:^1.5.0": - version: 1.8.1 - resolution: "ts-graphviz@npm:1.8.1" - checksum: 4936292e2b6104363b2d37f995fea766f8aefdabaecae10c963785e0c0b09da35bce78f961ac48897278f4f2148c2d71c7eeafaedcf273d8af06971ae5a01abc - languageName: node - linkType: hard - "ts-log@npm:^2.2.3": version: 2.2.5 resolution: "ts-log@npm:2.2.5" @@ -34206,18 +33435,6 @@ __metadata: languageName: node linkType: hard -"tsconfig-paths@npm:^3.10.1": - version: 3.14.2 - resolution: "tsconfig-paths@npm:3.14.2" - dependencies: - "@types/json5": "npm:^0.0.29" - json5: "npm:^1.0.2" - minimist: "npm:^1.2.6" - strip-bom: "npm:^3.0.0" - checksum: 17f23e98612a60cf23b80dc1d3b7b840879e41fcf603868fc3618a30f061ac7b463ef98cad8c28b68733b9bfe0cc40ffa2bcf29e94cf0d26e4f6addf7ac8527d - languageName: node - linkType: hard - "tsconfig-paths@npm:^4.1.2": version: 4.2.0 resolution: "tsconfig-paths@npm:4.2.0" @@ -34229,14 +33446,14 @@ __metadata: languageName: node linkType: hard -"tslib@npm:2.6.2, tslib@npm:^2.0.0, tslib@npm:^2.0.1, tslib@npm:^2.0.3, tslib@npm:^2.1.0, tslib@npm:^2.2.0, tslib@npm:^2.3.0, tslib@npm:^2.3.1, tslib@npm:^2.4.0, tslib@npm:^2.5.0, tslib@npm:^2.6.0, tslib@npm:^2.6.1, tslib@npm:^2.6.2": +"tslib@npm:2.6.2, tslib@npm:^2.0.0, tslib@npm:^2.0.1, tslib@npm:^2.0.3, tslib@npm:^2.1.0, tslib@npm:^2.2.0, tslib@npm:^2.3.0, tslib@npm:^2.3.1, tslib@npm:^2.4.0, tslib@npm:^2.5.0, tslib@npm:^2.6.1, tslib@npm:^2.6.2": version: 2.6.2 resolution: "tslib@npm:2.6.2" checksum: bd26c22d36736513980091a1e356378e8b662ded04204453d353a7f34a4c21ed0afc59b5f90719d4ba756e581a162ecbf93118dc9c6be5acf70aa309188166ca languageName: node linkType: hard -"tslib@npm:^1.11.1, tslib@npm:^1.13.0, tslib@npm:^1.8.1, tslib@npm:^1.9.0": +"tslib@npm:^1.11.1, tslib@npm:^1.13.0, tslib@npm:^1.9.0": version: 1.14.1 resolution: "tslib@npm:1.14.1" checksum: 7dbf34e6f55c6492637adb81b555af5e3b4f9cc6b998fb440dac82d3b42bdc91560a35a5fb75e20e24a076c651438234da6743d139e4feabf0783f3cdfe1dddb @@ -34250,17 +33467,6 @@ __metadata: languageName: node linkType: hard -"tsutils@npm:^3.21.0": - version: 3.21.0 - resolution: "tsutils@npm:3.21.0" - dependencies: - tslib: "npm:^1.8.1" - peerDependencies: - typescript: ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" - checksum: ea036bec1dd024e309939ffd49fda7a351c0e87a1b8eb049570dd119d447250e2c56e0e6c00554e8205760e7417793fdebff752a46e573fbe07d4f375502a5b2 - languageName: node - linkType: hard - "typanion@npm:^3.8.0": version: 3.14.0 resolution: "typanion@npm:3.14.0" @@ -34380,26 +33586,6 @@ __metadata: languageName: node linkType: hard -"typescript@npm:^3.9.10, typescript@npm:^3.9.7": - version: 3.9.10 - resolution: "typescript@npm:3.9.10" - bin: - tsc: bin/tsc - tsserver: bin/tsserver - checksum: f86a085eea24fdd665850c6be4dd69c7a71fe3d27ecc712be88cdc7a52d866f4f2416ad91c554df60f499dedcf288a24b3d8052e502833d8acc661a9d21bb98f - languageName: node - linkType: hard - -"typescript@npm:^4.0.0, typescript@npm:^4.9.5": - version: 4.9.5 - resolution: "typescript@npm:4.9.5" - bin: - tsc: bin/tsc - tsserver: bin/tsserver - checksum: 458f7220ab11e0fc191514cc41be1707645ec9a8c2d609448a448e18c522cef9646f58728f6811185a4c35613dacdf6c98cf8965c88b3541d0288c47291e4300 - languageName: node - linkType: hard - "typescript@npm:^5.2.2, typescript@npm:^5.3.2": version: 5.3.2 resolution: "typescript@npm:5.3.2" @@ -34420,26 +33606,6 @@ __metadata: languageName: node linkType: hard -"typescript@patch:typescript@npm%3A^3.9.10#optional!builtin, typescript@patch:typescript@npm%3A^3.9.7#optional!builtin": - version: 3.9.10 - resolution: "typescript@patch:typescript@npm%3A3.9.10#optional!builtin::version=3.9.10&hash=3bd3d3" - bin: - tsc: bin/tsc - tsserver: bin/tsserver - checksum: 77024c200e1f80d95185d5a973bcc4b0ecc12a77851390b1382e372195313769373a4b8b1642df4a8d4ae1df5911dcd9bde111cf6755eb4b6091f374e97c6dc5 - languageName: node - linkType: hard - -"typescript@patch:typescript@npm%3A^4.0.0#optional!builtin, typescript@patch:typescript@npm%3A^4.9.5#optional!builtin": - version: 4.9.5 - resolution: "typescript@patch:typescript@npm%3A4.9.5#optional!builtin::version=4.9.5&hash=289587" - bin: - tsc: bin/tsc - tsserver: bin/tsserver - checksum: 5659316360b5cc2d6f5931b346401fa534107b68b60179cf14970e27978f0936c1d5c46f4b5b8175f8cba0430f522b3ce355b4b724c0ea36ce6c0347fab25afd - languageName: node - linkType: hard - "typescript@patch:typescript@npm%3A^5.2.2#optional!builtin, typescript@patch:typescript@npm%3A^5.3.2#optional!builtin": version: 5.3.2 resolution: "typescript@patch:typescript@npm%3A5.3.2#optional!builtin::version=5.3.2&hash=29ae49" @@ -34575,13 +33741,6 @@ __metadata: languageName: node linkType: hard -"uniq@npm:^1.0.1": - version: 1.0.1 - resolution: "uniq@npm:1.0.1" - checksum: 8206535f83745ea83f9da7035f3b983fd6ed5e35b8ed7745441944e4065b616bc67cf0d0a23a86b40ee0074426f0607f0a138f9b78e124eb6a7a6a6966055709 - languageName: node - linkType: hard - "unique-filename@npm:^2.0.0": version: 2.0.1 resolution: "unique-filename@npm:2.0.1" @@ -35483,7 +34642,7 @@ __metadata: languageName: node linkType: hard -"walkdir@npm:^0.4.0, walkdir@npm:^0.4.1": +"walkdir@npm:^0.4.0": version: 0.4.1 resolution: "walkdir@npm:0.4.1" checksum: 54cbe7afc5fb811a55748b0bfa077a9a4aa43f568eb5857db9785af9728e1ad8b1ecf6b9ce6f14b405c6124939a92522e36aaa0397f3f52a9a7a08496f2eebe1