chore: upgrade Expo SDK 55 → 57 and React Native 0.83 → 0.86

This commit is contained in:
2026-07-12 16:55:34 -04:00
parent 553fe98013
commit 5cf0293220
57 changed files with 2111 additions and 1569 deletions
+1 -1
View File
@@ -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:*",
+2 -2
View File
@@ -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:"
},
-1
View File
@@ -10,7 +10,6 @@ import {
getInProgressTitleIds,
getMovieWatchCountSince,
getMovieWatchHistoryBuckets,
getNewAvailableFeed,
getRecommendationRows,
getRecommendationRowsForTitle,
getSeasonsByTitleIds,
+3 -3
View File
@@ -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);
+3 -3
View File
@@ -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]);
+3 -1
View File
@@ -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", () => ({
+6 -2
View File
@@ -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>[],
})),
+2 -3
View File
@@ -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" }),
),
}));
+1 -1
View File
@@ -40,7 +40,7 @@ const defaultSeasonDetails: MockSeasonDetails = {
};
const { mockGetTvSeasonDetails } = vi.hoisted(() => ({
mockGetTvSeasonDetails: vi.fn(),
mockGetTvSeasonDetails: vi.fn<() => Promise<MockSeasonDetails>>(),
}));
vi.mock("@sofa/tmdb/client", () => ({
+3 -3
View File
@@ -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", () => ({
+1 -1
View File
@@ -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",
);
+7 -5
View File
@@ -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
View File
@@ -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 ──────────────────────────────────────────────
+1 -1
View File
@@ -9,7 +9,7 @@
},
"dependencies": {
"@sofa/db": "workspace:*",
"better-sqlite3": "12.8.0",
"better-sqlite3": "12.11.1",
"drizzle-orm": "catalog:"
},
"devDependencies": {
+1 -1
View File
@@ -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(