Merge branch 'develop' into mahjong
This commit is contained in:
		
						commit
						4ae591a2c7
					
				|  | @ -0,0 +1,113 @@ | |||
| name: Storybook | ||||
| 
 | ||||
| on: | ||||
|   push: | ||||
|     branches: | ||||
|       - master | ||||
|       - develop | ||||
|       - dev/storybook8 # for testing | ||||
|   pull_request_target: | ||||
| 
 | ||||
| jobs: | ||||
|   build: | ||||
|     runs-on: ubuntu-latest | ||||
| 
 | ||||
|     env: | ||||
|       NODE_OPTIONS: "--max_old_space_size=7168" | ||||
| 
 | ||||
|     steps: | ||||
|     - uses: actions/checkout@v3.6.0 | ||||
|       if: github.event_name != 'pull_request_target' | ||||
|       with: | ||||
|         fetch-depth: 0 | ||||
|         submodules: true | ||||
|     - uses: actions/checkout@v3.6.0 | ||||
|       if: github.event_name == 'pull_request_target' | ||||
|       with: | ||||
|         fetch-depth: 0 | ||||
|         submodules: true | ||||
|         ref: "refs/pull/${{ github.event.number }}/merge" | ||||
|     - name: Checkout actual HEAD | ||||
|       if: github.event_name == 'pull_request_target' | ||||
|       id: rev | ||||
|       run: | | ||||
|         echo "base=$(git rev-list --parents -n1 HEAD | cut -d" " -f2)" >> $GITHUB_OUTPUT | ||||
|         git checkout $(git rev-list --parents -n1 HEAD | cut -d" " -f3) | ||||
|     - name: Install pnpm | ||||
|       uses: pnpm/action-setup@v2 | ||||
|       with: | ||||
|         version: 8 | ||||
|         run_install: false | ||||
|     - name: Use Node.js 20.x | ||||
|       uses: actions/setup-node@v3.8.1 | ||||
|       with: | ||||
|         node-version-file: '.node-version' | ||||
|         cache: 'pnpm' | ||||
|     - run: corepack enable | ||||
|     - run: pnpm i --frozen-lockfile | ||||
|     - name: Check pnpm-lock.yaml | ||||
|       run: git diff --exit-code pnpm-lock.yaml | ||||
|     - name: Build misskey-js | ||||
|       run: pnpm --filter misskey-js build | ||||
|     - name: Build storybook | ||||
|       run: pnpm --filter frontend build-storybook | ||||
|     - name: Publish to Chromatic | ||||
|       if: github.event_name != 'pull_request_target' && github.ref == 'refs/heads/master' | ||||
|       run: pnpm --filter frontend chromatic --exit-once-uploaded -d storybook-static | ||||
|       env: | ||||
|         CHROMATIC_PROJECT_TOKEN: ${{ secrets.CHROMATIC_PROJECT_TOKEN }} | ||||
|     - name: Publish to Chromatic | ||||
|       if: github.event_name != 'pull_request_target' && github.ref != 'refs/heads/master' | ||||
|       id: chromatic_push | ||||
|       run: | | ||||
|         DIFF="${{ github.event.before }} HEAD" | ||||
|         if [ "$DIFF" = "0000000000000000000000000000000000000000 HEAD" ]; then | ||||
|           DIFF="HEAD" | ||||
|         fi | ||||
|         CHROMATIC_PARAMETER="$(node packages/frontend/.storybook/changes.js $(git diff-tree --no-commit-id --name-only -r $(echo "$DIFF") | xargs))" | ||||
|         if [ "$CHROMATIC_PARAMETER" = " --skip" ]; then | ||||
|           echo "skip=true" >> $GITHUB_OUTPUT | ||||
|         fi | ||||
|         if pnpm --filter frontend chromatic -d storybook-static $(echo "$CHROMATIC_PARAMETER"); then | ||||
|           echo "success=true" >> $GITHUB_OUTPUT | ||||
|         else | ||||
|           echo "success=false" >> $GITHUB_OUTPUT | ||||
|         fi | ||||
|       env: | ||||
|         CHROMATIC_PROJECT_TOKEN: ${{ secrets.CHROMATIC_PROJECT_TOKEN }} | ||||
|     - name: Publish to Chromatic | ||||
|       if: github.event_name == 'pull_request_target' | ||||
|       id: chromatic_pull_request | ||||
|       run: | | ||||
|         DIFF="${{ steps.rev.outputs.base }} HEAD" | ||||
|         if [ "$DIFF" = "0000000000000000000000000000000000000000 HEAD" ]; then | ||||
|           DIFF="HEAD" | ||||
|         fi | ||||
|         CHROMATIC_PARAMETER="$(node packages/frontend/.storybook/changes.js $(git diff-tree --no-commit-id --name-only -r $(echo "$DIFF") | xargs))" | ||||
|         if [ "$CHROMATIC_PARAMETER" = " --skip" ]; then | ||||
|           echo "skip=true" >> $GITHUB_OUTPUT | ||||
|         fi | ||||
|         BRANCH="${{ github.event.pull_request.head.user.login }}:${{ github.event.pull_request.head.ref }}" | ||||
|         if [ "$BRANCH" = "misskey-dev:${{ github.event.pull_request.head.ref }}" ]; then | ||||
|           BRANCH="${{ github.event.pull_request.head.ref }}" | ||||
|         fi | ||||
|         pnpm --filter frontend chromatic --exit-once-uploaded -d storybook-static --branch-name $BRANCH $(echo "$CHROMATIC_PARAMETER") | ||||
|       env: | ||||
|         CHROMATIC_PROJECT_TOKEN: ${{ secrets.CHROMATIC_PROJECT_TOKEN }} | ||||
|     - name: Notify that Chromatic detects changes | ||||
|       uses: actions/github-script@v6.4.0 | ||||
|       if: github.event_name != 'pull_request_target' && steps.chromatic_push.outputs.success == 'false' | ||||
|       with: | ||||
|         github-token: ${{ secrets.GITHUB_TOKEN }} | ||||
|         script: | | ||||
|           github.rest.repos.createCommitComment({ | ||||
|             owner: context.repo.owner, | ||||
|             repo: context.repo.repo, | ||||
|             commit_sha: context.sha, | ||||
|             body: 'Chromatic detects changes. Please [review the changes on Chromatic](https://www.chromatic.com/builds?appId=6428f7d7b962f0b79f97d6e4).' | ||||
|           }) | ||||
|     - name: Upload Artifacts | ||||
|       uses: actions/upload-artifact@v3 | ||||
|       with: | ||||
|         name: storybook | ||||
|         path: packages/frontend/storybook-static | ||||
|  | @ -115,6 +115,7 @@ jobs: | |||
|       run: pnpm exec cypress install | ||||
|     - name: Cypress run | ||||
|       uses: cypress-io/github-action@v6 | ||||
|       timeout-minutes: 15 | ||||
|       with: | ||||
|         install: false | ||||
|         start: pnpm start:test | ||||
|  |  | |||
|  | @ -24,7 +24,7 @@ | |||
| - Fix: リモートユーザーのリアクション一覧がすべて見えてしまうのを修正 | ||||
|   * すべてのリモートユーザーのリアクション一覧を見えないようにします | ||||
| - Enhance: モデレーターはすべてのユーザーのリアクション一覧を見られるように | ||||
| - Fix: 特定のキーワードを含むノートが投稿された際、エラーに出来るような設定項目を追加 #13207 | ||||
| - Fix: 特定のキーワード及び正規表現にマッチする文字列を含むノートが投稿された際、エラーに出来るような設定項目を追加 #13207 | ||||
|   * デフォルトは空欄なので適用前と同等の動作になります | ||||
| 
 | ||||
