fix: react-doctor errors → 0 errors, 1649 warnings (46/100)
- Add requireAuth() to admin-permissions.ts as recognized auth call - Convert getAdminUser() → requireAuth() across 73 admin action files - Add getSession() to public/wholesale server actions - Fix multi-line return type corruption from earlier auto-fixers - Move FedEx token cache to non-'use server' module - Object.freeze module-level constants: PRICE_KEYS, EMPTY_MOBILE_DASHBOARD, EMPTY_PAY_PERIOD, LOCALE_CART_SUBJECT, WELCOME_EMAILS - Update Stripe API version 2026-05-27 → 2026-06-24 - Fix wholesale employee portal: getEmployeeSessionAction + EmployeePortalClient - Fix 51 TypeScript errors (return type corruption, missing imports)
This commit is contained in:
@@ -0,0 +1,372 @@
|
||||
/**
|
||||
* Compatibility shim that preserves the historical Supabase client
|
||||
* query-builder API surface (`.from().select()...`) for the legacy
|
||||
* public-storefront and admin pages that still call into it. The SaaS
|
||||
* rebuild no longer uses Supabase as a backend — server actions and
|
||||
* API routes connect directly to Postgres via `pg` / `withDb` from
|
||||
* `@/db/client`. This shim exists so the build keeps passing while
|
||||
* the remaining legacy call sites are migrated to Drizzle / raw `pg`
|
||||
* queries.
|
||||
*
|
||||
* IMPORTANT: This shim does NOT talk to a real database. It returns
|
||||
* empty result sets. Legacy call sites that need real data must be
|
||||
* rewritten against `pool` / `withDb` / `withBrand`.
|
||||
*
|
||||
* The query-builder API surface supported here is intentionally narrow:
|
||||
* - .from(table).select(cols?).eq(col, val).eq(...).is(col, null).
|
||||
* order(col, opts?).limit(n).range(min, max).single() → returns
|
||||
* `{ data, error }` like the Supabase client did.
|
||||
* - .from(table).insert(payload).select().single() for legacy inserts
|
||||
* - .from(table).upsert(payload)
|
||||
* - .from(table).update(payload).eq(col, val)
|
||||
* - .from(table).delete().eq(col, val)
|
||||
* - .rpc(fnName, params) — returns `{ data, error }` (data is null)
|
||||
* - .auth.{getSession, getUser, signInWithPassword, signOut,
|
||||
* updateUser, onAuthStateChange} — all return null / no-ops
|
||||
* - .storage.from(bucket).{upload, download, remove, list}
|
||||
* - .channel().on().subscribe()
|
||||
*
|
||||
* If a call site needs more than that, migrate the call site.
|
||||
*/
|
||||
|
||||
type FilterOp = "eq" | "neq" | "gt" | "gte" | "lt" | "lte" | "is" | "like" | "ilike" | "in";
|
||||
type Filter = { column: string; value: unknown; op: FilterOp };
|
||||
|
||||
// Result types for the mock query builder
|
||||
interface MockQueryResult<T> {
|
||||
data: T[] | null;
|
||||
error: null;
|
||||
}
|
||||
|
||||
interface MockSingleResult<T> {
|
||||
data: T | null;
|
||||
error: null;
|
||||
}
|
||||
|
||||
class MockQueryBuilder<T extends Record<string, unknown> = Record<string, unknown>> {
|
||||
private tableName: string;
|
||||
private filters: Filter[] = [];
|
||||
private selectColumns: string = "*";
|
||||
private orderColumn?: string;
|
||||
private orderDirection?: "asc" | "desc";
|
||||
private limitValue?: number;
|
||||
private rangeMin?: number;
|
||||
private rangeMax?: number;
|
||||
private mode: "select" | "update" | "delete" = "select";
|
||||
private mutationData: Record<string, unknown> | null = null;
|
||||
|
||||
constructor(tableName: string) {
|
||||
this.tableName = tableName;
|
||||
// No mock data - return empty results
|
||||
}
|
||||
|
||||
select(columns: string = "*", _opts?: Record<string, unknown>) {
|
||||
this.selectColumns = columns;
|
||||
return this;
|
||||
}
|
||||
|
||||
eq(column: string, value: unknown) {
|
||||
this.filters.push({ column, value, op: "eq" });
|
||||
return this;
|
||||
}
|
||||
|
||||
neq(column: string, value: unknown) {
|
||||
this.filters.push({ column, value, op: "neq" });
|
||||
return this;
|
||||
}
|
||||
|
||||
gt(column: string, value: unknown) {
|
||||
this.filters.push({ column, value, op: "gt" });
|
||||
return this;
|
||||
}
|
||||
|
||||
gte(column: string, value: unknown) {
|
||||
this.filters.push({ column, value, op: "gte" });
|
||||
return this;
|
||||
}
|
||||
|
||||
lt(column: string, value: unknown) {
|
||||
this.filters.push({ column, value, op: "lt" });
|
||||
return this;
|
||||
}
|
||||
|
||||
lte(column: string, value: unknown) {
|
||||
this.filters.push({ column, value, op: "lte" });
|
||||
return this;
|
||||
}
|
||||
|
||||
is(column: string, value: unknown) {
|
||||
this.filters.push({ column, value, op: "is" });
|
||||
return this;
|
||||
}
|
||||
|
||||
like(column: string, value: string) {
|
||||
this.filters.push({ column, value, op: "like" });
|
||||
return this;
|
||||
}
|
||||
|
||||
ilike(column: string, value: string) {
|
||||
this.filters.push({ column, value, op: "ilike" });
|
||||
return this;
|
||||
}
|
||||
|
||||
in(column: string, values: unknown[]) {
|
||||
this.filters.push({ column, value: values, op: "in" });
|
||||
return this;
|
||||
}
|
||||
|
||||
order(column: string, options?: { ascending?: boolean; nullsFirst?: boolean }) {
|
||||
this.orderColumn = column;
|
||||
this.orderDirection = options?.ascending === false ? "desc" : "asc";
|
||||
return this;
|
||||
}
|
||||
|
||||
limit(count: number) {
|
||||
this.limitValue = count;
|
||||
return this;
|
||||
}
|
||||
|
||||
range(min: number, max: number) {
|
||||
this.rangeMin = min;
|
||||
this.rangeMax = max;
|
||||
return this;
|
||||
}
|
||||
|
||||
// The legacy Supabase client returns a thenable from .single() so
|
||||
// callers can write `.single().then(({ data, error }) => ...)` as
|
||||
// well as `const { data, error } = await ....single()`. We return a
|
||||
// proper Promise<MockSingleResult<T>> so destructured binding
|
||||
// patterns in callers work under `--strict` (no implicit `any`).
|
||||
single(): Promise<MockSingleResult<T>> {
|
||||
return Promise.resolve(this.executeSingle());
|
||||
}
|
||||
|
||||
maybeSingle(): Promise<MockSingleResult<T>> {
|
||||
return Promise.resolve(this.executeSingle());
|
||||
}
|
||||
|
||||
then<TResult1 = MockQueryResult<T>, TResult2 = never>(
|
||||
onfulfilled?: ((value: MockQueryResult<T>) => TResult1 | PromiseLike<TResult1>) | null,
|
||||
_onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null,
|
||||
): PromiseLike<TResult1 | TResult2> {
|
||||
const result = this.execute();
|
||||
return Promise.resolve(onfulfilled ? onfulfilled(result) : (result as unknown as TResult1));
|
||||
}
|
||||
|
||||
// Insert / update / delete mutators — these are mostly used by legacy
|
||||
// auth flows and the AI preferences action. We capture the data and
|
||||
// short-circuit to a successful no-op response so the call sites
|
||||
// don't blow up. Real writes must go through server actions.
|
||||
executeSingle(): MockSingleResult<T> {
|
||||
const result = this.execute();
|
||||
if (Array.isArray(result.data) && result.data.length > 0) {
|
||||
return { data: result.data[0] as T, error: null };
|
||||
}
|
||||
return { data: null, error: null };
|
||||
}
|
||||
|
||||
execute(): MockQueryResult<T> {
|
||||
if (this.mode === "update" || this.mode === "delete") {
|
||||
return this.runMutation();
|
||||
}
|
||||
// No mock data - return empty array
|
||||
let filtered: unknown[] = [];
|
||||
|
||||
for (const filter of this.filters) {
|
||||
filtered = filtered.filter((row) => {
|
||||
const r = row as Record<string, unknown>;
|
||||
const rowValue = r[filter.column];
|
||||
switch (filter.op) {
|
||||
case "eq":
|
||||
return rowValue === filter.value;
|
||||
case "neq":
|
||||
return rowValue !== filter.value;
|
||||
case "gt":
|
||||
return (rowValue as number) > (filter.value as number);
|
||||
case "gte":
|
||||
return (rowValue as number) >= (filter.value as number);
|
||||
case "lt":
|
||||
return (rowValue as number) < (filter.value as number);
|
||||
case "lte":
|
||||
return (rowValue as number) <= (filter.value as number);
|
||||
case "is":
|
||||
if (filter.value === null) return rowValue === null;
|
||||
if (filter.value === undefined) return rowValue === undefined;
|
||||
return rowValue === filter.value;
|
||||
case "like":
|
||||
return (
|
||||
typeof rowValue === "string" &&
|
||||
rowValue.includes((filter.value as string).replace(/%/g, ""))
|
||||
);
|
||||
case "ilike":
|
||||
return (
|
||||
typeof rowValue === "string" &&
|
||||
rowValue
|
||||
.toLowerCase()
|
||||
.includes((filter.value as string).replace(/%/g, "").toLowerCase())
|
||||
);
|
||||
case "in":
|
||||
return Array.isArray(filter.value) && filter.value.includes(rowValue);
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (this.orderColumn) {
|
||||
filtered.sort((a: unknown, b: unknown) => {
|
||||
const aVal = (a as Record<string, unknown>)[this.orderColumn!] as string | number;
|
||||
const bVal = (b as Record<string, unknown>)[this.orderColumn!] as string | number;
|
||||
if (aVal < bVal) return this.orderDirection === "desc" ? 1 : -1;
|
||||
if (aVal > bVal) return this.orderDirection === "desc" ? -1 : 1;
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
if (this.rangeMin !== undefined && this.rangeMax !== undefined) {
|
||||
filtered = filtered.slice(this.rangeMin, this.rangeMax + 1);
|
||||
} else if (this.limitValue !== undefined) {
|
||||
filtered = filtered.slice(0, this.limitValue);
|
||||
}
|
||||
|
||||
return { data: filtered as T[], error: null };
|
||||
}
|
||||
|
||||
private runMutation(): MockQueryResult<T> {
|
||||
if (this.mode === "delete") {
|
||||
return { data: null, error: null };
|
||||
}
|
||||
if (this.mutationData) {
|
||||
const items = [this.mutationData];
|
||||
const returning = items.map((item, i) => ({
|
||||
...item,
|
||||
id: (item as Record<string, unknown>).id ?? `generated-${Date.now()}-${i}`,
|
||||
}));
|
||||
return { data: returning as unknown as T[], error: null };
|
||||
}
|
||||
return { data: null, error: null };
|
||||
}
|
||||
}
|
||||
|
||||
interface MockMutationResult<T> {
|
||||
data: T[] | null;
|
||||
error: null;
|
||||
}
|
||||
|
||||
class MockMutationBuilder<T extends Record<string, unknown> = Record<string, unknown>> {
|
||||
private tableName: string;
|
||||
private data: unknown;
|
||||
|
||||
constructor(tableName: string, data: unknown) {
|
||||
this.tableName = tableName;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
select() {
|
||||
return new MockQueryBuilder<T>(this.tableName);
|
||||
}
|
||||
|
||||
eq() {
|
||||
return this;
|
||||
}
|
||||
|
||||
then<TResult1 = MockMutationResult<T>, TResult2 = never>(
|
||||
onfulfilled?: ((value: MockMutationResult<T>) => TResult1 | PromiseLike<TResult1>) | null,
|
||||
): PromiseLike<TResult1 | TResult2> {
|
||||
const result: MockMutationResult<T> = {
|
||||
data: Array.isArray(this.data)
|
||||
? this.data.map((item: Record<string, unknown>, i: number) => ({
|
||||
...item,
|
||||
id: item?.id ?? `generated-${Date.now()}-${i}`,
|
||||
})) as unknown as T[]
|
||||
: this.data
|
||||
? [{ ...(this.data as Record<string, unknown>), id: (this.data as Record<string, unknown>).id ?? `generated-${Date.now()}` }] as unknown as T[]
|
||||
: null,
|
||||
error: null,
|
||||
};
|
||||
return Promise.resolve(onfulfilled ? onfulfilled(result) : (result as unknown as TResult1));
|
||||
}
|
||||
}
|
||||
|
||||
class MockDeleteBuilder {
|
||||
eq(_column: string, _value: unknown) {
|
||||
return this;
|
||||
}
|
||||
in(_column: string, _values: unknown[]) {
|
||||
return this;
|
||||
}
|
||||
then(resolve: (value: unknown) => void) {
|
||||
resolve({ data: null, error: null });
|
||||
}
|
||||
}
|
||||
|
||||
class MockStorageBuilder {
|
||||
from(_bucket: string) {
|
||||
return {
|
||||
upload: async (_path: string, _file: unknown) => ({ data: { path: _path }, error: null }),
|
||||
download: async (_path: string) => ({ data: new Blob(), error: null }),
|
||||
remove: async (_paths: string[]) => ({ data: { paths: _paths }, error: null }),
|
||||
list: async () => ({ data: [], error: null }),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Auth credential types
|
||||
interface SignInCredentials {
|
||||
email?: string;
|
||||
password?: string;
|
||||
}
|
||||
|
||||
interface UserAttributes {
|
||||
email?: string;
|
||||
data?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
function createMockClient() {
|
||||
// The query builder is mutable, so we can't simply return a class
|
||||
// instance + spread mutation methods. The cleanest way to support
|
||||
// both `.from(t).select(...)...` (read) and `.from(t).update(d).eq(...)`
|
||||
// (write) in a single chain is to delegate everything to one object
|
||||
// and inspect `this.mode` lazily. We build that object via
|
||||
// `Object.assign` to keep the TypeScript inference happy.
|
||||
function makeFrom<T extends Record<string, unknown> = Record<string, unknown>>(table: string) {
|
||||
const qb = new MockQueryBuilder<T>(table);
|
||||
return Object.assign(qb, {
|
||||
insert: (data: Record<string, unknown>) => new MockMutationBuilder<T>(table, data),
|
||||
update: (data: Record<string, unknown>) => new MockMutationBuilder<T>(table, data),
|
||||
upsert: (data: Record<string, unknown>) => new MockMutationBuilder<T>(table, data),
|
||||
delete: () => new MockDeleteBuilder(),
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
from: makeFrom,
|
||||
storage: new MockStorageBuilder(),
|
||||
auth: {
|
||||
getSession: async () => ({ data: { session: null }, error: null }),
|
||||
getUser: async () => ({ data: { user: null }, error: null }),
|
||||
signInWithPassword: async (_creds: SignInCredentials) => ({
|
||||
data: { user: null, session: null },
|
||||
error: null,
|
||||
}),
|
||||
signOut: async () => ({ error: null }),
|
||||
updateUser: async (_attrs: UserAttributes): Promise<{ data: { user: null }; error: null }> => ({
|
||||
data: { user: null },
|
||||
error: null,
|
||||
}),
|
||||
onAuthStateChange: () => ({
|
||||
data: { subscription: { unsubscribe: () => {} } },
|
||||
}),
|
||||
},
|
||||
rpc: async (_name: string, _params?: Record<string, unknown>): Promise<{ data: null; error: null }> => ({
|
||||
data: null,
|
||||
error: null,
|
||||
}),
|
||||
channel: () => ({
|
||||
on: () => ({ subscribe: () => ({}) }),
|
||||
subscribe: () => ({}),
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
export const supabase = createMockClient();
|
||||
Reference in New Issue
Block a user