Merge branch 'develop' into renovate/npm-happy-dom-vulnerability

This commit is contained in:
syuilo 2025-10-19 13:14:05 +09:00 committed by GitHub
commit 8d96d98550
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 186 additions and 121 deletions

View File

@ -85,9 +85,6 @@
"pnpm": { "pnpm": {
"overrides": { "overrides": {
"@aiscript-dev/aiscript-languageserver": "-" "@aiscript-dev/aiscript-languageserver": "-"
},
"patchedDependencies": {
"typeorm": "patches/typeorm.patch"
} }
} }
} }

View File

@ -0,0 +1,26 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
export class UnnecessaryNullDefault1760790899857 {
name = 'UnnecessaryNullDefault1760790899857'
/**
* @param {QueryRunner} queryRunner
*/
async up(queryRunner) {
await queryRunner.query(`ALTER TABLE "abuse_report_notification_recipient" ALTER COLUMN "userId" DROP DEFAULT`);
await queryRunner.query(`ALTER TABLE "abuse_report_notification_recipient" ALTER COLUMN "systemWebhookId" DROP DEFAULT`);
await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "urlPreviewUserAgent" DROP DEFAULT`);
}
/**
* @param {QueryRunner} queryRunner
*/
async down(queryRunner) {
await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "urlPreviewUserAgent" SET DEFAULT NULL`);
await queryRunner.query(`ALTER TABLE "abuse_report_notification_recipient" ALTER COLUMN "systemWebhookId" SET DEFAULT NULL`);
await queryRunner.query(`ALTER TABLE "abuse_report_notification_recipient" ALTER COLUMN "userId" SET DEFAULT NULL`);
}
}

View File

@ -6,8 +6,8 @@
import type { Directive } from 'vue'; import type { Directive } from 'vue';
import { getBgColor } from '@/utility/get-bg-color.js'; import { getBgColor } from '@/utility/get-bg-color.js';
export default { export const adaptiveBgDirective = {
mounted(src, binding, vn) { mounted(src) {
const parentBg = getBgColor(src.parentElement) ?? 'transparent'; const parentBg = getBgColor(src.parentElement) ?? 'transparent';
const myBg = window.getComputedStyle(src).backgroundColor; const myBg = window.getComputedStyle(src).backgroundColor;
@ -18,4 +18,4 @@ export default {
src.style.backgroundColor = myBg; src.style.backgroundColor = myBg;
} }
}, },
} as Directive; } as Directive<HTMLElement>;

View File

