feat: add fgo to games.
This commit is contained in:
@@ -0,0 +1,149 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"id": "fgo:fate-grand-order-fes-2026-chaldea-academy-festival-11th-anniversary-main-quest-c:2026-07-22",
|
||||||
|
"game": "fgo",
|
||||||
|
"title": "Fate/Grand Order Fes. 2026 Chaldea Academy Festival ~11th Anniversary~ Main Quest Completion Support Campaign",
|
||||||
|
"type": "story",
|
||||||
|
"summary": null,
|
||||||
|
"startsAt": "2026-07-22T00:00:00.000Z",
|
||||||
|
"startPrecision": "day",
|
||||||
|
"endsAt": "2026-08-31T00:00:00.000Z",
|
||||||
|
"endPrecision": "day",
|
||||||
|
"regionScoped": false,
|
||||||
|
"regionEnds": null,
|
||||||
|
"sourceUrl": "https://fategrandorder.fandom.com/wiki/Fate/Grand_Order_Fes._2026_Chaldea_Academy_Festival_~11th_Anniversary~_Main_Quest_Completion_Support_Campaign",
|
||||||
|
"sourceId": "fgo-fandom-events",
|
||||||
|
"status": "published",
|
||||||
|
"confidence": 0.95,
|
||||||
|
"extractionMethod": "parser",
|
||||||
|
"version": 1,
|
||||||
|
"firstSeenAt": "2026-08-14T00:00:00.000Z",
|
||||||
|
"updatedAt": "2026-08-14T00:00:00.000Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "fgo:fate-grand-order-11th-anniversary-summoning-campaign:2026-08-02",
|
||||||
|
"game": "fgo",
|
||||||
|
"title": "Fate/Grand Order ~11th Anniversary~ Summoning Campaign",
|
||||||
|
"type": "other",
|
||||||
|
"summary": null,
|
||||||
|
"startsAt": "2026-08-02T00:00:00.000Z",
|
||||||
|
"startPrecision": "day",
|
||||||
|
"endsAt": "2026-08-23T00:00:00.000Z",
|
||||||
|
"endPrecision": "day",
|
||||||
|
"regionScoped": false,
|
||||||
|
"regionEnds": null,
|
||||||
|
"sourceUrl": "https://fategrandorder.fandom.com/wiki/Fate/Grand_Order_%EF%BD%9E11th_Anniversary%EF%BD%9E_Summoning_Campaign",
|
||||||
|
"sourceId": "fgo-fandom-events",
|
||||||
|
"status": "published",
|
||||||
|
"confidence": 0.95,
|
||||||
|
"extractionMethod": "parser",
|
||||||
|
"version": 1,
|
||||||
|
"firstSeenAt": "2026-08-14T00:00:00.000Z",
|
||||||
|
"updatedAt": "2026-08-14T00:00:00.000Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "fgo:chaldea-great-southern-sea-showdown-summoning-campaign-1:2026-08-12",
|
||||||
|
"game": "fgo",
|
||||||
|
"title": "Chaldea Great Southern Sea Showdown!/Summoning Campaign 1",
|
||||||
|
"type": "other",
|
||||||
|
"summary": null,
|
||||||
|
"startsAt": "2026-08-12T00:00:00.000Z",
|
||||||
|
"startPrecision": "day",
|
||||||
|
"endsAt": "2026-09-02T00:00:00.000Z",
|
||||||
|
"endPrecision": "day",
|
||||||
|
"regionScoped": false,
|
||||||
|
"regionEnds": null,
|
||||||
|
"sourceUrl": "https://fategrandorder.fandom.com/wiki/Chaldea_Great_Southern_Sea_Showdown!/Summoning_Campaign_1",
|
||||||
|
"sourceId": "fgo-fandom-events",
|
||||||
|
"status": "published",
|
||||||
|
"confidence": 0.95,
|
||||||
|
"extractionMethod": "parser",
|
||||||
|
"version": 1,
|
||||||
|
"firstSeenAt": "2026-08-14T00:00:00.000Z",
|
||||||
|
"updatedAt": "2026-08-14T00:00:00.000Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "fgo:chaldea-great-southern-sea-showdown-summoning-campaign-male:2026-08-12",
|
||||||
|
"game": "fgo",
|
||||||
|
"title": "Chaldea Great Southern Sea Showdown!/Summoning Campaign (Male)",
|
||||||
|
"type": "other",
|
||||||
|
"summary": null,
|
||||||
|
"startsAt": "2026-08-12T00:00:00.000Z",
|
||||||
|
"startPrecision": "day",
|
||||||
|
"endsAt": "2026-09-02T00:00:00.000Z",
|
||||||
|
"endPrecision": "day",
|
||||||
|
"regionScoped": false,
|
||||||
|
"regionEnds": null,
|
||||||
|
"sourceUrl": "https://fategrandorder.fandom.com/wiki/Chaldea_Great_Southern_Sea_Showdown!/Summoning_Campaign_(Male)",
|
||||||
|
"sourceId": "fgo-fandom-events",
|
||||||
|
"status": "published",
|
||||||
|
"confidence": 0.95,
|
||||||
|
"extractionMethod": "parser",
|
||||||
|
"version": 1,
|
||||||
|
"firstSeenAt": "2026-08-14T00:00:00.000Z",
|
||||||
|
"updatedAt": "2026-08-14T00:00:00.000Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "fgo:chaldea-great-southern-sea-showdown:2026-08-12",
|
||||||
|
"game": "fgo",
|
||||||
|
"title": "Chaldea Great Southern Sea Showdown!",
|
||||||
|
"type": "other",
|
||||||
|
"summary": null,
|
||||||
|
"startsAt": "2026-08-12T00:00:00.000Z",
|
||||||
|
"startPrecision": "day",
|
||||||
|
"endsAt": "2026-09-02T00:00:00.000Z",
|
||||||
|
"endPrecision": "day",
|
||||||
|
"regionScoped": false,
|
||||||
|
"regionEnds": null,
|
||||||
|
"sourceUrl": "https://fategrandorder.fandom.com/wiki/Chaldea_Great_Southern_Sea_Showdown!",
|
||||||
|
"sourceId": "fgo-fandom-events",
|
||||||
|
"status": "published",
|
||||||
|
"confidence": 0.95,
|
||||||
|
"extractionMethod": "parser",
|
||||||
|
"version": 1,
|
||||||
|
"firstSeenAt": "2026-08-14T00:00:00.000Z",
|
||||||
|
"updatedAt": "2026-08-14T00:00:00.000Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "fgo:chaldea-great-southern-sea-showdown-summoning-campaign-2:2026-08-14",
|
||||||
|
"game": "fgo",
|
||||||
|
"title": "Chaldea Great Southern Sea Showdown!/Summoning Campaign 2",
|
||||||
|
"type": "other",
|
||||||
|
"summary": null,
|
||||||
|
"startsAt": "2026-08-14T00:00:00.000Z",
|
||||||
|
"startPrecision": "day",
|
||||||
|
"endsAt": "2026-09-02T00:00:00.000Z",
|
||||||
|
"endPrecision": "day",
|
||||||
|
"regionScoped": false,
|
||||||
|
"regionEnds": null,
|
||||||
|
"sourceUrl": "https://fategrandorder.fandom.com/wiki/Chaldea_Great_Southern_Sea_Showdown!/Summoning_Campaign_2",
|
||||||
|
"sourceId": "fgo-fandom-events",
|
||||||
|
"status": "published",
|
||||||
|
"confidence": 0.95,
|
||||||
|
"extractionMethod": "parser",
|
||||||
|
"version": 1,
|
||||||
|
"firstSeenAt": "2026-08-14T00:00:00.000Z",
|
||||||
|
"updatedAt": "2026-08-14T00:00:00.000Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "fgo:chaldea-great-southern-sea-showdown-summoning-campaign-3:2026-08-14",
|
||||||
|
"game": "fgo",
|
||||||
|
"title": "Chaldea Great Southern Sea Showdown!/Summoning Campaign 3",
|
||||||
|
"type": "other",
|
||||||
|
"summary": null,
|
||||||
|
"startsAt": "2026-08-14T00:00:00.000Z",
|
||||||
|
"startPrecision": "day",
|
||||||
|
"endsAt": "2026-09-02T00:00:00.000Z",
|
||||||
|
"endPrecision": "day",
|
||||||
|
"regionScoped": false,
|
||||||
|
"regionEnds": null,
|
||||||
|
"sourceUrl": "https://fategrandorder.fandom.com/wiki/Chaldea_Great_Southern_Sea_Showdown!/Summoning_Campaign_3",
|
||||||
|
"sourceId": "fgo-fandom-events",
|
||||||
|
"status": "published",
|
||||||
|
"confidence": 0.95,
|
||||||
|
"extractionMethod": "parser",
|
||||||
|
"version": 1,
|
||||||
|
"firstSeenAt": "2026-08-14T00:00:00.000Z",
|
||||||
|
"updatedAt": "2026-08-14T00:00:00.000Z"
|
||||||
|
}
|
||||||
|
]
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
@@ -106,6 +106,12 @@ const SOURCES: SourceSpec[] = [
|
|||||||
url: "https://bluearchive.wiki/wiki/Events",
|
url: "https://bluearchive.wiki/wiki/Events",
|
||||||
parserId: "bawiki",
|
parserId: "bawiki",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: "fgo-fandom-events",
|
||||||
|
game: "fgo",
|
||||||
|
url: "https://fategrandorder.fandom.com/api.php?action=parse&page=Event_List&prop=text&formatversion=2&format=json",
|
||||||
|
parserId: "fandom",
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
function toAdapter(spec: SourceSpec): Adapter {
|
function toAdapter(spec: SourceSpec): Adapter {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { eventId, type GachaEvent } from "../../shared/schema.ts";
|
import { eventId, type GachaEvent } from "../../shared/schema.ts";
|
||||||
import { parseOrdinalDateTimeRange } from "../dates.ts";
|
import { parseMonthDayRange, parseOpenRange, parseOrdinalDateTimeRange } from "../dates.ts";
|
||||||
import { text } from "../html.ts";
|
import { text } from "../html.ts";
|
||||||
import type { ParseContext } from "../adapters/types.ts";
|
import type { ParseContext } from "../adapters/types.ts";
|
||||||
import { inferType } from "./game8.ts";
|
import { inferType } from "./game8.ts";
|
||||||
@@ -59,6 +59,9 @@ const EDIT_SECTION = /<span\b[^>]*class="[^"]*mw-editsection[^"]*"[^>]*>[\s\S]*?
|
|||||||
*/
|
*/
|
||||||
const ARTICLE_LINK = /<a\b[^>]*href="(\/wiki\/(?!Special:)[^"#?]+)"/i;
|
const ARTICLE_LINK = /<a\b[^>]*href="(\/wiki\/(?!Special:)[^"#?]+)"/i;
|
||||||
|
|
||||||
|
const FGO_TABLE = /<table\b[^>]*id="(\d{2})(\d{4})"[^>]*class="[^"]*wikitable"[^>]*>([\s\S]*?)<\/table>/gi;
|
||||||
|
const FGO_ROW_BLOCK = /<th\b[^>]*colspan="2"[^>]*>([\s\S]*?)<\/th>[\s\S]*?<td\b[^>]*>([\s\S]*?)<\/td>\s*<td\b[^>]*>([\s\S]*?)<\/td>/gi;
|
||||||
|
|
||||||
/** The rendered HTML inside an `action=parse` response, or null. */
|
/** The rendered HTML inside an `action=parse` response, or null. */
|
||||||
export function renderedHtml(body: string): string | null {
|
export function renderedHtml(body: string): string | null {
|
||||||
let payload: unknown;
|
let payload: unknown;
|
||||||
@@ -69,9 +72,155 @@ export function renderedHtml(body: string): string | null {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const html = (payload as { parse?: { text?: unknown } } | null)?.parse?.text;
|
const html = (payload as { parse?: { text?: unknown } } | null)?.parse?.text;
|
||||||
|
if (typeof html === "object" && html !== null && '*' in html) {
|
||||||
|
return (html as any)['*'] as string;
|
||||||
|
}
|
||||||
return typeof html === "string" ? html : null;
|
return typeof html === "string" ? html : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function parseFgoEventsPage(rendered: string, ctx: ParseContext): GachaEvent[] {
|
||||||
|
const flat = rendered.replace(EDIT_SECTION, "").replace(/\s+/g, " ");
|
||||||
|
const nowMs = Date.parse(ctx.now);
|
||||||
|
const out: GachaEvent[] = [];
|
||||||
|
|
||||||
|
const ONGOING_BLOCK = /<h2\b[^>]*>[\s\S]*?<a\b[^>]*href="(\/wiki\/(?!Special:)[^"#?]+)"[^>]*>([\s\S]*?)<\/a>[\s\S]*?<\/h2>[\s\S]*?<b>Duration:<\/b>\s*([\s\S]*?)(?:<\/p>|<br\s*\/?>)/gi;
|
||||||
|
|
||||||
|
for (const match of flat.matchAll(ONGOING_BLOCK)) {
|
||||||
|
const href = match[1] ?? "";
|
||||||
|
const titleHtml = match[2] ?? "";
|
||||||
|
const title = text(titleHtml).trim();
|
||||||
|
if (!title || /permanent/i.test(title)) continue;
|
||||||
|
|
||||||
|
let rawDate = text(match[3] ?? "").replace(/ /g, " ").replace(/~/g, "-").replace(/JST/i, "").trim();
|
||||||
|
if (!rawDate) continue;
|
||||||
|
|
||||||
|
rawDate = rawDate.replace(/(,\s*\d{4})\s*-/, " -");
|
||||||
|
|
||||||
|
const parts = rawDate.split(/[-–—]/).map((s) => s.trim());
|
||||||
|
let start: any = null;
|
||||||
|
let end: any = null;
|
||||||
|
|
||||||
|
if (parts.length === 1 && parts[0]) {
|
||||||
|
const openMatch = parseOpenRange(parts[0]);
|
||||||
|
if (openMatch) {
|
||||||
|
start = openMatch.start;
|
||||||
|
end = null;
|
||||||
|
}
|
||||||
|
} else if (parts.length >= 2 && parts[0] && parts[1]) {
|
||||||
|
const rangeMatch = parseMonthDayRange(rawDate);
|
||||||
|
if (rangeMatch) {
|
||||||
|
start = rangeMatch.start;
|
||||||
|
end = rangeMatch.end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!start) continue;
|
||||||
|
if (end && end.iso <= start.iso) continue;
|
||||||
|
const endMs = end ? Date.parse(end.iso) : Date.parse(start.iso) + 86400000;
|
||||||
|
if (endMs < nowMs) continue;
|
||||||
|
|
||||||
|
out.push({
|
||||||
|
id: eventId(ctx.game, title, start.iso),
|
||||||
|
game: ctx.game,
|
||||||
|
title,
|
||||||
|
type: inferType(title),
|
||||||
|
summary: null,
|
||||||
|
startsAt: start.iso,
|
||||||
|
startPrecision: start.precision,
|
||||||
|
endsAt: end ? end.iso : null,
|
||||||
|
endPrecision: end ? end.precision : "unknown",
|
||||||
|
regionScoped: false,
|
||||||
|
regionEnds: null,
|
||||||
|
sourceUrl: new URL(href, ctx.sourceUrl).toString(),
|
||||||
|
sourceId: ctx.sourceId,
|
||||||
|
status: "published",
|
||||||
|
confidence: 0.95,
|
||||||
|
extractionMethod: "parser",
|
||||||
|
version: 1,
|
||||||
|
firstSeenAt: ctx.now,
|
||||||
|
updatedAt: ctx.now,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const tableMatch of flat.matchAll(FGO_TABLE)) {
|
||||||
|
const tableYear = parseInt(tableMatch[2]!, 10);
|
||||||
|
const innerHtml = tableMatch[3] ?? "";
|
||||||
|
|
||||||
|
for (const blockMatch of innerHtml.matchAll(FGO_ROW_BLOCK)) {
|
||||||
|
const thHtml = blockMatch[1] ?? "";
|
||||||
|
const imgTdHtml = blockMatch[2] ?? "";
|
||||||
|
const dateTdHtml = blockMatch[3] ?? "";
|
||||||
|
|
||||||
|
const title = text(thHtml).trim();
|
||||||
|
if (!title) continue;
|
||||||
|
|
||||||
|
let rawDate = text(dateTdHtml).replace(/ /g, " ").replace(/~/g, "-").trim();
|
||||||
|
if (!rawDate) continue;
|
||||||
|
|
||||||
|
const parts = rawDate.split(/[-–—]/).map((s) => s.trim());
|
||||||
|
let start: any = null;
|
||||||
|
let end: any = null;
|
||||||
|
|
||||||
|
if (parts.length === 1 && parts[0]) {
|
||||||
|
const openMatch = parseOpenRange(parts[0] + ", " + tableYear);
|
||||||
|
if (openMatch) {
|
||||||
|
start = openMatch.start;
|
||||||
|
end = null;
|
||||||
|
}
|
||||||
|
} else if (parts.length >= 2 && parts[0] && parts[1]) {
|
||||||
|
const mStart = /([A-Za-z]+)/.exec(parts[0])?.[1]?.toLowerCase() || "";
|
||||||
|
const mEnd = /([A-Za-z]+)/.exec(parts[1])?.[1]?.toLowerCase() || "";
|
||||||
|
const monthsList = ["jan","feb","mar","apr","may","jun","jul","aug","sep","oct","nov","dec"];
|
||||||
|
const startM = monthsList.findIndex((x) => mStart.startsWith(x));
|
||||||
|
const endM = monthsList.findIndex((x) => mEnd.startsWith(x));
|
||||||
|
|
||||||
|
let endYear = tableYear;
|
||||||
|
if (startM > -1 && endM > -1 && startM > endM) {
|
||||||
|
endYear = tableYear + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const rangeStr = parts[0] + " - " + parts[1] + ", " + endYear;
|
||||||
|
const rangeMatch = parseMonthDayRange(rangeStr);
|
||||||
|
if (rangeMatch) {
|
||||||
|
start = rangeMatch.start;
|
||||||
|
end = rangeMatch.end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!start) continue;
|
||||||
|
if (/permanent/i.test(title)) continue;
|
||||||
|
if (end && end.iso <= start.iso) continue;
|
||||||
|
const endMs = end ? Date.parse(end.iso) : Date.parse(start.iso) + 86400000;
|
||||||
|
if (endMs < nowMs) continue;
|
||||||
|
|
||||||
|
const href = ARTICLE_LINK.exec(thHtml)?.[1] || ARTICLE_LINK.exec(imgTdHtml)?.[1];
|
||||||
|
|
||||||
|
out.push({
|
||||||
|
id: eventId(ctx.game, title, start.iso),
|
||||||
|
game: ctx.game,
|
||||||
|
title,
|
||||||
|
type: inferType(title),
|
||||||
|
summary: null,
|
||||||
|
startsAt: start.iso,
|
||||||
|
startPrecision: start.precision,
|
||||||
|
endsAt: end ? end.iso : null,
|
||||||
|
endPrecision: end ? end.precision : "unknown",
|
||||||
|
regionScoped: false,
|
||||||
|
regionEnds: null,
|
||||||
|
sourceUrl: href === undefined ? ctx.sourceUrl : new URL(href, ctx.sourceUrl).toString(),
|
||||||
|
sourceId: ctx.sourceId,
|
||||||
|
status: "published",
|
||||||
|
confidence: 0.95,
|
||||||
|
extractionMethod: "parser",
|
||||||
|
version: 1,
|
||||||
|
firstSeenAt: ctx.now,
|
||||||
|
updatedAt: ctx.now,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
export function parseFandomEventsPage(
|
export function parseFandomEventsPage(
|
||||||
body: string,
|
body: string,
|
||||||
ctx: ParseContext,
|
ctx: ParseContext,
|
||||||
@@ -79,6 +228,16 @@ export function parseFandomEventsPage(
|
|||||||
const rendered = renderedHtml(body);
|
const rendered = renderedHtml(body);
|
||||||
if (rendered === null) return [];
|
if (rendered === null) return [];
|
||||||
|
|
||||||
|
// Branch for FGO
|
||||||
|
if (/<table\b[^>]*id="\d{6}"[^>]*class="[^"]*wikitable"/i.test(rendered)) {
|
||||||
|
const events = parseFgoEventsPage(rendered, ctx);
|
||||||
|
return events.sort((a, b) =>
|
||||||
|
a.startsAt === b.startsAt
|
||||||
|
? a.id.localeCompare(b.id)
|
||||||
|
: a.startsAt.localeCompare(b.startsAt),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const flat = rendered.replace(EDIT_SECTION, "").replace(/\s+/g, " ");
|
const flat = rendered.replace(EDIT_SECTION, "").replace(/\s+/g, " ");
|
||||||
const nowMs = Date.parse(ctx.now);
|
const nowMs = Date.parse(ctx.now);
|
||||||
const out: GachaEvent[] = [];
|
const out: GachaEvent[] = [];
|
||||||
@@ -177,7 +336,9 @@ export const fandomParser: SourceParser = {
|
|||||||
// the Cloudflare interstitial the plain page serves would all land here.
|
// the Cloudflare interstitial the plain page serves would all land here.
|
||||||
const rendered = renderedHtml(body);
|
const rendered = renderedHtml(body);
|
||||||
if (rendered === null) return false;
|
if (rendered === null) return false;
|
||||||
return /class="[^"]*wikitable/.test(rendered) && /Time Period/i.test(rendered);
|
const isStandard = /class="[^"]*wikitable/.test(rendered) && /Time Period/i.test(rendered);
|
||||||
|
const isFgo = /<table\b[^>]*id="\d{6}"[^>]*class="[^"]*wikitable"/i.test(rendered);
|
||||||
|
return isStandard || isFgo;
|
||||||
},
|
},
|
||||||
parse: parseFandomEventsPage,
|
parse: parseFandomEventsPage,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -94,6 +94,7 @@ export const GAMES: Record<GameId, GameMeta> = {
|
|||||||
// has to come from a source that states the clock. Inventing one moves real
|
// has to come from a source that states the clock. Inventing one moves real
|
||||||
// readers' day keys, and a wrong guess ticks the wrong box every night.
|
// readers' day keys, and a wrong guess ticks the wrong box every night.
|
||||||
ba: { id: "ba", name: "Blue Archive", short: "Blue Archive", hue: "#3FCBDD" , studio: "Nexon Games", dailyTasks: "Daily missions, AP" },
|
ba: { id: "ba", name: "Blue Archive", short: "Blue Archive", hue: "#3FCBDD" , studio: "Nexon Games", dailyTasks: "Daily missions, AP" },
|
||||||
|
fgo: { id: "fgo", name: "Fate/Grand Order", short: "FGO", hue: "#1D3A8F", studio: "Lasengle", dailyTasks: "Daily missions, AP" },
|
||||||
};
|
};
|
||||||
|
|
||||||
export const GAME_LIST: GameMeta[] = Object.values(GAMES);
|
export const GAME_LIST: GameMeta[] = Object.values(GAMES);
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ export const GameId = z.enum([
|
|||||||
"p5x", // Persona 5: The Phantom X
|
"p5x", // Persona 5: The Phantom X
|
||||||
"r1999", // Reverse: 1999
|
"r1999", // Reverse: 1999
|
||||||
"ba", // Blue Archive
|
"ba", // Blue Archive
|
||||||
|
"fgo", // Fate/Grand Order
|
||||||
]);
|
]);
|
||||||
export type GameId = z.infer<typeof GameId>;
|
export type GameId = z.infer<typeof GameId>;
|
||||||
|
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ const CASES: Array<{ adapter: Adapter; fixture: string }> = [
|
|||||||
// its content type. The fixture is the bytes the fetcher would store.
|
// its content type. The fixture is the bytes the fetcher would store.
|
||||||
{ adapter: adapter("r1999-fandom-events"), fixture: "fixtures/r1999/fandom-events-2026-08-17" },
|
{ adapter: adapter("r1999-fandom-events"), fixture: "fixtures/r1999/fandom-events-2026-08-17" },
|
||||||
{ adapter: adapter("ba-bawiki-events"), fixture: "fixtures/ba/bawiki-events-2026-08-17" },
|
{ adapter: adapter("ba-bawiki-events"), fixture: "fixtures/ba/bawiki-events-2026-08-17" },
|
||||||
|
{ adapter: adapter("fgo-fandom-events"), fixture: "fixtures/fgo/fandom-events-2026-08-18" },
|
||||||
];
|
];
|
||||||
|
|
||||||
async function runAdapter(adapter: Adapter, fixture: string) {
|
async function runAdapter(adapter: Adapter, fixture: string) {
|
||||||
|
|||||||
Reference in New Issue
Block a user