Compare commits
2 Commits
058f2bb94f
...
upgrade-ze
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7adb76f752 | ||
|
|
018dab38c0 |
@@ -8,7 +8,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@hono/node-server": "^1.19.5",
|
"@hono/node-server": "^1.19.5",
|
||||||
"@money/shared": "workspace:*",
|
"@money/shared": "link:../shared",
|
||||||
"better-auth": "^1.3.27",
|
"better-auth": "^1.3.27",
|
||||||
"hono": "^4.9.12",
|
"hono": "^4.9.12",
|
||||||
"plaid": "^39.0.0",
|
"plaid": "^39.0.0",
|
||||||
@@ -4,10 +4,10 @@ import 'react-native-reanimated';
|
|||||||
import { authClient } from '@/lib/auth-client';
|
import { authClient } from '@/lib/auth-client';
|
||||||
import { ZeroProvider } from '@rocicorp/zero/react';
|
import { ZeroProvider } from '@rocicorp/zero/react';
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { authDataSchema } from '@money/shared/auth';
|
import { authDataSchema } from '@/shared/src/auth';
|
||||||
import { Platform } from 'react-native';
|
import { Platform } from 'react-native';
|
||||||
import type { ZeroOptions } from '@rocicorp/zero';
|
import type { ZeroOptions } from '@rocicorp/zero';
|
||||||
import { schema, type Schema, createMutators, type Mutators, BASE_URL } from '@money/shared';
|
import { schema, type Schema, createMutators, type Mutators, BASE_URL } from '@/shared/src';
|
||||||
import { expoSQLiteStoreProvider } from "@rocicorp/zero/react-native";
|
import { expoSQLiteStoreProvider } from "@rocicorp/zero/react-native";
|
||||||
|
|
||||||
export const unstable_settings = {
|
export const unstable_settings = {
|
||||||
@@ -4,7 +4,6 @@ import { useQuery, useZero } from "@rocicorp/zero/react";
|
|||||||
import { queries, type Mutators, type Schema } from '@money/shared';
|
import { queries, type Mutators, type Schema } from '@money/shared';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { Link } from 'expo-router';
|
import { Link } from 'expo-router';
|
||||||
import Header from '@/components/Header';
|
|
||||||
|
|
||||||
export default function HomeScreen() {
|
export default function HomeScreen() {
|
||||||
const { data: session } = authClient.useSession();
|
const { data: session } = authClient.useSession();
|
||||||
@@ -69,7 +68,9 @@ export default function HomeScreen() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<View>
|
<View>
|
||||||
<Header />
|
<Link prefetch href="/settings">
|
||||||
|
<Button title="Settings" />
|
||||||
|
</Link>
|
||||||
<View style={{ flexDirection: "row" }}>
|
<View style={{ flexDirection: "row" }}>
|
||||||
<View style={{ backgroundColor: '' }}>
|
<View style={{ backgroundColor: '' }}>
|
||||||
{balances.map((bal, i) => <View key={bal.id} style={{ backgroundColor: i == accountIdx ? 'black' : undefined}}>
|
{balances.map((bal, i) => <View key={bal.id} style={{ backgroundColor: i == accountIdx ? 'black' : undefined}}>
|
||||||
41
app/settings.tsx
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||||
|
import { authClient } from '@/lib/auth-client';
|
||||||
|
import { Button, Linking, Text } from 'react-native';
|
||||||
|
import { useQuery, useZero } from "@rocicorp/zero/react";
|
||||||
|
import { queries, type Mutators, type Schema } from '@money/shared';
|
||||||
|
|
||||||
|
export default function HomeScreen() {
|
||||||
|
const { data: session } = authClient.useSession();
|
||||||
|
|
||||||
|
const onLogout = () => {
|
||||||
|
authClient.signOut();
|
||||||
|
}
|
||||||
|
const z = useZero<Schema, Mutators>();
|
||||||
|
const [user] = useQuery(queries.me(session));
|
||||||
|
const [plaidLink] = useQuery(queries.getPlaidLink(session));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SafeAreaView>
|
||||||
|
<Text>Hello {user?.name}</Text>
|
||||||
|
<Button onPress={onLogout} title="Logout" />
|
||||||
|
|
||||||
|
<Text>{JSON.stringify(plaidLink)}</Text>
|
||||||
|
{plaidLink ? <Button onPress={() => {
|
||||||
|
Linking.openURL(plaidLink.link);
|
||||||
|
}} title="Open Plaid" /> : <Text>No plaid link</Text>}
|
||||||
|
|
||||||
|
<Button onPress={() => {
|
||||||
|
z.mutate.link.create();
|
||||||
|
}} title="Generate Link" />
|
||||||
|
|
||||||
|
{plaidLink && <Button onPress={() => {
|
||||||
|
z.mutate.link.get({ link_token: plaidLink.token });
|
||||||
|
}} title="Check Link" />}
|
||||||
|
|
||||||
|
{plaidLink && <Button onPress={() => {
|
||||||
|
z.mutate.link.updateTransactions();
|
||||||
|
}} title="Update transactions" />}
|
||||||
|
</SafeAreaView>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
6
apps/expo/.gitignore
vendored
@@ -1,6 +0,0 @@
|
|||||||
|
|
||||||
# @generated expo-cli sync-2b81b286409207a5da26e14c78851eb30d8ccbdb
|
|
||||||
# The following patterns were generated by expo-cli
|
|
||||||
|
|
||||||
expo-env.d.ts
|
|
||||||
# @end expo-cli
|
|
||||||
@@ -1,130 +0,0 @@
|
|||||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
|
||||||
import { authClient } from '@/lib/auth-client';
|
|
||||||
import { Button, Linking, Platform, Pressable, Text, View } from 'react-native';
|
|
||||||
import { useQuery, useZero } from "@rocicorp/zero/react";
|
|
||||||
import { queries, type Mutators, type Schema } from '@money/shared';
|
|
||||||
import Header from '@/components/Header';
|
|
||||||
import { useEffect, useState, type ReactNode } from 'react';
|
|
||||||
|
|
||||||
export default function HomeScreen() {
|
|
||||||
const { data: session } = authClient.useSession();
|
|
||||||
|
|
||||||
const onLogout = () => {
|
|
||||||
authClient.signOut();
|
|
||||||
}
|
|
||||||
const z = useZero<Schema, Mutators>();
|
|
||||||
const [plaidLink] = useQuery(queries.getPlaidLink(session));
|
|
||||||
const [items] = useQuery(queries.getItems(session));
|
|
||||||
|
|
||||||
return (
|
|
||||||
<SafeAreaView>
|
|
||||||
<Header />
|
|
||||||
|
|
||||||
<UI
|
|
||||||
columns={[
|
|
||||||
{
|
|
||||||
name: "Banks",
|
|
||||||
items: items,
|
|
||||||
renderItem: (item, props) => <Row {...props}>{item.name}</Row>
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Family",
|
|
||||||
items: [],
|
|
||||||
renderItem() {
|
|
||||||
return <View></View>;
|
|
||||||
},
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
</SafeAreaView>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
type Col<T> = {
|
|
||||||
name: string;
|
|
||||||
items: T[];
|
|
||||||
renderItem: (item: T, props: { isSelected: boolean, isActive: boolean }) => ReactNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
type State = {
|
|
||||||
idx: number;
|
|
||||||
columns: Map<number, State>;
|
|
||||||
};
|
|
||||||
|
|
||||||
function UI({ columns }: { columns: Col<any>[] }) {
|
|
||||||
const [col, setCol] = useState(0);
|
|
||||||
const [state, setState] = useState<State>({
|
|
||||||
idx: 0,
|
|
||||||
columns: new Map(
|
|
||||||
Array.from({ length: columns.length })
|
|
||||||
.map((_, i) => ([i, { idx: 0, columns: new Map() }]))
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
const getColState = (res: State): State => {
|
|
||||||
let i = col;
|
|
||||||
while (i > 0) {
|
|
||||||
res = res.columns.get(res.idx)!;
|
|
||||||
i--;
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
const colState = getColState(state);
|
|
||||||
|
|
||||||
const curr = columns.at(col)!;
|
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (Platform.OS != 'web') return;
|
|
||||||
const handleKeyDown = (event: KeyboardEvent) => {
|
|
||||||
if (event.key === "j") {
|
|
||||||
setState((prev) => {
|
|
||||||
if (prev.idx + 1 == colState.columns.size) return prev;
|
|
||||||
return {...prev, ...{ idx: prev.idx + 1 }};
|
|
||||||
});
|
|
||||||
} else if (event.key === "k") {
|
|
||||||
setState((prev) => {
|
|
||||||
if (prev.idx == 0) return prev;
|
|
||||||
return {...prev, ...{ idx: prev.idx - 1 }};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
window.addEventListener("keydown", handleKeyDown);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
window.removeEventListener("keydown", handleKeyDown);
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<View style={{ flexDirection: "row" }}>
|
|
||||||
<Column>
|
|
||||||
{columns.map((c, i) => <Pressable onPress={() => { setCol(i) }}><Row isSelected={col == i} isActive={col == 0}>{c.name}</Row></Pressable>)}
|
|
||||||
</Column>
|
|
||||||
<Column>
|
|
||||||
{curr.items.map((item, i) => curr.renderItem(item, { isSelected: colState.idx == i, isActive: col == 1 }))}
|
|
||||||
</Column>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function Column({ children }: { children: ReactNode }) {
|
|
||||||
return (
|
|
||||||
<View>
|
|
||||||
{children}
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function Row({ children, isSelected, isActive }: { children: ReactNode, isSelected: boolean, isActive: boolean }) {
|
|
||||||
const color = isSelected ? 'white': undefined;
|
|
||||||
const backgroundColor = isSelected ? (isActive ? 'black' : 'gray'): undefined;
|
|
||||||
return (
|
|
||||||
<View>
|
|
||||||
<Text style={{ fontFamily: 'mono', color, backgroundColor }}>{children}</Text>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
import { authClient } from "@/lib/auth-client";
|
|
||||||
import { queries } from "@money/shared";
|
|
||||||
import { useQuery } from "@rocicorp/zero/react";
|
|
||||||
import { Link, usePathname, useRouter, type LinkProps } from "expo-router";
|
|
||||||
import { useEffect } from "react";
|
|
||||||
import { View, Text, Platform } from "react-native";
|
|
||||||
|
|
||||||
type Page = { name: string, href: LinkProps['href'] };
|
|
||||||
const PAGES: Page[] = [
|
|
||||||
{
|
|
||||||
name: "Home",
|
|
||||||
href: "/",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Settings",
|
|
||||||
href: "/settings",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
|
|
||||||
export default function Header() {
|
|
||||||
const router = useRouter();
|
|
||||||
const { data: session } = authClient.useSession();
|
|
||||||
const [user] = useQuery(queries.me(session));
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (Platform.OS != 'web') return;
|
|
||||||
const handleKeyDown = (event: KeyboardEvent) => {
|
|
||||||
if (event.key === "1" && event.ctrlKey) {
|
|
||||||
router.push(PAGES.at(0)!.href);
|
|
||||||
} else if (event.key === "2" && event.ctrlKey) {
|
|
||||||
router.push(PAGES.at(1)!.href);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
window.addEventListener("keydown", handleKeyDown);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
window.removeEventListener("keydown", handleKeyDown);
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
|
||||||
<View style={{ flexDirection: "row", justifyContent: "space-between", backgroundColor: "#f7e2c8" }}>
|
|
||||||
<View style={{ flexDirection: "row" }}>
|
|
||||||
{PAGES.map(page => <Page
|
|
||||||
key={page.name}
|
|
||||||
name={page.name}
|
|
||||||
href={page.href}
|
|
||||||
/>)}
|
|
||||||
</View>
|
|
||||||
|
|
||||||
<Link href={"#" as any}>
|
|
||||||
<Text style={{ fontFamily: 'mono' }}>{user?.name} </Text>
|
|
||||||
</Link>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function Page({ name, href }: Page) {
|
|
||||||
const path = usePathname();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Link href={href }>
|
|
||||||
<Text style={{ fontFamily: 'mono' }}>{path == href ? `[ ${name} ]` : ` ${name} `}</Text>
|
|
||||||
</Link>
|
|
||||||
)
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "@money/expo",
|
|
||||||
"main": "expo-router/entry",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"scripts": {
|
|
||||||
"start": "expo start",
|
|
||||||
"reset-project": "node ./scripts/reset-project.js",
|
|
||||||
"android": "expo start --android",
|
|
||||||
"ios": "expo start --ios",
|
|
||||||
"web": "expo start --web",
|
|
||||||
"build": "expo export --platform web",
|
|
||||||
"lint": "expo lint",
|
|
||||||
"db:migrate": "dotenv -- pnpm run --dir=shared db:migrate",
|
|
||||||
"db:gen": "dotenv -- pnpm run --dir=shared generate:zero"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@better-auth/expo": "^1.3.27",
|
|
||||||
"@expo/vector-icons": "^15.0.2",
|
|
||||||
"@money/shared": "workspace:*",
|
|
||||||
"@react-navigation/bottom-tabs": "^7.4.0",
|
|
||||||
"@react-navigation/elements": "^2.6.3",
|
|
||||||
"@react-navigation/native": "^7.1.8",
|
|
||||||
"@rocicorp/zero": "^0.23.2025090100",
|
|
||||||
"better-auth": "^1.3.27",
|
|
||||||
"drizzle-orm": "^0.44.6",
|
|
||||||
"expo": "~54.0.13",
|
|
||||||
"expo-constants": "~18.0.9",
|
|
||||||
"expo-crypto": "~15.0.7",
|
|
||||||
"expo-font": "~14.0.9",
|
|
||||||
"expo-haptics": "~15.0.7",
|
|
||||||
"expo-image": "~3.0.9",
|
|
||||||
"expo-linking": "~8.0.8",
|
|
||||||
"expo-router": "~6.0.11",
|
|
||||||
"expo-splash-screen": "~31.0.10",
|
|
||||||
"expo-sqlite": "~16.0.8",
|
|
||||||
"expo-status-bar": "~3.0.8",
|
|
||||||
"expo-symbols": "~1.0.7",
|
|
||||||
"expo-system-ui": "~6.0.7",
|
|
||||||
"expo-web-browser": "~15.0.8",
|
|
||||||
"pg": "^8.16.3",
|
|
||||||
"react": "19.1.0",
|
|
||||||
"react-dom": "19.1.0",
|
|
||||||
"react-native": "0.81.4",
|
|
||||||
"react-native-gesture-handler": "~2.28.0",
|
|
||||||
"react-native-reanimated": "~4.1.1",
|
|
||||||
"react-native-safe-area-context": "~5.6.0",
|
|
||||||
"react-native-screens": "~4.16.0",
|
|
||||||
"react-native-web": "~0.21.0",
|
|
||||||
"react-native-worklets": "0.5.1"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@types/pg": "^8.15.5",
|
|
||||||
"@types/react": "~19.1.0",
|
|
||||||
"dotenv-cli": "^10.0.0",
|
|
||||||
"drizzle-kit": "^0.31.5",
|
|
||||||
"eslint": "^9.25.0",
|
|
||||||
"eslint-config-expo": "~10.0.0",
|
|
||||||
"typescript": "~5.9.2"
|
|
||||||
},
|
|
||||||
"private": true
|
|
||||||
}
|
|
||||||
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 77 KiB After Width: | Height: | Size: 77 KiB |
|
Before Width: | Height: | Size: 4.0 KiB After Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 384 KiB After Width: | Height: | Size: 384 KiB |
|
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 5.0 KiB |
|
Before Width: | Height: | Size: 6.2 KiB After Width: | Height: | Size: 6.2 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
60
package.json
@@ -1,17 +1,71 @@
|
|||||||
{
|
{
|
||||||
"name": "money",
|
"name": "money",
|
||||||
"private": true,
|
"main": "expo-router/entry",
|
||||||
|
"version": "1.0.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
"start": "expo start",
|
||||||
|
"reset-project": "node ./scripts/reset-project.js",
|
||||||
|
"android": "expo start --android",
|
||||||
|
"ios": "expo start --ios",
|
||||||
|
"web": "expo start --web",
|
||||||
|
"build": "expo export --platform web",
|
||||||
|
"lint": "expo lint",
|
||||||
|
"db:migrate": "dotenv -- pnpm run --dir=shared db:migrate",
|
||||||
|
"db:gen": "dotenv -- pnpm run --dir=shared generate:zero",
|
||||||
"dev": "process-compose up -p 0"
|
"dev": "process-compose up -p 0"
|
||||||
},
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@better-auth/expo": "^1.3.27",
|
||||||
|
"@expo/vector-icons": "^15.0.2",
|
||||||
|
"@money/shared": "link:shared",
|
||||||
|
"@react-navigation/bottom-tabs": "^7.4.0",
|
||||||
|
"@react-navigation/elements": "^2.6.3",
|
||||||
|
"@react-navigation/native": "^7.1.8",
|
||||||
|
"@rocicorp/zero": "^0.24.2025101500",
|
||||||
|
"better-auth": "^1.3.27",
|
||||||
|
"drizzle-orm": "^0.44.6",
|
||||||
|
"expo": "~54.0.13",
|
||||||
|
"expo-constants": "~18.0.9",
|
||||||
|
"expo-crypto": "~15.0.7",
|
||||||
|
"expo-font": "~14.0.9",
|
||||||
|
"expo-haptics": "~15.0.7",
|
||||||
|
"expo-image": "~3.0.9",
|
||||||
|
"expo-linking": "~8.0.8",
|
||||||
|
"expo-router": "~6.0.11",
|
||||||
|
"expo-splash-screen": "~31.0.10",
|
||||||
|
"expo-sqlite": "~16.0.8",
|
||||||
|
"expo-status-bar": "~3.0.8",
|
||||||
|
"expo-symbols": "~1.0.7",
|
||||||
|
"expo-system-ui": "~6.0.7",
|
||||||
|
"expo-web-browser": "~15.0.8",
|
||||||
|
"pg": "^8.16.3",
|
||||||
|
"react": "19.1.0",
|
||||||
|
"react-dom": "19.1.0",
|
||||||
|
"react-native": "0.81.4",
|
||||||
|
"react-native-gesture-handler": "~2.28.0",
|
||||||
|
"react-native-reanimated": "~4.1.1",
|
||||||
|
"react-native-safe-area-context": "~5.6.0",
|
||||||
|
"react-native-screens": "~4.16.0",
|
||||||
|
"react-native-web": "~0.21.0",
|
||||||
|
"react-native-worklets": "0.5.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/pg": "^8.15.5",
|
||||||
|
"@types/react": "~19.1.0",
|
||||||
|
"dotenv-cli": "^10.0.0",
|
||||||
|
"drizzle-kit": "^0.31.5",
|
||||||
|
"eslint": "^9.25.0",
|
||||||
|
"eslint-config-expo": "~10.0.0",
|
||||||
|
"typescript": "~5.9.2"
|
||||||
|
},
|
||||||
|
"private": true,
|
||||||
"pnpm": {
|
"pnpm": {
|
||||||
"onlyBuiltDependencies": [
|
"onlyBuiltDependencies": [
|
||||||
"@rocicorp/zero-sqlite3"
|
"@rocicorp/zero-sqlite3"
|
||||||
],
|
],
|
||||||
"ignoredBuiltDependencies": [
|
"ignoredBuiltDependencies": [
|
||||||
"esbuild",
|
"esbuild",
|
||||||
"protobufjs",
|
"protobufjs"
|
||||||
"unrs-resolver"
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
3067
pnpm-lock.yaml
generated
@@ -1,4 +1,4 @@
|
|||||||
nodeLinker: hoisted
|
nodeLinker: hoisted
|
||||||
packages:
|
packages:
|
||||||
- 'apps/*'
|
- 'api'
|
||||||
- 'packages/*'
|
- 'shared'
|
||||||
|
|||||||
@@ -29,13 +29,14 @@ processes:
|
|||||||
command: "pnpm tsx ./scripts/set-machine-name.ts"
|
command: "pnpm tsx ./scripts/set-machine-name.ts"
|
||||||
|
|
||||||
expo:
|
expo:
|
||||||
command: "pnpm --filter=@money/expo start"
|
command: "pnpm start"
|
||||||
depends_on:
|
depends_on:
|
||||||
tailscale_machine_name:
|
tailscale_machine_name:
|
||||||
condition: process_completed_successfully
|
condition: process_completed_successfully
|
||||||
|
|
||||||
api:
|
api:
|
||||||
command: "pnpm --filter=@money/api dev"
|
command: "pnpm run dev"
|
||||||
|
working_dir: ./api
|
||||||
|
|
||||||
migrate:
|
migrate:
|
||||||
command: |
|
command: |
|
||||||
@@ -48,7 +49,7 @@ processes:
|
|||||||
db:
|
db:
|
||||||
condition: process_healthy
|
condition: process_healthy
|
||||||
zero:
|
zero:
|
||||||
command: npx zero-cache-dev -p packages/shared/src/schema.ts
|
command: npx zero-cache-dev -p shared/src/schema.ts
|
||||||
depends_on:
|
depends_on:
|
||||||
migrate:
|
migrate:
|
||||||
condition: process_completed_successfully
|
condition: process_completed_successfully
|
||||||
|
|||||||
@@ -9,10 +9,10 @@
|
|||||||
"./db": "./src/db/index.ts"
|
"./db": "./src/db/index.ts"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"drizzle-zero": "^0.14.3"
|
"drizzle-zero": "^0.15.1"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"generate:zero": "drizzle-zero generate -s ./src/db/schema/public.ts -o ./src/zero-schema.gen.ts -f && sed -i 's/enableLegacyQueries: true,/enableLegacyQueries: false,/g' src/zero-schema.gen.ts && sed -i 's/enableLegacyMutators: true,/enableLegacyMutators: false,/g' src/zero-schema.gen.ts",
|
"generate:zero": "drizzle-zero generate -s ./src/db/schema/public.ts -o ./src/zero-schema.gen.ts -f --disable-legacy-queries --disable-legacy-mutators",
|
||||||
"db:migrate": "drizzle-kit push"
|
"db:migrate": "drizzle-kit push"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -93,3 +93,9 @@ export const auditLogs = pgTable("audit_log", {
|
|||||||
action: text("action").notNull(),
|
action: text("action").notNull(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const plaidAccessTokens = pgTable("plaidAccessToken", {
|
||||||
|
id: text("id").primaryKey(),
|
||||||
|
userId: text("user_id").notNull(),
|
||||||
|
token: text("token").notNull(),
|
||||||
|
createdAt: timestamp("created_at").notNull().defaultNow(),
|
||||||
|
});
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { definePermissions } from "@rocicorp/zero";
|
||||||
import { pgTable, text, boolean, timestamp, uniqueIndex, decimal } from "drizzle-orm/pg-core";
|
import { pgTable, text, boolean, timestamp, uniqueIndex, decimal } from "drizzle-orm/pg-core";
|
||||||
|
|
||||||
export const users = pgTable(
|
export const users = pgTable(
|
||||||
@@ -45,13 +46,4 @@ export const balance = pgTable("balance", {
|
|||||||
name: text("name").notNull(),
|
name: text("name").notNull(),
|
||||||
createdAt: timestamp("created_at").notNull().defaultNow(),
|
createdAt: timestamp("created_at").notNull().defaultNow(),
|
||||||
updatedAt: timestamp("updated_at").notNull().defaultNow(),
|
updatedAt: timestamp("updated_at").notNull().defaultNow(),
|
||||||
});
|
})
|
||||||
|
|
||||||
export const plaidAccessTokens = pgTable("plaidAccessToken", {
|
|
||||||
id: text("id").primaryKey(),
|
|
||||||
name: text("name").notNull(),
|
|
||||||
logoUrl: text("logoUrl").notNull(),
|
|
||||||
userId: text("user_id").notNull(),
|
|
||||||
token: text("token").notNull(),
|
|
||||||
createdAt: timestamp("created_at").notNull().defaultNow(),
|
|
||||||
});
|
|
||||||
@@ -30,11 +30,5 @@ export const queries = {
|
|||||||
return builder.balance
|
return builder.balance
|
||||||
.where('user_id', '=', authData.user.id)
|
.where('user_id', '=', authData.user.id)
|
||||||
.orderBy('name', 'asc');
|
.orderBy('name', 'asc');
|
||||||
}),
|
|
||||||
getItems: syncedQueryWithContext('getItems', z.tuple([]), (authData: AuthData | null) => {
|
|
||||||
isLoggedIn(authData);
|
|
||||||
return builder.plaidAccessTokens
|
|
||||||
.where('userId', '=', authData.user.id)
|
|
||||||
.orderBy('createdAt', 'desc');
|
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
@@ -10,11 +10,9 @@
|
|||||||
|
|
||||||
import type { Row } from "@rocicorp/zero";
|
import type { Row } from "@rocicorp/zero";
|
||||||
import { createBuilder } from "@rocicorp/zero";
|
import { createBuilder } from "@rocicorp/zero";
|
||||||
import type { DrizzleToZeroSchema, ZeroCustomType } from "drizzle-zero";
|
import type { CustomType } from "drizzle-zero";
|
||||||
import type * as drizzleSchema from "./db/schema/public";
|
import type * as drizzleSchema from "./db/schema/public";
|
||||||
|
|
||||||
type ZeroSchema = DrizzleToZeroSchema<typeof drizzleSchema>;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Zero schema object.
|
* The Zero schema object.
|
||||||
* This type is auto-generated from your Drizzle schema definition.
|
* This type is auto-generated from your Drizzle schema definition.
|
||||||
@@ -27,8 +25,8 @@ export const schema = {
|
|||||||
id: {
|
id: {
|
||||||
type: "string",
|
type: "string",
|
||||||
optional: false,
|
optional: false,
|
||||||
customType: null as unknown as ZeroCustomType<
|
customType: null as unknown as CustomType<
|
||||||
ZeroSchema,
|
typeof drizzleSchema,
|
||||||
"balance",
|
"balance",
|
||||||
"id"
|
"id"
|
||||||
>,
|
>,
|
||||||
@@ -36,8 +34,8 @@ export const schema = {
|
|||||||
user_id: {
|
user_id: {
|
||||||
type: "string",
|
type: "string",
|
||||||
optional: false,
|
optional: false,
|
||||||
customType: null as unknown as ZeroCustomType<
|
customType: null as unknown as CustomType<
|
||||||
ZeroSchema,
|
typeof drizzleSchema,
|
||||||
"balance",
|
"balance",
|
||||||
"user_id"
|
"user_id"
|
||||||
>,
|
>,
|
||||||
@@ -46,8 +44,8 @@ export const schema = {
|
|||||||
plaid_id: {
|
plaid_id: {
|
||||||
type: "string",
|
type: "string",
|
||||||
optional: false,
|
optional: false,
|
||||||
customType: null as unknown as ZeroCustomType<
|
customType: null as unknown as CustomType<
|
||||||
ZeroSchema,
|
typeof drizzleSchema,
|
||||||
"balance",
|
"balance",
|
||||||
"plaid_id"
|
"plaid_id"
|
||||||
>,
|
>,
|
||||||
@@ -56,8 +54,8 @@ export const schema = {
|
|||||||
avaliable: {
|
avaliable: {
|
||||||
type: "number",
|
type: "number",
|
||||||
optional: false,
|
optional: false,
|
||||||
customType: null as unknown as ZeroCustomType<
|
customType: null as unknown as CustomType<
|
||||||
ZeroSchema,
|
typeof drizzleSchema,
|
||||||
"balance",
|
"balance",
|
||||||
"avaliable"
|
"avaliable"
|
||||||
>,
|
>,
|
||||||
@@ -65,8 +63,8 @@ export const schema = {
|
|||||||
current: {
|
current: {
|
||||||
type: "number",
|
type: "number",
|
||||||
optional: false,
|
optional: false,
|
||||||
customType: null as unknown as ZeroCustomType<
|
customType: null as unknown as CustomType<
|
||||||
ZeroSchema,
|
typeof drizzleSchema,
|
||||||
"balance",
|
"balance",
|
||||||
"current"
|
"current"
|
||||||
>,
|
>,
|
||||||
@@ -74,8 +72,8 @@ export const schema = {
|
|||||||
name: {
|
name: {
|
||||||
type: "string",
|
type: "string",
|
||||||
optional: false,
|
optional: false,
|
||||||
customType: null as unknown as ZeroCustomType<
|
customType: null as unknown as CustomType<
|
||||||
ZeroSchema,
|
typeof drizzleSchema,
|
||||||
"balance",
|
"balance",
|
||||||
"name"
|
"name"
|
||||||
>,
|
>,
|
||||||
@@ -83,8 +81,8 @@ export const schema = {
|
|||||||
createdAt: {
|
createdAt: {
|
||||||
type: "number",
|
type: "number",
|
||||||
optional: true,
|
optional: true,
|
||||||
customType: null as unknown as ZeroCustomType<
|
customType: null as unknown as CustomType<
|
||||||
ZeroSchema,
|
typeof drizzleSchema,
|
||||||
"balance",
|
"balance",
|
||||||
"createdAt"
|
"createdAt"
|
||||||
>,
|
>,
|
||||||
@@ -93,8 +91,8 @@ export const schema = {
|
|||||||
updatedAt: {
|
updatedAt: {
|
||||||
type: "number",
|
type: "number",
|
||||||
optional: true,
|
optional: true,
|
||||||
customType: null as unknown as ZeroCustomType<
|
customType: null as unknown as CustomType<
|
||||||
ZeroSchema,
|
typeof drizzleSchema,
|
||||||
"balance",
|
"balance",
|
||||||
"updatedAt"
|
"updatedAt"
|
||||||
>,
|
>,
|
||||||
@@ -103,77 +101,14 @@ export const schema = {
|
|||||||
},
|
},
|
||||||
primaryKey: ["id"],
|
primaryKey: ["id"],
|
||||||
},
|
},
|
||||||
plaidAccessTokens: {
|
|
||||||
name: "plaidAccessTokens",
|
|
||||||
columns: {
|
|
||||||
id: {
|
|
||||||
type: "string",
|
|
||||||
optional: false,
|
|
||||||
customType: null as unknown as ZeroCustomType<
|
|
||||||
ZeroSchema,
|
|
||||||
"plaidAccessTokens",
|
|
||||||
"id"
|
|
||||||
>,
|
|
||||||
},
|
|
||||||
name: {
|
|
||||||
type: "string",
|
|
||||||
optional: false,
|
|
||||||
customType: null as unknown as ZeroCustomType<
|
|
||||||
ZeroSchema,
|
|
||||||
"plaidAccessTokens",
|
|
||||||
"name"
|
|
||||||
>,
|
|
||||||
},
|
|
||||||
logoUrl: {
|
|
||||||
type: "string",
|
|
||||||
optional: false,
|
|
||||||
customType: null as unknown as ZeroCustomType<
|
|
||||||
ZeroSchema,
|
|
||||||
"plaidAccessTokens",
|
|
||||||
"logoUrl"
|
|
||||||
>,
|
|
||||||
},
|
|
||||||
userId: {
|
|
||||||
type: "string",
|
|
||||||
optional: false,
|
|
||||||
customType: null as unknown as ZeroCustomType<
|
|
||||||
ZeroSchema,
|
|
||||||
"plaidAccessTokens",
|
|
||||||
"userId"
|
|
||||||
>,
|
|
||||||
serverName: "user_id",
|
|
||||||
},
|
|
||||||
token: {
|
|
||||||
type: "string",
|
|
||||||
optional: false,
|
|
||||||
customType: null as unknown as ZeroCustomType<
|
|
||||||
ZeroSchema,
|
|
||||||
"plaidAccessTokens",
|
|
||||||
"token"
|
|
||||||
>,
|
|
||||||
},
|
|
||||||
createdAt: {
|
|
||||||
type: "number",
|
|
||||||
optional: true,
|
|
||||||
customType: null as unknown as ZeroCustomType<
|
|
||||||
ZeroSchema,
|
|
||||||
"plaidAccessTokens",
|
|
||||||
"createdAt"
|
|
||||||
>,
|
|
||||||
serverName: "created_at",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
primaryKey: ["id"],
|
|
||||||
serverName: "plaidAccessToken",
|
|
||||||
},
|
|
||||||
plaidLink: {
|
plaidLink: {
|
||||||
name: "plaidLink",
|
name: "plaidLink",
|
||||||
columns: {
|
columns: {
|
||||||
id: {
|
id: {
|
||||||
type: "string",
|
type: "string",
|
||||||
optional: false,
|
optional: false,
|
||||||
customType: null as unknown as ZeroCustomType<
|
customType: null as unknown as CustomType<
|
||||||
ZeroSchema,
|
typeof drizzleSchema,
|
||||||
"plaidLink",
|
"plaidLink",
|
||||||
"id"
|
"id"
|
||||||
>,
|
>,
|
||||||
@@ -181,8 +116,8 @@ export const schema = {
|
|||||||
user_id: {
|
user_id: {
|
||||||
type: "string",
|
type: "string",
|
||||||
optional: false,
|
optional: false,
|
||||||
customType: null as unknown as ZeroCustomType<
|
customType: null as unknown as CustomType<
|
||||||
ZeroSchema,
|
typeof drizzleSchema,
|
||||||
"plaidLink",
|
"plaidLink",
|
||||||
"user_id"
|
"user_id"
|
||||||
>,
|
>,
|
||||||
@@ -190,8 +125,8 @@ export const schema = {
|
|||||||
link: {
|
link: {
|
||||||
type: "string",
|
type: "string",
|
||||||
optional: false,
|
optional: false,
|
||||||
customType: null as unknown as ZeroCustomType<
|
customType: null as unknown as CustomType<
|
||||||
ZeroSchema,
|
typeof drizzleSchema,
|
||||||
"plaidLink",
|
"plaidLink",
|
||||||
"link"
|
"link"
|
||||||
>,
|
>,
|
||||||
@@ -199,8 +134,8 @@ export const schema = {
|
|||||||
token: {
|
token: {
|
||||||
type: "string",
|
type: "string",
|
||||||
optional: false,
|
optional: false,
|
||||||
customType: null as unknown as ZeroCustomType<
|
customType: null as unknown as CustomType<
|
||||||
ZeroSchema,
|
typeof drizzleSchema,
|
||||||
"plaidLink",
|
"plaidLink",
|
||||||
"token"
|
"token"
|
||||||
>,
|
>,
|
||||||
@@ -208,8 +143,8 @@ export const schema = {
|
|||||||
createdAt: {
|
createdAt: {
|
||||||
type: "number",
|
type: "number",
|
||||||
optional: true,
|
optional: true,
|
||||||
customType: null as unknown as ZeroCustomType<
|
customType: null as unknown as CustomType<
|
||||||
ZeroSchema,
|
typeof drizzleSchema,
|
||||||
"plaidLink",
|
"plaidLink",
|
||||||
"createdAt"
|
"createdAt"
|
||||||
>,
|
>,
|
||||||
@@ -224,8 +159,8 @@ export const schema = {
|
|||||||
id: {
|
id: {
|
||||||
type: "string",
|
type: "string",
|
||||||
optional: false,
|
optional: false,
|
||||||
customType: null as unknown as ZeroCustomType<
|
customType: null as unknown as CustomType<
|
||||||
ZeroSchema,
|
typeof drizzleSchema,
|
||||||
"transaction",
|
"transaction",
|
||||||
"id"
|
"id"
|
||||||
>,
|
>,
|
||||||
@@ -233,8 +168,8 @@ export const schema = {
|
|||||||
user_id: {
|
user_id: {
|
||||||
type: "string",
|
type: "string",
|
||||||
optional: false,
|
optional: false,
|
||||||
customType: null as unknown as ZeroCustomType<
|
customType: null as unknown as CustomType<
|
||||||
ZeroSchema,
|
typeof drizzleSchema,
|
||||||
"transaction",
|
"transaction",
|
||||||
"user_id"
|
"user_id"
|
||||||
>,
|
>,
|
||||||
@@ -242,8 +177,8 @@ export const schema = {
|
|||||||
plaid_id: {
|
plaid_id: {
|
||||||
type: "string",
|
type: "string",
|
||||||
optional: false,
|
optional: false,
|
||||||
customType: null as unknown as ZeroCustomType<
|
customType: null as unknown as CustomType<
|
||||||
ZeroSchema,
|
typeof drizzleSchema,
|
||||||
"transaction",
|
"transaction",
|
||||||
"plaid_id"
|
"plaid_id"
|
||||||
>,
|
>,
|
||||||
@@ -251,8 +186,8 @@ export const schema = {
|
|||||||
account_id: {
|
account_id: {
|
||||||
type: "string",
|
type: "string",
|
||||||
optional: false,
|
optional: false,
|
||||||
customType: null as unknown as ZeroCustomType<
|
customType: null as unknown as CustomType<
|
||||||
ZeroSchema,
|
typeof drizzleSchema,
|
||||||
"transaction",
|
"transaction",
|
||||||
"account_id"
|
"account_id"
|
||||||
>,
|
>,
|
||||||
@@ -260,8 +195,8 @@ export const schema = {
|
|||||||
name: {
|
name: {
|
||||||
type: "string",
|
type: "string",
|
||||||
optional: false,
|
optional: false,
|
||||||
customType: null as unknown as ZeroCustomType<
|
customType: null as unknown as CustomType<
|
||||||
ZeroSchema,
|
typeof drizzleSchema,
|
||||||
"transaction",
|
"transaction",
|
||||||
"name"
|
"name"
|
||||||
>,
|
>,
|
||||||
@@ -269,8 +204,8 @@ export const schema = {
|
|||||||
amount: {
|
amount: {
|
||||||
type: "number",
|
type: "number",
|
||||||
optional: false,
|
optional: false,
|
||||||
customType: null as unknown as ZeroCustomType<
|
customType: null as unknown as CustomType<
|
||||||
ZeroSchema,
|
typeof drizzleSchema,
|
||||||
"transaction",
|
"transaction",
|
||||||
"amount"
|
"amount"
|
||||||
>,
|
>,
|
||||||
@@ -278,8 +213,8 @@ export const schema = {
|
|||||||
datetime: {
|
datetime: {
|
||||||
type: "number",
|
type: "number",
|
||||||
optional: true,
|
optional: true,
|
||||||
customType: null as unknown as ZeroCustomType<
|
customType: null as unknown as CustomType<
|
||||||
ZeroSchema,
|
typeof drizzleSchema,
|
||||||
"transaction",
|
"transaction",
|
||||||
"datetime"
|
"datetime"
|
||||||
>,
|
>,
|
||||||
@@ -287,8 +222,8 @@ export const schema = {
|
|||||||
authorized_datetime: {
|
authorized_datetime: {
|
||||||
type: "number",
|
type: "number",
|
||||||
optional: true,
|
optional: true,
|
||||||
customType: null as unknown as ZeroCustomType<
|
customType: null as unknown as CustomType<
|
||||||
ZeroSchema,
|
typeof drizzleSchema,
|
||||||
"transaction",
|
"transaction",
|
||||||
"authorized_datetime"
|
"authorized_datetime"
|
||||||
>,
|
>,
|
||||||
@@ -296,8 +231,8 @@ export const schema = {
|
|||||||
json: {
|
json: {
|
||||||
type: "string",
|
type: "string",
|
||||||
optional: true,
|
optional: true,
|
||||||
customType: null as unknown as ZeroCustomType<
|
customType: null as unknown as CustomType<
|
||||||
ZeroSchema,
|
typeof drizzleSchema,
|
||||||
"transaction",
|
"transaction",
|
||||||
"json"
|
"json"
|
||||||
>,
|
>,
|
||||||
@@ -305,8 +240,8 @@ export const schema = {
|
|||||||
createdAt: {
|
createdAt: {
|
||||||
type: "number",
|
type: "number",
|
||||||
optional: true,
|
optional: true,
|
||||||
customType: null as unknown as ZeroCustomType<
|
customType: null as unknown as CustomType<
|
||||||
ZeroSchema,
|
typeof drizzleSchema,
|
||||||
"transaction",
|
"transaction",
|
||||||
"createdAt"
|
"createdAt"
|
||||||
>,
|
>,
|
||||||
@@ -315,8 +250,8 @@ export const schema = {
|
|||||||
updatedAt: {
|
updatedAt: {
|
||||||
type: "number",
|
type: "number",
|
||||||
optional: true,
|
optional: true,
|
||||||
customType: null as unknown as ZeroCustomType<
|
customType: null as unknown as CustomType<
|
||||||
ZeroSchema,
|
typeof drizzleSchema,
|
||||||
"transaction",
|
"transaction",
|
||||||
"updatedAt"
|
"updatedAt"
|
||||||
>,
|
>,
|
||||||
@@ -331,8 +266,8 @@ export const schema = {
|
|||||||
id: {
|
id: {
|
||||||
type: "string",
|
type: "string",
|
||||||
optional: false,
|
optional: false,
|
||||||
customType: null as unknown as ZeroCustomType<
|
customType: null as unknown as CustomType<
|
||||||
ZeroSchema,
|
typeof drizzleSchema,
|
||||||
"users",
|
"users",
|
||||||
"id"
|
"id"
|
||||||
>,
|
>,
|
||||||
@@ -340,8 +275,8 @@ export const schema = {
|
|||||||
name: {
|
name: {
|
||||||
type: "string",
|
type: "string",
|
||||||
optional: true,
|
optional: true,
|
||||||
customType: null as unknown as ZeroCustomType<
|
customType: null as unknown as CustomType<
|
||||||
ZeroSchema,
|
typeof drizzleSchema,
|
||||||
"users",
|
"users",
|
||||||
"name"
|
"name"
|
||||||
>,
|
>,
|
||||||
@@ -349,8 +284,8 @@ export const schema = {
|
|||||||
email: {
|
email: {
|
||||||
type: "string",
|
type: "string",
|
||||||
optional: false,
|
optional: false,
|
||||||
customType: null as unknown as ZeroCustomType<
|
customType: null as unknown as CustomType<
|
||||||
ZeroSchema,
|
typeof drizzleSchema,
|
||||||
"users",
|
"users",
|
||||||
"email"
|
"email"
|
||||||
>,
|
>,
|
||||||
@@ -358,8 +293,8 @@ export const schema = {
|
|||||||
emailVerified: {
|
emailVerified: {
|
||||||
type: "boolean",
|
type: "boolean",
|
||||||
optional: true,
|
optional: true,
|
||||||
customType: null as unknown as ZeroCustomType<
|
customType: null as unknown as CustomType<
|
||||||
ZeroSchema,
|
typeof drizzleSchema,
|
||||||
"users",
|
"users",
|
||||||
"emailVerified"
|
"emailVerified"
|
||||||
>,
|
>,
|
||||||
@@ -368,8 +303,8 @@ export const schema = {
|
|||||||
image: {
|
image: {
|
||||||
type: "string",
|
type: "string",
|
||||||
optional: true,
|
optional: true,
|
||||||
customType: null as unknown as ZeroCustomType<
|
customType: null as unknown as CustomType<
|
||||||
ZeroSchema,
|
typeof drizzleSchema,
|
||||||
"users",
|
"users",
|
||||||
"image"
|
"image"
|
||||||
>,
|
>,
|
||||||
@@ -377,8 +312,8 @@ export const schema = {
|
|||||||
createdAt: {
|
createdAt: {
|
||||||
type: "number",
|
type: "number",
|
||||||
optional: true,
|
optional: true,
|
||||||
customType: null as unknown as ZeroCustomType<
|
customType: null as unknown as CustomType<
|
||||||
ZeroSchema,
|
typeof drizzleSchema,
|
||||||
"users",
|
"users",
|
||||||
"createdAt"
|
"createdAt"
|
||||||
>,
|
>,
|
||||||
@@ -387,8 +322,8 @@ export const schema = {
|
|||||||
updatedAt: {
|
updatedAt: {
|
||||||
type: "number",
|
type: "number",
|
||||||
optional: true,
|
optional: true,
|
||||||
customType: null as unknown as ZeroCustomType<
|
customType: null as unknown as CustomType<
|
||||||
ZeroSchema,
|
typeof drizzleSchema,
|
||||||
"users",
|
"users",
|
||||||
"updatedAt"
|
"updatedAt"
|
||||||
>,
|
>,
|
||||||
@@ -414,11 +349,6 @@ export type Schema = typeof schema;
|
|||||||
* This type is auto-generated from your Drizzle schema definition.
|
* This type is auto-generated from your Drizzle schema definition.
|
||||||
*/
|
*/
|
||||||
export type Balance = Row<Schema["tables"]["balance"]>;
|
export type Balance = Row<Schema["tables"]["balance"]>;
|
||||||
/**
|
|
||||||
* Represents a row from the "plaidAccessTokens" table.
|
|
||||||
* This type is auto-generated from your Drizzle schema definition.
|
|
||||||
*/
|
|
||||||
export type PlaidAccessToken = Row<Schema["tables"]["plaidAccessTokens"]>;
|
|
||||||
/**
|
/**
|
||||||
* Represents a row from the "plaidLink" table.
|
* Represents a row from the "plaidLink" table.
|
||||||
* This type is auto-generated from your Drizzle schema definition.
|
* This type is auto-generated from your Drizzle schema definition.
|
||||||