@ -9,8 +9,8 @@ import { globalEvents } from '@/events.js';
const handlerMap = new WeakMap<any, any>(); const handlerMap = new WeakMap<any, any>();
export default { export const adaptiveBorderDirective = {
mounted(src, binding, vn) { mounted(src) {
function calc() { function calc() {
const parentBg = getBgColor(src.parentElement) ?? 'transparent'; const parentBg = getBgColor(src.parentElement) ?? 'transparent';
@ -30,7 +30,7 @@ export default {
globalEvents.on('themeChanged', calc); globalEvents.on('themeChanged', calc);
}, },
unmounted(src, binding, vn) { unmounted(src) {
globalEvents.off('themeChanged', handlerMap.get(src)); globalEvents.off('themeChanged', handlerMap.get(src));
}, },
} as Directive; } as Directive<HTMLElement>;

View File

@ -5,8 +5,8 @@
import type { Directive } from 'vue'; import type { Directive } from 'vue';
export default { export const animDirective = {
beforeMount(src, binding, vn) { beforeMount(src) {
src.style.opacity = '0'; src.style.opacity = '0';
src.style.transform = 'scale(0.9)'; src.style.transform = 'scale(0.9)';
// ページネーションと相性が悪いので // ページネーションと相性が悪いので
@ -14,10 +14,10 @@ export default {
src.classList.add('_zoom'); src.classList.add('_zoom');
}, },
mounted(src, binding, vn) { mounted(src) {
window.setTimeout(() => { window.setTimeout(() => {
src.style.opacity = '1'; src.style.opacity = '1';
src.style.transform = 'none'; src.style.transform = 'none';
}, 1); }, 1);
}, },
} as Directive; } as Directive<HTMLElement>;

View File

@ -6,12 +6,16 @@
import { throttle } from 'throttle-debounce'; import { throttle } from 'throttle-debounce';
import type { Directive } from 'vue'; import type { Directive } from 'vue';
export default { interface HTMLElementWithObserver extends HTMLElement {
mounted(src, binding, vn) { _observer_?: IntersectionObserver;
}
export const appearDirective = {
mounted(src, binding) {
const fn = binding.value; const fn = binding.value;
if (fn == null) return; if (fn == null) return;
const check = throttle(1000, (entries) => { const check = throttle<IntersectionObserverCallback>(1000, (entries) => {
if (entries.some(entry => entry.isIntersecting)) { if (entries.some(entry => entry.isIntersecting)) {
fn(); fn();
} }
@ -24,7 +28,7 @@ export default {
src._observer_ = observer; src._observer_ = observer;
}, },
unmounted(src, binding, vn) { unmounted(src) {
if (src._observer_) src._observer_.disconnect(); if (src._observer_) src._observer_.disconnect();
}, },
} as Directive; } as Directive<HTMLElementWithObserver, () => void>;

View File

@ -6,8 +6,8 @@
import type { Directive } from 'vue'; import type { Directive } from 'vue';
import { prefer } from '@/preferences.js'; import { prefer } from '@/preferences.js';
export default { export const clickAnimeDirective = {
mounted(el: HTMLElement, binding, vn) { mounted(el) {
if (!prefer.s.animation) return; if (!prefer.s.animation) return;
const target = el.children[0]; const target = el.children[0];
@ -37,4 +37,4 @@ export default {
target.classList.add('_anime_bounce_standBy'); target.classList.add('_anime_bounce_standBy');
}); });
}, },
} as Directive; } as Directive<HTMLElement>;

View File

@ -6,8 +6,12 @@
import type { Directive } from 'vue'; import type { Directive } from 'vue';
import { getScrollContainer, getScrollPosition } from '@@/js/scroll.js'; import { getScrollContainer, getScrollPosition } from '@@/js/scroll.js';
export default { interface HTMLElementWithRO extends HTMLElement {
mounted(src, binding, vn) { _ro_?: ResizeObserver;
}
export const followAppendDirective = {
mounted(src, binding) {
if (binding.value === false) return; if (binding.value === false) return;
let isBottom = true; let isBottom = true;
@ -34,7 +38,7 @@ export default {
src._ro_ = ro; src._ro_ = ro;
}, },
unmounted(src, binding, vn) { unmounted(src) {
if (src._ro_) src._ro_.unobserve(src); if (src._ro_) src._ro_.unobserve(src);
}, },
} as Directive; } as Directive<HTMLElementWithRO, boolean>;

View File

@ -37,8 +37,10 @@ function calc(src: Element) {
info.fn(width, height); info.fn(width, height);
} }
export default { type SizeCallback = (w: number, h: number) => void;
mounted(src, binding, vn) {
export const getSizeDirective = {
mounted(src, binding) {
const resize = new ResizeObserver((entries, observer) => { const resize = new ResizeObserver((entries, observer) => {
calc(src); calc(src);
}); });
@ -48,7 +50,7 @@ export default {
calc(src); calc(src);
}, },
unmounted(src, binding, vn) { unmounted(src, binding) {
binding.value(0, 0); binding.value(0, 0);
const info = mountings.get(src); const info = mountings.get(src);
if (!info) return; if (!info) return;
@ -56,4 +58,4 @@ export default {
if (info.intersection) info.intersection.disconnect(); if (info.intersection) info.intersection.disconnect();
mountings.delete(src); mountings.delete(src);
}, },
} as Directive<Element, (w: number, h: number) => void>; } as Directive<Element, SizeCallback>;

View File

@ -5,8 +5,14 @@
import type { Directive } from 'vue'; import type { Directive } from 'vue';
import { makeHotkey } from '@/utility/hotkey.js'; import { makeHotkey } from '@/utility/hotkey.js';
import type { Keymap } from '@/utility/hotkey.js';
export default { interface HTMLElementWithHotkey extends HTMLElement {
_hotkey_global?: boolean;
_keyHandler?: (ev: KeyboardEvent) => void;
}
export const hotkeyDirective = {
mounted(el, binding) { mounted(el, binding) {
el._hotkey_global = binding.modifiers.global === true; el._hotkey_global = binding.modifiers.global === true;
@ -20,10 +26,11 @@ export default {
}, },
unmounted(el) { unmounted(el) {
if (el._keyHandler == null) return;
if (el._hotkey_global) { if (el._hotkey_global) {
window.document.removeEventListener('keydown', el._keyHandler); window.document.removeEventListener('keydown', el._keyHandler);
} else { } else {
el.removeEventListener('keydown', el._keyHandler); el.removeEventListener('keydown', el._keyHandler);
} }
}, },
} as Directive; } as Directive<HTMLElementWithHotkey, Keymap>;

View File

@ -3,19 +3,19 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import type { App } from 'vue'; import type { App, Directive } from 'vue';
import userPreview from './user-preview.js'; import { userPreviewDirective } from './user-preview.js';
import getSize from './get-size.js'; import { getSizeDirective } from './get-size.js';
import ripple from './ripple.js'; import { rippleDirective } from './ripple.js';
import tooltip from './tooltip.js'; import { tooltipDirective } from './tooltip.js';
import hotkey from './hotkey.js'; import { hotkeyDirective } from './hotkey.js';
import appear from './appear.js'; import { appearDirective } from './appear.js';
import anim from './anim.js'; import { animDirective } from './anim.js';
import clickAnime from './click-anime.js'; import { clickAnimeDirective } from './click-anime.js';
import panel from './panel.js'; import { panelDirective } from './panel.js';
import adaptiveBorder from './adaptive-border.js'; import { adaptiveBorderDirective } from './adaptive-border.js';
import adaptiveBg from './adaptive-bg.js'; import { adaptiveBgDirective } from './adaptive-bg.js';
export default function(app: App) { export default function(app: App) {
for (const [key, value] of Object.entries(directives)) { for (const [key, value] of Object.entries(directives)) {
@ -24,16 +24,32 @@ export default function(app: App) {
} }
export const directives = { export const directives = {
'userPreview': userPreview, 'userPreview': userPreviewDirective,
'user-preview': userPreview, 'user-preview': userPreviewDirective,
'get-size': getSize, 'get-size': getSizeDirective,
'ripple': ripple, 'ripple': rippleDirective,
'tooltip': tooltip, 'tooltip': tooltipDirective,
'hotkey': hotkey, 'hotkey': hotkeyDirective,
'appear': appear, 'appear': appearDirective,
'anim': anim, 'anim': animDirective,
'click-anime': clickAnime, 'click-anime': clickAnimeDirective,
'panel': panel, 'panel': panelDirective,
'adaptive-border': adaptiveBorder, 'adaptive-border': adaptiveBorderDirective,
'adaptive-bg': adaptiveBg, 'adaptive-bg': adaptiveBgDirective,
}; } as Record<string, Directive>;
declare module 'vue' {
export interface ComponentCustomProperties {
vUserPreview: typeof userPreviewDirective;
vGetSize: typeof getSizeDirective;
vRipple: typeof rippleDirective;
vTooltip: typeof tooltipDirective;
vHotkey: typeof hotkeyDirective;
vAppear: typeof appearDirective;
vAnim: typeof animDirective;
vClickAnime: typeof clickAnimeDirective;
vPanel: typeof panelDirective;
vAdaptiveBorder: typeof adaptiveBorderDirective;
vAdaptiveBg: typeof adaptiveBgDirective;
}
}

View File

@ -6,8 +6,8 @@
import type { Directive } from 'vue'; import type { Directive } from 'vue';
import { getBgColor } from '@/utility/get-bg-color.js'; import { getBgColor } from '@/utility/get-bg-color.js';
export default { export const panelDirective = {
mounted(src, binding, vn) { mounted(src) {
const parentBg = getBgColor(src.parentElement) ?? 'transparent'; const parentBg = getBgColor(src.parentElement) ?? 'transparent';
const myBg = getComputedStyle(window.document.documentElement).getPropertyValue('--MI_THEME-panel'); const myBg = getComputedStyle(window.document.documentElement).getPropertyValue('--MI_THEME-panel');
@ -18,4 +18,4 @@ export default {
src.style.backgroundColor = 'var(--MI_THEME-panel)'; src.style.backgroundColor = 'var(--MI_THEME-panel)';
} }
}, },
} as Directive; } as Directive<HTMLElement>;

View File

@ -3,12 +3,13 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import type { Directive } from 'vue';
import MkRippleEffect from '@/components/MkRippleEffect.vue'; import MkRippleEffect from '@/components/MkRippleEffect.vue';
import { prefer } from '@/preferences.js'; import { prefer } from '@/preferences.js';
import { popup } from '@/os.js'; import { popup } from '@/os.js';
export default { export const rippleDirective = {
mounted(el, binding, vn) { mounted(el, binding) {
// 明示的に false であればバインドしない // 明示的に false であればバインドしない
if (binding.value === false) return; if (binding.value === false) return;
if (!prefer.s.animation) return; if (!prefer.s.animation) return;
@ -24,4 +25,4 @@ export default {
}); });
}); });
}, },
}; } as Directive<HTMLElement, boolean | undefined>;

View File

@ -14,13 +14,30 @@ import { popup, alert } from '@/os.js';
const start = isTouchUsing ? 'touchstart' : 'mouseenter'; const start = isTouchUsing ? 'touchstart' : 'mouseenter';
const end = isTouchUsing ? 'touchend' : 'mouseleave'; const end = isTouchUsing ? 'touchend' : 'mouseleave';
export default { type TooltipDirectiveState = {
mounted(el: HTMLElement, binding, vn) { text: string;
_close: null | (() => void);
showTimer: number | null;
hideTimer: number | null;
checkTimer: number | null;
show: () => void;
close: () => void;
};
interface TooltipDirectiveElement extends HTMLElement {
_tooltipDirective_?: TooltipDirectiveState;
}
type TooltipDirectiveModifiers = 'left' | 'right' | 'top' | 'bottom' | 'mfm' | 'noDelay';
type TooltipDirectiveArg = 'dialog';
export const tooltipDirective = {
mounted(el, binding) {
const delay = binding.modifiers.noDelay ? 0 : 100; const delay = binding.modifiers.noDelay ? 0 : 100;
const self = (el as any)._tooltipDirective_ = {} as any; const self = el._tooltipDirective_ = {} as TooltipDirectiveState;
self.text = binding.value as string; self.text = binding.value;
self._close = null; self._close = null;
self.showTimer = null; self.showTimer = null;
self.hideTimer = null; self.hideTimer = null;
@ -28,7 +45,7 @@ export default {
self.close = () => { self.close = () => {
if (self._close) { if (self._close) {
window.clearInterval(self.checkTimer); if (self.checkTimer) window.clearInterval(self.checkTimer);
self._close(); self._close();
self._close = null; self._close = null;
} }
@ -72,8 +89,8 @@ export default {
}); });
el.addEventListener(start, (ev) => { el.addEventListener(start, (ev) => {
window.clearTimeout(self.showTimer); if (self.showTimer) window.clearTimeout(self.showTimer);
window.clearTimeout(self.hideTimer); if (self.hideTimer) window.clearTimeout(self.hideTimer);
if (delay === 0) { if (delay === 0) {
self.show(); self.show();
} else { } else {
@ -82,8 +99,8 @@ export default {
}, { passive: true }); }, { passive: true });
el.addEventListener(end, () => { el.addEventListener(end, () => {
window.clearTimeout(self.showTimer); if (self.showTimer) window.clearTimeout(self.showTimer);
window.clearTimeout(self.hideTimer); if (self.hideTimer) window.clearTimeout(self.hideTimer);
if (delay === 0) { if (delay === 0) {
self.close(); self.close();
} else { } else {
@ -92,18 +109,23 @@ export default {
}, { passive: true }); }, { passive: true });
el.addEventListener('click', () => { el.addEventListener('click', () => {
window.clearTimeout(self.showTimer); if (self.showTimer) window.clearTimeout(self.showTimer);
self.close(); self.close();
}); });
}, },
updated(el, binding) { updated(el, binding) {
const self = el._tooltipDirective_; const self = el._tooltipDirective_;
if (self == null) return;
self.text = binding.value as string; self.text = binding.value as string;
}, },
unmounted(el, binding, vn) { unmounted(el) {
const self = el._tooltipDirective_; const self = el._tooltipDirective_;
window.clearInterval(self.checkTimer); if (self == null) return;
if (self.showTimer) window.clearTimeout(self.showTimer);
if (self.hideTimer) window.clearTimeout(self.hideTimer);
if (self.checkTimer) window.clearTimeout(self.checkTimer);
self.close();
}, },
} as Directive; } as Directive<TooltipDirectiveElement, string, TooltipDirectiveModifiers, TooltipDirectiveArg>;

View File

@ -5,18 +5,19 @@
import { defineAsyncComponent, ref } from 'vue'; import { defineAsyncComponent, ref } from 'vue';
import type { Directive } from 'vue'; import type { Directive } from 'vue';
import * as Misskey from 'misskey-js';
import { popup } from '@/os.js'; import { popup } from '@/os.js';
import { isTouchUsing } from '@/utility/touch.js'; import { isTouchUsing } from '@/utility/touch.js';
export class UserPreview { export class UserPreview {
private el; private el: HTMLElement;
private user; private user: string | Misskey.entities.UserDetailed;
private showTimer; private showTimer: number | null = null;
private hideTimer; private hideTimer: number | null = null;
private checkTimer; private checkTimer: number | null = null;
private promise; private promise: null | { cancel: () => void } = null;
constructor(el, user) { constructor(el: HTMLElement, user: string | Misskey.entities.UserDetailed) {
this.el = el; this.el = el;
this.user = user; this.user = user;
@ -43,10 +44,10 @@ export class UserPreview {
source: this.el, source: this.el,
}, { }, {
mouseover: () => { mouseover: () => {
window.clearTimeout(this.hideTimer); if (this.hideTimer) window.clearTimeout(this.hideTimer);
}, },
mouseleave: () => { mouseleave: () => {
window.clearTimeout(this.showTimer); if (this.showTimer) window.clearTimeout(this.showTimer);
this.hideTimer = window.setTimeout(this.close, 500); this.hideTimer = window.setTimeout(this.close, 500);
}, },
closed: () => dispose(), closed: () => dispose(),
@ -60,8 +61,8 @@ export class UserPreview {
this.checkTimer = window.setInterval(() => { this.checkTimer = window.setInterval(() => {
if (!window.document.body.contains(this.el)) { if (!window.document.body.contains(this.el)) {
window.clearTimeout(this.showTimer); if (this.showTimer) window.clearTimeout(this.showTimer);
window.clearTimeout(this.hideTimer); if (this.hideTimer) window.clearTimeout(this.hideTimer);
this.close(); this.close();
} }
}, 1000); }, 1000);
@ -69,26 +70,26 @@ export class UserPreview {
private close() { private close() {
if (this.promise) { if (this.promise) {
window.clearInterval(this.checkTimer); if (this.checkTimer) window.clearInterval(this.checkTimer);
this.promise.cancel(); this.promise.cancel();
this.promise = null; this.promise = null;
} }
} }
private onMouseover() { private onMouseover() {
window.clearTimeout(this.showTimer); if (this.showTimer) window.clearTimeout(this.showTimer);
window.clearTimeout(this.hideTimer); if (this.hideTimer) window.clearTimeout(this.hideTimer);
this.showTimer = window.setTimeout(this.show, 500); this.showTimer = window.setTimeout(this.show, 500);
} }
private onMouseleave() { private onMouseleave() {
window.clearTimeout(this.showTimer); if (this.showTimer) window.clearTimeout(this.showTimer);
window.clearTimeout(this.hideTimer); if (this.hideTimer) window.clearTimeout(this.hideTimer);
this.hideTimer = window.setTimeout(this.close, 500); this.hideTimer = window.setTimeout(this.close, 500);
} }
private onClick() { private onClick() {
window.clearTimeout(this.showTimer); if (this.showTimer) window.clearTimeout(this.showTimer);
this.close(); this.close();
} }
@ -105,8 +106,14 @@ export class UserPreview {
} }
} }
export default { interface UserPreviewDirectiveElement extends HTMLElement {
mounted(el: HTMLElement, binding, vn) { _userPreviewDirective_?: {
preview: UserPreview;
};
}
export const userPreviewDirective = {
mounted(el, binding) {
if (binding.value == null) return; if (binding.value == null) return;
if (isTouchUsing) return; if (isTouchUsing) return;
@ -117,10 +124,11 @@ export default {
self.preview = new UserPreview(el, binding.value); self.preview = new UserPreview(el, binding.value);
}, },
unmounted(el, binding, vn) { unmounted(el, binding) {
if (binding.value == null) return; if (binding.value == null) return;
const self = el._userPreviewDirective_; const self = el._userPreviewDirective_;
if (self == null) return;
self.preview.detach(); self.preview.detach();
}, },
} as Directive; } as Directive<UserPreviewDirectiveElement, string | Misskey.entities.UserDetailed>;

0
patches/.gitkeep Normal file
View File

View File

@ -1,17 +0,0 @@
diff --git a/driver/postgres/PostgresDriver.js b/driver/postgres/PostgresDriver.js
index e13b903c73b71113bb529552e59fb4ce0ca8af0c..50de6a60120ece7ebf49009eac588a5313343f39 100644
--- a/driver/postgres/PostgresDriver.js
+++ b/driver/postgres/PostgresDriver.js
@@ -819,10 +819,10 @@ class PostgresDriver {
const tableColumnDefault = typeof tableColumn.default === "string"
? JSON.parse(tableColumn.default.substring(1, tableColumn.default.length - 1))
: tableColumn.default;
- return OrmUtils_1.OrmUtils.deepCompare(columnMetadata.default, tableColumnDefault);
+ return OrmUtils_1.OrmUtils.deepCompare(columnMetadata.default, tableColumnDefault ?? null);
}
const columnDefault = this.lowerDefaultValueIfNecessary(this.normalizeDefault(columnMetadata));
- return columnDefault === tableColumn.default;
+ return columnDefault === tableColumn.default || columnDefault === undefined && tableColumn.default.toLowerCase() === 'null';
}
/**
* Normalizes "isUnique" value of the column.

View File

@ -9,11 +9,6 @@ overrides:
lodash: 4.17.21 lodash: 4.17.21
'@aiscript-dev/aiscript-languageserver': '-' '@aiscript-dev/aiscript-languageserver': '-'
patchedDependencies:
typeorm:
hash: b1141d7fd5ff7abdae28a93b2a634f595c7848518643c08d120698da1d2b4ebc
path: patches/typeorm.patch
importers: importers:
.: .:
@ -430,7 +425,7 @@ importers:
version: 4.2.0 version: 4.2.0
typeorm: typeorm:
specifier: 0.3.27 specifier: 0.3.27
version: 0.3.27(patch_hash=b1141d7fd5ff7abdae28a93b2a634f595c7848518643c08d120698da1d2b4ebc)(ioredis@5.8.0)(pg@8.16.3)(reflect-metadata@0.2.2) version: 0.3.27(ioredis@5.8.0)(pg@8.16.3)(reflect-metadata@0.2.2)
typescript: typescript:
specifier: 5.9.3 specifier: 5.9.3
version: 5.9.3 version: 5.9.3
@ -22133,7 +22128,7 @@ snapshots:
typedarray@0.0.6: {} typedarray@0.0.6: {}
typeorm@0.3.27(patch_hash=b1141d7fd5ff7abdae28a93b2a634f595c7848518643c08d120698da1d2b4ebc)(ioredis@5.8.0)(pg@8.16.3)(reflect-metadata@0.2.2): typeorm@0.3.27(ioredis@5.8.0)(pg@8.16.3)(reflect-metadata@0.2.2):
dependencies: dependencies:
'@sqltools/formatter': 1.2.5 '@sqltools/formatter': 1.2.5
ansis: 3.17.0 ansis: 3.17.0