completely re-implement note visibility as NoteVisibilityService
This commit is contained in:
parent
d1912362e0
commit
85ca2269e4
27 changed files with 925 additions and 420 deletions
|
|
@ -20,6 +20,7 @@ import { EnvService } from '@/core/EnvService.js';
|
|||
import { ApUtilityService } from '@/core/activitypub/ApUtilityService.js';
|
||||
import { ApLogService } from '@/core/ApLogService.js';
|
||||
import { UpdateInstanceQueue } from '@/core/UpdateInstanceQueue.js';
|
||||
import { NoteVisibilityService } from '@/core/NoteVisibilityService.js';
|
||||
import { AccountMoveService } from './AccountMoveService.js';
|
||||
import { AccountUpdateService } from './AccountUpdateService.js';
|
||||
import { AnnouncementService } from './AnnouncementService.js';
|
||||
|
|
@ -240,6 +241,7 @@ const $RegistryApiService: Provider = { provide: 'RegistryApiService', useExisti
|
|||
const $ReversiService: Provider = { provide: 'ReversiService', useExisting: ReversiService };
|
||||
const $TimeService: Provider = { provide: 'TimeService', useExisting: TimeService };
|
||||
const $EnvService: Provider = { provide: 'EnvService', useExisting: EnvService };
|
||||
const $NoteVisibilityService: Provider = { provide: 'NoteVisibilityService', useExisting: NoteVisibilityService };
|
||||
|
||||
const $ChartLoggerService: Provider = { provide: 'ChartLoggerService', useExisting: ChartLoggerService };
|
||||
const $FederationChart: Provider = { provide: 'FederationChart', useExisting: FederationChart };
|
||||
|
|
@ -400,6 +402,7 @@ const $SponsorsService: Provider = { provide: 'SponsorsService', useExisting: Sp
|
|||
ReversiService,
|
||||
TimeService,
|
||||
EnvService,
|
||||
NoteVisibilityService,
|
||||
|
||||
ChartLoggerService,
|
||||
FederationChart,
|
||||
|
|
@ -556,6 +559,7 @@ const $SponsorsService: Provider = { provide: 'SponsorsService', useExisting: Sp
|
|||
$ReversiService,
|
||||
$TimeService,
|
||||
$EnvService,
|
||||
$NoteVisibilityService,
|
||||
|
||||
$ChartLoggerService,
|
||||
$FederationChart,
|
||||
|
|
@ -713,6 +717,7 @@ const $SponsorsService: Provider = { provide: 'SponsorsService', useExisting: Sp
|
|||
ReversiService,
|
||||
TimeService,
|
||||
EnvService,
|
||||
NoteVisibilityService,
|
||||
|
||||
FederationChart,
|
||||
NotesChart,
|
||||
|
|
@ -867,6 +872,7 @@ const $SponsorsService: Provider = { provide: 'SponsorsService', useExisting: Sp
|
|||
$ReversiService,
|
||||
$TimeService,
|
||||
$EnvService,
|
||||
$NoteVisibilityService,
|
||||
|
||||
$FederationChart,
|
||||
$NotesChart,
|
||||
|
|
|
|||
|
|
@ -15,10 +15,11 @@ import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
|
|||
import { FanoutTimelineName, FanoutTimelineService } from '@/core/FanoutTimelineService.js';
|
||||
import { UtilityService } from '@/core/UtilityService.js';
|
||||
import { isUserRelated } from '@/misc/is-user-related.js';
|
||||
import { isPureRenote, isQuote, isRenote } from '@/misc/is-renote.js';
|
||||
import { isQuote, isRenote } from '@/misc/is-renote.js';
|
||||
import { CacheService } from '@/core/CacheService.js';
|
||||
import { isReply } from '@/misc/is-reply.js';
|
||||
import { isInstanceMuted } from '@/misc/is-instance-muted.js';
|
||||
import { NoteVisibilityService, PopulatedNote } from '@/core/NoteVisibilityService.js';
|
||||
|
||||
type TimelineOptions = {
|
||||
untilId: string | null,
|
||||
|
|
@ -56,6 +57,7 @@ export class FanoutTimelineEndpointService {
|
|||
private cacheService: CacheService,
|
||||
private fanoutTimelineService: FanoutTimelineService,
|
||||
private utilityService: UtilityService,
|
||||
private readonly noteVisibilityService: NoteVisibilityService,
|
||||
) {
|
||||
}
|
||||
|
||||
|
|
@ -110,52 +112,14 @@ export class FanoutTimelineEndpointService {
|
|||
filter = (note) => (!isRenote(note) || isQuote(note)) && parentFilter(note);
|
||||
}
|
||||
|
||||
if (ps.me) {
|
||||
const [
|
||||
userIdsWhoMeMuting,
|
||||
userIdsWhoMeMutingRenotes,
|
||||
userIdsWhoBlockingMe,
|
||||
userMutedInstances,
|
||||
myFollowings,
|
||||
myThreadMutings,
|
||||
myNoteMutings,
|
||||
me,
|
||||
] = await Promise.all([
|
||||
this.cacheService.userMutingsCache.fetch(ps.me.id),
|
||||
this.cacheService.renoteMutingsCache.fetch(ps.me.id),
|
||||
this.cacheService.userBlockedCache.fetch(ps.me.id),
|
||||
this.cacheService.userProfileCache.fetch(ps.me.id).then(p => new Set(p.mutedInstances)),
|
||||
this.cacheService.userFollowingsCache.fetch(ps.me.id).then(fs => new Set(fs.keys())),
|
||||
this.cacheService.threadMutingsCache.fetch(ps.me.id),
|
||||
this.cacheService.noteMutingsCache.fetch(ps.me.id),
|
||||
this.cacheService.findUserById(ps.me.id),
|
||||
]);
|
||||
{
|
||||
const me = ps.me ? await this.cacheService.findUserById(ps.me.id) : null;
|
||||
const data = await this.noteVisibilityService.populateData(me);
|
||||
|
||||
const parentFilter = filter;
|
||||
filter = (note) => {
|
||||
if (isUserRelated(note, userIdsWhoBlockingMe, ps.ignoreAuthorFromBlock)) return false;
|
||||
if (isUserRelated(note, userIdsWhoMeMuting, ps.ignoreAuthorFromMute)) return false;
|
||||
if (!ps.ignoreAuthorFromMute && isRenote(note) && !isQuote(note) && userIdsWhoMeMutingRenotes.has(note.userId)) return false;
|
||||
if (isInstanceMuted(note, userMutedInstances)) return false;
|
||||
|
||||
// Silenced users (when logged in)
|
||||
if (!ps.ignoreAuthorFromUserSilence && !myFollowings.has(note.userId)) {
|
||||
if (note.user?.isSilenced || note.user?.instance?.isSilenced) return false;
|
||||
if (note.reply?.user?.isSilenced || note.reply?.user?.instance?.isSilenced) return false;
|
||||
if (note.renote?.user?.isSilenced || note.renote?.user?.instance?.isSilenced) return false;
|
||||
}
|
||||
|
||||
// Muted threads / posts
|
||||
if (!ps.includeMutedNotes) {
|
||||
if (myThreadMutings.has(note.threadId ?? note.id) || myNoteMutings.has(note.id)) return false;
|
||||
if (note.replyId && myNoteMutings.has(note.replyId)) return false;
|
||||
if (note.renote && (myThreadMutings.has(note.renote.threadId ?? note.renote.id) || myNoteMutings.has(note.renote.id))) return false;
|
||||
}
|
||||
|
||||
// Invisible notes
|
||||
if (!this.noteEntityService.isVisibleForMeSync(note, me, myFollowings, userIdsWhoBlockingMe)) {
|
||||
return false;
|
||||
}
|
||||
const { accessible, silence } = this.noteVisibilityService.checkNoteVisibility(note as PopulatedNote, me, { data, filters: { includeSilencedAuthor: ps.ignoreAuthorFromUserSilence } });
|
||||
if (!accessible || silence) return false;
|
||||
|
||||
return parentFilter(note);
|
||||
};
|
||||
|
|
@ -191,36 +155,6 @@ export class FanoutTimelineEndpointService {
|
|||
};
|
||||
}
|
||||
|
||||
{
|
||||
const parentFilter = filter;
|
||||
filter = (note) => {
|
||||
// Silenced users (when logged out)
|
||||
if (!ps.ignoreAuthorFromUserSilence && !ps.me) {
|
||||
if (note.user?.isSilenced || note.user?.instance?.isSilenced) return false;
|
||||
if (note.reply?.user?.isSilenced || note.reply?.user?.instance?.isSilenced) return false;
|
||||
if (note.renote?.user?.isSilenced || note.renote?.user?.instance?.isSilenced) return false;
|
||||
}
|
||||
|
||||
return parentFilter(note);
|
||||
};
|
||||
}
|
||||
|
||||
// This one MUST be last!
|
||||
{
|
||||
const parentFilter = filter;
|
||||
filter = (note) => {
|
||||
// If this is a boost, then first run all checks for the boost target.
|
||||
if (isPureRenote(note) && note.renote) {
|
||||
if (!parentFilter(note.renote)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Either way, make sure to run the checks for the actual note too!
|
||||
return parentFilter(note);
|
||||
};
|
||||
}
|
||||
|
||||
const redisTimeline: MiNote[] = [];
|
||||
let readFromRedis = 0;
|
||||
let lastSuccessfulRate = 1; // rateをキャッシュする?
|
||||
|
|
|
|||
420
packages/backend/src/core/NoteVisibilityService.ts
Normal file
420
packages/backend/src/core/NoteVisibilityService.ts
Normal file
|
|
@ -0,0 +1,420 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: hazelnoot and other Sharkey contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { CacheService } from '@/core/CacheService.js';
|
||||
import type { MiNote } from '@/models/Note.js';
|
||||
import type { MiUser } from '@/models/User.js';
|
||||
import { MiInstance } from '@/models/Instance.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import { Packed } from '@/misc/json-schema.js';
|
||||
import { IdService } from '@/core/IdService.js';
|
||||
import { awaitAll } from '@/misc/prelude/await-all.js';
|
||||
import { FederatedInstanceService } from '@/core/FederatedInstanceService.js';
|
||||
import type { MiFollowing, NotesRepository } from '@/models/_.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
|
||||
/**
|
||||
* Visibility level for a given user towards a given post.
|
||||
*/
|
||||
export interface NoteVisibilityResult {
|
||||
/**
|
||||
* Whether the user has access to view this post.
|
||||
*/
|
||||
accessible: boolean;
|
||||
|
||||
/**
|
||||
* If the user should be shown only a redacted version of the post.
|
||||
* (see NoteEntityService.hideNote() for details.)
|
||||
*/
|
||||
redact: boolean;
|
||||
|
||||
/**
|
||||
* If false, the note should be visible by default. (normal case)
|
||||
* If true, the note should be hidden by default. (Silences, mutes, etc.)
|
||||
* If "timeline", the note should be hidden in timelines only. (following w/o replies)
|
||||
*/
|
||||
silence: boolean;
|
||||
}
|
||||
|
||||
export interface NoteVisibilityFilters {
|
||||
/**
|
||||
* If false, exclude replies to other users unless the "include replies to others in timeline" has been enabled for the note's author.
|
||||
* If true (default), then replies are treated like any other post.
|
||||
*/
|
||||
includeReplies?: boolean;
|
||||
|
||||
/**
|
||||
* If true, treat the note's author as never being silenced. Does not apply to reply or renote targets, unless they're by the same author.
|
||||
* If false (default), then silence is enforced for all notes.
|
||||
*/
|
||||
includeSilencedAuthor?: boolean;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class NoteVisibilityService {
|
||||
constructor(
|
||||
@Inject(DI.notesRepository)
|
||||
private readonly notesRepository: NotesRepository,
|
||||
|
||||
private readonly cacheService: CacheService,
|
||||
private readonly idService: IdService,
|
||||
private readonly federatedInstanceService: FederatedInstanceService,
|
||||
) {}
|
||||
|
||||
@bindThis
|
||||
public async checkNoteVisibilityAsync(note: MiNote | Packed<'Note'>, user: string | PopulatedUser, opts?: { filters?: NoteVisibilityFilters, hint?: Partial<NoteVisibilityData> }): Promise<NoteVisibilityResult> {
|
||||
if (typeof(user) === 'string') {
|
||||
user = await this.cacheService.findUserById(user);
|
||||
}
|
||||
|
||||
const populatedNote = await this.populateNote(note);
|
||||
const populatedData = await this.populateData(user, opts?.hint ?? {});
|
||||
|
||||
return this.checkNoteVisibility(populatedNote, user, { filters: opts?.filters, data: populatedData });
|
||||
}
|
||||
|
||||
private async populateNote(note: Packed<'Note'>, dive?: boolean): Promise<Packed<'Note'>>;
|
||||
private async populateNote(note: MiNote, dive?: boolean): Promise<PopulatedMiNote>;
|
||||
private async populateNote(note: MiNote | Packed<'Note'>, dive?: boolean): Promise<PopulatedNote>;
|
||||
private async populateNote(note: MiNote | Packed<'Note'>, dive = true): Promise<PopulatedNote> {
|
||||
// Packed<'Note'> is already fully loaded
|
||||
if (isPackedNote(note)) return note;
|
||||
|
||||
// noinspection ES6MissingAwait
|
||||
return await awaitAll({
|
||||
...note,
|
||||
user: this.getNoteUser(note),
|
||||
renote: dive ? this.getNoteRenote(note) : null,
|
||||
reply: dive ? this.getNoteReply(note) : null,
|
||||
});
|
||||
}
|
||||
|
||||
private async getNoteUser(note: MiNote): Promise<PopulatedMiNote['user']> {
|
||||
const user = note.user ?? await this.cacheService.findUserById(note.userId);
|
||||
return {
|
||||
...user,
|
||||
instance: user.instance ?? (user.host ? await this.federatedInstanceService.fetchOrRegister(user.host) : null),
|
||||
};
|
||||
}
|
||||
|
||||
private async getNoteRenote(note: MiNote): Promise<PopulatedMiNote['renote']> {
|
||||
if (!note.renoteId) return null;
|
||||
|
||||
const renote = note.renote ?? await this.notesRepository.findOneOrFail({
|
||||
where: { id: note.renoteId },
|
||||
relations: {
|
||||
user: {
|
||||
instance: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return await this.populateNote(renote, false);
|
||||
}
|
||||
|
||||
private async getNoteReply(note: MiNote): Promise<PopulatedMiNote['reply']> {
|
||||
if (!note.replyId) return null;
|
||||
|
||||
const reply = note.reply ?? await this.notesRepository.findOneOrFail({
|
||||
where: { id: note.replyId },
|
||||
relations: {
|
||||
user: {
|
||||
instance: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return await this.populateNote(reply, false);
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async populateData(user: PopulatedUser, hint?: Partial<NoteVisibilityData>): Promise<NoteVisibilityData> {
|
||||
// noinspection ES6MissingAwait
|
||||
const [
|
||||
userBlockers,
|
||||
userFollowings,
|
||||
userMutedThreads,
|
||||
userMutedNotes,
|
||||
userMutedUsers,
|
||||
userMutedUserRenotes,
|
||||
userMutedInstances,
|
||||
] = await Promise.all([
|
||||
user ? (hint?.userBlockers ?? this.cacheService.userBlockedCache.fetch(user.id)) : null,
|
||||
user ? (hint?.userFollowings ?? this.cacheService.userFollowingsCache.fetch(user.id)) : null,
|
||||
user ? (hint?.userMutedThreads ?? this.cacheService.threadMutingsCache.fetch(user.id)) : null,
|
||||
user ? (hint?.userMutedNotes ?? this.cacheService.noteMutingsCache.fetch(user.id)) : null,
|
||||
user ? (hint?.userMutedUsers ?? this.cacheService.userMutingsCache.fetch(user.id)) : null,
|
||||
user ? (hint?.userMutedUserRenotes ?? this.cacheService.renoteMutingsCache.fetch(user.id)) : null,
|
||||
user ? (hint?.userMutedInstances ?? this.cacheService.userProfileCache.fetch(user.id).then(p => new Set(p.mutedInstances))) : null,
|
||||
]);
|
||||
|
||||
return {
|
||||
userBlockers,
|
||||
userFollowings,
|
||||
userMutedThreads,
|
||||
userMutedNotes,
|
||||
userMutedUsers,
|
||||
userMutedUserRenotes,
|
||||
userMutedInstances,
|
||||
};
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public checkNoteVisibility(note: PopulatedNote, user: PopulatedUser, opts: { filters?: NoteVisibilityFilters, data: NoteVisibilityData }): NoteVisibilityResult {
|
||||
// Copy note since we mutate it below
|
||||
note = {
|
||||
...note,
|
||||
renote: note.renote ? { ...note.renote } : null,
|
||||
reply: note.reply ? { ...note.reply } : null,
|
||||
} as PopulatedNote;
|
||||
|
||||
this.syncVisibility(note);
|
||||
|
||||
const accessible = this.isAccessible(note, user, opts.data);
|
||||
const redact = this.shouldRedact(note, user, opts.data);
|
||||
const silence = this.shouldSilence(note, user, opts.data, opts.filters);
|
||||
|
||||
const baseVisibility = { accessible, redact, silence };
|
||||
|
||||
// For boosts (pure renotes), we must recurse and pick the lowest common access level.
|
||||
if (isPopulatedBoost(note)) {
|
||||
const boostVisibility = this.checkNoteVisibility(note.renote, user, opts);
|
||||
return {
|
||||
accessible: baseVisibility.accessible && boostVisibility.accessible,
|
||||
redact: baseVisibility.redact || boostVisibility.redact,
|
||||
silence: baseVisibility.silence || boostVisibility.silence,
|
||||
};
|
||||
}
|
||||
|
||||
return baseVisibility;
|
||||
}
|
||||
|
||||
// Based on NoteEntityService.isVisibleForMe
|
||||
private isAccessible(note: PopulatedNote, user: PopulatedUser, data: NoteVisibilityData): boolean {
|
||||
// We can always view our own notes
|
||||
if (user?.id === note.userId) return true;
|
||||
|
||||
// We can *never* view blocked notes
|
||||
if (data.userBlockers?.has(note.userId)) return false;
|
||||
|
||||
if (note.visibility === 'specified') {
|
||||
return this.isAccessibleDM(note, user);
|
||||
} else if (note.visibility === 'followers') {
|
||||
return this.isAccessibleFO(note, user, data);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private isAccessibleDM(note: PopulatedNote, user: PopulatedUser): boolean {
|
||||
// Must be logged in to view DM
|
||||
if (user == null) return false;
|
||||
|
||||
// Can be visible to me
|
||||
if (note.visibleUserIds?.includes(user.id)) return true;
|
||||
|
||||
// Otherwise invisible
|
||||
return false;
|
||||
}
|
||||
|
||||
private isAccessibleFO(note: PopulatedNote, user: PopulatedUser, data: NoteVisibilityData): boolean {
|
||||
// Must be logged in to view FO
|
||||
if (user == null) return false;
|
||||
|
||||
// Can be a reply to me
|
||||
if (note.reply?.userId === user.id) return true;
|
||||
|
||||
// Can mention me
|
||||
if (note.mentions?.includes(user.id)) return true;
|
||||
|
||||
// Can be visible to me
|
||||
if (note.visibleUserIds?.includes(user.id)) return true;
|
||||
|
||||
// Can be followed by me
|
||||
if (data.userFollowings?.has(note.userId)) return true;
|
||||
|
||||
// Can be two remote users, since we can't verify remote->remote following.
|
||||
if (note.userHost != null && user.host != null) return true;
|
||||
|
||||
// Otherwise invisible
|
||||
return false;
|
||||
}
|
||||
|
||||
// Based on NoteEntityService.treatVisibility
|
||||
@bindThis
|
||||
public syncVisibility(note: PopulatedNote): void {
|
||||
// Make followers-only
|
||||
if (note.user.makeNotesFollowersOnlyBefore && note.visibility !== 'specified' && note.visibility !== 'followers') {
|
||||
const followersOnlyBefore = note.user.makeNotesFollowersOnlyBefore * 1000;
|
||||
const createdAt = 'createdAt' in note
|
||||
? new Date(note.createdAt).getTime()
|
||||
: this.idService.parse(note.id).date.getTime();
|
||||
|
||||
// I don't understand this logic, but I tried to break it out for readability
|
||||
const followersOnlyOpt1 = followersOnlyBefore <= 0 && (Date.now() - createdAt > 0 - followersOnlyBefore);
|
||||
const followersOnlyOpt2 = followersOnlyBefore > 0 && (createdAt < followersOnlyBefore);
|
||||
if (followersOnlyOpt1 || followersOnlyOpt2) {
|
||||
note.visibility = 'followers';
|
||||
}
|
||||
}
|
||||
|
||||
// Recurse
|
||||
if (note.renote) {
|
||||
this.syncVisibility(note.renote);
|
||||
}
|
||||
if (note.reply) {
|
||||
this.syncVisibility(note.reply);
|
||||
}
|
||||
}
|
||||
|
||||
// Based on NoteEntityService.hideNote
|
||||
private shouldRedact(note: PopulatedNote, user: PopulatedUser, data: NoteVisibilityData): boolean {
|
||||
// Never redact our own notes
|
||||
if (user?.id === note.userId) return false;
|
||||
|
||||
// Redact if sign-in required
|
||||
if (note.user.requireSigninToViewContents && !user) return true;
|
||||
|
||||
// Redact if note has expired
|
||||
if (note.user.makeNotesHiddenBefore) {
|
||||
const hiddenBefore = note.user.makeNotesHiddenBefore * 1000;
|
||||
const createdAt = 'createdAt' in note
|
||||
? new Date(note.createdAt).getTime()
|
||||
: this.idService.parse(note.id).date.getTime();
|
||||
|
||||
// I don't understand this logic, but I tried to break it out for readability
|
||||
const hiddenOpt1 = hiddenBefore <= 0 && (Date.now() - createdAt > 0 - hiddenBefore);
|
||||
const hiddenOpt2 = hiddenBefore > 0 && (createdAt < hiddenBefore);
|
||||
if (hiddenOpt1 || hiddenOpt2) return true;
|
||||
}
|
||||
|
||||
// Redact if inaccessible.
|
||||
// We have to repeat the check in case note visibility changed in treatVisibility!
|
||||
if (!this.isAccessible(note, user, data)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Otherwise don't redact
|
||||
return false;
|
||||
}
|
||||
|
||||
// Based on inconsistent logic from all around the app
|
||||
private shouldSilence(note: PopulatedNote, user: PopulatedUser, data: NoteVisibilityData, filters: NoteVisibilityFilters | undefined): boolean {
|
||||
if (this.shouldSilenceForMute(note, data)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this.shouldSilenceForSilence(note, user, data, filters?.includeSilencedAuthor ?? false)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!filters?.includeReplies && this.shouldSilenceForFollowWithoutReplies(note, user, data)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private shouldSilenceForMute(note: PopulatedNote, data: NoteVisibilityData): boolean {
|
||||
// Silence if we've muted the thread
|
||||
if (data.userMutedThreads?.has(note.threadId ?? note.id)) return true;
|
||||
|
||||
// Silence if we've muted the note
|
||||
if (data.userMutedNotes?.has(note.id)) return true;
|
||||
|
||||
// Silence if we've muted the user
|
||||
if (data.userMutedUsers?.has(note.userId)) return true;
|
||||
|
||||
// Silence if we've muted renotes from the user
|
||||
if (isPopulatedBoost(note) && data.userMutedUserRenotes?.has(note.userId)) return true;
|
||||
|
||||
// Silence if we've muted the instance
|
||||
if (note.userHost && data.userMutedInstances?.has(note.userHost)) return true;
|
||||
|
||||
// Otherwise don't silence
|
||||
return false;
|
||||
}
|
||||
|
||||
private shouldSilenceForSilence(note: PopulatedNote, user: PopulatedUser, data: NoteVisibilityData, includeSilencedAuthor: boolean): boolean {
|
||||
// Don't silence if it's us
|
||||
if (note.userId === user?.id) return false;
|
||||
|
||||
// Don't silence if we're following
|
||||
if (data.userFollowings?.has(note.userId)) return false;
|
||||
|
||||
if (!includeSilencedAuthor) {
|
||||
// Silence if user is silenced
|
||||
if (note.user.isSilenced) return true;
|
||||
|
||||
// Silence if user instance is silenced
|
||||
if (note.user.instance?.isSilenced) return true;
|
||||
}
|
||||
|
||||
// Silence if renote is silenced
|
||||
if (note.renote && note.renote.userId !== note.userId && this.shouldSilenceForSilence(note.renote, user, data, false)) return true;
|
||||
|
||||
// Silence if reply is silenced
|
||||
if (note.reply && note.reply.userId !== note.userId && this.shouldSilenceForSilence(note.reply, user, data, false)) return true;
|
||||
|
||||
// Otherwise don't silence
|
||||
return false;
|
||||
}
|
||||
|
||||
private shouldSilenceForFollowWithoutReplies(note: PopulatedNote, user: PopulatedUser, data: NoteVisibilityData): boolean {
|
||||
// Don't silence if it's not a reply
|
||||
if (!note.reply) return false;
|
||||
|
||||
// Don't silence if it's a self-reply
|
||||
if (note.reply.userId === note.userId) return false;
|
||||
|
||||
// Don't silence if it's a reply to us
|
||||
if (note.reply.userId === user?.id) return false;
|
||||
|
||||
// Don't silence if it's our post
|
||||
if (note.userId === user?.id) return false;
|
||||
|
||||
// Don't silence if we follow w/ replies
|
||||
if (user && data.userFollowings?.get(user.id)?.withReplies) return false;
|
||||
|
||||
// Silence otherwise
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
export interface NoteVisibilityData {
|
||||
userBlockers: Set<string> | null;
|
||||
userFollowings: Map<string, Omit<MiFollowing, 'isFollowerHibernated'>> | null;
|
||||
userMutedThreads: Set<string> | null;
|
||||
userMutedNotes: Set<string> | null;
|
||||
userMutedUsers: Set<string> | null;
|
||||
userMutedUserRenotes: Set<string> | null;
|
||||
userMutedInstances: Set<string> | null;
|
||||
}
|
||||
|
||||
export type PopulatedUser = Pick<MiUser, 'id' | 'host'> | null | undefined;
|
||||
|
||||
export type PopulatedNote = PopulatedMiNote | Packed<'Note'>;
|
||||
|
||||
type PopulatedMiNote = MiNote & {
|
||||
user: MiUser & {
|
||||
instance: MiInstance | null,
|
||||
}
|
||||
renote: PopulatedMiNote | null,
|
||||
reply: PopulatedMiNote | null,
|
||||
};
|
||||
|
||||
function isPopulatedBoost(note: PopulatedNote): note is PopulatedNote & { renote: PopulatedNote } {
|
||||
return note.renoteId != null
|
||||
&& note.replyId == null
|
||||
&& note.text == null
|
||||
&& note.cw == null
|
||||
&& (note.fileIds == null || note.fileIds.length === 0);
|
||||
}
|
||||
|
||||
function isPackedNote(note: MiNote | Packed<'Note'>): note is Packed<'Note'> {
|
||||
// Kind of a hack: determine whether it's packed by looking for property that doesn't exist in MiNote
|
||||
return 'isFavorited' in note;
|
||||
}
|
||||
|
|
@ -128,29 +128,44 @@ export class QueryService {
|
|||
}
|
||||
|
||||
@bindThis
|
||||
public generateMutedUserQueryForNotes<E extends ObjectLiteral>(q: SelectQueryBuilder<E>, me: { id: MiUser['id'] }, exclude?: { id: MiUser['id'] }): SelectQueryBuilder<E> {
|
||||
// 投稿の作者をミュートしていない かつ
|
||||
// 投稿の返信先の作者をミュートしていない かつ
|
||||
// 投稿の引用元の作者をミュートしていない
|
||||
return this
|
||||
.andNotMutingUser(q, ':meId', 'note.userId', exclude)
|
||||
public generateMutedUserQueryForNotes<E extends ObjectLiteral>(q: SelectQueryBuilder<E>, me: { id: MiUser['id'] }, excludeAuthor = false): SelectQueryBuilder<E> {
|
||||
if (!excludeAuthor) {
|
||||
this
|
||||
// muted user
|
||||
.andNotMutingUser(q, ':meId', 'note.userId')
|
||||
// muted host
|
||||
.andWhere(new Brackets(qb => {
|
||||
qb.orWhere('note.userHost IS NULL');
|
||||
this.orFollowingUser(qb, ':meId', 'note.userId');
|
||||
this.orNotMutingInstance(qb, ':meId', 'note.userHost');
|
||||
}));
|
||||
}
|
||||
|
||||
return q
|
||||
// muted reply user
|
||||
.andWhere(new Brackets(qb => this
|
||||
.orNotMutingUser(qb, ':meId', 'note.replyUserId', exclude)
|
||||
.orNotMutingUser(qb, ':meId', 'note.replyUserId')
|
||||
.orWhere('note.replyUserId = note.userId')
|
||||
.orWhere('note.replyUserId IS NULL')))
|
||||
// muted renote user
|
||||
.andWhere(new Brackets(qb => this
|
||||
.orNotMutingUser(qb, ':meId', 'note.renoteUserId', exclude)
|
||||
.orNotMutingUser(qb, ':meId', 'note.renoteUserId')
|
||||
.orWhere('note.renoteUserId = note.userId')
|
||||
.orWhere('note.renoteUserId IS NULL')))
|
||||
// TODO exclude should also pass a host to skip these instances
|
||||
// mute instances
|
||||
.andWhere(new Brackets(qb => this
|
||||
.andNotMutingInstance(qb, ':meId', 'note.userHost')
|
||||
.orWhere('note.userHost IS NULL')))
|
||||
.andWhere(new Brackets(qb => this
|
||||
.orNotMutingInstance(qb, ':meId', 'note.replyUserHost')
|
||||
.orWhere('note.replyUserHost IS NULL')))
|
||||
.andWhere(new Brackets(qb => this
|
||||
.orNotMutingInstance(qb, ':meId', 'note.renoteUserHost')
|
||||
.orWhere('note.renoteUserHost IS NULL')))
|
||||
// muted reply host
|
||||
.andWhere(new Brackets(qb => {
|
||||
qb.orWhere('note.replyUserHost IS NULL');
|
||||
qb.orWhere('note.replyUserHost = note.userHost');
|
||||
this.orFollowingUser(qb, ':meId', 'note.replyUserId');
|
||||
this.orNotMutingInstance(qb, ':meId', 'note.replyUserHost');
|
||||
}))
|
||||
// muted renote host
|
||||
.andWhere(new Brackets(qb => {
|
||||
qb.orWhere('note.renoteUserHost IS NULL');
|
||||
qb.orWhere('note.renoteUserHost = note.userHost');
|
||||
this.orFollowingUser(qb, ':meId', 'note.renoteUserId');
|
||||
this.orNotMutingInstance(qb, ':meId', 'note.renoteUserHost');
|
||||
}))
|
||||
.setParameters({ meId: me.id });
|
||||
}
|
||||
|
||||
|
|
@ -238,10 +253,10 @@ export class QueryService {
|
|||
|
||||
@bindThis
|
||||
public generateSilencedUserQueryForNotes<E extends ObjectLiteral>(q: SelectQueryBuilder<E>, me?: { id: MiUser['id'] } | null, excludeAuthor = false): SelectQueryBuilder<E> {
|
||||
const checkFor = (key: 'user' | 'replyUser' | 'renoteUser') => {
|
||||
const checkFor = (key: 'user' | 'replyUser' | 'renoteUser', userKey: 'note.user' | 'reply.user' | 'renote.user') => {
|
||||
// These are de-duplicated, since most call sites already provide some of them.
|
||||
this.leftJoin(q, `note.${key}Instance`, `${key}Instance`); // note->instance
|
||||
this.leftJoin(q, `note.${key}`, key); // note->user
|
||||
this.leftJoin(q, userKey, key); // note->user
|
||||
|
||||
q.andWhere(new Brackets(qb => {
|
||||
// case 1: user does not exist (note is not reply/renote)
|
||||
|
|
@ -270,10 +285,10 @@ export class QueryService {
|
|||
}
|
||||
|
||||
if (!excludeAuthor) {
|
||||
checkFor('user');
|
||||
checkFor('user', 'note.user');
|
||||
}
|
||||
checkFor('replyUser');
|
||||
checkFor('renoteUser');
|
||||
checkFor('replyUser', 'reply.user');
|
||||
checkFor('renoteUser', 'renote.user');
|
||||
|
||||
return q;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ import { isQuote, isRenote } from '@/misc/is-renote.js';
|
|||
import { ReactionsBufferingService } from '@/core/ReactionsBufferingService.js';
|
||||
import { PER_NOTE_REACTION_USER_PAIR_CACHE_MAX } from '@/const.js';
|
||||
import { CacheService } from '@/core/CacheService.js';
|
||||
import { NoteVisibilityService } from '@/core/NoteVisibilityService.js';
|
||||
import type { DataSource } from 'typeorm';
|
||||
|
||||
const FALLBACK = '\u2764';
|
||||
|
|
@ -108,6 +109,7 @@ export class ReactionService {
|
|||
private notificationService: NotificationService,
|
||||
private perUserReactionsChart: PerUserReactionsChart,
|
||||
private readonly cacheService: CacheService,
|
||||
private readonly noteVisibilityService: NoteVisibilityService,
|
||||
) {
|
||||
}
|
||||
|
||||
|
|
@ -122,7 +124,8 @@ export class ReactionService {
|
|||
}
|
||||
|
||||
// check visibility
|
||||
if (!await this.noteEntityService.isVisibleForMe(note, user.id, { me: user })) {
|
||||
const { accessible } = await this.noteVisibilityService.checkNoteVisibilityAsync(note, user);
|
||||
if (!accessible) {
|
||||
throw new IdentifiableError('68e9d2d1-48bf-42c2-b90a-b20e09fd3d48', 'Note not accessible for you.');
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -401,6 +401,7 @@ export class WebhookTestService {
|
|||
text: note.text,
|
||||
cw: note.cw,
|
||||
userId: note.userId,
|
||||
userHost: note.userHost ?? null,
|
||||
user: await this.toPackedUserLite(note.user ?? generateDummyUser()),
|
||||
replyId: note.replyId,
|
||||
renoteId: note.renoteId,
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ import FederationChart from '@/core/chart/charts/federation.js';
|
|||
import { FetchInstanceMetadataService } from '@/core/FetchInstanceMetadataService.js';
|
||||
import { UpdateInstanceQueue } from '@/core/UpdateInstanceQueue.js';
|
||||
import { CacheService } from '@/core/CacheService.js';
|
||||
import { NoteVisibilityService } from '@/core/NoteVisibilityService.js';
|
||||
import { getApHrefNullable, getApId, getApIds, getApType, getNullableApId, isAccept, isActor, isAdd, isAnnounce, isApObject, isBlock, isCollectionOrOrderedCollection, isCreate, isDelete, isFlag, isFollow, isLike, isDislike, isMove, isPost, isReject, isRemove, isTombstone, isUndo, isUpdate, validActor, validPost, isActivity, IObjectWithId } from './type.js';
|
||||
import { ApNoteService } from './models/ApNoteService.js';
|
||||
import { ApLoggerService } from './ApLoggerService.js';
|
||||
|
|
@ -100,6 +101,7 @@ export class ApInboxService {
|
|||
private readonly federationChart: FederationChart,
|
||||
private readonly updateInstanceQueue: UpdateInstanceQueue,
|
||||
private readonly cacheService: CacheService,
|
||||
private readonly noteVisibilityService: NoteVisibilityService,
|
||||
) {
|
||||
this.logger = this.apLoggerService.logger;
|
||||
}
|
||||
|
|
@ -367,7 +369,8 @@ export class ApInboxService {
|
|||
const renote = await this.apNoteService.resolveNote(target, { resolver, sentFrom: getApId(target) });
|
||||
if (renote == null) return 'announce target is null';
|
||||
|
||||
if (!await this.noteEntityService.isVisibleForMe(renote, actor.id, { me: actor })) {
|
||||
const { accessible } = await this.noteVisibilityService.checkNoteVisibilityAsync(renote, actor);
|
||||
if (!accessible) {
|
||||
return 'skip: invalid actor for this activity';
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,8 +17,9 @@ import { DebounceLoader } from '@/misc/loader.js';
|
|||
import { IdService } from '@/core/IdService.js';
|
||||
import { ReactionsBufferingService } from '@/core/ReactionsBufferingService.js';
|
||||
import { QueryService } from '@/core/QueryService.js';
|
||||
import { isPackedPureRenote } from '@/misc/is-renote.js';
|
||||
import type { Config } from '@/config.js';
|
||||
import { NoteVisibilityService } from '@/core/NoteVisibilityService.js';
|
||||
import type { NoteVisibilityData } from '@/core/NoteVisibilityService.js';
|
||||
import type { OnModuleInit } from '@nestjs/common';
|
||||
import type { CacheService } from '../CacheService.js';
|
||||
import type { CustomEmojiService } from '../CustomEmojiService.js';
|
||||
|
|
@ -101,6 +102,9 @@ export class NoteEntityService implements OnModuleInit {
|
|||
@Inject(DI.noteFavoritesRepository)
|
||||
private noteFavoritesRepository: NoteFavoritesRepository,
|
||||
|
||||
// This is public to avoid weaving a whole new service through the Channel class hierarchy.
|
||||
public readonly noteVisibilityService: NoteVisibilityService,
|
||||
|
||||
private readonly queryService: QueryService,
|
||||
//private userEntityService: UserEntityService,
|
||||
//private driveFileEntityService: DriveFileEntityService,
|
||||
|
|
@ -121,6 +125,8 @@ export class NoteEntityService implements OnModuleInit {
|
|||
this.idService = this.moduleRef.get('IdService');
|
||||
}
|
||||
|
||||
// Implementation moved to NoteVisibilityService
|
||||
/*
|
||||
@bindThis
|
||||
private treatVisibility(packedNote: Packed<'Note'>): Packed<'Note'>['visibility'] {
|
||||
if (packedNote.visibility === 'public' || packedNote.visibility === 'home') {
|
||||
|
|
@ -136,24 +142,31 @@ export class NoteEntityService implements OnModuleInit {
|
|||
}
|
||||
return packedNote.visibility;
|
||||
}
|
||||
*/
|
||||
|
||||
@bindThis
|
||||
public async hideNotes(notes: Packed<'Note'>[], meId: string | null): Promise<void> {
|
||||
const myFollowing = meId ? new Map(await this.cacheService.userFollowingsCache.fetch(meId)) : new Map<string, Omit<MiFollowing, 'isFollowerHibernated'>>();
|
||||
const myBlockers = meId ? new Set(await this.cacheService.userBlockedCache.fetch(meId)) : new Set<string>();
|
||||
public async hideNotes(notes: Packed<'Note'>[], meId: string | null, hint?: Partial<NoteVisibilityData>): Promise<void> {
|
||||
const me = meId ? await this.cacheService.findUserById(meId) : null;
|
||||
const data = await this.noteVisibilityService.populateData(me, hint);
|
||||
|
||||
// This shouldn't actually await, but we have to wrap it anyway because hideNote() is async
|
||||
await Promise.all(notes.map(note => this.hideNote(note, meId, {
|
||||
myFollowing,
|
||||
myBlockers,
|
||||
})));
|
||||
for (const note of notes) {
|
||||
this.hideNote(note, me, data);
|
||||
}
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async hideNote(packedNote: Packed<'Note'>, meId: MiUser['id'] | null, hint?: {
|
||||
myFollowing?: ReadonlyMap<string, Omit<MiFollowing, 'isFollowerHibernated'>> | ReadonlySet<string>,
|
||||
myBlockers?: ReadonlySet<string>,
|
||||
}): Promise<void> {
|
||||
public async hideNoteAsync(packedNote: Packed<'Note'>, me: string | Pick<MiUser, 'id' | 'host'> | null, hint?: Partial<NoteVisibilityData>): Promise<void> {
|
||||
const { redact } = await this.noteVisibilityService.checkNoteVisibilityAsync(packedNote, me, { hint });
|
||||
|
||||
if (redact) {
|
||||
this.redactNoteContents(packedNote);
|
||||
}
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public hideNote(packedNote: Packed<'Note'>, me: Pick<MiUser, 'id' | 'host'> | null, data: NoteVisibilityData): void {
|
||||
// Implementation moved to NoteVisibilityService
|
||||
/*
|
||||
if (meId === packedNote.userId) return;
|
||||
|
||||
// TODO: isVisibleForMe を使うようにしても良さそう(型違うけど)
|
||||
|
|
@ -232,8 +245,17 @@ export class NoteEntityService implements OnModuleInit {
|
|||
|
||||
if (isBlocked) hide = true;
|
||||
}
|
||||
*/
|
||||
|
||||
const hide = this.noteVisibilityService.checkNoteVisibility(packedNote, me, { data }).redact;
|
||||
|
||||
if (hide) {
|
||||
this.redactNoteContents(packedNote);
|
||||
}
|
||||
}
|
||||
|
||||
private redactNoteContents(packedNote: Packed<'Note'>) {
|
||||
{
|
||||
packedNote.visibleUserIds = undefined;
|
||||
packedNote.fileIds = [];
|
||||
packedNote.files = [];
|
||||
|
|
@ -477,6 +499,8 @@ export class NoteEntityService implements OnModuleInit {
|
|||
return undefined;
|
||||
}
|
||||
|
||||
// Implementation moved to NoteVisibilityService
|
||||
/*
|
||||
@bindThis
|
||||
public async isVisibleForMe(note: MiNote, meId: MiUser['id'] | null, hint?: {
|
||||
myFollowing?: ReadonlySet<string>,
|
||||
|
|
@ -493,7 +517,7 @@ export class NoteEntityService implements OnModuleInit {
|
|||
}
|
||||
|
||||
@bindThis
|
||||
public isVisibleForMeSync(note: MiNote, me: Pick<MiUser, 'id' | 'host'> | null, myFollowings: ReadonlySet<string> | null, myBlockers: ReadonlySet<string> | null): boolean {
|
||||
public isVisibleForMeSync(note: MiNote | Packed<'Note'>, me: Pick<MiUser, 'id' | 'host'> | null, myFollowings: ReadonlySet<string> | null, myBlockers: ReadonlySet<string> | null): boolean {
|
||||
// We can always view our own notes
|
||||
if (me?.id === note.userId) {
|
||||
return true;
|
||||
|
|
@ -509,6 +533,8 @@ export class NoteEntityService implements OnModuleInit {
|
|||
if (note.visibility === 'specified') {
|
||||
if (me == null) {
|
||||
return false;
|
||||
} else if (!note.visibleUserIds) {
|
||||
return false;
|
||||
} else {
|
||||
// 指定されているかどうか
|
||||
return note.visibleUserIds.includes(me.id);
|
||||
|
|
@ -522,9 +548,13 @@ export class NoteEntityService implements OnModuleInit {
|
|||
} else if (note.reply && (me.id === note.reply.userId)) {
|
||||
// 自分の投稿に対するリプライ
|
||||
return true;
|
||||
} else if (!note.mentions) {
|
||||
return false;
|
||||
} else if (note.mentions.includes(me.id)) {
|
||||
// 自分へのメンション
|
||||
return true;
|
||||
} else if (!note.visibleUserIds) {
|
||||
return false;
|
||||
} else if (note.visibleUserIds.includes(me.id)) {
|
||||
// Explicitly visible to me
|
||||
return true;
|
||||
|
|
@ -533,19 +563,19 @@ export class NoteEntityService implements OnModuleInit {
|
|||
const following = myFollowings?.has(note.userId);
|
||||
const userHost = me.host;
|
||||
|
||||
/* If we know the following, everyhting is fine.
|
||||
|
||||
But if we do not know the following, it might be that both the
|
||||
author of the note and the author of the like are remote users,
|
||||
in which case we can never know the following. Instead we have
|
||||
to assume that the users are following each other.
|
||||
*/
|
||||
// If we know the following, everyhting is fine.
|
||||
//
|
||||
// But if we do not know the following, it might be that both the
|
||||
// author of the note and the author of the like are remote users,
|
||||
// in which case we can never know the following. Instead we have
|
||||
// to assume that the users are following each other.
|
||||
return following || (note.userHost != null && userHost != null);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
*/
|
||||
|
||||
@bindThis
|
||||
public async packAttachedFiles(fileIds: MiNote['fileIds'], packedFiles: Map<MiNote['fileIds'][number], Packed<'DriveFile'> | null>): Promise<Packed<'DriveFile'>[]> {
|
||||
|
|
@ -649,6 +679,7 @@ export class NoteEntityService implements OnModuleInit {
|
|||
createdAt: this.idService.parse(note.id).date.toISOString(),
|
||||
updatedAt: note.updatedAt ? note.updatedAt.toISOString() : undefined,
|
||||
userId: note.userId,
|
||||
userHost: note.userHost,
|
||||
user: packedUsers?.get(note.userId) ?? this.userEntityService.pack(note.user ?? note.userId, me),
|
||||
text: text,
|
||||
cw: note.cw,
|
||||
|
|
@ -719,12 +750,14 @@ export class NoteEntityService implements OnModuleInit {
|
|||
} : {}),
|
||||
});
|
||||
|
||||
this.treatVisibility(packed);
|
||||
this.noteVisibilityService.syncVisibility(packed);
|
||||
|
||||
if (!opts.skipHide) {
|
||||
await this.hideNote(packed, meId, meId == null ? undefined : {
|
||||
myFollowing: opts._hint_?.userFollowings.get(meId),
|
||||
myBlockers: opts._hint_?.userBlockers.get(meId),
|
||||
await this.hideNoteAsync(packed, meId, {
|
||||
userFollowings: meId ? opts._hint_?.userFollowings.get(meId) : null,
|
||||
userBlockers: meId ? opts._hint_?.userBlockers.get(meId) : null,
|
||||
userMutedNotes: opts._hint_?.mutedNotes,
|
||||
userMutedThreads: opts._hint_?.mutedThreads,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -50,6 +50,10 @@ export const packedNoteSchema = {
|
|||
optional: false, nullable: false,
|
||||
format: 'id',
|
||||
},
|
||||
userHost: {
|
||||
type: 'string',
|
||||
optional: false, nullable: true,
|
||||
},
|
||||
user: {
|
||||
type: 'object',
|
||||
ref: 'UserLite',
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
.leftJoin('(select "host" from "instance" where "isBubbled" = true)', 'bubbleInstance', '"bubbleInstance"."host" = "note"."userHost"')
|
||||
.andWhere('"bubbleInstance" IS NOT NULL');
|
||||
this.queryService
|
||||
.leftJoin(query, 'note.userInstance', 'userInstance', '"userInstance"."host" = "bubbleInstance"."host"');
|
||||
.leftJoin(query, 'note.userInstance', 'userInstance');
|
||||
|
||||
this.queryService.generateBlockedHostQueryForNote(query);
|
||||
this.queryService.generateSilencedUserQueryForNotes(query, me);
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import { NoteCreateService } from '@/core/NoteCreateService.js';
|
|||
import { DI } from '@/di-symbols.js';
|
||||
import { isQuote, isRenote } from '@/misc/is-renote.js';
|
||||
import { IdentifiableError } from '@/misc/identifiable-error.js';
|
||||
import { NoteVisibilityService } from '@/core/NoteVisibilityService.js';
|
||||
import { ApiError } from '../../error.js';
|
||||
|
||||
export const meta = {
|
||||
|
|
@ -261,6 +262,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
|
||||
private noteEntityService: NoteEntityService,
|
||||
private noteCreateService: NoteCreateService,
|
||||
private readonly noteVisibilityService: NoteVisibilityService,
|
||||
) {
|
||||
super(meta, paramDef, async (ps, me) => {
|
||||
if (ps.text && ps.text.length > this.config.maxNoteLength) {
|
||||
|
|
@ -303,7 +305,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
throw new ApiError(meta.errors.noSuchRenoteTarget);
|
||||
} else if (isRenote(renote) && !isQuote(renote)) {
|
||||
throw new ApiError(meta.errors.cannotReRenote);
|
||||
} else if (!await this.noteEntityService.isVisibleForMe(renote, me.id)) {
|
||||
} else if (!(await this.noteVisibilityService.checkNoteVisibilityAsync(renote, me)).accessible) {
|
||||
throw new ApiError(meta.errors.cannotRenoteDueToVisibility);
|
||||
}
|
||||
|
||||
|
|
@ -351,7 +353,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
throw new ApiError(meta.errors.noSuchReplyTarget);
|
||||
} else if (isRenote(reply) && !isQuote(reply)) {
|
||||
throw new ApiError(meta.errors.cannotReplyToPureRenote);
|
||||
} else if (!await this.noteEntityService.isVisibleForMe(reply, me.id, { me })) {
|
||||
} else if (!(await this.noteVisibilityService.checkNoteVisibilityAsync(reply, me)).accessible) {
|
||||
throw new ApiError(meta.errors.cannotReplyToInvisibleNote);
|
||||
} else if (reply.visibility === 'specified' && ps.visibility !== 'specified') {
|
||||
throw new ApiError(meta.errors.cannotReplyToSpecifiedVisibilityNoteWithExtendedVisibility);
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import { NoteEditService } from '@/core/NoteEditService.js';
|
|||
import { DI } from '@/di-symbols.js';
|
||||
import { isQuote, isRenote } from '@/misc/is-renote.js';
|
||||
import { IdentifiableError } from '@/misc/identifiable-error.js';
|
||||
import { NoteVisibilityService } from '@/core/NoteVisibilityService.js';
|
||||
import { ApiError } from '../../error.js';
|
||||
|
||||
export const meta = {
|
||||
|
|
@ -311,6 +312,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
|
||||
private noteEntityService: NoteEntityService,
|
||||
private noteEditService: NoteEditService,
|
||||
private readonly noteVisibilityService: NoteVisibilityService,
|
||||
) {
|
||||
super(meta, paramDef, async (ps, me) => {
|
||||
if (ps.text && ps.text.length > this.config.maxNoteLength) {
|
||||
|
|
@ -408,7 +410,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
throw new ApiError(meta.errors.noSuchReplyTarget);
|
||||
} else if (isRenote(reply) && !isQuote(reply)) {
|
||||
throw new ApiError(meta.errors.cannotReplyToPureRenote);
|
||||
} else if (!await this.noteEntityService.isVisibleForMe(reply, me.id, { me })) {
|
||||
} else if (!(await this.noteVisibilityService.checkNoteVisibilityAsync(reply, me)).accessible) {
|
||||
throw new ApiError(meta.errors.cannotReplyToInvisibleNote);
|
||||
} else if (reply.visibility === 'specified' && ps.visibility !== 'specified') {
|
||||
throw new ApiError(meta.errors.cannotReplyToSpecifiedVisibilityNoteWithExtendedVisibility);
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import { DI } from '@/di-symbols.js';
|
|||
import { CacheService } from '@/core/CacheService.js';
|
||||
import { hasText } from '@/models/Note.js';
|
||||
import { ApiLoggerService } from '@/server/api/ApiLoggerService.js';
|
||||
import { NoteVisibilityService } from '@/core/NoteVisibilityService.js';
|
||||
import { ApiError } from '../../error.js';
|
||||
|
||||
export const meta = {
|
||||
|
|
@ -84,6 +85,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
private roleService: RoleService,
|
||||
private readonly cacheService: CacheService,
|
||||
private readonly loggerService: ApiLoggerService,
|
||||
private readonly noteVisibilityService: NoteVisibilityService,
|
||||
) {
|
||||
super(meta, paramDef, async (ps, me) => {
|
||||
const note = await this.getterService.getNote(ps.noteId).catch(err => {
|
||||
|
|
@ -91,7 +93,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
throw err;
|
||||
});
|
||||
|
||||
if (!(await this.noteEntityService.isVisibleForMe(note, me?.id ?? null, { me }))) {
|
||||
const { accessible } = await this.noteVisibilityService.checkNoteVisibilityAsync(note, me);
|
||||
if (!accessible) {
|
||||
throw new ApiError(meta.errors.cannotTranslateInvisibleNote);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -220,7 +220,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
this.queryService.generateSuspendedUserQueryForNote(query, true);
|
||||
this.queryService.generateSilencedUserQueryForNotes(query, me, true);
|
||||
if (me) {
|
||||
this.queryService.generateMutedUserQueryForNotes(query, me, { id: ps.userId });
|
||||
this.queryService.generateMutedUserQueryForNotes(query, me, true);
|
||||
this.queryService.generateBlockedUserQueryForNotes(query, me);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import type { JsonObject, JsonValue } from '@/misc/json-value.js';
|
|||
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
|
||||
import { deepClone } from '@/misc/clone.js';
|
||||
import type Connection from '@/server/api/stream/Connection.js';
|
||||
import { NoteVisibilityFilters } from '@/core/NoteVisibilityService.js';
|
||||
|
||||
/**
|
||||
* Stream channel
|
||||
|
|
@ -26,6 +27,10 @@ export default abstract class Channel {
|
|||
public static readonly requireCredential: boolean;
|
||||
public static readonly kind?: string | null;
|
||||
|
||||
protected get noteVisibilityService() {
|
||||
return this.noteEntityService.noteVisibilityService;
|
||||
}
|
||||
|
||||
protected get user() {
|
||||
return this.connection.user;
|
||||
}
|
||||
|
|
@ -105,8 +110,14 @@ export default abstract class Channel {
|
|||
return this.connection.myRecentFavorites;
|
||||
}
|
||||
|
||||
protected async checkNoteVisibility(note: Packed<'Note'>, filters?: NoteVisibilityFilters) {
|
||||
// Don't use any of the local cached data, because this does everything through CacheService which is just as fast with updated data.
|
||||
return await this.noteVisibilityService.checkNoteVisibilityAsync(note, this.user, { filters });
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a note is visible to the current user *excluding* blocks and mutes.
|
||||
* @deprecated use isNoteHidden instead
|
||||
*/
|
||||
protected isNoteVisibleToMe(note: Packed<'Note'>): boolean {
|
||||
if (note.visibility === 'public') return true;
|
||||
|
|
@ -120,8 +131,9 @@ export default abstract class Channel {
|
|||
return note.visibleUserIds.includes(this.user.id);
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* ミュートとブロックされてるを処理する
|
||||
* @deprecated use isNoteHidden instead
|
||||
*/
|
||||
protected isNoteMutedOrBlocked(note: Packed<'Note'>): boolean {
|
||||
// Ignore notes that require sign-in
|
||||
|
|
@ -196,12 +208,11 @@ export default abstract class Channel {
|
|||
// If we didn't clone the notes here, different connections would asynchronously write
|
||||
// different values to the same object, resulting in a random value being sent to each frontend. -- Dakkar
|
||||
const clonedNote = deepClone(note);
|
||||
const notes = crawl(clonedNote);
|
||||
|
||||
// Hide notes before everything else, since this modifies fields that the other functions will check.
|
||||
await this.noteEntityService.hideNotes(notes, this.user.id);
|
||||
const notes = crawl(clonedNote);
|
||||
|
||||
const [myReactions, myRenotes, myFavorites, myThreadMutings, myNoteMutings] = await Promise.all([
|
||||
const [myReactions, myRenotes, myFavorites, myThreadMutings, myNoteMutings, myFollowings] = await Promise.all([
|
||||
this.noteEntityService.populateMyReactions(notes, this.user.id, {
|
||||
myReactions: this.myRecentReactions,
|
||||
}),
|
||||
|
|
@ -213,13 +224,25 @@ export default abstract class Channel {
|
|||
}),
|
||||
this.noteEntityService.populateMyTheadMutings(notes, this.user.id),
|
||||
this.noteEntityService.populateMyNoteMutings(notes, this.user.id),
|
||||
this.cacheService.userFollowingsCache.fetch(this.user.id),
|
||||
]);
|
||||
|
||||
note.myReaction = myReactions.get(note.id) ?? null;
|
||||
note.isRenoted = myRenotes.has(note.id);
|
||||
note.isFavorited = myFavorites.has(note.id);
|
||||
note.isMutingThread = myThreadMutings.has(note.id);
|
||||
note.isMutingNote = myNoteMutings.has(note.id);
|
||||
for (const n of notes) {
|
||||
// Sync visibility in case there's something like "makeNotesFollowersOnlyBefore" enabled
|
||||
this.noteVisibilityService.syncVisibility(n);
|
||||
|
||||
n.myReaction = myReactions.get(n.id) ?? null;
|
||||
n.isRenoted = myRenotes.has(n.id);
|
||||
n.isFavorited = myFavorites.has(n.id);
|
||||
n.isMutingThread = myThreadMutings.has(n.id);
|
||||
n.isMutingNote = myNoteMutings.has(n.id);
|
||||
n.user.bypassSilence = n.userId === this.user.id || myFollowings.has(n.userId);
|
||||
}
|
||||
|
||||
// Hide notes *after* we sync visibility
|
||||
await this.noteEntityService.hideNotes(notes, this.user.id, {
|
||||
userFollowings: myFollowings,
|
||||
});
|
||||
|
||||
return clonedNote;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,7 +41,8 @@ class AntennaChannel extends Channel {
|
|||
if (data.type === 'note') {
|
||||
const note = await this.noteEntityService.pack(data.body.id, this.user, { detail: true });
|
||||
|
||||
if (this.isNoteMutedOrBlocked(note)) return;
|
||||
const { accessible, silence } = await this.checkNoteVisibility(note, { includeReplies: true });
|
||||
if (!accessible || silence) return;
|
||||
|
||||
this.send('note', note);
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -8,9 +8,9 @@ import type { Packed } from '@/misc/json-schema.js';
|
|||
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import { RoleService } from '@/core/RoleService.js';
|
||||
import { isRenotePacked, isQuotePacked } from '@/misc/is-renote.js';
|
||||
import type { JsonObject } from '@/misc/json-value.js';
|
||||
import { UtilityService } from '@/core/UtilityService.js';
|
||||
import { isPackedPureRenote } from '@/misc/is-renote.js';
|
||||
import Channel, { MiChannelService } from '../channel.js';
|
||||
|
||||
class BubbleTimelineChannel extends Channel {
|
||||
|
|
@ -47,8 +47,6 @@ class BubbleTimelineChannel extends Channel {
|
|||
|
||||
@bindThis
|
||||
private async onNote(note: Packed<'Note'>) {
|
||||
const isMe = this.user?.id === note.userId;
|
||||
|
||||
if (this.withFiles && (note.fileIds == null || note.fileIds.length === 0)) return;
|
||||
if (!this.withBots && note.user.isBot) return;
|
||||
|
||||
|
|
@ -56,27 +54,9 @@ class BubbleTimelineChannel extends Channel {
|
|||
if (note.channelId != null) return;
|
||||
if (!this.utilityService.isBubbledHost(note.user.host)) return;
|
||||
|
||||
if (this.isNoteMutedOrBlocked(note)) return;
|
||||
|
||||
if (note.reply) {
|
||||
const reply = note.reply;
|
||||
// 自分のフォローしていないユーザーの visibility: followers な投稿への返信は弾く
|
||||
if (!this.isNoteVisibleToMe(reply)) return;
|
||||
if (!this.following.get(note.userId)?.withReplies) {
|
||||
// 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合
|
||||
if (reply.userId !== this.user?.id && !isMe && reply.userId !== note.userId) return;
|
||||
}
|
||||
}
|
||||
|
||||
// 純粋なリノート(引用リノートでないリノート)の場合
|
||||
if (isRenotePacked(note) && !isQuotePacked(note) && note.renote) {
|
||||
if (!this.withRenotes) return;
|
||||
if (note.renote.reply) {
|
||||
const reply = note.renote.reply;
|
||||
// 自分のフォローしていないユーザーの visibility: followers な投稿への返信のリノートは弾く
|
||||
if (!this.isNoteVisibleToMe(reply)) return;
|
||||
}
|
||||
}
|
||||
const { accessible, silence } = await this.checkNoteVisibility(note);
|
||||
if (!accessible || silence) return;
|
||||
if (!this.withRenotes && isPackedPureRenote(note)) return;
|
||||
|
||||
const clonedNote = await this.rePackNote(note);
|
||||
this.send('note', clonedNote);
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import type { Packed } from '@/misc/json-schema.js';
|
|||
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import type { JsonObject } from '@/misc/json-value.js';
|
||||
import { isPackedPureRenote } from '@/misc/is-renote.js';
|
||||
import Channel, { type MiChannelService } from '../channel.js';
|
||||
|
||||
class ChannelChannel extends Channel {
|
||||
|
|
@ -45,9 +46,9 @@ class ChannelChannel extends Channel {
|
|||
|
||||
if (this.withFiles && (note.fileIds == null || note.fileIds.length === 0)) return;
|
||||
|
||||
if (note.renote && note.text == null && (note.fileIds == null || note.fileIds.length === 0) && !this.withRenotes) return;
|
||||
|
||||
if (this.isNoteMutedOrBlocked(note)) return;
|
||||
const { accessible, silence } = await this.checkNoteVisibility(note, { includeReplies: true });
|
||||
if (!accessible || silence) return;
|
||||
if (!this.withRenotes && isPackedPureRenote(note)) return;
|
||||
|
||||
const clonedNote = await this.rePackNote(note);
|
||||
this.send('note', clonedNote);
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import { MetaService } from '@/core/MetaService.js';
|
|||
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import { RoleService } from '@/core/RoleService.js';
|
||||
import { isRenotePacked, isQuotePacked } from '@/misc/is-renote.js';
|
||||
import { isPackedPureRenote } from '@/misc/is-renote.js';
|
||||
import type { JsonObject } from '@/misc/json-value.js';
|
||||
import Channel, { type MiChannelService } from '../channel.js';
|
||||
|
||||
|
|
@ -48,36 +48,15 @@ class GlobalTimelineChannel extends Channel {
|
|||
|
||||
@bindThis
|
||||
private async onNote(note: Packed<'Note'>) {
|
||||
const isMe = this.user?.id === note.userId;
|
||||
|
||||
if (this.withFiles && (note.fileIds == null || note.fileIds.length === 0)) return;
|
||||
if (!this.withBots && note.user.isBot) return;
|
||||
|
||||
if (note.visibility !== 'public') return;
|
||||
if (note.channelId != null) return;
|
||||
|
||||
if (this.isNoteMutedOrBlocked(note)) return;
|
||||
if (!this.isNoteVisibleToMe(note)) return;
|
||||
|
||||
if (note.reply) {
|
||||
const reply = note.reply;
|
||||
// 自分のフォローしていないユーザーの visibility: followers な投稿への返信は弾く
|
||||
if (!this.isNoteVisibleToMe(reply)) return;
|
||||
if (!this.following.get(note.userId)?.withReplies) {
|
||||
// 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合
|
||||
if (reply.userId !== this.user?.id && !isMe && reply.userId !== note.userId) return;
|
||||
}
|
||||
}
|
||||
|
||||
// 純粋なリノート(引用リノートでないリノート)の場合
|
||||
if (isRenotePacked(note) && !isQuotePacked(note) && note.renote) {
|
||||
if (!this.withRenotes) return;
|
||||
if (note.renote.reply) {
|
||||
const reply = note.renote.reply;
|
||||
// 自分のフォローしていないユーザーの visibility: followers な投稿への返信のリノートは弾く
|
||||
if (!this.isNoteVisibleToMe(reply)) return;
|
||||
}
|
||||
}
|
||||
const { accessible, silence } = await this.checkNoteVisibility(note);
|
||||
if (!accessible || silence) return;
|
||||
if (!this.withRenotes && isPackedPureRenote(note)) return;
|
||||
|
||||
const clonedNote = await this.rePackNote(note);
|
||||
this.send('note', clonedNote);
|
||||
|
|
|
|||
|
|
@ -43,7 +43,8 @@ class HashtagChannel extends Channel {
|
|||
const matched = this.q.some(tags => tags.every(tag => noteTags.includes(normalizeForSearch(tag))));
|
||||
if (!matched) return;
|
||||
|
||||
if (this.isNoteMutedOrBlocked(note)) return;
|
||||
const { accessible, silence } = await this.checkNoteVisibility(note, { includeReplies: true });
|
||||
if (!accessible || silence) return;
|
||||
|
||||
const clonedNote = await this.rePackNote(note);
|
||||
this.send('note', clonedNote);
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import { Injectable } from '@nestjs/common';
|
|||
import type { Packed } from '@/misc/json-schema.js';
|
||||
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import { isRenotePacked, isQuotePacked } from '@/misc/is-renote.js';
|
||||
import { isPackedPureRenote } from '@/misc/is-renote.js';
|
||||
import type { JsonObject } from '@/misc/json-value.js';
|
||||
import Channel, { type MiChannelService } from '../channel.js';
|
||||
|
||||
|
|
@ -50,28 +50,9 @@ class HomeTimelineChannel extends Channel {
|
|||
if (!isMe && !this.following.has(note.userId)) return;
|
||||
}
|
||||
|
||||
if (this.isNoteMutedOrBlocked(note)) return;
|
||||
if (!this.isNoteVisibleToMe(note)) return;
|
||||
|
||||
if (note.reply) {
|
||||
const reply = note.reply;
|
||||
// 自分のフォローしていないユーザーの visibility: followers な投稿への返信は弾く
|
||||
if (!this.isNoteVisibleToMe(reply)) return;
|
||||
if (!this.following.get(note.userId)?.withReplies) {
|
||||
// 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合
|
||||
if (reply.userId !== this.user!.id && !isMe && reply.userId !== note.userId) return;
|
||||
}
|
||||
}
|
||||
|
||||
// 純粋なリノート(引用リノートでないリノート)の場合
|
||||
if (isRenotePacked(note) && !isQuotePacked(note) && note.renote) {
|
||||
if (!this.withRenotes) return;
|
||||
if (note.renote.reply) {
|
||||
const reply = note.renote.reply;
|
||||
// 自分のフォローしていないユーザーの visibility: followers な投稿への返信のリノートは弾く
|
||||
if (!this.isNoteVisibleToMe(reply)) return;
|
||||
}
|
||||
}
|
||||
const { accessible, silence } = await this.checkNoteVisibility(note);
|
||||
if (!accessible || silence) return;
|
||||
if (!this.withRenotes && isPackedPureRenote(note)) return;
|
||||
|
||||
const clonedNote = await this.rePackNote(note);
|
||||
this.send('note', clonedNote);
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import { MetaService } from '@/core/MetaService.js';
|
|||
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import { RoleService } from '@/core/RoleService.js';
|
||||
import { isRenotePacked, isQuotePacked } from '@/misc/is-renote.js';
|
||||
import { isPackedPureRenote } from '@/misc/is-renote.js';
|
||||
import type { JsonObject } from '@/misc/json-value.js';
|
||||
import Channel, { type MiChannelService } from '../channel.js';
|
||||
|
||||
|
|
@ -67,28 +67,10 @@ class HybridTimelineChannel extends Channel {
|
|||
(note.channelId != null && this.followingChannels.has(note.channelId))
|
||||
)) return;
|
||||
|
||||
if (this.isNoteMutedOrBlocked(note)) return;
|
||||
if (!this.isNoteVisibleToMe(note)) return;
|
||||
|
||||
if (note.reply) {
|
||||
const reply = note.reply;
|
||||
// 自分のフォローしていないユーザーの visibility: followers な投稿への返信は弾く
|
||||
if (!this.isNoteVisibleToMe(reply)) return;
|
||||
if (!this.following.get(note.userId)?.withReplies && !this.withReplies) {
|
||||
// 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合
|
||||
if (reply.userId !== this.user!.id && !isMe && reply.userId !== note.userId) return;
|
||||
}
|
||||
}
|
||||
|
||||
// 純粋なリノート(引用リノートでないリノート)の場合
|
||||
if (isRenotePacked(note) && !isQuotePacked(note) && note.renote) {
|
||||
if (!this.withRenotes) return;
|
||||
if (note.renote.reply) {
|
||||
const reply = note.renote.reply;
|
||||
// 自分のフォローしていないユーザーの visibility: followers な投稿への返信のリノートは弾く
|
||||
if (!this.isNoteVisibleToMe(reply)) return;
|
||||
}
|
||||
}
|
||||
const { accessible, silence } = await this.checkNoteVisibility(note);
|
||||
if (!accessible || silence) return;
|
||||
if (!this.withRenotes && isPackedPureRenote(note)) return;
|
||||
if (!this.withReplies && note.replyId != null) return;
|
||||
|
||||
const clonedNote = await this.rePackNote(note);
|
||||
this.send('note', clonedNote);
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import { MetaService } from '@/core/MetaService.js';
|
|||
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import { RoleService } from '@/core/RoleService.js';
|
||||
import { isQuotePacked, isRenotePacked } from '@/misc/is-renote.js';
|
||||
import { isPackedPureRenote } from '@/misc/is-renote.js';
|
||||
import type { JsonObject } from '@/misc/json-value.js';
|
||||
import Channel, { type MiChannelService } from '../channel.js';
|
||||
|
||||
|
|
@ -50,8 +50,6 @@ class LocalTimelineChannel extends Channel {
|
|||
|
||||
@bindThis
|
||||
private async onNote(note: Packed<'Note'>) {
|
||||
const isMe = this.user?.id === note.userId;
|
||||
|
||||
if (this.withFiles && (note.fileIds == null || note.fileIds.length === 0)) return;
|
||||
if (!this.withBots && note.user.isBot) return;
|
||||
|
||||
|
|
@ -59,28 +57,10 @@ class LocalTimelineChannel extends Channel {
|
|||
if (note.visibility !== 'public') return;
|
||||
if (note.channelId != null) return;
|
||||
|
||||
if (this.isNoteMutedOrBlocked(note)) return;
|
||||
if (!this.isNoteVisibleToMe(note)) return;
|
||||
|
||||
// 関係ない返信は除外
|
||||
if (note.reply) {
|
||||
const reply = note.reply;
|
||||
// 自分のフォローしていないユーザーの visibility: followers な投稿への返信は弾く
|
||||
if (!this.isNoteVisibleToMe(reply)) return;
|
||||
if (!this.following.get(note.userId)?.withReplies) {
|
||||
// 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合
|
||||
if (reply.userId !== this.user?.id && !isMe && reply.userId !== note.userId) return;
|
||||
}
|
||||
}
|
||||
|
||||
if (isRenotePacked(note) && !isQuotePacked(note) && note.renote) {
|
||||
if (!this.withRenotes) return;
|
||||
if (note.renote.reply) {
|
||||
const reply = note.renote.reply;
|
||||
// 自分のフォローしていないユーザーの visibility: followers な投稿への返信のリノートは弾く
|
||||
if (!this.isNoteVisibleToMe(reply)) return;
|
||||
}
|
||||
}
|
||||
const { accessible, silence } = await this.checkNoteVisibility(note);
|
||||
if (!accessible || silence) return;
|
||||
if (!this.withRenotes && isPackedPureRenote(note)) return;
|
||||
if (!this.withReplies && note.replyId != null) return;
|
||||
|
||||
const clonedNote = await this.rePackNote(note);
|
||||
this.send('note', clonedNote);
|
||||
|
|
|
|||
|
|
@ -35,24 +35,19 @@ class MainChannel extends Channel {
|
|||
if (isUserFromMutedInstance(data.body, this.userMutedInstances)) return;
|
||||
if (data.body.userId && this.userIdsWhoMeMuting.has(data.body.userId)) return;
|
||||
|
||||
if (data.body.note && data.body.note.isHidden) {
|
||||
if (this.isNoteMutedOrBlocked(data.body.note)) return;
|
||||
if (!this.isNoteVisibleToMe(data.body.id)) return;
|
||||
const note = await this.noteEntityService.pack(data.body.note.id, this.user, {
|
||||
detail: true,
|
||||
});
|
||||
data.body.note = note;
|
||||
if (data.body.note) {
|
||||
const { accessible, silence } = await this.checkNoteVisibility(data.body.note, { includeReplies: true });
|
||||
if (!accessible || silence) return;
|
||||
|
||||
data.body.note = await this.rePackNote(data.body.note);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'mention': {
|
||||
if (this.isNoteMutedOrBlocked(data.body)) return;
|
||||
if (data.body.isHidden) {
|
||||
const note = await this.noteEntityService.pack(data.body.id, this.user, {
|
||||
detail: true,
|
||||
});
|
||||
data.body = note;
|
||||
}
|
||||
const { accessible, silence } = await this.checkNoteVisibility(data.body, { includeReplies: true });
|
||||
if (!accessible || silence) return;
|
||||
|
||||
data.body = await this.rePackNote(data.body);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import { bindThis } from '@/decorators.js';
|
|||
import { RoleService } from '@/core/RoleService.js';
|
||||
import type { GlobalEvents } from '@/core/GlobalEventService.js';
|
||||
import type { JsonObject } from '@/misc/json-value.js';
|
||||
import { isQuotePacked, isRenotePacked } from '@/misc/is-renote.js';
|
||||
import { isPackedPureRenote, isQuotePacked, isRenotePacked } from '@/misc/is-renote.js';
|
||||
import Channel, { type MiChannelService } from '../channel.js';
|
||||
|
||||
class RoleTimelineChannel extends Channel {
|
||||
|
|
@ -49,26 +49,8 @@ class RoleTimelineChannel extends Channel {
|
|||
}
|
||||
if (note.visibility !== 'public') return;
|
||||
|
||||
if (this.isNoteMutedOrBlocked(note)) return;
|
||||
|
||||
if (note.reply) {
|
||||
const reply = note.reply;
|
||||
// 自分のフォローしていないユーザーの visibility: followers な投稿への返信は弾く
|
||||
if (!this.isNoteVisibleToMe(reply)) return;
|
||||
if (!this.following.get(note.userId)?.withReplies) {
|
||||
// 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合
|
||||
if (reply.userId !== this.user?.id && !isMe && reply.userId !== note.userId) return;
|
||||
}
|
||||
}
|
||||
|
||||
// 純粋なリノート(引用リノートでないリノート)の場合
|
||||
if (isRenotePacked(note) && !isQuotePacked(note) && note.renote) {
|
||||
if (note.renote.reply) {
|
||||
const reply = note.renote.reply;
|
||||
// 自分のフォローしていないユーザーの visibility: followers な投稿への返信のリノートは弾く
|
||||
if (!this.isNoteVisibleToMe(reply)) return;
|
||||
}
|
||||
}
|
||||
const { accessible, silence } = await this.checkNoteVisibility(note);
|
||||
if (!accessible || silence) return;
|
||||
|
||||
const clonedNote = await this.rePackNote(note);
|
||||
this.send('note', clonedNote);
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import type { Packed } from '@/misc/json-schema.js';
|
|||
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import { isRenotePacked, isQuotePacked } from '@/misc/is-renote.js';
|
||||
import { isPackedPureRenote } from '@/misc/is-renote.js';
|
||||
import type { JsonObject } from '@/misc/json-value.js';
|
||||
import Channel, { type MiChannelService } from '../channel.js';
|
||||
|
||||
|
|
@ -82,8 +82,6 @@ class UserListChannel extends Channel {
|
|||
|
||||
@bindThis
|
||||
private async onNote(note: Packed<'Note'>) {
|
||||
const isMe = this.user?.id === note.userId;
|
||||
|
||||
// チャンネル投稿は無視する
|
||||
if (note.channelId) return;
|
||||
|
||||
|
|
@ -91,28 +89,9 @@ class UserListChannel extends Channel {
|
|||
|
||||
if (!Object.hasOwn(this.membershipsMap, note.userId)) return;
|
||||
|
||||
if (this.isNoteMutedOrBlocked(note)) return;
|
||||
if (!this.isNoteVisibleToMe(note)) return;
|
||||
|
||||
if (note.reply) {
|
||||
const reply = note.reply;
|
||||
// 自分のフォローしていないユーザーの visibility: followers な投稿への返信は弾く
|
||||
if (!this.isNoteVisibleToMe(reply)) return;
|
||||
if (!this.following.get(note.userId)?.withReplies) {
|
||||
// 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合
|
||||
if (reply.userId !== this.user!.id && !isMe && reply.userId !== note.userId) return;
|
||||
}
|
||||
}
|
||||
|
||||
// 純粋なリノート(引用リノートでないリノート)の場合
|
||||
if (isRenotePacked(note) && !isQuotePacked(note) && note.renote) {
|
||||
if (!this.withRenotes) return;
|
||||
if (note.renote.reply) {
|
||||
const reply = note.renote.reply;
|
||||
// 自分のフォローしていないユーザーの visibility: followers な投稿への返信のリノートは弾く
|
||||
if (!this.isNoteVisibleToMe(reply)) return;
|
||||
}
|
||||
}
|
||||
const { accessible, silence } = await this.checkNoteVisibility(note, { includeReplies: true });
|
||||
if (!accessible || silence) return;
|
||||
if (!this.withRenotes && isPackedPureRenote(note)) return;
|
||||
|
||||
const clonedNote = await this.rePackNote(note);
|
||||
this.send('note', clonedNote);
|
||||
|
|
|
|||
385
pnpm-lock.yaml
generated
385
pnpm-lock.yaml
generated
|
|
@ -1508,8 +1508,8 @@ packages:
|
|||
'@apidevtools/swagger-methods@3.0.2':
|
||||
resolution: {integrity: sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg==}
|
||||
|
||||
'@asamuzakjp/css-color@3.1.1':
|
||||
resolution: {integrity: sha512-hpRD68SV2OMcZCsrbdkccTw5FXjNDLo5OuqSHyHZfwweGsDWZwDJ2+gONyNAbazZclobMirACLw0lk8WVxIqxA==}
|
||||
'@asamuzakjp/css-color@3.2.0':
|
||||
resolution: {integrity: sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==}
|
||||
|
||||
'@aws-crypto/crc32@5.2.0':
|
||||
resolution: {integrity: sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==}
|
||||
|
|
@ -1965,28 +1965,28 @@ packages:
|
|||
resolution: {integrity: sha512-JqWH1vsgdGcw2RR6VliXXdA0/59LttzlU8UlRT/iUUsEeWfYq8I+K0yhihEUTTHLRm1EXvpsCx3083EU15ecsA==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
'@csstools/css-calc@2.1.2':
|
||||
resolution: {integrity: sha512-TklMyb3uBB28b5uQdxjReG4L80NxAqgrECqLZFQbyLekwwlcDDS8r3f07DKqeo8C4926Br0gf/ZDe17Zv4wIuw==}
|
||||
'@csstools/css-calc@2.1.4':
|
||||
resolution: {integrity: sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
'@csstools/css-parser-algorithms': ^3.0.4
|
||||
'@csstools/css-tokenizer': ^3.0.3
|
||||
'@csstools/css-parser-algorithms': ^3.0.5
|
||||
'@csstools/css-tokenizer': ^3.0.4
|
||||
|
||||
'@csstools/css-color-parser@3.0.8':
|
||||
resolution: {integrity: sha512-pdwotQjCCnRPuNi06jFuP68cykU1f3ZWExLe/8MQ1LOs8Xq+fTkYgd+2V8mWUWMrOn9iS2HftPVaMZDaXzGbhQ==}
|
||||
'@csstools/css-color-parser@3.0.10':
|
||||
resolution: {integrity: sha512-TiJ5Ajr6WRd1r8HSiwJvZBiJOqtH86aHpUjq5aEKWHiII2Qfjqd/HCWKPOW8EP4vcspXbHnXrwIDlu5savQipg==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
'@csstools/css-parser-algorithms': ^3.0.4
|
||||
'@csstools/css-tokenizer': ^3.0.3
|
||||
'@csstools/css-parser-algorithms': ^3.0.5
|
||||
'@csstools/css-tokenizer': ^3.0.4
|
||||
|
||||
'@csstools/css-parser-algorithms@3.0.4':
|
||||
resolution: {integrity: sha512-Up7rBoV77rv29d3uKHUIVubz1BTcgyUK72IvCQAbfbMv584xHcGKCKbWh7i8hPrRJ7qU4Y8IO3IY9m+iTB7P3A==}
|
||||
'@csstools/css-parser-algorithms@3.0.5':
|
||||
resolution: {integrity: sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
'@csstools/css-tokenizer': ^3.0.3
|
||||
'@csstools/css-tokenizer': ^3.0.4
|
||||
|
||||
'@csstools/css-tokenizer@3.0.3':
|
||||
resolution: {integrity: sha512-UJnjoFsmxfKUdNYdWgOB0mWUypuLvAfQPH1+pyvRJs6euowbFkFC6P13w1l8mJyi3vxYMxc9kld5jZEGRQs6bw==}
|
||||
'@csstools/css-tokenizer@3.0.4':
|
||||
resolution: {integrity: sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
'@cypress/request@3.0.8':
|
||||
|
|
@ -2168,6 +2168,12 @@ packages:
|
|||
peerDependencies:
|
||||
eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
|
||||
|
||||
'@eslint-community/eslint-utils@4.7.0':
|
||||
resolution: {integrity: sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
peerDependencies:
|
||||
eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
|
||||
|
||||
'@eslint-community/regexpp@4.12.1':
|
||||
resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==}
|
||||
engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
|
||||
|
|
@ -3099,6 +3105,7 @@ packages:
|
|||
|
||||
'@readme/json-schema-ref-parser@1.2.0':
|
||||
resolution: {integrity: sha512-Bt3QVovFSua4QmHa65EHUmh2xS0XJ3rgTEUPH998f4OW4VVJke3BuS16f+kM0ZLOGdvIrzrPRqwihuv5BAjtrA==}
|
||||
deprecated: This package is no longer maintained. Please use `@apidevtools/json-schema-ref-parser` instead.
|
||||
|
||||
'@readme/openapi-parser@2.7.0':
|
||||
resolution: {integrity: sha512-P8WSr8WTOxilnT89tcCRKWYsG/II4sAwt1a/DIWub8xTtkrG9cCBBy/IUcvc5X8oGWN82MwcTA3uEkDrXZd/7A==}
|
||||
|
|
@ -4221,8 +4228,8 @@ packages:
|
|||
'@types/pg@8.6.1':
|
||||
resolution: {integrity: sha512-1Kc4oAGzAl7uqUStZCDvaLFqZrW9qWSjXOmBfdgyBP5La7Us6Mg4GBvRlSoaZMhQF/zSj1C8CtKMBkoiT8eL8w==}
|
||||
|
||||
'@types/prop-types@15.7.14':
|
||||
resolution: {integrity: sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==}
|
||||
'@types/prop-types@15.7.15':
|
||||
resolution: {integrity: sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==}
|
||||
|
||||
'@types/proxy-addr@2.0.3':
|
||||
resolution: {integrity: sha512-TgAHHO4tNG3HgLTUhB+hM4iwW6JUNeQHCLnF1DjaDA9c69PN+IasoFu2MYDhubFc+ZIw5c5t9DMtjvrD6R3Egg==}
|
||||
|
|
@ -4266,8 +4273,8 @@ packages:
|
|||
'@types/sanitize-html@2.15.0':
|
||||
resolution: {integrity: sha512-71Z6PbYsVKfp4i6Jvr37s5ql6if1Q/iJQT80NbaSi7uGaG8CqBMXP0pk/EsURAOuGdk5IJCd/vnzKrR7S3Txsw==}
|
||||
|
||||
'@types/scheduler@0.23.0':
|
||||
resolution: {integrity: sha512-YIoDCTH3Af6XM5VuwGG/QL/CJqga1Zm3NkU3HZ4ZHK2fRMPYP1VczsTUqtsf43PH/iJNVlPHAo2oWX7BSdB2Hw==}
|
||||
'@types/scheduler@0.26.0':
|
||||
resolution: {integrity: sha512-WFHp9YUJQ6CKshqoC37iOlHnQSmxNc795UhB26CyBBttrN9svdIrUjl/NjnNmfcwtncN0h/0PPAFWv9ovP8mLA==}
|
||||
|
||||
'@types/seedrandom@3.0.8':
|
||||
resolution: {integrity: sha512-TY1eezMU2zH2ozQoAFAQFOPpvP15g+ZgSfTZt31AUUH/Rxtnz3H+A/Sv1Snw2/amp//omibc+AEkTaA8KUeOLQ==}
|
||||
|
|
@ -4365,10 +4372,26 @@ packages:
|
|||
eslint: ^8.57.0 || ^9.0.0
|
||||
typescript: '>=4.8.4 <5.9.0'
|
||||
|
||||
'@typescript-eslint/project-service@8.39.1':
|
||||
resolution: {integrity: sha512-8fZxek3ONTwBu9ptw5nCKqZOSkXshZB7uAxuFF0J/wTMkKydjXCzqqga7MlFMpHi9DoG4BadhmTkITBcg8Aybw==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
peerDependencies:
|
||||
typescript: '>=4.8.4 <6.0.0'
|
||||
|
||||
'@typescript-eslint/scope-manager@8.31.0':
|
||||
resolution: {integrity: sha512-knO8UyF78Nt8O/B64i7TlGXod69ko7z6vJD9uhSlm0qkAbGeRUSudcm0+K/4CrRjrpiHfBCjMWlc08Vav1xwcw==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
||||
'@typescript-eslint/scope-manager@8.39.1':
|
||||
resolution: {integrity: sha512-RkBKGBrjgskFGWuyUGz/EtD8AF/GW49S21J8dvMzpJitOF1slLEbbHnNEtAHtnDAnx8qDEdRrULRnWVx27wGBw==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
||||
'@typescript-eslint/tsconfig-utils@8.39.1':
|
||||
resolution: {integrity: sha512-ePUPGVtTMR8XMU2Hee8kD0Pu4NDE1CN9Q1sxGSGd/mbOtGZDM7pnhXNJnzW63zk/q+Z54zVzj44HtwXln5CvHA==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
peerDependencies:
|
||||
typescript: '>=4.8.4 <6.0.0'
|
||||
|
||||
'@typescript-eslint/type-utils@8.31.0':
|
||||
resolution: {integrity: sha512-DJ1N1GdjI7IS7uRlzJuEDCgDQix3ZVYVtgeWEyhyn4iaoitpMBX6Ndd488mXSx0xah/cONAkEaYyylDyAeHMHg==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
|
@ -4380,12 +4403,22 @@ packages:
|
|||
resolution: {integrity: sha512-Ch8oSjVyYyJxPQk8pMiP2FFGYatqXQfQIaMp+TpuuLlDachRWpUAeEu1u9B/v/8LToehUIWyiKcA/w5hUFRKuQ==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
||||
'@typescript-eslint/types@8.39.1':
|
||||
resolution: {integrity: sha512-7sPDKQQp+S11laqTrhHqeAbsCfMkwJMrV7oTDvtDds4mEofJYir414bYKUEb8YPUm9QL3U+8f6L6YExSoAGdQw==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
||||
'@typescript-eslint/typescript-estree@8.31.0':
|
||||
resolution: {integrity: sha512-xLmgn4Yl46xi6aDSZ9KkyfhhtnYI15/CvHbpOy/eR5NWhK/BK8wc709KKwhAR0m4ZKRP7h07bm4BWUYOCuRpQQ==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
peerDependencies:
|
||||
typescript: '>=4.8.4 <5.9.0'
|
||||
|
||||
'@typescript-eslint/typescript-estree@8.39.1':
|
||||
resolution: {integrity: sha512-EKkpcPuIux48dddVDXyQBlKdeTPMmALqBUbEk38McWv0qVEZwOpVJBi7ugK5qVNgeuYjGNQxrrnoM/5+TI/BPw==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
peerDependencies:
|
||||
typescript: '>=4.8.4 <6.0.0'
|
||||
|
||||
'@typescript-eslint/utils@8.31.0':
|
||||
resolution: {integrity: sha512-qi6uPLt9cjTFxAb1zGNgTob4x9ur7xC6mHQJ8GwEzGMGE9tYniublmJaowOJ9V2jUzxrltTPfdG2nKlWsq0+Ww==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
|
@ -4393,10 +4426,21 @@ packages:
|
|||
eslint: ^8.57.0 || ^9.0.0
|
||||
typescript: '>=4.8.4 <5.9.0'
|
||||
|
||||
'@typescript-eslint/utils@8.39.1':
|
||||
resolution: {integrity: sha512-VF5tZ2XnUSTuiqZFXCZfZs1cgkdd3O/sSYmdo2EpSyDlC86UM/8YytTmKnehOW3TGAlivqTDT6bS87B/GQ/jyg==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
peerDependencies:
|
||||
eslint: ^8.57.0 || ^9.0.0
|
||||
typescript: '>=4.8.4 <6.0.0'
|
||||
|
||||
'@typescript-eslint/visitor-keys@8.31.0':
|
||||
resolution: {integrity: sha512-QcGHmlRHWOl93o64ZUMNewCdwKGU6WItOU52H0djgNmn1EOrhVudrDzXz4OycCRSCPwFCDrE2iIt5vmuUdHxuQ==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
||||
'@typescript-eslint/visitor-keys@8.39.1':
|
||||
resolution: {integrity: sha512-W8FQi6kEh2e8zVhQ0eeRnxdvIoOkAp/CPAahcNio6nO9dsIwb9b34z90KOlheoyuVf6LSOEdjlkxSkapNEc+4A==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
||||
'@ungap/structured-clone@1.2.0':
|
||||
resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==}
|
||||
|
||||
|
|
@ -4649,6 +4693,11 @@ packages:
|
|||
engines: {node: '>=0.4.0'}
|
||||
hasBin: true
|
||||
|
||||
acorn@8.15.0:
|
||||
resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==}
|
||||
engines: {node: '>=0.4.0'}
|
||||
hasBin: true
|
||||
|
||||
agent-base@7.1.0:
|
||||
resolution: {integrity: sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==}
|
||||
engines: {node: '>= 14'}
|
||||
|
|
@ -5538,8 +5587,8 @@ packages:
|
|||
resolution: {integrity: sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==}
|
||||
engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'}
|
||||
|
||||
cssstyle@4.3.0:
|
||||
resolution: {integrity: sha512-6r0NiY0xizYqfBvWp1G7WXJ06/bZyrk7Dc6PHql82C/pKGUTKu4yAX4Y8JPamb1ob9nBKuxWzCGTRuGwU3yxJQ==}
|
||||
cssstyle@4.6.0:
|
||||
resolution: {integrity: sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
csstype@3.1.3:
|
||||
|
|
@ -5630,6 +5679,15 @@ packages:
|
|||
supports-color:
|
||||
optional: true
|
||||
|
||||
debug@4.4.1:
|
||||
resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==}
|
||||
engines: {node: '>=6.0'}
|
||||
peerDependencies:
|
||||
supports-color: '*'
|
||||
peerDependenciesMeta:
|
||||
supports-color:
|
||||
optional: true
|
||||
|
||||
decamelize-keys@1.1.1:
|
||||
resolution: {integrity: sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
|
@ -5638,8 +5696,8 @@ packages:
|
|||
resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
decimal.js@10.5.0:
|
||||
resolution: {integrity: sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==}
|
||||
decimal.js@10.6.0:
|
||||
resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==}
|
||||
|
||||
decode-bmp@0.2.1:
|
||||
resolution: {integrity: sha512-NiOaGe+GN0KJqi2STf24hfMkFitDUaIoUU3eKvP/wAbLe8o6FuW5n/x7MHPR0HKvBokp6MQY/j7w8lewEeVCIA==}
|
||||
|
|
@ -6056,6 +6114,10 @@ packages:
|
|||
resolution: {integrity: sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
||||
eslint-visitor-keys@4.2.1:
|
||||
resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
||||
eslint@9.25.1:
|
||||
resolution: {integrity: sha512-E6Mtz9oGQWDCpV12319d59n4tx9zOTXSTmc8BLVxBx+G/0RdM5MvEEJLU9c0+aleoePYYgVTOsRblx433qmhWQ==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
|
@ -6070,6 +6132,10 @@ packages:
|
|||
resolution: {integrity: sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
||||
espree@10.4.0:
|
||||
resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
||||
esprima@4.0.1:
|
||||
resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==}
|
||||
engines: {node: '>=4'}
|
||||
|
|
@ -6354,6 +6420,7 @@ packages:
|
|||
fluent-ffmpeg@2.1.3:
|
||||
resolution: {integrity: sha512-Be3narBNt2s6bsaqP6Jzq91heDgOEaDCJAXcE3qcma/EJBSy5FB4cvO31XBInuAuKBx8Kptf8dkhjK0IOru39Q==}
|
||||
engines: {node: '>=18'}
|
||||
deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.
|
||||
|
||||
follow-redirects@1.15.9:
|
||||
resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==}
|
||||
|
|
@ -6538,10 +6605,12 @@ packages:
|
|||
|
||||
glob@7.2.3:
|
||||
resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
|
||||
deprecated: Glob versions prior to v9 are no longer supported
|
||||
|
||||
glob@8.1.0:
|
||||
resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==}
|
||||
engines: {node: '>=12'}
|
||||
deprecated: Glob versions prior to v9 are no longer supported
|
||||
|
||||
global-dirs@3.0.1:
|
||||
resolution: {integrity: sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==}
|
||||
|
|
@ -6827,6 +6896,7 @@ packages:
|
|||
|
||||
inflight@1.0.6:
|
||||
resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==}
|
||||
deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.
|
||||
|
||||
inherits@2.0.4:
|
||||
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
|
||||
|
|
@ -7494,6 +7564,7 @@ packages:
|
|||
|
||||
lodash.get@4.4.2:
|
||||
resolution: {integrity: sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==}
|
||||
deprecated: This package is deprecated. Use the optional chaining (?.) operator instead.
|
||||
|
||||
lodash.isarguments@3.1.0:
|
||||
resolution: {integrity: sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==}
|
||||
|
|
@ -7975,6 +8046,7 @@ packages:
|
|||
multer@1.4.5-lts.2:
|
||||
resolution: {integrity: sha512-VzGiVigcG9zUAoCNU+xShztrlr1auZOlurXynNvO9GiWD1/mTBbUljOKY+qMeazBqXgRnjzeEgJI/wyjJUHg9A==}
|
||||
engines: {node: '>= 6.0.0'}
|
||||
deprecated: Multer 1.x is impacted by a number of vulnerabilities, which have been patched in 2.x. You should upgrade to the latest 2.x version.
|
||||
|
||||
mute-stream@2.0.0:
|
||||
resolution: {integrity: sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==}
|
||||
|
|
@ -8055,6 +8127,7 @@ packages:
|
|||
node-domexception@1.0.0:
|
||||
resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==}
|
||||
engines: {node: '>=10.5.0'}
|
||||
deprecated: Use your platform's native DOMException instead
|
||||
|
||||
node-fetch@2.7.0:
|
||||
resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==}
|
||||
|
|
@ -8156,8 +8229,8 @@ packages:
|
|||
nth-check@2.1.1:
|
||||
resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==}
|
||||
|
||||
nwsapi@2.2.19:
|
||||
resolution: {integrity: sha512-94bcyI3RsqiZufXjkr3ltkI86iEl+I7uiHVDtcq9wJUTwYQJ5odHDeSzkkrRzi80jJ8MaeZgqKjH1bAWAFw9bA==}
|
||||
nwsapi@2.2.21:
|
||||
resolution: {integrity: sha512-o6nIY3qwiSXl7/LuOU0Dmuctd34Yay0yeuZRLFmDPrrdHpXKFndPj3hM+YEPVHYC5fx2otBx4Ilc/gyYSAUaIA==}
|
||||
|
||||
oauth@0.10.2:
|
||||
resolution: {integrity: sha512-JtFnB+8nxDEXgNyniwz573xxbKSOu3R8D40xQKqcjwJ2CDkYqUDI53o6IuzDJBx60Z8VKCm271+t8iFjakrl8Q==}
|
||||
|
|
@ -8466,6 +8539,10 @@ packages:
|
|||
resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
picomatch@4.0.3:
|
||||
resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
pid-port@1.0.2:
|
||||
resolution: {integrity: sha512-Khqp07zX8IJpmIg56bHrLxS3M0iSL4cq6wnMq8YE7r/hSw3Kn4QxYS6QJg8Bs22Z7CSVj7eSsxFuigYVIFWmjg==}
|
||||
engines: {node: '>=18'}
|
||||
|
|
@ -9745,10 +9822,12 @@ packages:
|
|||
superagent@9.0.2:
|
||||
resolution: {integrity: sha512-xuW7dzkUpcJq7QnhOsnNUgtYp3xRwpt2F7abdRYIpCsAt0hhUqia0EdxyXZQQpNmGtsCzYHryaKSV3q3GJnq7w==}
|
||||
engines: {node: '>=14.18.0'}
|
||||
deprecated: Please upgrade to superagent v10.2.2+, see release notes at https://github.com/forwardemail/superagent/releases/tag/v10.2.2 - maintenance is supported by Forward Email @ https://forwardemail.net
|
||||
|
||||
supertest@7.1.0:
|
||||
resolution: {integrity: sha512-5QeSO8hSrKghtcWEoPiO036fxH0Ii2wVQfFZSP0oqQhmjk8bOLhDFXr4JrvaFmPuEWUoq4znY3uSi8UzLKxGqw==}
|
||||
engines: {node: '>=14.18.0'}
|
||||
deprecated: Please upgrade to supertest v7.1.3+, see release notes at https://github.com/forwardemail/supertest/releases/tag/v7.1.3 - maintenance is supported by Forward Email @ https://forwardemail.net
|
||||
|
||||
supports-color@5.5.0:
|
||||
resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==}
|
||||
|
|
@ -9880,10 +9959,17 @@ packages:
|
|||
tldts-core@6.1.63:
|
||||
resolution: {integrity: sha512-H1XCt54xY+QPbwhTgmxLkepX0MVHu3USfMmejiCOdkMbRcP22Pn2FVF127r/GWXVDmXTRezyF3Ckvhn4Fs6j7Q==}
|
||||
|
||||
tldts-core@6.1.86:
|
||||
resolution: {integrity: sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==}
|
||||
|
||||
tldts@6.1.63:
|
||||
resolution: {integrity: sha512-YWwhsjyn9sB/1rOkSRYxvkN/wl5LFM1QDv6F2pVR+pb/jFne4EOBxHfkKVWvDIBEAw9iGOwwubHtQTm0WRT5sQ==}
|
||||
hasBin: true
|
||||
|
||||
tldts@6.1.86:
|
||||
resolution: {integrity: sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==}
|
||||
hasBin: true
|
||||
|
||||
tmp@0.2.3:
|
||||
resolution: {integrity: sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==}
|
||||
engines: {node: '>=14.14'}
|
||||
|
|
@ -9936,8 +10022,8 @@ packages:
|
|||
tr46@0.0.3:
|
||||
resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
|
||||
|
||||
tr46@5.1.0:
|
||||
resolution: {integrity: sha512-IUWnUK7ADYR5Sl1fZlO1INDUhVhatWl7BtJWsIhwJ0UAK7ilzzIa8uIqOO/aYVWHZPJkKbEL+362wrzoeRF7bw==}
|
||||
tr46@5.1.1:
|
||||
resolution: {integrity: sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
tree-kill@1.2.2:
|
||||
|
|
@ -10456,6 +10542,9 @@ packages:
|
|||
vue-component-type-helpers@2.2.10:
|
||||
resolution: {integrity: sha512-iDUO7uQK+Sab2tYuiP9D1oLujCWlhHELHMgV/cB13cuGbG4qwkLHvtfWb6FzvxrIOPDnU0oHsz2MlQjhYDeaHA==}
|
||||
|
||||
vue-component-type-helpers@3.0.5:
|
||||
resolution: {integrity: sha512-uoNZaJ+a1/zppa/Vgmi8zIOP2PHXDN2rT8NyF+zQRK6ZG94lNB9prcV0GdLJbY9i9lrD47JOVIH92SaiA7oJ1A==}
|
||||
|
||||
vue-demi@0.14.7:
|
||||
resolution: {integrity: sha512-EOG8KXDQNwkJILkx/gPcoL/7vH+hORoBaKgGe+6W7VFMvCYJfmF2dGbvgDroVnI8LU7/kTu8mbjRZGBU1z9NTA==}
|
||||
engines: {node: '>=12'}
|
||||
|
|
@ -10654,6 +10743,18 @@ packages:
|
|||
utf-8-validate:
|
||||
optional: true
|
||||
|
||||
ws@8.18.3:
|
||||
resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
peerDependencies:
|
||||
bufferutil: ^4.0.1
|
||||
utf-8-validate: '>=5.0.2'
|
||||
peerDependenciesMeta:
|
||||
bufferutil:
|
||||
optional: true
|
||||
utf-8-validate:
|
||||
optional: true
|
||||
|
||||
xev@3.0.2:
|
||||
resolution: {integrity: sha512-8kxuH95iMXzHZj+fwqfA4UrPcYOy6bGIgfWzo9Ji23JoEc30ge/Z++Ubkiuy8c0+M64nXmmxrmJ7C8wnuBhluw==}
|
||||
|
||||
|
|
@ -10763,12 +10864,12 @@ snapshots:
|
|||
|
||||
'@apidevtools/swagger-methods@3.0.2': {}
|
||||
|
||||
'@asamuzakjp/css-color@3.1.1':
|
||||
'@asamuzakjp/css-color@3.2.0':
|
||||
dependencies:
|
||||
'@csstools/css-calc': 2.1.2(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3)
|
||||
'@csstools/css-color-parser': 3.0.8(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3)
|
||||
'@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3)
|
||||
'@csstools/css-tokenizer': 3.0.3
|
||||
'@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)
|
||||
'@csstools/css-color-parser': 3.0.10(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)
|
||||
'@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4)
|
||||
'@csstools/css-tokenizer': 3.0.4
|
||||
lru-cache: 10.4.3
|
||||
optional: true
|
||||
|
||||
|
|
@ -11268,7 +11369,7 @@ snapshots:
|
|||
'@babel/traverse': 7.24.7
|
||||
'@babel/types': 7.27.1
|
||||
convert-source-map: 2.0.0
|
||||
debug: 4.4.0(supports-color@8.1.1)
|
||||
debug: 4.4.0(supports-color@5.5.0)
|
||||
gensync: 1.0.0-beta.2
|
||||
json5: 2.2.3
|
||||
semver: 6.3.1
|
||||
|
|
@ -11288,7 +11389,7 @@ snapshots:
|
|||
'@babel/traverse': 7.24.7
|
||||
'@babel/types': 7.25.7
|
||||
convert-source-map: 2.0.0
|
||||
debug: 4.4.0(supports-color@8.1.1)
|
||||
debug: 4.4.0(supports-color@5.5.0)
|
||||
gensync: 1.0.0-beta.2
|
||||
json5: 2.2.3
|
||||
semver: 6.3.1
|
||||
|
|
@ -11521,7 +11622,7 @@ snapshots:
|
|||
'@babel/helper-split-export-declaration': 7.24.7
|
||||
'@babel/parser': 7.27.2
|
||||
'@babel/types': 7.25.7
|
||||
debug: 4.4.0(supports-color@8.1.1)
|
||||
debug: 4.4.0(supports-color@5.5.0)
|
||||
globals: 11.12.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
|
@ -11636,26 +11737,26 @@ snapshots:
|
|||
'@csstools/color-helpers@5.0.2':
|
||||
optional: true
|
||||
|
||||
'@csstools/css-calc@2.1.2(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3)':
|
||||
'@csstools/css-calc@2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)':
|
||||
dependencies:
|
||||
'@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3)
|
||||
'@csstools/css-tokenizer': 3.0.3
|
||||
'@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4)
|
||||
'@csstools/css-tokenizer': 3.0.4
|
||||
optional: true
|
||||
|
||||
'@csstools/css-color-parser@3.0.8(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3)':
|
||||
'@csstools/css-color-parser@3.0.10(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)':
|
||||
dependencies:
|
||||
'@csstools/color-helpers': 5.0.2
|
||||
'@csstools/css-calc': 2.1.2(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3)
|
||||
'@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3)
|
||||
'@csstools/css-tokenizer': 3.0.3
|
||||
'@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)
|
||||
'@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4)
|
||||
'@csstools/css-tokenizer': 3.0.4
|
||||
optional: true
|
||||
|
||||
'@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3)':
|
||||
'@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4)':
|
||||
dependencies:
|
||||
'@csstools/css-tokenizer': 3.0.3
|
||||
'@csstools/css-tokenizer': 3.0.4
|
||||
optional: true
|
||||
|
||||
'@csstools/css-tokenizer@3.0.3':
|
||||
'@csstools/css-tokenizer@3.0.4':
|
||||
optional: true
|
||||
|
||||
'@cypress/request@3.0.8':
|
||||
|
|
@ -11793,6 +11894,11 @@ snapshots:
|
|||
eslint: 9.25.1
|
||||
eslint-visitor-keys: 3.4.3
|
||||
|
||||
'@eslint-community/eslint-utils@4.7.0(eslint@9.25.1)':
|
||||
dependencies:
|
||||
eslint: 9.25.1
|
||||
eslint-visitor-keys: 3.4.3
|
||||
|
||||
'@eslint-community/regexpp@4.12.1': {}
|
||||
|
||||
'@eslint/compat@1.1.1': {}
|
||||
|
|
@ -11800,7 +11906,7 @@ snapshots:
|
|||
'@eslint/config-array@0.20.0':
|
||||
dependencies:
|
||||
'@eslint/object-schema': 2.1.6
|
||||
debug: 4.4.0(supports-color@8.1.1)
|
||||
debug: 4.4.0(supports-color@5.5.0)
|
||||
minimatch: 3.1.2
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
|
@ -11814,7 +11920,7 @@ snapshots:
|
|||
'@eslint/eslintrc@3.3.1':
|
||||
dependencies:
|
||||
ajv: 6.12.6
|
||||
debug: 4.4.0(supports-color@8.1.1)
|
||||
debug: 4.4.0(supports-color@5.5.0)
|
||||
espree: 10.3.0
|
||||
globals: 14.0.0
|
||||
ignore: 5.3.2
|
||||
|
|
@ -13905,16 +14011,16 @@ snapshots:
|
|||
ts-dedent: 2.2.0
|
||||
type-fest: 2.19.0
|
||||
vue: 3.5.14(typescript@5.8.3)
|
||||
vue-component-type-helpers: 2.2.10
|
||||
vue-component-type-helpers: 3.0.5
|
||||
|
||||
'@stylistic/eslint-plugin@4.2.0(eslint@9.25.1)(typescript@5.8.3)':
|
||||
dependencies:
|
||||
'@typescript-eslint/utils': 8.31.0(eslint@9.25.1)(typescript@5.8.3)
|
||||
'@typescript-eslint/utils': 8.39.1(eslint@9.25.1)(typescript@5.8.3)
|
||||
eslint: 9.25.1
|
||||
eslint-visitor-keys: 4.2.0
|
||||
espree: 10.3.0
|
||||
eslint-visitor-keys: 4.2.1
|
||||
espree: 10.4.0
|
||||
estraverse: 5.3.0
|
||||
picomatch: 4.0.2
|
||||
picomatch: 4.0.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
- typescript
|
||||
|
|
@ -14065,7 +14171,7 @@ snapshots:
|
|||
|
||||
'@tokenizer/inflate@0.2.7':
|
||||
dependencies:
|
||||
debug: 4.4.0(supports-color@8.1.1)
|
||||
debug: 4.4.0(supports-color@5.5.0)
|
||||
fflate: 0.8.2
|
||||
token-types: 6.0.0
|
||||
transitivePeerDependencies:
|
||||
|
|
@ -14306,7 +14412,7 @@ snapshots:
|
|||
pg-protocol: 1.8.0
|
||||
pg-types: 2.2.0
|
||||
|
||||
'@types/prop-types@15.7.14': {}
|
||||
'@types/prop-types@15.7.15': {}
|
||||
|
||||
'@types/proxy-addr@2.0.3':
|
||||
dependencies:
|
||||
|
|
@ -14330,8 +14436,8 @@ snapshots:
|
|||
|
||||
'@types/react@18.0.28':
|
||||
dependencies:
|
||||
'@types/prop-types': 15.7.14
|
||||
'@types/scheduler': 0.23.0
|
||||
'@types/prop-types': 15.7.15
|
||||
'@types/scheduler': 0.26.0
|
||||
csstype: 3.1.3
|
||||
|
||||
'@types/readdir-glob@1.1.1':
|
||||
|
|
@ -14348,7 +14454,7 @@ snapshots:
|
|||
dependencies:
|
||||
htmlparser2: 8.0.1
|
||||
|
||||
'@types/scheduler@0.23.0': {}
|
||||
'@types/scheduler@0.26.0': {}
|
||||
|
||||
'@types/seedrandom@3.0.8': {}
|
||||
|
||||
|
|
@ -14455,22 +14561,40 @@ snapshots:
|
|||
'@typescript-eslint/types': 8.31.0
|
||||
'@typescript-eslint/typescript-estree': 8.31.0(typescript@5.8.3)
|
||||
'@typescript-eslint/visitor-keys': 8.31.0
|
||||
debug: 4.4.0(supports-color@8.1.1)
|
||||
debug: 4.4.0(supports-color@5.5.0)
|
||||
eslint: 9.25.1
|
||||
typescript: 5.8.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@typescript-eslint/project-service@8.39.1(typescript@5.8.3)':
|
||||
dependencies:
|
||||
'@typescript-eslint/tsconfig-utils': 8.39.1(typescript@5.8.3)
|
||||
'@typescript-eslint/types': 8.39.1
|
||||
debug: 4.4.1
|
||||
typescript: 5.8.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@typescript-eslint/scope-manager@8.31.0':
|
||||
dependencies:
|
||||
'@typescript-eslint/types': 8.31.0
|
||||
'@typescript-eslint/visitor-keys': 8.31.0
|
||||
|
||||
'@typescript-eslint/scope-manager@8.39.1':
|
||||
dependencies:
|
||||
'@typescript-eslint/types': 8.39.1
|
||||
'@typescript-eslint/visitor-keys': 8.39.1
|
||||
|
||||
'@typescript-eslint/tsconfig-utils@8.39.1(typescript@5.8.3)':
|
||||
dependencies:
|
||||
typescript: 5.8.3
|
||||
|
||||
'@typescript-eslint/type-utils@8.31.0(eslint@9.25.1)(typescript@5.8.3)':
|
||||
dependencies:
|
||||
'@typescript-eslint/typescript-estree': 8.31.0(typescript@5.8.3)
|
||||
'@typescript-eslint/utils': 8.31.0(eslint@9.25.1)(typescript@5.8.3)
|
||||
debug: 4.4.0(supports-color@8.1.1)
|
||||
debug: 4.4.0(supports-color@5.5.0)
|
||||
eslint: 9.25.1
|
||||
ts-api-utils: 2.1.0(typescript@5.8.3)
|
||||
typescript: 5.8.3
|
||||
|
|
@ -14479,11 +14603,13 @@ snapshots:
|
|||
|
||||
'@typescript-eslint/types@8.31.0': {}
|
||||
|
||||
'@typescript-eslint/types@8.39.1': {}
|
||||
|
||||
'@typescript-eslint/typescript-estree@8.31.0(typescript@5.8.3)':
|
||||
dependencies:
|
||||
'@typescript-eslint/types': 8.31.0
|
||||
'@typescript-eslint/visitor-keys': 8.31.0
|
||||
debug: 4.4.0(supports-color@8.1.1)
|
||||
debug: 4.4.0(supports-color@5.5.0)
|
||||
fast-glob: 3.3.3
|
||||
is-glob: 4.0.3
|
||||
minimatch: 9.0.5
|
||||
|
|
@ -14493,6 +14619,22 @@ snapshots:
|
|||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@typescript-eslint/typescript-estree@8.39.1(typescript@5.8.3)':
|
||||
dependencies:
|
||||
'@typescript-eslint/project-service': 8.39.1(typescript@5.8.3)
|
||||
'@typescript-eslint/tsconfig-utils': 8.39.1(typescript@5.8.3)
|
||||
'@typescript-eslint/types': 8.39.1
|
||||
'@typescript-eslint/visitor-keys': 8.39.1
|
||||
debug: 4.4.1
|
||||
fast-glob: 3.3.3
|
||||
is-glob: 4.0.3
|
||||
minimatch: 9.0.5
|
||||
semver: 7.7.2
|
||||
ts-api-utils: 2.1.0(typescript@5.8.3)
|
||||
typescript: 5.8.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@typescript-eslint/utils@8.31.0(eslint@9.25.1)(typescript@5.8.3)':
|
||||
dependencies:
|
||||
'@eslint-community/eslint-utils': 4.5.1(eslint@9.25.1)
|
||||
|
|
@ -14504,11 +14646,27 @@ snapshots:
|
|||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@typescript-eslint/utils@8.39.1(eslint@9.25.1)(typescript@5.8.3)':
|
||||
dependencies:
|
||||
'@eslint-community/eslint-utils': 4.7.0(eslint@9.25.1)
|
||||
'@typescript-eslint/scope-manager': 8.39.1
|
||||
'@typescript-eslint/types': 8.39.1
|
||||
'@typescript-eslint/typescript-estree': 8.39.1(typescript@5.8.3)
|
||||
eslint: 9.25.1
|
||||
typescript: 5.8.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@typescript-eslint/visitor-keys@8.31.0':
|
||||
dependencies:
|
||||
'@typescript-eslint/types': 8.31.0
|
||||
eslint-visitor-keys: 4.2.0
|
||||
|
||||
'@typescript-eslint/visitor-keys@8.39.1':
|
||||
dependencies:
|
||||
'@typescript-eslint/types': 8.39.1
|
||||
eslint-visitor-keys: 4.2.1
|
||||
|
||||
'@ungap/structured-clone@1.2.0': {}
|
||||
|
||||
'@vitejs/plugin-vue@5.2.3(vite@6.3.4(@types/node@22.15.2)(sass@1.87.0)(terser@5.39.0)(tsx@4.19.3))(vue@3.5.14(typescript@5.8.3))':
|
||||
|
|
@ -14520,7 +14678,7 @@ snapshots:
|
|||
dependencies:
|
||||
'@ampproject/remapping': 2.3.0
|
||||
'@bcoe/v8-coverage': 1.0.2
|
||||
debug: 4.4.0(supports-color@8.1.1)
|
||||
debug: 4.4.0(supports-color@5.5.0)
|
||||
istanbul-lib-coverage: 3.2.2
|
||||
istanbul-lib-report: 3.0.1
|
||||
istanbul-lib-source-maps: 5.0.6
|
||||
|
|
@ -14874,13 +15032,19 @@ snapshots:
|
|||
dependencies:
|
||||
acorn: 8.14.1
|
||||
|
||||
acorn-jsx@5.3.2(acorn@8.15.0):
|
||||
dependencies:
|
||||
acorn: 8.15.0
|
||||
|
||||
acorn@7.4.1: {}
|
||||
|
||||
acorn@8.14.1: {}
|
||||
|
||||
acorn@8.15.0: {}
|
||||
|
||||
agent-base@7.1.0:
|
||||
dependencies:
|
||||
debug: 4.4.0(supports-color@8.1.1)
|
||||
debug: 4.4.0(supports-color@5.5.0)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
|
|
@ -15292,7 +15456,7 @@ snapshots:
|
|||
dependencies:
|
||||
bytes: 3.1.2
|
||||
content-type: 1.0.5
|
||||
debug: 4.4.0(supports-color@8.1.1)
|
||||
debug: 4.4.0(supports-color@5.5.0)
|
||||
http-errors: 2.0.0
|
||||
iconv-lite: 0.6.3
|
||||
on-finished: 2.4.1
|
||||
|
|
@ -15914,9 +16078,9 @@ snapshots:
|
|||
dependencies:
|
||||
css-tree: 2.2.1
|
||||
|
||||
cssstyle@4.3.0:
|
||||
cssstyle@4.6.0:
|
||||
dependencies:
|
||||
'@asamuzakjp/css-color': 3.1.1
|
||||
'@asamuzakjp/css-color': 3.2.0
|
||||
rrweb-cssom: 0.8.0
|
||||
optional: true
|
||||
|
||||
|
|
@ -16038,6 +16202,11 @@ snapshots:
|
|||
ms: 2.1.3
|
||||
optionalDependencies:
|
||||
supports-color: 8.1.1
|
||||
optional: true
|
||||
|
||||
debug@4.4.1:
|
||||
dependencies:
|
||||
ms: 2.1.3
|
||||
|
||||
decamelize-keys@1.1.1:
|
||||
dependencies:
|
||||
|
|
@ -16046,7 +16215,7 @@ snapshots:
|
|||
|
||||
decamelize@1.2.0: {}
|
||||
|
||||
decimal.js@10.5.0:
|
||||
decimal.js@10.6.0:
|
||||
optional: true
|
||||
|
||||
decode-bmp@0.2.1:
|
||||
|
|
@ -16409,7 +16578,7 @@ snapshots:
|
|||
|
||||
esbuild-register@3.5.0(esbuild@0.25.3):
|
||||
dependencies:
|
||||
debug: 4.4.0(supports-color@8.1.1)
|
||||
debug: 4.4.0(supports-color@5.5.0)
|
||||
esbuild: 0.25.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
|
@ -16540,6 +16709,8 @@ snapshots:
|
|||
|
||||
eslint-visitor-keys@4.2.0: {}
|
||||
|
||||
eslint-visitor-keys@4.2.1: {}
|
||||
|
||||
eslint@9.25.1:
|
||||
dependencies:
|
||||
'@eslint-community/eslint-utils': 4.5.1(eslint@9.25.1)
|
||||
|
|
@ -16558,7 +16729,7 @@ snapshots:
|
|||
ajv: 6.12.6
|
||||
chalk: 4.1.2
|
||||
cross-spawn: 7.0.6
|
||||
debug: 4.4.0(supports-color@8.1.1)
|
||||
debug: 4.4.0(supports-color@5.5.0)
|
||||
escape-string-regexp: 4.0.0
|
||||
eslint-scope: 8.3.0
|
||||
eslint-visitor-keys: 4.2.0
|
||||
|
|
@ -16586,6 +16757,12 @@ snapshots:
|
|||
acorn-jsx: 5.3.2(acorn@8.14.1)
|
||||
eslint-visitor-keys: 4.2.0
|
||||
|
||||
espree@10.4.0:
|
||||
dependencies:
|
||||
acorn: 8.15.0
|
||||
acorn-jsx: 5.3.2(acorn@8.15.0)
|
||||
eslint-visitor-keys: 4.2.1
|
||||
|
||||
esprima@4.0.1: {}
|
||||
|
||||
esquery@1.6.0:
|
||||
|
|
@ -16758,7 +16935,7 @@ snapshots:
|
|||
content-type: 1.0.5
|
||||
cookie: 0.7.2
|
||||
cookie-signature: 1.2.2
|
||||
debug: 4.4.0(supports-color@8.1.1)
|
||||
debug: 4.4.0(supports-color@5.5.0)
|
||||
encodeurl: 2.0.0
|
||||
escape-html: 1.0.3
|
||||
etag: 1.8.1
|
||||
|
|
@ -16965,7 +17142,7 @@ snapshots:
|
|||
|
||||
finalhandler@2.1.0:
|
||||
dependencies:
|
||||
debug: 4.4.0(supports-color@8.1.1)
|
||||
debug: 4.4.0(supports-color@5.5.0)
|
||||
encodeurl: 2.0.0
|
||||
escape-html: 1.0.3
|
||||
on-finished: 2.4.1
|
||||
|
|
@ -17019,7 +17196,7 @@ snapshots:
|
|||
|
||||
follow-redirects@1.15.9(debug@4.4.0):
|
||||
optionalDependencies:
|
||||
debug: 4.4.0(supports-color@8.1.1)
|
||||
debug: 4.4.0(supports-color@5.5.0)
|
||||
|
||||
for-each@0.3.3:
|
||||
dependencies:
|
||||
|
|
@ -17434,7 +17611,7 @@ snapshots:
|
|||
http-proxy-agent@7.0.2:
|
||||
dependencies:
|
||||
agent-base: 7.1.3
|
||||
debug: 4.4.0(supports-color@8.1.1)
|
||||
debug: 4.4.0(supports-color@5.5.0)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
|
|
@ -17462,7 +17639,7 @@ snapshots:
|
|||
https-proxy-agent@7.0.6:
|
||||
dependencies:
|
||||
agent-base: 7.1.3
|
||||
debug: 4.4.0(supports-color@8.1.1)
|
||||
debug: 4.4.0(supports-color@5.5.0)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
|
|
@ -17564,7 +17741,7 @@ snapshots:
|
|||
dependencies:
|
||||
'@ioredis/commands': 1.2.0
|
||||
cluster-key-slot: 1.1.2
|
||||
debug: 4.4.0(supports-color@8.1.1)
|
||||
debug: 4.4.0(supports-color@5.5.0)
|
||||
denque: 2.1.0
|
||||
lodash.defaults: 4.2.0
|
||||
lodash.isarguments: 3.1.0
|
||||
|
|
@ -17807,7 +17984,7 @@ snapshots:
|
|||
|
||||
istanbul-lib-source-maps@4.0.1:
|
||||
dependencies:
|
||||
debug: 4.4.0(supports-color@8.1.1)
|
||||
debug: 4.4.0(supports-color@5.5.0)
|
||||
istanbul-lib-coverage: 3.2.2
|
||||
source-map: 0.6.1
|
||||
transitivePeerDependencies:
|
||||
|
|
@ -17816,7 +17993,7 @@ snapshots:
|
|||
istanbul-lib-source-maps@5.0.6:
|
||||
dependencies:
|
||||
'@jridgewell/trace-mapping': 0.3.25
|
||||
debug: 4.4.0(supports-color@8.1.1)
|
||||
debug: 4.4.0(supports-color@5.5.0)
|
||||
istanbul-lib-coverage: 3.2.2
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
|
@ -18215,14 +18392,14 @@ snapshots:
|
|||
|
||||
jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@6.0.5):
|
||||
dependencies:
|
||||
cssstyle: 4.3.0
|
||||
cssstyle: 4.6.0
|
||||
data-urls: 5.0.0
|
||||
decimal.js: 10.5.0
|
||||
decimal.js: 10.6.0
|
||||
html-encoding-sniffer: 4.0.0
|
||||
http-proxy-agent: 7.0.2
|
||||
https-proxy-agent: 7.0.6
|
||||
is-potential-custom-element-name: 1.0.1
|
||||
nwsapi: 2.2.19
|
||||
nwsapi: 2.2.21
|
||||
parse5: 7.3.0
|
||||
rrweb-cssom: 0.8.0
|
||||
saxes: 6.0.0
|
||||
|
|
@ -18233,7 +18410,7 @@ snapshots:
|
|||
whatwg-encoding: 3.1.1
|
||||
whatwg-mimetype: 4.0.0
|
||||
whatwg-url: 14.2.0
|
||||
ws: 8.18.1(bufferutil@4.0.9)(utf-8-validate@6.0.5)
|
||||
ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5)
|
||||
xml-name-validator: 5.0.0
|
||||
transitivePeerDependencies:
|
||||
- bufferutil
|
||||
|
|
@ -18845,7 +19022,7 @@ snapshots:
|
|||
micromark@4.0.0:
|
||||
dependencies:
|
||||
'@types/debug': 4.1.12
|
||||
debug: 4.4.0(supports-color@8.1.1)
|
||||
debug: 4.4.0(supports-color@5.5.0)
|
||||
decode-named-character-reference: 1.0.2
|
||||
devlop: 1.1.0
|
||||
micromark-core-commonmark: 2.0.0
|
||||
|
|
@ -19229,7 +19406,7 @@ snapshots:
|
|||
dependencies:
|
||||
boolbase: 1.0.0
|
||||
|
||||
nwsapi@2.2.19:
|
||||
nwsapi@2.2.21:
|
||||
optional: true
|
||||
|
||||
oauth@0.10.2: {}
|
||||
|
|
@ -19522,6 +19699,8 @@ snapshots:
|
|||
|
||||
picomatch@4.0.2: {}
|
||||
|
||||
picomatch@4.0.3: {}
|
||||
|
||||
pid-port@1.0.2:
|
||||
dependencies:
|
||||
execa: 8.0.1
|
||||
|
|
@ -20211,7 +20390,7 @@ snapshots:
|
|||
|
||||
require-in-the-middle@7.3.0:
|
||||
dependencies:
|
||||
debug: 4.4.0(supports-color@8.1.1)
|
||||
debug: 4.4.0(supports-color@5.5.0)
|
||||
module-details-from-path: 1.0.3
|
||||
resolve: 1.22.8
|
||||
transitivePeerDependencies:
|
||||
|
|
@ -20291,7 +20470,7 @@ snapshots:
|
|||
|
||||
router@2.2.0:
|
||||
dependencies:
|
||||
debug: 4.4.0(supports-color@8.1.1)
|
||||
debug: 4.4.0(supports-color@5.5.0)
|
||||
depd: 2.0.0
|
||||
is-promise: 4.0.0
|
||||
parseurl: 1.3.3
|
||||
|
|
@ -20411,7 +20590,7 @@ snapshots:
|
|||
|
||||
send@1.2.0:
|
||||
dependencies:
|
||||
debug: 4.4.0(supports-color@8.1.1)
|
||||
debug: 4.4.0(supports-color@5.5.0)
|
||||
encodeurl: 2.0.0
|
||||
escape-html: 1.0.3
|
||||
etag: 1.8.1
|
||||
|
|
@ -20673,7 +20852,7 @@ snapshots:
|
|||
socks-proxy-agent@8.0.2:
|
||||
dependencies:
|
||||
agent-base: 7.1.0
|
||||
debug: 4.4.0(supports-color@8.1.1)
|
||||
debug: 4.4.0(supports-color@5.5.0)
|
||||
socks: 2.7.1
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
|
@ -20783,7 +20962,7 @@ snapshots:
|
|||
arg: 5.0.2
|
||||
bluebird: 3.7.2
|
||||
check-more-types: 2.24.0
|
||||
debug: 4.4.0(supports-color@8.1.1)
|
||||
debug: 4.4.0(supports-color@5.5.0)
|
||||
execa: 5.1.1
|
||||
lazy-ass: 1.6.0
|
||||
ps-tree: 1.2.0
|
||||
|
|
@ -20960,7 +21139,7 @@ snapshots:
|
|||
dependencies:
|
||||
component-emitter: 1.3.1
|
||||
cookiejar: 2.1.4
|
||||
debug: 4.4.0(supports-color@8.1.1)
|
||||
debug: 4.4.0(supports-color@5.5.0)
|
||||
fast-safe-stringify: 2.1.1
|
||||
form-data: 4.0.2
|
||||
formidable: 3.5.4
|
||||
|
|
@ -21120,11 +21299,19 @@ snapshots:
|
|||
tldts-core@6.1.63:
|
||||
optional: true
|
||||
|
||||
tldts-core@6.1.86:
|
||||
optional: true
|
||||
|
||||
tldts@6.1.63:
|
||||
dependencies:
|
||||
tldts-core: 6.1.63
|
||||
optional: true
|
||||
|
||||
tldts@6.1.86:
|
||||
dependencies:
|
||||
tldts-core: 6.1.86
|
||||
optional: true
|
||||
|
||||
tmp@0.2.3: {}
|
||||
|
||||
tmpl@1.0.5: {}
|
||||
|
|
@ -21166,12 +21353,12 @@ snapshots:
|
|||
|
||||
tough-cookie@5.1.2:
|
||||
dependencies:
|
||||
tldts: 6.1.63
|
||||
tldts: 6.1.86
|
||||
optional: true
|
||||
|
||||
tr46@0.0.3: {}
|
||||
|
||||
tr46@5.1.0:
|
||||
tr46@5.1.1:
|
||||
dependencies:
|
||||
punycode: 2.3.1
|
||||
optional: true
|
||||
|
|
@ -21340,7 +21527,7 @@ snapshots:
|
|||
app-root-path: 3.1.0
|
||||
buffer: 6.0.3
|
||||
dayjs: 1.11.13
|
||||
debug: 4.4.0(supports-color@8.1.1)
|
||||
debug: 4.4.0(supports-color@5.5.0)
|
||||
dotenv: 16.5.0
|
||||
glob: 10.4.5
|
||||
reflect-metadata: 0.2.2
|
||||
|
|
@ -21540,7 +21727,7 @@ snapshots:
|
|||
vite-node@3.1.2(@types/node@22.15.2)(sass@1.87.0)(terser@5.39.0)(tsx@4.19.3):
|
||||
dependencies:
|
||||
cac: 6.7.14
|
||||
debug: 4.4.0(supports-color@8.1.1)
|
||||
debug: 4.4.0(supports-color@5.5.0)
|
||||
es-module-lexer: 1.6.0
|
||||
pathe: 2.0.3
|
||||
vite: 6.3.4(@types/node@22.15.2)(sass@1.87.0)(terser@5.39.0)(tsx@4.19.3)
|
||||
|
|
@ -21589,7 +21776,7 @@ snapshots:
|
|||
'@vitest/spy': 3.1.2
|
||||
'@vitest/utils': 3.1.2
|
||||
chai: 5.2.0
|
||||
debug: 4.4.0(supports-color@8.1.1)
|
||||
debug: 4.4.0(supports-color@5.5.0)
|
||||
expect-type: 1.2.1
|
||||
magic-string: 0.30.17
|
||||
pathe: 2.0.3
|
||||
|
|
@ -21661,6 +21848,8 @@ snapshots:
|
|||
|
||||
vue-component-type-helpers@2.2.10: {}
|
||||
|
||||
vue-component-type-helpers@3.0.5: {}
|
||||
|
||||
vue-demi@0.14.7(vue@3.5.14(typescript@5.8.3)):
|
||||
dependencies:
|
||||
vue: 3.5.14(typescript@5.8.3)
|
||||
|
|
@ -21682,7 +21871,7 @@ snapshots:
|
|||
|
||||
vue-eslint-parser@10.1.3(eslint@9.25.1):
|
||||
dependencies:
|
||||
debug: 4.4.0(supports-color@8.1.1)
|
||||
debug: 4.4.0(supports-color@5.5.0)
|
||||
eslint: 9.25.1
|
||||
eslint-scope: 8.3.0
|
||||
eslint-visitor-keys: 4.2.0
|
||||
|
|
@ -21795,7 +21984,7 @@ snapshots:
|
|||
|
||||
whatwg-url@14.2.0:
|
||||
dependencies:
|
||||
tr46: 5.1.0
|
||||
tr46: 5.1.1
|
||||
webidl-conversions: 7.0.0
|
||||
optional: true
|
||||
|
||||
|
|
@ -21893,6 +22082,12 @@ snapshots:
|
|||
bufferutil: 4.0.9
|
||||
utf-8-validate: 6.0.5
|
||||
|
||||
ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5):
|
||||
optionalDependencies:
|
||||
bufferutil: 4.0.9
|
||||
utf-8-validate: 6.0.5
|
||||
optional: true
|
||||
|
||||
xev@3.0.2: {}
|
||||
|
||||
xml-js@1.6.11:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue