mirror of
https://github.com/jakejarvis/sofa.git
synced 2026-08-29 03:55:38 -04:00
chore: upgrade Expo SDK 55 → 57 and React Native 0.83 → 0.86
This commit is contained in:
@@ -14,7 +14,7 @@
|
||||
"check-types": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@better-auth/drizzle-adapter": "1.5.6",
|
||||
"@better-auth/drizzle-adapter": "1.6.23",
|
||||
"@better-auth/expo": "catalog:",
|
||||
"@sofa/core": "workspace:*",
|
||||
"@sofa/db": "workspace:*",
|
||||
|
||||
@@ -45,9 +45,9 @@
|
||||
"@sofa/db": "workspace:*",
|
||||
"@sofa/logger": "workspace:*",
|
||||
"@sofa/tmdb": "workspace:*",
|
||||
"adm-zip": "0.5.16",
|
||||
"adm-zip": "0.5.18",
|
||||
"node-vibrant": "4.0.4",
|
||||
"sharp": "0.34.5",
|
||||
"sharp": "0.35.3",
|
||||
"thumbhash": "catalog:",
|
||||
"zod": "catalog:"
|
||||
},
|
||||
|
||||
@@ -10,7 +10,6 @@ import {
|
||||
getInProgressTitleIds,
|
||||
getMovieWatchCountSince,
|
||||
getMovieWatchHistoryBuckets,
|
||||
getNewAvailableFeed,
|
||||
getRecommendationRows,
|
||||
getRecommendationRowsForTitle,
|
||||
getSeasonsByTitleIds,
|
||||
|
||||
@@ -155,7 +155,7 @@ function fireAndForgetEnrichment(
|
||||
refreshTrailer(titleId).catch((err) => log.warn("Trailer enrichment failed:", err));
|
||||
}
|
||||
|
||||
type ImportResult = ReturnType<typeof _getOrFetchTitleByTmdbId>;
|
||||
type ImportResult = ReturnType<typeof fetchTitleByTmdbId>;
|
||||
|
||||
/** In-flight import promises keyed by `${tmdbId}-${type}` — coalesces concurrent calls */
|
||||
const inflightImports = new Map<string, ImportResult>();
|
||||
@@ -168,14 +168,14 @@ export function getOrFetchTitleByTmdbId(tmdbId: number, type: "movie" | "tv"): I
|
||||
return inflight;
|
||||
}
|
||||
|
||||
const promise = _getOrFetchTitleByTmdbId(tmdbId, type).finally(() => {
|
||||
const promise = fetchTitleByTmdbId(tmdbId, type).finally(() => {
|
||||
inflightImports.delete(key);
|
||||
}) as ImportResult;
|
||||
inflightImports.set(key, promise);
|
||||
return promise;
|
||||
}
|
||||
|
||||
async function _getOrFetchTitleByTmdbId(tmdbId: number, type: "movie" | "tv") {
|
||||
async function fetchTitleByTmdbId(tmdbId: number, type: "movie" | "tv") {
|
||||
log.debug(`Importing ${type} TMDB ${tmdbId}`);
|
||||
|
||||
const existing = getTitleByTmdbIdAndType(tmdbId, type);
|
||||
|
||||
@@ -141,15 +141,15 @@ type JobSchedule = {
|
||||
nextRunAt: string | null;
|
||||
};
|
||||
|
||||
let _getJobSchedules: (() => JobSchedule[]) | null = null;
|
||||
let jobScheduleProvider: (() => JobSchedule[]) | null = null;
|
||||
|
||||
/** Register the job schedule provider (called by the API server on startup). */
|
||||
export function registerJobScheduleProvider(fn: () => JobSchedule[]) {
|
||||
_getJobSchedules = fn;
|
||||
jobScheduleProvider = fn;
|
||||
}
|
||||
|
||||
function getJobsHealth(): SystemHealthData["jobs"] {
|
||||
const schedules = _getJobSchedules?.() ?? [];
|
||||
const schedules = jobScheduleProvider?.() ?? [];
|
||||
const scheduleMap = new Map(schedules.map((s) => [s.jobName, s]));
|
||||
|
||||
const latestByJob = getLatestCronRuns([...JOB_NAMES]);
|
||||
|
||||
@@ -11,7 +11,9 @@ import {
|
||||
} from "@sofa/test/db";
|
||||
|
||||
const { getWatchProviders } = vi.hoisted(() => ({
|
||||
getWatchProviders: vi.fn(async () => ({ results: {} as Record<string, unknown> })),
|
||||
getWatchProviders: vi.fn<() => Promise<{ results: Record<string, unknown> }>>(async () => ({
|
||||
results: {},
|
||||
})),
|
||||
}));
|
||||
|
||||
vi.mock("@sofa/tmdb/client", () => ({
|
||||
|
||||
@@ -4,11 +4,15 @@ import { persons, titleCast } from "@sofa/db/schema";
|
||||
import { clearAllTables, eq, insertTitle, testDb } from "@sofa/test/db";
|
||||
|
||||
const { mockGetMovieCredits, mockGetTvAggregateCredits } = vi.hoisted(() => ({
|
||||
mockGetMovieCredits: vi.fn(async () => ({
|
||||
mockGetMovieCredits: vi.fn<
|
||||
() => Promise<{ cast: Record<string, unknown>[]; crew: Record<string, unknown>[] }>
|
||||
>(async () => ({
|
||||
cast: [] as Record<string, unknown>[],
|
||||
crew: [] as Record<string, unknown>[],
|
||||
})),
|
||||
mockGetTvAggregateCredits: vi.fn(async () => ({
|
||||
mockGetTvAggregateCredits: vi.fn<
|
||||
() => Promise<{ cast: Record<string, unknown>[]; crew: Record<string, unknown>[] }>
|
||||
>(async () => ({
|
||||
cast: [] as Record<string, unknown>[],
|
||||
crew: [] as Record<string, unknown>[],
|
||||
})),
|
||||
|
||||
@@ -9,9 +9,8 @@ import {
|
||||
} from "@sofa/test/db";
|
||||
|
||||
const { mockGetTvExternalIds } = vi.hoisted(() => ({
|
||||
mockGetTvExternalIds: vi.fn(
|
||||
(): Promise<{ tvdb_id: number | null; imdb_id: string | null }> =>
|
||||
Promise.resolve({ tvdb_id: 55555, imdb_id: "tt9999999" }),
|
||||
mockGetTvExternalIds: vi.fn<() => Promise<{ tvdb_id: number | null; imdb_id: string | null }>>(
|
||||
() => Promise.resolve({ tvdb_id: 55555, imdb_id: "tt9999999" }),
|
||||
),
|
||||
}));
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ const defaultSeasonDetails: MockSeasonDetails = {
|
||||
};
|
||||
|
||||
const { mockGetTvSeasonDetails } = vi.hoisted(() => ({
|
||||
mockGetTvSeasonDetails: vi.fn(),
|
||||
mockGetTvSeasonDetails: vi.fn<() => Promise<MockSeasonDetails>>(),
|
||||
}));
|
||||
|
||||
vi.mock("@sofa/tmdb/client", () => ({
|
||||
|
||||
@@ -4,7 +4,7 @@ import { personFilmography, persons } from "@sofa/db/schema";
|
||||
import { clearAllTables, eq, testDb } from "@sofa/test/db";
|
||||
|
||||
const TINY_PNG = Buffer.from(
|
||||
"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQIHWP4//8/AwAI/AL+X2NDNwAAAABJRU5ErkJggg==",
|
||||
"iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAACXBIWXMAAAPoAAAD6AG1e1JrAAAAEUlEQVQImWP4z8DwH4QZYAwAR8oH+Xm0fdIAAAAASUVORK5CYII=",
|
||||
"base64",
|
||||
);
|
||||
|
||||
@@ -43,8 +43,8 @@ const defaultCombinedCredits = {
|
||||
};
|
||||
|
||||
const { mockGetPersonDetails, mockGetPersonCombinedCredits } = vi.hoisted(() => ({
|
||||
mockGetPersonDetails: vi.fn(),
|
||||
mockGetPersonCombinedCredits: vi.fn(),
|
||||
mockGetPersonDetails: vi.fn<() => Promise<typeof defaultPersonDetails>>(),
|
||||
mockGetPersonCombinedCredits: vi.fn<() => Promise<typeof defaultCombinedCredits>>(),
|
||||
}));
|
||||
|
||||
vi.mock("@sofa/tmdb/client", () => ({
|
||||
|
||||
@@ -4,7 +4,7 @@ import { episodes, seasons, titles } from "@sofa/db/schema";
|
||||
import { clearAllTables, eq, testDb } from "@sofa/test/db";
|
||||
|
||||
const TINY_PNG = Buffer.from(
|
||||
"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQIHWP4//8/AwAI/AL+X2NDNwAAAABJRU5ErkJggg==",
|
||||
"iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAACXBIWXMAAAPoAAAD6AG1e1JrAAAAEUlEQVQImWP4z8DwH4QZYAwAR8oH+Xm0fdIAAAAASUVORK5CYII=",
|
||||
"base64",
|
||||
);
|
||||
|
||||
|
||||
@@ -18,8 +18,8 @@ import {
|
||||
} from "../src/webhooks";
|
||||
|
||||
const { mockResolveMovieTmdbId, mockResolveShowTmdbId } = vi.hoisted(() => ({
|
||||
mockResolveMovieTmdbId: vi.fn(async () => null as number | null),
|
||||
mockResolveShowTmdbId: vi.fn(async () => null as number | null),
|
||||
mockResolveMovieTmdbId: vi.fn<() => Promise<number | null>>(async () => null),
|
||||
mockResolveShowTmdbId: vi.fn<() => Promise<number | null>>(async () => null),
|
||||
}));
|
||||
|
||||
vi.mock("../src/imports/resolve", () => ({
|
||||
@@ -28,8 +28,8 @@ vi.mock("../src/imports/resolve", () => ({
|
||||
}));
|
||||
|
||||
const { mockGetOrFetchTitleByTmdbId, mockRefreshTvChildren } = vi.hoisted(() => ({
|
||||
mockGetOrFetchTitleByTmdbId: vi.fn(async () => null as { id: string } | null),
|
||||
mockRefreshTvChildren: vi.fn(async () => {}),
|
||||
mockGetOrFetchTitleByTmdbId: vi.fn<() => Promise<{ id: string } | null>>(async () => null),
|
||||
mockRefreshTvChildren: vi.fn<() => Promise<void>>(async () => {}),
|
||||
}));
|
||||
|
||||
vi.mock("../src/metadata", () => ({
|
||||
@@ -38,7 +38,9 @@ vi.mock("../src/metadata", () => ({
|
||||
}));
|
||||
|
||||
const { mockGetTvDetails } = vi.hoisted(() => ({
|
||||
mockGetTvDetails: vi.fn(async () => ({ number_of_seasons: 1 })),
|
||||
mockGetTvDetails: vi.fn<() => Promise<{ number_of_seasons: number }>>(async () => ({
|
||||
number_of_seasons: 1,
|
||||
})),
|
||||
}));
|
||||
|
||||
vi.mock("@sofa/tmdb/client", () => ({
|
||||
|
||||
+23
-24
@@ -39,44 +39,43 @@ export class DatabaseRestoreInProgressError extends Error {
|
||||
// Use `closeDatabase()` for graceful shutdown instead.
|
||||
|
||||
const globalForDb = globalThis as unknown as {
|
||||
_db: ReturnType<typeof drizzle> | undefined;
|
||||
_client: Database | undefined;
|
||||
_accessBlocked: boolean | undefined;
|
||||
db: ReturnType<typeof drizzle> | undefined;
|
||||
client: Database | undefined;
|
||||
accessBlocked: boolean | undefined;
|
||||
};
|
||||
|
||||
const dbAccessBypass = new AsyncLocalStorage<boolean>();
|
||||
|
||||
function assertDatabaseAccessible() {
|
||||
if (globalForDb._accessBlocked && !dbAccessBypass.getStore()) {
|
||||
if (globalForDb.accessBlocked && !dbAccessBypass.getStore()) {
|
||||
throw new DatabaseRestoreInProgressError();
|
||||
}
|
||||
}
|
||||
|
||||
function getClient() {
|
||||
assertDatabaseAccessible();
|
||||
if (!globalForDb._client) {
|
||||
globalForDb._client = new Database(DATABASE_URL);
|
||||
globalForDb._client.run("PRAGMA journal_mode = WAL");
|
||||
globalForDb._client.run("PRAGMA foreign_keys = ON");
|
||||
globalForDb._client.run("PRAGMA busy_timeout = 5000");
|
||||
globalForDb._client.run("PRAGMA synchronous = NORMAL");
|
||||
globalForDb._client.run("PRAGMA cache_size = -64000");
|
||||
globalForDb._client.run("PRAGMA temp_store = MEMORY");
|
||||
globalForDb._client.run("PRAGMA mmap_size = 268435456");
|
||||
if (!globalForDb.client) {
|
||||
globalForDb.client = new Database(DATABASE_URL);
|
||||
globalForDb.client.run("PRAGMA journal_mode = WAL");
|
||||
globalForDb.client.run("PRAGMA foreign_keys = ON");
|
||||
globalForDb.client.run("PRAGMA busy_timeout = 5000");
|
||||
globalForDb.client.run("PRAGMA synchronous = NORMAL");
|
||||
globalForDb.client.run("PRAGMA cache_size = -64000");
|
||||
globalForDb.client.run("PRAGMA temp_store = MEMORY");
|
||||
globalForDb.client.run("PRAGMA mmap_size = 268435456");
|
||||
}
|
||||
return globalForDb._client;
|
||||
return globalForDb.client;
|
||||
}
|
||||
|
||||
function getDb() {
|
||||
assertDatabaseAccessible();
|
||||
if (!globalForDb._db) {
|
||||
globalForDb._db = drizzle({
|
||||
if (!globalForDb.db) {
|
||||
globalForDb.db = drizzle({
|
||||
client: getClient(),
|
||||
schema,
|
||||
logger: drizzleLogger,
|
||||
});
|
||||
}
|
||||
return globalForDb._db;
|
||||
return globalForDb.db;
|
||||
}
|
||||
|
||||
export const db = new Proxy({} as ReturnType<typeof drizzle>, {
|
||||
@@ -97,23 +96,23 @@ export function vacuumDatabase(into: string): void {
|
||||
}
|
||||
|
||||
export function isDatabaseAccessBlocked(): boolean {
|
||||
return globalForDb._accessBlocked === true;
|
||||
return globalForDb.accessBlocked === true;
|
||||
}
|
||||
|
||||
export async function withDatabaseAccessBlocked<T>(fn: () => Promise<T> | T): Promise<T> {
|
||||
globalForDb._accessBlocked = true;
|
||||
globalForDb.accessBlocked = true;
|
||||
try {
|
||||
return await dbAccessBypass.run(true, fn);
|
||||
} finally {
|
||||
globalForDb._accessBlocked = false;
|
||||
globalForDb.accessBlocked = false;
|
||||
}
|
||||
}
|
||||
|
||||
/** Close the current connection, and clear singletons so the Proxy re-initializes on next access. */
|
||||
export function closeDatabase() {
|
||||
globalForDb._client?.close();
|
||||
globalForDb._client = undefined;
|
||||
globalForDb._db = undefined;
|
||||
globalForDb.client?.close();
|
||||
globalForDb.client = undefined;
|
||||
globalForDb.db = undefined;
|
||||
}
|
||||
|
||||
// ─── Backup validation ──────────────────────────────────────────────
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@sofa/db": "workspace:*",
|
||||
"better-sqlite3": "12.8.0",
|
||||
"better-sqlite3": "12.11.1",
|
||||
"drizzle-orm": "catalog:"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -26,7 +26,7 @@ const {
|
||||
|
||||
export const testClient = new Database(":memory:");
|
||||
testClient.pragma("foreign_keys = ON");
|
||||
export const testDb = drizzle({ client: testClient, schema });
|
||||
export const testDb = drizzle({ client: testClient });
|
||||
|
||||
export function applyMigrations() {
|
||||
const dbPkgDir = path.resolve(
|
||||
|
||||
Reference in New Issue
Block a user