| ### Client | ||||
|  |  | |||
|  | @ -122,6 +122,19 @@ command. | |||
| If you have not changed it from the default, it will be "http://localhost:3000". | ||||
| If "port" in .config/default.yml is set to something other than 3000, you need to change the proxy settings in packages/frontend/vite.config.local-dev.ts. | ||||
| 
 | ||||
| ### `MK_DEV_PREFER=backend pnpm dev` | ||||
| pnpm dev has another mode with `MK_DEV_PREFER=backend`. | ||||
| 
 | ||||
| ``` | ||||
| MK_DEV_PREFER=backend pnpm dev | ||||
| ``` | ||||
| 
 | ||||
| - This mode is closer to the production environment than the default mode. | ||||
| - Vite runs behind the backend (the backend will proxy Vite at /vite). | ||||
| - You can see Misskey by accessing `http://localhost:3000` (Replace `3000` with the port configured with `port` in .config/default.yml). | ||||
| - To change the port of Vite, specify with `VITE_PORT` environment variable. | ||||
| - HMR may not work in some environments such as Windows. | ||||
| 
 | ||||
| ### Dev Container | ||||
| Instead of running `pnpm` locally, you can use Dev Container to set up your development environment. | ||||
| To use Dev Container, open the project directory on VSCode with Dev Containers installed.   | ||||
|  |  | |||
|  | @ -162,12 +162,12 @@ describe('After user signed in', () => { | |||
| 
 | ||||
|   it('successfully loads', () => { | ||||
| 		// 表示に時間がかかるのでデフォルト秒数だとタイムアウトする
 | ||||
| 		cy.get('[data-cy-user-setup-continue]', { timeout: 12000 }).should('be.visible'); | ||||
| 		cy.get('[data-cy-user-setup-continue]', { timeout: 30000 }).should('be.visible'); | ||||
|   }); | ||||
| 
 | ||||
| 	it('account setup wizard', () => { | ||||
| 		// 表示に時間がかかるのでデフォルト秒数だとタイムアウトする
 | ||||
| 		cy.get('[data-cy-user-setup-continue]', { timeout: 12000 }).click(); | ||||
| 		cy.get('[data-cy-user-setup-continue]', { timeout: 30000 }).click(); | ||||
| 
 | ||||
| 		cy.get('[data-cy-user-setup-user-name] input').type('ありす'); | ||||
| 		cy.get('[data-cy-user-setup-user-description] textarea').type('ほげ'); | ||||
|  | @ -205,7 +205,7 @@ describe('After user setup', () => { | |||
| 
 | ||||
| 		// アカウント初期設定ウィザード
 | ||||
| 		// 表示に時間がかかるのでデフォルト秒数だとタイムアウトする
 | ||||
| 		cy.get('[data-cy-user-setup] [data-cy-modal-window-close]', { timeout: 12000 }).click(); | ||||
| 		cy.get('[data-cy-user-setup] [data-cy-modal-window-close]', { timeout: 30000 }).click(); | ||||
| 		cy.get('[data-cy-modal-dialog-ok]').click(); | ||||
| 	}); | ||||
| 
 | ||||
|  |  | |||
|  | @ -14,7 +14,7 @@ describe('Router transition', () => { | |||
| 
 | ||||
| 			// アカウント初期設定ウィザード
 | ||||
| 			// 表示に時間がかかるのでデフォルト秒数だとタイムアウトする
 | ||||
| 			cy.get('[data-cy-user-setup] [data-cy-modal-window-close]', { timeout: 12000 }).click(); | ||||
| 			cy.get('[data-cy-user-setup] [data-cy-modal-window-close]', { timeout: 30000 }).click(); | ||||
| 			cy.wait(500); | ||||
| 			cy.get('[data-cy-modal-dialog-ok]').click(); | ||||
| 		}); | ||||
|  |  | |||
|  | @ -14,9 +14,9 @@ export type FanoutTimelineName = | |||
| 	| `homeTimeline:${string}` | ||||
| 	| `homeTimelineWithFiles:${string}` // only notes with files are included
 | ||||
| 	// local timeline
 | ||||
| 	| 'localTimeline' // replies are not included
 | ||||
| 	| 'localTimelineWithFiles' // only non-reply notes with files are included
 | ||||
| 	| 'localTimelineWithReplies' // only replies are included
 | ||||
| 	| `localTimeline` // replies are not included
 | ||||
| 	| `localTimelineWithFiles` // only non-reply notes with files are included
 | ||||
| 	| `localTimelineWithReplies` // only replies are included
 | ||||
| 	| `localTimelineWithReplyTo:${string}` // Only replies to specific local user are included. Parameter is reply user id.
 | ||||
| 
 | ||||
| 	// antenna
 | ||||
|  |  | |||
|  | @ -263,10 +263,8 @@ export class NoteCreateService implements OnApplicationShutdown { | |||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		if (!user.host) { | ||||
| 			if (this.utilityService.isKeyWordIncluded(data.cw ?? data.text ?? '', meta.prohibitedWords)) { | ||||
| 				throw new NoteCreateService.ContainsProhibitedWordsError(); | ||||
| 			} | ||||
| 		if (this.utilityService.isKeyWordIncluded(data.cw ?? data.text ?? '', meta.prohibitedWords)) { | ||||
| 			throw new NoteCreateService.ContainsProhibitedWordsError(); | ||||
| 		} | ||||
| 
 | ||||
| 		const inSilencedInstance = this.utilityService.isSilencedHost(meta.silencedHosts, user.host); | ||||
|  |  | |||
|  | @ -12,11 +12,11 @@ import type { Config } from '@/config.js'; | |||
| import { DI } from '@/di-symbols.js'; | ||||
| import { bindThis } from '@/decorators.js'; | ||||
| import type { Antenna } from '@/server/api/endpoints/i/import-antennas.js'; | ||||
| import { ApRequestCreator } from '@/core/activitypub/ApRequestService.js'; | ||||
| import type { DbQueue, DeliverQueue, EndedPollNotificationQueue, InboxQueue, ObjectStorageQueue, RelationshipQueue, SystemQueue, WebhookDeliverQueue } from './QueueModule.js'; | ||||
| import type { DbJobData, DeliverJobData, RelationshipJobData, ThinUser } from '../queue/types.js'; | ||||
| import type httpSignature from '@peertube/http-signature'; | ||||
| import type * as Bull from 'bullmq'; | ||||
| import { ApRequestCreator } from '@/core/activitypub/ApRequestService.js'; | ||||
| 
 | ||||
| @Injectable() | ||||
| export class QueueService { | ||||
|  |  | |||
|  | @ -31,7 +31,7 @@ export class ApMfmService { | |||
| 
 | ||||
| 		const parsed = mfm.parse(srcMfm); | ||||
| 
 | ||||
| 		if (!apAppend && parsed.every(n => ['text', 'unicodeEmoji', 'emojiCode', 'mention', 'hashtag', 'url'].includes(n.type))) { | ||||
| 		if (!apAppend && parsed?.every(n => ['text', 'unicodeEmoji', 'emojiCode', 'mention', 'hashtag', 'url'].includes(n.type))) { | ||||
| 			noMisskeyContent = true; | ||||
| 		} | ||||
| 
 | ||||
|  |  | |||
|  | @ -0,0 +1,9 @@ | |||
| import type { onRequestHookHandler } from 'fastify'; | ||||
| 
 | ||||
| export const handleRequestRedirectToOmitSearch: onRequestHookHandler = (request, reply, done) => { | ||||
| 	const index = request.url.indexOf('?'); | ||||
| 	if (~index) { | ||||
| 		reply.redirect(301, request.url.slice(0, index)); | ||||
| 	} | ||||
| 	done(); | ||||
| }; | ||||
|  | @ -34,7 +34,7 @@ export class RelationshipProcessorService { | |||
| 
 | ||||
| 	@bindThis | ||||
| 	public async processFollow(job: Bull.Job<RelationshipJobData>): Promise<string> { | ||||
| 		this.logger.info(`${job.data.from.id} is trying to follow ${job.data.to.id} ${job.data.withReplies ? 'with replies' : 'without replies'}`); | ||||
| 		this.logger.info(`${job.data.from.id} is trying to follow ${job.data.to.id} ${job.data.withReplies ? "with replies" : "without replies"}`); | ||||
| 		await this.userFollowingService.follow(job.data.from, job.data.to, { | ||||
| 			requestId: job.data.requestId, | ||||
| 			silent: job.data.silent, | ||||
|  |  | |||
|  | @ -27,6 +27,7 @@ import { LoggerService } from '@/core/LoggerService.js'; | |||
| import { bindThis } from '@/decorators.js'; | ||||
| import { isMimeImage } from '@/misc/is-mime-image.js'; | ||||
| import { correctFilename } from '@/misc/correct-filename.js'; | ||||
| import { handleRequestRedirectToOmitSearch } from '@/misc/fastify-hook-handlers.js'; | ||||
| import type { FastifyInstance, FastifyRequest, FastifyReply, FastifyPluginOptions } from 'fastify'; | ||||
| 
 | ||||
| const _filename = fileURLToPath(import.meta.url); | ||||
|  | @ -67,20 +68,23 @@ export class FileServerService { | |||
| 			done(); | ||||
| 		}); | ||||
| 
 | ||||
| 		fastify.get('/files/app-default.jpg', (request, reply) => { | ||||
| 			const file = fs.createReadStream(`${_dirname}/assets/dummy.png`); | ||||
| 			reply.header('Content-Type', 'image/jpeg'); | ||||
| 			reply.header('Cache-Control', 'max-age=31536000, immutable'); | ||||
| 			return reply.send(file); | ||||
| 		}); | ||||
| 		fastify.register((fastify, options, done) => { | ||||
| 			fastify.addHook('onRequest', handleRequestRedirectToOmitSearch); | ||||
| 			fastify.get('/files/app-default.jpg', (request, reply) => { | ||||
| 				const file = fs.createReadStream(`${_dirname}/assets/dummy.png`); | ||||
| 				reply.header('Content-Type', 'image/jpeg'); | ||||
| 				reply.header('Cache-Control', 'max-age=31536000, immutable'); | ||||
| 				return reply.send(file); | ||||
| 			}); | ||||
| 
 | ||||
| 		fastify.get<{ Params: { key: string; } }>('/files/:key', async (request, reply) => { | ||||
| 			return await this.sendDriveFile(request, reply) | ||||
| 				.catch(err => this.errorHandler(request, reply, err)); | ||||
| 		}); | ||||
| 		fastify.get<{ Params: { key: string; } }>('/files/:key/*', async (request, reply) => { | ||||
| 			return await this.sendDriveFile(request, reply) | ||||
| 				.catch(err => this.errorHandler(request, reply, err)); | ||||
| 			fastify.get<{ Params: { key: string; } }>('/files/:key', async (request, reply) => { | ||||
| 				return await this.sendDriveFile(request, reply) | ||||
| 					.catch(err => this.errorHandler(request, reply, err)); | ||||
| 			}); | ||||
| 			fastify.get<{ Params: { key: string; } }>('/files/:key/*', async (request, reply) => { | ||||
| 				return await reply.redirect(301, `${this.config.url}/files/${request.params.key}`); | ||||
| 			}); | ||||
| 			done(); | ||||
| 		}); | ||||
| 
 | ||||
| 		fastify.get<{ | ||||
|  |  | |||
|  | @ -37,12 +37,12 @@ export class NodeinfoServerService { | |||
| 	@bindThis | ||||
| 	public getLinks() { | ||||
| 		return [{ | ||||
| 			rel: 'http://nodeinfo.diaspora.software/ns/schema/2.1', | ||||
| 			href: this.config.url + nodeinfo2_1path, | ||||
| 		}, { | ||||
| 			rel: 'http://nodeinfo.diaspora.software/ns/schema/2.0', | ||||
| 			href: this.config.url + nodeinfo2_0path, | ||||
| 		}]; | ||||
| 				rel: 'http://nodeinfo.diaspora.software/ns/schema/2.1', | ||||
| 				href: this.config.url + nodeinfo2_1path | ||||
| 			}, { | ||||
| 				rel: 'http://nodeinfo.diaspora.software/ns/schema/2.0', | ||||
| 				href: this.config.url + nodeinfo2_0path, | ||||
| 			}]; | ||||
| 	} | ||||
| 
 | ||||
| 	@bindThis | ||||
|  |  | |||
|  | @ -25,8 +25,8 @@ export const meta = { | |||
| 				items: { | ||||
| 					type: 'object', | ||||
| 				}, | ||||
| 			}, | ||||
| 		}, | ||||
| 			} | ||||
| 		} | ||||
| 	}, | ||||
| } as const; | ||||
| 
 | ||||
|  |  | |||
|  | @ -71,7 +71,7 @@ export const paramDef = { | |||
| 	type: 'object', | ||||
| 	properties: { | ||||
| 		userId: { type: 'string', format: 'misskey:id' }, | ||||
| 		withReplies: { type: 'boolean' }, | ||||
| 		withReplies: { type: 'boolean' } | ||||
| 	}, | ||||
| 	required: ['userId'], | ||||
| } as const; | ||||
|  |  | |||
|  | @ -6,7 +6,7 @@ | |||
| import { Inject, Injectable } from '@nestjs/common'; | ||||
| import { Endpoint } from '@/server/api/endpoint-base.js'; | ||||
| import type { UsersRepository } from '@/models/_.js'; | ||||
| import { safeForSql } from '@/misc/safe-for-sql.js'; | ||||
| import { safeForSql } from "@/misc/safe-for-sql.js"; | ||||
| import { normalizeForSearch } from '@/misc/normalize-for-search.js'; | ||||
| import { UserEntityService } from '@/core/entities/UserEntityService.js'; | ||||
| import { DI } from '@/di-symbols.js'; | ||||
|  |  | |||
|  | @ -14,7 +14,7 @@ export const meta = { | |||
| 	tags: ['account'], | ||||
| 
 | ||||
| 	requireCredential: true, | ||||
| 	kind: 'read:account', | ||||
| 	kind: "read:account", | ||||
| 
 | ||||
| 	res: { | ||||
| 		type: 'object', | ||||
|  |  | |||
|  | @ -38,9 +38,9 @@ export const meta = { | |||
| 					type: 'array', | ||||
| 					uniqueItems: true, | ||||
| 					items: { | ||||
| 						type: 'string', | ||||
| 						type: 'string' | ||||
| 					}, | ||||
| 				}, | ||||
| 				} | ||||
| 			}, | ||||
| 		}, | ||||
| 	}, | ||||
|  |  | |||
|  | @ -35,7 +35,7 @@ export const meta = { | |||
| 					type: 'array', | ||||
| 					uniqueItems: true, | ||||
| 					items: { | ||||
| 						type: 'string', | ||||
| 						type: 'string' | ||||
| 					}, | ||||
| 				}, | ||||
| 				isAuthorized: { | ||||
|  |  | |||
|  | @ -22,7 +22,7 @@ export const meta = { | |||
| 
 | ||||
| 	res: { | ||||
| 		type: 'object', | ||||
| 	}, | ||||
| 	} | ||||
| } as const; | ||||
| 
 | ||||
| export const paramDef = { | ||||
|  |  | |||
|  | @ -22,7 +22,7 @@ export const meta = { | |||
| 
 | ||||
| 	res: { | ||||
| 		type: 'object', | ||||
| 	}, | ||||
| 	} | ||||
| } as const; | ||||
| 
 | ||||
| export const paramDef = { | ||||
|  |  | |||
|  | @ -22,8 +22,8 @@ export const meta = { | |||
| 						type: 'array', | ||||
| 						items: { | ||||
| 							type: 'string', | ||||
| 						}, | ||||
| 					}, | ||||
| 						} | ||||
| 					} | ||||
| 				}, | ||||
| 				domain: { | ||||
| 					type: 'string', | ||||
|  | @ -31,7 +31,7 @@ export const meta = { | |||
| 				}, | ||||
| 			}, | ||||
| 		}, | ||||
| 	}, | ||||
| 	} | ||||
| } as const; | ||||
| 
 | ||||
| export const paramDef = { | ||||
|  |  | |||
|  | @ -33,7 +33,7 @@ export const meta = { | |||
| 		properties: { | ||||
| 			id: { | ||||
| 				type: 'string', | ||||
| 				format: 'misskey:id', | ||||
| 				format: 'misskey:id' | ||||
| 			}, | ||||
| 			userId: { | ||||
| 				type: 'string', | ||||
|  | @ -45,7 +45,7 @@ export const meta = { | |||
| 				items: { | ||||
| 					type: 'string', | ||||
| 					enum: webhookEventTypes, | ||||
| 				}, | ||||
| 				} | ||||
| 			}, | ||||
| 			url: { type: 'string' }, | ||||
| 			secret: { type: 'string' }, | ||||
|  |  | |||
|  | @ -23,7 +23,7 @@ export const meta = { | |||
| 			properties: { | ||||
| 				id: { | ||||
| 					type: 'string', | ||||
| 					format: 'misskey:id', | ||||
| 					format: 'misskey:id' | ||||
| 				}, | ||||
| 				userId: { | ||||
| 					type: 'string', | ||||
|  | @ -35,7 +35,7 @@ export const meta = { | |||
| 					items: { | ||||
| 						type: 'string', | ||||
| 						enum: webhookEventTypes, | ||||
| 					}, | ||||
| 					} | ||||
| 				}, | ||||
| 				url: { type: 'string' }, | ||||
| 				secret: { type: 'string' }, | ||||
|  | @ -43,8 +43,8 @@ export const meta = { | |||
| 				latestSentAt: { type: 'string', format: 'date-time', nullable: true }, | ||||
| 				latestStatus: { type: 'integer', nullable: true }, | ||||
| 			}, | ||||
| 		}, | ||||
| 	}, | ||||
| 		} | ||||
| 	} | ||||
| } as const; | ||||
| 
 | ||||
| export const paramDef = { | ||||
|  |  | |||
|  | @ -30,7 +30,7 @@ export const meta = { | |||
| 		properties: { | ||||
| 			id: { | ||||
| 				type: 'string', | ||||
| 				format: 'misskey:id', | ||||
| 				format: 'misskey:id' | ||||
| 			}, | ||||
| 			userId: { | ||||
| 				type: 'string', | ||||
|  | @ -42,7 +42,7 @@ export const meta = { | |||
| 				items: { | ||||
| 					type: 'string', | ||||
| 					enum: webhookEventTypes, | ||||
| 				}, | ||||
| 				} | ||||
| 			}, | ||||
| 			url: { type: 'string' }, | ||||
| 			secret: { type: 'string' }, | ||||
|  |  | |||
|  | @ -47,7 +47,7 @@ export const meta = { | |||
| 		bothWithRepliesAndWithFiles: { | ||||
| 			message: 'Specifying both withReplies and withFiles is not supported', | ||||
| 			code: 'BOTH_WITH_REPLIES_AND_WITH_FILES', | ||||
| 			id: 'dfaa3eb7-8002-4cb7-bcc4-1095df46656f', | ||||
| 			id: 'dfaa3eb7-8002-4cb7-bcc4-1095df46656f' | ||||
| 		}, | ||||
| 	}, | ||||
| } as const; | ||||
|  |  | |||
|  | @ -18,7 +18,7 @@ export const meta = { | |||
| 		properties: { | ||||
| 			id: { | ||||
| 				type: 'string', | ||||
| 				format: 'misskey:id', | ||||
| 				format: 'misskey:id' | ||||
| 			}, | ||||
| 			required: { | ||||
| 				type: 'boolean', | ||||
|  | @ -34,8 +34,8 @@ export const meta = { | |||
| 				default: 'hello', | ||||
| 				nullable: true, | ||||
| 			}, | ||||
| 		}, | ||||
| 	}, | ||||
| 		} | ||||
| 	} | ||||
| } as const; | ||||
| 
 | ||||
| export const paramDef = { | ||||
|  |  | |||
|  | @ -34,6 +34,7 @@ import { ChannelEntityService } from '@/core/entities/ChannelEntityService.js'; | |||
| import type { ChannelsRepository, ClipsRepository, FlashsRepository, GalleryPostsRepository, MiMeta, NotesRepository, PagesRepository, ReversiGamesRepository, UserProfilesRepository, UsersRepository } from '@/models/_.js'; | ||||
| import type Logger from '@/logger.js'; | ||||
| import { deepClone } from '@/misc/clone.js'; | ||||
| import { handleRequestRedirectToOmitSearch } from '@/misc/fastify-hook-handlers.js'; | ||||
| import { bindThis } from '@/decorators.js'; | ||||
| import { FlashEntityService } from '@/core/entities/FlashEntityService.js'; | ||||
| import { RoleService } from '@/core/RoleService.js'; | ||||
|  | @ -253,11 +254,16 @@ export class ClientServerService { | |||
| 
 | ||||
| 		//#region vite assets
 | ||||
| 		if (this.config.clientManifestExists) { | ||||
| 			fastify.register(fastifyStatic, { | ||||
| 				root: viteOut, | ||||
| 				prefix: '/vite/', | ||||
| 				maxAge: ms('30 days'), | ||||
| 				decorateReply: false, | ||||
| 			fastify.register((fastify, options, done) => { | ||||
| 				fastify.register(fastifyStatic, { | ||||
| 					root: viteOut, | ||||
| 					prefix: '/vite/', | ||||
| 					maxAge: ms('30 days'), | ||||
| 					immutable: true, | ||||
| 					decorateReply: false, | ||||
| 				}); | ||||
| 				fastify.addHook('onRequest', handleRequestRedirectToOmitSearch); | ||||
| 				done(); | ||||
| 			}); | ||||
| 		} else { | ||||
| 			const port = (process.env.VITE_PORT ?? '5173'); | ||||
|  | @ -292,11 +298,16 @@ export class ClientServerService { | |||
| 			decorateReply: false, | ||||
| 		}); | ||||
| 
 | ||||
| 		fastify.register(fastifyStatic, { | ||||
| 			root: tarball, | ||||
| 			prefix: '/tarball/', | ||||
| 			immutable: true, | ||||
| 			decorateReply: false, | ||||
| 		fastify.register((fastify, options, done) => { | ||||
| 			fastify.register(fastifyStatic, { | ||||
| 				root: tarball, | ||||
| 				prefix: '/tarball/', | ||||
| 				maxAge: ms('30 days'), | ||||
| 				immutable: true, | ||||
| 				decorateReply: false, | ||||
| 			}); | ||||
| 			fastify.addHook('onRequest', handleRequestRedirectToOmitSearch); | ||||
| 			done(); | ||||
| 		}); | ||||
| 
 | ||||
| 		fastify.get('/favicon.ico', async (request, reply) => { | ||||
|  |  | |||
|  | @ -7,10 +7,11 @@ process.env.NODE_ENV = 'test'; | |||
| 
 | ||||
| import * as assert from 'assert'; | ||||
| import { MiNote } from '@/models/Note.js'; | ||||
| import type { Packed } from '@/misc/json-schema.js'; | ||||
| import { api, initTestDb, makeStreamCatcher, post, signup, uploadFile } from '../utils.js'; | ||||
| import type * as misskey from 'misskey-js'; | ||||
| import type{ Repository } from 'typeorm'; | ||||
| import type{ Repository } from 'typeorm' | ||||
| import type { Packed } from '@/misc/json-schema.js'; | ||||
| 
 | ||||
| 
 | ||||
| describe('Drive', () => { | ||||
| 	let Notes: Repository<MiNote>; | ||||
|  | @ -30,7 +31,7 @@ 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, | ||||
|  | @ -50,7 +51,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 が通過することで成功とする
 | ||||
|  | @ -58,27 +59,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); | ||||
|  | @ -88,6 +89,7 @@ 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); | ||||
| 	}); | ||||
| 
 | ||||
| 	}) | ||||
| }); | ||||
| 
 | ||||
|  |  | |||
|  | @ -663,7 +663,7 @@ describe('Note', () => { | |||
| 			assert.strictEqual(note2.body.error.code, 'CONTAINS_PROHIBITED_WORDS'); | ||||
| 		}); | ||||
| 
 | ||||
| 		test('禁止ワードを含んでいてもリモートノートはエラーにならない', async () => { | ||||
| 		test('禁止ワードを含んでるリモートノートもエラーになる', async () => { | ||||
| 			const prohibited = await api('admin/update-meta', { | ||||
| 				prohibitedWords: [ | ||||
| 					'test', | ||||
|  | @ -678,7 +678,7 @@ describe('Note', () => { | |||
| 				text: 'hogetesthuge', | ||||
| 			}, tom); | ||||
| 
 | ||||
| 			assert.strictEqual(note1.status, 200); | ||||
| 			assert.strictEqual(note1.status, 400); | ||||
| 		}); | ||||
| 	}); | ||||
| 
 | ||||
|  |  | |||
|  | @ -100,6 +100,7 @@ describe('ActivityPub', () => { | |||
| 		perRemoteUserUserTimelineCacheMax: 100, | ||||
| 		blockedHosts: [] as string[], | ||||
| 		sensitiveWords: [] as string[], | ||||
| 		prohibitedWords: [] as string[], | ||||
| 	} as MiMeta; | ||||
| 	let meta = metaInitial; | ||||
| 
 | ||||
|  |  | |||
|  | @ -13,10 +13,10 @@ import fetch, { File, RequestInit } 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 { entities } from '../src/postgres.js'; | ||||
| import { loadConfig } from '../src/config.js'; | ||||
| import type * as misskey from 'misskey-js'; | ||||
| import { Packed } from '@/misc/json-schema.js'; | ||||
| 
 | ||||
| export { server as startServer, jobQueue as startJobQueue } from '@/boot/common.js'; | ||||
| 
 | ||||
|  | @ -123,9 +123,9 @@ export function randomString(chars = 'abcdefghijklmnopqrstuvwxyz0123456789', len | |||
| function timeoutPromise<T>(p: Promise<T>, timeout: number): Promise<T> { | ||||
| 	return Promise.race([ | ||||
| 		p, | ||||
| 		new Promise((reject) => { | ||||
| 			setTimeout(() => { reject(new Error('timed out')); }, timeout); | ||||
| 		}) as never, | ||||
| 		new Promise((reject) =>{ | ||||
| 			setTimeout(() => { reject(new Error('timed out')); }, timeout) | ||||
| 		}) as never | ||||
| 	]); | ||||
| } | ||||
| 
 | ||||
|  | @ -343,7 +343,7 @@ export const uploadUrl = async (user: UserToken, url: string): Promise<Packed<'D | |||
| 		'main', | ||||
| 		(msg) => msg.type === 'urlUploadFinished' && msg.body.marker === marker, | ||||
| 		(msg) => msg.body.file as Packed<'DriveFile'>, | ||||
| 		60 * 1000, | ||||
| 		60 * 1000 | ||||
| 	); | ||||
| 
 | ||||
| 	await api('drive/files/upload-from-url', { | ||||
|  | @ -434,20 +434,20 @@ export const waitFire = async (user: UserToken, channel: string, trgr: () => any | |||
|  * @returns 時間内に正常に処理できた場合に通知からextractorを通した値を得る | ||||
|  */ | ||||
| export function makeStreamCatcher<T>( | ||||
| 	user: UserToken, | ||||
| 	channel: string, | ||||
| 	cond: (message: Record<string, any>) => boolean, | ||||
| 	extractor: (message: Record<string, any>) => T, | ||||
| 	timeout = 60 * 1000): Promise<T> { | ||||
| 	let ws: WebSocket; | ||||
| 		user: UserToken, | ||||
| 		channel: string, | ||||
| 		cond: (message: Record<string, any>) => boolean, | ||||
| 		extractor: (message: Record<string, any>) => T, | ||||
| 		timeout = 60 * 1000): Promise<T> { | ||||
| 	let ws: WebSocket | ||||
| 	const p = new Promise<T>(async (resolve) => { | ||||
| 		ws = await connectStream(user, channel, (msg) => { | ||||
| 			if (cond(msg)) { | ||||
| 				resolve(extractor(msg)); | ||||
| 				resolve(extractor(msg)) | ||||
| 			} | ||||
| 		}); | ||||
| 	}).finally(() => { | ||||
| 		ws.close(); | ||||
| 		ws?.close(); | ||||
| 	}); | ||||
| 
 | ||||
| 	return timeoutPromise(p, timeout); | ||||
|  |  | |||
|  | @ -3,25 +3,28 @@ | |||
|  * SPDX-License-Identifier: AGPL-3.0-only | ||||
|  */ | ||||
| 
 | ||||
| import { resolve } from 'node:path'; | ||||
| import { createRequire } from 'node:module'; | ||||
| import { dirname, join, resolve } from 'node:path'; | ||||
| import { fileURLToPath } from 'node:url'; | ||||
| import type { StorybookConfig } from '@storybook/vue3-vite'; | ||||
| import { type Plugin, mergeConfig } from 'vite'; | ||||
| import turbosnap from 'vite-plugin-turbosnap'; | ||||
| 
 | ||||
| const dirname = fileURLToPath(new URL('.', import.meta.url)); | ||||
| const require = createRequire(import.meta.url); | ||||
| const _dirname = fileURLToPath(new URL('.', import.meta.url)); | ||||
| 
 | ||||
| const config = { | ||||
| 	stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'], | ||||
| 	addons: [ | ||||
| 		'@storybook/addon-essentials', | ||||
| 		'@storybook/addon-interactions', | ||||
| 		'@storybook/addon-links', | ||||
| 		'@storybook/addon-storysource', | ||||
| 		resolve(dirname, '../node_modules/storybook-addon-misskey-theme'), | ||||
| 		getAbsolutePath('@storybook/addon-essentials'), | ||||
| 		getAbsolutePath('@storybook/addon-interactions'), | ||||
| 		getAbsolutePath('@storybook/addon-links'), | ||||
| 		getAbsolutePath('@storybook/addon-storysource'), | ||||
| 		getAbsolutePath('@storybook/addon-mdx-gfm'), | ||||
| 		resolve(_dirname, '../node_modules/storybook-addon-misskey-theme'), | ||||
| 	], | ||||
| 	framework: { | ||||
| 		name: '@storybook/vue3-vite', | ||||
| 		name: getAbsolutePath('@storybook/vue3-vite') as '@storybook/vue3-vite', | ||||
| 		options: {}, | ||||
| 	}, | ||||
| 	docs: { | ||||
|  | @ -37,10 +40,13 @@ const config = { | |||
| 		} | ||||
| 		return mergeConfig(config, { | ||||
| 			plugins: [ | ||||
| 				// XXX: https://github.com/IanVS/vite-plugin-turbosnap/issues/8
 | ||||
| 				(turbosnap as any as typeof turbosnap['default'])({ | ||||
| 					rootDir: config.root ?? process.cwd(), | ||||
| 				}), | ||||
| 				{ | ||||
| 					// XXX: https://github.com/IanVS/vite-plugin-turbosnap/issues/8
 | ||||
| 					...(turbosnap as any as typeof turbosnap['default'])({ | ||||
| 						rootDir: config.root ?? process.cwd(), | ||||
| 					}), | ||||
| 					name: 'fake-turbosnap', | ||||
| 				}, | ||||
| 			], | ||||
| 			build: { | ||||
| 				target: [ | ||||
|  | @ -53,3 +59,7 @@ const config = { | |||
| 	}, | ||||
| } satisfies StorybookConfig; | ||||
| export default config; | ||||
| 
 | ||||
| function getAbsolutePath(value: string): string { | ||||
| 	return dirname(require.resolve(join(value, 'package.json'))); | ||||
| } | ||||
|  |  | |||
|  | @ -3,8 +3,8 @@ | |||
|  * SPDX-License-Identifier: AGPL-3.0-only | ||||
|  */ | ||||
| 
 | ||||
| import { addons } from '@storybook/addons'; | ||||
| import { FORCE_REMOUNT } from '@storybook/core-events'; | ||||
| import { addons } from '@storybook/preview-api'; | ||||
| import { type Preview, setup } from '@storybook/vue3'; | ||||
| import isChromatic from 'chromatic/isChromatic'; | ||||
| import { initialize, mswDecorator } from 'msw-storybook-addon'; | ||||
|  |  | |||
|  | @ -8,7 +8,7 @@ | |||
| 		"build": "vite build", | ||||
| 		"storybook-dev": "nodemon --verbose --watch src --ext \"mdx,ts,vue\" --ignore \"*.stories.ts\" --exec \"pnpm build-storybook-pre && pnpm exec storybook dev -p 6006 --ci\"", | ||||
| 		"build-storybook-pre": "(tsc -p .storybook || echo done.) && node .storybook/generate.js && node .storybook/preload-locale.js && node .storybook/preload-theme.js", | ||||
| 		"build-storybook": "pnpm build-storybook-pre && storybook build", | ||||
| 		"build-storybook": "pnpm build-storybook-pre && storybook build --webpack-stats-json storybook-static", | ||||
| 		"chromatic": "chromatic", | ||||
| 		"test": "vitest --run --globals", | ||||
| 		"test-and-coverage": "vitest --run --coverage --globals", | ||||
|  | @ -28,7 +28,7 @@ | |||
| 		"@tabler/icons-webfont": "2.44.0", | ||||
| 		"@twemoji/parser": "15.0.0", | ||||
| 		"@vitejs/plugin-vue": "5.0.3", | ||||
| 		"@vue/compiler-sfc": "3.4.18", | ||||
| 		"@vue/compiler-sfc": "3.4.15", | ||||
| 		"aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.1.2", | ||||
| 		"astring": "1.8.6", | ||||
| 		"broadcast-channel": "7.0.0", | ||||
|  | @ -73,30 +73,30 @@ | |||
| 		"uuid": "9.0.1", | ||||
| 		"v-code-diff": "1.7.2", | ||||
| 		"vite": "5.1.0", | ||||
| 		"vue": "3.4.18", | ||||
| 		"vue": "3.4.15", | ||||
| 		"vuedraggable": "next" | ||||
| 	}, | ||||
| 	"devDependencies": { | ||||
| 		"@misskey-dev/eslint-plugin": "1.0.0", | ||||
| 		"@misskey-dev/summaly": "5.0.3", | ||||
| 		"@storybook/addon-actions": "7.6.10", | ||||
| 		"@storybook/addon-essentials": "7.6.10", | ||||
| 		"@storybook/addon-interactions": "7.6.10", | ||||
| 		"@storybook/addon-links": "7.6.10", | ||||
| 		"@storybook/addon-storysource": "7.6.10", | ||||
| 		"@storybook/addons": "7.6.10", | ||||
| 		"@storybook/blocks": "7.6.10", | ||||
| 		"@storybook/core-events": "7.6.10", | ||||
| 		"@storybook/jest": "0.2.3", | ||||
| 		"@storybook/manager-api": "7.6.10", | ||||
| 		"@storybook/preview-api": "7.6.10", | ||||
| 		"@storybook/react": "7.6.10", | ||||
| 		"@storybook/react-vite": "7.6.10", | ||||
| 		"@storybook/testing-library": "0.2.2", | ||||
| 		"@storybook/theming": "7.6.10", | ||||
| 		"@storybook/types": "7.6.10", | ||||
| 		"@storybook/vue3": "7.6.10", | ||||
| 		"@storybook/vue3-vite": "7.6.10", | ||||
| 		"@storybook/addon-actions": "8.0.0-beta.2", | ||||
| 		"@storybook/addon-essentials": "8.0.0-beta.2", | ||||
| 		"@storybook/addon-interactions": "8.0.0-beta.2", | ||||
| 		"@storybook/addon-links": "8.0.0-beta.2", | ||||
| 		"@storybook/addon-mdx-gfm": "8.0.0-beta.2", | ||||
| 		"@storybook/addon-storysource": "8.0.0-beta.2", | ||||
| 		"@storybook/blocks": "8.0.0-beta.2", | ||||
| 		"@storybook/components": "8.0.0-beta.2", | ||||
| 		"@storybook/core-events": "8.0.0-beta.2", | ||||
| 		"@storybook/manager-api": "8.0.0-beta.2", | ||||
| 		"@storybook/preview-api": "8.0.0-beta.2", | ||||
| 		"@storybook/react": "8.0.0-beta.2", | ||||
| 		"@storybook/react-vite": "8.0.0-beta.2", | ||||
| 		"@storybook/test": "8.0.0-beta.2", | ||||
| 		"@storybook/theming": "8.0.0-beta.2", | ||||
| 		"@storybook/types": "8.0.0-beta.2", | ||||
| 		"@storybook/vue3": "8.0.0-beta.2", | ||||
| 		"@storybook/vue3-vite": "8.0.0-beta.2", | ||||
| 		"@testing-library/vue": "8.0.2", | ||||
| 		"@types/escape-regexp": "0.0.3", | ||||
| 		"@types/estree": "1.0.5", | ||||
|  | @ -130,12 +130,12 @@ | |||
| 		"react": "18.2.0", | ||||
| 		"react-dom": "18.2.0", | ||||
| 		"start-server-and-test": "2.0.3", | ||||
| 		"storybook": "7.6.10", | ||||
| 		"storybook": "8.0.0-beta.2", | ||||
| 		"storybook-addon-misskey-theme": "github:misskey-dev/storybook-addon-misskey-theme", | ||||
| 		"vite-plugin-turbosnap": "1.0.3", | ||||
| 		"vitest": "0.34.6", | ||||
| 		"vitest-fetch-mock": "0.2.2", | ||||
| 		"vue-component-type-helpers": "^1.8.27", | ||||
| 		"vue-component-type-helpers": "1.8.27", | ||||
| 		"vue-eslint-parser": "9.4.2", | ||||
| 		"vue-tsc": "1.8.27" | ||||
| 	} | ||||
|  |  | |||
|  | @ -5,8 +5,7 @@ | |||
| 
 | ||||
| /* eslint-disable @typescript-eslint/explicit-function-return-type */ | ||||
| import { action } from '@storybook/addon-actions'; | ||||
| import { expect } from '@storybook/jest'; | ||||
| import { userEvent, waitFor, within } from '@storybook/testing-library'; | ||||
| import { expect, userEvent, waitFor, within } from '@storybook/test'; | ||||
| import { StoryObj } from '@storybook/vue3'; | ||||
| import { HttpResponse, http } from 'msw'; | ||||
| import { userDetailed } from '../../.storybook/fakes.js'; | ||||
|  |  | |||
|  | @ -123,7 +123,7 @@ function callback(response?: string) { | |||
| function onReceivedMessage(message: MessageEvent) { | ||||
| 	if (message.data.token) { | ||||
| 		if (props.instanceUrl && new URL(message.origin).host === new URL(props.instanceUrl).host) { | ||||
| 			callback(<string>message.data.token); | ||||
| 			callback(message.data.token); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -4,8 +4,7 @@ | |||
|  */ | ||||
| 
 | ||||
| /* eslint-disable @typescript-eslint/explicit-function-return-type */ | ||||
| import { expect } from '@storybook/jest'; | ||||
| import { userEvent, waitFor, within } from '@storybook/testing-library'; | ||||
| import { expect, userEvent, waitFor, within } from '@storybook/test'; | ||||
| import { StoryObj } from '@storybook/vue3'; | ||||
| import { galleryPost } from '../../.storybook/fakes.js'; | ||||
| import MkGalleryPostPreview from './MkGalleryPostPreview.vue'; | ||||
|  |  | |||
|  | @ -4,8 +4,7 @@ | |||
|  */ | ||||
| 
 | ||||
| /* eslint-disable @typescript-eslint/explicit-function-return-type */ | ||||
| import { expect } from '@storybook/jest'; | ||||
| import { userEvent, waitFor, within } from '@storybook/testing-library'; | ||||
| import { expect, userEvent, waitFor, within } from '@storybook/test'; | ||||
| import { StoryObj } from '@storybook/vue3'; | ||||
| import { onBeforeUnmount } from 'vue'; | ||||
| import MkSignupServerRules from './MkSignupDialog.rules.vue'; | ||||
|  |  | |||
|  | @ -4,8 +4,7 @@ | |||
|  */ | ||||
| 
 | ||||
| /* eslint-disable @typescript-eslint/explicit-function-return-type */ | ||||
| import { expect } from '@storybook/jest'; | ||||
| import { userEvent, within } from '@storybook/testing-library'; | ||||
| import { expect, userEvent, within } from '@storybook/test'; | ||||
| import { StoryObj } from '@storybook/vue3'; | ||||
| import MkA from './MkA.vue'; | ||||
| import { tick } from '@/scripts/test-utils.js'; | ||||
|  |  | |||
|  | @ -5,8 +5,7 @@ | |||
| 
 | ||||
| /* eslint-disable @typescript-eslint/explicit-function-return-type */ | ||||
| import { action } from '@storybook/addon-actions'; | ||||
| import { expect } from '@storybook/jest'; | ||||
| import { waitFor } from '@storybook/testing-library'; | ||||
| import { expect, waitFor } from '@storybook/test'; | ||||
| import { StoryObj } from '@storybook/vue3'; | ||||
| import MkError from './MkError.vue'; | ||||
| export const Default = { | ||||
|  |  | |||
|  | @ -5,8 +5,7 @@ | |||
| 
 | ||||
| /* eslint-disable @typescript-eslint/explicit-function-return-type */ | ||||
| import { StoryObj } from '@storybook/vue3'; | ||||
| import { within } from '@storybook/testing-library'; | ||||
| import { expect } from '@storybook/jest'; | ||||
| import { expect, within } from '@storybook/test'; | ||||
| import MkMisskeyFlavoredMarkdown from './MkMisskeyFlavoredMarkdown.js'; | ||||
| export const Default = { | ||||
| 	render(args) { | ||||
|  |  | |||
|  | @ -4,7 +4,7 @@ | |||
|  */ | ||||
| 
 | ||||
| /* eslint-disable @typescript-eslint/explicit-function-return-type */ | ||||
| import { waitFor } from '@storybook/testing-library'; | ||||
| import { waitFor } from '@storybook/test'; | ||||
| import { StoryObj } from '@storybook/vue3'; | ||||
| import MkPageHeader from './MkPageHeader.vue'; | ||||
| export const Empty = { | ||||
|  |  | |||
|  | @ -4,7 +4,7 @@ | |||
|  */ | ||||
| 
 | ||||
| /* eslint-disable @typescript-eslint/explicit-function-return-type */ | ||||
| import { expect } from '@storybook/jest'; | ||||
| import { expect } from '@storybook/test'; | ||||
| import { StoryObj } from '@storybook/vue3'; | ||||
| import MkTime from './MkTime.vue'; | ||||
| import { i18n } from '@/i18n.js'; | ||||
|  |  | |||
|  | @ -4,8 +4,7 @@ | |||
|  */ | ||||
| 
 | ||||
| /* eslint-disable @typescript-eslint/explicit-function-return-type */ | ||||
| import { expect } from '@storybook/jest'; | ||||
| import { userEvent, waitFor, within } from '@storybook/testing-library'; | ||||
| import { expect, userEvent, waitFor, within } from '@storybook/test'; | ||||
| import { StoryObj } from '@storybook/vue3'; | ||||
| import { HttpResponse, http } from 'msw'; | ||||
| import { commonHandlers } from '../../../.storybook/mocks.js'; | ||||
|  |  | |||
|  | @ -4,7 +4,7 @@ | |||
|  */ | ||||
| 
 | ||||
| /* eslint-disable @typescript-eslint/explicit-function-return-type */ | ||||
| import { expect } from '@storybook/jest'; | ||||
| import { expect } from '@storybook/test'; | ||||
| import { StoryObj } from '@storybook/vue3'; | ||||
| import { userDetailed } from '../../../.storybook/fakes.js'; | ||||
| import MkUserName from './MkUserName.vue'; | ||||
|  |  | |||
|  | @ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only | |||
| 				<div>{{ i18n.ts.youShouldUpgradeClient }}</div> | ||||
| 				<MkButton style="margin: 8px auto;" @click="reload">{{ i18n.ts.reload }}</MkButton> | ||||
| 			</template> | ||||
| 			<div><MkA to="/docs/general/troubleshooting" class="_link">{{ i18n.ts.troubleshooting }}</MkA></div> | ||||
| 			<div><MkLink url="https://misskey-hub.net/docs/for-users/resources/troubleshooting/" target="_blank">{{ i18n.ts.troubleshooting }}</MkLink></div> | ||||
| 			<div v-if="error" style="opacity: 0.7;">ERROR: {{ error }}</div> | ||||
| 		</div> | ||||
| 	</div> | ||||
|  | @ -28,6 +28,7 @@ SPDX-License-Identifier: AGPL-3.0-only | |||
| import { ref, computed } from 'vue'; | ||||
| import * as Misskey from 'misskey-js'; | ||||
| import MkButton from '@/components/MkButton.vue'; | ||||
| import MkLink from '@/components/MkLink.vue'; | ||||
| import { version } from '@/config.js'; | ||||
| import { misskeyApi } from '@/scripts/misskey-api.js'; | ||||
| import { unisonReload } from '@/scripts/unison-reload.js'; | ||||
|  |  | |||
|  | @ -101,7 +101,7 @@ const announcements = { | |||
| 	limit: 10, | ||||
| }; | ||||
| 
 | ||||
| const isTimelineAvailable = ref(instance.policies.ltlAvailable || instance.policies.gtlAvailable); | ||||
| const isTimelineAvailable = ref(instance.policies?.ltlAvailable || instance.policies?.gtlAvailable); | ||||
| 
 | ||||
| const showMenu = ref(false); | ||||
| const isDesktop = ref(window.innerWidth >= DESKTOP_THRESHOLD); | ||||
|  |  | |||
							
								
								
									
										3439
									
								
								pnpm-lock.yaml
								
								
								
								
							
							
						
						
									
										3439
									
								
								pnpm-lock.yaml
								
								
								
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							|  | @ -70,7 +70,7 @@ execa('pnpm', ['--filter', 'backend', 'dev'], { | |||
| 	stderr: process.stderr, | ||||
| }); | ||||
| 
 | ||||
| execa('pnpm', ['--filter', 'frontend', 'dev'], { | ||||
| execa('pnpm', ['--filter', 'frontend', process.env.MK_DEV_PREFER === 'backend' ? 'dev' : 'watch'], { | ||||
| 	cwd: _dirname + '/../', | ||||
| 	stdout: process.stdout, | ||||
| 	stderr: process.stderr, | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue