diff options
| author | dakkar <dakkar@thenautilus.net> | 2024-03-07 16:09:36 +0000 |
|---|---|---|
| committer | dakkar <dakkar@thenautilus.net> | 2024-03-07 16:09:36 +0000 |
| commit | dfff4d20731c9d356c28db6238a9e438618ff2e7 (patch) | |
| tree | 732119af4c809639356e71cd73f79eb41497d7dc | |
| parent | merge: check prohibited words when creating notes (!461) (diff) | |
| parent | fix(frontend): 周年の実績が閏年を考慮するように (#13525) (diff) | |
| download | sharkey-dfff4d20731c9d356c28db6238a9e438618ff2e7.tar.gz sharkey-dfff4d20731c9d356c28db6238a9e438618ff2e7.tar.bz2 sharkey-dfff4d20731c9d356c28db6238a9e438618ff2e7.zip | |
Merge remote-tracking branch 'misskey/develop' into future
55 files changed, 2128 insertions, 2073 deletions
diff --git a/.old/workflows/lint.yml b/.old/workflows/lint.yml deleted file mode 100644 index 8ad19b45c8..0000000000 --- a/.old/workflows/lint.yml +++ /dev/null @@ -1,95 +0,0 @@ -name: Lint - -on: - push: - branches: - - stable - - develop - paths: - - packages/** - pull_request: - paths: - - packages/backend/** - - packages/frontend/** - - packages/sw/** - - packages/misskey-js/** - - packages/shared/.eslintrc.js - -jobs: - pnpm_install: - runs-on: docker - steps: - - uses: actions/checkout@v4.1.1 - with: - fetch-depth: 0 - submodules: true - - uses: https://github.com/pnpm/action-setup@v2 - with: - version: 8 - run_install: false - - uses: https://code.forgejo.org/actions/setup-node@v4 - with: - node-version-file: '.node-version' - cache: 'pnpm' - - run: corepack enable - - run: pnpm i --frozen-lockfile - - lint: - needs: [pnpm_install] - runs-on: docker - continue-on-error: true - strategy: - matrix: - workspace: - - backend - - frontend - - sw - - misskey-js - steps: - - uses: actions/checkout@v4.1.1 - with: - fetch-depth: 0 - submodules: true - - uses: https://github.com/pnpm/action-setup@v2 - with: - version: 7 - run_install: false - - uses: https://code.forgejo.org/actions/setup-node@v4 - with: - node-version-file: '.node-version' - cache: 'pnpm' - - run: corepack enable - - run: pnpm i --frozen-lockfile - - run: pnpm --filter ${{ matrix.workspace }} run eslint - - typecheck: - needs: [pnpm_install] - runs-on: docker - continue-on-error: true - strategy: - matrix: - workspace: - - backend - - misskey-js - steps: - - uses: actions/checkout@v4.1.1 - with: - fetch-depth: 0 - submodules: true - - uses: https://github.com/pnpm/action-setup@v2 - with: - version: 7 - run_install: false - - uses: https://code.forgejo.org/actions/setup-node@v4 - with: - node-version-file: '.node-version' - cache: 'pnpm' - - run: corepack enable - - run: pnpm i --frozen-lockfile - - run: pnpm --filter misskey-js run build - if: ${{ matrix.workspace == 'backend' }} - - run: pnpm --filter misskey-reversi run build:tsc - if: ${{ matrix.workspace == 'backend' }} - - run: pnpm --filter misskey-bubble-game run build - if: ${{ matrix.workspace == 'backend' }} - - run: pnpm --filter ${{ matrix.workspace }} run typecheck diff --git a/CHANGELOG.md b/CHANGELOG.md index bafee277d2..be621e1ebd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,17 +1,19 @@ -<!-- -## 202x.x.x (unreleased) +## Unreleased ### General - ### Client -- +- Enhance: 自分のノートの添付ファイルから直接ファイルの詳細ページに飛べるように +- Enhance: 広告がMisskeyと同一ドメインの場合はRouterで遷移するように +- Enhance: リアクション・いいねの総数を表示するように +- Enhance: リアクション受け入れが「いいねのみ」の場合はリアクション絵文字一覧を表示しないように +- Fix: 一部のページ内リンクが正しく動作しない問題を修正 +- Fix: 周年の実績が閏年を考慮しない問題を修正 ### Server - ---> - ## 2024.3.1 ### General diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 50f323fab0..95f0383c70 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -307,6 +307,98 @@ export const handlers = [ Don't forget to re-run the `.storybook/generate.js` script after adding, editing, or removing the above files. +## Nest + +### Nest Service Circular dependency / Nestでサービスの循環参照でエラーが起きた場合 + +#### forwardRef +まずは簡単に`forwardRef`を試してみる + +```typescript +export class FooService { + constructor( + @Inject(forwardRef(() => BarService)) + private barService: BarService + ) { + } +} +``` + +#### OnModuleInit +できなければ`OnModuleInit`を使う + +```typescript +import { Injectable, OnModuleInit } from '@nestjs/common'; +import { ModuleRef } from '@nestjs/core'; +import { BarService } from '@/core/BarService'; + +@Injectable() +export class FooService implements OnModuleInit { + private barService: BarService // constructorから移動してくる + + constructor( + private moduleRef: ModuleRef, + ) { + } + + async onModuleInit() { + this.barService = this.moduleRef.get(BarService.name); + } + + public async niceMethod() { + return await this.barService.incredibleMethod({ hoge: 'fuga' }); + } +} +``` + +##### Service Unit Test +テストで`onModuleInit`を呼び出す必要がある + +```typescript +// import ... + +describe('test', () => { + let app: TestingModule; + let fooService: FooService; // for test case + let barService: BarService; // for test case + + beforeEach(async () => { + app = await Test.createTestingModule({ + imports: ..., + providers: [ + FooService, + { // mockする (mockは必須ではないかもしれない) + provide: BarService, + useFactory: () => ({ + incredibleMethod: jest.fn(), + }), + }, + { // Provideにする + provide: BarService.name, + useExisting: BarService, + }, + ], + }) + .useMocker(... + .compile(); + + fooService = app.get<FooService>(FooService); + barService = app.get<BarService>(BarService) as jest.Mocked<BarService>; + + // onModuleInitを実行する + await fooService.onModuleInit(); + }); + + test('nice', () => { + await fooService.niceMethod(); + + expect(barService.incredibleMethod).toHaveBeenCalled(); + expect(barService.incredibleMethod.mock.lastCall![0]) + .toEqual({ hoge: 'fuga' }); + }); +}) +``` + ## Notes ### Misskeyのドメイン固有の概念は`Mi`をprefixする diff --git a/locales/index.d.ts b/locales/index.d.ts index e407d2119b..b148161524 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -9167,7 +9167,11 @@ export interface Locale extends ILocale { */ "reactedBySomeUsers": ParameterizedString<"n">; /** - * {n}人がブーストしました + * {n}人がいいねしました + */ + "likedBySomeUsers": ParameterizedString<"n">; + /** + * {n}人がリノートしました */ "renotedBySomeUsers": ParameterizedString<"n">; /** diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 57f52c64b2..272bd2027e 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -2419,7 +2419,8 @@ _notification: sendTestNotification: "テスト通知を送信する" notificationWillBeDisplayedLikeThis: "通知はこのように表示されます" reactedBySomeUsers: "{n}人がリアクションしました" - renotedBySomeUsers: "{n}人がブーストしました" + likedBySomeUsers: "{n}人がいいねしました" + renotedBySomeUsers: "{n}人がリノートしました" followedBySomeUsers: "{n}人にフォローされました" flushNotification: "通知の履歴をリセットする" diff --git a/packages/backend/src/core/FetchInstanceMetadataService.ts b/packages/backend/src/core/FetchInstanceMetadataService.ts index bc270bd28f..8d173855f3 100644 --- a/packages/backend/src/core/FetchInstanceMetadataService.ts +++ b/packages/backend/src/core/FetchInstanceMetadataService.ts @@ -51,21 +51,35 @@ export class FetchInstanceMetadataService { } @bindThis - public async tryLock(host: string): Promise<boolean> { - const mutex = await this.redisClient.set(`fetchInstanceMetadata:mutex:${host}`, '1', 'GET'); - return mutex !== '1'; + // public for test + public async tryLock(host: string): Promise<string | null> { + // TODO: マイグレーションなのであとで消す (2024.3.1) + this.redisClient.del(`fetchInstanceMetadata:mutex:${host}`); + + return await this.redisClient.set( + `fetchInstanceMetadata:mutex:v2:${host}`, '1', + 'EX', 30, // 30秒したら自動でロック解除 https://github.com/misskey-dev/misskey/issues/13506#issuecomment-1975375395 + 'GET' // 古い値を返す(なかったらnull) + ); } @bindThis - public unlock(host: string): Promise<'OK'> { - return this.redisClient.set(`fetchInstanceMetadata:mutex:${host}`, '0'); + // public for test + public unlock(host: string): Promise<number> { + return this.redisClient.del(`fetchInstanceMetadata:mutex:v2:${host}`); } @bindThis public async fetchInstanceMetadata(instance: MiInstance, force = false): Promise<void> { const host = instance.host; - // Acquire mutex to ensure no parallel runs - if (!await this.tryLock(host)) return; + + // finallyでunlockされてしまうのでtry内でロックチェックをしない + // (returnであってもfinallyは実行される) + if (!force && await this.tryLock(host) === '1') { + // 1が返ってきていたらロックされているという意味なので、何もしない + return; + } + try { if (!force) { const _instance = await this.federatedInstanceService.fetch(host); diff --git a/packages/backend/src/core/entities/DriveFileEntityService.ts b/packages/backend/src/core/entities/DriveFileEntityService.ts index 8affe2b3bf..26bf386cbc 100644 --- a/packages/backend/src/core/entities/DriveFileEntityService.ts +++ b/packages/backend/src/core/entities/DriveFileEntityService.ts @@ -248,7 +248,7 @@ export class DriveFileEntityService { folder: opts.detail && file.folderId ? this.driveFolderEntityService.pack(file.folderId, { detail: true, }) : null, - userId: opts.withUser ? file.userId : null, + userId: file.userId, user: (opts.withUser && file.userId) ? this.userEntityService.pack(file.userId) : null, }); } diff --git a/packages/backend/src/core/entities/NoteEntityService.ts b/packages/backend/src/core/entities/NoteEntityService.ts index 86a8670f29..9f59f89d17 100644 --- a/packages/backend/src/core/entities/NoteEntityService.ts +++ b/packages/backend/src/core/entities/NoteEntityService.ts @@ -351,6 +351,7 @@ export class NoteEntityService implements OnModuleInit { visibleUserIds: note.visibility === 'specified' ? note.visibleUserIds : undefined, renoteCount: note.renoteCount, repliesCount: note.repliesCount, + reactionCount: Object.values(note.reactions).reduce((a, b) => a + b, 0), reactions: this.reactionService.convertLegacyReactions(note.reactions), reactionEmojis: this.customEmojiService.populateEmojis(reactionEmojiNames, host), reactionAndUserPairCache: opts.withReactionAndUserPairCache ? note.reactionAndUserPairCache : undefined, diff --git a/packages/backend/src/models/json-schema/note.ts b/packages/backend/src/models/json-schema/note.ts index bb4ccc7ee4..2641161c8b 100644 --- a/packages/backend/src/models/json-schema/note.ts +++ b/packages/backend/src/models/json-schema/note.ts @@ -223,6 +223,10 @@ export const packedNoteSchema = { }], }, }, + reactionCount: { + type: 'number', + optional: false, nullable: false, + }, renoteCount: { type: 'number', optional: false, nullable: false, diff --git a/packages/backend/src/server/web/boot.js b/packages/backend/src/server/web/boot.js index 6b9dc0a103..0543cc2b65 100644 --- a/packages/backend/src/server/web/boot.js +++ b/packages/backend/src/server/web/boot.js @@ -86,8 +86,8 @@ //#endregion //#region Script - function importAppScript() { - import(`/vite/${CLIENT_ENTRY}`) + async function importAppScript() { + await import(`/vite/${CLIENT_ENTRY}`) .catch(async e => { console.error(e); renderError('APP_IMPORT', e); diff --git a/packages/backend/test/e2e/2fa.ts b/packages/backend/test/e2e/2fa.ts index 87a3c227d6..13c56b88a6 100644 --- a/packages/backend/test/e2e/2fa.ts +++ b/packages/backend/test/e2e/2fa.ts @@ -187,7 +187,7 @@ describe('2要素認証', () => { }, 1000 * 60 * 2); test('が設定でき、OTPでログインできる。', async () => { - const registerResponse = await api('/i/2fa/register', { + const registerResponse = await api('i/2fa/register', { password, }, alice); assert.strictEqual(registerResponse.status, 200); @@ -197,18 +197,18 @@ describe('2要素認証', () => { assert.strictEqual(registerResponse.body.label, username); assert.strictEqual(registerResponse.body.issuer, config.host); - const doneResponse = await api('/i/2fa/done', { + const doneResponse = await api('i/2fa/done', { token: otpToken(registerResponse.body.secret), }, alice); assert.strictEqual(doneResponse.status, 200); - const usersShowResponse = await api('/users/show', { + const usersShowResponse = await api('users/show', { username, }, alice); assert.strictEqual(usersShowResponse.status, 200); assert.strictEqual(usersShowResponse.body.twoFactorEnabled, true); - const signinResponse = await api('/signin', { + const signinResponse = await api('signin', { ...signinParam(), token: otpToken(registerResponse.body.secret), }); @@ -216,24 +216,24 @@ describe('2要素認証', () => { assert.notEqual(signinResponse.body.i, undefined); // 後片付け - await api('/i/2fa/unregister', { + await api('i/2fa/unregister', { password, token: otpToken(registerResponse.body.secret), }, alice); }); test('が設定でき、セキュリティキーでログインできる。', async () => { - const registerResponse = await api('/i/2fa/register', { + const registerResponse = await api('i/2fa/register', { password, }, alice); assert.strictEqual(registerResponse.status, 200); - const doneResponse = await api('/i/2fa/done', { + const doneResponse = await api('i/2fa/done', { token: otpToken(registerResponse.body.secret), }, alice); assert.strictEqual(doneResponse.status, 200); - const registerKeyResponse = await api('/i/2fa/register-key', { + const registerKeyResponse = await api('i/2fa/register-key', { password, token: otpToken(registerResponse.body.secret), }, alice); @@ -243,23 +243,23 @@ describe('2要素認証', () => { const keyName = 'example-key'; const credentialId = crypto.randomBytes(0x41); - const keyDoneResponse = await api('/i/2fa/key-done', keyDoneParam({ + const keyDoneResponse = await api('i/2fa/key-done', keyDoneParam({ token: otpToken(registerResponse.body.secret), keyName, credentialId, creationOptions: registerKeyResponse.body, - }), alice); + }) as any, alice); assert.strictEqual(keyDoneResponse.status, 200); assert.strictEqual(keyDoneResponse.body.id, credentialId.toString('base64url')); assert.strictEqual(keyDoneResponse.body.name, keyName); - const usersShowResponse = await api('/users/show', { + const usersShowResponse = await api('users/show', { username, }); assert.strictEqual(usersShowResponse.status, 200); assert.strictEqual(usersShowResponse.body.securityKeys, true); - const signinResponse = await api('/signin', { + const signinResponse = await api('signin', { ...signinParam(), }); assert.strictEqual(signinResponse.status, 200); @@ -268,7 +268,7 @@ describe('2要素認証', () => { assert.notEqual(signinResponse.body.allowCredentials, undefined); assert.strictEqual(signinResponse.body.allowCredentials[0].id, credentialId.toString('base64url')); - const signinResponse2 = await api('/signin', signinWithSecurityKeyParam({ + const signinResponse2 = await api('signin', signinWithSecurityKeyParam({ keyName, credentialId, requestOptions: signinResponse.body, @@ -277,24 +277,24 @@ describe('2要素認証', () => { assert.notEqual(signinResponse2.body.i, undefined); // 後片付け - await api('/i/2fa/unregister', { + await api('i/2fa/unregister', { password, token: otpToken(registerResponse.body.secret), }, alice); }); test('が設定でき、セキュリティキーでパスワードレスログインできる。', async () => { - const registerResponse = await api('/i/2fa/register', { + const registerResponse = await api('i/2fa/register', { password, }, alice); assert.strictEqual(registerResponse.status, 200); - const doneResponse = await api('/i/2fa/done', { + const doneResponse = await api('i/2fa/done', { token: otpToken(registerResponse.body.secret), }, alice); assert.strictEqual(doneResponse.status, 200); - const registerKeyResponse = await api('/i/2fa/register-key', { + const registerKeyResponse = await api('i/2fa/register-key', { token: otpToken(registerResponse.body.secret), password, }, alice); @@ -302,33 +302,33 @@ describe('2要素認証', () => { const keyName = 'example-key'; const credentialId = crypto.randomBytes(0x41); - const keyDoneResponse = await api('/i/2fa/key-done', keyDoneParam({ + const keyDoneResponse = await api('i/2fa/key-done', keyDoneParam({ token: otpToken(registerResponse.body.secret), keyName, credentialId, creationOptions: registerKeyResponse.body, - }), alice); + }) as any, alice); assert.strictEqual(keyDoneResponse.status, 200); - const passwordLessResponse = await api('/i/2fa/password-less', { + const passwordLessResponse = await api('i/2fa/password-less', { value: true, }, alice); assert.strictEqual(passwordLessResponse.status, 204); - const usersShowResponse = await api('/users/show', { + const usersShowResponse = await api('users/show', { username, }); assert.strictEqual(usersShowResponse.status, 200); assert.strictEqual(usersShowResponse.body.usePasswordLessLogin, true); - const signinResponse = await api('/signin', { + const signinResponse = await api('signin', { ...signinParam(), password: '', }); assert.strictEqual(signinResponse.status, 200); assert.strictEqual(signinResponse.body.i, undefined); - const signinResponse2 = await api('/signin', { + const signinResponse2 = await api('signin', { ...signinWithSecurityKeyParam({ keyName, credentialId, @@ -340,24 +340,24 @@ describe('2要素認証', () => { assert.notEqual(signinResponse2.body.i, undefined); // 後片付け - await api('/i/2fa/unregister', { + await api('i/2fa/unregister', { password, token: otpToken(registerResponse.body.secret), }, alice); }); test('が設定でき、設定したセキュリティキーの名前を変更できる。', async () => { - const registerResponse = await api('/i/2fa/register', { + const registerResponse = await api('i/2fa/register', { password, }, alice); assert.strictEqual(registerResponse.status, 200); - const doneResponse = await api('/i/2fa/done', { + const doneResponse = await api('i/2fa/done', { token: otpToken(registerResponse.body.secret), }, alice); assert.strictEqual(doneResponse.status, 200); - const registerKeyResponse = await api('/i/2fa/register-key', { + const registerKeyResponse = await api('i/2fa/register-key', { token: otpToken(registerResponse.body.secret), password, }, alice); @@ -365,22 +365,22 @@ describe('2要素認証', () => { const keyName = 'example-key'; const credentialId = crypto.randomBytes(0x41); - const keyDoneResponse = await api('/i/2fa/key-done', keyDoneParam({ + const keyDoneResponse = await api('i/2fa/key-done', keyDoneParam({ token: otpToken(registerResponse.body.secret), keyName, credentialId, creationOptions: registerKeyResponse.body, - }), alice); + }) as any, alice); assert.strictEqual(keyDoneResponse.status, 200); const renamedKey = 'other-key'; - const updateKeyResponse = await api('/i/2fa/update-key', { + const updateKeyResponse = await api('i/2fa/update-key', { name: renamedKey, credentialId: credentialId.toString('base64url'), }, alice); assert.strictEqual(updateKeyResponse.status, 200); - const iResponse = await api('/i', { + const iResponse = await api('i', { }, alice); assert.strictEqual(iResponse.status, 200); const securityKeys = iResponse.body.securityKeysList.filter((s: { id: string; }) => s.id === credentialId.toString('base64url')); @@ -389,24 +389,24 @@ describe('2要素認証', () => { assert.notEqual(securityKeys[0].lastUsed, undefined); // 後片付け - await api('/i/2fa/unregister', { + await api('i/2fa/unregister', { password, token: otpToken(registerResponse.body.secret), }, alice); }); test('が設定でき、設定したセキュリティキーを削除できる。', async () => { - const registerResponse = await api('/i/2fa/register', { + const registerResponse = await api('i/2fa/register', { password, }, alice); assert.strictEqual(registerResponse.status, 200); - const doneResponse = await api('/i/2fa/done', { + const doneResponse = await api('i/2fa/done', { token: otpToken(registerResponse.body.secret), }, alice); assert.strictEqual(doneResponse.status, 200); - const registerKeyResponse = await api('/i/2fa/register-key', { + const registerKeyResponse = await api('i/2fa/register-key', { token: otpToken(registerResponse.body.secret), password, }, alice); @@ -414,20 +414,20 @@ describe('2要素認証', () => { const keyName = 'example-key'; const credentialId = crypto.randomBytes(0x41); - const keyDoneResponse = await api('/i/2fa/key-done', keyDoneParam({ + const keyDoneResponse = await api('i/2fa/key-done', keyDoneParam({ token: otpToken(registerResponse.body.secret), keyName, credentialId, creationOptions: registerKeyResponse.body, - }), alice); + }) as any, alice); assert.strictEqual(keyDoneResponse.status, 200); // テストの実行順によっては複数残ってるので全部消す - const iResponse = await api('/i', { + const iResponse = await api('i', { }, alice); assert.strictEqual(iResponse.status, 200); for (const key of iResponse.body.securityKeysList) { - const removeKeyResponse = await api('/i/2fa/remove-key', { + const removeKeyResponse = await api('i/2fa/remove-key', { token: otpToken(registerResponse.body.secret), password, credentialId: key.id, @@ -435,13 +435,13 @@ describe('2要素認証', () => { assert.strictEqual(removeKeyResponse.status, 200); } - const usersShowResponse = await api('/users/show', { + const usersShowResponse = await api('users/show', { username, }); assert.strictEqual(usersShowResponse.status, 200); assert.strictEqual(usersShowResponse.body.securityKeys, false); - const signinResponse = await api('/signin', { + const signinResponse = await api('signin', { ...signinParam(), token: otpToken(registerResponse.body.secret), }); @@ -449,43 +449,43 @@ describe('2要素認証', () => { assert.notEqual(signinResponse.body.i, undefined); // 後片付け - await api('/i/2fa/unregister', { + await api('i/2fa/unregister', { password, token: otpToken(registerResponse.body.secret), }, alice); }); test('が設定でき、設定解除できる。(パスワードのみでログインできる。)', async () => { - const registerResponse = await api('/i/2fa/register', { + const registerResponse = await api('i/2fa/register', { password, }, alice); assert.strictEqual(registerResponse.status, 200); - const doneResponse = await api('/i/2fa/done', { + const doneResponse = await api('i/2fa/done', { token: otpToken(registerResponse.body.secret), }, alice); assert.strictEqual(doneResponse.status, 200); - const usersShowResponse = await api('/users/show', { + const usersShowResponse = await api('users/show', { username, }); assert.strictEqual(usersShowResponse.status, 200); assert.strictEqual(usersShowResponse.body.twoFactorEnabled, true); - const unregisterResponse = await api('/i/2fa/unregister', { + const unregisterResponse = await api('i/2fa/unregister', { token: otpToken(registerResponse.body.secret), password, }, alice); assert.strictEqual(unregisterResponse.status, 204); - const signinResponse = await api('/signin', { + const signinResponse = await api('signin', { ...signinParam(), }); assert.strictEqual(signinResponse.status, 200); assert.notEqual(signinResponse.body.i, undefined); // 後片付け - await api('/i/2fa/unregister', { + await api('i/2fa/unregister', { password, token: otpToken(registerResponse.body.secret), }, alice); diff --git a/packages/backend/test/e2e/antennas.ts b/packages/backend/test/e2e/antennas.ts index 1a9d5bf1f0..7370b1963c 100644 --- a/packages/backend/test/e2e/antennas.ts +++ b/packages/backend/test/e2e/antennas.ts @@ -7,7 +7,6 @@ process.env.NODE_ENV = 'test'; import * as assert from 'assert'; import { DEFAULT_POLICIES } from '@/core/RoleService.js'; -import type { Packed } from '@/misc/json-schema.js'; import { api, failedApiCall, @@ -29,10 +28,7 @@ describe('アンテナ', () => { // エンティティとしてのアンテナを主眼においたテストを記述する // (Antennaを返すエンドポイント、Antennaエンティティを書き換えるエンドポイント、Antennaからノートを取得するエンドポイントをテストする) - // BUG misskey-jsとjson-schemaが一致していない。 - // - srcのenumにgroupが残っている - // - userGroupIdが残っている, isActiveがない - type Antenna = misskey.entities.Antenna | Packed<'Antenna'>; + type Antenna = misskey.entities.Antenna; type User = misskey.entities.SignupResponse; type Note = misskey.entities.Note; @@ -80,7 +76,7 @@ describe('アンテナ', () => { aliceList = await userList(alice, {}); bob = await signup({ username: 'bob' }); aliceList = await userList(alice, {}); - bobFile = (await uploadFile(bob)).body; + bobFile = (await uploadFile(bob)).body!; bobList = await userList(bob); carol = await signup({ username: 'carol' }); await api('users/lists/push', { listId: aliceList.id, userId: bob.id }, alice); @@ -129,9 +125,9 @@ describe('アンテナ', () => { beforeEach(async () => { // テスト間で影響し合わないように毎回全部消す。 for (const user of [alice, bob]) { - const list = await api('/antennas/list', {}, user); + const list = await api('antennas/list', {}, user); for (const antenna of list.body) { - await api('/antennas/delete', { antennaId: antenna.id }, user); + await api('antennas/delete', { antennaId: antenna.id }, user); } } }); @@ -141,11 +137,11 @@ describe('アンテナ', () => { test('が作成できること、キーが過不足なく入っていること。', async () => { const response = await successfulApiCall({ endpoint: 'antennas/create', - parameters: { ...defaultParam }, + parameters: defaultParam, user: alice, }); assert.match(response.id, /[0-9a-z]{10}/); - const expected = { + const expected: Antenna = { id: response.id, caseSensitive: false, createdAt: new Date(response.createdAt).toISOString(), @@ -161,7 +157,7 @@ describe('アンテナ', () => { withFile: false, withReplies: false, localOnly: false, - } as Antenna; + }; assert.deepStrictEqual(response, expected); }); @@ -202,27 +198,27 @@ describe('アンテナ', () => { }); const antennaParamPattern = [ - { parameters: (): object => ({ name: 'x'.repeat(100) }) }, - { parameters: (): object => ({ name: 'x' }) }, - { parameters: (): object => ({ src: 'home' }) }, - { parameters: (): object => ({ src: 'all' }) }, - { parameters: (): object => ({ src: 'users' }) }, - { parameters: (): object => ({ src: 'list' }) }, - { parameters: (): object => ({ userListId: null }) }, - { parameters: (): object => ({ src: 'list', userListId: aliceList.id }) }, - { parameters: (): object => ({ keywords: [['x']] }) }, - { parameters: (): object => ({ keywords: [['a', 'b', 'c'], ['x'], ['y'], ['z']] }) }, - { parameters: (): object => ({ excludeKeywords: [['a', 'b', 'c'], ['x'], ['y'], ['z']] }) }, - { parameters: (): object => ({ users: [alice.username] }) }, - { parameters: (): object => ({ users: [alice.username, bob.username, carol.username] }) }, - { parameters: (): object => ({ caseSensitive: false }) }, - { parameters: (): object => ({ caseSensitive: true }) }, - { parameters: (): object => ({ withReplies: false }) }, - { parameters: (): object => ({ withReplies: true }) }, - { parameters: (): object => ({ withFile: false }) }, - { parameters: (): object => ({ withFile: true }) }, - { parameters: (): object => ({ notify: false }) }, - { parameters: (): object => ({ notify: true }) }, + { parameters: () => ({ name: 'x'.repeat(100) }) }, + { parameters: () => ({ name: 'x' }) }, + { parameters: () => ({ src: 'home' as const }) }, + { parameters: () => ({ src: 'all' as const }) }, + { parameters: () => ({ src: 'users' as const }) }, + { parameters: () => ({ src: 'list' as const }) }, + { parameters: () => ({ userListId: null }) }, + { parameters: () => ({ src: 'list' as const, userListId: aliceList.id }) }, + { parameters: () => ({ keywords: [['x']] }) }, + { parameters: () => ({ keywords: [['a', 'b', 'c'], ['x'], ['y'], ['z']] }) }, + { parameters: () => ({ excludeKeywords: [['a', 'b', 'c'], ['x'], ['y'], ['z']] }) }, + { parameters: () => ({ users: [alice.username] }) }, + { parameters: () => ({ users: [alice.username, bob.username, carol.username] }) }, + { parameters: () => ({ caseSensitive: false }) }, + { parameters: () => ({ caseSensitive: true }) }, + { parameters: () => ({ withReplies: false }) }, + { parameters: () => ({ withReplies: true }) }, + { parameters: () => ({ withFile: false }) }, + { parameters: () => ({ withFile: true }) }, + { parameters: () => ({ notify: false }) }, + { parameters: () => ({ notify: true }) }, ]; test.each(antennaParamPattern)('を作成できること($#)', async ({ parameters }) => { const response = await successfulApiCall({ @@ -335,7 +331,7 @@ describe('アンテナ', () => { test.each([ { label: '全体から', - parameters: (): object => ({ src: 'all' }), + parameters: () => ({ src: 'all' }), posts: [ { note: (): Promise<Note> => post(alice, { text: `${keyword}` }), included: true }, { note: (): Promise<Note> => post(userFollowedByAlice, { text: `${keyword}` }), included: true }, @@ -346,7 +342,7 @@ describe('アンテナ', () => { { // BUG e4144a1 以降home指定は壊れている(allと同じ) label: 'ホーム指定はallと同じ', - parameters: (): object => ({ src: 'home' }), + parameters: () => ({ src: 'home' }), posts: [ { note: (): Promise<Note> => post(alice, { text: `${keyword}` }), included: true }, { note: (): Promise<Note> => post(userFollowedByAlice, { text: `${keyword}` }), included: true }, @@ -357,7 +353,7 @@ describe('アンテナ', () => { { // https://github.com/misskey-dev/misskey/issues/9025 label: 'ただし、フォロワー限定投稿とDM投稿を含まない。フォロワーであっても。', - parameters: (): object => ({}), + parameters: () => ({}), posts: [ { note: (): Promise<Note> => post(userFollowedByAlice, { text: `${keyword}`, visibility: 'public' }), included: true }, { note: (): Promise<Note> => post(userFollowedByAlice, { text: `${keyword}`, visibility: 'home' }), included: true }, @@ -367,56 +363,56 @@ describe('アンテナ', () => { }, { label: 'ブロックしているユーザーのノートは含む', - parameters: (): object => ({}), + parameters: () => ({}), posts: [ { note: (): Promise<Note> => post(userBlockedByAlice, { text: `${keyword}` }), included: true }, ], }, { label: 'ブロックされているユーザーのノートは含まない', - parameters: (): object => ({}), + parameters: () => ({}), posts: [ { note: (): Promise<Note> => post(userBlockingAlice, { text: `${keyword}` }) }, ], }, { label: 'ミュートしているユーザーのノートは含まない', - parameters: (): object => ({}), + parameters: () => ({}), posts: [ { note: (): Promise<Note> => post(userMutedByAlice, { text: `${keyword}` }) }, ], }, { label: 'ミュートされているユーザーのノートは含む', - parameters: (): object => ({}), + parameters: () => ({}), posts: [ { note: (): Promise<Note> => post(userMutingAlice, { text: `${keyword}` }), included: true }, ], }, { label: '「見つけやすくする」がOFFのユーザーのノートも含まれる', - parameters: (): object => ({}), + parameters: () => ({}), posts: [ { note: (): Promise<Note> => post(userNotExplorable, { text: `${keyword}` }), included: true }, ], }, { label: '鍵付きユーザーのノートも含まれる', - parameters: (): object => ({}), + parameters: () => ({}), posts: [ { note: (): Promise<Note> => post(userLocking, { text: `${keyword}` }), included: true }, ], }, { label: 'サイレンスのノートも含まれる', - parameters: (): object => ({}), + parameters: () => ({}), posts: [ { note: (): Promise<Note> => post(userSilenced, { text: `${keyword}` }), included: true }, ], }, { label: '削除ユーザーのノートも含まれる', - parameters: (): object => ({}), + parameters: () => ({}), posts: [ { note: (): Promise<Note> => post(userDeletedBySelf, { text: `${keyword}` }), included: true }, { note: (): Promise<Note> => post(userDeletedByAdmin, { text: `${keyword}` }), included: true }, @@ -424,7 +420,7 @@ describe('アンテナ', () => { }, { label: 'ユーザー指定で', - parameters: (): object => ({ src: 'users', users: [`@${bob.username}`, `@${carol.username}`] }), + parameters: () => ({ src: 'users', users: [`@${bob.username}`, `@${carol.username}`] }), posts: [ { note: (): Promise<Note> => post(alice, { text: `test ${keyword}` }) }, { note: (): Promise<Note> => post(bob, { text: `test ${keyword}` }), included: true }, @@ -433,7 +429,7 @@ describe('アンテナ', () => { }, { label: 'リスト指定で', - parameters: (): object => ({ src: 'list', userListId: aliceList.id }), + parameters: () => ({ src: 'list', userListId: aliceList.id }), posts: [ { note: (): Promise<Note> => post(alice, { text: `test ${keyword}` }) }, { note: (): Promise<Note> => post(bob, { text: `test ${keyword}` }), included: true }, @@ -442,14 +438,14 @@ describe('アンテナ', () => { }, { label: 'CWにもマッチする', - parameters: (): object => ({ keywords: [[keyword]] }), + parameters: () => ({ keywords: [[keyword]] }), posts: [ { note: (): Promise<Note> => post(bob, { text: 'test', cw: `cw ${keyword}` }), included: true }, ], }, { label: 'キーワード1つ', - parameters: (): object => ({ keywords: [[keyword]] }), + parameters: () => ({ keywords: [[keyword]] }), posts: [ { note: (): Promise<Note> => post(alice, { text: 'test' }) }, { note: (): Promise<Note> => post(bob, { text: `test ${keyword}` }), included: true }, @@ -458,7 +454,7 @@ describe('アンテナ', () => { }, { label: 'キーワード3つ(AND)', - parameters: (): object => ({ keywords: [['A', 'B', 'C']] }), + parameters: () => ({ keywords: [['A', 'B', 'C']] }), posts: [ { note: (): Promise<Note> => post(bob, { text: 'test A' }) }, { note: (): Promise<Note> => post(bob, { text: 'test A B' }) }, @@ -469,7 +465,7 @@ describe('アンテナ', () => { }, { label: 'キーワード3つ(OR)', - parameters: (): object => ({ keywords: [['A'], ['B'], ['C']] }), + parameters: () => ({ keywords: [['A'], ['B'], ['C']] }), posts: [ { note: (): Promise<Note> => post(bob, { text: 'test' }) }, { note: (): Promise<Note> => post(bob, { text: 'test A' }), included: true }, @@ -482,7 +478,7 @@ describe('アンテナ', () => { }, { label: '除外ワード3つ(AND)', - parameters: (): object => ({ excludeKeywords: [['A', 'B', 'C']] }), + parameters: () => ({ excludeKeywords: [['A', 'B', 'C']] }), posts: [ { note: (): Promise<Note> => post(bob, { text: `test ${keyword}` }), included: true }, { note: (): Promise<Note> => post(bob, { text: `test ${keyword} A` }), included: true }, @@ -495,7 +491,7 @@ describe('アンテナ', () => { }, { label: '除外ワード3つ(OR)', - parameters: (): object => ({ excludeKeywords: [['A'], ['B'], ['C']] }), + parameters: () => ({ excludeKeywords: [['A'], ['B'], ['C']] }), posts: [ { note: (): Promise<Note> => post(bob, { text: `test ${keyword}` }), included: true }, { note: (): Promise<Note> => post(bob, { text: `test ${keyword} A` }) }, @@ -508,7 +504,7 @@ describe('アンテナ', () => { }, { label: 'キーワード1つ(大文字小文字区別する)', - parameters: (): object => ({ keywords: [['KEYWORD']], caseSensitive: true }), + parameters: () => ({ keywords: [['KEYWORD']], caseSensitive: true }), posts: [ { note: (): Promise<Note> => post(bob, { text: 'keyword' }) }, { note: (): Promise<Note> => post(bob, { text: 'kEyWoRd' }) }, @@ -517,7 +513,7 @@ describe('アンテナ', () => { }, { label: 'キーワード1つ(大文字小文字区別しない)', - parameters: (): object => ({ keywords: [['KEYWORD']], caseSensitive: false }), + parameters: () => ({ keywords: [['KEYWORD']], caseSensitive: false }), posts: [ { note: (): Promise<Note> => post(bob, { text: 'keyword' }), included: true }, { note: (): Promise<Note> => post(bob, { text: 'kEyWoRd' }), included: true }, @@ -526,7 +522,7 @@ describe('アンテナ', () => { }, { label: '除外ワード1つ(大文字小文字区別する)', - parameters: (): object => ({ excludeKeywords: [['KEYWORD']], caseSensitive: true }), + parameters: () => ({ excludeKeywords: [['KEYWORD']], caseSensitive: true }), posts: [ { note: (): Promise<Note> => post(bob, { text: `${keyword}` }), included: true }, { note: (): Promise<Note> => post(bob, { text: `${keyword} keyword` }), included: true }, @@ -536,7 +532,7 @@ describe('アンテナ', () => { }, { label: '除外ワード1つ(大文字小文字区別しない)', - parameters: (): object => ({ excludeKeywords: [['KEYWORD']], caseSensitive: false }), + parameters: () => ({ excludeKeywords: [['KEYWORD']], caseSensitive: false }), posts: [ { note: (): Promise<Note> => post(bob, { text: `${keyword}` }), included: true }, { note: (): Promise<Note> => post(bob, { text: `${keyword} keyword` }) }, @@ -546,7 +542,7 @@ describe('アンテナ', () => { }, { label: '添付ファイルを問わない', - parameters: (): object => ({ withFile: false }), + parameters: () => ({ withFile: false }), posts: [ { note: (): Promise<Note> => post(bob, { text: `${keyword}`, fileIds: [bobFile.id] }), included: true }, { note: (): Promise<Note> => post(bob, { text: `${keyword}` }), included: true }, @@ -554,7 +550,7 @@ describe('アンテナ', () => { }, { label: '添付ファイル付きのみ', - parameters: (): object => ({ withFile: true }), + parameters: () => ({ withFile: true }), posts: [ { note: (): Promise<Note> => post(bob, { text: `${keyword}`, fileIds: [bobFile.id] }), included: true }, { note: (): Promise<Note> => post(bob, { text: `${keyword}` }) }, @@ -562,7 +558,7 @@ describe('アンテナ', () => { }, { label: 'リプライ以外', - parameters: (): object => ({ withReplies: false }), + parameters: () => ({ withReplies: false }), posts: [ { note: (): Promise<Note> => post(bob, { text: `${keyword}`, replyId: alicePost.id }) }, { note: (): Promise<Note> => post(bob, { text: `${keyword}` }), included: true }, @@ -570,7 +566,7 @@ describe('アンテナ', () => { }, { label: 'リプライも含む', - parameters: (): object => ({ withReplies: true }), + parameters: () => ({ withReplies: true }), posts: [ { note: (): Promise<Note> => post(bob, { text: `${keyword}`, replyId: alicePost.id }), included: true }, { note: (): Promise<Note> => post(bob, { text: `${keyword}` }), included: true }, @@ -633,7 +629,7 @@ describe('アンテナ', () => { endpoint: 'antennas/notes', parameters: { antennaId: antenna.id, ...paginationParam }, user: alice, - }) as any as Note[]; + }); }, offsetBy, 'desc'); }); diff --git a/packages/backend/test/e2e/api-visibility.ts b/packages/backend/test/e2e/api-visibility.ts index f92384525c..c61b0c2a86 100644 --- a/packages/backend/test/e2e/api-visibility.ts +++ b/packages/backend/test/e2e/api-visibility.ts @@ -6,7 +6,7 @@ process.env.NODE_ENV = 'test'; import * as assert from 'assert'; -import { api, post, signup } from '../utils.js'; +import { UserToken, api, post, signup } from '../utils.js'; import type * as misskey from 'misskey-js'; describe('API visibility', () => { @@ -24,38 +24,38 @@ describe('API visibility', () => { let target2: misskey.entities.SignupResponse; /** public-post */ - let pub: any; + let pub: misskey.entities.Note; /** home-post */ - let home: any; + let home: misskey.entities.Note; /** followers-post */ - let fol: any; + let fol: misskey.entities.Note; /** specified-post */ - let spe: any; + let spe: misskey.entities.Note; /** public-reply to target's post */ - let pubR: any; + let pubR: misskey.entities.Note; /** home-reply to target's post */ - let homeR: any; + let homeR: misskey.entities.Note; /** followers-reply to target's post */ - let folR: any; + let folR: misskey.entities.Note; /** specified-reply to target's post */ - let speR: any; + let speR: misskey.entities.Note; /** public-mention to target */ - let pubM: any; + let pubM: misskey.entities.Note; /** home-mention to target */ - let homeM: any; + let homeM: misskey.entities.Note; /** followers-mention to target */ - let folM: any; + let folM: misskey.entities.Note; /** specified-mention to target */ - let speM: any; + let speM: misskey.entities.Note; /** reply target post */ - let tgt: any; + let tgt: misskey.entities.Note; //#endregion - const show = async (noteId: any, by: any) => { - return await api('/notes/show', { + const show = async (noteId: misskey.entities.Note['id'], by?: UserToken) => { + return await api('notes/show', { noteId, }, by); }; @@ -70,7 +70,7 @@ describe('API visibility', () => { target2 = await signup({ username: 'target2' }); // follow alice <= follower - await api('/following/create', { userId: alice.id }, follower); + await api('following/create', { userId: alice.id }, follower); // normal posts pub = await post(alice, { text: 'x', visibility: 'public' }); @@ -111,7 +111,7 @@ describe('API visibility', () => { }); test('[show] public-postを未認証が見れる', async () => { - const res = await show(pub.id, null); + const res = await show(pub.id); assert.strictEqual(res.body.text, 'x'); }); @@ -132,7 +132,7 @@ describe('API visibility', () => { }); test('[show] home-postを未認証が見れる', async () => { - const res = await show(home.id, null); + const res = await show(home.id); assert.strictEqual(res.body.text, 'x'); }); @@ -153,7 +153,7 @@ describe('API visibility', () => { }); test('[show] followers-postを未認証が見れない', async () => { - const res = await show(fol.id, null); + const res = await show(fol.id); assert.strictEqual(res.body.isHidden, true); }); @@ -179,7 +179,7 @@ describe('API visibility', () => { }); test('[show] specified-postを未認証が見れない', async () => { - const res = await show(spe.id, null); + const res = await show(spe.id); assert.strictEqual(res.body.isHidden, true); }); //#endregion @@ -207,7 +207,7 @@ describe('API visibility', () => { }); test('[show] public-replyを未認証が見れる', async () => { - const res = await show(pubR.id, null); + const res = await show(pubR.id); assert.strictEqual(res.body.text, 'x'); }); @@ -233,7 +233,7 @@ describe('API visibility', () => { }); test('[show] home-replyを未認証が見れる', async () => { - const res = await show(homeR.id, null); + const res = await show(homeR.id); assert.strictEqual(res.body.text, 'x'); }); @@ -259,7 +259,7 @@ describe('API visibility', () => { }); test('[show] followers-replyを未認証が見れない', async () => { - const res = await show(folR.id, null); + const res = await show(folR.id); assert.strictEqual(res.body.isHidden, true); }); @@ -290,7 +290,7 @@ describe('API visibility', () => { }); test('[show] specified-replyを未認証が見れない', async () => { - const res = await show(speR.id, null); + const res = await show(speR.id); assert.strictEqual(res.body.isHidden, true); }); //#endregion @@ -318,7 +318,7 @@ describe('API visibility', () => { }); test('[show] public-mentionを未認証が見れる', async () => { - const res = await show(pubM.id, null); + const res = await show(pubM.id); assert.strictEqual(res.body.text, '@target x'); }); @@ -344,7 +344,7 @@ describe('API visibility', () => { }); test('[show] home-mentionを未認証が見れる', async () => { - const res = await show(homeM.id, null); + const res = await show(homeM.id); assert.strictEqual(res.body.text, '@target x'); }); @@ -370,7 +370,7 @@ describe('API visibility', () => { }); test('[show] followers-mentionを未認証が見れない', async () => { - const res = await show(folM.id, null); + const res = await show(folM.id); assert.strictEqual(res.body.isHidden, true); }); @@ -401,28 +401,28 @@ describe('API visibility', () => { }); test('[show] specified-mentionを未認証が見れない', async () => { - const res = await show(speM.id, null); + const res = await show(speM.id); assert.strictEqual(res.body.isHidden, true); }); //#endregion //#region HTL test('[HTL] public-post が 自分が見れる', async () => { - const res = await api('/notes/timeline', { limit: 100 }, alice); + const res = await api('notes/timeline', { limit: 100 }, alice); assert.strictEqual(res.status, 200); const notes = res.body.filter((n: any) => n.id === pub.id); assert.strictEqual(notes[0].text, 'x'); }); test('[HTL] public-post が 非フォロワーから見れない', async () => { - const res = await api('/notes/timeline', { limit: 100 }, other); + const res = await api('notes/timeline', { limit: 100 }, other); assert.strictEqual(res.status, 200); const notes = res.body.filter((n: any) => n.id === pub.id); assert.strictEqual(notes.length, 0); }); test('[HTL] followers-post が フォロワーから見れる', async () => { - const res = await api('/notes/timeline', { limit: 100 }, follower); + const res = await api('notes/timeline', { limit: 100 }, follower); assert.strictEqual(res.status, 200); const notes = res.body.filter((n: any) => n.id === fol.id); assert.strictEqual(notes[0].text, 'x'); @@ -431,21 +431,21 @@ describe('API visibility', () => { //#region RTL test('[replies] followers-reply が フォロワーから見れる', async () => { - const res = await api('/notes/replies', { noteId: tgt.id, limit: 100 }, follower); + const res = await api('notes/replies', { noteId: tgt.id, limit: 100 }, follower); assert.strictEqual(res.status, 200); const notes = res.body.filter((n: any) => n.id === folR.id); assert.strictEqual(notes[0].text, 'x'); }); test('[replies] followers-reply が 非フォロワー (リプライ先ではない) から見れない', async () => { - const res = await api('/notes/replies', { noteId: tgt.id, limit: 100 }, other); + const res = await api('notes/replies', { noteId: tgt.id, limit: 100 }, other); assert.strictEqual(res.status, 200); const notes = res.body.filter((n: any) => n.id === folR.id); assert.strictEqual(notes.length, 0); }); test('[replies] followers-reply が 非フォロワー (リプライ先である) から見れる', async () => { - const res = await api('/notes/replies', { noteId: tgt.id, limit: 100 }, target); + const res = await api('notes/replies', { noteId: tgt.id, limit: 100 }, target); assert.strictEqual(res.status, 200); const notes = res.body.filter((n: any) => n.id === folR.id); assert.strictEqual(notes[0].text, 'x'); @@ -454,14 +454,14 @@ describe('API visibility', () => { //#region MTL test('[mentions] followers-reply が 非フォロワー (リプライ先である) から見れる', async () => { - const res = await api('/notes/mentions', { limit: 100 }, target); + const res = await api('notes/mentions', { limit: 100 }, target); assert.strictEqual(res.status, 200); const notes = res.body.filter((n: any) => n.id === folR.id); assert.strictEqual(notes[0].text, 'x'); }); test('[mentions] followers-mention が 非フォロワー (メンション先である) から見れる', async () => { - const res = await api('/notes/mentions', { limit: 100 }, target); + const res = await api('notes/mentions', { limit: 100 }, target); assert.strictEqual(res.status, 200); const notes = res.body.filter((n: any) => n.id === folM.id); assert.strictEqual(notes[0].text, '@target x'); diff --git a/packages/backend/test/e2e/api.ts b/packages/backend/test/e2e/api.ts index b6eeec99d7..49c6a0636b 100644 --- a/packages/backend/test/e2e/api.ts +++ b/packages/backend/test/e2e/api.ts @@ -23,32 +23,32 @@ import type * as misskey from 'misskey-js'; describe('API', () => { let alice: misskey.entities.SignupResponse; let bob: misskey.entities.SignupResponse; - let carol: misskey.entities.SignupResponse; beforeAll(async () => { alice = await signup({ username: 'alice' }); bob = await signup({ username: 'bob' }); - carol = await signup({ username: 'carol' }); }, 1000 * 60 * 2); describe('General validation', () => { test('wrong type', async () => { - const res = await api('/test', { + const res = await api('test', { required: true, + // @ts-expect-error string must be string string: 42, }); assert.strictEqual(res.status, 400); }); test('missing require param', async () => { - const res = await api('/test', { + // @ts-expect-error required is required + const res = await api('test', { string: 'a', }); assert.strictEqual(res.status, 400); }); test('invalid misskey:id (empty string)', async () => { - const res = await api('/test', { + const res = await api('test', { required: true, id: '', }); @@ -56,7 +56,7 @@ describe('API', () => { }); test('valid misskey:id', async () => { - const res = await api('/test', { + const res = await api('test', { required: true, id: '8wvhjghbxu', }); @@ -64,7 +64,7 @@ describe('API', () => { }); test('default value', async () => { - const res = await api('/test', { + const res = await api('test', { required: true, string: 'a', }); @@ -73,7 +73,7 @@ describe('API', () => { }); test('can set null even if it has default value', async () => { - const res = await api('/test', { + const res = await api('test', { required: true, nullableDefault: null, }); @@ -82,7 +82,7 @@ describe('API', () => { }); test('cannot set undefined if it has default value', async () => { - const res = await api('/test', { + const res = await api('test', { required: true, nullableDefault: undefined, }); @@ -99,14 +99,14 @@ describe('API', () => { // aliceは管理者、APIを使える await successfulApiCall({ - endpoint: '/admin/get-index-stats', + endpoint: 'admin/get-index-stats', parameters: {}, user: alice, }); // bobは一般ユーザーだからダメ await failedApiCall({ - endpoint: '/admin/get-index-stats', + endpoint: 'admin/get-index-stats', parameters: {}, user: bob, }, { @@ -117,7 +117,7 @@ describe('API', () => { // publicアクセスももちろんダメ await failedApiCall({ - endpoint: '/admin/get-index-stats', + endpoint: 'admin/get-index-stats', parameters: {}, user: undefined, }, { @@ -128,7 +128,7 @@ describe('API', () => { // ごまがしもダメ await failedApiCall({ - endpoint: '/admin/get-index-stats', + endpoint: 'admin/get-index-stats', parameters: {}, user: { token: 'tsukawasete' }, }, { @@ -138,13 +138,13 @@ describe('API', () => { }); await successfulApiCall({ - endpoint: '/admin/get-index-stats', + endpoint: 'admin/get-index-stats', parameters: {}, user: { token: application2 }, }); await failedApiCall({ - endpoint: '/admin/get-index-stats', + endpoint: 'admin/get-index-stats', parameters: {}, user: { token: application }, }, { @@ -154,7 +154,7 @@ describe('API', () => { }); await failedApiCall({ - endpoint: '/admin/get-index-stats', + endpoint: 'admin/get-index-stats', parameters: {}, user: { token: application3 }, }, { @@ -164,7 +164,7 @@ describe('API', () => { }); await failedApiCall({ - endpoint: '/admin/get-index-stats', + endpoint: 'admin/get-index-stats', parameters: {}, user: { token: application4 }, }, { @@ -177,7 +177,7 @@ describe('API', () => { describe('Authentication header', () => { test('一般リクエスト', async () => { await successfulApiCall({ - endpoint: '/admin/get-index-stats', + endpoint: 'admin/get-index-stats', parameters: {}, user: { token: alice.token, @@ -211,7 +211,7 @@ describe('API', () => { describe('tokenエラー応答でWWW-Authenticate headerを送る', () => { describe('invalid_token', () => { test('一般リクエスト', async () => { - const result = await api('/admin/get-index-stats', {}, { + const result = await api('admin/get-index-stats', {}, { token: 'syuilo', bearer: true, }); @@ -246,7 +246,7 @@ describe('API', () => { describe('tokenがないとrealmだけおくる', () => { test('一般リクエスト', async () => { - const result = await api('/admin/get-index-stats', {}); + const result = await api('admin/get-index-stats', {}); assert.strictEqual(result.status, 401); assert.strictEqual(result.headers.get('WWW-Authenticate'), 'Bearer realm="Misskey"'); }); @@ -259,7 +259,8 @@ describe('API', () => { }); test('invalid_request', async () => { - const result = await api('/notes/create', { text: true }, { + // @ts-expect-error text must be string + const result = await api('notes/create', { text: true }, { token: alice.token, bearer: true, }); diff --git a/packages/backend/test/e2e/block.ts b/packages/backend/test/e2e/block.ts index cbd91e6e42..e4f798498f 100644 --- a/packages/backend/test/e2e/block.ts +++ b/packages/backend/test/e2e/block.ts @@ -22,7 +22,7 @@ describe('Block', () => { }, 1000 * 60 * 2); test('Block作成', async () => { - const res = await api('/blocking/create', { + const res = await api('blocking/create', { userId: bob.id, }, alice); @@ -30,7 +30,7 @@ describe('Block', () => { }); test('ブロックされているユーザーをフォローできない', async () => { - const res = await api('/following/create', { userId: alice.id }, bob); + const res = await api('following/create', { userId: alice.id }, bob); assert.strictEqual(res.status, 400); assert.strictEqual(res.body.error.id, 'c4ab57cc-4e41-45e9-bfd9-584f61e35ce0'); @@ -39,7 +39,7 @@ describe('Block', () => { test('ブロックされているユーザーにリアクションできない', async () => { const note = await post(alice, { text: 'hello' }); - const res = await api('/notes/reactions/create', { noteId: note.id, reaction: '👍' }, bob); + const res = await api('notes/reactions/create', { noteId: note.id, reaction: '👍' }, bob); assert.strictEqual(res.status, 400); assert.strictEqual(res.body.error.id, '20ef5475-9f38-4e4c-bd33-de6d979498ec'); @@ -48,7 +48,7 @@ describe('Block', () => { test('ブロックされているユーザーに返信できない', async () => { const note = await post(alice, { text: 'hello' }); - const res = await api('/notes/create', { replyId: note.id, text: 'yo' }, bob); + const res = await api('notes/create', { replyId: note.id, text: 'yo' }, bob); assert.strictEqual(res.status, 400); assert.strictEqual(res.body.error.id, 'b390d7e1-8a5e-46ed-b625-06271cafd3d3'); @@ -57,7 +57,7 @@ describe('Block', () => { test('ブロックされているユーザーのノートをRenoteできない', async () => { const note = await post(alice, { text: 'hello' }); - const res = await api('/notes/create', { renoteId: note.id, text: 'yo' }, bob); + const res = await api('notes/create', { renoteId: note.id, text: 'yo' }, bob); assert.strictEqual(res.status, 400); assert.strictEqual(res.body.error.id, 'b390d7e1-8a5e-46ed-b625-06271cafd3d3'); @@ -72,12 +72,13 @@ describe('Block', () => { const bobNote = await post(bob, { text: 'hi' }); const carolNote = await post(carol, { text: 'hi' }); - const res = await api('/notes/local-timeline', {}, bob); + const res = await api('notes/local-timeline', {}, bob); + const body = res.body as misskey.entities.Note[]; assert.strictEqual(res.status, 200); assert.strictEqual(Array.isArray(res.body), true); - assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), false); - assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); - assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), true); + assert.strictEqual(body.some(note => note.id === aliceNote.id), false); + assert.strictEqual(body.some(note => note.id === bobNote.id), true); + assert.strictEqual(body.some(note => note.id === carolNote.id), true); }); }); diff --git a/packages/backend/test/e2e/clips.ts b/packages/backend/test/e2e/clips.ts index 2cf397e22d..ba6f9d6a65 100644 --- a/packages/backend/test/e2e/clips.ts +++ b/packages/backend/test/e2e/clips.ts @@ -6,47 +6,34 @@ process.env.NODE_ENV = 'test'; import * as assert from 'assert'; -import { JTDDataType } from 'ajv/dist/jtd'; import { DEFAULT_POLICIES } from '@/core/RoleService.js'; -import type { Packed } from '@/misc/json-schema.js'; -import { paramDef as CreateParamDef } from '@/server/api/endpoints/clips/create.js'; -import { paramDef as UpdateParamDef } from '@/server/api/endpoints/clips/update.js'; -import { paramDef as DeleteParamDef } from '@/server/api/endpoints/clips/delete.js'; -import { paramDef as ShowParamDef } from '@/server/api/endpoints/clips/show.js'; -import { paramDef as FavoriteParamDef } from '@/server/api/endpoints/clips/favorite.js'; -import { paramDef as UnfavoriteParamDef } from '@/server/api/endpoints/clips/unfavorite.js'; -import { paramDef as AddNoteParamDef } from '@/server/api/endpoints/clips/add-note.js'; -import { paramDef as RemoveNoteParamDef } from '@/server/api/endpoints/clips/remove-note.js'; -import { paramDef as NotesParamDef } from '@/server/api/endpoints/clips/notes.js'; import { api, ApiRequest, failedApiCall, hiddenNote, post, signup, successfulApiCall } from '../utils.js'; +import type * as Misskey from 'misskey-js'; -describe('クリップ', () => { - type User = Packed<'User'>; - type Note = Packed<'Note'>; - type Clip = Packed<'Clip'>; +type Optional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>; - let alice: User; - let bob: User; - let aliceNote: Note; - let aliceHomeNote: Note; - let aliceFollowersNote: Note; - let aliceSpecifiedNote: Note; - let bobNote: Note; - let bobHomeNote: Note; - let bobFollowersNote: Note; - let bobSpecifiedNote: Note; +describe('クリップ', () => { + let alice: Misskey.entities.SignupResponse; + let bob: Misskey.entities.SignupResponse; + let aliceNote: Misskey.entities.Note; + let aliceHomeNote: Misskey.entities.Note; + let aliceFollowersNote: Misskey.entities.Note; + let aliceSpecifiedNote: Misskey.entities.Note; + let bobNote: Misskey.entities.Note; + let bobHomeNote: Misskey.entities.Note; + let bobFollowersNote: Misskey.entities.Note; + let bobSpecifiedNote: Misskey.entities.Note; const compareBy = <T extends { id: string }, >(selector: (s: T) => string = (s: T): string => s.id) => (a: T, b: T): number => { return selector(a).localeCompare(selector(b)); }; - type CreateParam = JTDDataType<typeof CreateParamDef>; - const defaultCreate = (): Partial<CreateParam> => ({ + const defaultCreate = (): Pick<Misskey.entities.ClipsCreateRequest, 'name'> => ({ name: 'test', }); - const create = async (parameters: Partial<CreateParam> = {}, request: Partial<ApiRequest> = {}): Promise<Clip> => { - const clip = await successfulApiCall<Clip>({ - endpoint: '/clips/create', + const create = async (parameters: Partial<Misskey.entities.ClipsCreateRequest> = {}, request: Partial<ApiRequest<'clips/create'>> = {}): Promise<Misskey.entities.Clip> => { + const clip = await successfulApiCall({ + endpoint: 'clips/create', parameters: { ...defaultCreate(), ...parameters, @@ -64,17 +51,16 @@ describe('クリップ', () => { return clip; }; - const createMany = async (parameters: Partial<CreateParam>, count = 10, user = alice): Promise<Clip[]> => { + const createMany = async (parameters: Partial<Misskey.entities.ClipsCreateRequest>, count = 10, user = alice): Promise<Misskey.entities.Clip[]> => { return await Promise.all([...Array(count)].map((_, i) => create({ name: `test${i}`, ...parameters, }, { user }))); }; - type UpdateParam = JTDDataType<typeof UpdateParamDef>; - const update = async (parameters: Partial<UpdateParam>, request: Partial<ApiRequest> = {}): Promise<Clip> => { - const clip = await successfulApiCall<Clip>({ - endpoint: '/clips/update', + const update = async (parameters: Optional<Misskey.entities.ClipsUpdateRequest, 'name'>, request: Partial<ApiRequest<'clips/update'>> = {}): Promise<Misskey.entities.Clip> => { + const clip = await successfulApiCall({ + endpoint: 'clips/update', parameters: { name: 'updated', ...parameters, @@ -92,41 +78,39 @@ describe('クリップ', () => { return clip; }; - type DeleteParam = JTDDataType<typeof DeleteParamDef>; - const deleteClip = async (parameters: DeleteParam, request: Partial<ApiRequest> = {}): Promise<void> => { - return await successfulApiCall<void>({ - endpoint: '/clips/delete', + const deleteClip = async (parameters: Misskey.entities.ClipsDeleteRequest, request: Partial<ApiRequest<'clips/delete'>> = {}): Promise<void> => { + return await successfulApiCall({ + endpoint: 'clips/delete', parameters, user: alice, ...request, }, { status: 204, - }); + }) as any as void; }; - type ShowParam = JTDDataType<typeof ShowParamDef>; - const show = async (parameters: ShowParam, request: Partial<ApiRequest> = {}): Promise<Clip> => { - return await successfulApiCall<Clip>({ - endpoint: '/clips/show', + const show = async (parameters: Misskey.entities.ClipsShowRequest, request: Partial<ApiRequest<'clips/show'>> = {}): Promise<Misskey.entities.Clip> => { + return await successfulApiCall({ + endpoint: 'clips/show', parameters, user: alice, ...request, }); }; - const list = async (request: Partial<ApiRequest>): Promise<Clip[]> => { - return successfulApiCall<Clip[]>({ - endpoint: '/clips/list', + const list = async (request: Partial<ApiRequest<'clips/list'>>): Promise<Misskey.entities.Clip[]> => { + return successfulApiCall({ + endpoint: 'clips/list', parameters: {}, user: alice, ...request, }); }; - const usersClips = async (request: Partial<ApiRequest>): Promise<Clip[]> => { - return await successfulApiCall<Clip[]>({ - endpoint: '/users/clips', - parameters: {}, + const usersClips = async (parameters: Misskey.entities.UsersClipsRequest, request: Partial<ApiRequest<'users/clips'>> = {}): Promise<Misskey.entities.Clip[]> => { + return await successfulApiCall({ + endpoint: 'users/clips', + parameters, user: alice, ...request, }); @@ -136,23 +120,22 @@ describe('クリップ', () => { alice = await signup({ username: 'alice' }); bob = await signup({ username: 'bob' }); - // FIXME: misskey-jsのNoteはoutdatedなので直接変換できない - aliceNote = await post(alice, { text: 'test' }) as any; - aliceHomeNote = await post(alice, { text: 'home only', visibility: 'home' }) as any; - aliceFollowersNote = await post(alice, { text: 'followers only', visibility: 'followers' }) as any; - aliceSpecifiedNote = await post(alice, { text: 'specified only', visibility: 'specified' }) as any; - bobNote = await post(bob, { text: 'test' }) as any; - bobHomeNote = await post(bob, { text: 'home only', visibility: 'home' }) as any; - bobFollowersNote = await post(bob, { text: 'followers only', visibility: 'followers' }) as any; - bobSpecifiedNote = await post(bob, { text: 'specified only', visibility: 'specified' }) as any; + aliceNote = await post(alice, { text: 'test' }); + aliceHomeNote = await post(alice, { text: 'home only', visibility: 'home' }); + aliceFollowersNote = await post(alice, { text: 'followers only', visibility: 'followers' }); + aliceSpecifiedNote = await post(alice, { text: 'specified only', visibility: 'specified' }); + bobNote = await post(bob, { text: 'test' }); + bobHomeNote = await post(bob, { text: 'home only', visibility: 'home' }); + bobFollowersNote = await post(bob, { text: 'followers only', visibility: 'followers' }); + bobSpecifiedNote = await post(bob, { text: 'specified only', visibility: 'specified' }); }, 1000 * 60 * 2); afterEach(async () => { // テスト間で影響し合わないように毎回全部消す。 for (const user of [alice, bob]) { - const list = await api('/clips/list', { limit: 11 }, user); + const list = await api('clips/list', { limit: 11 }, user); for (const clip of list.body) { - await api('/clips/delete', { clipId: clip.id }, user); + await api('clips/delete', { clipId: clip.id }, user); } } }); @@ -177,7 +160,7 @@ describe('クリップ', () => { } await failedApiCall({ - endpoint: '/clips/create', + endpoint: 'clips/create', parameters: defaultCreate(), user: alice, }, { @@ -204,7 +187,8 @@ describe('クリップ', () => { { label: 'descriptionが最大長+1', parameters: { description: 'a'.repeat(2049) } }, ]; test.each(createClipDenyPattern)('の作成は$labelならできない', async ({ parameters }) => failedApiCall({ - endpoint: '/clips/create', + endpoint: 'clips/create', + // @ts-expect-error invalid params parameters: { ...defaultCreate(), ...parameters, @@ -246,15 +230,15 @@ describe('クリップ', () => { code: 'NO_SUCH_CLIP', id: 'b4d92d70-b216-46fa-9a3f-a8c811699257', } }, - { label: '他人のクリップ', user: (): User => bob, assertion: { + { label: '他人のクリップ', user: () => bob, assertion: { code: 'NO_SUCH_CLIP', id: 'b4d92d70-b216-46fa-9a3f-a8c811699257', } }, ...createClipDenyPattern as any, ])('の更新は$labelならできない', async ({ parameters, user, assertion }) => failedApiCall({ - endpoint: '/clips/update', + endpoint: 'clips/update', parameters: { - clipId: (await create({}, { user: (user ?? ((): User => alice))() })).id, + clipId: (await create({}, { user: (user ?? (() => alice))() })).id, name: 'updated', ...parameters, }, @@ -279,14 +263,15 @@ describe('クリップ', () => { code: 'NO_SUCH_CLIP', id: '70ca08ba-6865-4630-b6fb-8494759aa754', } }, - { label: '他人のクリップ', user: (): User => bob, assertion: { + { label: '他人のクリップ', user: () => bob, assertion: { code: 'NO_SUCH_CLIP', id: '70ca08ba-6865-4630-b6fb-8494759aa754', } }, ])('の削除は$labelならできない', async ({ parameters, user, assertion }) => failedApiCall({ - endpoint: '/clips/delete', + endpoint: 'clips/delete', parameters: { - clipId: (await create({}, { user: (user ?? ((): User => alice))() })).id, + // @ts-expect-error clipId must not be null + clipId: (await create({}, { user: (user ?? (() => alice))() })).id, ...parameters, }, user: alice, @@ -306,7 +291,7 @@ describe('クリップ', () => { test('のID指定取得は他人のPrivateなクリップは取得できない', async () => { const clip = await create({ isPublic: false }, { user: bob } ); failedApiCall({ - endpoint: '/clips/show', + endpoint: 'clips/show', parameters: { clipId: clip.id }, user: alice, }, { @@ -323,7 +308,8 @@ describe('クリップ', () => { id: 'c3c5fe33-d62c-44d2-9ea5-d997703f5c20', } }, ])('のID指定取得は$labelならできない', async ({ parameters, assetion }) => failedApiCall({ - endpoint: '/clips/show', + endpoint: 'clips/show', + // @ts-expect-error clipId must not be undefined parameters: { ...parameters, }, @@ -356,27 +342,23 @@ describe('クリップ', () => { test('の一覧が取得できる(空)', async () => { const res = await usersClips({ - parameters: { - userId: alice.id, - }, + userId: alice.id, }); assert.deepStrictEqual(res, []); }); test.each([ { label: '' }, - { label: '他人アカウントから', user: (): User => bob }, + { label: '他人アカウントから', user: () => bob }, ])('の一覧が$label取得できる', async () => { const clips = await createMany({ isPublic: true }); const res = await usersClips({ - parameters: { - userId: alice.id, - }, + userId: alice.id, }); // 返ってくる配列には順序保障がないのでidでソートして厳密比較 assert.deepStrictEqual( - res.sort(compareBy<Clip>(s => s.id)), + res.sort(compareBy<Misskey.entities.Clip>(s => s.id)), clips.sort(compareBy(s => s.id))); // 認証状態で見たときだけisFavoritedが入っている @@ -386,17 +368,16 @@ describe('クリップ', () => { }); test.each([ - { label: '未認証', user: (): undefined => undefined }, + { label: '未認証', user: () => undefined }, { label: '存在しないユーザーのもの', parameters: { userId: 'xxxxxxx' } }, ])('の一覧は$labelでも取得できる', async ({ parameters, user }) => { const clips = await createMany({ isPublic: true }); const res = await usersClips({ - parameters: { - userId: alice.id, - limit: clips.length, - ...parameters, - }, - user: (user ?? ((): User => alice))(), + userId: alice.id, + limit: clips.length, + ...parameters, + }, { + user: (user ?? (() => alice))(), }); // 未認証で見たときはisFavoritedは入らない @@ -409,10 +390,8 @@ describe('クリップ', () => { await create({ isPublic: false }); const aliceClip = await create({ isPublic: true }); const res = await usersClips({ - parameters: { - userId: alice.id, - limit: 2, - }, + userId: alice.id, + limit: 2, }); assert.deepStrictEqual(res, [aliceClip]); }); @@ -421,17 +400,15 @@ describe('クリップ', () => { const clips = await createMany({ isPublic: true }, 7); clips.sort(compareBy(s => s.id)); const res = await usersClips({ - parameters: { - userId: alice.id, - sinceId: clips[1].id, - untilId: clips[5].id, - limit: 4, - }, + userId: alice.id, + sinceId: clips[1].id, + untilId: clips[5].id, + limit: 4, }); // Promise.allで返ってくる配列には順序保障がないのでidでソートして厳密比較 assert.deepStrictEqual( - res.sort(compareBy<Clip>(s => s.id)), + res.sort(compareBy<Misskey.entities.Clip>(s => s.id)), [clips[2], clips[3], clips[4]], // sinceIdとuntilId自体は結果に含まれない clips[1].id + ' ... ' + clips[3].id + ' with ' + clips.map(s => s.id) + ' vs. ' + res.map(s => s.id)); }); @@ -441,8 +418,9 @@ describe('クリップ', () => { { label: 'limitゼロ', parameters: { limit: 0 } }, { label: 'limit最大+1', parameters: { limit: 101 } }, ])('の一覧は$labelだと取得できない', async ({ parameters }) => failedApiCall({ - endpoint: '/users/clips', + endpoint: 'users/clips', parameters: { + // @ts-expect-error userId must not be undefined userId: alice.id, ...parameters, }, @@ -454,15 +432,15 @@ describe('クリップ', () => { })); test.each([ - { label: '作成', endpoint: '/clips/create' }, - { label: '更新', endpoint: '/clips/update' }, - { label: '削除', endpoint: '/clips/delete' }, - { label: '取得', endpoint: '/clips/list' }, - { label: 'お気に入り設定', endpoint: '/clips/favorite' }, - { label: 'お気に入り解除', endpoint: '/clips/unfavorite' }, - { label: 'お気に入り取得', endpoint: '/clips/my-favorites' }, - { label: 'ノート追加', endpoint: '/clips/add-note' }, - { label: 'ノート削除', endpoint: '/clips/remove-note' }, + { label: '作成', endpoint: 'clips/create' as const }, + { label: '更新', endpoint: 'clips/update' as const }, + { label: '削除', endpoint: 'clips/delete' as const }, + { label: '取得', endpoint: 'clips/list' as const }, + { label: 'お気に入り設定', endpoint: 'clips/favorite' as const }, + { label: 'お気に入り解除', endpoint: 'clips/unfavorite' as const }, + { label: 'お気に入り取得', endpoint: 'clips/my-favorites' as const }, + { label: 'ノート追加', endpoint: 'clips/add-note' as const }, + { label: 'ノート削除', endpoint: 'clips/remove-note' as const }, ])('の$labelは未認証ではできない', async ({ endpoint }) => await failedApiCall({ endpoint: endpoint, parameters: {}, @@ -474,35 +452,33 @@ describe('クリップ', () => { })); describe('のお気に入り', () => { - let aliceClip: Clip; + let aliceClip: Misskey.entities.Clip; - type FavoriteParam = JTDDataType<typeof FavoriteParamDef>; - const favorite = async (parameters: FavoriteParam, request: Partial<ApiRequest> = {}): Promise<void> => { - return successfulApiCall<void>({ - endpoint: '/clips/favorite', + const favorite = async (parameters: Misskey.entities.ClipsFavoriteRequest, request: Partial<ApiRequest<'clips/favorite'>> = {}): Promise<void> => { + return successfulApiCall({ + endpoint: 'clips/favorite', parameters, user: alice, ...request, }, { status: 204, - }); + }) as any as void; }; - type UnfavoriteParam = JTDDataType<typeof UnfavoriteParamDef>; - const unfavorite = async (parameters: UnfavoriteParam, request: Partial<ApiRequest> = {}): Promise<void> => { - return successfulApiCall<void>({ - endpoint: '/clips/unfavorite', + const unfavorite = async (parameters: Misskey.entities.ClipsUnfavoriteRequest, request: Partial<ApiRequest<'clips/unfavorite'>> = {}): Promise<void> => { + return successfulApiCall({ + endpoint: 'clips/unfavorite', parameters, user: alice, ...request, }, { status: 204, - }); + }) as any as void; }; - const myFavorites = async (request: Partial<ApiRequest> = {}): Promise<Clip[]> => { - return successfulApiCall<Clip[]>({ - endpoint: '/clips/my-favorites', + const myFavorites = async (request: Partial<ApiRequest<'clips/my-favorites'>> = {}): Promise<Misskey.entities.Clip[]> => { + return successfulApiCall({ + endpoint: 'clips/my-favorites', parameters: {}, user: alice, ...request, @@ -568,7 +544,7 @@ describe('クリップ', () => { test('は同じクリップに対して二回設定できない。', async () => { await favorite({ clipId: aliceClip.id }); await failedApiCall({ - endpoint: '/clips/favorite', + endpoint: 'clips/favorite', parameters: { clipId: aliceClip.id, }, @@ -586,14 +562,15 @@ describe('クリップ', () => { code: 'NO_SUCH_CLIP', id: '4c2aaeae-80d8-4250-9606-26cb1fdb77a5', } }, - { label: '他人のクリップ', user: (): User => bob, assertion: { + { label: '他人のクリップ', user: () => bob, assertion: { code: 'NO_SUCH_CLIP', id: '4c2aaeae-80d8-4250-9606-26cb1fdb77a5', } }, ])('の設定は$labelならできない', async ({ parameters, user, assertion }) => failedApiCall({ - endpoint: '/clips/favorite', + endpoint: 'clips/favorite', parameters: { - clipId: (await create({}, { user: (user ?? ((): User => alice))() })).id, + // @ts-expect-error clipId must not be null + clipId: (await create({}, { user: (user ?? (() => alice))() })).id, ...parameters, }, user: alice, @@ -619,7 +596,7 @@ describe('クリップ', () => { code: 'NO_SUCH_CLIP', id: '2603966e-b865-426c-94a7-af4a01241dc1', } }, - { label: '他人のクリップ', user: (): User => bob, assertion: { + { label: '他人のクリップ', user: () => bob, assertion: { code: 'NOT_FAVORITED', id: '90c3a9e8-b321-4dae-bf57-2bf79bbcc187', } }, @@ -628,9 +605,10 @@ describe('クリップ', () => { id: '90c3a9e8-b321-4dae-bf57-2bf79bbcc187', } }, ])('の設定解除は$labelならできない', async ({ parameters, user, assertion }) => failedApiCall({ - endpoint: '/clips/unfavorite', + endpoint: 'clips/unfavorite', parameters: { - clipId: (await create({}, { user: (user ?? ((): User => alice))() })).id, + // @ts-expect-error clipId must not be null + clipId: (await create({}, { user: (user ?? (() => alice))() })).id, ...parameters, }, user: alice, @@ -655,41 +633,38 @@ describe('クリップ', () => { }); describe('に紐づくノート', () => { - let aliceClip: Clip; + let aliceClip: Misskey.entities.Clip; - const sampleNotes = (): Note[] => [ + const sampleNotes = (): Misskey.entities.Note[] => [ aliceNote, aliceHomeNote, aliceFollowersNote, aliceSpecifiedNote, bobNote, bobHomeNote, bobFollowersNote, bobSpecifiedNote, ]; - type AddNoteParam = JTDDataType<typeof AddNoteParamDef>; - const addNote = async (parameters: AddNoteParam, request: Partial<ApiRequest> = {}): Promise<void> => { - return successfulApiCall<void>({ - endpoint: '/clips/add-note', + const addNote = async (parameters: Misskey.entities.ClipsAddNoteRequest, request: Partial<ApiRequest<'clips/add-note'>> = {}): Promise<void> => { + return successfulApiCall({ + endpoint: 'clips/add-note', parameters, user: alice, ...request, }, { status: 204, - }); + }) as any as void; }; - type RemoveNoteParam = JTDDataType<typeof RemoveNoteParamDef>; - const removeNote = async (parameters: RemoveNoteParam, request: Partial<ApiRequest> = {}): Promise<void> => { - return successfulApiCall<void>({ - endpoint: '/clips/remove-note', + const removeNote = async (parameters: Misskey.entities.ClipsRemoveNoteRequest, request: Partial<ApiRequest<'clips/remove-note'>> = {}): Promise<void> => { + return successfulApiCall({ + endpoint: 'clips/remove-note', parameters, user: alice, ...request, }, { status: 204, - }); + }) as any as void; }; - type NotesParam = JTDDataType<typeof NotesParamDef>; - const notes = async (parameters: Partial<NotesParam>, request: Partial<ApiRequest> = {}): Promise<Note[]> => { - return successfulApiCall<Note[]>({ - endpoint: '/clips/notes', + const notes = async (parameters: Misskey.entities.ClipsNotesRequest, request: Partial<ApiRequest<'clips/notes'>> = {}): Promise<Misskey.entities.Note[]> => { + return successfulApiCall({ + endpoint: 'clips/notes', parameters, user: alice, ...request, @@ -715,7 +690,7 @@ describe('クリップ', () => { test('として同じノートを二回紐づけることはできない', async () => { await addNote({ clipId: aliceClip.id, noteId: aliceNote.id }); await failedApiCall({ - endpoint: '/clips/add-note', + endpoint: 'clips/add-note', parameters: { clipId: aliceClip.id, noteId: aliceNote.id, @@ -733,11 +708,11 @@ describe('クリップ', () => { const noteLimit = DEFAULT_POLICIES.noteEachClipsLimit + 1; const noteList = await Promise.all([...Array(noteLimit)].map((_, i) => post(alice, { text: `test ${i}`, - }) as unknown)) as Note[]; + }) as unknown)) as Misskey.entities.Note[]; await Promise.all(noteList.map(s => addNote({ clipId: aliceClip.id, noteId: s.id }))); await failedApiCall({ - endpoint: '/clips/add-note', + endpoint: 'clips/add-note', parameters: { clipId: aliceClip.id, noteId: aliceNote.id, @@ -751,7 +726,7 @@ describe('クリップ', () => { }); test('は他人のクリップへ追加できない。', async () => await failedApiCall({ - endpoint: '/clips/add-note', + endpoint: 'clips/add-note', parameters: { clipId: aliceClip.id, noteId: aliceNote.id, @@ -774,18 +749,20 @@ describe('クリップ', () => { code: 'NO_SUCH_NOTE', id: 'fc8c0b49-c7a3-4664-a0a6-b418d386bb8b', } }, - { label: '他人のクリップ', user: (): object => bob, assetion: { + { label: '他人のクリップ', user: () => bob, assetion: { code: 'NO_SUCH_CLIP', id: 'd6e76cc0-a1b5-4c7c-a287-73fa9c716dcf', } }, ])('の追加は$labelだとできない', async ({ parameters, user, assetion }) => failedApiCall({ - endpoint: '/clips/add-note', + endpoint: 'clips/add-note', parameters: { + // @ts-expect-error clipId must not be undefined clipId: aliceClip.id, + // @ts-expect-error noteId must not be undefined noteId: aliceNote.id, ...parameters, }, - user: (user ?? ((): User => alice))(), + user: (user ?? (() => alice))(), }, { status: 400, code: 'INVALID_PARAM', @@ -810,18 +787,20 @@ describe('クリップ', () => { code: 'NO_SUCH_NOTE', id: 'aff017de-190e-434b-893e-33a9ff5049d8', // add-noteと異なる } }, - { label: '他人のクリップ', user: (): object => bob, assetion: { + { label: '他人のクリップ', user: () => bob, assetion: { code: 'NO_SUCH_CLIP', id: 'b80525c6-97f7-49d7-a42d-ebccd49cfd52', // add-noteと異なる } }, ])('の削除は$labelだとできない', async ({ parameters, user, assetion }) => failedApiCall({ - endpoint: '/clips/remove-note', + endpoint: 'clips/remove-note', parameters: { + // @ts-expect-error clipId must not be undefined clipId: aliceClip.id, + // @ts-expect-error noteId must not be undefined noteId: aliceNote.id, ...parameters, }, - user: (user ?? ((): User => alice))(), + user: (user ?? (() => alice))(), }, { status: 400, code: 'INVALID_PARAM', @@ -925,21 +904,22 @@ describe('クリップ', () => { code: 'NO_SUCH_CLIP', id: '1d7645e6-2b6d-4635-b0fe-fe22b0e72e00', } }, - { label: '他人のPrivateなクリップから', user: (): object => bob, assertion: { + { label: '他人のPrivateなクリップから', user: () => bob, assertion: { code: 'NO_SUCH_CLIP', id: '1d7645e6-2b6d-4635-b0fe-fe22b0e72e00', } }, - { label: '未認証でPrivateなクリップから', user: (): undefined => undefined, assertion: { + { label: '未認証でPrivateなクリップから', user: () => undefined, assertion: { code: 'NO_SUCH_CLIP', id: '1d7645e6-2b6d-4635-b0fe-fe22b0e72e00', } }, ])('は$labelだと取得できない', async ({ parameters, user, assertion }) => failedApiCall({ - endpoint: '/clips/notes', + endpoint: 'clips/notes', parameters: { + // @ts-expect-error clipId must not be undefined clipId: aliceClip.id, ...parameters, }, - user: (user ?? ((): User => alice))(), + user: (user ?? (() => alice))(), }, { status: 400, code: 'INVALID_PARAM', diff --git a/packages/backend/test/e2e/drive.ts b/packages/backend/test/e2e/drive.ts index 22ec66e2af..828c5200ef 100644 --- a/packages/backend/test/e2e/drive.ts +++ b/packages/backend/test/e2e/drive.ts @@ -6,22 +6,14 @@ process.env.NODE_ENV = 'test'; import * as assert from 'assert'; -import { MiNote } from '@/models/Note.js'; -import { api, initTestDb, makeStreamCatcher, post, signup, uploadFile } from '../utils.js'; +import { api, makeStreamCatcher, post, signup, uploadFile } from '../utils.js'; import type * as misskey from 'misskey-js'; -import type{ Repository } from 'typeorm' -import type { Packed } from '@/misc/json-schema.js'; - describe('Drive', () => { - let Notes: Repository<MiNote>; - let alice: misskey.entities.SignupResponse; let bob: misskey.entities.SignupResponse; beforeAll(async () => { - const connection = await initTestDb(true); - Notes = connection.getRepository(MiNote); alice = await signup({ username: 'alice' }); bob = await signup({ username: 'bob' }); }, 1000 * 60 * 2); @@ -31,13 +23,13 @@ describe('Drive', () => { const marker = Math.random().toString(); - const url = 'https://raw.githubusercontent.com/misskey-dev/misskey/develop/packages/backend/test/resources/Lenna.jpg' + const url = 'https://raw.githubusercontent.com/misskey-dev/misskey/develop/packages/backend/test/resources/Lenna.jpg'; const catcher = makeStreamCatcher( alice, 'main', (msg) => msg.type === 'urlUploadFinished' && msg.body.marker === marker, - (msg) => msg.body.file as Packed<'DriveFile'>, + (msg) => msg.body.file, 10 * 1000); const res = await api('drive/files/upload-from-url', { @@ -51,7 +43,7 @@ describe('Drive', () => { assert.strictEqual(res.status, 204); assert.strictEqual(file.name, 'Lenna.jpg'); assert.strictEqual(file.type, 'image/jpeg'); - }) + }); test('ローカルからアップロードできる', async () => { // APIレスポンスを直接使用するので utils.js uploadFile が通過することで成功とする @@ -59,27 +51,27 @@ describe('Drive', () => { const res = await uploadFile(alice, { path: 'Lenna.jpg', name: 'テスト画像' }); assert.strictEqual(res.body?.name, 'テスト画像.jpg'); - assert.strictEqual(res.body?.type, 'image/jpeg'); - }) + assert.strictEqual(res.body.type, 'image/jpeg'); + }); test('添付ノート一覧を取得できる', async () => { - const ids = (await Promise.all([uploadFile(alice), uploadFile(alice), uploadFile(alice)])).map(elm => elm.body!.id) + const ids = (await Promise.all([uploadFile(alice), uploadFile(alice), uploadFile(alice)])).map(elm => elm.body!.id); const note0 = await post(alice, { fileIds: [ids[0]] }); const note1 = await post(alice, { fileIds: [ids[0], ids[1]] }); const attached0 = await api('drive/files/attached-notes', { fileId: ids[0] }, alice); assert.strictEqual(attached0.body.length, 2); - assert.strictEqual(attached0.body[0].id, note1.id) - assert.strictEqual(attached0.body[1].id, note0.id) + assert.strictEqual(attached0.body[0].id, note1.id); + assert.strictEqual(attached0.body[1].id, note0.id); const attached1 = await api('drive/files/attached-notes', { fileId: ids[1] }, alice); assert.strictEqual(attached1.body.length, 1); - assert.strictEqual(attached1.body[0].id, note1.id) + assert.strictEqual(attached1.body[0].id, note1.id); const attached2 = await api('drive/files/attached-notes', { fileId: ids[2] }, alice); - assert.strictEqual(attached2.body.length, 0) - }) + assert.strictEqual(attached2.body.length, 0); + }); test('添付ノート一覧は他の人から見えない', async () => { const file = await uploadFile(alice); @@ -89,7 +81,5 @@ describe('Drive', () => { const res = await api('drive/files/attached-notes', { fileId: file.body!.id }, bob); assert.strictEqual(res.status, 400); assert.strictEqual('error' in res.body, true); - - }) + }); }); - diff --git a/packages/backend/test/e2e/endpoints.ts b/packages/backend/test/e2e/endpoints.ts index d469597805..bc89dc37f4 100644 --- a/packages/backend/test/e2e/endpoints.ts +++ b/packages/backend/test/e2e/endpoints.ts @@ -79,6 +79,7 @@ describe('Endpoints', () => { test('クエリをインジェクションできない', async () => { const res = await api('signin', { username: 'test1', + // @ts-expect-error password must be string password: { $gt: '', }, @@ -103,7 +104,7 @@ describe('Endpoints', () => { const myLocation = '七森中'; const myBirthday = '2000-09-07'; - const res = await api('/i/update', { + const res = await api('i/update', { name: myName, location: myLocation, birthday: myBirthday, @@ -117,7 +118,7 @@ describe('Endpoints', () => { }); test('名前を空白にできる', async () => { - const res = await api('/i/update', { + const res = await api('i/update', { name: ' ', }, alice); assert.strictEqual(res.status, 200); @@ -125,11 +126,11 @@ describe('Endpoints', () => { }); test('誕生日の設定を削除できる', async () => { - await api('/i/update', { + await api('i/update', { birthday: '2000-09-07', }, alice); - const res = await api('/i/update', { + const res = await api('i/update', { birthday: null, }, alice); @@ -139,7 +140,7 @@ describe('Endpoints', () => { }); test('不正な誕生日の形式で怒られる', async () => { - const res = await api('/i/update', { + const res = await api('i/update', { birthday: '2000/09/07', }, alice); assert.strictEqual(res.status, 400); @@ -148,7 +149,7 @@ describe('Endpoints', () => { describe('users/show', () => { test('ユーザーが取得できる', async () => { - const res = await api('/users/show', { + const res = await api('users/show', { userId: alice.id, }, alice); @@ -158,14 +159,14 @@ describe('Endpoints', () => { }); test('ユーザーが存在しなかったら怒る', async () => { - const res = await api('/users/show', { + const res = await api('users/show', { userId: '000000000000000000000000', }); assert.strictEqual(res.status, 404); }); test('間違ったIDで怒られる', async () => { - const res = await api('/users/show', { + const res = await api('users/show', { userId: 'kyoppie', }); assert.strictEqual(res.status, 404); @@ -178,7 +179,7 @@ describe('Endpoints', () => { text: 'test', }); - const res = await api('/notes/show', { + const res = await api('notes/show', { noteId: myPost.id, }, alice); @@ -189,14 +190,14 @@ describe('Endpoints', () => { }); test('投稿が存在しなかったら怒る', async () => { - const res = await api('/notes/show', { + const res = await api('notes/show', { noteId: '000000000000000000000000', }); assert.strictEqual(res.status, 400); }); test('間違ったIDで怒られる', async () => { - const res = await api('/notes/show', { + const res = await api('notes/show', { noteId: 'kyoppie', }); assert.strictEqual(res.status, 400); @@ -207,14 +208,14 @@ describe('Endpoints', () => { test('リアクションできる', async () => { const bobPost = await post(bob, { text: 'hi' }); - const res = await api('/notes/reactions/create', { + const res = await api('notes/reactions/create', { noteId: bobPost.id, reaction: '🚀', }, alice); assert.strictEqual(res.status, 204); - const resNote = await api('/notes/show', { + const resNote = await api('notes/show', { noteId: bobPost.id, }, alice); @@ -225,7 +226,7 @@ describe('Endpoints', () => { test('自分の投稿にもリアクションできる', async () => { const myPost = await post(alice, { text: 'hi' }); - const res = await api('/notes/reactions/create', { + const res = await api('notes/reactions/create', { noteId: myPost.id, reaction: '🚀', }, alice); @@ -236,19 +237,19 @@ describe('Endpoints', () => { test('二重にリアクションすると上書きされる', async () => { const bobPost = await post(bob, { text: 'hi' }); - await api('/notes/reactions/create', { + await api('notes/reactions/create', { noteId: bobPost.id, reaction: '🥰', }, alice); - const res = await api('/notes/reactions/create', { + const res = await api('notes/reactions/create', { noteId: bobPost.id, reaction: '🚀', }, alice); assert.strictEqual(res.status, 204); - const resNote = await api('/notes/show', { + const resNote = await api('notes/show', { noteId: bobPost.id, }, alice); @@ -257,7 +258,7 @@ describe('Endpoints', () => { }); test('存在しない投稿にはリアクションできない', async () => { - const res = await api('/notes/reactions/create', { + const res = await api('notes/reactions/create', { noteId: '000000000000000000000000', reaction: '🚀', }, alice); @@ -266,13 +267,14 @@ describe('Endpoints', () => { }); test('空のパラメータで怒られる', async () => { - const res = await api('/notes/reactions/create', {}, alice); + // @ts-expect-error param must not be empty + const res = await api('notes/reactions/create', {}, alice); assert.strictEqual(res.status, 400); }); test('間違ったIDで怒られる', async () => { - const res = await api('/notes/reactions/create', { + const res = await api('notes/reactions/create', { noteId: 'kyoppie', reaction: '🚀', }, alice); @@ -283,7 +285,7 @@ describe('Endpoints', () => { describe('following/create', () => { test('フォローできる', async () => { - const res = await api('/following/create', { + const res = await api('following/create', { userId: alice.id, }, bob); @@ -301,7 +303,7 @@ describe('Endpoints', () => { }); test('既にフォローしている場合は怒る', async () => { - const res = await api('/following/create', { + const res = await api('following/create', { userId: alice.id, }, bob); @@ -309,7 +311,7 @@ describe('Endpoints', () => { }); test('存在しないユーザーはフォローできない', async () => { - const res = await api('/following/create', { + const res = await api('following/create', { userId: '000000000000000000000000', }, alice); @@ -317,7 +319,7 @@ describe('Endpoints', () => { }); test('自分自身はフォローできない', async () => { - const res = await api('/following/create', { + const res = await api('following/create', { userId: alice.id, }, alice); @@ -325,13 +327,14 @@ describe('Endpoints', () => { }); test('空のパラメータで怒られる', async () => { - const res = await api('/following/create', {}, alice); + // @ts-expect-error params must not be empty + const res = await api('following/create', {}, alice); assert.strictEqual(res.status, 400); }); test('間違ったIDで怒られる', async () => { - const res = await api('/following/create', { + const res = await api('following/create', { userId: 'foo', }, alice); @@ -341,11 +344,11 @@ describe('Endpoints', () => { describe('following/delete', () => { test('フォロー解除できる', async () => { - await api('/following/create', { + await api('following/create', { userId: alice.id, }, bob); - const res = await api('/following/delete', { + const res = await api('following/delete', { userId: alice.id, }, bob); @@ -363,7 +366,7 @@ describe('Endpoints', () => { }); test('フォローしていない場合は怒る', async () => { - const res = await api('/following/delete', { + const res = await api('following/delete', { userId: alice.id, }, bob); @@ -371,7 +374,7 @@ describe('Endpoints', () => { }); test('存在しないユーザーはフォロー解除できない', async () => { - const res = await api('/following/delete', { + const res = await api('following/delete', { userId: '000000000000000000000000', }, alice); @@ -379,7 +382,7 @@ describe('Endpoints', () => { }); test('自分自身はフォロー解除できない', async () => { - const res = await api('/following/delete', { + const res = await api('following/delete', { userId: alice.id, }, alice); @@ -387,13 +390,14 @@ describe('Endpoints', () => { }); test('空のパラメータで怒られる', async () => { - const res = await api('/following/delete', {}, alice); + // @ts-expect-error params must not be empty + const res = await api('following/delete', {}, alice); assert.strictEqual(res.status, 400); }); test('間違ったIDで怒られる', async () => { - const res = await api('/following/delete', { + const res = await api('following/delete', { userId: 'kyoppie', }, alice); @@ -403,20 +407,20 @@ describe('Endpoints', () => { describe('channels/search', () => { test('空白検索で一覧を取得できる', async () => { - await api('/channels/create', { + await api('channels/create', { name: 'aaa', description: 'bbb', }, bob); - await api('/channels/create', { + await api('channels/create', { name: 'ccc1', description: 'ddd1', }, bob); - await api('/channels/create', { + await api('channels/create', { name: 'ccc2', description: 'ddd2', }, bob); - const res = await api('/channels/search', { + const res = await api('channels/search', { query: '', }, bob); @@ -425,7 +429,7 @@ describe('Endpoints', () => { assert.strictEqual(res.body.length, 3); }); test('名前のみの検索で名前を検索できる', async () => { - const res = await api('/channels/search', { + const res = await api('channels/search', { query: 'aaa', type: 'nameOnly', }, bob); @@ -436,7 +440,7 @@ describe('Endpoints', () => { assert.strictEqual(res.body[0].name, 'aaa'); }); test('名前のみの検索で名前を複数検索できる', async () => { - const res = await api('/channels/search', { + const res = await api('channels/search', { query: 'ccc', type: 'nameOnly', }, bob); @@ -446,7 +450,7 @@ describe('Endpoints', () => { assert.strictEqual(res.body.length, 2); }); test('名前のみの検索で説明は検索できない', async () => { - const res = await api('/channels/search', { + const res = await api('channels/search', { query: 'bbb', type: 'nameOnly', }, bob); @@ -456,7 +460,7 @@ describe('Endpoints', () => { assert.strictEqual(res.body.length, 0); }); test('名前と説明の検索で名前を検索できる', async () => { - const res = await api('/channels/search', { + const res = await api('channels/search', { query: 'ccc1', }, bob); @@ -466,7 +470,7 @@ describe('Endpoints', () => { assert.strictEqual(res.body[0].name, 'ccc1'); }); test('名前と説明での検索で説明を検索できる', async () => { - const res = await api('/channels/search', { + const res = await api('channels/search', { query: 'ddd1', }, bob); @@ -476,7 +480,7 @@ describe('Endpoints', () => { assert.strictEqual(res.body[0].name, 'ccc1'); }); test('名前と説明の検索で名前を複数検索できる', async () => { - const res = await api('/channels/search', { + const res = await api('channels/search', { query: 'ccc', }, bob); @@ -485,7 +489,7 @@ describe('Endpoints', () => { assert.strictEqual(res.body.length, 2); }); test('名前と説明での検索で説明を複数検索できる', async () => { - const res = await api('/channels/search', { + const res = await api('channels/search', { query: 'ddd', }, bob); @@ -506,7 +510,7 @@ describe('Endpoints', () => { await uploadFile(alice, { blob: new Blob([new Uint8Array(1024)]), }); - const res = await api('/drive', {}, alice); + const res = await api('drive', {}, alice); assert.strictEqual(res.status, 200); assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true); expect(res.body).toHaveProperty('usage', 1792); @@ -519,7 +523,7 @@ describe('Endpoints', () => { assert.strictEqual(res.status, 200); assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true); - assert.strictEqual(res.body.name, 'Lenna.jpg'); + assert.strictEqual(res.body!.name, 'Lenna.jpg'); }); test('ファイルに名前を付けられる', async () => { @@ -527,7 +531,7 @@ describe('Endpoints', () => { assert.strictEqual(res.status, 200); assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true); - assert.strictEqual(res.body.name, 'Belmond.jpg'); + assert.strictEqual(res.body!.name, 'Belmond.jpg'); }); test('ファイルに名前を付けられるが、拡張子は正しいものになる', async () => { @@ -535,11 +539,12 @@ describe('Endpoints', () => { assert.strictEqual(res.status, 200); assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true); - assert.strictEqual(res.body.name, 'Belmond.png.jpg'); + assert.strictEqual(res.body!.name, 'Belmond.png.jpg'); }); test('ファイル無しで怒られる', async () => { - const res = await api('/drive/files/create', {}, alice); + // @ts-expect-error params must not be empty + const res = await api('drive/files/create', {}, alice); assert.strictEqual(res.status, 400); }); @@ -549,14 +554,14 @@ describe('Endpoints', () => { assert.strictEqual(res.status, 200); assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true); - assert.strictEqual(res.body.name, 'image.svg'); - assert.strictEqual(res.body.type, 'image/svg+xml'); + assert.strictEqual(res.body!.name, 'image.svg'); + assert.strictEqual(res.body!.type, 'image/svg+xml'); }); for (const type of ['webp', 'avif']) { const mediaType = `image/${type}`; - const getWebpublicType = async (user: any, fileId: string): Promise<string> => { + const getWebpublicType = async (user: misskey.entities.SignupResponse, fileId: string): Promise<string> => { // drive/files/create does not expose webpublicType directly, so get it by posting it const res = await post(user, { text: mediaType, @@ -573,10 +578,10 @@ describe('Endpoints', () => { const res = await uploadFile(alice, { path }); assert.strictEqual(res.status, 200); - assert.strictEqual(res.body.name, path); - assert.strictEqual(res.body.type, mediaType); + assert.strictEqual(res.body!.name, path); + assert.strictEqual(res.body!.type, mediaType); - const webpublicType = await getWebpublicType(alice, res.body.id); + const webpublicType = await getWebpublicType(alice, res.body!.id); assert.strictEqual(webpublicType, 'image/webp'); }); @@ -584,10 +589,10 @@ describe('Endpoints', () => { const path = `without-alpha.${type}`; const res = await uploadFile(alice, { path }); assert.strictEqual(res.status, 200); - assert.strictEqual(res.body.name, path); - assert.strictEqual(res.body.type, mediaType); + assert.strictEqual(res.body!.name, path); + assert.strictEqual(res.body!.type, mediaType); - const webpublicType = await getWebpublicType(alice, res.body.id); + const webpublicType = await getWebpublicType(alice, res.body!.id); assert.strictEqual(webpublicType, 'image/webp'); }); } @@ -598,8 +603,8 @@ describe('Endpoints', () => { const file = (await uploadFile(alice)).body; const newName = 'いちごパスタ.png'; - const res = await api('/drive/files/update', { - fileId: file.id, + const res = await api('drive/files/update', { + fileId: file!.id, name: newName, }, alice); @@ -611,8 +616,8 @@ describe('Endpoints', () => { test('他人のファイルは更新できない', async () => { const file = (await uploadFile(alice)).body; - const res = await api('/drive/files/update', { - fileId: file.id, + const res = await api('drive/files/update', { + fileId: file!.id, name: 'いちごパスタ.png', }, bob); @@ -621,12 +626,12 @@ describe('Endpoints', () => { test('親フォルダを更新できる', async () => { const file = (await uploadFile(alice)).body; - const folder = (await api('/drive/folders/create', { + const folder = (await api('drive/folders/create', { name: 'test', }, alice)).body; - const res = await api('/drive/files/update', { - fileId: file.id, + const res = await api('drive/files/update', { + fileId: file!.id, folderId: folder.id, }, alice); @@ -638,17 +643,17 @@ describe('Endpoints', () => { test('親フォルダを無しにできる', async () => { const file = (await uploadFile(alice)).body; - const folder = (await api('/drive/folders/create', { + const folder = (await api('drive/folders/create', { name: 'test', }, alice)).body; - await api('/drive/files/update', { - fileId: file.id, + await api('drive/files/update', { + fileId: file!.id, folderId: folder.id, }, alice); - const res = await api('/drive/files/update', { - fileId: file.id, + const res = await api('drive/files/update', { + fileId: file!.id, folderId: null, }, alice); @@ -659,12 +664,12 @@ describe('Endpoints', () => { test('他人のフォルダには入れられない', async () => { const file = (await uploadFile(alice)).body; - const folder = (await api('/drive/folders/create', { + const folder = (await api('drive/folders/create', { name: 'test', }, bob)).body; - const res = await api('/drive/files/update', { - fileId: file.id, + const res = await api('drive/files/update', { + fileId: file!.id, folderId: folder.id, }, alice); @@ -674,8 +679,8 @@ describe('Endpoints', () => { test('存在しないフォルダで怒られる', async () => { const file = (await uploadFile(alice)).body; - const res = await api('/drive/files/update', { - fileId: file.id, + const res = await api('drive/files/update', { + fileId: file!.id, folderId: '000000000000000000000000', }, alice); @@ -685,8 +690,8 @@ describe('Endpoints', () => { test('不正なフォルダIDで怒られる', async () => { const file = (await uploadFile(alice)).body; - const res = await api('/drive/files/update', { - fileId: file.id, + const res = await api('drive/files/update', { + fileId: file!.id, folderId: 'foo', }, alice); @@ -694,7 +699,7 @@ describe('Endpoints', () => { }); test('ファイルが存在しなかったら怒る', async () => { - const res = await api('/drive/files/update', { + const res = await api('drive/files/update', { fileId: '000000000000000000000000', name: 'いちごパスタ.png', }, alice); @@ -706,8 +711,8 @@ describe('Endpoints', () => { const file = (await uploadFile(alice)).body; const newName = ''; - const res = await api('/drive/files/update', { - fileId: file.id, + const res = await api('drive/files/update', { + fileId: file!.id, name: newName, }, alice); @@ -715,7 +720,7 @@ describe('Endpoints', () => { }); test('間違ったIDで怒られる', async () => { - const res = await api('/drive/files/update', { + const res = await api('drive/files/update', { fileId: 'kyoppie', name: 'いちごパスタ.png', }, alice); @@ -726,7 +731,7 @@ describe('Endpoints', () => { describe('drive/folders/create', () => { test('フォルダを作成できる', async () => { - const res = await api('/drive/folders/create', { + const res = await api('drive/folders/create', { name: 'test', }, alice); @@ -738,11 +743,11 @@ describe('Endpoints', () => { describe('drive/folders/update', () => { test('名前を更新できる', async () => { - const folder = (await api('/drive/folders/create', { + const folder = (await api('drive/folders/create', { name: 'test', }, alice)).body; - const res = await api('/drive/folders/update', { + const res = await api('drive/folders/update', { folderId: folder.id, name: 'new name', }, alice); @@ -753,11 +758,11 @@ describe('Endpoints', () => { }); test('他人のフォルダを更新できない', async () => { - const folder = (await api('/drive/folders/create', { + const folder = (await api('drive/folders/create', { name: 'test', }, bob)).body; - const res = await api('/drive/folders/update', { + const res = await api('drive/folders/update', { folderId: folder.id, name: 'new name', }, alice); @@ -766,14 +771,14 @@ describe('Endpoints', () => { }); test('親フォルダを更新できる', async () => { - const folder = (await api('/drive/folders/create', { + const folder = (await api('drive/folders/create', { name: 'test', }, alice)).body; - const parentFolder = (await api('/drive/folders/create', { + const parentFolder = (await api('drive/folders/create', { name: 'parent', }, alice)).body; - const res = await api('/drive/folders/update', { + const res = await api('drive/folders/update', { folderId: folder.id, parentId: parentFolder.id, }, alice); @@ -784,18 +789,18 @@ describe('Endpoints', () => { }); test('親フォルダを無しに更新できる', async () => { - const folder = (await api('/drive/folders/create', { + const folder = (await api('drive/folders/create', { name: 'test', }, alice)).body; - const parentFolder = (await api('/drive/folders/create', { + const parentFolder = (await api('drive/folders/create', { name: 'parent', }, alice)).body; - await api('/drive/folders/update', { + await api('drive/folders/update', { folderId: folder.id, parentId: parentFolder.id, }, alice); - const res = await api('/drive/folders/update', { + const res = await api('drive/folders/update', { folderId: folder.id, parentId: null, }, alice); @@ -806,14 +811,14 @@ describe('Endpoints', () => { }); test('他人のフォルダを親フォルダに設定できない', async () => { - const folder = (await api('/drive/folders/create', { + const folder = (await api('drive/folders/create', { name: 'test', }, alice)).body; - const parentFolder = (await api('/drive/folders/create', { + const parentFolder = (await api('drive/folders/create', { name: 'parent', }, bob)).body; - const res = await api('/drive/folders/update', { + const res = await api('drive/folders/update', { folderId: folder.id, parentId: parentFolder.id, }, alice); @@ -822,18 +827,18 @@ describe('Endpoints', () => { }); test('フォルダが循環するような構造にできない', async () => { - const folder = (await api('/drive/folders/create', { + const folder = (await api('drive/folders/create', { name: 'test', }, alice)).body; - const parentFolder = (await api('/drive/folders/create', { + const parentFolder = (await api('drive/folders/create', { name: 'parent', }, alice)).body; - await api('/drive/folders/update', { + await api('drive/folders/update', { folderId: parentFolder.id, parentId: folder.id, }, alice); - const res = await api('/drive/folders/update', { + const res = await api('drive/folders/update', { folderId: folder.id, parentId: parentFolder.id, }, alice); @@ -842,25 +847,25 @@ describe('Endpoints', () => { }); test('フォルダが循環するような構造にできない(再帰的)', async () => { - const folderA = (await api('/drive/folders/create', { + const folderA = (await api('drive/folders/create', { name: 'test', }, alice)).body; - const folderB = (await api('/drive/folders/create', { + const folderB = (await api('drive/folders/create', { name: 'test', }, alice)).body; - const folderC = (await api('/drive/folders/create', { + const folderC = (await api('drive/folders/create', { name: 'test', }, alice)).body; - await api('/drive/folders/update', { + await api('drive/folders/update', { folderId: folderB.id, parentId: folderA.id, }, alice); - await api('/drive/folders/update', { + await api('drive/folders/update', { folderId: folderC.id, parentId: folderB.id, }, alice); - const res = await api('/drive/folders/update', { + const res = await api('drive/folders/update', { folderId: folderA.id, parentId: folderC.id, }, alice); @@ -869,11 +874,11 @@ describe('Endpoints', () => { }); test('フォルダが循環するような構造にできない(自身)', async () => { - const folderA = (await api('/drive/folders/create', { + const folderA = (await api('drive/folders/create', { name: 'test', }, alice)).body; - const res = await api('/drive/folders/update', { + const res = await api('drive/folders/update', { folderId: folderA.id, parentId: folderA.id, }, alice); @@ -882,11 +887,11 @@ describe('Endpoints', () => { }); test('存在しない親フォルダを設定できない', async () => { - const folder = (await api('/drive/folders/create', { + const folder = (await api('drive/folders/create', { name: 'test', }, alice)).body; - const res = await api('/drive/folders/update', { + const res = await api('drive/folders/update', { folderId: folder.id, parentId: '000000000000000000000000', }, alice); @@ -895,11 +900,11 @@ describe('Endpoints', () => { }); test('不正な親フォルダIDで怒られる', async () => { - const folder = (await api('/drive/folders/create', { + const folder = (await api('drive/folders/create', { name: 'test', }, alice)).body; - const res = await api('/drive/folders/update', { + const res = await api('drive/folders/update', { folderId: folder.id, parentId: 'foo', }, alice); @@ -908,7 +913,7 @@ describe('Endpoints', () => { }); test('存在しないフォルダを更新できない', async () => { - const res = await api('/drive/folders/update', { + const res = await api('drive/folders/update', { folderId: '000000000000000000000000', }, alice); @@ -916,7 +921,7 @@ describe('Endpoints', () => { }); test('不正なフォルダIDで怒られる', async () => { - const res = await api('/drive/folders/update', { + const res = await api('drive/folders/update', { folderId: 'foo', }, alice); @@ -937,7 +942,7 @@ describe('Endpoints', () => { visibleUserIds: [alice.id], }); - const res = await api('/notes/replies', { + const res = await api('notes/replies', { noteId: alicePost.id, }, carol); @@ -949,7 +954,7 @@ describe('Endpoints', () => { describe('notes/timeline', () => { test('フォロワー限定投稿が含まれる', async () => { - await api('/following/create', { + await api('following/create', { userId: carol.id, }, dave); @@ -958,7 +963,7 @@ describe('Endpoints', () => { visibility: 'followers', }); - const res = await api('/notes/timeline', {}, dave); + const res = await api('notes/timeline', {}, dave); assert.strictEqual(res.status, 200); assert.strictEqual(Array.isArray(res.body), true); @@ -979,12 +984,12 @@ describe('Endpoints', () => { test('他者に関するメモを更新できる', async () => { const memo = '10月まで低浮上とのこと。'; - const res1 = await api('/users/update-memo', { + const res1 = await api('users/update-memo', { memo, userId: bob.id, }, alice); - const res2 = await api('/users/show', { + const res2 = await api('users/show', { userId: bob.id, }, alice); assert.strictEqual(res1.status, 204); @@ -994,12 +999,12 @@ describe('Endpoints', () => { test('自分に関するメモを更新できる', async () => { const memo = 'チケットを月末までに買う。'; - const res1 = await api('/users/update-memo', { + const res1 = await api('users/update-memo', { memo, userId: alice.id, }, alice); - const res2 = await api('/users/show', { + const res2 = await api('users/show', { userId: alice.id, }, alice); assert.strictEqual(res1.status, 204); @@ -1009,17 +1014,17 @@ describe('Endpoints', () => { test('メモを削除できる', async () => { const memo = '10月まで低浮上とのこと。'; - await api('/users/update-memo', { + await api('users/update-memo', { memo, userId: bob.id, }, alice); - await api('/users/update-memo', { + await api('users/update-memo', { memo: '', userId: bob.id, }, alice); - const res = await api('/users/show', { + const res = await api('users/show', { userId: bob.id, }, alice); @@ -1032,21 +1037,21 @@ describe('Endpoints', () => { const memoCarolToBob = '例の件について今度問いただす。'; await Promise.all([ - api('/users/update-memo', { + api('users/update-memo', { memo: memoAliceToBob, userId: bob.id, }, alice), - api('/users/update-memo', { + api('users/update-memo', { memo: memoCarolToBob, userId: bob.id, }, carol), ]); const [resAlice, resCarol] = await Promise.all([ - api('/users/show', { + api('users/show', { userId: bob.id, }, alice), - api('/users/show', { + api('users/show', { userId: bob.id, }, carol), ]); diff --git a/packages/backend/test/e2e/exports.ts b/packages/backend/test/e2e/exports.ts index eb03935a2a..80a5331a6d 100644 --- a/packages/backend/test/e2e/exports.ts +++ b/packages/backend/test/e2e/exports.ts @@ -18,7 +18,7 @@ describe('export-clips', () => { // XXX: Any better way to get the result? async function pollFirstDriveFile() { while (true) { - const files = (await api('/drive/files', {}, alice)).body; + const files = (await api('drive/files', {}, alice)).body; if (!files.length) { await new Promise(r => setTimeout(r, 100)); continue; @@ -26,7 +26,7 @@ describe('export-clips', () => { if (files.length > 1) { throw new Error('Too many files?'); } - const file = (await api('/drive/files/show', { fileId: files[0].id }, alice)).body; + const file = (await api('drive/files/show', { fileId: files[0].id }, alice)).body; const res = await fetch(new URL(new URL(file.url).pathname, `http://127.0.0.1:${port}`)); return await res.json(); } @@ -44,16 +44,16 @@ describe('export-clips', () => { beforeEach(async () => { // Clean all clips and files of alice - const clips = (await api('/clips/list', {}, alice)).body; + const clips = (await api('clips/list', {}, alice)).body; for (const clip of clips) { - const res = await api('/clips/delete', { clipId: clip.id }, alice); + const res = await api('clips/delete', { clipId: clip.id }, alice); if (res.status !== 204) { throw new Error('Failed to delete clip'); } } - const files = (await api('/drive/files', {}, alice)).body; + const files = (await api('drive/files', {}, alice)).body; for (const file of files) { - const res = await api('/drive/files/delete', { fileId: file.id }, alice); + const res = await api('drive/files/delete', { fileId: file.id }, alice); if (res.status !== 204) { throw new Error('Failed to delete file'); } @@ -61,13 +61,13 @@ describe('export-clips', () => { }); test('basic export', async () => { - let res = await api('/clips/create', { + let res = await api('clips/create', { name: 'foo', description: 'bar', }, alice); assert.strictEqual(res.status, 200); - res = await api('/i/export-clips', {}, alice); + res = await api('i/export-clips', {}, alice); assert.strictEqual(res.status, 204); const exported = await pollFirstDriveFile(); @@ -77,7 +77,7 @@ describe('export-clips', () => { }); test('export with notes', async () => { - let res = await api('/clips/create', { + let res = await api('clips/create', { name: 'foo', description: 'bar', }, alice); @@ -96,14 +96,14 @@ describe('export-clips', () => { }); for (const note of [note1, note2]) { - res = await api('/clips/add-note', { + res = await api('clips/add-note', { clipId: clip.id, noteId: note.id, }, alice); assert.strictEqual(res.status, 204); } - res = await api('/i/export-clips', {}, alice); + res = await api('i/export-clips', {}, alice); assert.strictEqual(res.status, 204); const exported = await pollFirstDriveFile(); @@ -116,14 +116,14 @@ describe('export-clips', () => { }); test('multiple clips', async () => { - let res = await api('/clips/create', { + let res = await api('clips/create', { name: 'kawaii', description: 'kawaii', }, alice); assert.strictEqual(res.status, 200); const clip1 = res.body; - res = await api('/clips/create', { + res = await api('clips/create', { name: 'yuri', description: 'yuri', }, alice); @@ -138,19 +138,19 @@ describe('export-clips', () => { text: 'baz2', }); - res = await api('/clips/add-note', { + res = await api('clips/add-note', { clipId: clip1.id, noteId: note1.id, }, alice); assert.strictEqual(res.status, 204); - res = await api('/clips/add-note', { + res = await api('clips/add-note', { clipId: clip2.id, noteId: note2.id, }, alice); assert.strictEqual(res.status, 204); - res = await api('/i/export-clips', {}, alice); + res = await api('i/export-clips', {}, alice); assert.strictEqual(res.status, 204); const exported = await pollFirstDriveFile(); @@ -163,7 +163,7 @@ describe('export-clips', () => { }); test('Clipping other user\'s note', async () => { - let res = await api('/clips/create', { + let res = await api('clips/create', { name: 'kawaii', description: 'kawaii', }, alice); @@ -175,13 +175,13 @@ describe('export-clips', () => { visibility: 'followers', }); - res = await api('/clips/add-note', { + res = await api('clips/add-note', { clipId: clip.id, noteId: note.id, }, alice); assert.strictEqual(res.status, 204); - res = await api('/i/export-clips', {}, alice); + res = await api('i/export-clips', {}, alice); assert.strictEqual(res.status, 204); const exported = await pollFirstDriveFile(); diff --git a/packages/backend/test/e2e/fetch-resource.ts b/packages/backend/test/e2e/fetch-resource.ts index 74033b7dff..4851ed14be 100644 --- a/packages/backend/test/e2e/fetch-resource.ts +++ b/packages/backend/test/e2e/fetch-resource.ts @@ -23,13 +23,13 @@ const JSON_UTF8 = 'application/json; charset=utf-8'; describe('Webリソース', () => { let alice: misskey.entities.SignupResponse; - let aliceUploadedFile: any; - let alicesPost: any; - let alicePage: any; - let alicePlay: any; - let aliceClip: any; - let aliceGalleryPost: any; - let aliceChannel: any; + let aliceUploadedFile: misskey.entities.DriveFile | null; + let alicesPost: misskey.entities.Note; + let alicePage: misskey.entities.Page; + let alicePlay: misskey.entities.Flash; + let aliceClip: misskey.entities.Clip; + let aliceGalleryPost: misskey.entities.GalleryPost; + let aliceChannel: misskey.entities.Channel; let bob: misskey.entities.SignupResponse; @@ -77,7 +77,7 @@ describe('Webリソース', () => { beforeAll(async () => { alice = await signup({ username: 'alice' }); - aliceUploadedFile = await uploadFile(alice); + aliceUploadedFile = (await uploadFile(alice)).body; alicesPost = await post(alice, { text: 'test', }); @@ -85,7 +85,7 @@ describe('Webリソース', () => { alicePlay = await play(alice, {}); aliceClip = await clip(alice, {}); aliceGalleryPost = await galleryPost(alice, { - fileIds: [aliceUploadedFile.body.id], + fileIds: [aliceUploadedFile!.id], }); aliceChannel = await channel(alice, {}); diff --git a/packages/backend/test/e2e/ff-visibility.ts b/packages/backend/test/e2e/ff-visibility.ts index b59dd8824a..5d0c70a3c2 100644 --- a/packages/backend/test/e2e/ff-visibility.ts +++ b/packages/backend/test/e2e/ff-visibility.ts @@ -19,15 +19,15 @@ describe('FF visibility', () => { }, 1000 * 60 * 2); test('followingVisibility, followersVisibility がともに public なユーザーのフォロー/フォロワーを誰でも見れる', async () => { - await api('/i/update', { + await api('i/update', { followingVisibility: 'public', followersVisibility: 'public', }, alice); - const followingRes = await api('/users/following', { + const followingRes = await api('users/following', { userId: alice.id, }, bob); - const followersRes = await api('/users/followers', { + const followersRes = await api('users/followers', { userId: alice.id, }, bob); @@ -39,36 +39,36 @@ describe('FF visibility', () => { test('followingVisibility が public であれば followersVisibility の設定に関わらずユーザーのフォローを誰でも見れる', async () => { { - await api('/i/update', { + await api('i/update', { followingVisibility: 'public', followersVisibility: 'public', }, alice); - const followingRes = await api('/users/following', { + const followingRes = await api('users/following', { userId: alice.id, }, bob); assert.strictEqual(followingRes.status, 200); assert.strictEqual(Array.isArray(followingRes.body), true); } { - await api('/i/update', { + await api('i/update', { followingVisibility: 'public', followersVisibility: 'followers', }, alice); - const followingRes = await api('/users/following', { + const followingRes = await api('users/following', { userId: alice.id, }, bob); assert.strictEqual(followingRes.status, 200); assert.strictEqual(Array.isArray(followingRes.body), true); } { - await api('/i/update', { + await api('i/update', { followingVisibility: 'public', followersVisibility: 'private', }, alice); - const followingRes = await api('/users/following', { + const followingRes = await api('users/following', { userId: alice.id, }, bob); assert.strictEqual(followingRes.status, 200); @@ -78,36 +78,36 @@ describe('FF visibility', () => { test('followersVisibility が public であれば followingVisibility の設定に関わらずユーザーのフォロワーを誰でも見れる', async () => { { - await api('/i/update', { + await api('i/update', { followingVisibility: 'public', followersVisibility: 'public', }, alice); - const followersRes = await api('/users/followers', { + const followersRes = await api('users/followers', { userId: alice.id, }, bob); assert.strictEqual(followersRes.status, 200); assert.strictEqual(Array.isArray(followersRes.body), true); } { - await api('/i/update', { + await api('i/update', { followingVisibility: 'followers', followersVisibility: 'public', }, alice); - const followersRes = await api('/users/followers', { + const followersRes = await api('users/followers', { userId: alice.id, }, bob); assert.strictEqual(followersRes.status, 200); assert.strictEqual(Array.isArray(followersRes.body), true); } { - await api('/i/update', { + await api('i/update', { followingVisibility: 'private', followersVisibility: 'public', }, alice); - const followersRes = await api('/users/followers', { + const followersRes = await api('users/followers', { userId: alice.id, }, bob); assert.strictEqual(followersRes.status, 200); @@ -116,15 +116,15 @@ describe('FF visibility', () => { }); test('followingVisibility, followersVisibility がともに followers なユーザーのフォロー/フォロワーを自分で見れる', async () => { - await api('/i/update', { + await api('i/update', { followingVisibility: 'followers', followersVisibility: 'followers', }, alice); - const followingRes = await api('/users/following', { + const followingRes = await api('users/following', { userId: alice.id, }, alice); - const followersRes = await api('/users/followers', { + const followersRes = await api('users/followers', { userId: alice.id, }, alice); @@ -136,36 +136,36 @@ describe('FF visibility', () => { test('followingVisibility が followers なユーザーのフォローを followersVisibility の設定に関わらず自分で見れる', async () => { { - await api('/i/update', { + await api('i/update', { followingVisibility: 'followers', followersVisibility: 'public', }, alice); - const followingRes = await api('/users/following', { + const followingRes = await api('users/following', { userId: alice.id, }, alice); assert.strictEqual(followingRes.status, 200); assert.strictEqual(Array.isArray(followingRes.body), true); } { - await api('/i/update', { + await api('i/update', { followingVisibility: 'followers', followersVisibility: 'followers', }, alice); - const followingRes = await api('/users/following', { + const followingRes = await api('users/following', { userId: alice.id, }, alice); assert.strictEqual(followingRes.status, 200); assert.strictEqual(Array.isArray(followingRes.body), true); } { - await api('/i/update', { + await api('i/update', { followingVisibility: 'followers', followersVisibility: 'private', }, alice); - const followingRes = await api('/users/following', { + const followingRes = await api('users/following', { userId: alice.id, }, alice); assert.strictEqual(followingRes.status, 200); @@ -175,36 +175,36 @@ describe('FF visibility', () => { test('followersVisibility が followers なユーザーのフォロワーを followingVisibility の設定に関わらず自分で見れる', async () => { { - await api('/i/update', { + await api('i/update', { followingVisibility: 'public', followersVisibility: 'followers', }, alice); - const followersRes = await api('/users/followers', { + const followersRes = await api('users/followers', { userId: alice.id, }, alice); assert.strictEqual(followersRes.status, 200); assert.strictEqual(Array.isArray(followersRes.body), true); } { - await api('/i/update', { + await api('i/update', { followingVisibility: 'followers', followersVisibility: 'followers', }, alice); - const followersRes = await api('/users/followers', { + const followersRes = await api('users/followers', { userId: alice.id, }, alice); assert.strictEqual(followersRes.status, 200); assert.strictEqual(Array.isArray(followersRes.body), true); } { - await api('/i/update', { + await api('i/update', { followingVisibility: 'private', followersVisibility: 'followers', }, alice); - const followersRes = await api('/users/followers', { + const followersRes = await api('users/followers', { userId: alice.id, }, alice); assert.strictEqual(followersRes.status, 200); @@ -213,15 +213,15 @@ describe('FF visibility', () => { }); test('followingVisibility, followersVisibility がともに followers なユーザーのフォロー/フォロワーを非フォロワーが見れない', async () => { - await api('/i/update', { + await api('i/update', { followingVisibility: 'followers', followersVisibility: 'followers', }, alice); - const followingRes = await api('/users/following', { + const followingRes = await api('users/following', { userId: alice.id, }, bob); - const followersRes = await api('/users/followers', { + const followersRes = await api('users/followers', { userId: alice.id, }, bob); @@ -231,34 +231,34 @@ describe('FF visibility', () => { test('followingVisibility が followers なユーザーのフォローを followersVisibility の設定に関わらず非フォロワーが見れない', async () => { { - await api('/i/update', { + await api('i/update', { followingVisibility: 'followers', followersVisibility: 'public', }, alice); - const followingRes = await api('/users/following', { + const followingRes = await api('users/following', { userId: alice.id, }, bob); assert.strictEqual(followingRes.status, 400); } { - await api('/i/update', { + await api('i/update', { followingVisibility: 'followers', followersVisibility: 'followers', }, alice); - const followingRes = await api('/users/following', { + const followingRes = await api('users/following', { userId: alice.id, }, bob); assert.strictEqual(followingRes.status, 400); } { - await api('/i/update', { + await api('i/update', { followingVisibility: 'followers', followersVisibility: 'private', }, alice); - const followingRes = await api('/users/following', { + const followingRes = await api('users/following', { userId: alice.id, }, bob); assert.strictEqual(followingRes.status, 400); @@ -267,34 +267,34 @@ describe('FF visibility', () => { test('followersVisibility が followers なユーザーのフォロワーを followingVisibility の設定に関わらず非フォロワーが見れない', async () => { { - await api('/i/update', { + await api('i/update', { followingVisibility: 'public', followersVisibility: 'followers', }, alice); - const followersRes = await api('/users/followers', { + const followersRes = await api('users/followers', { userId: alice.id, }, bob); assert.strictEqual(followersRes.status, 400); } { - await api('/i/update', { + await api('i/update', { followingVisibility: 'followers', followersVisibility: 'followers', }, alice); - const followersRes = await api('/users/followers', { + const followersRes = await api('users/followers', { userId: alice.id, }, bob); assert.strictEqual(followersRes.status, 400); } { - await api('/i/update', { + await api('i/update', { followingVisibility: 'private', followersVisibility: 'followers', }, alice); - const followersRes = await api('/users/followers', { + const followersRes = await api('users/followers', { userId: alice.id, }, bob); assert.strictEqual(followersRes.status, 400); @@ -302,19 +302,19 @@ describe('FF visibility', () => { }); test('followingVisibility, followersVisibility がともに followers なユーザーのフォロー/フォロワーをフォロワーが見れる', async () => { - await api('/i/update', { + await api('i/update', { followingVisibility: 'followers', followersVisibility: 'followers', }, alice); - await api('/following/create', { + await api('following/create', { userId: alice.id, }, bob); - const followingRes = await api('/users/following', { + const followingRes = await api('users/following', { userId: alice.id, }, bob); - const followersRes = await api('/users/followers', { + const followersRes = await api('users/followers', { userId: alice.id, }, bob); @@ -326,45 +326,45 @@ describe('FF visibility', () => { test('followingVisibility が followers なユーザーのフォローを followersVisibility の設定に関わらずフォロワーが見れる', async () => { { - await api('/i/update', { + await api('i/update', { followingVisibility: 'followers', followersVisibility: 'public', }, alice); - await api('/following/create', { + await api('following/create', { userId: alice.id, }, bob); - const followingRes = await api('/users/following', { + const followingRes = await api('users/following', { userId: alice.id, }, bob); assert.strictEqual(followingRes.status, 200); assert.strictEqual(Array.isArray(followingRes.body), true); } { - await api('/i/update', { + await api('i/update', { followingVisibility: 'followers', followersVisibility: 'followers', }, alice); - await api('/following/create', { + await api('following/create', { userId: alice.id, }, bob); - const followingRes = await api('/users/following', { + const followingRes = await api('users/following', { userId: alice.id, }, bob); assert.strictEqual(followingRes.status, 200); assert.strictEqual(Array.isArray(followingRes.body), true); } { - await api('/i/update', { + await api('i/update', { followingVisibility: 'followers', followersVisibility: 'private', }, alice); - await api('/following/create', { + await api('following/create', { userId: alice.id, }, bob); - const followingRes = await api('/users/following', { + const followingRes = await api('users/following', { userId: alice.id, }, bob); assert.strictEqual(followingRes.status, 200); @@ -374,45 +374,45 @@ describe('FF visibility', () => { test('followersVisibility が followers なユーザーのフォロワーを followingVisibility の設定に関わらずフォロワーが見れる', async () => { { - await api('/i/update', { + await api('i/update', { followingVisibility: 'public', followersVisibility: 'followers', }, alice); - await api('/following/create', { + await api('following/create', { userId: alice.id, }, bob); - const followersRes = await api('/users/followers', { + const followersRes = await api('users/followers', { userId: alice.id, }, bob); assert.strictEqual(followersRes.status, 200); assert.strictEqual(Array.isArray(followersRes.body), true); } { - await api('/i/update', { + await api('i/update', { followingVisibility: 'followers', followersVisibility: 'followers', }, alice); - await api('/following/create', { + await api('following/create', { userId: alice.id, }, bob); - const followersRes = await api('/users/followers', { + const followersRes = await api('users/followers', { userId: alice.id, }, bob); assert.strictEqual(followersRes.status, 200); assert.strictEqual(Array.isArray(followersRes.body), true); } { - await api('/i/update', { + await api('i/update', { followingVisibility: 'private', followersVisibility: 'followers', }, alice); - await api('/following/create', { + await api('following/create', { userId: alice.id, }, bob); - const followersRes = await api('/users/followers', { + const followersRes = await api('users/followers', { userId: alice.id, }, bob); assert.strictEqual(followersRes.status, 200); @@ -421,15 +421,15 @@ describe('FF visibility', () => { }); test('followingVisibility, followersVisibility がともに private なユーザーのフォロー/フォロワーを自分で見れる', async () => { - await api('/i/update', { + await api('i/update', { followingVisibility: 'private', followersVisibility: 'private', }, alice); - const followingRes = await api('/users/following', { + const followingRes = await api('users/following', { userId: alice.id, }, alice); - const followersRes = await api('/users/followers', { + const followersRes = await api('users/followers', { userId: alice.id, }, alice); @@ -441,36 +441,36 @@ describe('FF visibility', () => { test('followingVisibility が private なユーザーのフォローを followersVisibility の設定に関わらず自分で見れる', async () => { { - await api('/i/update', { + await api('i/update', { followingVisibility: 'private', followersVisibility: 'public', }, alice); - const followingRes = await api('/users/following', { + const followingRes = await api('users/following', { userId: alice.id, }, alice); assert.strictEqual(followingRes.status, 200); assert.strictEqual(Array.isArray(followingRes.body), true); } { - await api('/i/update', { + await api('i/update', { followingVisibility: 'private', followersVisibility: 'followers', }, alice); - const followingRes = await api('/users/following', { + const followingRes = await api('users/following', { userId: alice.id, }, alice); assert.strictEqual(followingRes.status, 200); assert.strictEqual(Array.isArray(followingRes.body), true); } { - await api('/i/update', { + await api('i/update', { followingVisibility: 'private', followersVisibility: 'private', }, alice); - const followingRes = await api('/users/following', { + const followingRes = await api('users/following', { userId: alice.id, }, alice); assert.strictEqual(followingRes.status, 200); @@ -480,36 +480,36 @@ describe('FF visibility', () => { test('followersVisibility が private なユーザーのフォロワーを followingVisibility の設定に関わらず自分で見れる', async () => { { - await api('/i/update', { + await api('i/update', { followingVisibility: 'public', followersVisibility: 'private', }, alice); - const followersRes = await api('/users/followers', { + const followersRes = await api('users/followers', { userId: alice.id, }, alice); assert.strictEqual(followersRes.status, 200); assert.strictEqual(Array.isArray(followersRes.body), true); } { - await api('/i/update', { + await api('i/update', { followingVisibility: 'followers', followersVisibility: 'private', }, alice); - const followersRes = await api('/users/followers', { + const followersRes = await api('users/followers', { userId: alice.id, }, alice); assert.strictEqual(followersRes.status, 200); assert.strictEqual(Array.isArray(followersRes.body), true); } { - await api('/i/update', { + await api('i/update', { followingVisibility: 'private', followersVisibility: 'private', }, alice); - const followersRes = await api('/users/followers', { + const followersRes = await api('users/followers', { userId: alice.id, }, alice); assert.strictEqual(followersRes.status, 200); @@ -518,15 +518,15 @@ describe('FF visibility', () => { }); test('followingVisibility, followersVisibility がともに private なユーザーのフォロー/フォロワーを他人が見れない', async () => { - await api('/i/update', { + await api('i/update', { followingVisibility: 'private', followersVisibility: 'private', }, alice); - const followingRes = await api('/users/following', { + const followingRes = await api('users/following', { userId: alice.id, }, bob); - const followersRes = await api('/users/followers', { + const followersRes = await api('users/followers', { userId: alice.id, }, bob); @@ -536,34 +536,34 @@ describe('FF visibility', () => { test('followingVisibility が private なユーザーのフォローを followersVisibility の設定に関わらず他人が見れない', async () => { { - await api('/i/update', { + await api('i/update', { followingVisibility: 'private', followersVisibility: 'public', }, alice); - const followingRes = await api('/users/following', { + const followingRes = await api('users/following', { userId: alice.id, }, bob); assert.strictEqual(followingRes.status, 400); } { - await api('/i/update', { + await api('i/update', { followingVisibility: 'private', followersVisibility: 'followers', }, alice); - const followingRes = await api('/users/following', { + const followingRes = await api('users/following', { userId: alice.id, }, bob); assert.strictEqual(followingRes.status, 400); } { - await api('/i/update', { + await api('i/update', { followingVisibility: 'private', followersVisibility: 'private', }, alice); - const followingRes = await api('/users/following', { + const followingRes = await api('users/following', { userId: alice.id, }, bob); assert.strictEqual(followingRes.status, 400); @@ -572,34 +572,34 @@ describe('FF visibility', () => { test('followersVisibility が private なユーザーのフォロワーを followingVisibility の設定に関わらず他人が見れない', async () => { { - await api('/i/update', { + await api('i/update', { followingVisibility: 'public', followersVisibility: 'private', }, alice); - const followersRes = await api('/users/followers', { + const followersRes = await api('users/followers', { userId: alice.id, }, bob); assert.strictEqual(followersRes.status, 400); } { - await api('/i/update', { + await api('i/update', { followingVisibility: 'followers', followersVisibility: 'private', }, alice); - const followersRes = await api('/users/followers', { + const followersRes = await api('users/followers', { userId: alice.id, }, bob); assert.strictEqual(followersRes.status, 400); } { - await api('/i/update', { + await api('i/update', { followingVisibility: 'private', followersVisibility: 'private', }, alice); - const followersRes = await api('/users/followers', { + const followersRes = await api('users/followers', { userId: alice.id, }, bob); assert.strictEqual(followersRes.status, 400); @@ -609,7 +609,7 @@ describe('FF visibility', () => { describe('AP', () => { test('followingVisibility が public 以外ならばAPからはフォローを取得できない', async () => { { - await api('/i/update', { + await api('i/update', { followingVisibility: 'public', }, alice); @@ -617,7 +617,7 @@ describe('FF visibility', () => { assert.strictEqual(followingRes.status, 200); } { - await api('/i/update', { + await api('i/update', { followingVisibility: 'followers', }, alice); @@ -625,7 +625,7 @@ describe('FF visibility', () => { assert.strictEqual(followingRes.status, 403); } { - await api('/i/update', { + await api('i/update', { followingVisibility: 'private', }, alice); @@ -636,7 +636,7 @@ describe('FF visibility', () => { test('followersVisibility が public 以外ならばAPからはフォロワーを取得できない', async () => { { - await api('/i/update', { + await api('i/update', { followersVisibility: 'public', }, alice); @@ -644,7 +644,7 @@ describe('FF visibility', () => { assert.strictEqual(followersRes.status, 200); } { - await api('/i/update', { + await api('i/update', { followersVisibility: 'followers', }, alice); @@ -652,7 +652,7 @@ describe('FF visibility', () => { assert.strictEqual(followersRes.status, 403); } { - await api('/i/update', { + await api('i/update', { followersVisibility: 'private', }, alice); diff --git a/packages/backend/test/e2e/move.ts b/packages/backend/test/e2e/move.ts index f6417e39b5..4e5306da97 100644 --- a/packages/backend/test/e2e/move.ts +++ b/packages/backend/test/e2e/move.ts @@ -55,7 +55,7 @@ describe('Account Move', () => { }, 1000 * 10); test('Able to create an alias', async () => { - const res = await api('/i/update', { + const res = await api('i/update', { alsoKnownAs: [`@alice@${url.hostname}`], }, bob); @@ -67,7 +67,7 @@ describe('Account Move', () => { }); test('Able to create a local alias without hostname', async () => { - await api('/i/update', { + await api('i/update', { alsoKnownAs: ['@alice'], }, bob); @@ -77,7 +77,7 @@ describe('Account Move', () => { }); test('Able to create a local alias without @', async () => { - await api('/i/update', { + await api('i/update', { alsoKnownAs: ['alice'], }, bob); @@ -87,7 +87,7 @@ describe('Account Move', () => { }); test('Able to set remote user (but may fail)', async () => { - const res = await api('/i/update', { + const res = await api('i/update', { alsoKnownAs: ['@syuilo@example.com'], }, bob); @@ -97,7 +97,7 @@ describe('Account Move', () => { }); test('Unable to add duplicated aliases to alsoKnownAs', async () => { - const res = await api('/i/update', { + const res = await api('i/update', { alsoKnownAs: [`@alice@${url.hostname}`, `@alice@${url.hostname}`], }, bob); @@ -107,7 +107,7 @@ describe('Account Move', () => { }); test('Unable to add itself', async () => { - const res = await api('/i/update', { + const res = await api('i/update', { alsoKnownAs: [`@bob@${url.hostname}`], }, bob); @@ -117,7 +117,7 @@ describe('Account Move', () => { }); test('Unable to add a nonexisting local account to alsoKnownAs', async () => { - const res1 = await api('/i/update', { + const res1 = await api('i/update', { alsoKnownAs: [`@nonexist@${url.hostname}`], }, bob); @@ -125,7 +125,7 @@ describe('Account Move', () => { assert.strictEqual(res1.body.error.code, 'NO_SUCH_USER'); assert.strictEqual(res1.body.error.id, 'fcd2eef9-a9b2-4c4f-8624-038099e90aa5'); - const res2 = await api('/i/update', { + const res2 = await api('i/update', { alsoKnownAs: ['@alice', 'nonexist'], }, bob); @@ -135,7 +135,7 @@ describe('Account Move', () => { }); test('Able to add two existing local account to alsoKnownAs', async () => { - await api('/i/update', { + await api('i/update', { alsoKnownAs: [`@alice@${url.hostname}`, `@carol@${url.hostname}`], }, bob); @@ -146,10 +146,10 @@ describe('Account Move', () => { }); test('Able to properly overwrite alsoKnownAs', async () => { - await api('/i/update', { + await api('i/update', { alsoKnownAs: [`@alice@${url.hostname}`], }, bob); - await api('/i/update', { + await api('i/update', { alsoKnownAs: [`@carol@${url.hostname}`, `@dave@${url.hostname}`], }, bob); @@ -164,27 +164,27 @@ describe('Account Move', () => { let antennaId = ''; beforeAll(async () => { - await api('/i/update', { + await api('i/update', { alsoKnownAs: [`@alice@${url.hostname}`], }, root); - const listRoot = await api('/users/lists/create', { + const listRoot = await api('users/lists/create', { name: secureRndstr(8), }, root); - await api('/users/lists/push', { + await api('users/lists/push', { listId: listRoot.body.id, userId: alice.id, }, root); - await api('/following/create', { + await api('following/create', { userId: root.id, }, alice); - await api('/following/create', { + await api('following/create', { userId: eve.id, }, alice); - const antenna = await api('/antennas/create', { + const antenna = await api('antennas/create', { name: secureRndstr(8), src: 'home', - keywords: [secureRndstr(8)], + keywords: [[secureRndstr(8)]], excludeKeywords: [], users: [], caseSensitive: false, @@ -195,48 +195,48 @@ describe('Account Move', () => { }, alice); antennaId = antenna.body.id; - await api('/i/update', { + await api('i/update', { alsoKnownAs: [`@alice@${url.hostname}`], }, bob); - await api('/following/create', { + await api('following/create', { userId: alice.id, }, carol); - await api('/mute/create', { + await api('mute/create', { userId: alice.id, }, dave); - await api('/blocking/create', { + await api('blocking/create', { userId: alice.id, }, dave); - await api('/following/create', { + await api('following/create', { userId: eve.id, }, dave); - await api('/following/create', { + await api('following/create', { userId: dave.id, }, eve); - const listEve = await api('/users/lists/create', { + const listEve = await api('users/lists/create', { name: secureRndstr(8), }, eve); - await api('/users/lists/push', { + await api('users/lists/push', { listId: listEve.body.id, userId: bob.id, }, eve); - await api('/i/update', { + await api('i/update', { isLocked: true, }, frank); - await api('/following/create', { + await api('following/create', { userId: frank.id, }, alice); - await api('/following/requests/accept', { + await api('following/requests/accept', { userId: alice.id, }, frank); }, 1000 * 10); test('Prohibit the root account from moving', async () => { - const res = await api('/i/move', { + const res = await api('i/move', { moveToAccount: `@bob@${url.hostname}`, }, root); @@ -246,7 +246,7 @@ describe('Account Move', () => { }); test('Unable to move to a nonexisting local account', async () => { - const res = await api('/i/move', { + const res = await api('i/move', { moveToAccount: `@nonexist@${url.hostname}`, }, alice); @@ -256,7 +256,7 @@ describe('Account Move', () => { }); test('Unable to move if alsoKnownAs is invalid', async () => { - const res = await api('/i/move', { + const res = await api('i/move', { moveToAccount: `@carol@${url.hostname}`, }, alice); @@ -266,7 +266,7 @@ describe('Account Move', () => { }); test('Relationships have been properly migrated', async () => { - const move = await api('/i/move', { + const move = await api('i/move', { moveToAccount: `@bob@${url.hostname}`, }, alice); @@ -275,13 +275,13 @@ describe('Account Move', () => { await sleep(1000 * 3); // wait for jobs to finish // Unfollow delayed? - const aliceFollowings = await api('/users/following', { + const aliceFollowings = await api('users/following', { userId: alice.id, }, alice); assert.strictEqual(aliceFollowings.status, 200); assert.strictEqual(aliceFollowings.body.length, 3); - const carolFollowings = await api('/users/following', { + const carolFollowings = await api('users/following', { userId: carol.id, }, carol); assert.strictEqual(carolFollowings.status, 200); @@ -289,25 +289,25 @@ describe('Account Move', () => { assert.strictEqual(carolFollowings.body[0].followeeId, bob.id); assert.strictEqual(carolFollowings.body[1].followeeId, alice.id); - const blockings = await api('/blocking/list', {}, dave); + const blockings = await api('blocking/list', {}, dave); assert.strictEqual(blockings.status, 200); assert.strictEqual(blockings.body.length, 2); assert.strictEqual(blockings.body[0].blockeeId, bob.id); assert.strictEqual(blockings.body[1].blockeeId, alice.id); - const mutings = await api('/mute/list', {}, dave); + const mutings = await api('mute/list', {}, dave); assert.strictEqual(mutings.status, 200); assert.strictEqual(mutings.body.length, 2); assert.strictEqual(mutings.body[0].muteeId, bob.id); assert.strictEqual(mutings.body[1].muteeId, alice.id); - const rootLists = await api('/users/lists/list', {}, root); + const rootLists = await api('users/lists/list', {}, root); assert.strictEqual(rootLists.status, 200); assert.strictEqual(rootLists.body[0].userIds.length, 2); assert.ok(rootLists.body[0].userIds.find((id: string) => id === bob.id)); assert.ok(rootLists.body[0].userIds.find((id: string) => id === alice.id)); - const eveLists = await api('/users/lists/list', {}, eve); + const eveLists = await api('users/lists/list', {}, eve); assert.strictEqual(eveLists.status, 200); assert.strictEqual(eveLists.body[0].userIds.length, 1); assert.ok(eveLists.body[0].userIds.find((id: string) => id === bob.id)); @@ -315,13 +315,13 @@ describe('Account Move', () => { test('A locked account automatically accept the follow request if it had already accepted the old account.', async () => { await successfulApiCall({ - endpoint: '/following/create', + endpoint: 'following/create', parameters: { userId: frank.id, }, user: bob, }); - const followers = await api('/users/followers', { + const followers = await api('users/followers', { userId: frank.id, }, frank); @@ -333,7 +333,7 @@ describe('Account Move', () => { test('Unfollowed after 10 sec (24 hours in production).', async () => { await sleep(1000 * 8); - const following = await api('/users/following', { + const following = await api('users/following', { userId: alice.id, }, alice); @@ -342,7 +342,7 @@ describe('Account Move', () => { }); test('Unable to move if the destination account has already moved.', async () => { - const res = await api('/i/move', { + const res = await api('i/move', { moveToAccount: `@alice@${url.hostname}`, }, bob); @@ -352,7 +352,7 @@ describe('Account Move', () => { }); test('Follow and follower counts are properly adjusted', async () => { - await api('/following/create', { + await api('following/create', { userId: alice.id, }, eve); const newAlice = await Users.findOneByOrFail({ id: alice.id }); @@ -365,7 +365,7 @@ describe('Account Move', () => { assert.strictEqual(newEve.followingCount, 1); assert.strictEqual(newEve.followersCount, 1); - await api('/following/delete', { + await api('following/delete', { userId: alice.id, }, eve); newEve = await Users.findOneByOrFail({ id: eve.id }); @@ -374,49 +374,49 @@ describe('Account Move', () => { }); test.each([ - '/antennas/create', - '/channels/create', - '/channels/favorite', - '/channels/follow', - '/channels/unfavorite', - '/channels/unfollow', - '/clips/add-note', - '/clips/create', - '/clips/favorite', - '/clips/remove-note', - '/clips/unfavorite', - '/clips/update', - '/drive/files/upload-from-url', - '/flash/create', - '/flash/like', - '/flash/unlike', - '/flash/update', - '/following/create', - '/gallery/posts/create', - '/gallery/posts/like', - '/gallery/posts/unlike', - '/gallery/posts/update', - '/i/claim-achievement', - '/i/move', - '/i/import-blocking', - '/i/import-following', - '/i/import-muting', - '/i/import-user-lists', - '/i/pin', - '/mute/create', - '/notes/create', - '/notes/favorites/create', - '/notes/polls/vote', - '/notes/reactions/create', - '/pages/create', - '/pages/like', - '/pages/unlike', - '/pages/update', - '/renote-mute/create', - '/users/lists/create', - '/users/lists/pull', - '/users/lists/push', - ])('Prohibit access after moving: %s', async (endpoint) => { + 'antennas/create', + 'channels/create', + 'channels/favorite', + 'channels/follow', + 'channels/unfavorite', + 'channels/unfollow', + 'clips/add-note', + 'clips/create', + 'clips/favorite', + 'clips/remove-note', + 'clips/unfavorite', + 'clips/update', + 'drive/files/upload-from-url', + 'flash/create', + 'flash/like', + 'flash/unlike', + 'flash/update', + 'following/create', + 'gallery/posts/create', + 'gallery/posts/like', + 'gallery/posts/unlike', + 'gallery/posts/update', + 'i/claim-achievement', + 'i/move', + 'i/import-blocking', + 'i/import-following', + 'i/import-muting', + 'i/import-user-lists', + 'i/pin', + 'mute/create', + 'notes/create', + 'notes/favorites/create', + 'notes/polls/vote', + 'notes/reactions/create', + 'pages/create', + 'pages/like', + 'pages/unlike', + 'pages/update', + 'renote-mute/create', + 'users/lists/create', + 'users/lists/pull', + 'users/lists/push', + ] as const)('Prohibit access after moving: %s', async (endpoint) => { const res = await api(endpoint, {}, alice); assert.strictEqual(res.status, 403); assert.strictEqual(res.body.error.code, 'YOUR_ACCOUNT_MOVED'); @@ -424,11 +424,11 @@ describe('Account Move', () => { }); test('Prohibit access after moving: /antennas/update', async () => { - const res = await api('/antennas/update', { + const res = await api('antennas/update', { antennaId, name: secureRndstr(8), src: 'users', - keywords: [secureRndstr(8)], + keywords: [[secureRndstr(8)]], excludeKeywords: [], users: [eve.id], caseSensitive: false, @@ -447,12 +447,12 @@ describe('Account Move', () => { const res = await uploadFile(alice); assert.strictEqual(res.status, 403); - assert.strictEqual(res.body.error.code, 'YOUR_ACCOUNT_MOVED'); - assert.strictEqual(res.body.error.id, '56f20ec9-fd06-4fa5-841b-edd6d7d4fa31'); + assert.strictEqual((res.body! as any as { error: misskey.api.APIError }).error.code, 'YOUR_ACCOUNT_MOVED'); + assert.strictEqual((res.body! as any as { error: misskey.api.APIError }).error.id, '56f20ec9-fd06-4fa5-841b-edd6d7d4fa31'); }); test('Prohibit updating alsoKnownAs after moving', async () => { - const res = await api('/i/update', { + const res = await api('i/update', { alsoKnownAs: [`@eve@${url.hostname}`], }, alice); diff --git a/packages/backend/test/e2e/mute.ts b/packages/backend/test/e2e/mute.ts index 1d28e07b7d..0e52c5decc 100644 --- a/packages/backend/test/e2e/mute.ts +++ b/packages/backend/test/e2e/mute.ts @@ -19,21 +19,31 @@ describe('Mute', () => { alice = await signup({ username: 'alice' }); bob = await signup({ username: 'bob' }); carol = await signup({ username: 'carol' }); + + // Mute: alice ==> carol + await api('mute/create', { + userId: carol.id, + }, alice); }, 1000 * 60 * 2); test('ミュート作成', async () => { - const res = await api('/mute/create', { - userId: carol.id, + const res = await api('mute/create', { + userId: bob.id, }, alice); assert.strictEqual(res.status, 204); + + // 単体でも走らせられるように副作用消す + await api('mute/delete', { + userId: bob.id, + }, alice); }); test('「自分宛ての投稿」にミュートしているユーザーの投稿が含まれない', async () => { const bobNote = await post(bob, { text: '@alice hi' }); const carolNote = await post(carol, { text: '@alice hi' }); - const res = await api('/notes/mentions', {}, alice); + const res = await api('notes/mentions', {}, alice); assert.strictEqual(res.status, 200); assert.strictEqual(Array.isArray(res.body), true); @@ -43,11 +53,11 @@ describe('Mute', () => { test('ミュートしているユーザーからメンションされても、hasUnreadMentions が true にならない', async () => { // 状態リセット - await api('/i/read-all-unread-notes', {}, alice); + await api('i/read-all-unread-notes', {}, alice); await post(carol, { text: '@alice hi' }); - const res = await api('/i', {}, alice); + const res = await api('i', {}, alice); assert.strictEqual(res.status, 200); assert.strictEqual(res.body.hasUnreadMentions, false); @@ -55,7 +65,7 @@ describe('Mute', () => { test('ミュートしているユーザーからメンションされても、ストリームに unreadMention イベントが流れてこない', async () => { // 状態リセット - await api('/i/read-all-unread-notes', {}, alice); + await api('i/read-all-unread-notes', {}, alice); const fired = await waitFire(alice, 'main', () => post(carol, { text: '@alice hi' }), msg => msg.type === 'unreadMention'); @@ -64,8 +74,8 @@ describe('Mute', () => { test('ミュートしているユーザーからメンションされても、ストリームに unreadNotification イベントが流れてこない', async () => { // 状態リセット - await api('/i/read-all-unread-notes', {}, alice); - await api('/notifications/mark-all-as-read', {}, alice); + await api('i/read-all-unread-notes', {}, alice); + await api('notifications/mark-all-as-read', {}, alice); const fired = await waitFire(alice, 'main', () => post(carol, { text: '@alice hi' }), msg => msg.type === 'unreadNotification'); @@ -78,7 +88,7 @@ describe('Mute', () => { const bobNote = await post(bob, { text: 'hi' }); const carolNote = await post(carol, { text: 'hi' }); - const res = await api('/notes/local-timeline', {}, alice); + const res = await api('notes/local-timeline', {}, alice); assert.strictEqual(res.status, 200); assert.strictEqual(Array.isArray(res.body), true); @@ -94,7 +104,7 @@ describe('Mute', () => { renoteId: carolNote.id, }); - const res = await api('/notes/local-timeline', {}, alice); + const res = await api('notes/local-timeline', {}, alice); assert.strictEqual(res.status, 200); assert.strictEqual(Array.isArray(res.body), true); @@ -110,7 +120,7 @@ describe('Mute', () => { await react(bob, aliceNote, 'like'); await react(carol, aliceNote, 'like'); - const res = await api('/i/notifications', {}, alice); + const res = await api('i/notifications', {}, alice); assert.strictEqual(res.status, 200); assert.strictEqual(Array.isArray(res.body), true); @@ -123,7 +133,7 @@ describe('Mute', () => { await post(bob, { text: '@alice hi', replyId: aliceNote.id }); await post(carol, { text: '@alice hi', replyId: aliceNote.id }); - const res = await api('/i/notifications', {}, alice); + const res = await api('i/notifications', {}, alice); assert.strictEqual(res.status, 200); assert.strictEqual(Array.isArray(res.body), true); @@ -137,7 +147,7 @@ describe('Mute', () => { await post(bob, { text: '@alice hi' }); await post(carol, { text: '@alice hi' }); - const res = await api('/i/notifications', {}, alice); + const res = await api('i/notifications', {}, alice); assert.strictEqual(res.status, 200); assert.strictEqual(Array.isArray(res.body), true); @@ -151,7 +161,7 @@ describe('Mute', () => { await post(bob, { text: 'hi', renoteId: aliceNote.id }); await post(carol, { text: 'hi', renoteId: aliceNote.id }); - const res = await api('/i/notifications', {}, alice); + const res = await api('i/notifications', {}, alice); assert.strictEqual(res.status, 200); assert.strictEqual(Array.isArray(res.body), true); @@ -165,7 +175,7 @@ describe('Mute', () => { await post(bob, { renoteId: aliceNote.id }); await post(carol, { renoteId: aliceNote.id }); - const res = await api('/i/notifications', {}, alice); + const res = await api('i/notifications', {}, alice); assert.strictEqual(res.status, 200); assert.strictEqual(Array.isArray(res.body), true); @@ -175,30 +185,36 @@ describe('Mute', () => { }); test('通知にミュートしているユーザーからのフォロー通知が含まれない', async () => { - await api('/i/follow', { userId: alice.id }, bob); - await api('/i/follow', { userId: alice.id }, carol); + await api('following/create', { userId: alice.id }, bob); + await api('following/create', { userId: alice.id }, carol); - const res = await api('/i/notifications', {}, alice); + const res = await api('i/notifications', {}, alice); assert.strictEqual(res.status, 200); assert.strictEqual(Array.isArray(res.body), true); assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true); assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false); + + await api('following/delete', { userId: alice.id }, bob); + await api('following/delete', { userId: alice.id }, carol); }); test('通知にミュートしているユーザーからのフォローリクエストが含まれない', async () => { - await api('/i/update/', { isLocked: true }, alice); - await api('/following/create', { userId: alice.id }, bob); - await api('/following/create', { userId: alice.id }, carol); + await api('i/update', { isLocked: true }, alice); + await api('following/create', { userId: alice.id }, bob); + await api('following/create', { userId: alice.id }, carol); - const res = await api('/i/notifications', {}, alice); + const res = await api('i/notifications', {}, alice); assert.strictEqual(res.status, 200); assert.strictEqual(Array.isArray(res.body), true); assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true); assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false); + + await api('following/delete', { userId: alice.id }, bob); + await api('following/delete', { userId: alice.id }, carol); }); }); @@ -208,7 +224,7 @@ describe('Mute', () => { await react(bob, aliceNote, 'like'); await react(carol, aliceNote, 'like'); - const res = await api('/i/notifications-grouped', {}, alice); + const res = await api('i/notifications-grouped', {}, alice); assert.strictEqual(res.status, 200); assert.strictEqual(Array.isArray(res.body), true); @@ -220,7 +236,7 @@ describe('Mute', () => { await post(bob, { text: '@alice hi', replyId: aliceNote.id }); await post(carol, { text: '@alice hi', replyId: aliceNote.id }); - const res = await api('/i/notifications-grouped', {}, alice); + const res = await api('i/notifications-grouped', {}, alice); assert.strictEqual(res.status, 200); assert.strictEqual(Array.isArray(res.body), true); @@ -234,7 +250,7 @@ describe('Mute', () => { await post(bob, { text: '@alice hi' }); await post(carol, { text: '@alice hi' }); - const res = await api('/i/notifications-grouped', {}, alice); + const res = await api('i/notifications-grouped', {}, alice); assert.strictEqual(res.status, 200); assert.strictEqual(Array.isArray(res.body), true); @@ -248,7 +264,7 @@ describe('Mute', () => { await post(bob, { text: 'hi', renoteId: aliceNote.id }); await post(carol, { text: 'hi', renoteId: aliceNote.id }); - const res = await api('/i/notifications-grouped', {}, alice); + const res = await api('i/notifications-grouped', {}, alice); assert.strictEqual(res.status, 200); assert.strictEqual(Array.isArray(res.body), true); @@ -262,7 +278,7 @@ describe('Mute', () => { await post(bob, { renoteId: aliceNote.id }); await post(carol, { renoteId: aliceNote.id }); - const res = await api('/i/notifications-grouped', {}, alice); + const res = await api('i/notifications-grouped', {}, alice); assert.strictEqual(res.status, 200); assert.strictEqual(Array.isArray(res.body), true); @@ -272,24 +288,27 @@ describe('Mute', () => { }); test('通知にミュートしているユーザーからのフォロー通知が含まれない', async () => { - await api('/i/follow', { userId: alice.id }, bob); - await api('/i/follow', { userId: alice.id }, carol); + await api('following/create', { userId: alice.id }, bob); + await api('following/create', { userId: alice.id }, carol); - const res = await api('/i/notifications-grouped', {}, alice); + const res = await api('i/notifications-grouped', {}, alice); assert.strictEqual(res.status, 200); assert.strictEqual(Array.isArray(res.body), true); assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true); assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false); + + await api('following/delete', { userId: alice.id }, bob); + await api('following/delete', { userId: alice.id }, carol); }); test('通知にミュートしているユーザーからのフォローリクエストが含まれない', async () => { - await api('/i/update/', { isLocked: true }, alice); - await api('/following/create', { userId: alice.id }, bob); - await api('/following/create', { userId: alice.id }, carol); + await api('i/update', { isLocked: true }, alice); + await api('following/create', { userId: alice.id }, bob); + await api('following/create', { userId: alice.id }, carol); - const res = await api('/i/notifications-grouped', {}, alice); + const res = await api('i/notifications-grouped', {}, alice); assert.strictEqual(res.status, 200); assert.strictEqual(Array.isArray(res.body), true); diff --git a/packages/backend/test/e2e/note.ts b/packages/backend/test/e2e/note.ts index 2406204f41..11016f58ae 100644 --- a/packages/backend/test/e2e/note.ts +++ b/packages/backend/test/e2e/note.ts @@ -31,7 +31,7 @@ describe('Note', () => { text: 'test', }; - const res = await api('/notes/create', post, alice); + const res = await api('notes/create', post, alice); assert.strictEqual(res.status, 200); assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true); @@ -41,7 +41,7 @@ describe('Note', () => { test('ファイルを添付できる', async () => { const file = await uploadUrl(alice, 'https://raw.githubusercontent.com/misskey-dev/misskey/develop/packages/backend/test/resources/Lenna.jpg'); - const res = await api('/notes/create', { + const res = await api('notes/create', { fileIds: [file.id], }, alice); @@ -53,7 +53,7 @@ describe('Note', () => { test('他人のファイルで怒られる', async () => { const file = await uploadUrl(bob, 'https://raw.githubusercontent.com/misskey-dev/misskey/develop/packages/backend/test/resources/Lenna.jpg'); - const res = await api('/notes/create', { + const res = await api('notes/create', { text: 'test', fileIds: [file.id], }, alice); @@ -64,7 +64,7 @@ describe('Note', () => { }, 1000 * 10); test('存在しないファイルで怒られる', async () => { - const res = await api('/notes/create', { + const res = await api('notes/create', { text: 'test', fileIds: ['000000000000000000000000'], }, alice); @@ -75,7 +75,7 @@ describe('Note', () => { }); test('不正なファイルIDで怒られる', async () => { - const res = await api('/notes/create', { + const res = await api('notes/create', { fileIds: ['kyoppie'], }, alice); assert.strictEqual(res.status, 400); @@ -93,7 +93,7 @@ describe('Note', () => { replyId: bobPost.id, }; - const res = await api('/notes/create', alicePost, alice); + const res = await api('notes/create', alicePost, alice); assert.strictEqual(res.status, 200); assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true); @@ -111,7 +111,7 @@ describe('Note', () => { renoteId: bobPost.id, }; - const res = await api('/notes/create', alicePost, alice); + const res = await api('notes/create', alicePost, alice); assert.strictEqual(res.status, 200); assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true); @@ -129,7 +129,7 @@ describe('Note', () => { renoteId: bobPost.id, }; - const res = await api('/notes/create', alicePost, alice); + const res = await api('notes/create', alicePost, alice); assert.strictEqual(res.status, 200); assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true); @@ -142,7 +142,7 @@ describe('Note', () => { const bobPost = await post(bob, { text: 'test', }); - const res = await api('/notes/create', { + const res = await api('notes/create', { text: ' ', renoteId: bobPost.id, }, alice); @@ -152,7 +152,7 @@ describe('Note', () => { }); test('visibility: followersでrenoteできる', async () => { - const createRes = await api('/notes/create', { + const createRes = await api('notes/create', { text: 'test', visibility: 'followers', }, alice); @@ -160,7 +160,7 @@ describe('Note', () => { assert.strictEqual(createRes.status, 200); const renoteId = createRes.body.createdNote.id; - const renoteRes = await api('/notes/create', { + const renoteRes = await api('notes/create', { visibility: 'followers', renoteId, }, alice); @@ -169,7 +169,7 @@ describe('Note', () => { assert.strictEqual(renoteRes.body.createdNote.renoteId, renoteId); assert.strictEqual(renoteRes.body.createdNote.visibility, 'followers'); - const deleteRes = await api('/notes/delete', { + const deleteRes = await api('notes/delete', { noteId: renoteRes.body.createdNote.id, }, alice); @@ -177,11 +177,11 @@ describe('Note', () => { }); test('visibility: followersなノートに対してフォロワーはリプライできる', async () => { - await api('/following/create', { + await api('following/create', { userId: alice.id, }, bob); - const aliceNote = await api('/notes/create', { + const aliceNote = await api('notes/create', { text: 'direct note to bob', visibility: 'followers', }, alice); @@ -189,7 +189,7 @@ describe('Note', () => { assert.strictEqual(aliceNote.status, 200); const replyId = aliceNote.body.createdNote.id; - const bobReply = await api('/notes/create', { + const bobReply = await api('notes/create', { text: 'reply to alice note', replyId, }, bob); @@ -197,20 +197,20 @@ describe('Note', () => { assert.strictEqual(bobReply.status, 200); assert.strictEqual(bobReply.body.createdNote.replyId, replyId); - await api('/following/delete', { + await api('following/delete', { userId: alice.id, }, bob); }); test('visibility: followersなノートに対してフォロワーでないユーザーがリプライしようとすると怒られる', async () => { - const aliceNote = await api('/notes/create', { + const aliceNote = await api('notes/create', { text: 'direct note to bob', visibility: 'followers', }, alice); assert.strictEqual(aliceNote.status, 200); - const bobReply = await api('/notes/create', { + const bobReply = await api('notes/create', { text: 'reply to alice note', replyId: aliceNote.body.createdNote.id, }, bob); @@ -220,7 +220,7 @@ describe('Note', () => { }); test('visibility: specifiedなノートに対してvisibility: specifiedで返信できる', async () => { - const aliceNote = await api('/notes/create', { + const aliceNote = await api('notes/create', { text: 'direct note to bob', visibility: 'specified', visibleUserIds: [bob.id], @@ -228,7 +228,7 @@ describe('Note', () => { assert.strictEqual(aliceNote.status, 200); - const bobReply = await api('/notes/create', { + const bobReply = await api('notes/create', { text: 'reply to alice note', replyId: aliceNote.body.createdNote.id, visibility: 'specified', @@ -239,7 +239,7 @@ describe('Note', () => { }); test('visibility: specifiedなノートに対してvisibility: follwersで返信しようとすると怒られる', async () => { - const aliceNote = await api('/notes/create', { + const aliceNote = await api('notes/create', { text: 'direct note to bob', visibility: 'specified', visibleUserIds: [bob.id], @@ -247,7 +247,7 @@ describe('Note', () => { assert.strictEqual(aliceNote.status, 200); - const bobReply = await api('/notes/create', { + const bobReply = await api('notes/create', { text: 'reply to alice note with visibility: followers', replyId: aliceNote.body.createdNote.id, visibility: 'followers', @@ -261,7 +261,7 @@ describe('Note', () => { const post = { text: '!'.repeat(MAX_NOTE_TEXT_LENGTH), // 3000文字 }; - const res = await api('/notes/create', post, alice); + const res = await api('notes/create', post, alice); assert.strictEqual(res.status, 200); }); @@ -269,7 +269,7 @@ describe('Note', () => { const post = { text: '!'.repeat(MAX_NOTE_TEXT_LENGTH + 1), // 3001文字 }; - const res = await api('/notes/create', post, alice); + const res = await api('notes/create', post, alice); assert.strictEqual(res.status, 400); }); @@ -278,7 +278,7 @@ describe('Note', () => { text: 'test', replyId: '000000000000000000000000', }; - const res = await api('/notes/create', post, alice); + const res = await api('notes/create', post, alice); assert.strictEqual(res.status, 400); }); @@ -286,7 +286,7 @@ describe('Note', () => { const post = { renoteId: '000000000000000000000000', }; - const res = await api('/notes/create', post, alice); + const res = await api('notes/create', post, alice); assert.strictEqual(res.status, 400); }); @@ -295,7 +295,7 @@ describe('Note', () => { text: 'test', replyId: 'foo', }; - const res = await api('/notes/create', post, alice); + const res = await api('notes/create', post, alice); assert.strictEqual(res.status, 400); }); @@ -303,7 +303,7 @@ describe('Note', () => { const post = { renoteId: 'foo', }; - const res = await api('/notes/create', post, alice); + const res = await api('notes/create', post, alice); assert.strictEqual(res.status, 400); }); @@ -312,7 +312,7 @@ describe('Note', () => { text: '@ghost yo', }; - const res = await api('/notes/create', post, alice); + const res = await api('notes/create', post, alice); assert.strictEqual(res.status, 200); assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true); @@ -324,7 +324,7 @@ describe('Note', () => { text: '@bob @bob @bob yo', }; - const res = await api('/notes/create', post, alice); + const res = await api('notes/create', post, alice); assert.strictEqual(res.status, 200); assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true); @@ -337,25 +337,25 @@ describe('Note', () => { describe('添付ファイル情報', () => { test('ファイルを添付した場合、投稿成功時にファイル情報入りのレスポンスが帰ってくる', async () => { const file = await uploadFile(alice); - const res = await api('/notes/create', { - fileIds: [file.body.id], + const res = await api('notes/create', { + fileIds: [file.body!.id], }, alice); assert.strictEqual(res.status, 200); assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true); assert.strictEqual(res.body.createdNote.files.length, 1); - assert.strictEqual(res.body.createdNote.files[0].id, file.body.id); + assert.strictEqual(res.body.createdNote.files[0].id, file.body!.id); }); test('ファイルを添付した場合、タイムラインでファイル情報入りのレスポンスが帰ってくる', async () => { const file = await uploadFile(alice); - const createdNote = await api('/notes/create', { - fileIds: [file.body.id], + const createdNote = await api('notes/create', { + fileIds: [file.body!.id], }, alice); assert.strictEqual(createdNote.status, 200); - const res = await api('/notes', { + const res = await api('notes', { withFiles: true, }, alice); @@ -364,23 +364,23 @@ describe('Note', () => { const myNote = res.body.find((note: { id: string; files: { id: string }[] }) => note.id === createdNote.body.createdNote.id); assert.notEqual(myNote, null); assert.strictEqual(myNote.files.length, 1); - assert.strictEqual(myNote.files[0].id, file.body.id); + assert.strictEqual(myNote.files[0].id, file.body!.id); }); test('ファイルが添付されたノートをリノートした場合、タイムラインでファイル情報入りのレスポンスが帰ってくる', async () => { const file = await uploadFile(alice); - const createdNote = await api('/notes/create', { - fileIds: [file.body.id], + const createdNote = await api('notes/create', { + fileIds: [file.body!.id], }, alice); assert.strictEqual(createdNote.status, 200); - const renoted = await api('/notes/create', { + const renoted = await api('notes/create', { renoteId: createdNote.body.createdNote.id, }, alice); assert.strictEqual(renoted.status, 200); - const res = await api('/notes', { + const res = await api('notes', { renote: true, }, alice); @@ -389,24 +389,24 @@ describe('Note', () => { const myNote = res.body.find((note: { id: string }) => note.id === renoted.body.createdNote.id); assert.notEqual(myNote, null); assert.strictEqual(myNote.renote.files.length, 1); - assert.strictEqual(myNote.renote.files[0].id, file.body.id); + assert.strictEqual(myNote.renote.files[0].id, file.body!.id); }); test('ファイルが添付されたノートに返信した場合、タイムラインでファイル情報入りのレスポンスが帰ってくる', async () => { const file = await uploadFile(alice); - const createdNote = await api('/notes/create', { - fileIds: [file.body.id], + const createdNote = await api('notes/create', { + fileIds: [file.body!.id], }, alice); assert.strictEqual(createdNote.status, 200); - const reply = await api('/notes/create', { + const reply = await api('notes/create', { replyId: createdNote.body.createdNote.id, text: 'this is reply', }, alice); assert.strictEqual(reply.status, 200); - const res = await api('/notes', { + const res = await api('notes', { reply: true, }, alice); @@ -415,29 +415,29 @@ describe('Note', () => { const myNote = res.body.find((note: { id: string }) => note.id === reply.body.createdNote.id); assert.notEqual(myNote, null); assert.strictEqual(myNote.reply.files.length, 1); - assert.strictEqual(myNote.reply.files[0].id, file.body.id); + assert.strictEqual(myNote.reply.files[0].id, file.body!.id); }); test('ファイルが添付されたノートへの返信をリノートした場合、タイムラインでファイル情報入りのレスポンスが帰ってくる', async () => { const file = await uploadFile(alice); - const createdNote = await api('/notes/create', { - fileIds: [file.body.id], + const createdNote = await api('notes/create', { + fileIds: [file.body!.id], }, alice); assert.strictEqual(createdNote.status, 200); - const reply = await api('/notes/create', { + const reply = await api('notes/create', { replyId: createdNote.body.createdNote.id, text: 'this is reply', }, alice); assert.strictEqual(reply.status, 200); - const renoted = await api('/notes/create', { + const renoted = await api('notes/create', { renoteId: reply.body.createdNote.id, }, alice); assert.strictEqual(renoted.status, 200); - const res = await api('/notes', { + const res = await api('notes', { renote: true, }, alice); @@ -446,7 +446,7 @@ describe('Note', () => { const myNote = res.body.find((note: { id: string }) => note.id === renoted.body.createdNote.id); assert.notEqual(myNote, null); assert.strictEqual(myNote.renote.reply.files.length, 1); - assert.strictEqual(myNote.renote.reply.files[0].id, file.body.id); + assert.strictEqual(myNote.renote.reply.files[0].id, file.body!.id); }); test('NSFWが強制されている場合変更できない', async () => { @@ -472,7 +472,7 @@ describe('Note', () => { priority: 0, value: true, }, - }, + } as any, }, alice); assert.strictEqual(res.status, 200); @@ -483,15 +483,15 @@ describe('Note', () => { }, alice); assert.strictEqual(assign.status, 204); - assert.strictEqual(file.body.isSensitive, false); + assert.strictEqual(file.body!.isSensitive, false); const nsfwfile = await uploadFile(alice); assert.strictEqual(nsfwfile.status, 200); - assert.strictEqual(nsfwfile.body.isSensitive, true); + assert.strictEqual(nsfwfile.body!.isSensitive, true); const liftnsfw = await api('drive/files/update', { - fileId: nsfwfile.body.id, + fileId: nsfwfile.body!.id, isSensitive: false, }, alice); @@ -499,7 +499,7 @@ describe('Note', () => { assert.strictEqual(liftnsfw.body.error.code, 'RESTRICTED_BY_ROLE'); const oldaddnsfw = await api('drive/files/update', { - fileId: file.body.id, + fileId: file.body!.id, isSensitive: true, }, alice); @@ -518,7 +518,7 @@ describe('Note', () => { describe('notes/create', () => { test('投票を添付できる', async () => { - const res = await api('/notes/create', { + const res = await api('notes/create', { text: 'test', poll: { choices: ['foo', 'bar'], @@ -531,14 +531,15 @@ describe('Note', () => { }); test('投票の選択肢が無くて怒られる', async () => { - const res = await api('/notes/create', { + const res = await api('notes/create', { + // @ts-expect-error poll must not be empty poll: {}, }, alice); assert.strictEqual(res.status, 400); }); test('投票の選択肢が無くて怒られる (空の配列)', async () => { - const res = await api('/notes/create', { + const res = await api('notes/create', { poll: { choices: [], }, @@ -547,7 +548,7 @@ describe('Note', () => { }); test('投票の選択肢が1つで怒られる', async () => { - const res = await api('/notes/create', { + const res = await api('notes/create', { poll: { choices: ['Strawberry Pasta'], }, @@ -556,14 +557,14 @@ describe('Note', () => { }); test('投票できる', async () => { - const { body } = await api('/notes/create', { + const { body } = await api('notes/create', { text: 'test', poll: { choices: ['sakura', 'izumi', 'ako'], }, }, alice); - const res = await api('/notes/polls/vote', { + const res = await api('notes/polls/vote', { noteId: body.createdNote.id, choice: 1, }, alice); @@ -572,19 +573,19 @@ describe('Note', () => { }); test('複数投票できない', async () => { - const { body } = await api('/notes/create', { + const { body } = await api('notes/create', { text: 'test', poll: { choices: ['sakura', 'izumi', 'ako'], }, }, alice); - await api('/notes/polls/vote', { + await api('notes/polls/vote', { noteId: body.createdNote.id, choice: 0, }, alice); - const res = await api('/notes/polls/vote', { + const res = await api('notes/polls/vote', { noteId: body.createdNote.id, choice: 2, }, alice); @@ -593,7 +594,7 @@ describe('Note', () => { }); test('許可されている場合は複数投票できる', async () => { - const { body } = await api('/notes/create', { + const { body } = await api('notes/create', { text: 'test', poll: { choices: ['sakura', 'izumi', 'ako'], @@ -601,17 +602,17 @@ describe('Note', () => { }, }, alice); - await api('/notes/polls/vote', { + await api('notes/polls/vote', { noteId: body.createdNote.id, choice: 0, }, alice); - await api('/notes/polls/vote', { + await api('notes/polls/vote', { noteId: body.createdNote.id, choice: 1, }, alice); - const res = await api('/notes/polls/vote', { + const res = await api('notes/polls/vote', { noteId: body.createdNote.id, choice: 2, }, alice); @@ -620,7 +621,7 @@ describe('Note', () => { }); test('締め切られている場合は投票できない', async () => { - const { body } = await api('/notes/create', { + const { body } = await api('notes/create', { text: 'test', poll: { choices: ['sakura', 'izumi', 'ako'], @@ -630,7 +631,7 @@ describe('Note', () => { await new Promise(x => setTimeout(x, 2)); - const res = await api('/notes/polls/vote', { + const res = await api('notes/polls/vote', { noteId: body.createdNote.id, choice: 1, }, alice); @@ -649,7 +650,7 @@ describe('Note', () => { await new Promise(x => setTimeout(x, 2)); - const note1 = await api('/notes/create', { + const note1 = await api('notes/create', { text: 'hogetesthuge', }, alice); @@ -666,7 +667,7 @@ describe('Note', () => { assert.strictEqual(sensitive.status, 204); - const note2 = await api('/notes/create', { + const note2 = await api('notes/create', { text: 'hogetesthuge', }, alice); @@ -683,7 +684,7 @@ describe('Note', () => { assert.strictEqual(sensitive.status, 204); - const note2 = await api('/notes/create', { + const note2 = await api('notes/create', { text: 'hogeTesthuge', }, alice); @@ -702,7 +703,7 @@ describe('Note', () => { await new Promise(x => setTimeout(x, 2)); - const note1 = await api('/notes/create', { + const note1 = await api('notes/create', { text: 'hogetesthuge', }, alice); @@ -719,7 +720,7 @@ describe('Note', () => { assert.strictEqual(prohibited.status, 204); - const note2 = await api('/notes/create', { + const note2 = await api('notes/create', { text: 'hogetesthuge', }, alice); @@ -736,7 +737,7 @@ describe('Note', () => { assert.strictEqual(prohibited.status, 204); - const note2 = await api('/notes/create', { + const note2 = await api('notes/create', { text: 'hogeTesthuge', }, alice); @@ -755,7 +756,7 @@ describe('Note', () => { await new Promise(x => setTimeout(x, 2)); - const note1 = await api('/notes/create', { + const note1 = await api('notes/create', { text: 'hogetesthuge', }, tom); @@ -783,7 +784,7 @@ describe('Note', () => { priority: 1, value: 0, }, - }, + } as any, }, alice); assert.strictEqual(res.status, 200); @@ -799,7 +800,7 @@ describe('Note', () => { await new Promise(x => setTimeout(x, 2)); - const note = await api('/notes/create', { + const note = await api('notes/create', { text: '@bob potentially annoying text', }, alice); @@ -837,7 +838,7 @@ describe('Note', () => { priority: 1, value: 0, }, - }, + } as any, }, alice); assert.strictEqual(res.status, 200); @@ -853,10 +854,10 @@ describe('Note', () => { await new Promise(x => setTimeout(x, 2)); - const note = await api('/notes/create', { + const note = await api('notes/create', { text: 'potentially annoying text', visibility: 'specified', - visibleUserIds: [ bob.id ], + visibleUserIds: [bob.id], }, alice); assert.strictEqual(note.status, 400); @@ -893,7 +894,7 @@ describe('Note', () => { priority: 1, value: 1, }, - }, + } as any, }, alice); assert.strictEqual(res.status, 200); @@ -909,10 +910,10 @@ describe('Note', () => { await new Promise(x => setTimeout(x, 2)); - const note = await api('/notes/create', { + const note = await api('notes/create', { text: '@bob potentially annoying text', visibility: 'specified', - visibleUserIds: [ bob.id ], + visibleUserIds: [bob.id], }, alice); assert.strictEqual(note.status, 200); diff --git a/packages/backend/test/e2e/renote-mute.ts b/packages/backend/test/e2e/renote-mute.ts index 403de0cb8d..9826068e48 100644 --- a/packages/backend/test/e2e/renote-mute.ts +++ b/packages/backend/test/e2e/renote-mute.ts @@ -22,7 +22,7 @@ describe('Renote Mute', () => { }, 1000 * 60 * 2); test('ミュート作成', async () => { - const res = await api('/renote-mute/create', { + const res = await api('renote-mute/create', { userId: carol.id, }, alice); @@ -37,7 +37,7 @@ describe('Renote Mute', () => { // redisに追加されるのを待つ await sleep(100); - const res = await api('/notes/local-timeline', {}, alice); + const res = await api('notes/local-timeline', {}, alice); assert.strictEqual(res.status, 200); assert.strictEqual(Array.isArray(res.body), true); @@ -54,7 +54,7 @@ describe('Renote Mute', () => { // redisに追加されるのを待つ await sleep(100); - const res = await api('/notes/local-timeline', {}, alice); + const res = await api('notes/local-timeline', {}, alice); assert.strictEqual(res.status, 200); assert.strictEqual(Array.isArray(res.body), true); diff --git a/packages/backend/test/e2e/streaming.ts b/packages/backend/test/e2e/streaming.ts index 57ce73ba60..edb930617f 100644 --- a/packages/backend/test/e2e/streaming.ts +++ b/packages/backend/test/e2e/streaming.ts @@ -601,7 +601,7 @@ describe('Streaming', () => { // #10443 test('ミュートしているサーバのノートがリストTLに流れない', async () => { - await api('/i/update', { + await api('i/update', { mutedInstances: ['example.com'], }, chitose); @@ -618,7 +618,7 @@ describe('Streaming', () => { // #10443 test('ミュートしているサーバのノートに対するリプライがリストTLに流れない', async () => { - await api('/i/update', { + await api('i/update', { mutedInstances: ['example.com'], }, chitose); @@ -635,7 +635,7 @@ describe('Streaming', () => { // #10443 test('ミュートしているサーバのノートに対するリノートがリストTLに流れない', async () => { - await api('/i/update', { + await api('i/update', { mutedInstances: ['example.com'], }, chitose); diff --git a/packages/backend/test/e2e/thread-mute.ts b/packages/backend/test/e2e/thread-mute.ts index b4570cdef1..53bb6eb765 100644 --- a/packages/backend/test/e2e/thread-mute.ts +++ b/packages/backend/test/e2e/thread-mute.ts @@ -24,12 +24,12 @@ describe('Note thread mute', () => { const bobNote = await post(bob, { text: '@alice @carol root note' }); const aliceReply = await post(alice, { replyId: bobNote.id, text: '@bob @carol child note' }); - await api('/notes/thread-muting/create', { noteId: bobNote.id }, alice); + await api('notes/thread-muting/create', { noteId: bobNote.id }, alice); const carolReply = await post(carol, { replyId: bobNote.id, text: '@bob @alice child note' }); const carolReplyWithoutMention = await post(carol, { replyId: aliceReply.id, text: 'child note' }); - const res = await api('/notes/mentions', {}, alice); + const res = await api('notes/mentions', {}, alice); assert.strictEqual(res.status, 200); assert.strictEqual(Array.isArray(res.body), true); @@ -40,15 +40,15 @@ describe('Note thread mute', () => { test('ミュートしているスレッドからメンションされても、hasUnreadMentions が true にならない', async () => { // 状態リセット - await api('/i/read-all-unread-notes', {}, alice); + await api('i/read-all-unread-notes', {}, alice); const bobNote = await post(bob, { text: '@alice @carol root note' }); - await api('/notes/thread-muting/create', { noteId: bobNote.id }, alice); + await api('notes/thread-muting/create', { noteId: bobNote.id }, alice); const carolReply = await post(carol, { replyId: bobNote.id, text: '@bob @alice child note' }); - const res = await api('/i', {}, alice); + const res = await api('i', {}, alice); assert.strictEqual(res.status, 200); assert.strictEqual(res.body.hasUnreadMentions, false); @@ -56,11 +56,11 @@ describe('Note thread mute', () => { test('ミュートしているスレッドからメンションされても、ストリームに unreadMention イベントが流れてこない', () => new Promise<void>(async done => { // 状態リセット - await api('/i/read-all-unread-notes', {}, alice); + await api('i/read-all-unread-notes', {}, alice); const bobNote = await post(bob, { text: '@alice @carol root note' }); - await api('/notes/thread-muting/create', { noteId: bobNote.id }, alice); + await api('notes/thread-muting/create', { noteId: bobNote.id }, alice); let fired = false; @@ -84,12 +84,12 @@ describe('Note thread mute', () => { const bobNote = await post(bob, { text: '@alice @carol root note' }); const aliceReply = await post(alice, { replyId: bobNote.id, text: '@bob @carol child note' }); - await api('/notes/thread-muting/create', { noteId: bobNote.id }, alice); + await api('notes/thread-muting/create', { noteId: bobNote.id }, alice); const carolReply = await post(carol, { replyId: bobNote.id, text: '@bob @alice child note' }); const carolReplyWithoutMention = await post(carol, { replyId: aliceReply.id, text: 'child note' }); - const res = await api('/i/notifications', {}, alice); + const res = await api('i/notifications', {}, alice); assert.strictEqual(res.status, 200); assert.strictEqual(Array.isArray(res.body), true); diff --git a/packages/backend/test/e2e/timelines.ts b/packages/backend/test/e2e/timelines.ts index 0e71d707dd..5487292afc 100644 --- a/packages/backend/test/e2e/timelines.ts +++ b/packages/backend/test/e2e/timelines.ts @@ -26,7 +26,7 @@ describe('Timelines', () => { await waitForPushToTl(); - const res = await api('/notes/timeline', { limit: 100 }, alice); + const res = await api('notes/timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true); assert.strictEqual(res.body.find((note: any) => note.id === aliceNote.id).text, 'hi'); @@ -35,14 +35,14 @@ describe('Timelines', () => { test.concurrent('フォローしているユーザーのノートが含まれる', async () => { const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); - await api('/following/create', { userId: bob.id }, alice); + await api('following/create', { userId: bob.id }, alice); await sleep(1000); const bobNote = await post(bob, { text: 'hi' }); const carolNote = await post(carol, { text: 'hi' }); await waitForPushToTl(); - const res = await api('/notes/timeline', { limit: 100 }, alice); + const res = await api('notes/timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false); @@ -51,14 +51,14 @@ describe('Timelines', () => { test.concurrent('フォローしているユーザーの visibility: followers なノートが含まれる', async () => { const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); - await api('/following/create', { userId: bob.id }, alice); + await api('following/create', { userId: bob.id }, alice); await sleep(1000); const bobNote = await post(bob, { text: 'hi', visibility: 'followers' }); const carolNote = await post(carol, { text: 'hi' }); await waitForPushToTl(); - const res = await api('/notes/timeline', { limit: 100 }, alice); + const res = await api('notes/timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); assert.strictEqual(res.body.find((note: any) => note.id === bobNote.id).text, 'hi'); @@ -68,14 +68,14 @@ describe('Timelines', () => { test.concurrent('withReplies: false でフォローしているユーザーの他人への返信が含まれない', async () => { const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); - await api('/following/create', { userId: bob.id }, alice); + await api('following/create', { userId: bob.id }, alice); await sleep(1000); const carolNote = await post(carol, { text: 'hi' }); const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id }); await waitForPushToTl(); - const res = await api('/notes/timeline', { limit: 100 }, alice); + const res = await api('notes/timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false); @@ -84,15 +84,15 @@ describe('Timelines', () => { test.concurrent('withReplies: true でフォローしているユーザーの他人への返信が含まれる', async () => { const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); - await api('/following/create', { userId: bob.id }, alice); - await api('/following/update', { userId: bob.id, withReplies: true }, alice); + await api('following/create', { userId: bob.id }, alice); + await api('following/update', { userId: bob.id, withReplies: true }, alice); await sleep(1000); const carolNote = await post(carol, { text: 'hi' }); const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id }); await waitForPushToTl(); - const res = await api('/notes/timeline', { limit: 100 }, alice); + const res = await api('notes/timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false); @@ -101,15 +101,15 @@ describe('Timelines', () => { test.concurrent('withReplies: true でフォローしているユーザーの他人へのDM返信が含まれない', async () => { const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); - await api('/following/create', { userId: bob.id }, alice); - await api('/following/update', { userId: bob.id, withReplies: true }, alice); + await api('following/create', { userId: bob.id }, alice); + await api('following/update', { userId: bob.id, withReplies: true }, alice); await sleep(1000); const carolNote = await post(carol, { text: 'hi' }); const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id, visibility: 'specified', visibleUserIds: [carolNote.id] }); await waitForPushToTl(); - const res = await api('/notes/timeline', { limit: 100 }, alice); + const res = await api('notes/timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false); @@ -118,15 +118,15 @@ describe('Timelines', () => { test.concurrent('withReplies: true でフォローしているユーザーの他人の visibility: followers な投稿への返信が含まれない', async () => { const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); - await api('/following/create', { userId: bob.id }, alice); - await api('/following/update', { userId: bob.id, withReplies: true }, alice); + await api('following/create', { userId: bob.id }, alice); + await api('following/update', { userId: bob.id, withReplies: true }, alice); await sleep(1000); const carolNote = await post(carol, { text: 'hi', visibility: 'followers' }); const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id }); await waitForPushToTl(); - const res = await api('/notes/timeline', { limit: 100 }, alice); + const res = await api('notes/timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false); @@ -135,17 +135,17 @@ describe('Timelines', () => { test.concurrent('withReplies: true でフォローしているユーザーの行った別のフォローしているユーザーの visibility: followers な投稿への返信が含まれる', async () => { const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); - await api('/following/create', { userId: bob.id }, alice); - await api('/following/create', { userId: carol.id }, alice); - await api('/following/create', { userId: carol.id }, bob); - await api('/following/update', { userId: bob.id, withReplies: true }, alice); + await api('following/create', { userId: bob.id }, alice); + await api('following/create', { userId: carol.id }, alice); + await api('following/create', { userId: carol.id }, bob); + await api('following/update', { userId: bob.id, withReplies: true }, alice); await sleep(1000); const carolNote = await post(carol, { text: 'hi', visibility: 'followers' }); const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id }); await waitForPushToTl(); - const res = await api('/notes/timeline', { limit: 100 }, alice); + const res = await api('notes/timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), true); @@ -155,16 +155,16 @@ describe('Timelines', () => { test.concurrent('withReplies: true でフォローしているユーザーの行った別のフォローしているユーザーの投稿への visibility: specified な返信が含まれない', async () => { const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); - await api('/following/create', { userId: bob.id }, alice); - await api('/following/create', { userId: carol.id }, alice); - await api('/following/update', { userId: bob.id, withReplies: true }, alice); + await api('following/create', { userId: bob.id }, alice); + await api('following/create', { userId: carol.id }, alice); + await api('following/update', { userId: bob.id, withReplies: true }, alice); await sleep(1000); const carolNote = await post(carol, { text: 'hi' }); const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id, visibility: 'specified', visibleUserIds: [carolNote.id] }); await waitForPushToTl(); - const res = await api('/notes/timeline', { limit: 100 }, alice); + const res = await api('notes/timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), true); @@ -173,14 +173,14 @@ describe('Timelines', () => { test.concurrent('withReplies: false でフォローしているユーザーのそのユーザー自身への返信が含まれる', async () => { const [alice, bob] = await Promise.all([signup(), signup()]); - await api('/following/create', { userId: bob.id }, alice); + await api('following/create', { userId: bob.id }, alice); await sleep(1000); const bobNote1 = await post(bob, { text: 'hi' }); const bobNote2 = await post(bob, { text: 'hi', replyId: bobNote1.id }); await waitForPushToTl(); - const res = await api('/notes/timeline', { limit: 100 }, alice); + const res = await api('notes/timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote1.id), true); assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), true); @@ -189,14 +189,14 @@ describe('Timelines', () => { test.concurrent('withReplies: false でフォローしているユーザーからの自分への返信が含まれる', async () => { const [alice, bob] = await Promise.all([signup(), signup()]); - await api('/following/create', { userId: bob.id }, alice); + await api('following/create', { userId: bob.id }, alice); await sleep(1000); const aliceNote = await post(alice, { text: 'hi' }); const bobNote = await post(bob, { text: 'hi', replyId: aliceNote.id }); await waitForPushToTl(); - const res = await api('/notes/timeline', { limit: 100 }, alice); + const res = await api('notes/timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); @@ -210,7 +210,7 @@ describe('Timelines', () => { await waitForPushToTl(); - const res = await api('/notes/timeline', { limit: 100 }, alice); + const res = await api('notes/timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true); @@ -219,14 +219,14 @@ describe('Timelines', () => { test.concurrent('フォローしているユーザーの他人の投稿のリノートが含まれる', async () => { const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); - await api('/following/create', { userId: bob.id }, alice); + await api('following/create', { userId: bob.id }, alice); await sleep(1000); const carolNote = await post(carol, { text: 'hi' }); const bobNote = await post(bob, { renoteId: carolNote.id }); await waitForPushToTl(); - const res = await api('/notes/timeline', { limit: 100 }, alice); + const res = await api('notes/timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false); @@ -235,14 +235,14 @@ describe('Timelines', () => { test.concurrent('[withRenotes: false] フォローしているユーザーの他人の投稿のリノートが含まれない', async () => { const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); - await api('/following/create', { userId: bob.id }, alice); + await api('following/create', { userId: bob.id }, alice); await sleep(1000); const carolNote = await post(carol, { text: 'hi' }); const bobNote = await post(bob, { renoteId: carolNote.id }); await waitForPushToTl(); - const res = await api('/notes/timeline', { + const res = await api('notes/timeline', { withRenotes: false, }, alice); @@ -253,14 +253,14 @@ describe('Timelines', () => { test.concurrent('[withRenotes: false] フォローしているユーザーの他人の投稿の引用が含まれる', async () => { const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); - await api('/following/create', { userId: bob.id }, alice); + await api('following/create', { userId: bob.id }, alice); await sleep(1000); const carolNote = await post(carol, { text: 'hi' }); const bobNote = await post(bob, { text: 'hi', renoteId: carolNote.id }); await waitForPushToTl(); - const res = await api('/notes/timeline', { + const res = await api('notes/timeline', { withRenotes: false, }, alice); @@ -271,13 +271,13 @@ describe('Timelines', () => { test.concurrent('フォローしているユーザーの他人への visibility: specified なノートが含まれない', async () => { const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); - await api('/following/create', { userId: bob.id }, alice); + await api('following/create', { userId: bob.id }, alice); await sleep(1000); const bobNote = await post(bob, { text: 'hi', visibility: 'specified', visibleUserIds: [carol.id] }); await waitForPushToTl(); - const res = await api('/notes/timeline', { limit: 100 }, alice); + const res = await api('notes/timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); }); @@ -285,15 +285,15 @@ describe('Timelines', () => { test.concurrent('フォローしているユーザーが行ったミュートしているユーザーのリノートが含まれない', async () => { const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); - await api('/following/create', { userId: bob.id }, alice); - await api('/mute/create', { userId: carol.id }, alice); + await api('following/create', { userId: bob.id }, alice); + await api('mute/create', { userId: carol.id }, alice); await sleep(1000); const carolNote = await post(carol, { text: 'hi' }); const bobNote = await post(bob, { text: 'hi', renoteId: carolNote.id }); await waitForPushToTl(); - const res = await api('/notes/timeline', { limit: 100 }, alice); + const res = await api('notes/timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false); @@ -302,16 +302,16 @@ describe('Timelines', () => { test.concurrent('withReplies: true でフォローしているユーザーが行ったミュートしているユーザーの投稿への返信が含まれない', async () => { const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); - await api('/following/create', { userId: bob.id }, alice); - await api('/following/update', { userId: bob.id, withReplies: true }, alice); - await api('/mute/create', { userId: carol.id }, alice); + await api('following/create', { userId: bob.id }, alice); + await api('following/update', { userId: bob.id, withReplies: true }, alice); + await api('mute/create', { userId: carol.id }, alice); await sleep(1000); const carolNote = await post(carol, { text: 'hi' }); const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id }); await waitForPushToTl(); - const res = await api('/notes/timeline', { limit: 100 }, alice); + const res = await api('notes/timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false); @@ -321,13 +321,13 @@ describe('Timelines', () => { const [alice, bob] = await Promise.all([signup(), signup({ host: genHost() })]); await sendEnvUpdateRequest({ key: 'FORCE_FOLLOW_REMOTE_USER_FOR_TESTING', value: 'true' }); - await api('/following/create', { userId: bob.id }, alice); + await api('following/create', { userId: bob.id }, alice); const bobNote = await post(bob, { text: 'hi' }); await waitForPushToTl(); - const res = await api('/notes/timeline', { limit: 100 }, alice); + const res = await api('notes/timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); }); @@ -336,13 +336,13 @@ describe('Timelines', () => { const [alice, bob] = await Promise.all([signup(), signup({ host: genHost() })]); await sendEnvUpdateRequest({ key: 'FORCE_FOLLOW_REMOTE_USER_FOR_TESTING', value: 'true' }); - await api('/following/create', { userId: bob.id }, alice); + await api('following/create', { userId: bob.id }, alice); const bobNote = await post(bob, { text: 'hi', visibility: 'home' }); await waitForPushToTl(); - const res = await api('/notes/timeline', { limit: 100 }, alice); + const res = await api('notes/timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); }); @@ -350,7 +350,7 @@ describe('Timelines', () => { test.concurrent('[withFiles: true] フォローしているユーザーのファイル付きノートのみ含まれる', async () => { const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); - await api('/following/create', { userId: bob.id }, alice); + await api('following/create', { userId: bob.id }, alice); await sleep(1000); const [bobFile, carolFile] = await Promise.all([ uploadUrl(bob, 'https://raw.githubusercontent.com/misskey-dev/assets/main/public/icon.png'), @@ -363,7 +363,7 @@ describe('Timelines', () => { await waitForPushToTl(); - const res = await api('/notes/timeline', { limit: 100, withFiles: true }, alice); + const res = await api('notes/timeline', { limit: 100, withFiles: true }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote1.id), false); assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), true); @@ -374,14 +374,14 @@ describe('Timelines', () => { test.concurrent('フォローしているユーザーのチャンネル投稿が含まれない', async () => { const [alice, bob] = await Promise.all([signup(), signup()]); - const channel = await api('/channels/create', { name: 'channel' }, bob).then(x => x.body); - await api('/following/create', { userId: bob.id }, alice); + const channel = await api('channels/create', { name: 'channel' }, bob).then(x => x.body); + await api('following/create', { userId: bob.id }, alice); await sleep(1000); const bobNote = await post(bob, { text: 'hi', channelId: channel.id }); await waitForPushToTl(); - const res = await api('/notes/timeline', { limit: 100 }, alice); + const res = await api('notes/timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); }); @@ -393,7 +393,7 @@ describe('Timelines', () => { await waitForPushToTl(); - const res = await api('/notes/timeline', { limit: 100 }, alice); + const res = await api('notes/timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true); assert.strictEqual(res.body.find((note: any) => note.id === aliceNote.id).text, 'hi'); @@ -402,13 +402,13 @@ describe('Timelines', () => { test.concurrent('フォローしているユーザーの自身を visibleUserIds に指定した visibility: specified なノートが含まれる', async () => { const [alice, bob] = await Promise.all([signup(), signup()]); - await api('/following/create', { userId: bob.id }, alice); + await api('following/create', { userId: bob.id }, alice); await sleep(1000); const bobNote = await post(bob, { text: 'hi', visibility: 'specified', visibleUserIds: [alice.id] }); await waitForPushToTl(); - const res = await api('/notes/timeline', { limit: 100 }, alice); + const res = await api('notes/timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); assert.strictEqual(res.body.find((note: any) => note.id === bobNote.id).text, 'hi'); @@ -421,7 +421,7 @@ describe('Timelines', () => { await waitForPushToTl(); - const res = await api('/notes/timeline', { limit: 100 }, alice); + const res = await api('notes/timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); }); @@ -429,13 +429,13 @@ describe('Timelines', () => { test.concurrent('フォローしているユーザーの自身を visibleUserIds に指定していない visibility: specified なノートが含まれない', async () => { const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); - await api('/following/create', { userId: bob.id }, alice); + await api('following/create', { userId: bob.id }, alice); await sleep(1000); const bobNote = await post(bob, { text: 'hi', visibility: 'specified', visibleUserIds: [carol.id] }); await waitForPushToTl(); - const res = await api('/notes/timeline', { limit: 100 }, alice); + const res = await api('notes/timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); }); @@ -448,7 +448,7 @@ describe('Timelines', () => { await waitForPushToTl(); - const res = await api('/notes/timeline', { limit: 100 }, alice); + const res = await api('notes/timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true); assert.strictEqual(res.body.find((note: any) => note.id === aliceNote.id).text, 'ok'); @@ -463,7 +463,7 @@ describe('Timelines', () => { await waitForPushToTl(); - const res = await api('/notes/timeline', { limit: 100 }, alice); + const res = await api('notes/timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); assert.strictEqual(res.body.find((note: any) => note.id === bobNote.id).text, 'ok'); @@ -479,7 +479,7 @@ describe('Timelines', () => { await waitForPushToTl(); - const res = await api('/notes/timeline', { limit: 100 }, alice); + const res = await api('notes/timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); }); @@ -494,7 +494,7 @@ describe('Timelines', () => { await waitForPushToTl(); - const res = await api('/notes/local-timeline', { limit: 100 }, alice); + const res = await api('notes/local-timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false); @@ -508,7 +508,7 @@ describe('Timelines', () => { await waitForPushToTl(); - const res = await api('/notes/local-timeline', { limit: 100 }, alice); + const res = await api('notes/local-timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), true); @@ -522,7 +522,7 @@ describe('Timelines', () => { await waitForPushToTl(); - const res = await api('/notes/local-timeline', { limit: 100 }, alice); + const res = await api('notes/local-timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote1.id), true); assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), true); @@ -531,12 +531,12 @@ describe('Timelines', () => { test.concurrent('チャンネル投稿が含まれない', async () => { const [alice, bob] = await Promise.all([signup(), signup()]); - const channel = await api('/channels/create', { name: 'channel' }, bob).then(x => x.body); + const channel = await api('channels/create', { name: 'channel' }, bob).then(x => x.body); const bobNote = await post(bob, { text: 'hi', channelId: channel.id }); await waitForPushToTl(); - const res = await api('/notes/local-timeline', { limit: 100 }, alice); + const res = await api('notes/local-timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); }); @@ -548,7 +548,7 @@ describe('Timelines', () => { await waitForPushToTl(); - const res = await api('/notes/local-timeline', { limit: 100 }, alice); + const res = await api('notes/local-timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); }); @@ -557,14 +557,14 @@ describe('Timelines', () => { test.concurrent('フォローしているユーザーの visibility: home なノートが含まれない', async () => { const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); - await api('/following/create', { userId: carol.id }, alice); + await api('following/create', { userId: carol.id }, alice); await sleep(1000); const carolNote = await post(carol, { text: 'hi', visibility: 'home' }); const bobNote = await post(bob, { text: 'hi' }); await waitForPushToTl(); - const res = await api('/notes/local-timeline', { limit: 100 }, alice); + const res = await api('notes/local-timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false); @@ -573,14 +573,14 @@ describe('Timelines', () => { test.concurrent('ミュートしているユーザーのノートが含まれない', async () => { const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); - await api('/mute/create', { userId: carol.id }, alice); + await api('mute/create', { userId: carol.id }, alice); await sleep(1000); const carolNote = await post(carol, { text: 'hi' }); const bobNote = await post(bob, { text: 'hi' }); await waitForPushToTl(); - const res = await api('/notes/local-timeline', { limit: 100 }, alice); + const res = await api('notes/local-timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false); @@ -589,15 +589,15 @@ describe('Timelines', () => { test.concurrent('フォローしているユーザーが行ったミュートしているユーザーのリノートが含まれない', async () => { const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); - await api('/following/create', { userId: bob.id }, alice); - await api('/mute/create', { userId: carol.id }, alice); + await api('following/create', { userId: bob.id }, alice); + await api('mute/create', { userId: carol.id }, alice); await sleep(1000); const carolNote = await post(carol, { text: 'hi' }); const bobNote = await post(bob, { text: 'hi', renoteId: carolNote.id }); await waitForPushToTl(); - const res = await api('/notes/local-timeline', { limit: 100 }, alice); + const res = await api('notes/local-timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false); @@ -606,16 +606,16 @@ describe('Timelines', () => { test.concurrent('withReplies: true でフォローしているユーザーが行ったミュートしているユーザーの投稿への返信が含まれない', async () => { const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); - await api('/following/create', { userId: bob.id }, alice); - await api('/following/update', { userId: bob.id, withReplies: true }, alice); - await api('/mute/create', { userId: carol.id }, alice); + await api('following/create', { userId: bob.id }, alice); + await api('following/update', { userId: bob.id, withReplies: true }, alice); + await api('mute/create', { userId: carol.id }, alice); await sleep(1000); const carolNote = await post(carol, { text: 'hi' }); const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id }); await waitForPushToTl(); - const res = await api('/notes/local-timeline', { limit: 100 }, alice); + const res = await api('notes/local-timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false); @@ -624,14 +624,14 @@ describe('Timelines', () => { test.concurrent('withReplies: false でフォローしているユーザーからの自分への返信が含まれる', async () => { const [alice, bob] = await Promise.all([signup(), signup()]); - await api('/following/create', { userId: bob.id }, alice); + await api('following/create', { userId: bob.id }, alice); await sleep(1000); const aliceNote = await post(alice, { text: 'hi' }); const bobNote = await post(bob, { text: 'hi', replyId: aliceNote.id }); await waitForPushToTl(); - const res = await api('/notes/local-timeline', { limit: 100 }, alice); + const res = await api('notes/local-timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); @@ -645,7 +645,7 @@ describe('Timelines', () => { await waitForPushToTl(); - const res = await api('/notes/local-timeline', { limit: 100, withReplies: true }, alice); + const res = await api('notes/local-timeline', { limit: 100, withReplies: true }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); }); @@ -659,7 +659,7 @@ describe('Timelines', () => { await waitForPushToTl(); - const res = await api('/notes/local-timeline', { limit: 100, withFiles: true }, alice); + const res = await api('notes/local-timeline', { limit: 100, withFiles: true }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote1.id), false); assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), true); @@ -674,7 +674,7 @@ describe('Timelines', () => { await waitForPushToTl(); - const res = await api('/notes/hybrid-timeline', { limit: 100 }, alice); + const res = await api('notes/hybrid-timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); }); @@ -686,7 +686,7 @@ describe('Timelines', () => { await waitForPushToTl(); - const res = await api('/notes/hybrid-timeline', { limit: 100 }, alice); + const res = await api('notes/hybrid-timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); }); @@ -694,13 +694,13 @@ describe('Timelines', () => { test.concurrent('フォローしているローカルユーザーの visibility: home なノートが含まれる', async () => { const [alice, bob] = await Promise.all([signup(), signup()]); - await api('/following/create', { userId: bob.id }, alice); + await api('following/create', { userId: bob.id }, alice); await sleep(1000); const bobNote = await post(bob, { text: 'hi', visibility: 'home' }); await waitForPushToTl(); - const res = await api('/notes/hybrid-timeline', { limit: 100 }, alice); + const res = await api('notes/hybrid-timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); }); @@ -708,14 +708,14 @@ describe('Timelines', () => { test.concurrent('withReplies: false でフォローしているユーザーからの自分への返信が含まれる', async () => { const [alice, bob] = await Promise.all([signup(), signup()]); - await api('/following/create', { userId: bob.id }, alice); + await api('following/create', { userId: bob.id }, alice); await sleep(1000); const aliceNote = await post(alice, { text: 'hi' }); const bobNote = await post(bob, { text: 'hi', replyId: aliceNote.id }); await waitForPushToTl(); - const res = await api('/notes/hybrid-timeline', { limit: 100 }, alice); + const res = await api('notes/hybrid-timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); @@ -729,7 +729,7 @@ describe('Timelines', () => { await waitForPushToTl(); - const res = await api('/notes/hybrid-timeline', { limit: 100 }, alice); + const res = await api('notes/hybrid-timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), true); @@ -742,7 +742,7 @@ describe('Timelines', () => { await waitForPushToTl(); - const res = await api('/notes/local-timeline', { limit: 100 }, alice); + const res = await api('notes/local-timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); }); @@ -751,13 +751,13 @@ describe('Timelines', () => { const [alice, bob] = await Promise.all([signup(), signup({ host: genHost() })]); await sendEnvUpdateRequest({ key: 'FORCE_FOLLOW_REMOTE_USER_FOR_TESTING', value: 'true' }); - await api('/following/create', { userId: bob.id }, alice); + await api('following/create', { userId: bob.id }, alice); const bobNote = await post(bob, { text: 'hi' }); await waitForPushToTl(); - const res = await api('/notes/hybrid-timeline', { limit: 100 }, alice); + const res = await api('notes/hybrid-timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); }); @@ -766,13 +766,13 @@ describe('Timelines', () => { const [alice, bob] = await Promise.all([signup(), signup({ host: genHost() })]); await sendEnvUpdateRequest({ key: 'FORCE_FOLLOW_REMOTE_USER_FOR_TESTING', value: 'true' }); - await api('/following/create', { userId: bob.id }, alice); + await api('following/create', { userId: bob.id }, alice); const bobNote = await post(bob, { text: 'hi', visibility: 'home' }); await waitForPushToTl(); - const res = await api('/notes/hybrid-timeline', { limit: 100 }, alice); + const res = await api('notes/hybrid-timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); }); @@ -785,7 +785,7 @@ describe('Timelines', () => { await waitForPushToTl(); - const res = await api('/notes/hybrid-timeline', { limit: 100, withReplies: true }, alice); + const res = await api('notes/hybrid-timeline', { limit: 100, withReplies: true }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); }); @@ -799,7 +799,7 @@ describe('Timelines', () => { await waitForPushToTl(); - const res = await api('/notes/hybrid-timeline', { limit: 100, withFiles: true }, alice); + const res = await api('notes/hybrid-timeline', { limit: 100, withFiles: true }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote1.id), false); assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), true); @@ -810,14 +810,14 @@ describe('Timelines', () => { test.concurrent('リスインしているフォローしていないユーザーのノートが含まれる', async () => { const [alice, bob] = await Promise.all([signup(), signup()]); - const list = await api('/users/lists/create', { name: 'list' }, alice).then(res => res.body); - await api('/users/lists/push', { listId: list.id, userId: bob.id }, alice); + const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body); + await api('users/lists/push', { listId: list.id, userId: bob.id }, alice); await sleep(1000); const bobNote = await post(bob, { text: 'hi' }); await waitForPushToTl(); - const res = await api('/notes/user-list-timeline', { listId: list.id }, alice); + const res = await api('notes/user-list-timeline', { listId: list.id }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); }); @@ -825,14 +825,14 @@ describe('Timelines', () => { test.concurrent('リスインしているフォローしていないユーザーの visibility: home なノートが含まれる', async () => { const [alice, bob] = await Promise.all([signup(), signup()]); - const list = await api('/users/lists/create', { name: 'list' }, alice).then(res => res.body); - await api('/users/lists/push', { listId: list.id, userId: bob.id }, alice); + const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body); + await api('users/lists/push', { listId: list.id, userId: bob.id }, alice); await sleep(1000); const bobNote = await post(bob, { text: 'hi', visibility: 'home' }); await waitForPushToTl(); - const res = await api('/notes/user-list-timeline', { listId: list.id }, alice); + const res = await api('notes/user-list-timeline', { listId: list.id }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); }); @@ -840,14 +840,14 @@ describe('Timelines', () => { test.concurrent('リスインしているフォローしていないユーザーの visibility: followers なノートが含まれない', async () => { const [alice, bob] = await Promise.all([signup(), signup()]); - const list = await api('/users/lists/create', { name: 'list' }, alice).then(res => res.body); - await api('/users/lists/push', { listId: list.id, userId: bob.id }, alice); + const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body); + await api('users/lists/push', { listId: list.id, userId: bob.id }, alice); await sleep(1000); const bobNote = await post(bob, { text: 'hi', visibility: 'followers' }); await waitForPushToTl(); - const res = await api('/notes/user-list-timeline', { listId: list.id }, alice); + const res = await api('notes/user-list-timeline', { listId: list.id }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); }); @@ -855,15 +855,15 @@ describe('Timelines', () => { test.concurrent('リスインしているフォローしていないユーザーの他人への返信が含まれない', async () => { const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); - const list = await api('/users/lists/create', { name: 'list' }, alice).then(res => res.body); - await api('/users/lists/push', { listId: list.id, userId: bob.id }, alice); + const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body); + await api('users/lists/push', { listId: list.id, userId: bob.id }, alice); await sleep(1000); const carolNote = await post(carol, { text: 'hi' }); const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id }); await waitForPushToTl(); - const res = await api('/notes/user-list-timeline', { listId: list.id }, alice); + const res = await api('notes/user-list-timeline', { listId: list.id }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); }); @@ -871,15 +871,15 @@ describe('Timelines', () => { test.concurrent('リスインしているフォローしていないユーザーのユーザー自身への返信が含まれる', async () => { const [alice, bob] = await Promise.all([signup(), signup()]); - const list = await api('/users/lists/create', { name: 'list' }, alice).then(res => res.body); - await api('/users/lists/push', { listId: list.id, userId: bob.id }, alice); + const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body); + await api('users/lists/push', { listId: list.id, userId: bob.id }, alice); await sleep(1000); const bobNote1 = await post(bob, { text: 'hi' }); const bobNote2 = await post(bob, { text: 'hi', replyId: bobNote1.id }); await waitForPushToTl(); - const res = await api('/notes/user-list-timeline', { listId: list.id }, alice); + const res = await api('notes/user-list-timeline', { listId: list.id }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote1.id), true); assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), true); @@ -888,32 +888,50 @@ describe('Timelines', () => { test.concurrent('withReplies: false でリスインしているフォローしていないユーザーからの自分への返信が含まれる', async () => { const [alice, bob] = await Promise.all([signup(), signup()]); - const list = await api('/users/lists/create', { name: 'list' }, alice).then(res => res.body); - await api('/users/lists/push', { listId: list.id, userId: bob.id }, alice); + const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body); + await api('users/lists/push', { listId: list.id, userId: bob.id }, alice); + await api('users/lists/update-membership', { listId: list.id, userId: bob.id, withReplies: false }, alice); await sleep(1000); const aliceNote = await post(alice, { text: 'hi' }); const bobNote = await post(bob, { text: 'hi', replyId: aliceNote.id }); await waitForPushToTl(); - const res = await api('/notes/user-list-timeline', { listId: list.id, withReplies: false }, alice); + const res = await api('notes/user-list-timeline', { listId: list.id }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); }); + test.concurrent('withReplies: false でリスインしているフォローしていないユーザーの他人への返信が含まれない', async () => { + const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); + + const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body); + await api('users/lists/push', { listId: list.id, userId: bob.id }, alice); + await api('users/lists/update-membership', { listId: list.id, userId: bob.id, withReplies: false }, alice); + await sleep(1000); + const carolNote = await post(carol, { text: 'hi' }); + const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id }); + + await waitForPushToTl(); + + const res = await api('notes/user-list-timeline', { listId: list.id }, alice); + + assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); + }); + test.concurrent('withReplies: true でリスインしているフォローしていないユーザーの他人への返信が含まれる', async () => { const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); - const list = await api('/users/lists/create', { name: 'list' }, alice).then(res => res.body); - await api('/users/lists/push', { listId: list.id, userId: bob.id }, alice); - await api('/users/lists/update-membership', { listId: list.id, userId: bob.id, withReplies: true }, alice); + const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body); + await api('users/lists/push', { listId: list.id, userId: bob.id }, alice); + await api('users/lists/update-membership', { listId: list.id, userId: bob.id, withReplies: true }, alice); await sleep(1000); const carolNote = await post(carol, { text: 'hi' }); const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id }); await waitForPushToTl(); - const res = await api('/notes/user-list-timeline', { listId: list.id }, alice); + const res = await api('notes/user-list-timeline', { listId: list.id }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); }); @@ -921,15 +939,15 @@ describe('Timelines', () => { test.concurrent('リスインしているフォローしているユーザーの visibility: home なノートが含まれる', async () => { const [alice, bob] = await Promise.all([signup(), signup()]); - await api('/following/create', { userId: bob.id }, alice); - const list = await api('/users/lists/create', { name: 'list' }, alice).then(res => res.body); - await api('/users/lists/push', { listId: list.id, userId: bob.id }, alice); + await api('following/create', { userId: bob.id }, alice); + const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body); + await api('users/lists/push', { listId: list.id, userId: bob.id }, alice); await sleep(1000); const bobNote = await post(bob, { text: 'hi', visibility: 'home' }); await waitForPushToTl(); - const res = await api('/notes/user-list-timeline', { listId: list.id }, alice); + const res = await api('notes/user-list-timeline', { listId: list.id }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); }); @@ -937,15 +955,15 @@ describe('Timelines', () => { test.concurrent('リスインしているフォローしているユーザーの visibility: followers なノートが含まれる', async () => { const [alice, bob] = await Promise.all([signup(), signup()]); - await api('/following/create', { userId: bob.id }, alice); - const list = await api('/users/lists/create', { name: 'list' }, alice).then(res => res.body); - await api('/users/lists/push', { listId: list.id, userId: bob.id }, alice); + await api('following/create', { userId: bob.id }, alice); + const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body); + await api('users/lists/push', { listId: list.id, userId: bob.id }, alice); await sleep(1000); const bobNote = await post(bob, { text: 'hi', visibility: 'followers' }); await waitForPushToTl(); - const res = await api('/notes/user-list-timeline', { listId: list.id }, alice); + const res = await api('notes/user-list-timeline', { listId: list.id }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); assert.strictEqual(res.body.find((note: any) => note.id === bobNote.id).text, 'hi'); @@ -954,14 +972,14 @@ describe('Timelines', () => { test.concurrent('リスインしている自分の visibility: followers なノートが含まれる', async () => { const [alice] = await Promise.all([signup(), signup()]); - const list = await api('/users/lists/create', { name: 'list' }, alice).then(res => res.body); - await api('/users/lists/push', { listId: list.id, userId: alice.id }, alice); + const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body); + await api('users/lists/push', { listId: list.id, userId: alice.id }, alice); await sleep(1000); const aliceNote = await post(alice, { text: 'hi', visibility: 'followers' }); await waitForPushToTl(); - const res = await api('/notes/user-list-timeline', { listId: list.id }, alice); + const res = await api('notes/user-list-timeline', { listId: list.id }, alice); assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true); assert.strictEqual(res.body.find((note: any) => note.id === aliceNote.id).text, 'hi'); @@ -970,15 +988,15 @@ describe('Timelines', () => { test.concurrent('リスインしているユーザーのチャンネルノートが含まれない', async () => { const [alice, bob] = await Promise.all([signup(), signup()]); - const channel = await api('/channels/create', { name: 'channel' }, bob).then(x => x.body); - const list = await api('/users/lists/create', { name: 'list' }, alice).then(res => res.body); - await api('/users/lists/push', { listId: list.id, userId: bob.id }, alice); + const channel = await api('channels/create', { name: 'channel' }, bob).then(x => x.body); + const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body); + await api('users/lists/push', { listId: list.id, userId: bob.id }, alice); await sleep(1000); const bobNote = await post(bob, { text: 'hi', channelId: channel.id }); await waitForPushToTl(); - const res = await api('/notes/user-list-timeline', { listId: list.id }, alice); + const res = await api('notes/user-list-timeline', { listId: list.id }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); }); @@ -986,15 +1004,15 @@ describe('Timelines', () => { test.concurrent('[withFiles: true] リスインしているユーザーのファイル付きノートのみ含まれる', async () => { const [alice, bob] = await Promise.all([signup(), signup()]); - const list = await api('/users/lists/create', { name: 'list' }, alice).then(res => res.body); - await api('/users/lists/push', { listId: list.id, userId: bob.id }, alice); + const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body); + await api('users/lists/push', { listId: list.id, userId: bob.id }, alice); const file = await uploadUrl(bob, 'https://raw.githubusercontent.com/misskey-dev/assets/main/public/icon.png'); const bobNote1 = await post(bob, { text: 'hi' }); const bobNote2 = await post(bob, { fileIds: [file.id] }); await waitForPushToTl(); - const res = await api('/notes/user-list-timeline', { listId: list.id, withFiles: true }, alice); + const res = await api('notes/user-list-timeline', { listId: list.id, withFiles: true }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote1.id), false); assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), true); @@ -1003,14 +1021,14 @@ describe('Timelines', () => { test.concurrent('リスインしているユーザーの自身宛ての visibility: specified なノートが含まれる', async () => { const [alice, bob] = await Promise.all([signup(), signup()]); - const list = await api('/users/lists/create', { name: 'list' }, alice).then(res => res.body); - await api('/users/lists/push', { listId: list.id, userId: bob.id }, alice); + const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body); + await api('users/lists/push', { listId: list.id, userId: bob.id }, alice); await sleep(1000); const bobNote = await post(bob, { text: 'hi', visibility: 'specified', visibleUserIds: [alice.id] }); await waitForPushToTl(); - const res = await api('/notes/user-list-timeline', { listId: list.id }, alice); + const res = await api('notes/user-list-timeline', { listId: list.id }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); assert.strictEqual(res.body.find((note: any) => note.id === bobNote.id).text, 'hi'); @@ -1019,15 +1037,15 @@ describe('Timelines', () => { test.concurrent('リスインしているユーザーの自身宛てではない visibility: specified なノートが含まれない', async () => { const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); - const list = await api('/users/lists/create', { name: 'list' }, alice).then(res => res.body); - await api('/users/lists/push', { listId: list.id, userId: bob.id }, alice); - await api('/users/lists/push', { listId: list.id, userId: carol.id }, alice); + const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body); + await api('users/lists/push', { listId: list.id, userId: bob.id }, alice); + await api('users/lists/push', { listId: list.id, userId: carol.id }, alice); await sleep(1000); const bobNote = await post(bob, { text: 'hi', visibility: 'specified', visibleUserIds: [carol.id] }); await waitForPushToTl(); - const res = await api('/notes/user-list-timeline', { listId: list.id }, alice); + const res = await api('notes/user-list-timeline', { listId: list.id }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); }); @@ -1041,7 +1059,7 @@ describe('Timelines', () => { await waitForPushToTl(); - const res = await api('/users/notes', { userId: bob.id }, alice); + const res = await api('users/notes', { userId: bob.id }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); }); @@ -1053,7 +1071,7 @@ describe('Timelines', () => { await waitForPushToTl(); - const res = await api('/users/notes', { userId: bob.id }, alice); + const res = await api('users/notes', { userId: bob.id }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); }); @@ -1061,13 +1079,13 @@ describe('Timelines', () => { test.concurrent('フォローしているユーザーの visibility: followers なノートが含まれる', async () => { const [alice, bob] = await Promise.all([signup(), signup()]); - await api('/following/create', { userId: bob.id }, alice); + await api('following/create', { userId: bob.id }, alice); await sleep(1000); const bobNote = await post(bob, { text: 'hi', visibility: 'followers' }); await waitForPushToTl(); - const res = await api('/users/notes', { userId: bob.id }, alice); + const res = await api('users/notes', { userId: bob.id }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); assert.strictEqual(res.body.find((note: any) => note.id === bobNote.id).text, 'hi'); @@ -1080,7 +1098,7 @@ describe('Timelines', () => { await waitForPushToTl(); - const res = await api('/users/notes', { userId: alice.id }, alice); + const res = await api('users/notes', { userId: alice.id }, alice); assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true); assert.strictEqual(res.body.find((note: any) => note.id === aliceNote.id).text, 'hi'); @@ -1089,12 +1107,12 @@ describe('Timelines', () => { test.concurrent('チャンネル投稿が含まれない', async () => { const [alice, bob] = await Promise.all([signup(), signup()]); - const channel = await api('/channels/create', { name: 'channel' }, bob).then(x => x.body); + const channel = await api('channels/create', { name: 'channel' }, bob).then(x => x.body); const bobNote = await post(bob, { text: 'hi', channelId: channel.id }); await waitForPushToTl(); - const res = await api('/users/notes', { userId: bob.id }, alice); + const res = await api('users/notes', { userId: bob.id }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); }); @@ -1108,7 +1126,7 @@ describe('Timelines', () => { await waitForPushToTl(); - const res = await api('/users/notes', { userId: bob.id }, alice); + const res = await api('users/notes', { userId: bob.id }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote1.id), true); assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), false); @@ -1123,7 +1141,7 @@ describe('Timelines', () => { await waitForPushToTl(); - const res = await api('/users/notes', { userId: bob.id, withReplies: true }, alice); + const res = await api('users/notes', { userId: bob.id, withReplies: true }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote1.id), true); assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), true); @@ -1138,7 +1156,7 @@ describe('Timelines', () => { await waitForPushToTl(); - const res = await api('/users/notes', { userId: bob.id, withReplies: true }, alice); + const res = await api('users/notes', { userId: bob.id, withReplies: true }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote1.id), true); assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), false); @@ -1153,7 +1171,7 @@ describe('Timelines', () => { await waitForPushToTl(); - const res = await api('/users/notes', { userId: bob.id, withFiles: true }, alice); + const res = await api('users/notes', { userId: bob.id, withFiles: true }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote1.id), false); assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), true); @@ -1162,12 +1180,12 @@ describe('Timelines', () => { test.concurrent('[withChannelNotes: true] チャンネル投稿が含まれる', async () => { const [alice, bob] = await Promise.all([signup(), signup()]); - const channel = await api('/channels/create', { name: 'channel' }, bob).then(x => x.body); + const channel = await api('channels/create', { name: 'channel' }, bob).then(x => x.body); const bobNote = await post(bob, { text: 'hi', channelId: channel.id }); await waitForPushToTl(); - const res = await api('/users/notes', { userId: bob.id, withChannelNotes: true }, alice); + const res = await api('users/notes', { userId: bob.id, withChannelNotes: true }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); }); @@ -1175,12 +1193,12 @@ describe('Timelines', () => { test.concurrent('[withChannelNotes: true] 他人が取得した場合センシティブチャンネル投稿が含まれない', async () => { const [alice, bob] = await Promise.all([signup(), signup()]); - const channel = await api('/channels/create', { name: 'channel', isSensitive: true }, bob).then(x => x.body); + const channel = await api('channels/create', { name: 'channel', isSensitive: true }, bob).then(x => x.body); const bobNote = await post(bob, { text: 'hi', channelId: channel.id }); await waitForPushToTl(); - const res = await api('/users/notes', { userId: bob.id, withChannelNotes: true }, alice); + const res = await api('users/notes', { userId: bob.id, withChannelNotes: true }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); }); @@ -1188,12 +1206,12 @@ describe('Timelines', () => { test.concurrent('[withChannelNotes: true] 自分が取得した場合センシティブチャンネル投稿が含まれる', async () => { const [bob] = await Promise.all([signup()]); - const channel = await api('/channels/create', { name: 'channel', isSensitive: true }, bob).then(x => x.body); + const channel = await api('channels/create', { name: 'channel', isSensitive: true }, bob).then(x => x.body); const bobNote = await post(bob, { text: 'hi', channelId: channel.id }); await waitForPushToTl(); - const res = await api('/users/notes', { userId: bob.id, withChannelNotes: true }, bob); + const res = await api('users/notes', { userId: bob.id, withChannelNotes: true }, bob); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); }); @@ -1201,14 +1219,14 @@ describe('Timelines', () => { test.concurrent('ミュートしているユーザーに関連する投稿が含まれない', async () => { const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); - await api('/mute/create', { userId: carol.id }, alice); + await api('mute/create', { userId: carol.id }, alice); await sleep(1000); const carolNote = await post(carol, { text: 'hi' }); const bobNote = await post(bob, { text: 'hi', renoteId: carolNote.id }); await waitForPushToTl(); - const res = await api('/users/notes', { userId: bob.id }, alice); + const res = await api('users/notes', { userId: bob.id }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); }); @@ -1216,7 +1234,7 @@ describe('Timelines', () => { test.concurrent('ミュートしていても userId に指定したユーザーの投稿が含まれる', async () => { const [alice, bob] = await Promise.all([signup(), signup()]); - await api('/mute/create', { userId: bob.id }, alice); + await api('mute/create', { userId: bob.id }, alice); await sleep(1000); const bobNote1 = await post(bob, { text: 'hi' }); const bobNote2 = await post(bob, { text: 'hi', replyId: bobNote1.id }); @@ -1224,7 +1242,7 @@ describe('Timelines', () => { await waitForPushToTl(); - const res = await api('/users/notes', { userId: bob.id }, alice); + const res = await api('users/notes', { userId: bob.id }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote1.id), true); assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), true); @@ -1238,7 +1256,7 @@ describe('Timelines', () => { await waitForPushToTl(); - const res = await api('/users/notes', { userId: alice.id, withReplies: true }, alice); + const res = await api('users/notes', { userId: alice.id, withReplies: true }, alice); assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true); }); @@ -1250,7 +1268,7 @@ describe('Timelines', () => { await waitForPushToTl(); - const res = await api('/users/notes', { userId: bob.id, withReplies: true }, alice); + const res = await api('users/notes', { userId: bob.id, withReplies: true }, alice); assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); }); diff --git a/packages/backend/test/e2e/user-notes.ts b/packages/backend/test/e2e/user-notes.ts index 6897cf08c6..331e053935 100644 --- a/packages/backend/test/e2e/user-notes.ts +++ b/packages/backend/test/e2e/user-notes.ts @@ -11,9 +11,9 @@ import type * as misskey from 'misskey-js'; describe('users/notes', () => { let alice: misskey.entities.SignupResponse; - let jpgNote: any; - let pngNote: any; - let jpgPngNote: any; + let jpgNote: misskey.entities.Note; + let pngNote: misskey.entities.Note; + let jpgPngNote: misskey.entities.Note; beforeAll(async () => { alice = await signup({ username: 'alice' }); @@ -31,7 +31,7 @@ describe('users/notes', () => { }, 1000 * 60 * 2); test('withFiles', async () => { - const res = await api('/users/notes', { + const res = await api('users/notes', { userId: alice.id, withFiles: true, }, alice); diff --git a/packages/backend/test/e2e/users.ts b/packages/backend/test/e2e/users.ts index 19b905dd47..f5e1234c57 100644 --- a/packages/backend/test/e2e/users.ts +++ b/packages/backend/test/e2e/users.ts @@ -8,7 +8,7 @@ process.env.NODE_ENV = 'test'; import * as assert from 'assert'; import { inspect } from 'node:util'; import { DEFAULT_POLICIES } from '@/core/RoleService.js'; -import { api, page, post, role, signup, successfulApiCall, uploadFile } from '../utils.js'; +import { api, post, role, signup, successfulApiCall, uploadFile } from '../utils.js'; import type * as misskey from 'misskey-js'; describe('ユーザー', () => { @@ -24,31 +24,12 @@ describe('ユーザー', () => { }, {}); }; - // BUG misskey-jsとjson-schemaと実際に返ってくるデータが全部違う - type UserLite = misskey.entities.UserLite & { - badgeRoles: any[], - }; - - type UserDetailedNotMe = UserLite & - misskey.entities.UserDetailed & { - roles: any[], - }; - - type MeDetailed = UserDetailedNotMe & - misskey.entities.MeDetailed & { - achievements: object[], - loggedInDays: number, - policies: object, - }; - - type User = MeDetailed & { token: string }; - - const show = async (id: string, me = root): Promise<MeDetailed | UserDetailedNotMe> => { - return successfulApiCall({ endpoint: 'users/show', parameters: { userId: id }, user: me }) as any; + const show = async (id: string, me = root): Promise<misskey.entities.UserDetailed> => { + return successfulApiCall({ endpoint: 'users/show', parameters: { userId: id }, user: me }); }; // UserLiteのキーが過不足なく入っている? - const userLite = (user: User): Partial<UserLite> => { + const userLite = (user: misskey.entities.UserLite): Partial<misskey.entities.UserLite> => { return stripUndefined({ id: user.id, name: user.name, @@ -72,7 +53,7 @@ describe('ユーザー', () => { }; // UserDetailedNotMeのキーが過不足なく入っている? - const userDetailedNotMe = (user: User): Partial<UserDetailedNotMe> => { + const userDetailedNotMe = (user: misskey.entities.SignupResponse): Partial<misskey.entities.UserDetailedNotMe> => { return stripUndefined({ ...userLite(user), url: user.url, @@ -114,7 +95,7 @@ describe('ユーザー', () => { }; // Relations関連のキーが過不足なく入っている? - const userDetailedNotMeWithRelations = (user: User): Partial<UserDetailedNotMe> => { + const userDetailedNotMeWithRelations = (user: misskey.entities.SignupResponse): Partial<misskey.entities.UserDetailedNotMe> => { return stripUndefined({ ...userDetailedNotMe(user), isFollowing: user.isFollowing ?? false, @@ -131,7 +112,7 @@ describe('ユーザー', () => { }; // MeDetailedのキーが過不足なく入っている? - const meDetailed = (user: User, security = false): Partial<MeDetailed> => { + const meDetailed = (user: misskey.entities.SignupResponse, security = false): Partial<misskey.entities.MeDetailed> => { return stripUndefined({ ...userDetailedNotMe(user), avatarId: user.avatarId, @@ -162,6 +143,7 @@ describe('ユーザー', () => { mutedWords: user.mutedWords, hardMutedWords: user.hardMutedWords, mutedInstances: user.mutedInstances, + // @ts-expect-error 後方互換性 mutingNotificationTypes: user.mutingNotificationTypes, notificationRecieveConfig: user.notificationRecieveConfig, emailNotificationTypes: user.emailNotificationTypes, @@ -176,61 +158,53 @@ describe('ユーザー', () => { }); }; - let root: User; - let alice: User; + let root: misskey.entities.SignupResponse; + let alice: misskey.entities.SignupResponse; let aliceNote: misskey.entities.Note; - let alicePage: misskey.entities.Page; - let aliceList: misskey.entities.UserList; - let bob: User; - let bobNote: misskey.entities.Note; + let bob: misskey.entities.SignupResponse; - let carol: User; - let dave: User; - let ellen: User; - let frank: User; + // NOTE: これがないと落ちる(bob の updatedAt が null になってしまうため?) + let bobNote: misskey.entities.Note; // eslint-disable-line @typescript-eslint/no-unused-vars - let usersReplying: User[]; + let carol: misskey.entities.SignupResponse; - let userNoNote: User; - let userNotExplorable: User; - let userLocking: User; - let userAdmin: User; - let roleAdmin: any; - let userModerator: User; - let roleModerator: any; - let userRolePublic: User; - let rolePublic: any; - let userRoleBadge: User; - let roleBadge: any; - let userSilenced: User; - let roleSilenced: any; - let userSuspended: User; - let userDeletedBySelf: User; - let userDeletedByAdmin: User; - let userFollowingAlice: User; - let userFollowedByAlice: User; - let userBlockingAlice: User; - let userBlockedByAlice: User; - let userMutingAlice: User; - let userMutedByAlice: User; - let userRnMutingAlice: User; - let userRnMutedByAlice: User; - let userFollowRequesting: User; - let userFollowRequested: User; + let usersReplying: misskey.entities.SignupResponse[]; + + let userNoNote: misskey.entities.SignupResponse; + let userNotExplorable: misskey.entities.SignupResponse; + let userLocking: misskey.entities.SignupResponse; + let userAdmin: misskey.entities.SignupResponse; + let roleAdmin: misskey.entities.Role; + let userModerator: misskey.entities.SignupResponse; + let roleModerator: misskey.entities.Role; + let userRolePublic: misskey.entities.SignupResponse; + let rolePublic: misskey.entities.Role; + let userRoleBadge: misskey.entities.SignupResponse; + let roleBadge: misskey.entities.Role; + let userSilenced: misskey.entities.SignupResponse; + let roleSilenced: misskey.entities.Role; + let userSuspended: misskey.entities.SignupResponse; + let userDeletedBySelf: misskey.entities.SignupResponse; + let userDeletedByAdmin: misskey.entities.SignupResponse; + let userFollowingAlice: misskey.entities.SignupResponse; + let userFollowedByAlice: misskey.entities.SignupResponse; + let userBlockingAlice: misskey.entities.SignupResponse; + let userBlockedByAlice: misskey.entities.SignupResponse; + let userMutingAlice: misskey.entities.SignupResponse; + let userMutedByAlice: misskey.entities.SignupResponse; + let userRnMutingAlice: misskey.entities.SignupResponse; + let userRnMutedByAlice: misskey.entities.SignupResponse; + let userFollowRequesting: misskey.entities.SignupResponse; + let userFollowRequested: misskey.entities.SignupResponse; beforeAll(async () => { root = await signup({ username: 'root' }); alice = await signup({ username: 'alice' }); - aliceNote = await post(alice, { text: 'test' }) as any; - alicePage = await page(alice); - aliceList = (await api('users/list/create', { name: 'aliceList' }, alice)).body; + aliceNote = await post(alice, { text: 'test' }); bob = await signup({ username: 'bob' }); - bobNote = await post(bob, { text: 'test' }) as any; + bobNote = await post(bob, { text: 'test' }); carol = await signup({ username: 'carol' }); - dave = await signup({ username: 'dave' }); - ellen = await signup({ username: 'ellen' }); - frank = await signup({ username: 'frank' }); // @alice -> @replyingへのリプライ。Promise.allで一気に作るとtimeoutしてしまうのでreduceで一つ一つawaitする usersReplying = await [...Array(10)].map((_, i) => i).reduce(async (acc, i) => { @@ -241,7 +215,7 @@ describe('ユーザー', () => { } return (await acc).concat(u); - }, Promise.resolve([] as User[])); + }, Promise.resolve([] as misskey.entities.SignupResponse[])); userNoNote = await signup({ username: 'userNoNote' }); userNotExplorable = await signup({ username: 'userNotExplorable' }); @@ -309,7 +283,7 @@ describe('ユーザー', () => { beforeEach(async () => { alice = { ...alice, - ...await successfulApiCall({ endpoint: 'i', parameters: {}, user: alice }) as any, + ...await successfulApiCall({ endpoint: 'i', parameters: {}, user: alice }), }; aliceNote = await successfulApiCall({ endpoint: 'notes/show', parameters: { noteId: aliceNote.id }, user: alice }); }); @@ -322,7 +296,7 @@ describe('ユーザー', () => { endpoint: 'signup', parameters: { username: 'zoe', password: 'password' }, user: undefined, - }) as unknown as User; // BUG MeDetailedに足りないキーがある + }) as unknown as misskey.entities.SignupResponse; // BUG MeDetailedに足りないキーがある // signupの時はtokenが含まれる特別なMeDetailedが返ってくる assert.match(response.token, /[a-zA-Z0-9]{16}/); @@ -332,7 +306,7 @@ describe('ユーザー', () => { assert.strictEqual(response.name, null); assert.strictEqual(response.username, 'zoe'); assert.strictEqual(response.host, null); - assert.match(response.avatarUrl, /^[-a-zA-Z0-9@:%._\+~#&?=\/]+$/); + response.avatarUrl && assert.match(response.avatarUrl, /^[-a-zA-Z0-9@:%._\+~#&?=\/]+$/); assert.strictEqual(response.avatarBlurhash, null); assert.deepStrictEqual(response.avatarDecorations, []); assert.strictEqual(response.isBot, false); @@ -407,6 +381,7 @@ describe('ユーザー', () => { assert.deepStrictEqual(response.unreadAnnouncements, []); assert.deepStrictEqual(response.mutedWords, []); assert.deepStrictEqual(response.mutedInstances, []); + // @ts-expect-error 後方互換のため assert.deepStrictEqual(response.mutingNotificationTypes, []); assert.deepStrictEqual(response.notificationRecieveConfig, {}); assert.deepStrictEqual(response.emailNotificationTypes, ['follow', 'receiveFollowRequest']); @@ -436,68 +411,66 @@ describe('ユーザー', () => { //#region 自分の情報の更新(i/update) test.each([ - { parameters: (): object => ({ name: null }) }, - { parameters: (): object => ({ name: 'x'.repeat(50) }) }, - { parameters: (): object => ({ name: 'x' }) }, - { parameters: (): object => ({ name: 'My name' }) }, - { parameters: (): object => ({ description: null }) }, - { parameters: (): object => ({ description: 'x'.repeat(1500) }) }, - { parameters: (): object => ({ description: 'x' }) }, - { parameters: (): object => ({ description: 'My description' }) }, - { parameters: (): object => ({ location: null }) }, - { parameters: (): object => ({ location: 'x'.repeat(50) }) }, - { parameters: (): object => ({ location: 'x' }) }, - { parameters: (): object => ({ location: 'My location' }) }, - { parameters: (): object => ({ birthday: '0000-00-00' }) }, - { parameters: (): object => ({ birthday: '9999-99-99' }) }, - { parameters: (): object => ({ lang: 'en-US' }) }, - { parameters: (): object => ({ fields: [] }) }, - { parameters: (): object => ({ fields: [{ name: 'x', value: 'x' }] }) }, - { parameters: (): object => ({ fields: [{ name: 'x'.repeat(3000), value: 'x'.repeat(3000) }] }) }, // BUG? fieldには制限がない - { parameters: (): object => ({ fields: Array(16).fill({ name: 'x', value: 'y' }) }) }, - { parameters: (): object => ({ isLocked: true }) }, - { parameters: (): object => ({ isLocked: false }) }, - { parameters: (): object => ({ isExplorable: false }) }, - { parameters: (): object => ({ isExplorable: true }) }, - { parameters: (): object => ({ hideOnlineStatus: true }) }, - { parameters: (): object => ({ hideOnlineStatus: false }) }, - { parameters: (): object => ({ publicReactions: false }) }, - { parameters: (): object => ({ publicReactions: true }) }, - { parameters: (): object => ({ autoAcceptFollowed: true }) }, - { parameters: (): object => ({ autoAcceptFollowed: false }) }, - { parameters: (): object => ({ noCrawle: true }) }, - { parameters: (): object => ({ noCrawle: false }) }, - { parameters: (): object => ({ preventAiLearning: false }) }, - { parameters: (): object => ({ preventAiLearning: true }) }, - { parameters: (): object => ({ isBot: true }) }, - { parameters: (): object => ({ isBot: false }) }, - { parameters: (): object => ({ isCat: true }) }, - { parameters: (): object => ({ isCat: false }) }, - { parameters: (): object => ({ speakAsCat: true }) }, - { parameters: (): object => ({ speakAsCat: false }) }, - { parameters: (): object => ({ injectFeaturedNote: true }) }, - { parameters: (): object => ({ injectFeaturedNote: false }) }, - { parameters: (): object => ({ receiveAnnouncementEmail: true }) }, - { parameters: (): object => ({ receiveAnnouncementEmail: false }) }, - { parameters: (): object => ({ alwaysMarkNsfw: true }) }, - { parameters: (): object => ({ alwaysMarkNsfw: false }) }, - { parameters: (): object => ({ autoSensitive: true }) }, - { parameters: (): object => ({ autoSensitive: false }) }, - { parameters: (): object => ({ followingVisibility: 'private' }) }, - { parameters: (): object => ({ followingVisibility: 'followers' }) }, - { parameters: (): object => ({ followingVisibility: 'public' }) }, - { parameters: (): object => ({ followersVisibility: 'private' }) }, - { parameters: (): object => ({ followersVisibility: 'followers' }) }, - { parameters: (): object => ({ followersVisibility: 'public' }) }, - { parameters: (): object => ({ mutedWords: Array(19).fill(['xxxxx']) }) }, - { parameters: (): object => ({ mutedWords: [['x'.repeat(194)]] }) }, - { parameters: (): object => ({ mutedWords: [] }) }, - { parameters: (): object => ({ mutedInstances: ['xxxx.xxxxx'] }) }, - { parameters: (): object => ({ mutedInstances: [] }) }, - { parameters: (): object => ({ notificationRecieveConfig: { mention: { type: 'following' } } }) }, - { parameters: (): object => ({ notificationRecieveConfig: {} }) }, - { parameters: (): object => ({ emailNotificationTypes: ['mention', 'reply', 'quote', 'follow', 'receiveFollowRequest'] }) }, - { parameters: (): object => ({ emailNotificationTypes: [] }) }, + { parameters: () => ({ name: null }) }, + { parameters: () => ({ name: 'x'.repeat(50) }) }, + { parameters: () => ({ name: 'x' }) }, + { parameters: () => ({ name: 'My name' }) }, + { parameters: () => ({ description: null }) }, + { parameters: () => ({ description: 'x'.repeat(1500) }) }, + { parameters: () => ({ description: 'x' }) }, + { parameters: () => ({ description: 'My description' }) }, + { parameters: () => ({ location: null }) }, + { parameters: () => ({ location: 'x'.repeat(50) }) }, + { parameters: () => ({ location: 'x' }) }, + { parameters: () => ({ location: 'My location' }) }, + { parameters: () => ({ birthday: '0000-00-00' }) }, + { parameters: () => ({ birthday: '9999-99-99' }) }, + { parameters: () => ({ lang: 'en-US' as const }) }, + { parameters: () => ({ fields: [] }) }, + { parameters: () => ({ fields: [{ name: 'x', value: 'x' }] }) }, + { parameters: () => ({ fields: [{ name: 'x'.repeat(3000), value: 'x'.repeat(3000) }] }) }, // BUG? fieldには制限がない + { parameters: () => ({ fields: Array(16).fill({ name: 'x', value: 'y' }) }) }, + { parameters: () => ({ isLocked: true }) }, + { parameters: () => ({ isLocked: false }) }, + { parameters: () => ({ isExplorable: false }) }, + { parameters: () => ({ isExplorable: true }) }, + { parameters: () => ({ hideOnlineStatus: true }) }, + { parameters: () => ({ hideOnlineStatus: false }) }, + { parameters: () => ({ publicReactions: false }) }, + { parameters: () => ({ publicReactions: true }) }, + { parameters: () => ({ autoAcceptFollowed: true }) }, + { parameters: () => ({ autoAcceptFollowed: false }) }, + { parameters: () => ({ noCrawle: true }) }, + { parameters: () => ({ noCrawle: false }) }, + { parameters: () => ({ preventAiLearning: false }) }, + { parameters: () => ({ preventAiLearning: true }) }, + { parameters: () => ({ isBot: true }) }, + { parameters: () => ({ isBot: false }) }, + { parameters: () => ({ isCat: true }) }, + { parameters: () => ({ isCat: false }) }, + { parameters: () => ({ injectFeaturedNote: true }) }, + { parameters: () => ({ injectFeaturedNote: false }) }, + { parameters: () => ({ receiveAnnouncementEmail: true }) }, + { parameters: () => ({ receiveAnnouncementEmail: false }) }, + { parameters: () => ({ alwaysMarkNsfw: true }) }, + { parameters: () => ({ alwaysMarkNsfw: false }) }, + { parameters: () => ({ autoSensitive: true }) }, + { parameters: () => ({ autoSensitive: false }) }, + { parameters: () => ({ followingVisibility: 'private' as const }) }, + { parameters: () => ({ followingVisibility: 'followers' as const }) }, + { parameters: () => ({ followingVisibility: 'public' as const }) }, + { parameters: () => ({ followersVisibility: 'private' as const }) }, + { parameters: () => ({ followersVisibility: 'followers' as const }) }, + { parameters: () => ({ followersVisibility: 'public' as const }) }, + { parameters: () => ({ mutedWords: Array(19).fill(['xxxxx']) }) }, + { parameters: () => ({ mutedWords: [['x'.repeat(194)]] }) }, + { parameters: () => ({ mutedWords: [] }) }, + { parameters: () => ({ mutedInstances: ['xxxx.xxxxx'] }) }, + { parameters: () => ({ mutedInstances: [] }) }, + { parameters: () => ({ notificationRecieveConfig: { mention: { type: 'following' } } }) }, + { parameters: () => ({ notificationRecieveConfig: {} }) }, + { parameters: () => ({ emailNotificationTypes: ['mention', 'reply', 'quote', 'follow', 'receiveFollowRequest'] }) }, + { parameters: () => ({ emailNotificationTypes: [] }) }, ] as const)('を書き換えることができる($#)', async ({ parameters }) => { const response = await successfulApiCall({ endpoint: 'i/update', parameters: parameters(), user: alice }); const expected = { ...meDetailed(alice, true), ...parameters() }; @@ -506,13 +479,13 @@ describe('ユーザー', () => { test('を書き換えることができる(Avatar)', async () => { const aliceFile = (await uploadFile(alice)).body; - const parameters = { avatarId: aliceFile.id }; + const parameters = { avatarId: aliceFile!.id }; const response = await successfulApiCall({ endpoint: 'i/update', parameters: parameters, user: alice }); assert.match(response.avatarUrl ?? '.', /^[-a-zA-Z0-9@:%._\+~#&?=\/]+$/); assert.match(response.avatarBlurhash ?? '.', /[ -~]{54}/); const expected = { ...meDetailed(alice, true), - avatarId: aliceFile.id, + avatarId: aliceFile!.id, avatarBlurhash: response.avatarBlurhash, avatarUrl: response.avatarUrl, }; @@ -531,13 +504,13 @@ describe('ユーザー', () => { test('を書き換えることができる(Banner)', async () => { const aliceFile = (await uploadFile(alice)).body; - const parameters = { bannerId: aliceFile.id }; + const parameters = { bannerId: aliceFile!.id }; const response = await successfulApiCall({ endpoint: 'i/update', parameters: parameters, user: alice }); assert.match(response.bannerUrl ?? '.', /^[-a-zA-Z0-9@:%._\+~#&?=\/]+$/); assert.match(response.bannerBlurhash ?? '.', /[ -~]{54}/); const expected = { ...meDetailed(alice, true), - bannerId: aliceFile.id, + bannerId: aliceFile!.id, bannerBlurhash: response.bannerBlurhash, bannerUrl: response.bannerUrl, }; @@ -612,13 +585,13 @@ describe('ユーザー', () => { //#region ユーザー(users) test.each([ - { label: 'ID昇順', parameters: { limit: 5 }, selector: (u: UserLite): string => u.id }, - { label: 'フォロワー昇順', parameters: { sort: '+follower' }, selector: (u: UserDetailedNotMe): string => String(u.followersCount) }, - { label: 'フォロワー降順', parameters: { sort: '-follower' }, selector: (u: UserDetailedNotMe): string => String(u.followersCount) }, - { label: '登録日時昇順', parameters: { sort: '+createdAt' }, selector: (u: UserDetailedNotMe): string => u.createdAt }, - { label: '登録日時降順', parameters: { sort: '-createdAt' }, selector: (u: UserDetailedNotMe): string => u.createdAt }, - { label: '投稿日時昇順', parameters: { sort: '+updatedAt' }, selector: (u: UserDetailedNotMe): string => String(u.updatedAt) }, - { label: '投稿日時降順', parameters: { sort: '-updatedAt' }, selector: (u: UserDetailedNotMe): string => String(u.updatedAt) }, + { label: 'ID昇順', parameters: { limit: 5 }, selector: (u: misskey.entities.UserLite): string => u.id }, + { label: 'フォロワー昇順', parameters: { sort: '+follower' }, selector: (u: misskey.entities.UserDetailedNotMe): string => String(u.followersCount) }, + { label: 'フォロワー降順', parameters: { sort: '-follower' }, selector: (u: misskey.entities.UserDetailedNotMe): string => String(u.followersCount) }, + { label: '登録日時昇順', parameters: { sort: '+createdAt' }, selector: (u: misskey.entities.UserDetailedNotMe): string => u.createdAt }, + { label: '登録日時降順', parameters: { sort: '-createdAt' }, selector: (u: misskey.entities.UserDetailedNotMe): string => u.createdAt }, + { label: '投稿日時昇順', parameters: { sort: '+updatedAt' }, selector: (u: misskey.entities.UserDetailedNotMe): string => String(u.updatedAt) }, + { label: '投稿日時降順', parameters: { sort: '-updatedAt' }, selector: (u: misskey.entities.UserDetailedNotMe): string => String(u.updatedAt) }, ] as const)('をリスト形式で取得することができる($label)', async ({ parameters, selector }) => { const response = await successfulApiCall({ endpoint: 'users', parameters, user: alice }); @@ -631,15 +604,15 @@ describe('ユーザー', () => { assert.deepStrictEqual(response, expected); }); test.each([ - { label: '「見つけやすくする」がOFFのユーザーが含まれない', user: (): User => userNotExplorable, excluded: true }, - { label: 'ミュートユーザーが含まれない', user: (): User => userMutedByAlice, excluded: true }, - { label: 'ブロックされているユーザーが含まれない', user: (): User => userBlockedByAlice, excluded: true }, - { label: 'ブロックしてきているユーザーが含まれる', user: (): User => userBlockingAlice, excluded: true }, - { label: '承認制ユーザーが含まれる', user: (): User => userLocking }, - { label: 'サイレンスユーザーが含まれる', user: (): User => userSilenced }, - { label: 'サスペンドユーザーが含まれない', user: (): User => userSuspended, excluded: true }, - { label: '削除済ユーザーが含まれる', user: (): User => userDeletedBySelf }, - { label: '削除済(byAdmin)ユーザーが含まれる', user: (): User => userDeletedByAdmin }, + { label: '「見つけやすくする」がOFFのユーザーが含まれない', user: () => userNotExplorable, excluded: true }, + { label: 'ミュートユーザーが含まれない', user: () => userMutedByAlice, excluded: true }, + { label: 'ブロックされているユーザーが含まれない', user: () => userBlockedByAlice, excluded: true }, + { label: 'ブロックしてきているユーザーが含まれる', user: () => userBlockingAlice, excluded: true }, + { label: '承認制ユーザーが含まれる', user: () => userLocking }, + { label: 'サイレンスユーザーが含まれる', user: () => userSilenced }, + { label: 'サスペンドユーザーが含まれない', user: () => userSuspended, excluded: true }, + { label: '削除済ユーザーが含まれる', user: () => userDeletedBySelf }, + { label: '削除済(byAdmin)ユーザーが含まれる', user: () => userDeletedByAdmin }, ] as const)('をリスト形式で取得することができ、結果に$label', async ({ user, excluded }) => { const parameters = { limit: 100 }; const response = await successfulApiCall({ endpoint: 'users', parameters, user: alice }); @@ -653,39 +626,44 @@ describe('ユーザー', () => { //#region ユーザー情報(users/show) test.each([ - { label: 'ID指定で自分自身を', parameters: (): object => ({ userId: alice.id }), user: (): User => alice, type: meDetailed }, - { label: 'ID指定で他人を', parameters: (): object => ({ userId: alice.id }), user: (): User => bob, type: userDetailedNotMeWithRelations }, - { label: 'ID指定かつ未認証', parameters: (): object => ({ userId: alice.id }), user: undefined, type: userDetailedNotMe }, - { label: '@指定で自分自身を', parameters: (): object => ({ username: alice.username }), user: (): User => alice, type: meDetailed }, - { label: '@指定で他人を', parameters: (): object => ({ username: alice.username }), user: (): User => bob, type: userDetailedNotMeWithRelations }, - { label: '@指定かつ未認証', parameters: (): object => ({ username: alice.username }), user: undefined, type: userDetailedNotMe }, + { label: 'ID指定で自分自身を', parameters: () => ({ userId: alice.id }), user: () => alice, type: meDetailed }, + { label: 'ID指定で他人を', parameters: () => ({ userId: alice.id }), user: () => bob, type: userDetailedNotMeWithRelations }, + { label: 'ID指定かつ未認証', parameters: () => ({ userId: alice.id }), user: undefined, type: userDetailedNotMe }, + { label: '@指定で自分自身を', parameters: () => ({ username: alice.username }), user: () => alice, type: meDetailed }, + { label: '@指定で他人を', parameters: () => ({ username: alice.username }), user: () => bob, type: userDetailedNotMeWithRelations }, + { label: '@指定かつ未認証', parameters: () => ({ username: alice.username }), user: undefined, type: userDetailedNotMe }, ] as const)('を取得することができる($label)', async ({ parameters, user, type }) => { const response = await successfulApiCall({ endpoint: 'users/show', parameters: parameters(), user: user?.() }); const expected = type(alice); assert.deepStrictEqual(response, expected); }); test.each([ - { label: 'Administratorになっている', user: (): User => userAdmin, me: (): User => userAdmin, selector: (user: User): unknown => user.isAdmin }, - { label: '自分以外から見たときはAdministratorか判定できない', user: (): User => userAdmin, selector: (user: User): unknown => user.isAdmin, expected: (): undefined => undefined }, - { label: 'Moderatorになっている', user: (): User => userModerator, me: (): User => userModerator, selector: (user: User): unknown => user.isModerator }, - { label: '自分以外から見たときはModeratorか判定できない', user: (): User => userModerator, selector: (user: User): unknown => user.isModerator, expected: (): undefined => undefined }, - { label: 'サイレンスになっている', user: (): User => userSilenced, selector: (user: User): unknown => user.isSilenced }, - //{ label: 'サスペンドになっている', user: (): User => userSuspended, selector: (user: User): unknown => user.isSuspended }, - { label: '削除済みになっている', user: (): User => userDeletedBySelf, me: (): User => userDeletedBySelf, selector: (user: User): unknown => user.isDeleted }, - { label: '自分以外から見たときは削除済みか判定できない', user: (): User => userDeletedBySelf, selector: (user: User): unknown => user.isDeleted, expected: (): undefined => undefined }, - { label: '削除済み(byAdmin)になっている', user: (): User => userDeletedByAdmin, me: (): User => userDeletedByAdmin, selector: (user: User): unknown => user.isDeleted }, - { label: '自分以外から見たときは削除済み(byAdmin)か判定できない', user: (): User => userDeletedByAdmin, selector: (user: User): unknown => user.isDeleted, expected: (): undefined => undefined }, - { label: 'フォロー中になっている', user: (): User => userFollowedByAlice, selector: (user: User): unknown => user.isFollowing }, - { label: 'フォローされている', user: (): User => userFollowingAlice, selector: (user: User): unknown => user.isFollowed }, - { label: 'ブロック中になっている', user: (): User => userBlockedByAlice, selector: (user: User): unknown => user.isBlocking }, - { label: 'ブロックされている', user: (): User => userBlockingAlice, selector: (user: User): unknown => user.isBlocked }, - { label: 'ミュート中になっている', user: (): User => userMutedByAlice, selector: (user: User): unknown => user.isMuted }, - { label: 'リノートミュート中になっている', user: (): User => userRnMutedByAlice, selector: (user: User): unknown => user.isRenoteMuted }, - { label: 'フォローリクエスト中になっている', user: (): User => userFollowRequested, me: (): User => userFollowRequesting, selector: (user: User): unknown => user.hasPendingFollowRequestFromYou }, - { label: 'フォローリクエストされている', user: (): User => userFollowRequesting, me: (): User => userFollowRequested, selector: (user: User): unknown => user.hasPendingFollowRequestToYou }, + { label: 'Administratorになっている', user: () => userAdmin, me: () => userAdmin, selector: (user: misskey.entities.MeDetailed) => user.isAdmin }, + // @ts-expect-error UserDetailedNotMe doesn't include isAdmin + { label: '自分以外から見たときはAdministratorか判定できない', user: () => userAdmin, selector: (user: misskey.entities.UserDetailedNotMe) => user.isAdmin, expected: () => undefined }, + { label: 'Moderatorになっている', user: () => userModerator, me: () => userModerator, selector: (user: misskey.entities.MeDetailed) => user.isModerator }, + // @ts-expect-error UserDetailedNotMe doesn't include isModerator + { label: '自分以外から見たときはModeratorか判定できない', user: () => userModerator, selector: (user: misskey.entities.UserDetailedNotMe) => user.isModerator, expected: () => undefined }, + { label: 'サイレンスになっている', user: () => userSilenced, selector: (user: misskey.entities.UserDetailed) => user.isSilenced }, + // FIXME: 落ちる + //{ label: 'サスペンドになっている', user: () => userSuspended, selector: (user: misskey.entities.UserDetailed) => user.isSuspended }, + { label: '削除済みになっている', user: () => userDeletedBySelf, me: () => userDeletedBySelf, selector: (user: misskey.entities.MeDetailed) => user.isDeleted }, + // @ts-expect-error UserDetailedNotMe doesn't include isDeleted + { label: '自分以外から見たときは削除済みか判定できない', user: () => userDeletedBySelf, selector: (user: misskey.entities.UserDetailedNotMe) => user.isDeleted, expected: () => undefined }, + { label: '削除済み(byAdmin)になっている', user: () => userDeletedByAdmin, me: () => userDeletedByAdmin, selector: (user: misskey.entities.MeDetailed) => user.isDeleted }, + // @ts-expect-error UserDetailedNotMe doesn't include isDeleted + { label: '自分以外から見たときは削除済み(byAdmin)か判定できない', user: () => userDeletedByAdmin, selector: (user: misskey.entities.UserDetailedNotMe) => user.isDeleted, expected: () => undefined }, + { label: 'フォロー中になっている', user: () => userFollowedByAlice, selector: (user: misskey.entities.UserDetailed) => user.isFollowing }, + { label: 'フォローされている', user: () => userFollowingAlice, selector: (user: misskey.entities.UserDetailed) => user.isFollowed }, + { label: 'ブロック中になっている', user: () => userBlockedByAlice, selector: (user: misskey.entities.UserDetailed) => user.isBlocking }, + { label: 'ブロックされている', user: () => userBlockingAlice, selector: (user: misskey.entities.UserDetailed) => user.isBlocked }, + { label: 'ミュート中になっている', user: () => userMutedByAlice, selector: (user: misskey.entities.UserDetailed) => user.isMuted }, + { label: 'リノートミュート中になっている', user: () => userRnMutedByAlice, selector: (user: misskey.entities.UserDetailed) => user.isRenoteMuted }, + { label: 'フォローリクエスト中になっている', user: () => userFollowRequested, me: () => userFollowRequesting, selector: (user: misskey.entities.UserDetailed) => user.hasPendingFollowRequestFromYou }, + { label: 'フォローリクエストされている', user: () => userFollowRequesting, me: () => userFollowRequested, selector: (user: misskey.entities.UserDetailed) => user.hasPendingFollowRequestToYou }, ] as const)('を取得することができ、$labelこと', async ({ user, me, selector, expected }) => { const response = await successfulApiCall({ endpoint: 'users/show', parameters: { userId: user().id }, user: me?.() ?? alice }); - assert.strictEqual(selector(response), (expected ?? ((): true => true))()); + assert.strictEqual(selector(response as any), (expected ?? ((): true => true))()); }); test('を取得することができ、Publicなロールがセットされていること', async () => { const response = await successfulApiCall({ endpoint: 'users/show', parameters: { userId: userRolePublic.id }, user: alice }); @@ -727,17 +705,18 @@ describe('ユーザー', () => { assert.deepStrictEqual(response, expected); }); test.each([ - { label: '「見つけやすくする」がOFFのユーザーが含まれる', user: (): User => userNotExplorable }, - { label: 'ミュートユーザーが含まれる', user: (): User => userMutedByAlice }, - { label: 'ブロックされているユーザーが含まれる', user: (): User => userBlockedByAlice }, - { label: 'ブロックしてきているユーザーが含まれる', user: (): User => userBlockingAlice }, - { label: '承認制ユーザーが含まれる', user: (): User => userLocking }, - { label: 'サイレンスユーザーが含まれる', user: (): User => userSilenced }, - { label: 'サスペンドユーザーが(モデレーターが見るときは)含まれる', user: (): User => userSuspended, me: (): User => root }, + { label: '「見つけやすくする」がOFFのユーザーが含まれる', user: () => userNotExplorable }, + { label: 'ミュートユーザーが含まれる', user: () => userMutedByAlice }, + { label: 'ブロックされているユーザーが含まれる', user: () => userBlockedByAlice }, + { label: 'ブロックしてきているユーザーが含まれる', user: () => userBlockingAlice }, + { label: '承認制ユーザーが含まれる', user: () => userLocking }, + { label: 'サイレンスユーザーが含まれる', user: () => userSilenced }, + { label: 'サスペンドユーザーが(モデレーターが見るときは)含まれる', user: () => userSuspended, me: () => root }, // BUG サスペンドユーザーを一般ユーザーから見るとrootユーザーが返ってくる - //{ label: 'サスペンドユーザーが(一般ユーザーが見るときは)含まれない', user: (): User => userSuspended, me: (): User => bob, excluded: true }, - { label: '削除済ユーザーが含まれる', user: (): User => userDeletedBySelf }, - { label: '削除済(byAdmin)ユーザーが含まれる', user: (): User => userDeletedByAdmin }, + //{ label: 'サスペンドユーザーが(一般ユーザーが見るときは)含まれない', user: () => userSuspended, me: () => bob, excluded: true }, + { label: '削除済ユーザーが含まれる', user: () => userDeletedBySelf }, + { label: '削除済(byAdmin)ユーザーが含まれる', user: () => userDeletedByAdmin }, + // @ts-expect-error excluded は上でコメントアウトされているので ] as const)('をID指定のリスト形式で取得することができ、結果に$label', async ({ user, me, excluded }) => { const parameters = { userIds: [user().id] }; const response = await successfulApiCall({ endpoint: 'users/show', parameters, user: me?.() ?? alice }); @@ -762,15 +741,15 @@ describe('ユーザー', () => { assert.deepStrictEqual(response, expected); }); test.each([ - { label: '「見つけやすくする」がOFFのユーザーが含まれる', user: (): User => userNotExplorable }, - { label: 'ミュートユーザーが含まれる', user: (): User => userMutedByAlice }, - { label: 'ブロックされているユーザーが含まれる', user: (): User => userBlockedByAlice }, - { label: 'ブロックしてきているユーザーが含まれる', user: (): User => userBlockingAlice }, - { label: '承認制ユーザーが含まれる', user: (): User => userLocking }, - { label: 'サイレンスユーザーが含まれる', user: (): User => userSilenced }, - { label: 'サスペンドユーザーが含まれない', user: (): User => userSuspended, excluded: true }, - { label: '削除済ユーザーが含まれる', user: (): User => userDeletedBySelf }, - { label: '削除済(byAdmin)ユーザーが含まれる', user: (): User => userDeletedByAdmin }, + { label: '「見つけやすくする」がOFFのユーザーが含まれる', user: () => userNotExplorable }, + { label: 'ミュートユーザーが含まれる', user: () => userMutedByAlice }, + { label: 'ブロックされているユーザーが含まれる', user: () => userBlockedByAlice }, + { label: 'ブロックしてきているユーザーが含まれる', user: () => userBlockingAlice }, + { label: '承認制ユーザーが含まれる', user: () => userLocking }, + { label: 'サイレンスユーザーが含まれる', user: () => userSilenced }, + { label: 'サスペンドユーザーが含まれない', user: () => userSuspended, excluded: true }, + { label: '削除済ユーザーが含まれる', user: () => userDeletedBySelf }, + { label: '削除済(byAdmin)ユーザーが含まれる', user: () => userDeletedByAdmin }, ] as const)('を検索することができ、結果に$labelが含まれる', async ({ user, excluded }) => { const parameters = { query: user().username, limit: 1 }; const response = await successfulApiCall({ endpoint: 'users/search', parameters, user: alice }); @@ -784,30 +763,30 @@ describe('ユーザー', () => { //#region ID指定検索(users/search-by-username-and-host) test.each([ - { label: '自分', parameters: { username: 'alice' }, user: (): User[] => [alice] }, - { label: '自分かつusernameが大文字', parameters: { username: 'ALICE' }, user: (): User[] => [alice] }, - { label: 'ローカルのフォロイーでノートなし', parameters: { username: 'userFollowedByAlice' }, user: (): User[] => [userFollowedByAlice] }, - { label: 'ローカルでノートなしは検索に載らない', parameters: { username: 'userNoNote' }, user: (): User[] => [] }, - { label: 'ローカルの他人1', parameters: { username: 'bob' }, user: (): User[] => [bob] }, - { label: 'ローカルの他人2', parameters: { username: 'bob', host: null }, user: (): User[] => [bob] }, - { label: 'ローカルの他人3', parameters: { username: 'bob', host: '.' }, user: (): User[] => [bob] }, - { label: 'ローカル', parameters: { host: null, limit: 1 }, user: (): User[] => [userFollowedByAlice] }, - { label: 'ローカル', parameters: { host: '.', limit: 1 }, user: (): User[] => [userFollowedByAlice] }, + { label: '自分', parameters: { username: 'alice' }, user: () => [alice] }, + { label: '自分かつusernameが大文字', parameters: { username: 'ALICE' }, user: () => [alice] }, + { label: 'ローカルのフォロイーでノートなし', parameters: { username: 'userFollowedByAlice' }, user: () => [userFollowedByAlice] }, + { label: 'ローカルでノートなしは検索に載らない', parameters: { username: 'userNoNote' }, user: () => [] }, + { label: 'ローカルの他人1', parameters: { username: 'bob' }, user: () => [bob] }, + { label: 'ローカルの他人2', parameters: { username: 'bob', host: null }, user: () => [bob] }, + { label: 'ローカルの他人3', parameters: { username: 'bob', host: '.' }, user: () => [bob] }, + { label: 'ローカル', parameters: { host: null, limit: 1 }, user: () => [userFollowedByAlice] }, + { label: 'ローカル', parameters: { host: '.', limit: 1 }, user: () => [userFollowedByAlice] }, ])('をID&ホスト指定で検索できる($label)', async ({ parameters, user }) => { const response = await successfulApiCall({ endpoint: 'users/search-by-username-and-host', parameters, user: alice }); const expected = await Promise.all(user().map(u => show(u.id, alice))); assert.deepStrictEqual(response, expected); }); test.each([ - { label: '「見つけやすくする」がOFFのユーザーが含まれる', user: (): User => userNotExplorable }, - { label: 'ミュートユーザーが含まれる', user: (): User => userMutedByAlice }, - { label: 'ブロックされているユーザーが含まれる', user: (): User => userBlockedByAlice }, - { label: 'ブロックしてきているユーザーが含まれる', user: (): User => userBlockingAlice }, - { label: '承認制ユーザーが含まれる', user: (): User => userLocking }, - { label: 'サイレンスユーザーが含まれる', user: (): User => userSilenced }, - { label: 'サスペンドユーザーが含まれない', user: (): User => userSuspended, excluded: true }, - { label: '削除済ユーザーが含まれる', user: (): User => userDeletedBySelf }, - { label: '削除済(byAdmin)ユーザーが含まれる', user: (): User => userDeletedByAdmin }, + { label: '「見つけやすくする」がOFFのユーザーが含まれる', user: () => userNotExplorable }, + { label: 'ミュートユーザーが含まれる', user: () => userMutedByAlice }, + { label: 'ブロックされているユーザーが含まれる', user: () => userBlockedByAlice }, + { label: 'ブロックしてきているユーザーが含まれる', user: () => userBlockingAlice }, + { label: '承認制ユーザーが含まれる', user: () => userLocking }, + { label: 'サイレンスユーザーが含まれる', user: () => userSilenced }, + { label: 'サスペンドユーザーが含まれない', user: () => userSuspended, excluded: true }, + { label: '削除済ユーザーが含まれる', user: () => userDeletedBySelf }, + { label: '削除済(byAdmin)ユーザーが含まれる', user: () => userDeletedByAdmin }, ] as const)('をID&ホスト指定で検索でき、結果に$label', async ({ user, excluded }) => { const parameters = { username: user().username }; const response = await successfulApiCall({ endpoint: 'users/search-by-username-and-host', parameters, user: alice }); @@ -829,15 +808,15 @@ describe('ユーザー', () => { assert.deepStrictEqual(response, expected); }); test.each([ - { label: '「見つけやすくする」がOFFのユーザーが含まれる', user: (): User => userNotExplorable }, - { label: 'ミュートユーザーが含まれる', user: (): User => userMutedByAlice }, - { label: 'ブロックされているユーザーが含まれる', user: (): User => userBlockedByAlice }, - { label: 'ブロックしてきているユーザーが含まれない', user: (): User => userBlockingAlice, excluded: true }, - { label: '承認制ユーザーが含まれる', user: (): User => userLocking }, - { label: 'サイレンスユーザーが含まれる', user: (): User => userSilenced }, - //{ label: 'サスペンドユーザーが含まれない', user: (): User => userSuspended, excluded: true }, - { label: '削除済ユーザーが含まれる', user: (): User => userDeletedBySelf }, - { label: '削除済(byAdmin)ユーザーが含まれる', user: (): User => userDeletedByAdmin }, + { label: '「見つけやすくする」がOFFのユーザーが含まれる', user: () => userNotExplorable }, + { label: 'ミュートユーザーが含まれる', user: () => userMutedByAlice }, + { label: 'ブロックされているユーザーが含まれる', user: () => userBlockedByAlice }, + { label: 'ブロックしてきているユーザーが含まれない', user: () => userBlockingAlice, excluded: true }, + { label: '承認制ユーザーが含まれる', user: () => userLocking }, + { label: 'サイレンスユーザーが含まれる', user: () => userSilenced }, + //{ label: 'サスペンドユーザーが含まれない', user: () => userSuspended, excluded: true }, + { label: '削除済ユーザーが含まれる', user: () => userDeletedBySelf }, + { label: '削除済(byAdmin)ユーザーが含まれる', user: () => userDeletedByAdmin }, ] as const)('がよくリプライをするユーザーのリストを取得でき、結果に$label', async ({ user, excluded }) => { const replyTo = (await successfulApiCall({ endpoint: 'users/notes', parameters: { userId: user().id }, user: undefined }))[0]; await post(alice, { text: `@${user().username} test`, replyId: replyTo.id }); @@ -851,12 +830,12 @@ describe('ユーザー', () => { //#region ハッシュタグ(hashtags/users) test.each([ - { label: 'フォロワー昇順', sort: { sort: '+follower' }, selector: (u: UserDetailedNotMe): string => String(u.followersCount) }, - { label: 'フォロワー降順', sort: { sort: '-follower' }, selector: (u: UserDetailedNotMe): string => String(u.followersCount) }, - { label: '登録日時昇順', sort: { sort: '+createdAt' }, selector: (u: UserDetailedNotMe): string => u.createdAt }, - { label: '登録日時降順', sort: { sort: '-createdAt' }, selector: (u: UserDetailedNotMe): string => u.createdAt }, - { label: '投稿日時昇順', sort: { sort: '+updatedAt' }, selector: (u: UserDetailedNotMe): string => String(u.updatedAt) }, - { label: '投稿日時降順', sort: { sort: '-updatedAt' }, selector: (u: UserDetailedNotMe): string => String(u.updatedAt) }, + { label: 'フォロワー昇順', sort: { sort: '+follower' }, selector: (u: misskey.entities.UserDetailedNotMe): string => String(u.followersCount) }, + { label: 'フォロワー降順', sort: { sort: '-follower' }, selector: (u: misskey.entities.UserDetailedNotMe): string => String(u.followersCount) }, + { label: '登録日時昇順', sort: { sort: '+createdAt' }, selector: (u: misskey.entities.UserDetailedNotMe): string => u.createdAt }, + { label: '登録日時降順', sort: { sort: '-createdAt' }, selector: (u: misskey.entities.UserDetailedNotMe): string => u.createdAt }, + { label: '投稿日時昇順', sort: { sort: '+updatedAt' }, selector: (u: misskey.entities.UserDetailedNotMe): string => String(u.updatedAt) }, + { label: '投稿日時降順', sort: { sort: '-updatedAt' }, selector: (u: misskey.entities.UserDetailedNotMe): string => String(u.updatedAt) }, ] as const)('をハッシュタグ指定で取得することができる($label)', async ({ sort, selector }) => { const hashtag = 'test_hashtag'; await successfulApiCall({ endpoint: 'i/update', parameters: { description: `#${hashtag}` }, user: alice }); @@ -870,15 +849,15 @@ describe('ユーザー', () => { assert.deepStrictEqual(response, expected); }); test.each([ - { label: '「見つけやすくする」がOFFのユーザーが含まれる', user: (): User => userNotExplorable }, - { label: 'ミュートユーザーが含まれる', user: (): User => userMutedByAlice }, - { label: 'ブロックされているユーザーが含まれる', user: (): User => userBlockedByAlice }, - { label: 'ブロックしてきているユーザーが含まれる', user: (): User => userBlockingAlice }, - { label: '承認制ユーザーが含まれる', user: (): User => userLocking }, - { label: 'サイレンスユーザーが含まれる', user: (): User => userSilenced }, - { label: 'サスペンドユーザーが含まれない', user: (): User => userSuspended, excluded: true }, - { label: '削除済ユーザーが含まれる', user: (): User => userDeletedBySelf }, - { label: '削除済(byAdmin)ユーザーが含まれる', user: (): User => userDeletedByAdmin }, + { label: '「見つけやすくする」がOFFのユーザーが含まれる', user: () => userNotExplorable }, + { label: 'ミュートユーザーが含まれる', user: () => userMutedByAlice }, + { label: 'ブロックされているユーザーが含まれる', user: () => userBlockedByAlice }, + { label: 'ブロックしてきているユーザーが含まれる', user: () => userBlockingAlice }, + { label: '承認制ユーザーが含まれる', user: () => userLocking }, + { label: 'サイレンスユーザーが含まれる', user: () => userSilenced }, + { label: 'サスペンドユーザーが含まれない', user: () => userSuspended, excluded: true }, + { label: '削除済ユーザーが含まれる', user: () => userDeletedBySelf }, + { label: '削除済(byAdmin)ユーザーが含まれる', user: () => userDeletedByAdmin }, ] as const)('をハッシュタグ指定で取得することができ、結果に$label', async ({ user, excluded }) => { const hashtag = `user_test${user().username}`; if (user() !== userSuspended) { diff --git a/packages/backend/test/global.d.ts b/packages/backend/test/global.d.ts new file mode 100644 index 0000000000..0363073356 --- /dev/null +++ b/packages/backend/test/global.d.ts @@ -0,0 +1,7 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type FIXME = any; diff --git a/packages/backend/test/prelude/get-api-validator.ts b/packages/backend/test/prelude/get-api-validator.ts index b86a7a978d..7aa7a92702 100644 --- a/packages/backend/test/prelude/get-api-validator.ts +++ b/packages/backend/test/prelude/get-api-validator.ts @@ -4,10 +4,10 @@ */ import Ajv from 'ajv'; -import { Schema } from '@/misc/schema'; +import { Schema } from '@/misc/json-schema.js'; export const getValidator = (paramDef: Schema) => { - const ajv = new Ajv({ + const ajv = new Ajv.default({ useDefaults: true, }); ajv.addFormat('misskey:id', /^[a-zA-Z0-9]+$/); diff --git a/packages/backend/test/tsconfig.json b/packages/backend/test/tsconfig.json index 4597ff8780..2b562acda8 100644 --- a/packages/backend/test/tsconfig.json +++ b/packages/backend/test/tsconfig.json @@ -5,7 +5,7 @@ "noImplicitAny": true, "noImplicitReturns": true, "noUnusedParameters": false, - "noUnusedLocals": true, + "noUnusedLocals": false, "noFallthroughCasesInSwitch": true, "declaration": false, "sourceMap": true, @@ -18,6 +18,7 @@ "strict": true, "strictNullChecks": true, "strictPropertyInitialization": false, + "skipLibCheck": true, "experimentalDecorators": true, "emitDecoratorMetadata": true, "resolveJsonModule": true, diff --git a/packages/backend/test/unit/AnnouncementService.ts b/packages/backend/test/unit/AnnouncementService.ts index fc35837420..aa082ff2f2 100644 --- a/packages/backend/test/unit/AnnouncementService.ts +++ b/packages/backend/test/unit/AnnouncementService.ts @@ -51,7 +51,7 @@ describe('AnnouncementService', () => { function createAnnouncement(data: Partial<MiAnnouncement & { createdAt: Date }> = {}) { return announcementsRepository.insert({ - id: genAidx(data.createdAt ?? new Date()), + id: genAidx(data.createdAt?.getTime() ?? Date.now()), updatedAt: null, title: 'Title', text: 'Text', diff --git a/packages/backend/test/unit/FetchInstanceMetadataService.ts b/packages/backend/test/unit/FetchInstanceMetadataService.ts index e6e68ccd6d..bf8f3ab0e3 100644 --- a/packages/backend/test/unit/FetchInstanceMetadataService.ts +++ b/packages/backend/test/unit/FetchInstanceMetadataService.ts @@ -19,8 +19,8 @@ import { DI } from '@/di-symbols.js'; import type { TestingModule } from '@nestjs/testing'; function mockRedis() { - const hash = {}; - const set = jest.fn((key, value) => { + const hash = {} as any; + const set = jest.fn((key: string, value) => { const ret = hash[key]; hash[key] = value; return ret; @@ -56,12 +56,13 @@ describe('FetchInstanceMetadataService', () => { } else if (token === DI.redis) { return mockRedis; } + return null; }) .compile(); app.enableShutdownHooks(); - fetchInstanceMetadataService = app.get<FetchInstanceMetadataService>(FetchInstanceMetadataService); + fetchInstanceMetadataService = app.get<FetchInstanceMetadataService>(FetchInstanceMetadataService) as jest.Mocked<FetchInstanceMetadataService>; federatedInstanceService = app.get<FederatedInstanceService>(FederatedInstanceService) as jest.Mocked<FederatedInstanceService>; redisClient = app.get<Redis>(DI.redis) as jest.Mocked<Redis>; httpRequestService = app.get<HttpRequestService>(HttpRequestService) as jest.Mocked<HttpRequestService>; @@ -74,11 +75,12 @@ describe('FetchInstanceMetadataService', () => { test('Lock and update', async () => { redisClient.set = mockRedis(); const now = Date.now(); - federatedInstanceService.fetch.mockReturnValue({ infoUpdatedAt: { getTime: () => { return now - 10 * 1000 * 60 * 60 * 24; } } }); + federatedInstanceService.fetch.mockResolvedValue({ infoUpdatedAt: { getTime: () => { return now - 10 * 1000 * 60 * 60 * 24; } } } as any); httpRequestService.getJson.mockImplementation(() => { throw Error(); }); const tryLockSpy = jest.spyOn(fetchInstanceMetadataService, 'tryLock'); const unlockSpy = jest.spyOn(fetchInstanceMetadataService, 'unlock'); - await fetchInstanceMetadataService.fetchInstanceMetadata({ host: 'example.com' }); + + await fetchInstanceMetadataService.fetchInstanceMetadata({ host: 'example.com' } as any); expect(tryLockSpy).toHaveBeenCalledTimes(1); expect(unlockSpy).toHaveBeenCalledTimes(1); expect(federatedInstanceService.fetch).toHaveBeenCalledTimes(1); @@ -88,11 +90,12 @@ describe('FetchInstanceMetadataService', () => { test('Lock and don\'t update', async () => { redisClient.set = mockRedis(); const now = Date.now(); - federatedInstanceService.fetch.mockReturnValue({ infoUpdatedAt: { getTime: () => now } }); + federatedInstanceService.fetch.mockResolvedValue({ infoUpdatedAt: { getTime: () => now } } as any); httpRequestService.getJson.mockImplementation(() => { throw Error(); }); const tryLockSpy = jest.spyOn(fetchInstanceMetadataService, 'tryLock'); const unlockSpy = jest.spyOn(fetchInstanceMetadataService, 'unlock'); - await fetchInstanceMetadataService.fetchInstanceMetadata({ host: 'example.com' }); + + await fetchInstanceMetadataService.fetchInstanceMetadata({ host: 'example.com' } as any); expect(tryLockSpy).toHaveBeenCalledTimes(1); expect(unlockSpy).toHaveBeenCalledTimes(1); expect(federatedInstanceService.fetch).toHaveBeenCalledTimes(1); @@ -101,15 +104,33 @@ describe('FetchInstanceMetadataService', () => { test('Do nothing when lock not acquired', async () => { redisClient.set = mockRedis(); - federatedInstanceService.fetch.mockReturnValue({ infoUpdatedAt: { getTime: () => now - 10 * 1000 * 60 * 60 * 24 } }); + const now = Date.now(); + federatedInstanceService.fetch.mockResolvedValue({ infoUpdatedAt: { getTime: () => now - 10 * 1000 * 60 * 60 * 24 } } as any); httpRequestService.getJson.mockImplementation(() => { throw Error(); }); + await fetchInstanceMetadataService.tryLock('example.com'); const tryLockSpy = jest.spyOn(fetchInstanceMetadataService, 'tryLock'); const unlockSpy = jest.spyOn(fetchInstanceMetadataService, 'unlock'); - await fetchInstanceMetadataService.tryLock('example.com'); - await fetchInstanceMetadataService.fetchInstanceMetadata({ host: 'example.com' }); - expect(tryLockSpy).toHaveBeenCalledTimes(2); + + await fetchInstanceMetadataService.fetchInstanceMetadata({ host: 'example.com' } as any); + expect(tryLockSpy).toHaveBeenCalledTimes(1); expect(unlockSpy).toHaveBeenCalledTimes(0); expect(federatedInstanceService.fetch).toHaveBeenCalledTimes(0); expect(httpRequestService.getJson).toHaveBeenCalledTimes(0); }); + + test('Do when lock not acquired but forced', async () => { + redisClient.set = mockRedis(); + const now = Date.now(); + federatedInstanceService.fetch.mockResolvedValue({ infoUpdatedAt: { getTime: () => now - 10 * 1000 * 60 * 60 * 24 } } as any); + httpRequestService.getJson.mockImplementation(() => { throw Error(); }); + await fetchInstanceMetadataService.tryLock('example.com'); + const tryLockSpy = jest.spyOn(fetchInstanceMetadataService, 'tryLock'); + const unlockSpy = jest.spyOn(fetchInstanceMetadataService, 'unlock'); + + await fetchInstanceMetadataService.fetchInstanceMetadata({ host: 'example.com' } as any, true); + expect(tryLockSpy).toHaveBeenCalledTimes(0); + expect(unlockSpy).toHaveBeenCalledTimes(1); + expect(federatedInstanceService.fetch).toHaveBeenCalledTimes(0); + expect(httpRequestService.getJson).toHaveBeenCalled(); + }); }); diff --git a/packages/backend/test/unit/RelayService.ts b/packages/backend/test/unit/RelayService.ts index f2a67dba46..9676abf07b 100644 --- a/packages/backend/test/unit/RelayService.ts +++ b/packages/backend/test/unit/RelayService.ts @@ -90,7 +90,8 @@ describe('RelayService', () => { expect(queueService.deliver).toHaveBeenCalled(); expect(queueService.deliver.mock.lastCall![1]?.type).toBe('Undo'); - expect(queueService.deliver.mock.lastCall![1]?.object.type).toBe('Follow'); + expect(typeof queueService.deliver.mock.lastCall![1]?.object).toBe('object'); + expect((queueService.deliver.mock.lastCall![1]?.object as any).type).toBe('Follow'); expect(queueService.deliver.mock.lastCall![2]).toBe('https://example.com'); //expect(queueService.deliver.mock.lastCall![0].username).toBe('relay.actor'); diff --git a/packages/backend/test/unit/RoleService.ts b/packages/backend/test/unit/RoleService.ts index fe5ad31597..19d03570e0 100644 --- a/packages/backend/test/unit/RoleService.ts +++ b/packages/backend/test/unit/RoleService.ts @@ -228,11 +228,14 @@ describe('RoleService', () => { }, target: 'conditional', condFormula: { + id: '232a4221-9816-49a6-a967-ae0fac52ec5e', type: 'and', values: [{ + id: '2a37ef43-2d93-4c4d-87f6-f2fdb7d9b530', type: 'followersMoreThanOrEq', value: 10, }, { + id: '1bd67839-b126-4f92-bad0-4e285dab453b', type: 'createdMoreThan', sec: 60 * 60 * 24 * 7, }], diff --git a/packages/backend/test/utils.ts b/packages/backend/test/utils.ts index cd5dddd68d..86814fffe0 100644 --- a/packages/backend/test/utils.ts +++ b/packages/backend/test/utils.ts @@ -9,11 +9,10 @@ import { basename, isAbsolute } from 'node:path'; import { randomUUID } from 'node:crypto'; import { inspect } from 'node:util'; import WebSocket, { ClientOptions } from 'ws'; -import fetch, { File, RequestInit } from 'node-fetch'; +import fetch, { File, RequestInit, type Headers } from 'node-fetch'; import { DataSource } from 'typeorm'; import { JSDOM } from 'jsdom'; import { DEFAULT_POLICIES } from '@/core/RoleService.js'; -import { Packed } from '@/misc/json-schema.js'; import { validateContentTypeSetAsActivityPub } from '@/core/activitypub/misc/validator.js'; import { entities } from '../src/postgres.js'; import { loadConfig } from '../src/config.js'; @@ -21,7 +20,7 @@ import type * as misskey from 'misskey-js'; export { server as startServer, jobQueue as startJobQueue } from '@/boot/common.js'; -interface UserToken { +export interface UserToken { token: string; bearer?: boolean; } @@ -35,20 +34,15 @@ export const cookie = (me: UserToken): string => { return `token=${me.token};`; }; -export const api = async (endpoint: string, params: any, me?: UserToken) => { - const normalized = endpoint.replace(/^\//, ''); - return await request(`api/${normalized}`, params, me); -}; - -export type ApiRequest = { - endpoint: string, - parameters: object, +export type ApiRequest<E extends keyof misskey.Endpoints, P extends misskey.Endpoints[E]['req'] = misskey.Endpoints[E]['req']> = { + endpoint: E, + parameters: P, user: UserToken | undefined, }; -export const successfulApiCall = async <T, >(request: ApiRequest, assertion: { +export const successfulApiCall = async <E extends keyof misskey.Endpoints, P extends misskey.Endpoints[E]['req']>(request: ApiRequest<E, P>, assertion: { status?: number, -} = {}): Promise<T> => { +} = {}): Promise<misskey.api.SwitchCaseResponseType<E, P>> => { const { endpoint, parameters, user } = request; const res = await api(endpoint, parameters, user); const status = assertion.status ?? (res.body == null ? 204 : 200); @@ -56,7 +50,7 @@ export const successfulApiCall = async <T, >(request: ApiRequest, assertion: { return res.body; }; -export const failedApiCall = async <T, >(request: ApiRequest, assertion: { +export const failedApiCall = async <T, E extends keyof misskey.Endpoints, P extends misskey.Endpoints[E]['req']>(request: ApiRequest<E, P>, assertion: { status: number, code: string, id: string @@ -70,7 +64,7 @@ export const failedApiCall = async <T, >(request: ApiRequest, assertion: { return res.body; }; -const request = async (path: string, params: any, me?: UserToken): Promise<{ +export const api = async <E extends keyof misskey.Endpoints>(path: E, params: misskey.Endpoints[E]['req'], me?: UserToken): Promise<{ status: number, headers: Headers, body: any @@ -86,7 +80,7 @@ const request = async (path: string, params: any, me?: UserToken): Promise<{ bodyAuth.i = me.token; } - const res = await relativeFetch(path, { + const res = await relativeFetch(`api/${path}`, { method: 'POST', headers, body: JSON.stringify(Object.assign(bodyAuth, params)), @@ -141,7 +135,7 @@ export const signup = async (params?: Partial<misskey.Endpoints['signup']['req'] return res.body; }; -export const post = async (user: UserToken, params?: misskey.Endpoints['notes/create']['req']): Promise<misskey.entities.Note> => { +export const post = async (user: UserToken, params: misskey.Endpoints['notes/create']['req']): Promise<misskey.entities.Note> => { const q = params; const res = await api('notes/create', q, user); @@ -159,8 +153,8 @@ export const createAppToken = async (user: UserToken, permissions: (typeof missk }; // 非公開ノートをAPI越しに見たときのノート NoteEntityService.ts -export const hiddenNote = (note: any): any => { - const temp = { +export const hiddenNote = (note: misskey.entities.Note): misskey.entities.Note => { + const temp: misskey.entities.Note = { ...note, fileIds: [], files: [], @@ -173,21 +167,22 @@ export const hiddenNote = (note: any): any => { return temp; }; -export const react = async (user: UserToken, note: any, reaction: string): Promise<any> => { +export const react = async (user: UserToken, note: misskey.entities.Note, reaction: string): Promise<void> => { await api('notes/reactions/create', { noteId: note.id, reaction: reaction, }, user); }; -export const userList = async (user: UserToken, userList: any = {}): Promise<any> => { +export const userList = async (user: UserToken, userList: Partial<misskey.entities.UserList> = {}): Promise<misskey.entities.UserList> => { const res = await api('users/lists/create', { name: 'test', + ...userList, }, user); return res.body; }; -export const page = async (user: UserToken, page: any = {}): Promise<any> => { +export const page = async (user: UserToken, page: Partial<misskey.entities.Page> = {}): Promise<misskey.entities.Page> => { const res = await api('pages/create', { alignCenter: false, content: [ @@ -198,7 +193,7 @@ export const page = async (user: UserToken, page: any = {}): Promise<any> => { }, ], eyeCatchingImageId: null, - font: 'sans-serif', + font: 'sans-serif' as any, hideTitleWhenPinned: false, name: '1678594845072', script: '', @@ -210,7 +205,7 @@ export const page = async (user: UserToken, page: any = {}): Promise<any> => { return res.body; }; -export const play = async (user: UserToken, play: any = {}): Promise<any> => { +export const play = async (user: UserToken, play: Partial<misskey.entities.Flash> = {}): Promise<misskey.entities.Flash> => { const res = await api('flash/create', { permissions: [], script: 'test', @@ -221,7 +216,7 @@ export const play = async (user: UserToken, play: any = {}): Promise<any> => { return res.body; }; -export const clip = async (user: UserToken, clip: any = {}): Promise<any> => { +export const clip = async (user: UserToken, clip: Partial<misskey.entities.Clip> = {}): Promise<misskey.entities.Clip> => { const res = await api('clips/create', { description: null, isPublic: true, @@ -231,18 +226,18 @@ export const clip = async (user: UserToken, clip: any = {}): Promise<any> => { return res.body; }; -export const galleryPost = async (user: UserToken, channel: any = {}): Promise<any> => { +export const galleryPost = async (user: UserToken, galleryPost: Partial<misskey.entities.GalleryPost> = {}): Promise<misskey.entities.GalleryPost> => { const res = await api('gallery/posts/create', { description: null, fileIds: [], isSensitive: false, title: 'test', - ...channel, + ...galleryPost, }, user); return res.body; }; -export const channel = async (user: UserToken, channel: any = {}): Promise<any> => { +export const channel = async (user: UserToken, channel: Partial<misskey.entities.Channel> = {}): Promise<misskey.entities.Channel> => { const res = await api('channels/create', { bannerId: null, description: null, @@ -252,7 +247,7 @@ export const channel = async (user: UserToken, channel: any = {}): Promise<any> return res.body; }; -export const role = async (user: UserToken, role: any = {}, policies: any = {}): Promise<any> => { +export const role = async (user: UserToken, role: Partial<misskey.entities.Role> = {}, policies: any = {}): Promise<misskey.entities.Role> => { const res = await api('admin/roles/create', { asBadge: false, canEditMembersByModerator: false, @@ -260,7 +255,7 @@ export const role = async (user: UserToken, role: any = {}, policies: any = {}): condFormula: { id: 'ebef1684-672d-49b6-ad82-1b3ec3784f85', type: 'isRemote', - }, + } as any, description: '', displayOrder: 0, iconUrl: null, @@ -298,7 +293,7 @@ interface UploadOptions { export const uploadFile = async (user?: UserToken, { path, name, blob }: UploadOptions = {}): Promise<{ status: number, headers: Headers, - body: misskey.Endpoints['drive/files/create']['res'] | null + body: misskey.entities.DriveFile | null }> => { const absPath = path == null ? new URL('resources/Lenna.jpg', import.meta.url) @@ -335,14 +330,14 @@ export const uploadFile = async (user?: UserToken, { path, name, blob }: UploadO }; }; -export const uploadUrl = async (user: UserToken, url: string): Promise<Packed<'DriveFile'>> => { +export const uploadUrl = async (user: UserToken, url: string): Promise<misskey.entities.DriveFile> => { const marker = Math.random().toString(); const catcher = makeStreamCatcher( user, 'main', (msg) => msg.type === 'urlUploadFinished' && msg.body.marker === marker, - (msg) => msg.body.file as Packed<'DriveFile'>, + (msg) => msg.body.file, 60 * 1000, ); diff --git a/packages/frontend/src/boot/main-boot.ts b/packages/frontend/src/boot/main-boot.ts index 34612fee73..18dfdcdc51 100644 --- a/packages/frontend/src/boot/main-boot.ts +++ b/packages/frontend/src/boot/main-boot.ts @@ -189,14 +189,26 @@ export async function mainBoot() { if ($i.followersCount >= 500) claimAchievement('followers500'); if ($i.followersCount >= 1000) claimAchievement('followers1000'); - if (Date.now() - new Date($i.createdAt).getTime() > 1000 * 60 * 60 * 24 * 365) { - claimAchievement('passedSinceAccountCreated1'); - } - if (Date.now() - new Date($i.createdAt).getTime() > 1000 * 60 * 60 * 24 * 365 * 2) { - claimAchievement('passedSinceAccountCreated2'); - } - if (Date.now() - new Date($i.createdAt).getTime() > 1000 * 60 * 60 * 24 * 365 * 3) { + const createdAt = new Date($i.createdAt); + const createdAtThreeYearsLater = new Date($i.createdAt); + createdAtThreeYearsLater.setFullYear(createdAtThreeYearsLater.getFullYear() + 3); + if (now >= createdAtThreeYearsLater) { claimAchievement('passedSinceAccountCreated3'); + claimAchievement('passedSinceAccountCreated2'); + claimAchievement('passedSinceAccountCreated1'); + } else { + const createdAtTwoYearsLater = new Date($i.createdAt); + createdAtTwoYearsLater.setFullYear(createdAtTwoYearsLater.getFullYear() + 2); + if (now >= createdAtTwoYearsLater) { + claimAchievement('passedSinceAccountCreated2'); + claimAchievement('passedSinceAccountCreated1'); + } else { + const createdAtOneYearLater = new Date($i.createdAt); + createdAtOneYearLater.setFullYear(createdAtOneYearLater.getFullYear() + 1); + if (now >= createdAtOneYearLater) { + claimAchievement('passedSinceAccountCreated1'); + } + } } if (claimedAchievements.length >= 30) { @@ -231,7 +243,7 @@ export async function mainBoot() { const latestDonationInfoShownAt = miLocalStorage.getItem('latestDonationInfoShownAt'); const neverShowDonationInfo = miLocalStorage.getItem('neverShowDonationInfo'); - if (neverShowDonationInfo !== 'true' && (new Date($i.createdAt).getTime() < (Date.now() - (1000 * 60 * 60 * 24 * 3))) && !location.pathname.startsWith('/miauth')) { + if (neverShowDonationInfo !== 'true' && (createdAt.getTime() < (Date.now() - (1000 * 60 * 60 * 24 * 3))) && !location.pathname.startsWith('/miauth')) { if (latestDonationInfoShownAt == null || (new Date(latestDonationInfoShownAt).getTime() < (Date.now() - (1000 * 60 * 60 * 24 * 30)))) { popup(defineAsyncComponent(() => import('@/components/MkDonation.vue')), {}, {}, 'closed'); } diff --git a/packages/frontend/src/components/MkMediaAudio.vue b/packages/frontend/src/components/MkMediaAudio.vue index 6351f5cfbe..23539427e3 100644 --- a/packages/frontend/src/components/MkMediaAudio.vue +++ b/packages/frontend/src/components/MkMediaAudio.vue @@ -69,7 +69,7 @@ import * as os from '@/os.js'; import bytes from '@/filters/bytes.js'; import { hms } from '@/filters/hms.js'; import MkMediaRange from '@/components/MkMediaRange.vue'; -import { iAmModerator } from '@/account.js'; +import { $i, iAmModerator } from '@/account.js'; const props = defineProps<{ audio: Misskey.entities.DriveFile; @@ -99,8 +99,6 @@ function showMenu(ev: MouseEvent) { if (iAmModerator) { menu.push({ - type: 'divider', - }, { text: props.audio.isSensitive ? i18n.ts.unmarkAsSensitive : i18n.ts.markAsSensitive, icon: props.audio.isSensitive ? 'ph-eye ph-bold ph-lg' : 'ph-eye-slash ph-bold ph-lg', danger: true, @@ -108,6 +106,17 @@ function showMenu(ev: MouseEvent) { }); } + if ($i?.id === props.audio.userId) { + menu.push({ + type: 'divider', + }, { + type: 'link' as const, + text: i18n.ts._fileViewer.title, + icon: 'ti ti-info-circle', + to: `/my/drive/file/${props.audio.id}`, + }); + } + menuShowing.value = true; os.popupMenu(menu, ev.currentTarget ?? ev.target, { align: 'right', diff --git a/packages/frontend/src/components/MkMediaImage.vue b/packages/frontend/src/components/MkMediaImage.vue index 3f9cff8b71..af9d7d824c 100644 --- a/packages/frontend/src/components/MkMediaImage.vue +++ b/packages/frontend/src/components/MkMediaImage.vue @@ -60,7 +60,7 @@ import ImgWithBlurhash from '@/components/MkImgWithBlurhash.vue'; import { defaultStore } from '@/store.js'; import { i18n } from '@/i18n.js'; import * as os from '@/os.js'; -import { iAmModerator } from '@/account.js'; +import { $i, iAmModerator } from '@/account.js'; const props = withDefaults(defineProps<{ image: Misskey.entities.DriveFile; @@ -115,6 +115,13 @@ function showMenu(ev: MouseEvent) { action: () => { os.apiWithDialog('drive/files/update', { fileId: props.image.id, isSensitive: true }); }, + }] : []), ...($i?.id === props.image.userId ? [{ + type: 'divider' as const, + }, { + type: 'link' as const, + text: i18n.ts._fileViewer.title, + icon: 'ti ti-info-circle', + to: `/my/drive/file/${props.image.id}`, }] : [])], ev.currentTarget ?? ev.target); } diff --git a/packages/frontend/src/components/MkMediaVideo.vue b/packages/frontend/src/components/MkMediaVideo.vue index 7c14ade130..c64d87e8d6 100644 --- a/packages/frontend/src/components/MkMediaVideo.vue +++ b/packages/frontend/src/components/MkMediaVideo.vue @@ -97,7 +97,7 @@ import * as os from '@/os.js'; import { isFullscreenNotSupported } from '@/scripts/device-kind.js'; import hasAudio from '@/scripts/media-has-audio.js'; import MkMediaRange from '@/components/MkMediaRange.vue'; -import { iAmModerator } from '@/account.js'; +import { $i, iAmModerator } from '@/account.js'; const props = defineProps<{ video: Misskey.entities.DriveFile; @@ -125,8 +125,6 @@ function showMenu(ev: MouseEvent) { if (iAmModerator) { menu.push({ - type: 'divider', - }, { text: props.video.isSensitive ? i18n.ts.unmarkAsSensitive : i18n.ts.markAsSensitive, icon: props.video.isSensitive ? 'ph-eye ph-bold ph-lg' : 'ph-eye-slash ph-bold ph-lg', danger: true, @@ -134,6 +132,17 @@ function showMenu(ev: MouseEvent) { }); } + if ($i?.id === props.video.userId) { + menu.push({ + type: 'divider', + }, { + type: 'link' as const, + text: i18n.ts._fileViewer.title, + icon: 'ti ti-info-circle', + to: `/my/drive/file/${props.video.id}`, + }); + } + menuShowing.value = true; os.popupMenu(menu, ev.currentTarget ?? ev.target, { align: 'right', diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue index 9a667c3118..430f0269b8 100644 --- a/packages/frontend/src/components/MkNote.vue +++ b/packages/frontend/src/components/MkNote.vue @@ -97,7 +97,7 @@ SPDX-License-Identifier: AGPL-3.0-only </div> <MkA v-if="appearNote.channel && !inChannel" :class="$style.channel" :to="`/channels/${appearNote.channel.id}`"><i class="ph-television ph-bold ph-lg"></i> {{ appearNote.channel.name }}</MkA> </div> - <MkReactionsViewer :note="appearNote" :maxNumber="16" @click.stop @mockUpdateMyReaction="emitUpdReaction"> + <MkReactionsViewer v-if="appearNote.reactionAcceptance !== 'likeOnly'" :note="appearNote" :maxNumber="16" @click.stop @mockUpdateMyReaction="emitUpdReaction"> <template #more> <div :class="$style.reactionOmitted">{{ i18n.ts.more }}</div> </template> @@ -105,7 +105,7 @@ SPDX-License-Identifier: AGPL-3.0-only <footer :class="$style.footer"> <button :class="$style.footerButton" class="_button" @click.stop @click="reply()"> <i class="ph-arrow-u-up-left ph-bold ph-lg"></i> - <p v-if="appearNote.repliesCount > 0" :class="$style.footerButtonCount">{{ appearNote.repliesCount }}</p> + <p v-if="appearNote.repliesCount > 0" :class="$style.footerButtonCount">{{ number(appearNote.repliesCount) }}</p> </button> <button v-if="canRenote" @@ -117,7 +117,7 @@ SPDX-License-Identifier: AGPL-3.0-only @mousedown="renoted ? undoRenote(appearNote) : boostVisibility()" > <i class="ph-rocket-launch ph-bold ph-lg"></i> - <p v-if="appearNote.renoteCount > 0" :class="$style.footerButtonCount">{{ appearNote.renoteCount }}</p> + <p v-if="appearNote.renoteCount > 0" :class="$style.footerButtonCount">{{ number(appearNote.renoteCount) }}</p> </button> <button v-else :class="$style.footerButton" class="_button" disabled> <i class="ph-prohibit ph-bold ph-lg"></i> @@ -135,12 +135,12 @@ SPDX-License-Identifier: AGPL-3.0-only <button v-if="appearNote.myReaction == null && appearNote.reactionAcceptance !== 'likeOnly'" ref="likeButton" :class="$style.footerButton" class="_button" @click.stop @click="like()"> <i class="ph-heart ph-bold ph-lg"></i> </button> - <button v-if="appearNote.myReaction == null" ref="reactButton" :class="$style.footerButton" class="_button" @mousedown="react()"> - <i v-if="appearNote.reactionAcceptance === 'likeOnly'" class="ph-heart ph-bold ph-lg"></i> + <button ref="reactButton" :class="$style.footerButton" class="_button" @click="toggleReact()"> + <i v-if="appearNote.reactionAcceptance === 'likeOnly' && appearNote.myReaction != null" class="ph-heart ph-bold ph-lg" style="color: var(--eventReactionHeart);"></i> + <i v-else-if="appearNote.myReaction != null" class="ph-minus ph-bold ph-lg" style="color: var(--accent);"></i> + <i v-else-if="appearNote.reactionAcceptance === 'likeOnly'" class="ph-heart ph-bold ph-lg"></i> <i v-else class="ph-smiley ph-bold ph-lg"></i> - </button> - <button v-if="appearNote.myReaction != null" ref="reactButton" :class="$style.footerButton" class="_button" @click.stop @click="undoReact(appearNote)"> - <i class="ph-minus ph-bold ph-lg"></i> + <p v-if="appearNote.reactionCount > 0" :class="$style.footerButtonCount">{{ number(appearNote.reactionCount) }}</p> </button> <button v-if="defaultStore.state.showClipButtonInNoteFooter" ref="clipButton" :class="$style.footerButton" class="_button" @mousedown="clip()"> <i class="ph-paperclip ph-bold ph-lg"></i> @@ -195,6 +195,7 @@ import { pleaseLogin } from '@/scripts/please-login.js'; import { focusPrev, focusNext } from '@/scripts/focus.js'; import { checkWordMute } from '@/scripts/check-word-mute.js'; import { userPage } from '@/filters/user.js'; +import number from '@/filters/number.js'; import * as os from '@/os.js'; import * as sound from '@/scripts/sound.js'; import { misskeyApi } from '@/scripts/misskey-api.js'; @@ -626,6 +627,14 @@ function undoRenote(note) : void { } } +function toggleReact() { + if (appearNote.value.myReaction == null) { + react(); + } else { + undoReact(appearNote.value); + } +} + function onContextmenu(ev: MouseEvent): void { if (props.mock) { return; diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue index 3d15f69f73..a901010776 100644 --- a/packages/frontend/src/components/MkNoteDetailed.vue +++ b/packages/frontend/src/components/MkNoteDetailed.vue @@ -113,10 +113,10 @@ SPDX-License-Identifier: AGPL-3.0-only <MkTime :time="appearNote.createdAt" mode="detail" colored/> </MkA> </div> - <MkReactionsViewer ref="reactionsViewer" :note="appearNote"/> + <MkReactionsViewer v-if="appearNote.reactionAcceptance !== 'likeOnly'" ref="reactionsViewer" :note="appearNote"/> <button class="_button" :class="$style.noteFooterButton" @click="reply()"> <i class="ph-arrow-u-up-left ph-bold ph-lg"></i> - <p v-if="appearNote.repliesCount > 0" :class="$style.noteFooterButtonCount">{{ appearNote.repliesCount }}</p> + <p v-if="appearNote.repliesCount > 0" :class="$style.noteFooterButtonCount">{{ number(appearNote.repliesCount) }}</p> </button> <button v-if="canRenote" @@ -127,7 +127,7 @@ SPDX-License-Identifier: AGPL-3.0-only @mousedown="renoted ? undoRenote() : boostVisibility()" > <i class="ph-rocket-launch ph-bold ph-lg"></i> - <p v-if="appearNote.renoteCount > 0" :class="$style.noteFooterButtonCount">{{ appearNote.renoteCount }}</p> + <p v-if="appearNote.renoteCount > 0" :class="$style.noteFooterButtonCount">{{ number(appearNote.renoteCount) }}</p> </button> <button v-else class="_button" :class="$style.noteFooterButton" disabled> <i class="ph-prohibit ph-bold ph-lg"></i> @@ -144,12 +144,12 @@ SPDX-License-Identifier: AGPL-3.0-only <button v-if="appearNote.myReaction == null && appearNote.reactionAcceptance !== 'likeOnly'" ref="likeButton" :class="$style.noteFooterButton" class="_button" @mousedown="like()"> <i class="ph-heart ph-bold ph-lg"></i> </button> - <button v-if="appearNote.myReaction == null" ref="reactButton" :class="$style.noteFooterButton" class="_button" @mousedown="react()"> - <i v-if="appearNote.reactionAcceptance === 'likeOnly'" class="ph-heart ph-bold ph-lg"></i> + <button ref="reactButton" :class="$style.noteFooterButton" class="_button" @click="toggleReact()"> + <i v-if="appearNote.reactionAcceptance === 'likeOnly' && appearNote.myReaction != null" class="ph-heart ph-bold ph-lg" style="color: var(--eventReactionHeart);"></i> + <i v-else-if="appearNote.myReaction != null" class="ph-minus ph-bold ph-lg" style="color: var(--accent);"></i> + <i v-else-if="appearNote.reactionAcceptance === 'likeOnly'" class="ph-heart ph-bold ph-lg"></i> <i v-else class="ph-smiley ph-bold ph-lg"></i> - </button> - <button v-if="appearNote.myReaction != null" ref="reactButton" class="_button" :class="[$style.noteFooterButton, $style.reacted]" @click="undoReact(appearNote)"> - <i class="ph-minus ph-bold ph-lg"></i> + <p v-if="appearNote.reactionCount > 0" :class="$style.noteFooterButtonCount">{{ number(appearNote.reactionCount) }}</p> </button> <button v-if="defaultStore.state.showClipButtonInNoteFooter" ref="clipButton" class="_button" :class="$style.noteFooterButton" @mousedown="clip()"> <i class="ph-paperclip ph-bold ph-lg"></i> @@ -236,6 +236,7 @@ import { pleaseLogin } from '@/scripts/please-login.js'; import { checkWordMute } from '@/scripts/check-word-mute.js'; import { userPage } from '@/filters/user.js'; import { notePage } from '@/filters/note.js'; +import number from '@/filters/number.js'; import * as os from '@/os.js'; import { misskeyApi } from '@/scripts/misskey-api.js'; import * as sound from '@/scripts/sound.js'; @@ -594,11 +595,11 @@ function like(): void { } } -function undoReact(note): void { - const oldReaction = note.myReaction; +function undoReact(targetNote: Misskey.entities.Note): void { + const oldReaction = targetNote.myReaction; if (!oldReaction) return; misskeyApi('notes/reactions/delete', { - noteId: note.id, + noteId: targetNote.id, }); } @@ -619,6 +620,14 @@ function undoRenote() : void { } } +function toggleReact() { + if (appearNote.value.myReaction == null) { + react(); + } else { + undoReact(appearNote.value); + } +} + function onContextmenu(ev: MouseEvent): void { const isLink = (el: HTMLElement): boolean => { if (el.tagName === 'A') return true; diff --git a/packages/frontend/src/components/MkNotification.vue b/packages/frontend/src/components/MkNotification.vue index 562cc38bf3..656e6f6635 100644 --- a/packages/frontend/src/components/MkNotification.vue +++ b/packages/frontend/src/components/MkNotification.vue @@ -8,6 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only <div :class="$style.head"> <MkAvatar v-if="['pollEnded', 'note', 'edited'].includes(notification.type) && notification.note" :class="$style.icon" :user="notification.note.user" link preview/> <MkAvatar v-else-if="['roleAssigned', 'achievementEarned'].includes(notification.type)" :class="$style.icon" :user="$i" link preview/> + <div v-else-if="notification.type === 'reaction:grouped' && notification.note.reactionAcceptance === 'likeOnly'" :class="[$style.icon, $style.icon_reactionGroup]"><i class="ph-smiley ph-bold ph-lg" style="line-height: 1;"></i></div> <div v-else-if="notification.type === 'reaction:grouped'" :class="[$style.icon, $style.icon_reactionGroup]"><i class="ph-smiley ph-bold ph-lg" style="line-height: 1;"></i></div> <div v-else-if="notification.type === 'renote:grouped'" :class="[$style.icon, $style.icon_renoteGroup]"><i class="ph-rocket-launch ph-bold ph-lg" style="line-height: 1;"></i></div> <img v-else-if="notification.type === 'test'" :class="$style.icon" :src="infoImageUrl"/> @@ -60,6 +61,7 @@ SPDX-License-Identifier: AGPL-3.0-only <span v-else-if="notification.type === 'achievementEarned'">{{ i18n.ts._notification.achievementEarned }}</span> <span v-else-if="notification.type === 'test'">{{ i18n.ts._notification.testNotification }}</span> <MkA v-else-if="notification.type === 'follow' || notification.type === 'mention' || notification.type === 'reply' || notification.type === 'renote' || notification.type === 'quote' || notification.type === 'reaction' || notification.type === 'receiveFollowRequest' || notification.type === 'followRequestAccepted'" v-user-preview="notification.user.id" :class="$style.headerName" :to="userPage(notification.user)"><MkUserName :user="notification.user"/></MkA> + <span v-else-if="notification.type === 'reaction:grouped' && notification.note.reactionAcceptance === 'likeOnly'">{{ i18n.tsx._notification.likedBySomeUsers({ n: notification.reactions.length }) }}</span> <span v-else-if="notification.type === 'reaction:grouped'">{{ i18n.tsx._notification.reactedBySomeUsers({ n: notification.reactions.length }) }}</span> <span v-else-if="notification.type === 'renote:grouped'">{{ i18n.tsx._notification.renotedBySomeUsers({ n: notification.users.length }) }}</span> <span v-else-if="notification.type === 'app'">{{ notification.header }}</span> @@ -211,6 +213,7 @@ const rejectFollowRequest = () => { } .icon_reactionGroup, +.icon_reactionGroupHeart, .icon_renoteGroup { display: grid; align-items: center; @@ -223,11 +226,15 @@ const rejectFollowRequest = () => { } .icon_reactionGroup { - background: #e99a0b; + background: var(--eventReaction); +} + +.icon_reactionGroupHeart { + background: var(--eventReactionHeart); } .icon_renoteGroup { - background: #36d298; + background: var(--eventRenote); } .icon_app { @@ -256,49 +263,49 @@ const rejectFollowRequest = () => { .t_follow, .t_followRequestAccepted, .t_receiveFollowRequest { padding: 3px; - background: #36aed2; + background: var(--eventFollow); pointer-events: none; } .t_renote { padding: 3px; - background: #36d298; + background: var(--eventRenote); pointer-events: none; } .t_quote { padding: 3px; - background: #36d298; + background: var(--eventRenote); pointer-events: none; } .t_reply { padding: 3px; - background: #007aff; + background: var(--eventReply); pointer-events: none; } .t_mention { padding: 3px; - background: #88a6b7; + background: var(--eventOther); pointer-events: none; } .t_pollEnded { padding: 3px; - background: #88a6b7; + background: var(--eventOther); pointer-events: none; } .t_achievementEarned { padding: 3px; - background: #cb9a11; + background: var(--eventAchievement); pointer-events: none; } .t_roleAssigned { padding: 3px; - background: #88a6b7; + background: var(--eventOther); pointer-events: none; } diff --git a/packages/frontend/src/components/MkTutorialDialog.Note.vue b/packages/frontend/src/components/MkTutorialDialog.Note.vue index 5544434b5f..57216058fd 100644 --- a/packages/frontend/src/components/MkTutorialDialog.Note.vue +++ b/packages/frontend/src/components/MkTutorialDialog.Note.vue @@ -63,6 +63,7 @@ const exampleNote = reactive<Misskey.entities.Note>({ reactionAcceptance: null, renoteCount: 0, repliesCount: 1, + reactionCount: 0, reactions: {}, reactionEmojis: {}, fileIds: [], diff --git a/packages/frontend/src/components/MkTutorialDialog.PostNote.vue b/packages/frontend/src/components/MkTutorialDialog.PostNote.vue index 1771559a9b..b0561d4bae 100644 --- a/packages/frontend/src/components/MkTutorialDialog.PostNote.vue +++ b/packages/frontend/src/components/MkTutorialDialog.PostNote.vue @@ -68,6 +68,7 @@ const exampleCWNote = reactive<Misskey.entities.Note>({ reactionAcceptance: null, renoteCount: 0, repliesCount: 1, + reactionCount: 0, reactions: {}, reactionEmojis: {}, fileIds: [], diff --git a/packages/frontend/src/components/MkTutorialDialog.Sensitive.vue b/packages/frontend/src/components/MkTutorialDialog.Sensitive.vue index 4b4e8ea8f8..f155ad7bcb 100644 --- a/packages/frontend/src/components/MkTutorialDialog.Sensitive.vue +++ b/packages/frontend/src/components/MkTutorialDialog.Sensitive.vue @@ -58,6 +58,7 @@ const exampleNote = reactive<Misskey.entities.Note>({ reactionAcceptance: null, renoteCount: 0, repliesCount: 1, + reactionCount: 0, reactions: {}, reactionEmojis: {}, fileIds: ['0000000002'], diff --git a/packages/frontend/src/components/global/MkAd.vue b/packages/frontend/src/components/global/MkAd.vue index f13a161ae8..c01211443d 100644 --- a/packages/frontend/src/components/global/MkAd.vue +++ b/packages/frontend/src/components/global/MkAd.vue @@ -14,10 +14,20 @@ SPDX-License-Identifier: AGPL-3.0-only [$style.form_vertical]: chosen.place === 'vertical', }]" > - <a :href="chosen.url" target="_blank" :class="$style.link"> + <component + :is="self ? 'MkA' : 'a'" + :class="$style.link" + v-bind="self ? { + to: chosen.url.substring(local.length), + } : { + href: chosen.url, + rel: 'nofollow noopener', + target: '_blank', + }" + > <img :src="chosen.imageUrl" :class="$style.img"> <button class="_button" :class="$style.i" @click.prevent.stop="toggleMenu"><i :class="$style.iIcon" class="ph-info ph-bold ph-lg"></i></button> - </a> + </component> </div> <div v-else :class="$style.menu"> <div :class="$style.menuContainer"> @@ -32,10 +42,10 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { ref } from 'vue'; +import { ref, computed } from 'vue'; import { i18n } from '@/i18n.js'; import { instance } from '@/instance.js'; -import { host } from '@/config.js'; +import { url as local, host } from '@/config.js'; import MkButton from '@/components/MkButton.vue'; import { defaultStore } from '@/store.js'; import * as os from '@/os.js'; @@ -96,6 +106,9 @@ const choseAd = (): Ad | null => { }; const chosen = ref(choseAd()); + +const self = computed(() => chosen.value?.url.startsWith(local)); + const shouldHide = ref(!defaultStore.state.forceShowAds && $i && $i.policies.canHideAds && (props.specify == null)); function reduceFrequency(): void { diff --git a/packages/frontend/src/nirax.ts b/packages/frontend/src/nirax.ts index 616fb104e6..6a8ea09ed6 100644 --- a/packages/frontend/src/nirax.ts +++ b/packages/frontend/src/nirax.ts @@ -373,7 +373,7 @@ export class Router extends EventEmitter<RouterEvent> implements IRouter { this.currentRoute.value = res.route; this.currentKey = res.route.globalCacheKey ?? key ?? path; - if (emitChange) { + if (emitChange && res.route.path !== '/:(*)') { this.emit('change', { beforePath, path, @@ -408,13 +408,17 @@ export class Router extends EventEmitter<RouterEvent> implements IRouter { if (cancel) return; } const res = this.navigate(path, null); - this.emit('push', { - beforePath, - path: res._parsedRoute.fullPath, - route: res.route, - props: res.props, - key: this.currentKey, - }); + if (res.route.path === '/:(*)') { + location.href = path; + } else { + this.emit('push', { + beforePath, + path: res._parsedRoute.fullPath, + route: res.route, + props: res.props, + key: this.currentKey, + }); + } } public replace(path: string, key?: string | null) { diff --git a/packages/frontend/src/scripts/use-note-capture.ts b/packages/frontend/src/scripts/use-note-capture.ts index 3baa45d50f..1b3626bff5 100644 --- a/packages/frontend/src/scripts/use-note-capture.ts +++ b/packages/frontend/src/scripts/use-note-capture.ts @@ -54,6 +54,7 @@ export function useNoteCapture(props: { const currentCount = (note.value.reactions || {})[reaction] || 0; note.value.reactions[reaction] = currentCount + 1; + note.value.reactionCount += 1; if ($i && (body.userId === $i.id)) { note.value.myReaction = reaction; @@ -68,6 +69,7 @@ export function useNoteCapture(props: { const currentCount = (note.value.reactions || {})[reaction] || 0; note.value.reactions[reaction] = Math.max(0, currentCount - 1); + note.value.reactionCount = Math.max(0, note.value.reactionCount - 1); if (note.value.reactions[reaction] === 0) delete note.value.reactions[reaction]; if ($i && (body.userId === $i.id)) { diff --git a/packages/frontend/src/style.scss b/packages/frontend/src/style.scss index d876009961..e5a18134ee 100644 --- a/packages/frontend/src/style.scss +++ b/packages/frontend/src/style.scss @@ -41,6 +41,13 @@ --thread-width: 2px; //--ad: rgb(255 169 0 / 10%); + --eventFollow: #36aed2; + --eventRenote: #36d298; + --eventReply: #007aff; + --eventReactionHeart: #dd2e44; + --eventReaction: #e99a0b; + --eventAchievement: #cb9a11; + --eventOther: #88a6b7; } html.radius-misskey { diff --git a/packages/frontend/vite.config.local-dev.ts b/packages/frontend/vite.config.local-dev.ts index 4c19dfbc66..3a02309a4f 100644 --- a/packages/frontend/vite.config.local-dev.ts +++ b/packages/frontend/vite.config.local-dev.ts @@ -49,6 +49,9 @@ const devConfig = { }, '/url': httpUrl, '/proxy': httpUrl, + '/_info_card_': httpUrl, + '/bios': httpUrl, + '/cli': httpUrl, }, }, build: { diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts index 6b4ee9361e..db14823d37 100644 --- a/packages/misskey-js/src/autogen/types.ts +++ b/packages/misskey-js/src/autogen/types.ts @@ -4119,6 +4119,7 @@ export type components = { reactions: { [key: string]: number; }; + reactionCount: number; renoteCount: number; repliesCount: number; uri?: string; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b54383a88f..7074523beb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1102,10 +1102,10 @@ importers: devDependencies: '@typescript-eslint/eslint-plugin': specifier: ^6.12.0 - version: 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.54.0)(typescript@5.1.6) + version: 6.12.0(@typescript-eslint/parser@6.12.0)(eslint@8.54.0)(typescript@5.1.6) '@typescript-eslint/parser': specifier: ^6.12.0 - version: 6.21.0(eslint@8.54.0)(typescript@5.1.6) + version: 6.12.0(eslint@8.54.0)(typescript@5.1.6) eslint: specifier: ^8.54.0 version: 8.54.0 @@ -1325,7 +1325,7 @@ importers: devDependencies: '@misskey-dev/eslint-plugin': specifier: 1.0.0 - version: 1.0.0(@typescript-eslint/eslint-plugin@6.21.0)(@typescript-eslint/parser@7.1.0)(eslint-plugin-import@2.29.1)(eslint@8.57.0) + version: 1.0.0(@typescript-eslint/eslint-plugin@6.18.1)(@typescript-eslint/parser@7.1.0)(eslint-plugin-import@2.29.1)(eslint@8.57.0) '@typescript-eslint/parser': specifier: 7.1.0 version: 7.1.0(eslint@8.57.0)(typescript@5.3.3) @@ -1993,14 +1993,14 @@ packages: dependencies: '@ampproject/remapping': 2.2.1 '@babel/code-frame': 7.23.4 - '@babel/generator': 7.23.6 + '@babel/generator': 7.23.4 '@babel/helper-compilation-targets': 7.22.15 '@babel/helper-module-transforms': 7.23.3(@babel/core@7.23.3) '@babel/helpers': 7.23.4 '@babel/parser': 7.23.4 '@babel/template': 7.22.15 - '@babel/traverse': 7.24.0 - '@babel/types': 7.24.0 + '@babel/traverse': 7.23.4 + '@babel/types': 7.23.4 convert-source-map: 2.0.0 debug: 4.3.4(supports-color@8.1.1) gensync: 1.0.0-beta.2 @@ -2033,6 +2033,16 @@ packages: - supports-color dev: true + /@babel/generator@7.23.4: + resolution: {integrity: sha512-esuS49Cga3HcThFNebGhlgsrVLkvhqvYDTzgjfFFlHJcIfLe5jFmRRfCQ1KuBfc4Jrtn3ndLgKWAKjBE+IraYQ==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.4 + '@jridgewell/gen-mapping': 0.3.3 + '@jridgewell/trace-mapping': 0.3.20 + jsesc: 2.5.2 + dev: true + /@babel/generator@7.23.6: resolution: {integrity: sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==} engines: {node: '>=6.9.0'} @@ -2047,14 +2057,14 @@ packages: resolution: {integrity: sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.24.0 + '@babel/types': 7.23.4 dev: true /@babel/helper-builder-binary-assignment-operator-visitor@7.22.15: resolution: {integrity: sha512-QkBXwGgaoC2GtGZRoma6kv7Szfv06khvhFav67ZExau2RaXzy8MpHSMO2PNoP2XtmQphJQRHFfg77Bq731Yizw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.24.0 + '@babel/types': 7.23.4 dev: true /@babel/helper-compilation-targets@7.22.15: @@ -2074,65 +2084,47 @@ packages: dependencies: '@babel/compat-data': 7.23.5 '@babel/helper-validator-option': 7.23.5 - browserslist: 4.23.0 + browserslist: 4.22.2 lru-cache: 5.1.1 semver: 6.3.1 dev: true - /@babel/helper-create-class-features-plugin@7.23.5(@babel/core@7.24.0): - resolution: {integrity: sha512-QELlRWxSpgdwdJzSJn4WAhKC+hvw/AtHbbrIoncKHkhKKR/luAlKkgBDcri1EzWAo8f8VvYVryEHN4tax/V67A==} + /@babel/helper-create-class-features-plugin@7.23.6(@babel/core@7.23.3): + resolution: {integrity: sha512-cBXU1vZni/CpGF29iTu4YRbOZt3Wat6zCoMDxRF1MayiEc4URxOj31tT65HUM0CRpMowA3HCJaAOVOUnMf96cw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.24.0 - '@babel/helper-annotate-as-pure': 7.22.5 - '@babel/helper-environment-visitor': 7.22.20 - '@babel/helper-function-name': 7.23.0 - '@babel/helper-member-expression-to-functions': 7.23.0 - '@babel/helper-optimise-call-expression': 7.22.5 - '@babel/helper-replace-supers': 7.22.20(@babel/core@7.24.0) - '@babel/helper-skip-transparent-expression-wrappers': 7.22.5 - '@babel/helper-split-export-declaration': 7.22.6 - semver: 6.3.1 - dev: true - - /@babel/helper-create-class-features-plugin@7.24.0(@babel/core@7.24.0): - resolution: {integrity: sha512-QAH+vfvts51BCsNZ2PhY6HAggnlS6omLLFTsIpeqZk/MmJ6cW7tgz5yRv0fMJThcr6FmbMrENh1RgrWPTYA76g==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - dependencies: - '@babel/core': 7.24.0 + '@babel/core': 7.23.3 '@babel/helper-annotate-as-pure': 7.22.5 '@babel/helper-environment-visitor': 7.22.20 '@babel/helper-function-name': 7.23.0 '@babel/helper-member-expression-to-functions': 7.23.0 '@babel/helper-optimise-call-expression': 7.22.5 - '@babel/helper-replace-supers': 7.22.20(@babel/core@7.24.0) + '@babel/helper-replace-supers': 7.22.20(@babel/core@7.23.3) '@babel/helper-skip-transparent-expression-wrappers': 7.22.5 '@babel/helper-split-export-declaration': 7.22.6 semver: 6.3.1 dev: true - /@babel/helper-create-regexp-features-plugin@7.22.15(@babel/core@7.24.0): + /@babel/helper-create-regexp-features-plugin@7.22.15(@babel/core@7.23.3): resolution: {integrity: sha512-29FkPLFjn4TPEa3RE7GpW+qbE8tlsu3jntNYNfcGsc49LphF1PQIiD+vMZ1z1xVOKt+93khA9tc2JBs3kBjA7w==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.24.0 + '@babel/core': 7.23.3 '@babel/helper-annotate-as-pure': 7.22.5 regexpu-core: 5.3.2 semver: 6.3.1 dev: true - /@babel/helper-define-polyfill-provider@0.4.4(@babel/core@7.24.0): + /@babel/helper-define-polyfill-provider@0.4.4(@babel/core@7.23.3): resolution: {integrity: sha512-QcJMILQCu2jm5TFPGA3lCpJJTeEP+mqeXooG/NZbg/h5FTFi6V0+99ahlRsW8/kRLyb24LZVCCiclDedhLKcBA==} peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 dependencies: - '@babel/core': 7.24.0 + '@babel/core': 7.23.3 '@babel/helper-compilation-targets': 7.23.6 '@babel/helper-plugin-utils': 7.22.5 debug: 4.3.4(supports-color@8.1.1) @@ -2151,29 +2143,29 @@ packages: resolution: {integrity: sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/template': 7.24.0 - '@babel/types': 7.24.0 + '@babel/template': 7.22.15 + '@babel/types': 7.23.4 dev: true /@babel/helper-hoist-variables@7.22.5: resolution: {integrity: sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.24.0 + '@babel/types': 7.23.4 dev: true /@babel/helper-member-expression-to-functions@7.23.0: resolution: {integrity: sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.24.0 + '@babel/types': 7.23.4 dev: true /@babel/helper-module-imports@7.22.15: resolution: {integrity: sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.24.0 + '@babel/types': 7.23.4 dev: true /@babel/helper-module-transforms@7.23.3(@babel/core@7.23.3): @@ -2208,7 +2200,7 @@ packages: resolution: {integrity: sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.24.0 + '@babel/types': 7.23.4 dev: true /@babel/helper-plugin-utils@7.22.5: @@ -2216,25 +2208,25 @@ packages: engines: {node: '>=6.9.0'} dev: true - /@babel/helper-remap-async-to-generator@7.22.20(@babel/core@7.24.0): + /@babel/helper-remap-async-to-generator@7.22.20(@babel/core@7.23.3): resolution: {integrity: sha512-pBGyV4uBqOns+0UvhsTO8qgl8hO89PmiDYv+/COyp1aeMcmfrfruz+/nCMFiYyFF/Knn0yfrC85ZzNFjembFTw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.24.0 + '@babel/core': 7.23.3 '@babel/helper-annotate-as-pure': 7.22.5 '@babel/helper-environment-visitor': 7.22.20 '@babel/helper-wrap-function': 7.22.20 dev: true - /@babel/helper-replace-supers@7.22.20(@babel/core@7.24.0): + /@babel/helper-replace-supers@7.22.20(@babel/core@7.23.3): resolution: {integrity: sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.24.0 + '@babel/core': 7.23.3 '@babel/helper-environment-visitor': 7.22.20 '@babel/helper-member-expression-to-functions': 7.23.0 '@babel/helper-optimise-call-expression': 7.22.5 @@ -2244,27 +2236,35 @@ packages: resolution: {integrity: sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.24.0 + '@babel/types': 7.23.4 dev: true /@babel/helper-skip-transparent-expression-wrappers@7.22.5: resolution: {integrity: sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.24.0 + '@babel/types': 7.23.4 dev: true /@babel/helper-split-export-declaration@7.22.6: resolution: {integrity: sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.24.0 + '@babel/types': 7.23.4 dev: true + /@babel/helper-string-parser@7.22.5: + resolution: {integrity: sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==} + engines: {node: '>=6.9.0'} + /@babel/helper-string-parser@7.23.4: resolution: {integrity: sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==} engines: {node: '>=6.9.0'} + /@babel/helper-validator-identifier@7.22.15: + resolution: {integrity: sha512-4E/F9IIEi8WR94324mbDUMo074YTheJmd7eZF5vITTeYchqAi6sYXRLHUVsmkdmY4QjfKTcB2jB7dVP3NaBElQ==} + engines: {node: '>=6.9.0'} + /@babel/helper-validator-identifier@7.22.20: resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==} engines: {node: '>=6.9.0'} @@ -2284,8 +2284,8 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/helper-function-name': 7.23.0 - '@babel/template': 7.24.0 - '@babel/types': 7.24.0 + '@babel/template': 7.22.15 + '@babel/types': 7.23.4 dev: true /@babel/helpers@7.23.4: @@ -2293,8 +2293,8 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/template': 7.22.15 - '@babel/traverse': 7.24.0 - '@babel/types': 7.24.0 + '@babel/traverse': 7.23.4 + '@babel/types': 7.23.4 transitivePeerDependencies: - supports-color dev: true @@ -2323,22 +2323,14 @@ packages: engines: {node: '>=6.0.0'} hasBin: true dependencies: - '@babel/types': 7.24.0 + '@babel/types': 7.22.17 /@babel/parser@7.23.4: resolution: {integrity: sha512-vf3Xna6UEprW+7t6EtOmFpHNAuxw3xqPZghy+brsnusscJRW5BMUzzHZc5ICjULee81WeUV2jjakG09MDglJXQ==} engines: {node: '>=6.0.0'} hasBin: true dependencies: - '@babel/types': 7.24.0 - dev: true - - /@babel/parser@7.23.6: - resolution: {integrity: sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ==} - engines: {node: '>=6.0.0'} - hasBin: true - dependencies: - '@babel/types': 7.24.0 + '@babel/types': 7.23.4 dev: true /@babel/parser@7.23.9: @@ -2346,7 +2338,7 @@ packages: engines: {node: '>=6.0.0'} hasBin: true dependencies: - '@babel/types': 7.24.0 + '@babel/types': 7.23.4 /@babel/parser@7.24.0: resolution: {integrity: sha512-QuP/FxEAzMSjXygs8v4N9dvdXzEHN4W1oF3PxuWAtPo08UdM17u89RDMgjLn/mlc56iM0HlLmVkO/wgR+rDgHg==} @@ -2356,46 +2348,46 @@ packages: '@babel/types': 7.24.0 dev: true - /@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.23.3(@babel/core@7.24.0): + /@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.23.3(@babel/core@7.23.3): resolution: {integrity: sha512-iRkKcCqb7iGnq9+3G6rZ+Ciz5VywC4XNRHe57lKM+jOeYAoR0lVqdeeDRfh0tQcTfw/+vBhHn926FmQhLtlFLQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.24.0 + '@babel/core': 7.23.3 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.23.3(@babel/core@7.24.0): + /@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.23.3(@babel/core@7.23.3): resolution: {integrity: sha512-WwlxbfMNdVEpQjZmK5mhm7oSwD3dS6eU+Iwsi4Knl9wAletWem7kaRsGOG+8UEbRyqxY4SS5zvtfXwX+jMxUwQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.13.0 dependencies: - '@babel/core': 7.24.0 + '@babel/core': 7.23.3 '@babel/helper-plugin-utils': 7.22.5 '@babel/helper-skip-transparent-expression-wrappers': 7.22.5 - '@babel/plugin-transform-optional-chaining': 7.23.4(@babel/core@7.24.0) + '@babel/plugin-transform-optional-chaining': 7.23.4(@babel/core@7.23.3) dev: true - /@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.23.3(@babel/core@7.24.0): + /@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.23.3(@babel/core@7.23.3): resolution: {integrity: sha512-XaJak1qcityzrX0/IU5nKHb34VaibwP3saKqG6a/tppelgllOH13LUann4ZCIBcVOeE6H18K4Vx9QKkVww3z/w==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.24.0 + '@babel/core': 7.23.3 '@babel/helper-environment-visitor': 7.22.20 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.24.0): + /@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.23.3): resolution: {integrity: sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.0 + '@babel/core': 7.23.3 dev: true /@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.23.3): @@ -2407,15 +2399,6 @@ packages: '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.24.0): - resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.24.0 - '@babel/helper-plugin-utils': 7.22.5 - dev: true - /@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.23.3): resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==} peerDependencies: @@ -2434,70 +2417,61 @@ packages: '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.24.0): - resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.24.0 - '@babel/helper-plugin-utils': 7.22.5 - dev: true - - /@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.24.0): + /@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.23.3): resolution: {integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.0 + '@babel/core': 7.23.3 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.24.0): + /@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.23.3): resolution: {integrity: sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.0 + '@babel/core': 7.23.3 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-export-namespace-from@7.8.3(@babel/core@7.24.0): + /@babel/plugin-syntax-export-namespace-from@7.8.3(@babel/core@7.23.3): resolution: {integrity: sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.0 + '@babel/core': 7.23.3 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-flow@7.23.3(@babel/core@7.24.0): + /@babel/plugin-syntax-flow@7.23.3(@babel/core@7.23.3): resolution: {integrity: sha512-YZiAIpkJAwQXBJLIQbRFayR5c+gJ35Vcz3bg954k7cd73zqjvhacJuL9RbrzPz8qPmZdgqP6EUKwy0PCNhaaPA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.0 + '@babel/core': 7.23.3 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-import-assertions@7.23.3(@babel/core@7.24.0): + /@babel/plugin-syntax-import-assertions@7.23.3(@babel/core@7.23.3): resolution: {integrity: sha512-lPgDSU+SJLK3xmFDTV2ZRQAiM7UuUjGidwBywFavObCiZc1BeAAcMtHJKUya92hPHO+at63JJPLygilZard8jw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.0 + '@babel/core': 7.23.3 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-import-attributes@7.23.3(@babel/core@7.24.0): + /@babel/plugin-syntax-import-attributes@7.23.3(@babel/core@7.23.3): resolution: {integrity: sha512-pawnE0P9g10xgoP7yKr6CK63K2FMsTE+FZidZO/1PwRdzmAPVs+HS1mAURUsgaoxammTJvULUdIkEK0gOcU2tA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.0 + '@babel/core': 7.23.3 '@babel/helper-plugin-utils': 7.22.5 dev: true @@ -2510,15 +2484,6 @@ packages: '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.24.0): - resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.24.0 - '@babel/helper-plugin-utils': 7.22.5 - dev: true - /@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.23.3): resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} peerDependencies: @@ -2528,15 +2493,6 @@ packages: '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.24.0): - resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.24.0 - '@babel/helper-plugin-utils': 7.22.5 - dev: true - /@babel/plugin-syntax-jsx@7.23.3(@babel/core@7.23.3): resolution: {integrity: sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==} engines: {node: '>=6.9.0'} @@ -2547,16 +2503,6 @@ packages: '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-jsx@7.23.3(@babel/core@7.24.0): - resolution: {integrity: sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.24.0 - '@babel/helper-plugin-utils': 7.22.5 - dev: true - /@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.23.3): resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} peerDependencies: @@ -2566,15 +2512,6 @@ packages: '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.24.0): - resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.24.0 - '@babel/helper-plugin-utils': 7.22.5 - dev: true - /@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.23.3): resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} peerDependencies: @@ -2584,15 +2521,6 @@ packages: '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.24.0): - resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.24.0 - '@babel/helper-plugin-utils': 7.22.5 - dev: true - /@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.23.3): resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} peerDependencies: @@ -2602,15 +2530,6 @@ packages: '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.24.0): - resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.24.0 - '@babel/helper-plugin-utils': 7.22.5 - dev: true - /@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.23.3): resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} peerDependencies: @@ -2620,15 +2539,6 @@ packages: '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.24.0): - resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.24.0 - '@babel/helper-plugin-utils': 7.22.5 - dev: true - /@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.23.3): resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} peerDependencies: @@ -2638,15 +2548,6 @@ packages: '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.24.0): - resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.24.0 - '@babel/helper-plugin-utils': 7.22.5 - dev: true - /@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.23.3): resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} peerDependencies: @@ -2656,22 +2557,13 @@ packages: '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.24.0): - resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.24.0 - '@babel/helper-plugin-utils': 7.22.5 - dev: true - - /@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.24.0): + /@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.23.3): resolution: {integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.0 + '@babel/core': 7.23.3 '@babel/helper-plugin-utils': 7.22.5 dev: true @@ -2685,16 +2577,6 @@ packages: '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.24.0): - resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.24.0 - '@babel/helper-plugin-utils': 7.22.5 - dev: true - /@babel/plugin-syntax-typescript@7.23.3(@babel/core@7.23.3): resolution: {integrity: sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ==} engines: {node: '>=6.9.0'} @@ -2705,719 +2587,709 @@ packages: '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-typescript@7.23.3(@babel/core@7.24.0): - resolution: {integrity: sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.24.0 - '@babel/helper-plugin-utils': 7.22.5 - dev: true - - /@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.24.0): + /@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.23.3): resolution: {integrity: sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.24.0 - '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.24.0) + '@babel/core': 7.23.3 + '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.3) '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-arrow-functions@7.23.3(@babel/core@7.24.0): + /@babel/plugin-transform-arrow-functions@7.23.3(@babel/core@7.23.3): resolution: {integrity: sha512-NzQcQrzaQPkaEwoTm4Mhyl8jI1huEL/WWIEvudjTCMJ9aBZNpsJbMASx7EQECtQQPS/DcnFpo0FIh3LvEO9cxQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.0 + '@babel/core': 7.23.3 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-async-generator-functions@7.23.4(@babel/core@7.24.0): + /@babel/plugin-transform-async-generator-functions@7.23.4(@babel/core@7.23.3): resolution: {integrity: sha512-efdkfPhHYTtn0G6n2ddrESE91fgXxjlqLsnUtPWnJs4a4mZIbUaK7ffqKIIUKXSHwcDvaCVX6GXkaJJFqtX7jw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.0 + '@babel/core': 7.23.3 '@babel/helper-environment-visitor': 7.22.20 '@babel/helper-plugin-utils': 7.22.5 - '@babel/helper-remap-async-to-generator': 7.22.20(@babel/core@7.24.0) - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.24.0) + '@babel/helper-remap-async-to-generator': 7.22.20(@babel/core@7.23.3) + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.23.3) dev: true - /@babel/plugin-transform-async-to-generator@7.23.3(@babel/core@7.24.0): + /@babel/plugin-transform-async-to-generator@7.23.3(@babel/core@7.23.3): resolution: {integrity: sha512-A7LFsKi4U4fomjqXJlZg/u0ft/n8/7n7lpffUP/ZULx/DtV9SGlNKZolHH6PE8Xl1ngCc0M11OaeZptXVkfKSw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.0 + '@babel/core': 7.23.3 '@babel/helper-module-imports': 7.22.15 '@babel/helper-plugin-utils': 7.22.5 - '@babel/helper-remap-async-to-generator': 7.22.20(@babel/core@7.24.0) + '@babel/helper-remap-async-to-generator': 7.22.20(@babel/core@7.23.3) dev: true - /@babel/plugin-transform-block-scoped-functions@7.23.3(@babel/core@7.24.0): + /@babel/plugin-transform-block-scoped-functions@7.23.3(@babel/core@7.23.3): resolution: {integrity: sha512-vI+0sIaPIO6CNuM9Kk5VmXcMVRiOpDh7w2zZt9GXzmE/9KD70CUEVhvPR/etAeNK/FAEkhxQtXOzVF3EuRL41A==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.0 + '@babel/core': 7.23.3 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-block-scoping@7.23.4(@babel/core@7.24.0): + /@babel/plugin-transform-block-scoping@7.23.4(@babel/core@7.23.3): resolution: {integrity: sha512-0QqbP6B6HOh7/8iNR4CQU2Th/bbRtBp4KS9vcaZd1fZ0wSh5Fyssg0UCIHwxh+ka+pNDREbVLQnHCMHKZfPwfw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.0 + '@babel/core': 7.23.3 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-class-properties@7.23.3(@babel/core@7.24.0): + /@babel/plugin-transform-class-properties@7.23.3(@babel/core@7.23.3): resolution: {integrity: sha512-uM+AN8yCIjDPccsKGlw271xjJtGii+xQIF/uMPS8H15L12jZTsLfF4o5vNO7d/oUguOyfdikHGc/yi9ge4SGIg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.0 - '@babel/helper-create-class-features-plugin': 7.23.5(@babel/core@7.24.0) + '@babel/core': 7.23.3 + '@babel/helper-create-class-features-plugin': 7.23.6(@babel/core@7.23.3) '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-class-static-block@7.23.4(@babel/core@7.24.0): + /@babel/plugin-transform-class-static-block@7.23.4(@babel/core@7.23.3): resolution: {integrity: sha512-nsWu/1M+ggti1SOALj3hfx5FXzAY06fwPJsUZD4/A5e1bWi46VUIWtD+kOX6/IdhXGsXBWllLFDSnqSCdUNydQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.12.0 dependencies: - '@babel/core': 7.24.0 - '@babel/helper-create-class-features-plugin': 7.23.5(@babel/core@7.24.0) + '@babel/core': 7.23.3 + '@babel/helper-create-class-features-plugin': 7.23.6(@babel/core@7.23.3) '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.24.0) + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.23.3) dev: true - /@babel/plugin-transform-classes@7.23.5(@babel/core@7.24.0): + /@babel/plugin-transform-classes@7.23.5(@babel/core@7.23.3): resolution: {integrity: sha512-jvOTR4nicqYC9yzOHIhXG5emiFEOpappSJAl73SDSEDcybD+Puuze8Tnpb9p9qEyYup24tq891gkaygIFvWDqg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.0 + '@babel/core': 7.23.3 '@babel/helper-annotate-as-pure': 7.22.5 '@babel/helper-compilation-targets': 7.23.6 '@babel/helper-environment-visitor': 7.22.20 '@babel/helper-function-name': 7.23.0 '@babel/helper-optimise-call-expression': 7.22.5 '@babel/helper-plugin-utils': 7.22.5 - '@babel/helper-replace-supers': 7.22.20(@babel/core@7.24.0) + '@babel/helper-replace-supers': 7.22.20(@babel/core@7.23.3) '@babel/helper-split-export-declaration': 7.22.6 globals: 11.12.0 dev: true - /@babel/plugin-transform-computed-properties@7.23.3(@babel/core@7.24.0): + /@babel/plugin-transform-computed-properties@7.23.3(@babel/core@7.23.3): resolution: {integrity: sha512-dTj83UVTLw/+nbiHqQSFdwO9CbTtwq1DsDqm3CUEtDrZNET5rT5E6bIdTlOftDTDLMYxvxHNEYO4B9SLl8SLZw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.0 + '@babel/core': 7.23.3 '@babel/helper-plugin-utils': 7.22.5 - '@babel/template': 7.24.0 + '@babel/template': 7.22.15 dev: true - /@babel/plugin-transform-destructuring@7.23.3(@babel/core@7.24.0): + /@babel/plugin-transform-destructuring@7.23.3(@babel/core@7.23.3): resolution: {integrity: sha512-n225npDqjDIr967cMScVKHXJs7rout1q+tt50inyBCPkyZ8KxeI6d+GIbSBTT/w/9WdlWDOej3V9HE5Lgk57gw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.0 + '@babel/core': 7.23.3 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-dotall-regex@7.23.3(@babel/core@7.24.0): + /@babel/plugin-transform-dotall-regex@7.23.3(@babel/core@7.23.3): resolution: {integrity: sha512-vgnFYDHAKzFaTVp+mneDsIEbnJ2Np/9ng9iviHw3P/KVcgONxpNULEW/51Z/BaFojG2GI2GwwXck5uV1+1NOYQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.0 - '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.24.0) + '@babel/core': 7.23.3 + '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.3) '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-duplicate-keys@7.23.3(@babel/core@7.24.0): + /@babel/plugin-transform-duplicate-keys@7.23.3(@babel/core@7.23.3): resolution: {integrity: sha512-RrqQ+BQmU3Oyav3J+7/myfvRCq7Tbz+kKLLshUmMwNlDHExbGL7ARhajvoBJEvc+fCguPPu887N+3RRXBVKZUA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.0 + '@babel/core': 7.23.3 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-dynamic-import@7.23.4(@babel/core@7.24.0): + /@babel/plugin-transform-dynamic-import@7.23.4(@babel/core@7.23.3): resolution: {integrity: sha512-V6jIbLhdJK86MaLh4Jpghi8ho5fGzt3imHOBu/x0jlBaPYqDoWz4RDXjmMOfnh+JWNaQleEAByZLV0QzBT4YQQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.0 + '@babel/core': 7.23.3 '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.24.0) + '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.23.3) dev: true - /@babel/plugin-transform-exponentiation-operator@7.23.3(@babel/core@7.24.0): + /@babel/plugin-transform-exponentiation-operator@7.23.3(@babel/core@7.23.3): resolution: {integrity: sha512-5fhCsl1odX96u7ILKHBj4/Y8vipoqwsJMh4csSA8qFfxrZDEA4Ssku2DyNvMJSmZNOEBT750LfFPbtrnTP90BQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.0 + '@babel/core': 7.23.3 '@babel/helper-builder-binary-assignment-operator-visitor': 7.22.15 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-export-namespace-from@7.23.4(@babel/core@7.24.0): + /@babel/plugin-transform-export-namespace-from@7.23.4(@babel/core@7.23.3): resolution: {integrity: sha512-GzuSBcKkx62dGzZI1WVgTWvkkz84FZO5TC5T8dl/Tht/rAla6Dg/Mz9Yhypg+ezVACf/rgDuQt3kbWEv7LdUDQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.0 + '@babel/core': 7.23.3 '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.24.0) + '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.23.3) dev: true - /@babel/plugin-transform-flow-strip-types@7.23.3(@babel/core@7.24.0): + /@babel/plugin-transform-flow-strip-types@7.23.3(@babel/core@7.23.3): resolution: {integrity: sha512-26/pQTf9nQSNVJCrLB1IkHUKyPxR+lMrH2QDPG89+Znu9rAMbtrybdbWeE9bb7gzjmE5iXHEY+e0HUwM6Co93Q==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.0 + '@babel/core': 7.23.3 '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-flow': 7.23.3(@babel/core@7.24.0) + '@babel/plugin-syntax-flow': 7.23.3(@babel/core@7.23.3) dev: true - /@babel/plugin-transform-for-of@7.23.6(@babel/core@7.24.0): + /@babel/plugin-transform-for-of@7.23.6(@babel/core@7.23.3): resolution: {integrity: sha512-aYH4ytZ0qSuBbpfhuofbg/e96oQ7U2w1Aw/UQmKT+1l39uEhUPoFS3fHevDc1G0OvewyDudfMKY1OulczHzWIw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.0 + '@babel/core': 7.23.3 '@babel/helper-plugin-utils': 7.22.5 '@babel/helper-skip-transparent-expression-wrappers': 7.22.5 dev: true - /@babel/plugin-transform-function-name@7.23.3(@babel/core@7.24.0): + /@babel/plugin-transform-function-name@7.23.3(@babel/core@7.23.3): resolution: {integrity: sha512-I1QXp1LxIvt8yLaib49dRW5Okt7Q4oaxao6tFVKS/anCdEOMtYwWVKoiOA1p34GOWIZjUK0E+zCp7+l1pfQyiw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.0 + '@babel/core': 7.23.3 '@babel/helper-compilation-targets': 7.23.6 '@babel/helper-function-name': 7.23.0 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-json-strings@7.23.4(@babel/core@7.24.0): + /@babel/plugin-transform-json-strings@7.23.4(@babel/core@7.23.3): resolution: {integrity: sha512-81nTOqM1dMwZ/aRXQ59zVubN9wHGqk6UtqRK+/q+ciXmRy8fSolhGVvG09HHRGo4l6fr/c4ZhXUQH0uFW7PZbg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.0 + '@babel/core': 7.23.3 '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.24.0) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.23.3) dev: true - /@babel/plugin-transform-literals@7.23.3(@babel/core@7.24.0): + /@babel/plugin-transform-literals@7.23.3(@babel/core@7.23.3): resolution: {integrity: sha512-wZ0PIXRxnwZvl9AYpqNUxpZ5BiTGrYt7kueGQ+N5FiQ7RCOD4cm8iShd6S6ggfVIWaJf2EMk8eRzAh52RfP4rQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.0 + '@babel/core': 7.23.3 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-logical-assignment-operators@7.23.4(@babel/core@7.24.0): + /@babel/plugin-transform-logical-assignment-operators@7.23.4(@babel/core@7.23.3): resolution: {integrity: sha512-Mc/ALf1rmZTP4JKKEhUwiORU+vcfarFVLfcFiolKUo6sewoxSEgl36ak5t+4WamRsNr6nzjZXQjM35WsU+9vbg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.0 + '@babel/core': 7.23.3 '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.24.0) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.23.3) dev: true - /@babel/plugin-transform-member-expression-literals@7.23.3(@babel/core@7.24.0): + /@babel/plugin-transform-member-expression-literals@7.23.3(@babel/core@7.23.3): resolution: {integrity: sha512-sC3LdDBDi5x96LA+Ytekz2ZPk8i/Ck+DEuDbRAll5rknJ5XRTSaPKEYwomLcs1AA8wg9b3KjIQRsnApj+q51Ag==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.0 + '@babel/core': 7.23.3 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-modules-amd@7.23.3(@babel/core@7.24.0): + /@babel/plugin-transform-modules-amd@7.23.3(@babel/core@7.23.3): resolution: {integrity: sha512-vJYQGxeKM4t8hYCKVBlZX/gtIY2I7mRGFNcm85sgXGMTBcoV3QdVtdpbcWEbzbfUIUZKwvgFT82mRvaQIebZzw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.0 - '@babel/helper-module-transforms': 7.23.3(@babel/core@7.24.0) + '@babel/core': 7.23.3 + '@babel/helper-module-transforms': 7.23.3(@babel/core@7.23.3) '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-modules-commonjs@7.23.3(@babel/core@7.24.0): + /@babel/plugin-transform-modules-commonjs@7.23.3(@babel/core@7.23.3): resolution: {integrity: sha512-aVS0F65LKsdNOtcz6FRCpE4OgsP2OFnW46qNxNIX9h3wuzaNcSQsJysuMwqSibC98HPrf2vCgtxKNwS0DAlgcA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.0 - '@babel/helper-module-transforms': 7.23.3(@babel/core@7.24.0) + '@babel/core': 7.23.3 + '@babel/helper-module-transforms': 7.23.3(@babel/core@7.23.3) '@babel/helper-plugin-utils': 7.22.5 '@babel/helper-simple-access': 7.22.5 dev: true - /@babel/plugin-transform-modules-systemjs@7.23.3(@babel/core@7.24.0): + /@babel/plugin-transform-modules-systemjs@7.23.3(@babel/core@7.23.3): resolution: {integrity: sha512-ZxyKGTkF9xT9YJuKQRo19ewf3pXpopuYQd8cDXqNzc3mUNbOME0RKMoZxviQk74hwzfQsEe66dE92MaZbdHKNQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.0 + '@babel/core': 7.23.3 '@babel/helper-hoist-variables': 7.22.5 - '@babel/helper-module-transforms': 7.23.3(@babel/core@7.24.0) + '@babel/helper-module-transforms': 7.23.3(@babel/core@7.23.3) '@babel/helper-plugin-utils': 7.22.5 '@babel/helper-validator-identifier': 7.22.20 dev: true - /@babel/plugin-transform-modules-umd@7.23.3(@babel/core@7.24.0): + /@babel/plugin-transform-modules-umd@7.23.3(@babel/core@7.23.3): resolution: {integrity: sha512-zHsy9iXX2nIsCBFPud3jKn1IRPWg3Ing1qOZgeKV39m1ZgIdpJqvlWVeiHBZC6ITRG0MfskhYe9cLgntfSFPIg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.0 - '@babel/helper-module-transforms': 7.23.3(@babel/core@7.24.0) + '@babel/core': 7.23.3 + '@babel/helper-module-transforms': 7.23.3(@babel/core@7.23.3) '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-named-capturing-groups-regex@7.22.5(@babel/core@7.24.0): + /@babel/plugin-transform-named-capturing-groups-regex@7.22.5(@babel/core@7.23.3): resolution: {integrity: sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.24.0 - '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.24.0) + '@babel/core': 7.23.3 + '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.3) '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-new-target@7.23.3(@babel/core@7.24.0): + /@babel/plugin-transform-new-target@7.23.3(@babel/core@7.23.3): resolution: {integrity: sha512-YJ3xKqtJMAT5/TIZnpAR3I+K+WaDowYbN3xyxI8zxx/Gsypwf9B9h0VB+1Nh6ACAAPRS5NSRje0uVv5i79HYGQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.0 + '@babel/core': 7.23.3 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-nullish-coalescing-operator@7.23.4(@babel/core@7.24.0): + /@babel/plugin-transform-nullish-coalescing-operator@7.23.4(@babel/core@7.23.3): resolution: {integrity: sha512-jHE9EVVqHKAQx+VePv5LLGHjmHSJR76vawFPTdlxR/LVJPfOEGxREQwQfjuZEOPTwG92X3LINSh3M40Rv4zpVA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.0 + '@babel/core': 7.23.3 '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.24.0) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.23.3) dev: true - /@babel/plugin-transform-numeric-separator@7.23.4(@babel/core@7.24.0): + /@babel/plugin-transform-numeric-separator@7.23.4(@babel/core@7.23.3): resolution: {integrity: sha512-mps6auzgwjRrwKEZA05cOwuDc9FAzoyFS4ZsG/8F43bTLf/TgkJg7QXOrPO1JO599iA3qgK9MXdMGOEC8O1h6Q==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.0 + '@babel/core': 7.23.3 '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.24.0) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.23.3) dev: true - /@babel/plugin-transform-object-rest-spread@7.23.4(@babel/core@7.24.0): + /@babel/plugin-transform-object-rest-spread@7.23.4(@babel/core@7.23.3): resolution: {integrity: sha512-9x9K1YyeQVw0iOXJlIzwm8ltobIIv7j2iLyP2jIhEbqPRQ7ScNgwQufU2I0Gq11VjyG4gI4yMXt2VFags+1N3g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/compat-data': 7.23.5 - '@babel/core': 7.24.0 + '@babel/core': 7.23.3 '@babel/helper-compilation-targets': 7.23.6 '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.24.0) - '@babel/plugin-transform-parameters': 7.23.3(@babel/core@7.24.0) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.23.3) + '@babel/plugin-transform-parameters': 7.23.3(@babel/core@7.23.3) dev: true - /@babel/plugin-transform-object-super@7.23.3(@babel/core@7.24.0): + /@babel/plugin-transform-object-super@7.23.3(@babel/core@7.23.3): resolution: {integrity: sha512-BwQ8q0x2JG+3lxCVFohg+KbQM7plfpBwThdW9A6TMtWwLsbDA01Ek2Zb/AgDN39BiZsExm4qrXxjk+P1/fzGrA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.0 + '@babel/core': 7.23.3 '@babel/helper-plugin-utils': 7.22.5 - '@babel/helper-replace-supers': 7.22.20(@babel/core@7.24.0) + '@babel/helper-replace-supers': 7.22.20(@babel/core@7.23.3) dev: true - /@babel/plugin-transform-optional-catch-binding@7.23.4(@babel/core@7.24.0): + /@babel/plugin-transform-optional-catch-binding@7.23.4(@babel/core@7.23.3): resolution: {integrity: sha512-XIq8t0rJPHf6Wvmbn9nFxU6ao4c7WhghTR5WyV8SrJfUFzyxhCm4nhC+iAp3HFhbAKLfYpgzhJ6t4XCtVwqO5A==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.0 + '@babel/core': 7.23.3 '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.24.0) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.23.3) dev: true - /@babel/plugin-transform-optional-chaining@7.23.4(@babel/core@7.24.0): + /@babel/plugin-transform-optional-chaining@7.23.4(@babel/core@7.23.3): resolution: {integrity: sha512-ZU8y5zWOfjM5vZ+asjgAPwDaBjJzgufjES89Rs4Lpq63O300R/kOz30WCLo6BxxX6QVEilwSlpClnG5cZaikTA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.0 + '@babel/core': 7.23.3 '@babel/helper-plugin-utils': 7.22.5 '@babel/helper-skip-transparent-expression-wrappers': 7.22.5 - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.24.0) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.23.3) dev: true - /@babel/plugin-transform-parameters@7.23.3(@babel/core@7.24.0): + /@babel/plugin-transform-parameters@7.23.3(@babel/core@7.23.3): resolution: {integrity: sha512-09lMt6UsUb3/34BbECKVbVwrT9bO6lILWln237z7sLaWnMsTi7Yc9fhX5DLpkJzAGfaReXI22wP41SZmnAA3Vw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.0 + '@babel/core': 7.23.3 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-private-methods@7.23.3(@babel/core@7.24.0): + /@babel/plugin-transform-private-methods@7.23.3(@babel/core@7.23.3): resolution: {integrity: sha512-UzqRcRtWsDMTLrRWFvUBDwmw06tCQH9Rl1uAjfh6ijMSmGYQ+fpdB+cnqRC8EMh5tuuxSv0/TejGL+7vyj+50g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.0 - '@babel/helper-create-class-features-plugin': 7.23.5(@babel/core@7.24.0) + '@babel/core': 7.23.3 + '@babel/helper-create-class-features-plugin': 7.23.6(@babel/core@7.23.3) '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-private-property-in-object@7.23.4(@babel/core@7.24.0): + /@babel/plugin-transform-private-property-in-object@7.23.4(@babel/core@7.23.3): resolution: {integrity: sha512-9G3K1YqTq3F4Vt88Djx1UZ79PDyj+yKRnUy7cZGSMe+a7jkwD259uKKuUzQlPkGam7R+8RJwh5z4xO27fA1o2A==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.0 + '@babel/core': 7.23.3 '@babel/helper-annotate-as-pure': 7.22.5 - '@babel/helper-create-class-features-plugin': 7.23.5(@babel/core@7.24.0) + '@babel/helper-create-class-features-plugin': 7.23.6(@babel/core@7.23.3) '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.24.0) + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.23.3) dev: true - /@babel/plugin-transform-property-literals@7.23.3(@babel/core@7.24.0): + /@babel/plugin-transform-property-literals@7.23.3(@babel/core@7.23.3): resolution: {integrity: sha512-jR3Jn3y7cZp4oEWPFAlRsSWjxKe4PZILGBSd4nis1TsC5qeSpb+nrtihJuDhNI7QHiVbUaiXa0X2RZY3/TI6Nw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.0 + '@babel/core': 7.23.3 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-regenerator@7.23.3(@babel/core@7.24.0): + /@babel/plugin-transform-regenerator@7.23.3(@babel/core@7.23.3): resolution: {integrity: sha512-KP+75h0KghBMcVpuKisx3XTu9Ncut8Q8TuvGO4IhY+9D5DFEckQefOuIsB/gQ2tG71lCke4NMrtIPS8pOj18BQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.0 + '@babel/core': 7.23.3 '@babel/helper-plugin-utils': 7.22.5 regenerator-transform: 0.15.2 dev: true - /@babel/plugin-transform-reserved-words@7.23.3(@babel/core@7.24.0): + /@babel/plugin-transform-reserved-words@7.23.3(@babel/core@7.23.3): resolution: {integrity: sha512-QnNTazY54YqgGxwIexMZva9gqbPa15t/x9VS+0fsEFWplwVpXYZivtgl43Z1vMpc1bdPP2PP8siFeVcnFvA3Cg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.0 + '@babel/core': 7.23.3 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-shorthand-properties@7.23.3(@babel/core@7.24.0): + /@babel/plugin-transform-shorthand-properties@7.23.3(@babel/core@7.23.3): resolution: {integrity: sha512-ED2fgqZLmexWiN+YNFX26fx4gh5qHDhn1O2gvEhreLW2iI63Sqm4llRLCXALKrCnbN4Jy0VcMQZl/SAzqug/jg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.0 + '@babel/core': 7.23.3 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-spread@7.23.3(@babel/core@7.24.0): + /@babel/plugin-transform-spread@7.23.3(@babel/core@7.23.3): resolution: {integrity: sha512-VvfVYlrlBVu+77xVTOAoxQ6mZbnIq5FM0aGBSFEcIh03qHf+zNqA4DC/3XMUozTg7bZV3e3mZQ0i13VB6v5yUg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.0 + '@babel/core': 7.23.3 '@babel/helper-plugin-utils': 7.22.5 '@babel/helper-skip-transparent-expression-wrappers': 7.22.5 dev: true - /@babel/plugin-transform-sticky-regex@7.23.3(@babel/core@7.24.0): + /@babel/plugin-transform-sticky-regex@7.23.3(@babel/core@7.23.3): resolution: {integrity: sha512-HZOyN9g+rtvnOU3Yh7kSxXrKbzgrm5X4GncPY1QOquu7epga5MxKHVpYu2hvQnry/H+JjckSYRb93iNfsioAGg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.0 + '@babel/core': 7.23.3 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-template-literals@7.23.3(@babel/core@7.24.0): + /@babel/plugin-transform-template-literals@7.23.3(@babel/core@7.23.3): resolution: {integrity: sha512-Flok06AYNp7GV2oJPZZcP9vZdszev6vPBkHLwxwSpaIqx75wn6mUd3UFWsSsA0l8nXAKkyCmL/sR02m8RYGeHg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.0 + '@babel/core': 7.23.3 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-typeof-symbol@7.23.3(@babel/core@7.24.0): + /@babel/plugin-transform-typeof-symbol@7.23.3(@babel/core@7.23.3): resolution: {integrity: sha512-4t15ViVnaFdrPC74be1gXBSMzXk3B4Us9lP7uLRQHTFpV5Dvt33pn+2MyyNxmN3VTTm3oTrZVMUmuw3oBnQ2oQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.0 + '@babel/core': 7.23.3 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-typescript@7.23.6(@babel/core@7.24.0): + /@babel/plugin-transform-typescript@7.23.6(@babel/core@7.23.3): resolution: {integrity: sha512-6cBG5mBvUu4VUD04OHKnYzbuHNP8huDsD3EDqqpIpsswTDoqHCjLoHb6+QgsV1WsT2nipRqCPgxD3LXnEO7XfA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.0 + '@babel/core': 7.23.3 '@babel/helper-annotate-as-pure': 7.22.5 - '@babel/helper-create-class-features-plugin': 7.24.0(@babel/core@7.24.0) + '@babel/helper-create-class-features-plugin': 7.23.6(@babel/core@7.23.3) '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-typescript': 7.23.3(@babel/core@7.24.0) + '@babel/plugin-syntax-typescript': 7.23.3(@babel/core@7.23.3) dev: true - /@babel/plugin-transform-unicode-escapes@7.23.3(@babel/core@7.24.0): + /@babel/plugin-transform-unicode-escapes@7.23.3(@babel/core@7.23.3): resolution: {integrity: sha512-OMCUx/bU6ChE3r4+ZdylEqAjaQgHAgipgW8nsCfu5pGqDcFytVd91AwRvUJSBZDz0exPGgnjoqhgRYLRjFZc9Q==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.0 + '@babel/core': 7.23.3 '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-unicode-property-regex@7.23.3(@babel/core@7.24.0): + /@babel/plugin-transform-unicode-property-regex@7.23.3(@babel/core@7.23.3): resolution: {integrity: sha512-KcLIm+pDZkWZQAFJ9pdfmh89EwVfmNovFBcXko8szpBeF8z68kWIPeKlmSOkT9BXJxs2C0uk+5LxoxIv62MROA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.0 - '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.24.0) + '@babel/core': 7.23.3 + '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.3) '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-unicode-regex@7.23.3(@babel/core@7.24.0): + /@babel/plugin-transform-unicode-regex@7.23.3(@babel/core@7.23.3): resolution: {integrity: sha512-wMHpNA4x2cIA32b/ci3AfwNgheiva2W0WUKWTK7vBHBhDKfPsc5cFGNWm69WBqpwd86u1qwZ9PWevKqm1A3yAw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.0 - '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.24.0) + '@babel/core': 7.23.3 + '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.3) '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-unicode-sets-regex@7.23.3(@babel/core@7.24.0): + /@babel/plugin-transform-unicode-sets-regex@7.23.3(@babel/core@7.23.3): resolution: {integrity: sha512-W7lliA/v9bNR83Qc3q1ip9CQMZ09CcHDbHfbLRDNuAhn1Mvkr1ZNF7hPmztMQvtTGVLJ9m8IZqWsTkXOml8dbw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.24.0 - '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.24.0) + '@babel/core': 7.23.3 + '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.3) '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/preset-env@7.23.6(@babel/core@7.24.0): + /@babel/preset-env@7.23.6(@babel/core@7.23.3): resolution: {integrity: sha512-2XPn/BqKkZCpzYhUUNZ1ssXw7DcXfKQEjv/uXZUXgaebCMYmkEsfZ2yY+vv+xtXv50WmL5SGhyB6/xsWxIvvOQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/compat-data': 7.23.5 - '@babel/core': 7.24.0 + '@babel/core': 7.23.3 '@babel/helper-compilation-targets': 7.23.6 '@babel/helper-plugin-utils': 7.22.5 '@babel/helper-validator-option': 7.23.5 - '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.23.3(@babel/core@7.24.0) - '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.23.3(@babel/core@7.24.0) - '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.23.3(@babel/core@7.24.0) - '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.24.0) - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.24.0) - '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.24.0) - '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.24.0) - '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.24.0) - '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.24.0) - '@babel/plugin-syntax-import-assertions': 7.23.3(@babel/core@7.24.0) - '@babel/plugin-syntax-import-attributes': 7.23.3(@babel/core@7.24.0) - '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.24.0) - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.24.0) - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.24.0) - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.24.0) - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.24.0) - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.24.0) - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.24.0) - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.24.0) - '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.24.0) - '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.24.0) - '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.24.0) - '@babel/plugin-transform-arrow-functions': 7.23.3(@babel/core@7.24.0) - '@babel/plugin-transform-async-generator-functions': 7.23.4(@babel/core@7.24.0) - '@babel/plugin-transform-async-to-generator': 7.23.3(@babel/core@7.24.0) - '@babel/plugin-transform-block-scoped-functions': 7.23.3(@babel/core@7.24.0) - '@babel/plugin-transform-block-scoping': 7.23.4(@babel/core@7.24.0) - '@babel/plugin-transform-class-properties': 7.23.3(@babel/core@7.24.0) - '@babel/plugin-transform-class-static-block': 7.23.4(@babel/core@7.24.0) - '@babel/plugin-transform-classes': 7.23.5(@babel/core@7.24.0) - '@babel/plugin-transform-computed-properties': 7.23.3(@babel/core@7.24.0) - '@babel/plugin-transform-destructuring': 7.23.3(@babel/core@7.24.0) - '@babel/plugin-transform-dotall-regex': 7.23.3(@babel/core@7.24.0) - '@babel/plugin-transform-duplicate-keys': 7.23.3(@babel/core@7.24.0) - '@babel/plugin-transform-dynamic-import': 7.23.4(@babel/core@7.24.0) - '@babel/plugin-transform-exponentiation-operator': 7.23.3(@babel/core@7.24.0) - '@babel/plugin-transform-export-namespace-from': 7.23.4(@babel/core@7.24.0) - '@babel/plugin-transform-for-of': 7.23.6(@babel/core@7.24.0) - '@babel/plugin-transform-function-name': 7.23.3(@babel/core@7.24.0) - '@babel/plugin-transform-json-strings': 7.23.4(@babel/core@7.24.0) - '@babel/plugin-transform-literals': 7.23.3(@babel/core@7.24.0) - '@babel/plugin-transform-logical-assignment-operators': 7.23.4(@babel/core@7.24.0) - '@babel/plugin-transform-member-expression-literals': 7.23.3(@babel/core@7.24.0) - '@babel/plugin-transform-modules-amd': 7.23.3(@babel/core@7.24.0) - '@babel/plugin-transform-modules-commonjs': 7.23.3(@babel/core@7.24.0) - '@babel/plugin-transform-modules-systemjs': 7.23.3(@babel/core@7.24.0) - '@babel/plugin-transform-modules-umd': 7.23.3(@babel/core@7.24.0) - '@babel/plugin-transform-named-capturing-groups-regex': 7.22.5(@babel/core@7.24.0) - '@babel/plugin-transform-new-target': 7.23.3(@babel/core@7.24.0) - '@babel/plugin-transform-nullish-coalescing-operator': 7.23.4(@babel/core@7.24.0) - '@babel/plugin-transform-numeric-separator': 7.23.4(@babel/core@7.24.0) - '@babel/plugin-transform-object-rest-spread': 7.23.4(@babel/core@7.24.0) - '@babel/plugin-transform-object-super': 7.23.3(@babel/core@7.24.0) - '@babel/plugin-transform-optional-catch-binding': 7.23.4(@babel/core@7.24.0) - '@babel/plugin-transform-optional-chaining': 7.23.4(@babel/core@7.24.0) - '@babel/plugin-transform-parameters': 7.23.3(@babel/core@7.24.0) - '@babel/plugin-transform-private-methods': 7.23.3(@babel/core@7.24.0) - '@babel/plugin-transform-private-property-in-object': 7.23.4(@babel/core@7.24.0) - '@babel/plugin-transform-property-literals': 7.23.3(@babel/core@7.24.0) - '@babel/plugin-transform-regenerator': 7.23.3(@babel/core@7.24.0) - '@babel/plugin-transform-reserved-words': 7.23.3(@babel/core@7.24.0) - '@babel/plugin-transform-shorthand-properties': 7.23.3(@babel/core@7.24.0) - '@babel/plugin-transform-spread': 7.23.3(@babel/core@7.24.0) - '@babel/plugin-transform-sticky-regex': 7.23.3(@babel/core@7.24.0) - '@babel/plugin-transform-template-literals': 7.23.3(@babel/core@7.24.0) - '@babel/plugin-transform-typeof-symbol': 7.23.3(@babel/core@7.24.0) - '@babel/plugin-transform-unicode-escapes': 7.23.3(@babel/core@7.24.0) - '@babel/plugin-transform-unicode-property-regex': 7.23.3(@babel/core@7.24.0) - '@babel/plugin-transform-unicode-regex': 7.23.3(@babel/core@7.24.0) - '@babel/plugin-transform-unicode-sets-regex': 7.23.3(@babel/core@7.24.0) - '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.24.0) - babel-plugin-polyfill-corejs2: 0.4.7(@babel/core@7.24.0) - babel-plugin-polyfill-corejs3: 0.8.7(@babel/core@7.24.0) - babel-plugin-polyfill-regenerator: 0.5.4(@babel/core@7.24.0) + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.23.3(@babel/core@7.23.3) + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.23.3(@babel/core@7.23.3) + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.23.3(@babel/core@7.23.3) + '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.23.3) + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.23.3) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.23.3) + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.23.3) + '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.23.3) + '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.23.3) + '@babel/plugin-syntax-import-assertions': 7.23.3(@babel/core@7.23.3) + '@babel/plugin-syntax-import-attributes': 7.23.3(@babel/core@7.23.3) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.23.3) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.23.3) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.23.3) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.23.3) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.23.3) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.23.3) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.23.3) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.23.3) + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.23.3) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.23.3) + '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.23.3) + '@babel/plugin-transform-arrow-functions': 7.23.3(@babel/core@7.23.3) + '@babel/plugin-transform-async-generator-functions': 7.23.4(@babel/core@7.23.3) + '@babel/plugin-transform-async-to-generator': 7.23.3(@babel/core@7.23.3) + '@babel/plugin-transform-block-scoped-functions': 7.23.3(@babel/core@7.23.3) + '@babel/plugin-transform-block-scoping': 7.23.4(@babel/core@7.23.3) + '@babel/plugin-transform-class-properties': 7.23.3(@babel/core@7.23.3) + '@babel/plugin-transform-class-static-block': 7.23.4(@babel/core@7.23.3) + '@babel/plugin-transform-classes': 7.23.5(@babel/core@7.23.3) + '@babel/plugin-transform-computed-properties': 7.23.3(@babel/core@7.23.3) + '@babel/plugin-transform-destructuring': 7.23.3(@babel/core@7.23.3) + '@babel/plugin-transform-dotall-regex': 7.23.3(@babel/core@7.23.3) + '@babel/plugin-transform-duplicate-keys': 7.23.3(@babel/core@7.23.3) + '@babel/plugin-transform-dynamic-import': 7.23.4(@babel/core@7.23.3) + '@babel/plugin-transform-exponentiation-operator': 7.23.3(@babel/core@7.23.3) + '@babel/plugin-transform-export-namespace-from': 7.23.4(@babel/core@7.23.3) + '@babel/plugin-transform-for-of': 7.23.6(@babel/core@7.23.3) + '@babel/plugin-transform-function-name': 7.23.3(@babel/core@7.23.3) + '@babel/plugin-transform-json-strings': 7.23.4(@babel/core@7.23.3) + '@babel/plugin-transform-literals': 7.23.3(@babel/core@7.23.3) + '@babel/plugin-transform-logical-assignment-operators': 7.23.4(@babel/core@7.23.3) + '@babel/plugin-transform-member-expression-literals': 7.23.3(@babel/core@7.23.3) + '@babel/plugin-transform-modules-amd': 7.23.3(@babel/core@7.23.3) + '@babel/plugin-transform-modules-commonjs': 7.23.3(@babel/core@7.23.3) + '@babel/plugin-transform-modules-systemjs': 7.23.3(@babel/core@7.23.3) + '@babel/plugin-transform-modules-umd': 7.23.3(@babel/core@7.23.3) + '@babel/plugin-transform-named-capturing-groups-regex': 7.22.5(@babel/core@7.23.3) + '@babel/plugin-transform-new-target': 7.23.3(@babel/core@7.23.3) + '@babel/plugin-transform-nullish-coalescing-operator': 7.23.4(@babel/core@7.23.3) + '@babel/plugin-transform-numeric-separator': 7.23.4(@babel/core@7.23.3) + '@babel/plugin-transform-object-rest-spread': 7.23.4(@babel/core@7.23.3) + '@babel/plugin-transform-object-super': 7.23.3(@babel/core@7.23.3) + '@babel/plugin-transform-optional-catch-binding': 7.23.4(@babel/core@7.23.3) + '@babel/plugin-transform-optional-chaining': 7.23.4(@babel/core@7.23.3) + '@babel/plugin-transform-parameters': 7.23.3(@babel/core@7.23.3) + '@babel/plugin-transform-private-methods': 7.23.3(@babel/core@7.23.3) + '@babel/plugin-transform-private-property-in-object': 7.23.4(@babel/core@7.23.3) + '@babel/plugin-transform-property-literals': 7.23.3(@babel/core@7.23.3) + '@babel/plugin-transform-regenerator': 7.23.3(@babel/core@7.23.3) + '@babel/plugin-transform-reserved-words': 7.23.3(@babel/core@7.23.3) + '@babel/plugin-transform-shorthand-properties': 7.23.3(@babel/core@7.23.3) + '@babel/plugin-transform-spread': 7.23.3(@babel/core@7.23.3) + '@babel/plugin-transform-sticky-regex': 7.23.3(@babel/core@7.23.3) + '@babel/plugin-transform-template-literals': 7.23.3(@babel/core@7.23.3) + '@babel/plugin-transform-typeof-symbol': 7.23.3(@babel/core@7.23.3) + '@babel/plugin-transform-unicode-escapes': 7.23.3(@babel/core@7.23.3) + '@babel/plugin-transform-unicode-property-regex': 7.23.3(@babel/core@7.23.3) + '@babel/plugin-transform-unicode-regex': 7.23.3(@babel/core@7.23.3) + '@babel/plugin-transform-unicode-sets-regex': 7.23.3(@babel/core@7.23.3) + '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.23.3) + babel-plugin-polyfill-corejs2: 0.4.7(@babel/core@7.23.3) + babel-plugin-polyfill-corejs3: 0.8.7(@babel/core@7.23.3) + babel-plugin-polyfill-regenerator: 0.5.4(@babel/core@7.23.3) core-js-compat: 3.34.0 semver: 6.3.1 transitivePeerDependencies: - supports-color dev: true - /@babel/preset-flow@7.23.3(@babel/core@7.24.0): + /@babel/preset-flow@7.23.3(@babel/core@7.23.3): resolution: {integrity: sha512-7yn6hl8RIv+KNk6iIrGZ+D06VhVY35wLVf23Cz/mMu1zOr7u4MMP4j0nZ9tLf8+4ZFpnib8cFYgB/oYg9hfswA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.0 + '@babel/core': 7.23.3 '@babel/helper-plugin-utils': 7.22.5 '@babel/helper-validator-option': 7.23.5 - '@babel/plugin-transform-flow-strip-types': 7.23.3(@babel/core@7.24.0) + '@babel/plugin-transform-flow-strip-types': 7.23.3(@babel/core@7.23.3) dev: true - /@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.24.0): + /@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.23.3): resolution: {integrity: sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==} peerDependencies: '@babel/core': ^7.0.0-0 || ^8.0.0-0 <8.0.0 dependencies: - '@babel/core': 7.24.0 + '@babel/core': 7.23.3 '@babel/helper-plugin-utils': 7.22.5 - '@babel/types': 7.24.0 + '@babel/types': 7.23.4 esutils: 2.0.3 dev: true - /@babel/preset-typescript@7.23.3(@babel/core@7.24.0): + /@babel/preset-typescript@7.23.3(@babel/core@7.23.3): resolution: {integrity: sha512-17oIGVlqz6CchO9RFYn5U6ZpWRZIngayYCtrPRSgANSwC2V1Jb+iP74nVxzzXJte8b8BYxrL1yY96xfhTBrNNQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.0 + '@babel/core': 7.23.3 '@babel/helper-plugin-utils': 7.22.5 '@babel/helper-validator-option': 7.23.5 - '@babel/plugin-syntax-jsx': 7.23.3(@babel/core@7.24.0) - '@babel/plugin-transform-modules-commonjs': 7.23.3(@babel/core@7.24.0) - '@babel/plugin-transform-typescript': 7.23.6(@babel/core@7.24.0) + '@babel/plugin-syntax-jsx': 7.23.3(@babel/core@7.23.3) + '@babel/plugin-transform-modules-commonjs': 7.23.3(@babel/core@7.23.3) + '@babel/plugin-transform-typescript': 7.23.6(@babel/core@7.23.3) dev: true - /@babel/register@7.22.15(@babel/core@7.24.0): + /@babel/register@7.22.15(@babel/core@7.23.3): resolution: {integrity: sha512-V3Q3EqoQdn65RCgTLwauZaTfd1ShhwPmbBv+1dkZV/HpCGMKVyn6oFcRlI7RaKqiDQjX2Qd3AuoEguBgdjIKlg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.0 + '@babel/core': 7.23.3 clone-deep: 4.0.1 find-cache-dir: 2.1.0 make-dir: 2.1.0 @@ -3448,7 +3320,7 @@ packages: dependencies: '@babel/code-frame': 7.23.4 '@babel/parser': 7.23.4 - '@babel/types': 7.24.0 + '@babel/types': 7.23.4 dev: true /@babel/template@7.24.0: @@ -3460,6 +3332,24 @@ packages: '@babel/types': 7.24.0 dev: true + /@babel/traverse@7.23.4: + resolution: {integrity: sha512-IYM8wSUwunWTB6tFC2dkKZhxbIjHoWemdK+3f8/wq8aKhbUscxD5MX72ubd90fxvFknaLPeGw5ycU84V1obHJg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/code-frame': 7.23.4 + '@babel/generator': 7.23.4 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-function-name': 7.23.0 + '@babel/helper-hoist-variables': 7.22.5 + '@babel/helper-split-export-declaration': 7.22.6 + '@babel/parser': 7.23.4 + '@babel/types': 7.23.4 + debug: 4.3.4(supports-color@8.1.1) + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + dev: true + /@babel/traverse@7.24.0: resolution: {integrity: sha512-HfuJlI8qq3dEDmNU5ChzzpZRWq+oxCZQyMzIMEqLho+AQnhMnKQUzH6ydo3RBl/YjPCuk68Y6s0Gx0AeyULiWw==} engines: {node: '>=6.9.0'} @@ -3478,6 +3368,22 @@ packages: - supports-color dev: true + /@babel/types@7.22.17: + resolution: {integrity: sha512-YSQPHLFtQNE5xN9tHuZnzu8vPr61wVTBZdfv1meex1NBosa4iT05k/Jw06ddJugi4bk7The/oSwQGFcksmEJQg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-string-parser': 7.22.5 + '@babel/helper-validator-identifier': 7.22.15 + to-fast-properties: 2.0.0 + + /@babel/types@7.23.4: + resolution: {integrity: sha512-7uIFwVYpoplT5jp/kVv6EF93VaJ8H+Yn5IczYiaAi98ajzjfoZfslet/e0sLh+wVBjb2qqIut1b0S26VSafsSQ==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-string-parser': 7.23.4 + '@babel/helper-validator-identifier': 7.22.20 + to-fast-properties: 2.0.0 + /@babel/types@7.24.0: resolution: {integrity: sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==} engines: {node: '>=6.9.0'} @@ -3485,6 +3391,7 @@ packages: '@babel/helper-string-parser': 7.23.4 '@babel/helper-validator-identifier': 7.22.20 to-fast-properties: 2.0.0 + dev: true /@base2/pretty-print-object@1.0.1: resolution: {integrity: sha512-4iri8i1AqYHJE2DstZYkyEprg6Pq6sKx3xn5FpySk9sNhH7qN2LLlHJCfDTZRILNwQNPD7mATWM0TBui7uC1pA==} @@ -4798,7 +4705,7 @@ packages: resolution: {integrity: sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@babel/core': 7.24.0 + '@babel/core': 7.23.3 '@jest/types': 29.6.3 '@jridgewell/trace-mapping': 0.3.20 babel-plugin-istanbul: 6.1.1 @@ -4990,7 +4897,7 @@ packages: eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.11.0)(eslint@8.53.0) dev: true - /@misskey-dev/eslint-plugin@1.0.0(@typescript-eslint/eslint-plugin@6.21.0)(@typescript-eslint/parser@7.1.0)(eslint-plugin-import@2.29.1)(eslint@8.57.0): + /@misskey-dev/eslint-plugin@1.0.0(@typescript-eslint/eslint-plugin@6.18.1)(@typescript-eslint/parser@7.1.0)(eslint-plugin-import@2.29.1)(eslint@8.57.0): resolution: {integrity: sha512-dh6UbcrNDVg5DD8k8Qh4ab30OPpuEYIlJCqaBV/lkIV8wNN/AfCJ2V7iTP8V8KjryM4t+sf5IqzQLQnT0mWI4A==} peerDependencies: '@typescript-eslint/eslint-plugin': '>= 6' @@ -4998,7 +4905,7 @@ packages: eslint: '>= 3' eslint-plugin-import: '>= 2' dependencies: - '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@7.1.0)(eslint@8.57.0)(typescript@5.3.3) + '@typescript-eslint/eslint-plugin': 6.18.1(@typescript-eslint/parser@7.1.0)(eslint@8.57.0)(typescript@5.3.3) '@typescript-eslint/parser': 7.1.0(eslint@8.57.0)(typescript@5.3.3) eslint: 8.57.0 eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.1.0)(eslint@8.57.0) @@ -5440,7 +5347,7 @@ packages: optional: true dependencies: '@rollup/pluginutils': 5.1.0(rollup@4.12.0) - magic-string: 0.30.7 + magic-string: 0.30.5 rollup: 4.12.0 dev: false @@ -6172,7 +6079,7 @@ packages: /@storybook/addon-docs@8.0.0-beta.6: resolution: {integrity: sha512-VLys4EuL8XVhmu1QxUiUG5keID8v/FsC5L71Y0Wcf5D+ll6ZD8vCqEtbMY3TiJJ9NqqNIcmcG3bG6JVXOYcD8g==} dependencies: - '@babel/core': 7.24.0 + '@babel/core': 7.23.3 '@mdx-js/react': 3.0.1(@types/react@18.0.28)(react@18.2.0) '@storybook/blocks': 8.0.0-beta.6(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0) '@storybook/client-logger': 8.0.0-beta.6 @@ -6413,8 +6320,8 @@ packages: resolution: {integrity: sha512-sREQYnPds2bwQS7FLbRy7oaxGvOmYhPEYVf93pWKyo/qwSWyXEXbqGCGT6bNhSl/xzqXX7VryLDmuOoHmVTh1g==} hasBin: true dependencies: - '@babel/core': 7.24.0 - '@babel/types': 7.24.0 + '@babel/core': 7.23.3 + '@babel/types': 7.23.4 '@ndelangen/get-tarball': 3.0.7 '@storybook/codemod': 8.0.0-beta.6 '@storybook/core-common': 8.0.0-beta.6 @@ -6444,7 +6351,7 @@ packages: prettier: 3.2.5 prompts: 2.4.2 read-pkg-up: 7.0.1 - semver: 7.6.0 + semver: 7.5.4 strip-json-comments: 3.1.1 tempy: 1.0.1 tiny-invariant: 1.3.1 @@ -6468,9 +6375,9 @@ packages: /@storybook/codemod@8.0.0-beta.6: resolution: {integrity: sha512-ttQYDkhKmtU6Qbg+Kgn4K2XXf8XMpa2euuC6PmYffBD7/qLiGfABfBc4FHKRv4yScnvKK7Ehy7K0lvipfg6tXw==} dependencies: - '@babel/core': 7.24.0 - '@babel/preset-env': 7.23.6(@babel/core@7.24.0) - '@babel/types': 7.24.0 + '@babel/core': 7.23.3 + '@babel/preset-env': 7.23.6(@babel/core@7.23.3) + '@babel/types': 7.23.4 '@storybook/csf': 0.1.2 '@storybook/csf-tools': 8.0.0-beta.6 '@storybook/node-logger': 8.0.0-beta.6 @@ -6617,10 +6524,10 @@ packages: /@storybook/csf-tools@8.0.0-beta.6: resolution: {integrity: sha512-wwzbE6f8ykrvIeZlXYTba0IA8D5GPSyZ4L0+PqRAYHm3ozu0DXqtm4USDHKrjYAzuD+W+fG/6qIOQmsWYbNmpA==} dependencies: - '@babel/generator': 7.23.6 - '@babel/parser': 7.24.0 - '@babel/traverse': 7.24.0 - '@babel/types': 7.24.0 + '@babel/generator': 7.23.4 + '@babel/parser': 7.23.9 + '@babel/traverse': 7.23.4 + '@babel/types': 7.23.4 '@storybook/csf': 0.1.2 '@storybook/types': 8.0.0-beta.6 fs-extra: 11.1.1 @@ -6936,7 +6843,7 @@ packages: ts-dedent: 2.2.0 type-fest: 2.19.0 vue: 3.4.21(typescript@5.3.3) - vue-component-type-helpers: 1.8.27 + vue-component-type-helpers: 2.0.6 transitivePeerDependencies: - encoding - supports-color @@ -7479,8 +7386,8 @@ packages: /@types/babel__core@7.20.5: resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} dependencies: - '@babel/parser': 7.23.6 - '@babel/types': 7.24.0 + '@babel/parser': 7.23.9 + '@babel/types': 7.23.4 '@types/babel__generator': 7.6.7 '@types/babel__template': 7.4.4 '@types/babel__traverse': 7.20.4 @@ -7489,20 +7396,20 @@ packages: /@types/babel__generator@7.6.7: resolution: {integrity: sha512-6Sfsq+EaaLrw4RmdFWE9Onp63TOUue71AWb4Gpa6JxzgTYtimbM086WnYTy2U67AofR++QKCo08ZP6pwx8YFHQ==} dependencies: - '@babel/types': 7.24.0 + '@babel/types': 7.23.4 dev: true /@types/babel__template@7.4.4: resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} dependencies: - '@babel/parser': 7.23.6 - '@babel/types': 7.24.0 + '@babel/parser': 7.23.9 + '@babel/types': 7.23.4 dev: true /@types/babel__traverse@7.20.4: resolution: {integrity: sha512-mSM/iKUk5fDDrEV/e83qY+Cr3I1+Q3qqTuEn++HAWYjEa1+NxZr6CNrcJGf2ZTnq4HoFGC3zaTPZTobCzCFukA==} dependencies: - '@babel/types': 7.24.0 + '@babel/types': 7.23.4 dev: true /@types/bcryptjs@2.4.6: @@ -7675,7 +7582,7 @@ packages: /@types/hast@3.0.4: resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} dependencies: - '@types/unist': 2.0.6 + '@types/unist': 3.0.2 dev: true /@types/htmlescape@1.1.3: @@ -7770,7 +7677,7 @@ packages: /@types/mdast@4.0.3: resolution: {integrity: sha512-LsjtqsyF+d2/yFOYaN22dHZI1Cpwkrj+g06G8+qtUKlhovPW89YhqSnfKtMbkgmEtYpH2gydRNULd6y8mciAFg==} dependencies: - '@types/unist': 2.0.6 + '@types/unist': 3.0.2 dev: true /@types/mdx@2.0.3: @@ -8014,10 +7921,6 @@ packages: resolution: {integrity: sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw==} dev: true - /@types/unist@2.0.6: - resolution: {integrity: sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==} - dev: true - /@types/unist@3.0.2: resolution: {integrity: sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==} dev: true @@ -8102,8 +8005,8 @@ packages: - supports-color dev: true - /@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.54.0)(typescript@5.1.6): - resolution: {integrity: sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==} + /@typescript-eslint/eslint-plugin@6.12.0(@typescript-eslint/parser@6.12.0)(eslint@8.54.0)(typescript@5.1.6): + resolution: {integrity: sha512-XOpZ3IyJUIV1b15M7HVOpgQxPPF7lGXgsfcEIu3yDxFPaf/xZKt7s9QO/pbk7vpWQyVulpJbu4E5LwpZiQo4kA==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: '@typescript-eslint/parser': ^6.0.0 || ^6.0.0-alpha @@ -8114,25 +8017,25 @@ packages: optional: true dependencies: '@eslint-community/regexpp': 4.10.0 - '@typescript-eslint/parser': 6.21.0(eslint@8.54.0)(typescript@5.1.6) - '@typescript-eslint/scope-manager': 6.21.0 - '@typescript-eslint/type-utils': 6.21.0(eslint@8.54.0)(typescript@5.1.6) - '@typescript-eslint/utils': 6.21.0(eslint@8.54.0)(typescript@5.1.6) - '@typescript-eslint/visitor-keys': 6.21.0 + '@typescript-eslint/parser': 6.12.0(eslint@8.54.0)(typescript@5.1.6) + '@typescript-eslint/scope-manager': 6.12.0 + '@typescript-eslint/type-utils': 6.12.0(eslint@8.54.0)(typescript@5.1.6) + '@typescript-eslint/utils': 6.12.0(eslint@8.54.0)(typescript@5.1.6) + '@typescript-eslint/visitor-keys': 6.12.0 debug: 4.3.4(supports-color@8.1.1) eslint: 8.54.0 graphemer: 1.4.0 ignore: 5.3.0 natural-compare: 1.4.0 - semver: 7.6.0 + semver: 7.5.4 ts-api-utils: 1.0.3(typescript@5.1.6) typescript: 5.1.6 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@7.1.0)(eslint@8.57.0)(typescript@5.3.3): - resolution: {integrity: sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==} + /@typescript-eslint/eslint-plugin@6.18.1(@typescript-eslint/parser@7.1.0)(eslint@8.57.0)(typescript@5.3.3): + resolution: {integrity: sha512-nISDRYnnIpk7VCFrGcu1rnZfM1Dh9LRHnfgdkjcbi/l7g16VYRri3TjXi9Ir4lOZSw5N/gnV/3H7jIPQ8Q4daA==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: '@typescript-eslint/parser': ^6.0.0 || ^6.0.0-alpha @@ -8144,10 +8047,10 @@ packages: dependencies: '@eslint-community/regexpp': 4.10.0 '@typescript-eslint/parser': 7.1.0(eslint@8.57.0)(typescript@5.3.3) - '@typescript-eslint/scope-manager': 6.21.0 - '@typescript-eslint/type-utils': 6.21.0(eslint@8.57.0)(typescript@5.3.3) - '@typescript-eslint/utils': 6.21.0(eslint@8.57.0)(typescript@5.3.3) - '@typescript-eslint/visitor-keys': 6.21.0 + '@typescript-eslint/scope-manager': 6.18.1 + '@typescript-eslint/type-utils': 6.18.1(eslint@8.57.0)(typescript@5.3.3) + '@typescript-eslint/utils': 6.18.1(eslint@8.57.0)(typescript@5.3.3) + '@typescript-eslint/visitor-keys': 6.18.1 debug: 4.3.4(supports-color@8.1.1) eslint: 8.57.0 graphemer: 1.4.0 @@ -8182,7 +8085,7 @@ packages: graphemer: 1.4.0 ignore: 5.3.0 natural-compare: 1.4.0 - semver: 7.6.0 + semver: 7.5.4 ts-api-utils: 1.0.3(typescript@5.3.3) typescript: 5.3.3 transitivePeerDependencies: @@ -8210,8 +8113,8 @@ packages: - supports-color dev: true - /@typescript-eslint/parser@6.21.0(eslint@8.54.0)(typescript@5.1.6): - resolution: {integrity: sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==} + /@typescript-eslint/parser@6.12.0(eslint@8.54.0)(typescript@5.1.6): + resolution: {integrity: sha512-s8/jNFPKPNRmXEnNXfuo1gemBdVmpQsK1pcu+QIvuNJuhFzGrpD7WjOcvDc/+uEdfzSYpNu7U/+MmbScjoQ6vg==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: eslint: ^7.0.0 || ^8.0.0 @@ -8220,10 +8123,10 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/scope-manager': 6.21.0 - '@typescript-eslint/types': 6.21.0 - '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.1.6) - '@typescript-eslint/visitor-keys': 6.21.0 + '@typescript-eslint/scope-manager': 6.12.0 + '@typescript-eslint/types': 6.12.0 + '@typescript-eslint/typescript-estree': 6.12.0(typescript@5.1.6) + '@typescript-eslint/visitor-keys': 6.12.0 debug: 4.3.4(supports-color@8.1.1) eslint: 8.54.0 typescript: 5.1.6 @@ -8260,12 +8163,20 @@ packages: '@typescript-eslint/visitor-keys': 6.11.0 dev: true - /@typescript-eslint/scope-manager@6.21.0: - resolution: {integrity: sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==} + /@typescript-eslint/scope-manager@6.12.0: + resolution: {integrity: sha512-5gUvjg+XdSj8pcetdL9eXJzQNTl3RD7LgUiYTl8Aabdi8hFkaGSYnaS6BLc0BGNaDH+tVzVwmKtWvu0jLgWVbw==} engines: {node: ^16.0.0 || >=18.0.0} dependencies: - '@typescript-eslint/types': 6.21.0 - '@typescript-eslint/visitor-keys': 6.21.0 + '@typescript-eslint/types': 6.12.0 + '@typescript-eslint/visitor-keys': 6.12.0 + dev: true + + /@typescript-eslint/scope-manager@6.18.1: + resolution: {integrity: sha512-BgdBwXPFmZzaZUuw6wKiHKIovms97a7eTImjkXCZE04TGHysG+0hDQPmygyvgtkoB/aOQwSM/nWv3LzrOIQOBw==} + engines: {node: ^16.0.0 || >=18.0.0} + dependencies: + '@typescript-eslint/types': 6.18.1 + '@typescript-eslint/visitor-keys': 6.18.1 dev: true /@typescript-eslint/scope-manager@7.1.0: @@ -8296,8 +8207,8 @@ packages: - supports-color dev: true - /@typescript-eslint/type-utils@6.21.0(eslint@8.54.0)(typescript@5.1.6): - resolution: {integrity: sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==} + /@typescript-eslint/type-utils@6.12.0(eslint@8.54.0)(typescript@5.1.6): + resolution: {integrity: sha512-WWmRXxhm1X8Wlquj+MhsAG4dU/Blvf1xDgGaYCzfvStP2NwPQh6KBvCDbiOEvaE0filhranjIlK/2fSTVwtBng==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: eslint: ^7.0.0 || ^8.0.0 @@ -8306,8 +8217,8 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.1.6) - '@typescript-eslint/utils': 6.21.0(eslint@8.54.0)(typescript@5.1.6) + '@typescript-eslint/typescript-estree': 6.12.0(typescript@5.1.6) + '@typescript-eslint/utils': 6.12.0(eslint@8.54.0)(typescript@5.1.6) debug: 4.3.4(supports-color@8.1.1) eslint: 8.54.0 ts-api-utils: 1.0.3(typescript@5.1.6) @@ -8316,8 +8227,8 @@ packages: - supports-color dev: true - /@typescript-eslint/type-utils@6.21.0(eslint@8.57.0)(typescript@5.3.3): - resolution: {integrity: sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==} + /@typescript-eslint/type-utils@6.18.1(eslint@8.57.0)(typescript@5.3.3): + resolution: {integrity: sha512-wyOSKhuzHeU/5pcRDP2G2Ndci+4g653V43gXTpt4nbyoIOAASkGDA9JIAgbQCdCkcr1MvpSYWzxTz0olCn8+/Q==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: eslint: ^7.0.0 || ^8.0.0 @@ -8326,8 +8237,8 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.3.3) - '@typescript-eslint/utils': 6.21.0(eslint@8.57.0)(typescript@5.3.3) + '@typescript-eslint/typescript-estree': 6.18.1(typescript@5.3.3) + '@typescript-eslint/utils': 6.18.1(eslint@8.57.0)(typescript@5.3.3) debug: 4.3.4(supports-color@8.1.1) eslint: 8.57.0 ts-api-utils: 1.0.3(typescript@5.3.3) @@ -8361,8 +8272,13 @@ packages: engines: {node: ^16.0.0 || >=18.0.0} dev: true - /@typescript-eslint/types@6.21.0: - resolution: {integrity: sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==} + /@typescript-eslint/types@6.12.0: + resolution: {integrity: sha512-MA16p/+WxM5JG/F3RTpRIcuOghWO30//VEOvzubM8zuOOBYXsP+IfjoCXXiIfy2Ta8FRh9+IO9QLlaFQUU+10Q==} + engines: {node: ^16.0.0 || >=18.0.0} + dev: true + + /@typescript-eslint/types@6.18.1: + resolution: {integrity: sha512-4TuMAe+tc5oA7wwfqMtB0Y5OrREPF1GeJBAjqwgZh1lEMH5PJQgWgHGfYufVB51LtjD+peZylmeyxUXPfENLCw==} engines: {node: ^16.0.0 || >=18.0.0} dev: true @@ -8392,8 +8308,8 @@ packages: - supports-color dev: true - /@typescript-eslint/typescript-estree@6.21.0(typescript@5.1.6): - resolution: {integrity: sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==} + /@typescript-eslint/typescript-estree@6.12.0(typescript@5.1.6): + resolution: {integrity: sha512-vw9E2P9+3UUWzhgjyyVczLWxZ3GuQNT7QpnIY3o5OMeLO/c8oHljGc8ZpryBMIyympiAAaKgw9e5Hl9dCWFOYw==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: typescript: '*' @@ -8401,21 +8317,20 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/types': 6.21.0 - '@typescript-eslint/visitor-keys': 6.21.0 + '@typescript-eslint/types': 6.12.0 + '@typescript-eslint/visitor-keys': 6.12.0 debug: 4.3.4(supports-color@8.1.1) globby: 11.1.0 is-glob: 4.0.3 - minimatch: 9.0.3 - semver: 7.6.0 + semver: 7.5.4 ts-api-utils: 1.0.3(typescript@5.1.6) typescript: 5.1.6 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/typescript-estree@6.21.0(typescript@5.3.3): - resolution: {integrity: sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==} + /@typescript-eslint/typescript-estree@6.18.1(typescript@5.3.3): + resolution: {integrity: sha512-fv9B94UAhywPRhUeeV/v+3SBDvcPiLxRZJw/xZeeGgRLQZ6rLMG+8krrJUyIf6s1ecWTzlsbp0rlw7n9sjufHA==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: typescript: '*' @@ -8423,8 +8338,8 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/types': 6.21.0 - '@typescript-eslint/visitor-keys': 6.21.0 + '@typescript-eslint/types': 6.18.1 + '@typescript-eslint/visitor-keys': 6.18.1 debug: 4.3.4(supports-color@8.1.1) globby: 11.1.0 is-glob: 4.0.3 @@ -8451,7 +8366,7 @@ packages: globby: 11.1.0 is-glob: 4.0.3 minimatch: 9.0.3 - semver: 7.6.0 + semver: 7.5.4 ts-api-utils: 1.0.3(typescript@5.3.3) typescript: 5.3.3 transitivePeerDependencies: @@ -8477,8 +8392,8 @@ packages: - typescript dev: true - /@typescript-eslint/utils@6.21.0(eslint@8.54.0)(typescript@5.1.6): - resolution: {integrity: sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==} + /@typescript-eslint/utils@6.12.0(eslint@8.54.0)(typescript@5.1.6): + resolution: {integrity: sha512-LywPm8h3tGEbgfyjYnu3dauZ0U7R60m+miXgKcZS8c7QALO9uWJdvNoP+duKTk2XMWc7/Q3d/QiCuLN9X6SWyQ==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: eslint: ^7.0.0 || ^8.0.0 @@ -8486,18 +8401,18 @@ packages: '@eslint-community/eslint-utils': 4.4.0(eslint@8.54.0) '@types/json-schema': 7.0.15 '@types/semver': 7.5.8 - '@typescript-eslint/scope-manager': 6.21.0 - '@typescript-eslint/types': 6.21.0 - '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.1.6) + '@typescript-eslint/scope-manager': 6.12.0 + '@typescript-eslint/types': 6.12.0 + '@typescript-eslint/typescript-estree': 6.12.0(typescript@5.1.6) eslint: 8.54.0 - semver: 7.6.0 + semver: 7.5.4 transitivePeerDependencies: - supports-color - typescript dev: true - /@typescript-eslint/utils@6.21.0(eslint@8.57.0)(typescript@5.3.3): - resolution: {integrity: sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==} + /@typescript-eslint/utils@6.18.1(eslint@8.57.0)(typescript@5.3.3): + resolution: {integrity: sha512-zZmTuVZvD1wpoceHvoQpOiewmWu3uP9FuTWo8vqpy2ffsmfCE8mklRPi+vmnIYAIk9t/4kOThri2QCDgor+OpQ==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: eslint: ^7.0.0 || ^8.0.0 @@ -8505,9 +8420,9 @@ packages: '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) '@types/json-schema': 7.0.15 '@types/semver': 7.5.8 - '@typescript-eslint/scope-manager': 6.21.0 - '@typescript-eslint/types': 6.21.0 - '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.3.3) + '@typescript-eslint/scope-manager': 6.18.1 + '@typescript-eslint/types': 6.18.1 + '@typescript-eslint/typescript-estree': 6.18.1(typescript@5.3.3) eslint: 8.57.0 semver: 7.6.0 transitivePeerDependencies: @@ -8528,7 +8443,7 @@ packages: '@typescript-eslint/types': 7.1.0 '@typescript-eslint/typescript-estree': 7.1.0(typescript@5.3.3) eslint: 8.57.0 - semver: 7.6.0 + semver: 7.5.4 transitivePeerDependencies: - supports-color - typescript @@ -8542,11 +8457,19 @@ packages: eslint-visitor-keys: 3.4.3 dev: true - /@typescript-eslint/visitor-keys@6.21.0: - resolution: {integrity: sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==} + /@typescript-eslint/visitor-keys@6.12.0: + resolution: {integrity: sha512-rg3BizTZHF1k3ipn8gfrzDXXSFKyOEB5zxYXInQ6z0hUvmQlhaZQzK+YmHmNViMA9HzW5Q9+bPPt90bU6GQwyw==} engines: {node: ^16.0.0 || >=18.0.0} dependencies: - '@typescript-eslint/types': 6.21.0 + '@typescript-eslint/types': 6.12.0 + eslint-visitor-keys: 3.4.3 + dev: true + + /@typescript-eslint/visitor-keys@6.18.1: + resolution: {integrity: sha512-/kvt0C5lRqGoCfsbmm7/CwMqoSkY3zzHLIjdhHZQW3VFrnz7ATecOHR7nb7V+xn4286MBxfnQfQhAmCI0u+bJA==} + engines: {node: ^16.0.0 || >=18.0.0} + dependencies: + '@typescript-eslint/types': 6.18.1 eslint-visitor-keys: 3.4.3 dev: true @@ -8621,7 +8544,7 @@ packages: /@vitest/snapshot@0.34.6: resolution: {integrity: sha512-B3OZqYn6k4VaN011D+ve+AA4whM4QkcwcrwaKwAbyyvS/NB1hCWjFIBQxAQQSQir9/RtyAAGuq+4RJmbn2dH4w==} dependencies: - magic-string: 0.30.5 + magic-string: 0.30.7 pathe: 1.1.2 pretty-format: 29.7.0 dev: true @@ -9315,12 +9238,12 @@ packages: resolution: {integrity: sha512-fpWrvyVHEKyeEvbKZTVOeZF3VSKKWtJxFIxX/jaVPf+cLbGUSitjb49pHLqPV2BUNNZ0LcoeEGfE/YCpyDYHIw==} dev: false - /babel-core@7.0.0-bridge.0(@babel/core@7.24.0): + /babel-core@7.0.0-bridge.0(@babel/core@7.23.3): resolution: {integrity: sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.0 + '@babel/core': 7.23.3 dev: true /babel-jest@29.7.0(@babel/core@7.23.3): @@ -9358,44 +9281,44 @@ packages: resolution: {integrity: sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@babel/template': 7.24.0 - '@babel/types': 7.24.0 + '@babel/template': 7.22.15 + '@babel/types': 7.23.4 '@types/babel__core': 7.20.5 '@types/babel__traverse': 7.20.4 dev: true - /babel-plugin-polyfill-corejs2@0.4.7(@babel/core@7.24.0): + /babel-plugin-polyfill-corejs2@0.4.7(@babel/core@7.23.3): resolution: {integrity: sha512-LidDk/tEGDfuHW2DWh/Hgo4rmnw3cduK6ZkOI1NPFceSK3n/yAGeOsNT7FLnSGHkXj3RHGSEVkN3FsCTY6w2CQ==} peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 dependencies: '@babel/compat-data': 7.23.5 - '@babel/core': 7.24.0 - '@babel/helper-define-polyfill-provider': 0.4.4(@babel/core@7.24.0) + '@babel/core': 7.23.3 + '@babel/helper-define-polyfill-provider': 0.4.4(@babel/core@7.23.3) semver: 6.3.1 transitivePeerDependencies: - supports-color dev: true - /babel-plugin-polyfill-corejs3@0.8.7(@babel/core@7.24.0): + /babel-plugin-polyfill-corejs3@0.8.7(@babel/core@7.23.3): resolution: {integrity: sha512-KyDvZYxAzkC0Aj2dAPyDzi2Ym15e5JKZSK+maI7NAwSqofvuFglbSsxE7wUOvTg9oFVnHMzVzBKcqEb4PJgtOA==} peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 dependencies: - '@babel/core': 7.24.0 - '@babel/helper-define-polyfill-provider': 0.4.4(@babel/core@7.24.0) + '@babel/core': 7.23.3 + '@babel/helper-define-polyfill-provider': 0.4.4(@babel/core@7.23.3) core-js-compat: 3.34.0 transitivePeerDependencies: - supports-color dev: true - /babel-plugin-polyfill-regenerator@0.5.4(@babel/core@7.24.0): + /babel-plugin-polyfill-regenerator@0.5.4(@babel/core@7.23.3): resolution: {integrity: sha512-S/x2iOCvDaCASLYsOOgWOq4bCfKYVqvO/uxjkaYyZ3rVsVE3CeAI/c84NpyuBBymEgNvHgjEot3a9/Z/kXvqsg==} peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 dependencies: - '@babel/core': 7.24.0 - '@babel/helper-define-polyfill-provider': 0.4.4(@babel/core@7.24.0) + '@babel/core': 7.23.3 + '@babel/helper-define-polyfill-provider': 0.4.4(@babel/core@7.23.3) transitivePeerDependencies: - supports-color dev: true @@ -9435,7 +9358,7 @@ packages: resolution: {integrity: sha512-GAwkz0AihzY5bkwIY5QDR+LvsRQgB/B+1foMPvi0FZPMl5fjD7ICiznUiBdLYMH1QYe6vqu4gWYytZOccLouFw==} engines: {node: '>= 10.0.0'} dependencies: - '@babel/types': 7.24.0 + '@babel/types': 7.22.17 /bail@2.0.2: resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} @@ -9621,7 +9544,7 @@ packages: hasBin: true dependencies: caniuse-lite: 1.0.30001564 - electron-to-chromium: 1.4.686 + electron-to-chromium: 1.4.591 node-releases: 2.0.13 update-browserslist-db: 1.0.13(browserslist@4.22.1) dev: true @@ -9631,8 +9554,8 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true dependencies: - caniuse-lite: 1.0.30001591 - electron-to-chromium: 1.4.686 + caniuse-lite: 1.0.30001571 + electron-to-chromium: 1.4.616 node-releases: 2.0.14 update-browserslist-db: 1.0.13(browserslist@4.22.2) dev: true @@ -9642,10 +9565,11 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true dependencies: - caniuse-lite: 1.0.30001591 - electron-to-chromium: 1.4.686 + caniuse-lite: 1.0.30001594 + electron-to-chromium: 1.4.692 node-releases: 2.0.14 update-browserslist-db: 1.0.13(browserslist@4.23.0) + dev: false /bs-logger@0.2.6: resolution: {integrity: sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==} @@ -9838,7 +9762,7 @@ packages: resolution: {integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==} dependencies: browserslist: 4.23.0 - caniuse-lite: 1.0.30001591 + caniuse-lite: 1.0.30001571 lodash.memoize: 4.1.2 lodash.uniq: 4.5.0 dev: false @@ -9847,8 +9771,12 @@ packages: resolution: {integrity: sha512-DqAOf+rhof+6GVx1y+xzbFPeOumfQnhYzVnZD6LAXijR77yPtm9mfOcqOnT3mpnJiZVT+kwLAFnRlZcIz+c6bg==} dev: true - /caniuse-lite@1.0.30001591: - resolution: {integrity: sha512-PCzRMei/vXjJyL5mJtzNiUCKP59dm8Apqc3PH8gJkMnMXZGox93RbE76jHsmLwmIo6/3nsYIpJtx0O7u5PqFuQ==} + /caniuse-lite@1.0.30001571: + resolution: {integrity: sha512-tYq/6MoXhdezDLFZuCO/TKboTzuQ/xR5cFdgXPfDtM7/kchBO3b4VWghE/OAi/DV7tTdhmLjZiZBZi1fA/GheQ==} + + /caniuse-lite@1.0.30001594: + resolution: {integrity: sha512-VblSX6nYqyJVs8DKFMldE2IVCJjZ225LW00ydtUWwh5hk9IfkTOffO6r8gJNsH0qqqeAF8KrbMYA2VEwTlGW5g==} + dev: false /canonicalize@1.0.8: resolution: {integrity: sha512-0CNTVCLZggSh7bc5VkX5WWPWO+cyZbNd07IHIsSXLia/eAq+r836hgk+8BKoEh7949Mda87VUOitx5OddVj64A==} @@ -10344,7 +10272,7 @@ packages: resolution: {integrity: sha512-vCrqcSIq4//Gx74TXXCGnHpulY1dskqLTFGDmhrGxzeXL8lF8kvXv6mpNWlJj1uD4DW23D4ljAqbY4RRaaUZIw==} dependencies: '@babel/parser': 7.22.16 - '@babel/types': 7.24.0 + '@babel/types': 7.22.17 /content-disposition@0.5.4: resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} @@ -10459,6 +10387,7 @@ packages: node-fetch: 2.7.0 transitivePeerDependencies: - encoding + dev: false /cross-fetch@4.0.0: resolution: {integrity: sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==} @@ -10836,15 +10765,9 @@ packages: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} dev: true - /deepmerge@4.2.2: - resolution: {integrity: sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==} - engines: {node: '>=0.10.0'} - dev: false - /deepmerge@4.3.1: resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} engines: {node: '>=0.10.0'} - dev: true /default-browser-id@3.0.0: resolution: {integrity: sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==} @@ -11022,14 +10945,6 @@ packages: dependencies: domelementtype: 2.3.0 - /domutils@3.0.1: - resolution: {integrity: sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==} - dependencies: - dom-serializer: 2.0.0 - domelementtype: 2.3.0 - domhandler: 5.0.3 - dev: false - /domutils@3.1.0: resolution: {integrity: sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==} dependencies: @@ -11095,8 +11010,17 @@ packages: dependencies: jake: 10.8.5 - /electron-to-chromium@1.4.686: - resolution: {integrity: sha512-3avY1B+vUzNxEgkBDpKOP8WarvUAEwpRaiCL0He5OKWEFxzaOFiq4WoZEZe7qh0ReS7DiWoHMnYoQCKxNZNzSg==} + /electron-to-chromium@1.4.591: + resolution: {integrity: sha512-vLv/P7wwAPKQoY+CVMyyI6rsTp+A14KGtPXx92oz1FY41AAqa9l6Wkizcixg0LDuJgyeo8xgNN9+9hsnGp66UA==} + dev: true + + /electron-to-chromium@1.4.616: + resolution: {integrity: sha512-1n7zWYh8eS0L9Uy+GskE0lkBUNK83cXTVJI0pU3mGprFsbfSdAc15VTFbo+A+Bq4pwstmL30AVcEU3Fo463lNg==} + dev: true + + /electron-to-chromium@1.4.692: + resolution: {integrity: sha512-d5rZRka9n2Y3MkWRN74IoAsxR0HK3yaAt7T50e3iT9VZmCCQDT3geXUO5ZRMhDToa1pkCeQXuNo+0g+NfDOVPA==} + dev: false /emittery@0.13.1: resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} @@ -12905,15 +12829,6 @@ packages: engines: {node: '>=0.10'} dev: false - /htmlparser2@8.0.1: - resolution: {integrity: sha512-4lVbmc1diZC7GUJQtRQ5yBAeUCL1exyMwmForWkRLnwyzWBFxN633SALPMGYaWZvKe9j1pRZJpauvmxENSp/EA==} - dependencies: - domelementtype: 2.3.0 - domhandler: 5.0.3 - domutils: 3.0.1 - entities: 4.5.0 - dev: false - /htmlparser2@8.0.2: resolution: {integrity: sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==} dependencies: @@ -13557,7 +13472,7 @@ packages: engines: {node: '>=8'} dependencies: '@babel/core': 7.23.3 - '@babel/parser': 7.23.6 + '@babel/parser': 7.23.9 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.2 semver: 6.3.1 @@ -13570,7 +13485,7 @@ packages: engines: {node: '>=10'} dependencies: '@babel/core': 7.23.3 - '@babel/parser': 7.23.6 + '@babel/parser': 7.23.9 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.2 semver: 7.5.4 @@ -13969,10 +13884,10 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@babel/core': 7.23.3 - '@babel/generator': 7.23.6 + '@babel/generator': 7.23.4 '@babel/plugin-syntax-jsx': 7.23.3(@babel/core@7.23.3) '@babel/plugin-syntax-typescript': 7.23.3(@babel/core@7.23.3) - '@babel/types': 7.24.0 + '@babel/types': 7.23.4 '@jest/expect-utils': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 @@ -14145,18 +14060,18 @@ packages: '@babel/preset-env': optional: true dependencies: - '@babel/core': 7.24.0 - '@babel/parser': 7.24.0 - '@babel/plugin-transform-class-properties': 7.23.3(@babel/core@7.24.0) - '@babel/plugin-transform-modules-commonjs': 7.23.3(@babel/core@7.24.0) - '@babel/plugin-transform-nullish-coalescing-operator': 7.23.4(@babel/core@7.24.0) - '@babel/plugin-transform-optional-chaining': 7.23.4(@babel/core@7.24.0) - '@babel/plugin-transform-private-methods': 7.23.3(@babel/core@7.24.0) - '@babel/preset-env': 7.23.6(@babel/core@7.24.0) - '@babel/preset-flow': 7.23.3(@babel/core@7.24.0) - '@babel/preset-typescript': 7.23.3(@babel/core@7.24.0) - '@babel/register': 7.22.15(@babel/core@7.24.0) - babel-core: 7.0.0-bridge.0(@babel/core@7.24.0) + '@babel/core': 7.23.3 + '@babel/parser': 7.23.9 + '@babel/plugin-transform-class-properties': 7.23.3(@babel/core@7.23.3) + '@babel/plugin-transform-modules-commonjs': 7.23.3(@babel/core@7.23.3) + '@babel/plugin-transform-nullish-coalescing-operator': 7.23.4(@babel/core@7.23.3) + '@babel/plugin-transform-optional-chaining': 7.23.4(@babel/core@7.23.3) + '@babel/plugin-transform-private-methods': 7.23.3(@babel/core@7.23.3) + '@babel/preset-env': 7.23.6(@babel/core@7.23.3) + '@babel/preset-flow': 7.23.3(@babel/core@7.23.3) + '@babel/preset-typescript': 7.23.3(@babel/core@7.23.3) + '@babel/register': 7.22.15(@babel/core@7.23.3) + babel-core: 7.0.0-bridge.0(@babel/core@7.23.3) chalk: 4.1.2 flow-parser: 0.202.0 graceful-fs: 4.2.11 @@ -14629,7 +14544,6 @@ packages: engines: {node: '>=12'} dependencies: '@jridgewell/sourcemap-codec': 1.4.15 - dev: true /magic-string@0.30.7: resolution: {integrity: sha512-8vBuFF/I/+OSLRmdf2wwFCJCz+nSn0m6DPvGH1fS/KiQoSaR+sETbov0eIk9KhEKy8CYqIkIAnbohxT/4H0kuA==} @@ -17211,8 +17125,8 @@ packages: engines: {node: '>=16.14.0'} dependencies: '@babel/core': 7.23.3 - '@babel/traverse': 7.24.0 - '@babel/types': 7.24.0 + '@babel/traverse': 7.23.4 + '@babel/types': 7.23.4 '@types/babel__core': 7.20.5 '@types/babel__traverse': 7.20.4 '@types/doctrine': 0.0.9 @@ -17733,9 +17647,9 @@ packages: /sanitize-html@2.12.1: resolution: {integrity: sha512-Plh+JAn0UVDpBRP/xEjsk+xDCoOvMBwQUf/K+/cBAVuTbtX8bj2VB7S1sL1dssVpykqp0/KPSesHrqXtokVBpA==} dependencies: - deepmerge: 4.2.2 + deepmerge: 4.3.1 escape-string-regexp: 4.0.0 - htmlparser2: 8.0.1 + htmlparser2: 8.0.2 is-plain-object: 5.0.0 parse-srcset: 1.0.2 postcss: 8.4.35 @@ -19449,6 +19363,7 @@ packages: browserslist: 4.23.0 escalade: 3.1.1 picocolors: 1.0.0 + dev: false /uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} @@ -19627,7 +19542,7 @@ packages: peerDependencies: vitest: '>=0.16.0' dependencies: - cross-fetch: 3.1.6 + cross-fetch: 3.1.5 vitest: 0.34.6(happy-dom@13.6.2)(sass@1.71.1)(terser@5.28.1) transitivePeerDependencies: - encoding @@ -19770,6 +19685,10 @@ packages: resolution: {integrity: sha512-6bnLkn8O0JJyiFSIF0EfCogzeqNXpnjJ0vW/SZzNHfe6sPx30lTtTXlE5TFs2qhJlAtDFybStVNpL73cPe3OMQ==} dev: true + /vue-component-type-helpers@2.0.6: + resolution: {integrity: sha512-qdGXCtoBrwqk1BT6r2+1Wcvl583ZVkuSZ3or7Y1O2w5AvWtlvvxwjGhmz5DdPJS9xqRdDlgTJ/38ehWnEi0tFA==} + dev: true + /vue-demi@0.14.7(vue@3.4.21): resolution: {integrity: sha512-EOG8KXDQNwkJILkx/gPcoL/7vH+hORoBaKgGe+6W7VFMvCYJfmF2dGbvgDroVnI8LU7/kTu8mbjRZGBU1z9NTA==} engines: {node: '>=12'} @@ -19790,8 +19709,8 @@ packages: peerDependencies: vue: '>=2' dependencies: - '@babel/parser': 7.24.0 - '@babel/types': 7.24.0 + '@babel/parser': 7.23.9 + '@babel/types': 7.23.4 '@vue/compiler-dom': 3.4.18 '@vue/compiler-sfc': 3.4.21 ast-types: 0.16.1 @@ -20056,7 +19975,7 @@ packages: engines: {node: '>= 10.0.0'} dependencies: '@babel/parser': 7.22.16 - '@babel/types': 7.24.0 + '@babel/types': 7.22.17 assert-never: 1.2.1 babel-walk: 3.0.0-canary-5 |