Compare commits
2 Commits
bcaf378111
...
80e104a9f4
| Author | SHA1 | Date |
|---|---|---|
|
|
80e104a9f4 | |
|
|
9876a331c1 |
|
|
@ -13,6 +13,7 @@
|
|||
"ioredis": "^5.10.1",
|
||||
"pg": "^8.20.0",
|
||||
"prism-media": "^1.3.5",
|
||||
"sharp": "^0.34.5",
|
||||
"youtubei.js": "^17.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,56 @@
|
|||
{
|
||||
"version": 1,
|
||||
"description": "Fishing mini-game rarity modifiers and result art background palette.",
|
||||
"rarities": [
|
||||
{
|
||||
"id": "common",
|
||||
"displayName": "Common",
|
||||
"displayNameKo": "일반",
|
||||
"rollRate": 58,
|
||||
"rewardMultiplier": 1.0,
|
||||
"reactionWindowMultiplier": 1.0,
|
||||
"tensionMultiplier": 1.0,
|
||||
"backgroundColor": "#6B7280"
|
||||
},
|
||||
{
|
||||
"id": "uncommon",
|
||||
"displayName": "Uncommon",
|
||||
"displayNameKo": "고급",
|
||||
"rollRate": 24,
|
||||
"rewardMultiplier": 1.2,
|
||||
"reactionWindowMultiplier": 1.08,
|
||||
"tensionMultiplier": 1.08,
|
||||
"backgroundColor": "#22C55E"
|
||||
},
|
||||
{
|
||||
"id": "rare",
|
||||
"displayName": "Rare",
|
||||
"displayNameKo": "희귀",
|
||||
"rollRate": 11,
|
||||
"rewardMultiplier": 1.55,
|
||||
"reactionWindowMultiplier": 1.16,
|
||||
"tensionMultiplier": 1.14,
|
||||
"backgroundColor": "#3B82F6"
|
||||
},
|
||||
{
|
||||
"id": "epic",
|
||||
"displayName": "Epic",
|
||||
"displayNameKo": "영웅",
|
||||
"rollRate": 5,
|
||||
"rewardMultiplier": 2.1,
|
||||
"reactionWindowMultiplier": 1.28,
|
||||
"tensionMultiplier": 1.24,
|
||||
"backgroundColor": "#A855F7"
|
||||
},
|
||||
{
|
||||
"id": "legendary",
|
||||
"displayName": "Legendary",
|
||||
"displayNameKo": "전설",
|
||||
"rollRate": 2,
|
||||
"rewardMultiplier": 3.0,
|
||||
"reactionWindowMultiplier": 1.42,
|
||||
"tensionMultiplier": 1.36,
|
||||
"backgroundColor": "#F59E0B"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -278,13 +278,14 @@ export const en: TranslationSchema = {
|
|||
titleActive: 'Fishing Session',
|
||||
titleEnded: 'Fishing Session Ended',
|
||||
status: 'Status',
|
||||
rarity: 'Rarity',
|
||||
targetFish: 'Target Fish',
|
||||
distance: 'Distance',
|
||||
tension: 'Line Tension',
|
||||
reward: 'Reward',
|
||||
threadHint: 'Use /fishing cast to play again, or /fishing end to delete the thread.',
|
||||
catchResultTitle: 'Big Catch!',
|
||||
catchResultBody: 'You caught **{{fish}}** and earned **{{reward}} G**.',
|
||||
catchResultBody: 'You caught a **{{rarity}} {{fish}}** and earned **{{reward}} G**.',
|
||||
states: {
|
||||
hooked: 'Hooked',
|
||||
resting: 'Resting',
|
||||
|
|
|
|||
|
|
@ -278,13 +278,14 @@ export const ko: TranslationSchema = {
|
|||
titleActive: '낚시 세션',
|
||||
titleEnded: '낚시 세션 종료',
|
||||
status: '상태',
|
||||
rarity: '레어도',
|
||||
targetFish: '대상 물고기',
|
||||
distance: '거리',
|
||||
tension: '끊어짐 게이지',
|
||||
reward: '보상',
|
||||
threadHint: '/fishing cast로 다시 시작하거나 /fishing end로 스레드를 삭제할 수 있습니다.',
|
||||
catchResultTitle: '낚시 성공!',
|
||||
catchResultBody: '**{{fish}}**를 낚았습니다. **{{reward}} G**를 획득했습니다.',
|
||||
catchResultBody: '**{{rarity}} {{fish}}**를 낚았습니다. **{{reward}} G**를 획득했습니다.',
|
||||
states: {
|
||||
hooked: '입질 중',
|
||||
resting: '휴식 중',
|
||||
|
|
|
|||
|
|
@ -232,6 +232,7 @@ export interface TranslationSchema {
|
|||
titleActive: string;
|
||||
titleEnded: string;
|
||||
status: string;
|
||||
rarity: string;
|
||||
targetFish: string;
|
||||
distance: string;
|
||||
tension: string;
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
} from 'discord.js';
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import sharp from 'sharp';
|
||||
import { SupportedLocale, t } from '../i18n';
|
||||
import { RefinementService } from './RefinementService';
|
||||
import { logger } from '../utils/logger';
|
||||
|
|
@ -41,6 +42,21 @@ interface FishingCatalogFile {
|
|||
fish: FishingCatalogEntry[];
|
||||
}
|
||||
|
||||
interface FishingRarityEntry {
|
||||
id: string;
|
||||
displayName: string;
|
||||
displayNameKo?: string;
|
||||
rollRate: number;
|
||||
rewardMultiplier: number;
|
||||
reactionWindowMultiplier: number;
|
||||
tensionMultiplier: number;
|
||||
backgroundColor: string;
|
||||
}
|
||||
|
||||
interface FishingRarityFile {
|
||||
rarities: FishingRarityEntry[];
|
||||
}
|
||||
|
||||
interface FishingSession {
|
||||
guildId: string;
|
||||
userId: string;
|
||||
|
|
@ -50,6 +66,7 @@ interface FishingSession {
|
|||
thread: ThreadChannel;
|
||||
controlMessage: Message;
|
||||
currentFish: FishingCatalogEntry;
|
||||
currentRarity: FishingRarityEntry;
|
||||
fishPosition: FishingDirection;
|
||||
selectedAction: FishingAction | null;
|
||||
phaseStartedAt: number;
|
||||
|
|
@ -71,7 +88,7 @@ const REST_DISTANCE_INCREASE = 8;
|
|||
const DISTANCE_BAR_WIDTH = 12;
|
||||
const TENSION_BAR_WIDTH = 12;
|
||||
const ROUND_DURATION_MS = 2500;
|
||||
const REACTION_MIN_MS = 1200;
|
||||
const REACTION_MIN_MS = 1000;
|
||||
const REACTION_GRACE_MS = 300;
|
||||
const SESSION_TICK_MS = 250;
|
||||
|
||||
|
|
@ -80,6 +97,7 @@ export class FishingService {
|
|||
private static sessionsByThread = new Map<string, FishingSession>();
|
||||
private static threadEnterPromises = new Map<string, Promise<{ thread: ThreadChannel; existed: boolean }>>();
|
||||
private static fishingCatalog = this.loadFishingCatalog();
|
||||
private static fishingRarities = this.loadFishingRarities();
|
||||
|
||||
static async enterThread(interaction: ChatInputCommandInteraction) {
|
||||
if (!interaction.guildId || !interaction.channel || interaction.channel.type !== ChannelType.GuildText) {
|
||||
|
|
@ -206,7 +224,7 @@ export class FishingService {
|
|||
}
|
||||
|
||||
const elapsed = Date.now() - session.phaseStartedAt;
|
||||
const reactionWindowMs = this.getReactionWindowMs(session.currentFish);
|
||||
const reactionWindowMs = this.getReactionWindowMs(session.currentFish, session.currentRarity);
|
||||
|
||||
if (
|
||||
session.selectedAction
|
||||
|
|
@ -242,11 +260,17 @@ export class FishingService {
|
|||
private static async resolveSuccessfulPull(session: FishingSession) {
|
||||
const distanceReduction = this.rollRange(session.currentFish.distanceReductionByPosition[session.fishPosition]);
|
||||
session.distance = Math.max(0, session.distance - distanceReduction);
|
||||
session.lineTension = Math.min(MAX_TENSION, session.lineTension + MATCH_TENSION_INCREASE);
|
||||
session.lineTension = Math.min(
|
||||
MAX_TENSION,
|
||||
session.lineTension + Math.max(1, Math.round(MATCH_TENSION_INCREASE * session.currentRarity.tensionMultiplier)),
|
||||
);
|
||||
session.status = session.lineTension >= MAX_TENSION ? 'failed' : 'tense';
|
||||
|
||||
if (session.distance <= 0) {
|
||||
const reward = this.rollRange(session.currentFish.rewardGold);
|
||||
const reward = Math.max(
|
||||
1,
|
||||
Math.round(this.rollRange(session.currentFish.rewardGold) * session.currentRarity.rewardMultiplier),
|
||||
);
|
||||
session.reward = reward;
|
||||
await RefinementService.addGold(session.userId, session.guildId, reward);
|
||||
await this.finishSession(session, 'success', false);
|
||||
|
|
@ -263,7 +287,10 @@ export class FishingService {
|
|||
}
|
||||
|
||||
private static async resolveMiss(session: FishingSession) {
|
||||
session.lineTension = Math.min(MAX_TENSION, session.lineTension + MISS_TENSION_INCREASE);
|
||||
session.lineTension = Math.min(
|
||||
MAX_TENSION,
|
||||
session.lineTension + Math.max(1, Math.round(MISS_TENSION_INCREASE * session.currentRarity.tensionMultiplier)),
|
||||
);
|
||||
session.status = session.selectedAction && session.selectedAction !== 'rest' ? 'missed' : 'hooked';
|
||||
|
||||
if (session.lineTension >= MAX_TENSION) {
|
||||
|
|
@ -338,7 +365,7 @@ export class FishingService {
|
|||
}
|
||||
|
||||
private static buildEmbed(
|
||||
session: Pick<FishingSession, 'locale' | 'currentFish' | 'fishPosition' | 'selectedAction' | 'phaseStartedAt' | 'distance' | 'lineTension' | 'status' | 'reward'>,
|
||||
session: Pick<FishingSession, 'locale' | 'currentFish' | 'currentRarity' | 'fishPosition' | 'selectedAction' | 'phaseStartedAt' | 'distance' | 'lineTension' | 'status' | 'reward'>,
|
||||
) {
|
||||
const distanceProgress = MAX_DISTANCE - session.distance;
|
||||
const distanceBar = this.buildGauge(distanceProgress, MAX_DISTANCE, DISTANCE_BAR_WIDTH);
|
||||
|
|
@ -347,7 +374,13 @@ export class FishingService {
|
|||
const selectedAction = this.formatSelectedAction(session.selectedAction);
|
||||
|
||||
const embed = new EmbedBuilder()
|
||||
.setColor(session.status === 'success' ? 0x57f287 : session.status === 'failed' ? 0xed4245 : 0x5865f2)
|
||||
.setColor(
|
||||
session.status === 'success'
|
||||
? this.hexToColorInt(session.currentRarity.backgroundColor)
|
||||
: session.status === 'failed'
|
||||
? 0xed4245
|
||||
: 0x5865f2,
|
||||
)
|
||||
.setTitle(
|
||||
session.status === 'success' || session.status === 'failed'
|
||||
? t(session.locale, 'commands.fishing.titleEnded')
|
||||
|
|
@ -430,6 +463,7 @@ export class FishingService {
|
|||
thread: ThreadChannel;
|
||||
}) {
|
||||
const currentFish = this.pickFishByRate();
|
||||
const currentRarity = this.pickRarityByRate();
|
||||
const sessionBase = {
|
||||
guildId: params.guildId,
|
||||
userId: params.userId,
|
||||
|
|
@ -438,6 +472,7 @@ export class FishingService {
|
|||
threadId: params.thread.id,
|
||||
thread: params.thread,
|
||||
currentFish,
|
||||
currentRarity,
|
||||
fishPosition: this.randomDirection(),
|
||||
selectedAction: null,
|
||||
phaseStartedAt: Date.now(),
|
||||
|
|
@ -463,7 +498,9 @@ export class FishingService {
|
|||
this.sessionsByUser.set(this.getUserKey(session.guildId, session.userId), session);
|
||||
this.sessionsByThread.set(session.threadId, session);
|
||||
|
||||
logger.info(`[Fishing] Started session for ${session.userId} in thread ${session.threadId}.`);
|
||||
logger.info(
|
||||
`[Fishing] Started session for ${session.userId} in thread ${session.threadId} (${session.currentRarity.id} ${session.currentFish.id}).`,
|
||||
);
|
||||
|
||||
session.tickInterval = setInterval(() => {
|
||||
void this.tickSession(session);
|
||||
|
|
@ -507,27 +544,52 @@ export class FishingService {
|
|||
|
||||
private static async sendCatchResult(session: FishingSession) {
|
||||
const artPath = this.pickRandomArtPath(session.currentFish);
|
||||
const rarityName = this.getRarityDisplayName(session.currentRarity, session.locale);
|
||||
const rarityBadge = this.getRarityBadge(session.currentRarity.id);
|
||||
const embed = new EmbedBuilder()
|
||||
.setColor(0x57f287)
|
||||
.setTitle(t(session.locale, 'commands.fishing.catchResultTitle'))
|
||||
.setColor(this.hexToColorInt(session.currentRarity.backgroundColor))
|
||||
.setTitle(`${rarityBadge} ${t(session.locale, 'commands.fishing.catchResultTitle')} · ${rarityName}`)
|
||||
.setDescription(
|
||||
t(session.locale, 'commands.fishing.catchResultBody', {
|
||||
rarity: rarityName,
|
||||
fish: session.currentFish.displayName,
|
||||
reward: String(session.reward ?? 0),
|
||||
}),
|
||||
);
|
||||
)
|
||||
.addFields({
|
||||
name: t(session.locale, 'commands.fishing.rarity'),
|
||||
value: rarityName,
|
||||
inline: true,
|
||||
});
|
||||
|
||||
if (artPath && fs.existsSync(artPath)) {
|
||||
const fileName = path.basename(artPath);
|
||||
const fileName = `${session.currentFish.id}_${session.currentRarity.id}.png`;
|
||||
try {
|
||||
const composite = await this.composeRarityArt(artPath, session.currentRarity.backgroundColor);
|
||||
embed.setImage(`attachment://${fileName}`);
|
||||
await session.thread.send({
|
||||
embeds: [embed],
|
||||
files: [new AttachmentBuilder(artPath, { name: fileName })],
|
||||
files: [
|
||||
new AttachmentBuilder(composite ?? artPath, { name: fileName }),
|
||||
],
|
||||
});
|
||||
logger.info(
|
||||
`[Fishing] Sent catch result with art for ${session.userId} (${session.currentRarity.id} ${session.currentFish.id}).`,
|
||||
);
|
||||
return;
|
||||
} catch (error) {
|
||||
logger.warn('Failed to send fishing catch result with art, retrying without attachment:', error);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
await session.thread.send({ embeds: [embed] });
|
||||
logger.info(
|
||||
`[Fishing] Sent catch result without art for ${session.userId} (${session.currentRarity.id} ${session.currentFish.id}).`,
|
||||
);
|
||||
} catch (error) {
|
||||
logger.error('Failed to send fishing catch result message:', error);
|
||||
}
|
||||
}
|
||||
|
||||
static previewFishLane(position: FishingDirection) {
|
||||
|
|
@ -580,9 +642,24 @@ export class FishingService {
|
|||
return this.fishingCatalog[this.fishingCatalog.length - 1];
|
||||
}
|
||||
|
||||
private static getReactionWindowMs(fish: FishingCatalogEntry) {
|
||||
private static pickRarityByRate() {
|
||||
const totalRate = this.fishingRarities.reduce((sum, rarity) => sum + rarity.rollRate, 0);
|
||||
let roll = Math.random() * totalRate;
|
||||
|
||||
for (const rarity of this.fishingRarities) {
|
||||
roll -= rarity.rollRate;
|
||||
if (roll <= 0) {
|
||||
return rarity;
|
||||
}
|
||||
}
|
||||
|
||||
return this.fishingRarities[this.fishingRarities.length - 1];
|
||||
}
|
||||
|
||||
private static getReactionWindowMs(fish: FishingCatalogEntry, rarity: FishingRarityEntry) {
|
||||
const configured = Math.round(fish.reactionWindowSec * 1000);
|
||||
return Math.max(REACTION_MIN_MS, Math.min(configured, ROUND_DURATION_MS));
|
||||
const adjusted = Math.round(configured / Math.max(1, rarity.reactionWindowMultiplier));
|
||||
return Math.max(REACTION_MIN_MS, Math.min(adjusted, ROUND_DURATION_MS));
|
||||
}
|
||||
|
||||
private static rollRange(range: FishingRange) {
|
||||
|
|
@ -606,6 +683,58 @@ export class FishingService {
|
|||
return path.resolve(__dirname, '..', '..', relativePath);
|
||||
}
|
||||
|
||||
private static loadFishingRarities(): FishingRarityEntry[] {
|
||||
const rarityPath = this.resolveResourcePath('resource/data/fishing/fish_rarities.json');
|
||||
const raw = fs.readFileSync(rarityPath, 'utf-8');
|
||||
const parsed = JSON.parse(raw) as FishingRarityFile;
|
||||
|
||||
if (!parsed.rarities?.length) {
|
||||
throw new Error('Fishing rarity catalog is empty.');
|
||||
}
|
||||
|
||||
return parsed.rarities;
|
||||
}
|
||||
|
||||
private static getRarityDisplayName(rarity: FishingRarityEntry, locale: SupportedLocale) {
|
||||
return locale === 'ko' && rarity.displayNameKo ? rarity.displayNameKo : rarity.displayName;
|
||||
}
|
||||
|
||||
private static getRarityBadge(rarityId: string) {
|
||||
if (rarityId === 'legendary') return '🟠';
|
||||
if (rarityId === 'epic') return '🟣';
|
||||
if (rarityId === 'rare') return '🔵';
|
||||
if (rarityId === 'uncommon') return '🟢';
|
||||
return '⚪';
|
||||
}
|
||||
|
||||
private static async composeRarityArt(artPath: string, backgroundColor: string) {
|
||||
try {
|
||||
const sourceBuffer = fs.readFileSync(artPath);
|
||||
const metadata = await sharp(sourceBuffer).metadata();
|
||||
const width = metadata.width ?? 512;
|
||||
const height = metadata.height ?? 512;
|
||||
|
||||
return await sharp({
|
||||
create: {
|
||||
width,
|
||||
height,
|
||||
channels: 4,
|
||||
background: backgroundColor,
|
||||
},
|
||||
})
|
||||
.composite([{ input: sourceBuffer }])
|
||||
.png()
|
||||
.toBuffer();
|
||||
} catch (error) {
|
||||
logger.warn('Failed to compose rarity fishing art, falling back to original asset:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static hexToColorInt(value: string) {
|
||||
return Number.parseInt(value.replace('#', ''), 16);
|
||||
}
|
||||
|
||||
private static formatSelectedAction(action: FishingAction | null) {
|
||||
if (action === 'left') return '⬅️';
|
||||
if (action === 'center') return '⏺️';
|
||||
|
|
|
|||
316
yarn.lock
316
yarn.lock
|
|
@ -582,7 +582,7 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@emnapi/runtime@npm:^1.4.3":
|
||||
"@emnapi/runtime@npm:^1.4.3, @emnapi/runtime@npm:^1.7.0":
|
||||
version: 1.9.1
|
||||
resolution: "@emnapi/runtime@npm:1.9.1"
|
||||
dependencies:
|
||||
|
|
@ -893,6 +893,233 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@img/colour@npm:^1.0.0":
|
||||
version: 1.1.0
|
||||
resolution: "@img/colour@npm:1.1.0"
|
||||
checksum: 10c0/2ebea2c0bbaee73b99badcefa04e1e71d83f36e5369337d3121dca841f4569533c4e2faddda6d62dd247f0d5cca143711f9446c59bcce81e427ba433a7a94a17
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@img/sharp-darwin-arm64@npm:0.34.5":
|
||||
version: 0.34.5
|
||||
resolution: "@img/sharp-darwin-arm64@npm:0.34.5"
|
||||
dependencies:
|
||||
"@img/sharp-libvips-darwin-arm64": "npm:1.2.4"
|
||||
dependenciesMeta:
|
||||
"@img/sharp-libvips-darwin-arm64":
|
||||
optional: true
|
||||
conditions: os=darwin & cpu=arm64
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@img/sharp-darwin-x64@npm:0.34.5":
|
||||
version: 0.34.5
|
||||
resolution: "@img/sharp-darwin-x64@npm:0.34.5"
|
||||
dependencies:
|
||||
"@img/sharp-libvips-darwin-x64": "npm:1.2.4"
|
||||
dependenciesMeta:
|
||||
"@img/sharp-libvips-darwin-x64":
|
||||
optional: true
|
||||
conditions: os=darwin & cpu=x64
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@img/sharp-libvips-darwin-arm64@npm:1.2.4":
|
||||
version: 1.2.4
|
||||
resolution: "@img/sharp-libvips-darwin-arm64@npm:1.2.4"
|
||||
conditions: os=darwin & cpu=arm64
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@img/sharp-libvips-darwin-x64@npm:1.2.4":
|
||||
version: 1.2.4
|
||||
resolution: "@img/sharp-libvips-darwin-x64@npm:1.2.4"
|
||||
conditions: os=darwin & cpu=x64
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@img/sharp-libvips-linux-arm64@npm:1.2.4":
|
||||
version: 1.2.4
|
||||
resolution: "@img/sharp-libvips-linux-arm64@npm:1.2.4"
|
||||
conditions: os=linux & cpu=arm64 & libc=glibc
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@img/sharp-libvips-linux-arm@npm:1.2.4":
|
||||
version: 1.2.4
|
||||
resolution: "@img/sharp-libvips-linux-arm@npm:1.2.4"
|
||||
conditions: os=linux & cpu=arm & libc=glibc
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@img/sharp-libvips-linux-ppc64@npm:1.2.4":
|
||||
version: 1.2.4
|
||||
resolution: "@img/sharp-libvips-linux-ppc64@npm:1.2.4"
|
||||
conditions: os=linux & cpu=ppc64 & libc=glibc
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@img/sharp-libvips-linux-riscv64@npm:1.2.4":
|
||||
version: 1.2.4
|
||||
resolution: "@img/sharp-libvips-linux-riscv64@npm:1.2.4"
|
||||
conditions: os=linux & cpu=riscv64 & libc=glibc
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@img/sharp-libvips-linux-s390x@npm:1.2.4":
|
||||
version: 1.2.4
|
||||
resolution: "@img/sharp-libvips-linux-s390x@npm:1.2.4"
|
||||
conditions: os=linux & cpu=s390x & libc=glibc
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@img/sharp-libvips-linux-x64@npm:1.2.4":
|
||||
version: 1.2.4
|
||||
resolution: "@img/sharp-libvips-linux-x64@npm:1.2.4"
|
||||
conditions: os=linux & cpu=x64 & libc=glibc
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@img/sharp-libvips-linuxmusl-arm64@npm:1.2.4":
|
||||
version: 1.2.4
|
||||
resolution: "@img/sharp-libvips-linuxmusl-arm64@npm:1.2.4"
|
||||
conditions: os=linux & cpu=arm64 & libc=musl
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@img/sharp-libvips-linuxmusl-x64@npm:1.2.4":
|
||||
version: 1.2.4
|
||||
resolution: "@img/sharp-libvips-linuxmusl-x64@npm:1.2.4"
|
||||
conditions: os=linux & cpu=x64 & libc=musl
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@img/sharp-linux-arm64@npm:0.34.5":
|
||||
version: 0.34.5
|
||||
resolution: "@img/sharp-linux-arm64@npm:0.34.5"
|
||||
dependencies:
|
||||
"@img/sharp-libvips-linux-arm64": "npm:1.2.4"
|
||||
dependenciesMeta:
|
||||
"@img/sharp-libvips-linux-arm64":
|
||||
optional: true
|
||||
conditions: os=linux & cpu=arm64 & libc=glibc
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@img/sharp-linux-arm@npm:0.34.5":
|
||||
version: 0.34.5
|
||||
resolution: "@img/sharp-linux-arm@npm:0.34.5"
|
||||
dependencies:
|
||||
"@img/sharp-libvips-linux-arm": "npm:1.2.4"
|
||||
dependenciesMeta:
|
||||
"@img/sharp-libvips-linux-arm":
|
||||
optional: true
|
||||
conditions: os=linux & cpu=arm & libc=glibc
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@img/sharp-linux-ppc64@npm:0.34.5":
|
||||
version: 0.34.5
|
||||
resolution: "@img/sharp-linux-ppc64@npm:0.34.5"
|
||||
dependencies:
|
||||
"@img/sharp-libvips-linux-ppc64": "npm:1.2.4"
|
||||
dependenciesMeta:
|
||||
"@img/sharp-libvips-linux-ppc64":
|
||||
optional: true
|
||||
conditions: os=linux & cpu=ppc64 & libc=glibc
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@img/sharp-linux-riscv64@npm:0.34.5":
|
||||
version: 0.34.5
|
||||
resolution: "@img/sharp-linux-riscv64@npm:0.34.5"
|
||||
dependencies:
|
||||
"@img/sharp-libvips-linux-riscv64": "npm:1.2.4"
|
||||
dependenciesMeta:
|
||||
"@img/sharp-libvips-linux-riscv64":
|
||||
optional: true
|
||||
conditions: os=linux & cpu=riscv64 & libc=glibc
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@img/sharp-linux-s390x@npm:0.34.5":
|
||||
version: 0.34.5
|
||||
resolution: "@img/sharp-linux-s390x@npm:0.34.5"
|
||||
dependencies:
|
||||
"@img/sharp-libvips-linux-s390x": "npm:1.2.4"
|
||||
dependenciesMeta:
|
||||
"@img/sharp-libvips-linux-s390x":
|
||||
optional: true
|
||||
conditions: os=linux & cpu=s390x & libc=glibc
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@img/sharp-linux-x64@npm:0.34.5":
|
||||
version: 0.34.5
|
||||
resolution: "@img/sharp-linux-x64@npm:0.34.5"
|
||||
dependencies:
|
||||
"@img/sharp-libvips-linux-x64": "npm:1.2.4"
|
||||
dependenciesMeta:
|
||||
"@img/sharp-libvips-linux-x64":
|
||||
optional: true
|
||||
conditions: os=linux & cpu=x64 & libc=glibc
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@img/sharp-linuxmusl-arm64@npm:0.34.5":
|
||||
version: 0.34.5
|
||||
resolution: "@img/sharp-linuxmusl-arm64@npm:0.34.5"
|
||||
dependencies:
|
||||
"@img/sharp-libvips-linuxmusl-arm64": "npm:1.2.4"
|
||||
dependenciesMeta:
|
||||
"@img/sharp-libvips-linuxmusl-arm64":
|
||||
optional: true
|
||||
conditions: os=linux & cpu=arm64 & libc=musl
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@img/sharp-linuxmusl-x64@npm:0.34.5":
|
||||
version: 0.34.5
|
||||
resolution: "@img/sharp-linuxmusl-x64@npm:0.34.5"
|
||||
dependencies:
|
||||
"@img/sharp-libvips-linuxmusl-x64": "npm:1.2.4"
|
||||
dependenciesMeta:
|
||||
"@img/sharp-libvips-linuxmusl-x64":
|
||||
optional: true
|
||||
conditions: os=linux & cpu=x64 & libc=musl
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@img/sharp-wasm32@npm:0.34.5":
|
||||
version: 0.34.5
|
||||
resolution: "@img/sharp-wasm32@npm:0.34.5"
|
||||
dependencies:
|
||||
"@emnapi/runtime": "npm:^1.7.0"
|
||||
conditions: cpu=wasm32
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@img/sharp-win32-arm64@npm:0.34.5":
|
||||
version: 0.34.5
|
||||
resolution: "@img/sharp-win32-arm64@npm:0.34.5"
|
||||
conditions: os=win32 & cpu=arm64
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@img/sharp-win32-ia32@npm:0.34.5":
|
||||
version: 0.34.5
|
||||
resolution: "@img/sharp-win32-ia32@npm:0.34.5"
|
||||
conditions: os=win32 & cpu=ia32
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@img/sharp-win32-x64@npm:0.34.5":
|
||||
version: 0.34.5
|
||||
resolution: "@img/sharp-win32-x64@npm:0.34.5"
|
||||
conditions: os=win32 & cpu=x64
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@ioredis/commands@npm:1.5.1":
|
||||
version: 1.5.1
|
||||
resolution: "@ioredis/commands@npm:1.5.1"
|
||||
|
|
@ -2967,7 +3194,7 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"detect-libc@npm:^2.0.0":
|
||||
"detect-libc@npm:^2.0.0, detect-libc@npm:^2.1.2":
|
||||
version: 2.1.2
|
||||
resolution: "detect-libc@npm:2.1.2"
|
||||
checksum: 10c0/acc675c29a5649fa1fb6e255f993b8ee829e510b6b56b0910666949c80c364738833417d0edb5f90e4e46be17228b0f2b66a010513984e18b15deeeac49369c4
|
||||
|
|
@ -4587,6 +4814,7 @@ __metadata:
|
|||
prettier: "npm:^3.8.1"
|
||||
prism-media: "npm:^1.3.5"
|
||||
prisma: "npm:7.6.0"
|
||||
sharp: "npm:^0.34.5"
|
||||
ts-jest: "npm:^29.4.6"
|
||||
tsx: "npm:^4.21.0"
|
||||
typescript: "npm:^6.0.2"
|
||||
|
|
@ -5743,6 +5971,90 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"sharp@npm:^0.34.5":
|
||||
version: 0.34.5
|
||||
resolution: "sharp@npm:0.34.5"
|
||||
dependencies:
|
||||
"@img/colour": "npm:^1.0.0"
|
||||
"@img/sharp-darwin-arm64": "npm:0.34.5"
|
||||
"@img/sharp-darwin-x64": "npm:0.34.5"
|
||||
"@img/sharp-libvips-darwin-arm64": "npm:1.2.4"
|
||||
"@img/sharp-libvips-darwin-x64": "npm:1.2.4"
|
||||
"@img/sharp-libvips-linux-arm": "npm:1.2.4"
|
||||
"@img/sharp-libvips-linux-arm64": "npm:1.2.4"
|
||||
"@img/sharp-libvips-linux-ppc64": "npm:1.2.4"
|
||||
"@img/sharp-libvips-linux-riscv64": "npm:1.2.4"
|
||||
"@img/sharp-libvips-linux-s390x": "npm:1.2.4"
|
||||
"@img/sharp-libvips-linux-x64": "npm:1.2.4"
|
||||
"@img/sharp-libvips-linuxmusl-arm64": "npm:1.2.4"
|
||||
"@img/sharp-libvips-linuxmusl-x64": "npm:1.2.4"
|
||||
"@img/sharp-linux-arm": "npm:0.34.5"
|
||||
"@img/sharp-linux-arm64": "npm:0.34.5"
|
||||
"@img/sharp-linux-ppc64": "npm:0.34.5"
|
||||
"@img/sharp-linux-riscv64": "npm:0.34.5"
|
||||
"@img/sharp-linux-s390x": "npm:0.34.5"
|
||||
"@img/sharp-linux-x64": "npm:0.34.5"
|
||||
"@img/sharp-linuxmusl-arm64": "npm:0.34.5"
|
||||
"@img/sharp-linuxmusl-x64": "npm:0.34.5"
|
||||
"@img/sharp-wasm32": "npm:0.34.5"
|
||||
"@img/sharp-win32-arm64": "npm:0.34.5"
|
||||
"@img/sharp-win32-ia32": "npm:0.34.5"
|
||||
"@img/sharp-win32-x64": "npm:0.34.5"
|
||||
detect-libc: "npm:^2.1.2"
|
||||
semver: "npm:^7.7.3"
|
||||
dependenciesMeta:
|
||||
"@img/sharp-darwin-arm64":
|
||||
optional: true
|
||||
"@img/sharp-darwin-x64":
|
||||
optional: true
|
||||
"@img/sharp-libvips-darwin-arm64":
|
||||
optional: true
|
||||
"@img/sharp-libvips-darwin-x64":
|
||||
optional: true
|
||||
"@img/sharp-libvips-linux-arm":
|
||||
optional: true
|
||||
"@img/sharp-libvips-linux-arm64":
|
||||
optional: true
|
||||
"@img/sharp-libvips-linux-ppc64":
|
||||
optional: true
|
||||
"@img/sharp-libvips-linux-riscv64":
|
||||
optional: true
|
||||
"@img/sharp-libvips-linux-s390x":
|
||||
optional: true
|
||||
"@img/sharp-libvips-linux-x64":
|
||||
optional: true
|
||||
"@img/sharp-libvips-linuxmusl-arm64":
|
||||
optional: true
|
||||
"@img/sharp-libvips-linuxmusl-x64":
|
||||
optional: true
|
||||
"@img/sharp-linux-arm":
|
||||
optional: true
|
||||
"@img/sharp-linux-arm64":
|
||||
optional: true
|
||||
"@img/sharp-linux-ppc64":
|
||||
optional: true
|
||||
"@img/sharp-linux-riscv64":
|
||||
optional: true
|
||||
"@img/sharp-linux-s390x":
|
||||
optional: true
|
||||
"@img/sharp-linux-x64":
|
||||
optional: true
|
||||
"@img/sharp-linuxmusl-arm64":
|
||||
optional: true
|
||||
"@img/sharp-linuxmusl-x64":
|
||||
optional: true
|
||||
"@img/sharp-wasm32":
|
||||
optional: true
|
||||
"@img/sharp-win32-arm64":
|
||||
optional: true
|
||||
"@img/sharp-win32-ia32":
|
||||
optional: true
|
||||
"@img/sharp-win32-x64":
|
||||
optional: true
|
||||
checksum: 10c0/fd79e29df0597a7d5704b8461c51f944ead91a5243691697be6e8243b966402beda53ddc6f0a53b96ea3cb8221f0b244aa588114d3ebf8734fb4aefd41ab802f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"shebang-command@npm:^2.0.0":
|
||||
version: 2.0.0
|
||||
resolution: "shebang-command@npm:2.0.0"
|
||||
|
|
|
|||
Loading…
Reference in New Issue